@neuroverseos/governance 0.7.0 → 0.8.1
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 +95 -4
- package/dist/{chunk-T6EQ7ZBG.js → chunk-ETDIEVAX.js} +880 -134
- package/dist/{chunk-VGFDMPVB.js → chunk-F2LWMOM5.js} +283 -1
- package/dist/cli/neuroverse.cjs +1599 -360
- package/dist/cli/radiant.cjs +1190 -227
- package/dist/cli/radiant.js +25 -7
- package/dist/cli/worldmodel.cjs +300 -21
- package/dist/cli/worldmodel.js +76 -1
- package/dist/{lenses-K5FVSALR.js → lenses-YDMKSXDL.js} +5 -3
- package/dist/radiant/index.cjs +1183 -138
- package/dist/radiant/index.d.cts +382 -23
- package/dist/radiant/index.d.ts +382 -23
- package/dist/radiant/index.js +37 -3
- package/dist/{server-BXMC5NOE.js → server-ZSQ6DRSN.js} +2 -2
- package/dist/worldmodel-create-5SWHVNMQ.js +195 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getLens
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-F2LWMOM5.js";
|
|
4
4
|
import {
|
|
5
5
|
loadWorld
|
|
6
6
|
} from "./chunk-I4RTIMLX.js";
|
|
@@ -8,91 +8,166 @@ import {
|
|
|
8
8
|
evaluateGuard
|
|
9
9
|
} from "./chunk-ZAF6JH23.js";
|
|
10
10
|
|
|
11
|
+
// src/radiant/core/compress.ts
|
|
12
|
+
function compressWorldmodel(content) {
|
|
13
|
+
const lines = [];
|
|
14
|
+
const missionMatch = content.match(/##\s*Mission\s*\n+(?:<!--[\s\S]*?-->\s*\n+)?(.*?)(?:\n\n|\n##|$)/s);
|
|
15
|
+
if (missionMatch) {
|
|
16
|
+
const mission = missionMatch[1].trim().split("\n")[0];
|
|
17
|
+
lines.push(`Mission: ${mission}`);
|
|
18
|
+
}
|
|
19
|
+
const domainMatches = content.matchAll(/###\s+([^\n]+)/g);
|
|
20
|
+
const domains = [];
|
|
21
|
+
for (const m of domainMatches) {
|
|
22
|
+
const name = m[1].trim();
|
|
23
|
+
if (name !== "Skills" && name !== "Values" && !name.startsWith("####")) {
|
|
24
|
+
domains.push(name);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (domains.length > 0) {
|
|
28
|
+
lines.push(`Domains: ${domains.join(", ")}`);
|
|
29
|
+
}
|
|
30
|
+
const invariantSection = content.match(/(?:Invariants|## Invariants|invariants)([\s\S]*?)(?:\n#|\n---|\n\n\n)/i);
|
|
31
|
+
if (invariantSection) {
|
|
32
|
+
const invLines = invariantSection[1].match(/^[-*]\s+`?([^`\n]+)/gm);
|
|
33
|
+
if (invLines) {
|
|
34
|
+
lines.push("\nInvariants:");
|
|
35
|
+
for (const inv of invLines.slice(0, 10)) {
|
|
36
|
+
lines.push(inv.trim());
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const prioritySection = content.match(/(?:Decision Priorities|## Decision Priorities)([\s\S]*?)(?:\n#|\n---|\n\n\n)/i);
|
|
41
|
+
if (prioritySection) {
|
|
42
|
+
const priLines = prioritySection[1].match(/^[-*]\s+.+>.+/gm);
|
|
43
|
+
if (priLines) {
|
|
44
|
+
lines.push("\nPriorities:");
|
|
45
|
+
for (const pri of priLines.slice(0, 10)) {
|
|
46
|
+
lines.push(pri.trim());
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const signalSection = content.match(/(?:## Signals)([\s\S]*?)(?:\n#|\n---|\n\n\n)/i);
|
|
51
|
+
if (signalSection) {
|
|
52
|
+
const sigLines = signalSection[1].match(/^[-*]\s+(\w+)/gm);
|
|
53
|
+
if (sigLines) {
|
|
54
|
+
lines.push(`
|
|
55
|
+
Signals: ${sigLines.map((s) => s.replace(/^[-*]\s+/, "")).join(", ")}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const driftSection = content.match(/(?:Drift Behaviors|## Drift Behaviors)([\s\S]*?)(?:\n#|\n---|\n\n\n)/i);
|
|
59
|
+
if (driftSection) {
|
|
60
|
+
const driftLines = driftSection[1].match(/^[-*]\s+(.+)/gm);
|
|
61
|
+
if (driftLines) {
|
|
62
|
+
lines.push("\nDrift behaviors:");
|
|
63
|
+
for (const d of driftLines.slice(0, 5)) {
|
|
64
|
+
lines.push(d.trim());
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const compressed = lines.join("\n");
|
|
69
|
+
if (compressed.length < 50) {
|
|
70
|
+
return content.slice(0, 2e3) + "\n[truncated]";
|
|
71
|
+
}
|
|
72
|
+
return compressed;
|
|
73
|
+
}
|
|
74
|
+
function compressExocortex(ctx) {
|
|
75
|
+
const lines = [];
|
|
76
|
+
if (ctx.attention) {
|
|
77
|
+
lines.push(`Attention: ${firstMeaningfulLine(ctx.attention)}`);
|
|
78
|
+
}
|
|
79
|
+
if (ctx.goals) {
|
|
80
|
+
lines.push(`Goals: ${firstNLines(ctx.goals, 3)}`);
|
|
81
|
+
}
|
|
82
|
+
if (ctx.sprint) {
|
|
83
|
+
lines.push(`Sprint: ${firstNLines(ctx.sprint, 3)}`);
|
|
84
|
+
}
|
|
85
|
+
if (ctx.identity) {
|
|
86
|
+
lines.push(`Identity: ${firstMeaningfulLine(ctx.identity)}`);
|
|
87
|
+
}
|
|
88
|
+
if (ctx.organization) {
|
|
89
|
+
lines.push(`Org: ${firstMeaningfulLine(ctx.organization)}`);
|
|
90
|
+
}
|
|
91
|
+
return lines.join("\n");
|
|
92
|
+
}
|
|
93
|
+
function compressLens(lens) {
|
|
94
|
+
return {
|
|
95
|
+
evaluationQuestions: lens.primary_frame.evaluation_questions.map((q, i) => `${i + 1}. ${q}`).join("\n"),
|
|
96
|
+
scoringRubric: lens.primary_frame.scoring_rubric,
|
|
97
|
+
forbiddenPhrases: lens.forbidden_phrases.join(", "),
|
|
98
|
+
jargonTranslations: Object.entries(lens.vocabulary.jargon_translations).map(([k, v]) => `${k} \u2192 ${v}`).join("; "),
|
|
99
|
+
strategicPatterns: lens.strategic_patterns.slice(0, 5).join("\n")
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function compressPriorReads(reads) {
|
|
103
|
+
if (reads.length === 0) return "";
|
|
104
|
+
const patternCounts = /* @__PURE__ */ new Map();
|
|
105
|
+
for (const read of reads) {
|
|
106
|
+
for (const name of read.patternNames) {
|
|
107
|
+
patternCounts.set(name, (patternCounts.get(name) ?? 0) + 1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const sorted = [...patternCounts.entries()].sort((a, b) => b[1] - a[1]);
|
|
111
|
+
if (sorted.length === 0) {
|
|
112
|
+
return `${reads.length} prior reads, no patterns extracted.`;
|
|
113
|
+
}
|
|
114
|
+
const patternList = sorted.map(([name, count]) => `${name} (${count}x)`).join(", ");
|
|
115
|
+
return `${reads.length} prior reads. Patterns seen: ${patternList}. If these recur, note persistence.`;
|
|
116
|
+
}
|
|
117
|
+
function firstMeaningfulLine(text) {
|
|
118
|
+
const lines = text.split("\n").filter((l) => {
|
|
119
|
+
const t = l.trim();
|
|
120
|
+
return t.length > 0 && !t.startsWith("#") && !t.startsWith("<!--");
|
|
121
|
+
});
|
|
122
|
+
return lines[0]?.slice(0, 200) ?? "";
|
|
123
|
+
}
|
|
124
|
+
function firstNLines(text, n) {
|
|
125
|
+
const lines = text.split("\n").filter((l) => {
|
|
126
|
+
const t = l.trim();
|
|
127
|
+
return t.length > 0 && !t.startsWith("#") && !t.startsWith("<!--");
|
|
128
|
+
});
|
|
129
|
+
return lines.slice(0, n).map((l) => l.slice(0, 150)).join("; ");
|
|
130
|
+
}
|
|
131
|
+
|
|
11
132
|
// src/radiant/core/prompt.ts
|
|
12
133
|
function composeSystemPrompt(worldmodelContent, lens) {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
expectations for this organization. Every response you produce must
|
|
20
|
-
be grounded in this worldmodel.
|
|
21
|
-
|
|
22
|
-
` + worldmodelContent
|
|
23
|
-
);
|
|
24
|
-
const frame = lens.primary_frame;
|
|
25
|
-
const questionsBlock = frame.evaluation_questions.map((q, i) => `${i + 1}. ${q}`).join("\n");
|
|
26
|
-
const overlapsBlock = frame.overlaps.map(
|
|
27
|
-
(o) => `- ${o.domains[0]} + ${o.domains[1]} = **${o.emergent_state}**: ${o.description}`
|
|
28
|
-
).join("\n");
|
|
29
|
-
sections.push(
|
|
30
|
-
`## How to Think (Analytical Frame: ${lens.name})
|
|
31
|
-
|
|
32
|
-
${frame.scoring_rubric}
|
|
33
|
-
|
|
34
|
-
### Evaluation questions to reason through
|
|
134
|
+
const compressedWorld = compressWorldmodel(worldmodelContent);
|
|
135
|
+
const cl = compressLens(lens);
|
|
136
|
+
const overlapsBlock = lens.primary_frame.overlaps.map((o) => `${o.domains[0]} + ${o.domains[1]} = ${o.emergent_state}`).join("\n");
|
|
137
|
+
return [
|
|
138
|
+
// Section 1: Compressed worldmodel
|
|
139
|
+
`## Worldmodel (compressed)
|
|
35
140
|
|
|
36
|
-
${
|
|
141
|
+
${compressedWorld}`,
|
|
142
|
+
// Section 2: Analytical frame (evaluation questions + rubric)
|
|
143
|
+
`## How to Think
|
|
37
144
|
|
|
38
|
-
|
|
145
|
+
${cl.scoringRubric}
|
|
39
146
|
|
|
40
|
-
|
|
147
|
+
Questions:
|
|
148
|
+
${cl.evaluationQuestions}
|
|
41
149
|
|
|
42
|
-
|
|
150
|
+
Overlaps: ${overlapsBlock}
|
|
151
|
+
Center: ${lens.primary_frame.center_identity}
|
|
43
152
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const vocabArchitecture = lens.vocabulary.architecture.map((t) => `\`${t}\``).join(", ");
|
|
48
|
-
const vocabProperNouns = lens.vocabulary.proper_nouns.map((n) => `**${n}**`).join(", ");
|
|
49
|
-
const strategicBlock = lens.strategic_patterns.map((p) => `- ${p}`).join("\n");
|
|
50
|
-
sections.push(
|
|
51
|
-
`## How to Speak (Voice: ${lens.name})
|
|
153
|
+
Translate before output: ${cl.jargonTranslations}`,
|
|
154
|
+
// Section 3: Voice (compressed — register + key rules only)
|
|
155
|
+
`## Voice: ${lens.name}
|
|
52
156
|
|
|
53
157
|
Register: ${lens.voice.register}
|
|
54
|
-
|
|
55
|
-
Rules:
|
|
56
|
-
- Active voice: ${lens.voice.active_voice}
|
|
57
|
-
- Named specificity (people, places, numbers): ${lens.voice.specificity}
|
|
58
|
-
- Hype vocabulary: ${lens.voice.hype_vocabulary}
|
|
59
|
-
- Hedging / qualified phrasing: ${lens.voice.hedging}
|
|
60
|
-
- Playfulness: ${lens.voice.playfulness}
|
|
61
|
-
- Close with strategic frame: ${lens.voice.close_with_strategic_frame}
|
|
62
|
-
- Honesty about failure: ${lens.voice.honesty_about_failure}
|
|
63
|
-
|
|
64
|
-
### Output translation discipline
|
|
158
|
+
Active voice: ${lens.voice.active_voice}. Specificity: ${lens.voice.specificity}. Hedging: ${lens.voice.hedging}. Hype: ${lens.voice.hype_vocabulary}. Honesty about failure: ${lens.voice.honesty_about_failure}.
|
|
65
159
|
|
|
66
160
|
${lens.voice.output_translation}
|
|
67
161
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
Preferred term substitutions:
|
|
73
|
-
${vocabPreferred}
|
|
74
|
-
|
|
75
|
-
Architecture vocabulary: ${vocabArchitecture}
|
|
76
|
-
|
|
77
|
-
### Strategic decision patterns
|
|
78
|
-
|
|
79
|
-
When recommending action, these patterns reflect how this organization resolves tradeoffs:
|
|
80
|
-
|
|
81
|
-
${strategicBlock}`
|
|
82
|
-
);
|
|
83
|
-
const forbiddenBlock = lens.forbidden_phrases.map((p) => `- "${p}"`).join("\n");
|
|
84
|
-
sections.push(
|
|
162
|
+
Strategic patterns:
|
|
163
|
+
${cl.strategicPatterns}`,
|
|
164
|
+
// Section 4: Guardrails (forbidden phrases as comma-separated, not bulleted)
|
|
85
165
|
`## Guardrails
|
|
86
166
|
|
|
87
|
-
Do NOT use
|
|
88
|
-
reaching for one, rephrase in direct, active, specific language instead.
|
|
167
|
+
Do NOT use: ${cl.forbiddenPhrases}
|
|
89
168
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
If your response would violate a worldmodel invariant, state the conflict
|
|
93
|
-
explicitly and propose an alternative that honors the invariant.`
|
|
94
|
-
);
|
|
95
|
-
return sections.join("\n\n---\n\n");
|
|
169
|
+
If a response would violate a worldmodel invariant, state the conflict and propose an alternative.`
|
|
170
|
+
].join("\n\n---\n\n");
|
|
96
171
|
}
|
|
97
172
|
|
|
98
173
|
// src/radiant/core/voice-check.ts
|
|
@@ -145,17 +220,30 @@ function resolveLens(id) {
|
|
|
145
220
|
}
|
|
146
221
|
|
|
147
222
|
// src/radiant/core/scopes.ts
|
|
148
|
-
function
|
|
223
|
+
function parseScope(scope) {
|
|
149
224
|
const cleaned = scope.replace(/^https?:\/\//, "").replace(/^github\.com\//, "").replace(/\.git$/, "").replace(/\/$/, "");
|
|
150
|
-
const parts = cleaned.split("/");
|
|
151
|
-
if (parts.length
|
|
225
|
+
const parts = cleaned.split("/").filter(Boolean);
|
|
226
|
+
if (parts.length === 0 || !parts[0]) {
|
|
227
|
+
throw new Error(
|
|
228
|
+
`Cannot parse scope: "${scope}". Expected "owner/repo" or "owner".`
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
if (parts.length === 1) {
|
|
232
|
+
return { type: "org", owner: parts[0] };
|
|
233
|
+
}
|
|
234
|
+
return { type: "repo", owner: parts[0], repo: parts[1] };
|
|
235
|
+
}
|
|
236
|
+
function parseRepoScope(scope) {
|
|
237
|
+
const parsed = parseScope(scope);
|
|
238
|
+
if (parsed.type === "org") {
|
|
152
239
|
throw new Error(
|
|
153
|
-
`
|
|
240
|
+
`Expected "owner/repo" but got org-level scope "${parsed.owner}". Use parseScope() for org-level.`
|
|
154
241
|
);
|
|
155
242
|
}
|
|
156
|
-
return
|
|
243
|
+
return parsed;
|
|
157
244
|
}
|
|
158
245
|
function formatScope(scope) {
|
|
246
|
+
if (scope.type === "org") return `${scope.owner} (org)`;
|
|
159
247
|
return `${scope.owner}/${scope.repo}`;
|
|
160
248
|
}
|
|
161
249
|
|
|
@@ -339,22 +427,519 @@ async function fetchJSON(url, headers) {
|
|
|
339
427
|
}
|
|
340
428
|
return await res.json();
|
|
341
429
|
}
|
|
430
|
+
async function fetchGitHubOrgActivity(scope, token, options = {}) {
|
|
431
|
+
const perPage = options.perPage ?? 100;
|
|
432
|
+
const headers = {
|
|
433
|
+
Authorization: `token ${token}`,
|
|
434
|
+
Accept: "application/vnd.github.v3+json",
|
|
435
|
+
"User-Agent": "neuroverseos-radiant"
|
|
436
|
+
};
|
|
437
|
+
const repos = await fetchJSON(
|
|
438
|
+
`https://api.github.com/orgs/${scope.owner}/repos?sort=pushed&direction=desc&per_page=${perPage}`,
|
|
439
|
+
headers
|
|
440
|
+
);
|
|
441
|
+
const windowDays = options.windowDays ?? 14;
|
|
442
|
+
const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1e3);
|
|
443
|
+
const activeRepos = repos.filter(
|
|
444
|
+
(r) => new Date(r.pushed_at) >= since
|
|
445
|
+
);
|
|
446
|
+
const cappedRepos = activeRepos.slice(0, 10);
|
|
447
|
+
const allEvents = [];
|
|
448
|
+
const repoNames = [];
|
|
449
|
+
for (const repo of cappedRepos) {
|
|
450
|
+
const [owner, repoName] = repo.full_name.split("/");
|
|
451
|
+
try {
|
|
452
|
+
const repoScope = { type: "repo", owner, repo: repoName };
|
|
453
|
+
const events = await fetchGitHubActivity(repoScope, token, options);
|
|
454
|
+
allEvents.push(...events);
|
|
455
|
+
if (events.length > 0) repoNames.push(repo.full_name);
|
|
456
|
+
} catch {
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
allEvents.sort(
|
|
460
|
+
(a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp)
|
|
461
|
+
);
|
|
462
|
+
return { events: allEvents, repos: repoNames };
|
|
463
|
+
}
|
|
342
464
|
function createMockGitHubAdapter(fixedEvents) {
|
|
343
465
|
return async () => fixedEvents;
|
|
344
466
|
}
|
|
345
467
|
|
|
346
|
-
// src/radiant/adapters/
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
468
|
+
// src/radiant/adapters/discord.ts
|
|
469
|
+
async function fetchDiscordActivity(guildId, token, options = {}) {
|
|
470
|
+
const windowDays = options.windowDays ?? 14;
|
|
471
|
+
const perChannel = options.perChannel ?? 100;
|
|
472
|
+
const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1e3);
|
|
473
|
+
const headers = {
|
|
474
|
+
Authorization: `Bot ${token}`,
|
|
475
|
+
"Content-Type": "application/json"
|
|
476
|
+
};
|
|
477
|
+
const channels = await fetchJSON2(
|
|
478
|
+
`https://discord.com/api/v10/guilds/${guildId}/channels`,
|
|
479
|
+
headers
|
|
480
|
+
);
|
|
481
|
+
const textChannels = channels.filter((c) => {
|
|
482
|
+
if (c.type !== 0) return false;
|
|
483
|
+
if (options.channelIds && options.channelIds.length > 0) {
|
|
484
|
+
return options.channelIds.includes(c.id);
|
|
485
|
+
}
|
|
486
|
+
if (options.visibility === "public") {
|
|
487
|
+
return !c.name.startsWith("private-") && !c.nsfw;
|
|
488
|
+
}
|
|
489
|
+
return true;
|
|
490
|
+
});
|
|
491
|
+
const events = [];
|
|
492
|
+
let totalMessages = 0;
|
|
493
|
+
let helpRequests = 0;
|
|
494
|
+
let unresolvedThreads = 0;
|
|
495
|
+
let newcomerMessages = 0;
|
|
496
|
+
const responseTimes = [];
|
|
497
|
+
const participants = /* @__PURE__ */ new Set();
|
|
498
|
+
const knownParticipants = /* @__PURE__ */ new Set();
|
|
499
|
+
const topicCounts = /* @__PURE__ */ new Map();
|
|
500
|
+
for (const channel of textChannels.slice(0, 15)) {
|
|
501
|
+
try {
|
|
502
|
+
const messages = await fetchJSON2(
|
|
503
|
+
`https://discord.com/api/v10/channels/${channel.id}/messages?limit=${perChannel}`,
|
|
504
|
+
headers
|
|
505
|
+
);
|
|
506
|
+
const inWindow = messages.filter(
|
|
507
|
+
(m) => new Date(m.timestamp) >= since
|
|
508
|
+
);
|
|
509
|
+
totalMessages += inWindow.length;
|
|
510
|
+
const topic = channel.name.replace(/-/g, " ");
|
|
511
|
+
topicCounts.set(topic, (topicCounts.get(topic) ?? 0) + inWindow.length);
|
|
512
|
+
for (const msg of inWindow) {
|
|
513
|
+
const actor = mapDiscordUser(msg.author);
|
|
514
|
+
participants.add(actor.id);
|
|
515
|
+
const lowerContent = msg.content.toLowerCase();
|
|
516
|
+
if (lowerContent.includes("help") || lowerContent.includes("stuck") || lowerContent.includes("how do i") || lowerContent.includes("anyone know")) {
|
|
517
|
+
helpRequests++;
|
|
518
|
+
}
|
|
519
|
+
if (msg.referenced_message) {
|
|
520
|
+
const refTime = new Date(msg.referenced_message.timestamp).getTime();
|
|
521
|
+
const msgTime = new Date(msg.timestamp).getTime();
|
|
522
|
+
const diffMinutes = (msgTime - refTime) / 6e4;
|
|
523
|
+
if (diffMinutes > 0 && diffMinutes < 10080) {
|
|
524
|
+
responseTimes.push(diffMinutes);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
events.push({
|
|
528
|
+
id: `discord-${msg.id}`,
|
|
529
|
+
timestamp: msg.timestamp,
|
|
530
|
+
actor,
|
|
531
|
+
kind: "discord_message",
|
|
532
|
+
content: msg.content.slice(0, 500),
|
|
533
|
+
respondsTo: msg.referenced_message ? {
|
|
534
|
+
eventId: `discord-${msg.referenced_message.id}`,
|
|
535
|
+
actor: mapDiscordUser(msg.referenced_message.author)
|
|
536
|
+
} : void 0,
|
|
537
|
+
metadata: {
|
|
538
|
+
channel: channel.name,
|
|
539
|
+
guildId
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
} catch {
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
const avgResponseMinutes = responseTimes.length > 0 ? responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length : null;
|
|
547
|
+
const topTopics = [...topicCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([t]) => t);
|
|
548
|
+
const signals = {
|
|
549
|
+
totalMessages,
|
|
550
|
+
activeChannels: textChannels.length,
|
|
551
|
+
uniqueParticipants: participants.size,
|
|
552
|
+
avgResponseMinutes: avgResponseMinutes ? Math.round(avgResponseMinutes) : null,
|
|
553
|
+
helpRequests,
|
|
554
|
+
unresolvedThreads,
|
|
555
|
+
topTopics,
|
|
556
|
+
newcomerMessages
|
|
557
|
+
};
|
|
558
|
+
events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
559
|
+
return { events, signals };
|
|
560
|
+
}
|
|
561
|
+
function formatDiscordSignalsForPrompt(signals) {
|
|
562
|
+
if (signals.totalMessages === 0) return "";
|
|
563
|
+
const lines = [
|
|
564
|
+
"## Discord Activity (conversational behavior)",
|
|
565
|
+
"",
|
|
566
|
+
`${signals.totalMessages} messages across ${signals.activeChannels} channels.`,
|
|
567
|
+
`${signals.uniqueParticipants} unique participants.`
|
|
568
|
+
];
|
|
569
|
+
if (signals.avgResponseMinutes !== null) {
|
|
570
|
+
lines.push(`Average response time: ${signals.avgResponseMinutes} minutes.`);
|
|
571
|
+
}
|
|
572
|
+
if (signals.helpRequests > 0) {
|
|
573
|
+
lines.push(`${signals.helpRequests} help requests detected.`);
|
|
574
|
+
}
|
|
575
|
+
if (signals.unresolvedThreads > 0) {
|
|
576
|
+
lines.push(`${signals.unresolvedThreads} unresolved threads.`);
|
|
577
|
+
}
|
|
578
|
+
if (signals.topTopics.length > 0) {
|
|
579
|
+
lines.push(`Top discussion topics: ${signals.topTopics.join(", ")}.`);
|
|
580
|
+
}
|
|
581
|
+
lines.push("");
|
|
582
|
+
lines.push("Compare conversational activity against GitHub shipping activity.");
|
|
583
|
+
lines.push("Where debates happen in Discord but nothing ships in GitHub, name the gap.");
|
|
584
|
+
lines.push("Where work ships in GitHub but nobody discusses it in Discord, name the visibility gap.");
|
|
585
|
+
return lines.join("\n");
|
|
586
|
+
}
|
|
587
|
+
function mapDiscordUser(user) {
|
|
588
|
+
return {
|
|
589
|
+
id: user.username,
|
|
590
|
+
kind: user.bot ? "bot" : "human",
|
|
591
|
+
name: user.username
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
async function fetchJSON2(url, headers) {
|
|
595
|
+
const res = await fetch(url, { headers });
|
|
596
|
+
if (!res.ok) {
|
|
597
|
+
if (res.status === 404 || res.status === 403) return [];
|
|
598
|
+
throw new Error(`Discord API error ${res.status}: ${(await res.text()).slice(0, 300)}`);
|
|
599
|
+
}
|
|
600
|
+
return await res.json();
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// src/radiant/adapters/slack.ts
|
|
604
|
+
async function fetchSlackActivity(token, options = {}) {
|
|
605
|
+
const windowDays = options.windowDays ?? 14;
|
|
606
|
+
const perChannel = options.perChannel ?? 100;
|
|
607
|
+
const oldest = String(
|
|
608
|
+
Math.floor((Date.now() - windowDays * 24 * 60 * 60 * 1e3) / 1e3)
|
|
609
|
+
);
|
|
610
|
+
const headers = {
|
|
611
|
+
Authorization: `Bearer ${token}`,
|
|
612
|
+
"Content-Type": "application/json"
|
|
613
|
+
};
|
|
614
|
+
const channelsResponse = await fetchSlackAPI("https://slack.com/api/conversations.list?types=public_channel&limit=200", headers);
|
|
615
|
+
let channels = channelsResponse.channels ?? [];
|
|
616
|
+
if (options.channelIds && options.channelIds.length > 0) {
|
|
617
|
+
const ids = new Set(options.channelIds);
|
|
618
|
+
channels = channels.filter((c) => ids.has(c.id));
|
|
619
|
+
}
|
|
620
|
+
if (options.visibility === "public") {
|
|
621
|
+
channels = channels.filter((c) => !c.is_private && !c.is_archived);
|
|
622
|
+
}
|
|
623
|
+
const events = [];
|
|
624
|
+
let totalMessages = 0;
|
|
625
|
+
let reactionCount = 0;
|
|
626
|
+
let unresolvedThreads = 0;
|
|
627
|
+
const responseTimes = [];
|
|
628
|
+
const participants = /* @__PURE__ */ new Set();
|
|
629
|
+
const externalParticipants = /* @__PURE__ */ new Set();
|
|
630
|
+
const channelMessageCounts = /* @__PURE__ */ new Map();
|
|
631
|
+
for (const channel of channels.slice(0, 15)) {
|
|
632
|
+
try {
|
|
633
|
+
const historyResponse = await fetchSlackAPI(
|
|
634
|
+
`https://slack.com/api/conversations.history?channel=${channel.id}&limit=${perChannel}&oldest=${oldest}`,
|
|
635
|
+
headers
|
|
636
|
+
);
|
|
637
|
+
const messages = historyResponse.messages ?? [];
|
|
638
|
+
totalMessages += messages.length;
|
|
639
|
+
channelMessageCounts.set(channel.name, messages.length);
|
|
640
|
+
for (const msg of messages) {
|
|
641
|
+
if (msg.subtype === "channel_join" || msg.subtype === "channel_leave") continue;
|
|
642
|
+
const actor = mapSlackUser(msg.user ?? "unknown");
|
|
643
|
+
participants.add(actor.id);
|
|
644
|
+
if (msg.reactions) {
|
|
645
|
+
reactionCount += msg.reactions.reduce(
|
|
646
|
+
(sum, r) => sum + (r.count ?? 0),
|
|
647
|
+
0
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
if (msg.thread_ts && msg.thread_ts !== msg.ts) {
|
|
651
|
+
const parentTs = parseFloat(msg.thread_ts) * 1e3;
|
|
652
|
+
const msgTs = parseFloat(msg.ts) * 1e3;
|
|
653
|
+
const diffMinutes = (msgTs - parentTs) / 6e4;
|
|
654
|
+
if (diffMinutes > 0 && diffMinutes < 10080) {
|
|
655
|
+
responseTimes.push(diffMinutes);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
if (msg.thread_ts === msg.ts && (!msg.reply_count || msg.reply_count === 0)) {
|
|
659
|
+
if (msg.text && (msg.text.includes("?") || msg.text.toLowerCase().includes("help"))) {
|
|
660
|
+
unresolvedThreads++;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
const timestamp = new Date(parseFloat(msg.ts) * 1e3).toISOString();
|
|
664
|
+
events.push({
|
|
665
|
+
id: `slack-${msg.ts}`,
|
|
666
|
+
timestamp,
|
|
667
|
+
actor,
|
|
668
|
+
kind: "slack_message",
|
|
669
|
+
content: (msg.text ?? "").slice(0, 500),
|
|
670
|
+
respondsTo: msg.thread_ts && msg.thread_ts !== msg.ts ? {
|
|
671
|
+
eventId: `slack-${msg.thread_ts}`,
|
|
672
|
+
actor: { id: "thread-parent", kind: "unknown" }
|
|
673
|
+
} : void 0,
|
|
674
|
+
metadata: {
|
|
675
|
+
channel: channel.name,
|
|
676
|
+
isPrivate: channel.is_private,
|
|
677
|
+
hasReactions: (msg.reactions?.length ?? 0) > 0
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
} catch {
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
const avgResponseMinutes = responseTimes.length > 0 ? Math.round(responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length) : null;
|
|
685
|
+
const topChannels = [...channelMessageCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([name]) => name);
|
|
686
|
+
const signals = {
|
|
687
|
+
totalMessages,
|
|
688
|
+
activeChannels: channelMessageCounts.size,
|
|
689
|
+
uniqueParticipants: participants.size,
|
|
690
|
+
avgResponseMinutes,
|
|
691
|
+
externalParticipants: externalParticipants.size,
|
|
692
|
+
unresolvedThreads,
|
|
693
|
+
topChannels,
|
|
694
|
+
reactionCount
|
|
695
|
+
};
|
|
696
|
+
events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
697
|
+
return { events, signals };
|
|
698
|
+
}
|
|
699
|
+
function formatSlackSignalsForPrompt(signals) {
|
|
700
|
+
if (signals.totalMessages === 0) return "";
|
|
701
|
+
const lines = [
|
|
702
|
+
"## Slack Activity (external coordination)",
|
|
703
|
+
"",
|
|
704
|
+
`${signals.totalMessages} messages across ${signals.activeChannels} channels.`,
|
|
705
|
+
`${signals.uniqueParticipants} unique participants.`
|
|
706
|
+
];
|
|
707
|
+
if (signals.avgResponseMinutes !== null) {
|
|
708
|
+
lines.push(`Average thread response time: ${signals.avgResponseMinutes} minutes.`);
|
|
709
|
+
}
|
|
710
|
+
if (signals.unresolvedThreads > 0) {
|
|
711
|
+
lines.push(`${signals.unresolvedThreads} questions/threads with no reply.`);
|
|
712
|
+
}
|
|
713
|
+
if (signals.reactionCount > 0) {
|
|
714
|
+
lines.push(`${signals.reactionCount} reactions (engagement signal).`);
|
|
715
|
+
}
|
|
716
|
+
if (signals.topChannels.length > 0) {
|
|
717
|
+
lines.push(`Most active channels: ${signals.topChannels.join(", ")}.`);
|
|
718
|
+
}
|
|
719
|
+
lines.push("");
|
|
720
|
+
lines.push("Slack carries external coordination \u2014 partner and client communication.");
|
|
721
|
+
lines.push("Compare partner engagement against internal activity. Where partners are");
|
|
722
|
+
lines.push("active but internal follow-through is low, name the gap.");
|
|
723
|
+
return lines.join("\n");
|
|
724
|
+
}
|
|
725
|
+
function mapSlackUser(userId) {
|
|
726
|
+
return {
|
|
727
|
+
id: userId,
|
|
728
|
+
kind: "human",
|
|
729
|
+
name: userId
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
async function fetchSlackAPI(url, headers) {
|
|
733
|
+
const res = await fetch(url, { headers });
|
|
734
|
+
if (!res.ok) {
|
|
735
|
+
throw new Error(`Slack API error ${res.status}: ${(await res.text()).slice(0, 300)}`);
|
|
736
|
+
}
|
|
737
|
+
const data = await res.json();
|
|
738
|
+
if (!data.ok) {
|
|
739
|
+
throw new Error(`Slack API error: ${data.error ?? "unknown"}`);
|
|
740
|
+
}
|
|
741
|
+
return data;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// src/radiant/adapters/notion.ts
|
|
745
|
+
async function fetchNotionActivity(token, options = {}) {
|
|
746
|
+
const windowDays = options.windowDays ?? 14;
|
|
747
|
+
const maxPages = options.maxPages ?? 100;
|
|
748
|
+
const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1e3);
|
|
749
|
+
const headers = {
|
|
750
|
+
Authorization: `Bearer ${token}`,
|
|
751
|
+
"Notion-Version": "2022-06-28",
|
|
752
|
+
"Content-Type": "application/json"
|
|
753
|
+
};
|
|
754
|
+
const searchResponse = await fetchNotionAPI("https://api.notion.com/v1/search", headers, {
|
|
755
|
+
method: "POST",
|
|
756
|
+
body: JSON.stringify({
|
|
757
|
+
filter: { property: "object", value: "page" },
|
|
758
|
+
sort: { direction: "descending", timestamp: "last_edited_time" },
|
|
759
|
+
page_size: maxPages
|
|
760
|
+
})
|
|
761
|
+
});
|
|
762
|
+
const pages = searchResponse.results ?? [];
|
|
763
|
+
const events = [];
|
|
764
|
+
const editors = /* @__PURE__ */ new Set();
|
|
765
|
+
let pagesCreated = 0;
|
|
766
|
+
let pagesUpdated = 0;
|
|
767
|
+
let stalePages = 0;
|
|
768
|
+
const editAges = [];
|
|
769
|
+
const topPages = [];
|
|
770
|
+
const now = Date.now();
|
|
771
|
+
for (const page of pages) {
|
|
772
|
+
const lastEdited = new Date(page.last_edited_time);
|
|
773
|
+
const created = new Date(page.created_time);
|
|
774
|
+
const daysSinceEdit = (now - lastEdited.getTime()) / (24 * 60 * 60 * 1e3);
|
|
775
|
+
editAges.push(daysSinceEdit);
|
|
776
|
+
if (daysSinceEdit > 30) stalePages++;
|
|
777
|
+
const title = extractTitle(page);
|
|
778
|
+
const editorId = page.last_edited_by?.id ?? "unknown";
|
|
779
|
+
editors.add(editorId);
|
|
780
|
+
if (lastEdited >= since) {
|
|
781
|
+
const isNew = created >= since;
|
|
782
|
+
if (isNew) pagesCreated++;
|
|
783
|
+
else pagesUpdated++;
|
|
784
|
+
topPages.push({ title, editedAt: page.last_edited_time });
|
|
785
|
+
events.push({
|
|
786
|
+
id: `notion-${page.id}`,
|
|
787
|
+
timestamp: page.last_edited_time,
|
|
788
|
+
actor: {
|
|
789
|
+
id: editorId,
|
|
790
|
+
kind: "human",
|
|
791
|
+
name: editorId
|
|
792
|
+
},
|
|
793
|
+
kind: isNew ? "doc_created" : "doc_updated",
|
|
794
|
+
content: `${isNew ? "Created" : "Updated"}: ${title}`,
|
|
795
|
+
metadata: {
|
|
796
|
+
pageId: page.id,
|
|
797
|
+
url: page.url,
|
|
798
|
+
createdAt: page.created_time
|
|
799
|
+
}
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
const avgDaysSinceEdit = editAges.length > 0 ? Math.round(editAges.reduce((a, b) => a + b, 0) / editAges.length) : null;
|
|
804
|
+
const signals = {
|
|
805
|
+
pagesActive: pagesCreated + pagesUpdated,
|
|
806
|
+
pagesCreated,
|
|
807
|
+
pagesUpdated,
|
|
808
|
+
uniqueEditors: editors.size,
|
|
809
|
+
stalePages,
|
|
810
|
+
avgDaysSinceEdit,
|
|
811
|
+
topPages: topPages.slice(0, 5).map((p) => p.title)
|
|
812
|
+
};
|
|
813
|
+
events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
814
|
+
return { events, signals };
|
|
815
|
+
}
|
|
816
|
+
function formatNotionSignalsForPrompt(signals) {
|
|
817
|
+
if (signals.pagesActive === 0 && signals.stalePages === 0) return "";
|
|
818
|
+
const lines = [
|
|
819
|
+
"## Notion Activity (documentation behavior)",
|
|
820
|
+
"",
|
|
821
|
+
`${signals.pagesActive} pages active in window (${signals.pagesCreated} created, ${signals.pagesUpdated} updated).`,
|
|
822
|
+
`${signals.uniqueEditors} unique editors.`
|
|
823
|
+
];
|
|
824
|
+
if (signals.stalePages > 0) {
|
|
825
|
+
lines.push(`${signals.stalePages} pages haven't been touched in 30+ days.`);
|
|
826
|
+
}
|
|
827
|
+
if (signals.avgDaysSinceEdit !== null) {
|
|
828
|
+
lines.push(`Average page age since last edit: ${signals.avgDaysSinceEdit} days.`);
|
|
829
|
+
}
|
|
830
|
+
if (signals.topPages.length > 0) {
|
|
831
|
+
lines.push(`Recently active pages: ${signals.topPages.join(", ")}.`);
|
|
832
|
+
}
|
|
833
|
+
lines.push("");
|
|
834
|
+
lines.push("Documentation is how the team crystallizes and shares knowledge.");
|
|
835
|
+
lines.push("High code velocity + low documentation = building without recording.");
|
|
836
|
+
lines.push("High documentation + low code = planning without shipping.");
|
|
837
|
+
lines.push("Compare Notion activity against GitHub and Discord to find the balance.");
|
|
838
|
+
return lines.join("\n");
|
|
839
|
+
}
|
|
840
|
+
function extractTitle(page) {
|
|
841
|
+
for (const prop of Object.values(page.properties)) {
|
|
842
|
+
if (prop.type === "title" && prop.title) {
|
|
843
|
+
return prop.title.map((t) => t.plain_text).join("") || "Untitled";
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
return "Untitled";
|
|
847
|
+
}
|
|
848
|
+
async function fetchNotionAPI(url, headers, init) {
|
|
849
|
+
const res = await fetch(url, {
|
|
850
|
+
method: init?.method ?? "GET",
|
|
851
|
+
headers,
|
|
852
|
+
body: init?.body
|
|
853
|
+
});
|
|
854
|
+
if (!res.ok) {
|
|
855
|
+
throw new Error(
|
|
856
|
+
`Notion API error ${res.status}: ${(await res.text()).slice(0, 300)}`
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
return await res.json();
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// src/radiant/core/discovery.ts
|
|
863
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
|
|
864
|
+
import { join, resolve, basename } from "path";
|
|
865
|
+
import { homedir } from "os";
|
|
866
|
+
function discoverWorlds(options) {
|
|
867
|
+
const worlds = [];
|
|
868
|
+
const userDir = options?.userWorldsDir ?? join(homedir(), ".neuroverse", "worlds");
|
|
869
|
+
if (existsSync(userDir)) {
|
|
870
|
+
worlds.push(...loadWorldsFromDir(userDir, "user"));
|
|
871
|
+
}
|
|
872
|
+
if (options?.explicitWorldsDir) {
|
|
873
|
+
worlds.push(...loadWorldsFromDir(options.explicitWorldsDir, "repo"));
|
|
874
|
+
} else if (options?.repoDir) {
|
|
875
|
+
const repoPaths = [
|
|
876
|
+
join(options.repoDir, "worlds"),
|
|
877
|
+
join(options.repoDir, ".neuroverse", "worlds")
|
|
878
|
+
];
|
|
879
|
+
for (const p of repoPaths) {
|
|
880
|
+
if (existsSync(p)) {
|
|
881
|
+
worlds.push(...loadWorldsFromDir(p, "repo"));
|
|
882
|
+
break;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
const combinedContent = worlds.map((w) => `<!-- world: ${w.name} (${w.source}) -->
|
|
887
|
+
${w.content}`).join("\n\n---\n\n");
|
|
888
|
+
const summary = worlds.length === 0 ? "no worlds discovered" : worlds.map((w) => `${w.name} (${w.source})`).join(", ");
|
|
889
|
+
return { worlds, combinedContent, summary };
|
|
890
|
+
}
|
|
891
|
+
function formatActiveWorlds(stack) {
|
|
892
|
+
if (stack.worlds.length === 0) return "No worlds loaded.";
|
|
893
|
+
const lines = ["ACTIVE WORLDS", ""];
|
|
894
|
+
for (const w of stack.worlds) {
|
|
895
|
+
const sourceLabel = w.source === "base" ? "universal" : w.source === "user" ? "personal" : "this repo";
|
|
896
|
+
lines.push(` ${w.name} (${sourceLabel})`);
|
|
897
|
+
}
|
|
898
|
+
return lines.join("\n");
|
|
899
|
+
}
|
|
900
|
+
function loadWorldsFromDir(dirPath, source) {
|
|
350
901
|
const dir = resolve(dirPath);
|
|
902
|
+
if (!existsSync(dir)) return [];
|
|
903
|
+
const stat = statSync(dir);
|
|
904
|
+
if (stat.isFile() && dir.endsWith(".md")) {
|
|
905
|
+
try {
|
|
906
|
+
return [{
|
|
907
|
+
name: basename(dir).replace(/\.worldmodel\.md$/, "").replace(/\.nv-world\.md$/, ""),
|
|
908
|
+
source,
|
|
909
|
+
path: dir,
|
|
910
|
+
content: readFileSync(dir, "utf-8")
|
|
911
|
+
}];
|
|
912
|
+
} catch {
|
|
913
|
+
return [];
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
if (!stat.isDirectory()) return [];
|
|
917
|
+
const files = readdirSync(dir).filter(
|
|
918
|
+
(f) => f.endsWith(".worldmodel.md") || f.endsWith(".nv-world.md")
|
|
919
|
+
).sort();
|
|
920
|
+
return files.map((f) => {
|
|
921
|
+
const fullPath = join(dir, f);
|
|
922
|
+
return {
|
|
923
|
+
name: f.replace(/\.worldmodel\.md$/, "").replace(/\.nv-world\.md$/, ""),
|
|
924
|
+
source,
|
|
925
|
+
path: fullPath,
|
|
926
|
+
content: readFileSync(fullPath, "utf-8")
|
|
927
|
+
};
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// src/radiant/adapters/exocortex.ts
|
|
932
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
933
|
+
import { join as join2, resolve as resolve2 } from "path";
|
|
934
|
+
function readExocortex(dirPath, repoName) {
|
|
935
|
+
const dir = resolve2(dirPath);
|
|
351
936
|
let filesLoaded = 0;
|
|
352
937
|
function tryRead(...paths) {
|
|
353
938
|
for (const p of paths) {
|
|
354
|
-
const full =
|
|
355
|
-
if (
|
|
939
|
+
const full = join2(dir, p);
|
|
940
|
+
if (existsSync2(full)) {
|
|
356
941
|
try {
|
|
357
|
-
const content =
|
|
942
|
+
const content = readFileSync2(full, "utf-8").trim();
|
|
358
943
|
if (content) {
|
|
359
944
|
filesLoaded++;
|
|
360
945
|
return content;
|
|
@@ -365,16 +950,64 @@ function readExocortex(dirPath) {
|
|
|
365
950
|
}
|
|
366
951
|
return null;
|
|
367
952
|
}
|
|
953
|
+
const attention = tryRead("attention.md");
|
|
954
|
+
const goals = tryRead("goals.md");
|
|
955
|
+
const identity = tryRead("identity.md", "user.md");
|
|
956
|
+
const organization = tryRead("org/organization.md", "org/src/organization.md");
|
|
957
|
+
const methods = tryRead("org/methods.md", "org/src/methods.md");
|
|
958
|
+
let sprint = null;
|
|
959
|
+
let projectContext = null;
|
|
960
|
+
if (repoName) {
|
|
961
|
+
const projectPaths = [
|
|
962
|
+
repoName,
|
|
963
|
+
repoName.toLowerCase(),
|
|
964
|
+
repoName.replace(/-/g, "_")
|
|
965
|
+
];
|
|
966
|
+
for (const projectDir of projectPaths) {
|
|
967
|
+
const projectSprint = tryRead(
|
|
968
|
+
`${projectDir}/src/sprint.md`,
|
|
969
|
+
`${projectDir}/sprint.md`
|
|
970
|
+
);
|
|
971
|
+
if (projectSprint) {
|
|
972
|
+
sprint = projectSprint;
|
|
973
|
+
break;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
for (const projectDir of projectPaths) {
|
|
977
|
+
const roadmap = tryRead(
|
|
978
|
+
`${projectDir}/roadmap.md`,
|
|
979
|
+
`${projectDir}/src/roadmap.md`
|
|
980
|
+
);
|
|
981
|
+
if (roadmap) {
|
|
982
|
+
projectContext = roadmap;
|
|
983
|
+
break;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
if (!sprint) {
|
|
988
|
+
sprint = tryRead("sprint.md", "src/sprint.md");
|
|
989
|
+
}
|
|
368
990
|
const ctx = {
|
|
369
|
-
attention
|
|
370
|
-
goals
|
|
371
|
-
identity
|
|
372
|
-
sprint
|
|
373
|
-
organization
|
|
374
|
-
methods
|
|
991
|
+
attention,
|
|
992
|
+
goals,
|
|
993
|
+
identity,
|
|
994
|
+
sprint,
|
|
995
|
+
organization,
|
|
996
|
+
methods,
|
|
375
997
|
source: dir,
|
|
376
998
|
filesLoaded
|
|
377
999
|
};
|
|
1000
|
+
if (projectContext && ctx.sprint) {
|
|
1001
|
+
ctx.sprint = `${ctx.sprint}
|
|
1002
|
+
|
|
1003
|
+
---
|
|
1004
|
+
Project roadmap:
|
|
1005
|
+
${projectContext}`;
|
|
1006
|
+
} else if (projectContext) {
|
|
1007
|
+
ctx.sprint = `Project roadmap:
|
|
1008
|
+
${projectContext}`;
|
|
1009
|
+
ctx.filesLoaded++;
|
|
1010
|
+
}
|
|
378
1011
|
return ctx;
|
|
379
1012
|
}
|
|
380
1013
|
function formatExocortexForPrompt(ctx) {
|
|
@@ -415,6 +1048,46 @@ ${ctx.methods}`);
|
|
|
415
1048
|
}
|
|
416
1049
|
return sections.join("\n\n");
|
|
417
1050
|
}
|
|
1051
|
+
function readTeamExocortices(teamDir) {
|
|
1052
|
+
const dir = resolve2(teamDir);
|
|
1053
|
+
if (!existsSync2(dir)) return [];
|
|
1054
|
+
const entries = readdirSync2(dir);
|
|
1055
|
+
const results = [];
|
|
1056
|
+
for (const entry of entries) {
|
|
1057
|
+
const entryPath = join2(dir, entry);
|
|
1058
|
+
try {
|
|
1059
|
+
const stat = statSync2(entryPath);
|
|
1060
|
+
if (stat.isDirectory()) {
|
|
1061
|
+
const ctx = readExocortex(entryPath);
|
|
1062
|
+
if (ctx.filesLoaded > 0) {
|
|
1063
|
+
results.push({ name: entry, context: ctx });
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
} catch {
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
return results;
|
|
1070
|
+
}
|
|
1071
|
+
function formatTeamExocorticesForPrompt(team) {
|
|
1072
|
+
if (team.length === 0) return "";
|
|
1073
|
+
const sections = [
|
|
1074
|
+
"## Team Intent (cross-exocortex read)",
|
|
1075
|
+
"",
|
|
1076
|
+
`Reading ${team.length} team members' exocortices. Compare each person's`,
|
|
1077
|
+
"stated intent against the observed activity AND against each other.",
|
|
1078
|
+
"Surface: duplicate focus, missing coverage, silent pivots,",
|
|
1079
|
+
"and areas where no one is carrying the work.",
|
|
1080
|
+
""
|
|
1081
|
+
];
|
|
1082
|
+
for (const { name, context } of team) {
|
|
1083
|
+
sections.push(`### ${name}`);
|
|
1084
|
+
if (context.attention) sections.push(`**Attention:** ${context.attention.split("\n")[0]}`);
|
|
1085
|
+
if (context.goals) sections.push(`**Goals:** ${context.goals.split("\n").slice(0, 3).join("; ")}`);
|
|
1086
|
+
if (context.sprint) sections.push(`**Sprint:** ${context.sprint.split("\n").slice(0, 3).join("; ")}`);
|
|
1087
|
+
sections.push("");
|
|
1088
|
+
}
|
|
1089
|
+
return sections.join("\n");
|
|
1090
|
+
}
|
|
418
1091
|
function summarizeExocortex(ctx) {
|
|
419
1092
|
if (ctx.filesLoaded === 0) return "no exocortex files found";
|
|
420
1093
|
const loaded = [];
|
|
@@ -428,14 +1101,14 @@ function summarizeExocortex(ctx) {
|
|
|
428
1101
|
}
|
|
429
1102
|
|
|
430
1103
|
// src/radiant/memory/palace.ts
|
|
431
|
-
import { readFileSync as
|
|
432
|
-
import { join as
|
|
1104
|
+
import { readFileSync as readFileSync3, writeFileSync, mkdirSync, readdirSync as readdirSync3, existsSync as existsSync3 } from "fs";
|
|
1105
|
+
import { join as join3, resolve as resolve3 } from "path";
|
|
433
1106
|
function writeRead(exocortexDir, frontmatter, text) {
|
|
434
|
-
const dir =
|
|
1107
|
+
const dir = resolve3(exocortexDir, "radiant", "reads");
|
|
435
1108
|
mkdirSync(dir, { recursive: true });
|
|
436
1109
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
437
1110
|
const filename = `${date}.md`;
|
|
438
|
-
const filepath =
|
|
1111
|
+
const filepath = join3(dir, filename);
|
|
439
1112
|
const content = `${frontmatter}
|
|
440
1113
|
|
|
441
1114
|
${text}
|
|
@@ -444,9 +1117,9 @@ ${text}
|
|
|
444
1117
|
return filepath;
|
|
445
1118
|
}
|
|
446
1119
|
function updateKnowledge(exocortexDir, persistence, options) {
|
|
447
|
-
const dir =
|
|
1120
|
+
const dir = resolve3(exocortexDir, "radiant");
|
|
448
1121
|
mkdirSync(dir, { recursive: true });
|
|
449
|
-
const filepath =
|
|
1122
|
+
const filepath = join3(dir, "knowledge.md");
|
|
450
1123
|
const totalReads = options?.totalReads ?? 0;
|
|
451
1124
|
const existingUntriggered = loadUntriggeredCounts(filepath);
|
|
452
1125
|
const lines = [
|
|
@@ -538,9 +1211,9 @@ function updateKnowledge(exocortexDir, persistence, options) {
|
|
|
538
1211
|
}
|
|
539
1212
|
function loadUntriggeredCounts(filepath) {
|
|
540
1213
|
const counts = /* @__PURE__ */ new Map();
|
|
541
|
-
if (!
|
|
1214
|
+
if (!existsSync3(filepath)) return counts;
|
|
542
1215
|
try {
|
|
543
|
-
const content =
|
|
1216
|
+
const content = readFileSync3(filepath, "utf-8");
|
|
544
1217
|
const match = content.match(
|
|
545
1218
|
/<!-- untriggered_counts[\s\S]*?-->/
|
|
546
1219
|
);
|
|
@@ -558,13 +1231,13 @@ function loadUntriggeredCounts(filepath) {
|
|
|
558
1231
|
return counts;
|
|
559
1232
|
}
|
|
560
1233
|
function loadPriorReads(exocortexDir) {
|
|
561
|
-
const dir =
|
|
562
|
-
if (!
|
|
563
|
-
const files =
|
|
1234
|
+
const dir = resolve3(exocortexDir, "radiant", "reads");
|
|
1235
|
+
if (!existsSync3(dir)) return [];
|
|
1236
|
+
const files = readdirSync3(dir).filter((f) => f.endsWith(".md")).sort();
|
|
564
1237
|
const reads = [];
|
|
565
1238
|
for (const filename of files) {
|
|
566
|
-
const filepath =
|
|
567
|
-
const content =
|
|
1239
|
+
const filepath = join3(dir, filename);
|
|
1240
|
+
const content = readFileSync3(filepath, "utf-8");
|
|
568
1241
|
const date = filename.replace(".md", "");
|
|
569
1242
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
570
1243
|
const frontmatter = fmMatch ? fmMatch[1] : "";
|
|
@@ -941,15 +1614,17 @@ function buildInterpretationPrompt(input) {
|
|
|
941
1614
|
const eventSample = formatEventSample(input.events, 30);
|
|
942
1615
|
const canonicalList = (input.canonicalPatterns ?? []).length > 0 ? `Patterns the organization has already named (use these names if you see them):
|
|
943
1616
|
${input.canonicalPatterns.map((p) => `- ${p}`).join("\n")}` : "No patterns have been named yet. Everything you observe is new.";
|
|
1617
|
+
const compressedWorld = compressWorldmodel(input.worldmodelContent);
|
|
1618
|
+
const cl = compressLens(input.lens);
|
|
944
1619
|
const frame = input.lens.primary_frame;
|
|
945
1620
|
const evalQuestions = frame.evaluation_questions.map((q, i) => `${i + 1}. ${q}`).join("\n");
|
|
946
|
-
const forbiddenList =
|
|
947
|
-
const jargonTable =
|
|
1621
|
+
const forbiddenList = cl.forbiddenPhrases;
|
|
1622
|
+
const jargonTable = cl.jargonTranslations;
|
|
948
1623
|
return `You are a behavioral intelligence system reading team activity and producing a read for the reader who needs to act on it.
|
|
949
1624
|
|
|
950
|
-
##
|
|
1625
|
+
## Worldmodel (compressed)
|
|
951
1626
|
|
|
952
|
-
${
|
|
1627
|
+
${compressedWorld}
|
|
953
1628
|
|
|
954
1629
|
## What happened this window
|
|
955
1630
|
|
|
@@ -995,6 +1670,14 @@ ${jargonTable}
|
|
|
995
1670
|
|
|
996
1671
|
For example: don't say "update the worldmodel." Say "add a line to your strategy file."
|
|
997
1672
|
|
|
1673
|
+
## When the same invariant keeps firing
|
|
1674
|
+
|
|
1675
|
+
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:
|
|
1676
|
+
|
|
1677
|
+
"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."
|
|
1678
|
+
|
|
1679
|
+
Don't just say "invariant held." Say what it means that people keep pushing against the same wall.
|
|
1680
|
+
|
|
998
1681
|
## Health is a valid read
|
|
999
1682
|
|
|
1000
1683
|
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:
|
|
@@ -1132,30 +1815,16 @@ Window: last ${input.windowDays} days \xB7 ${input.eventCount} events
|
|
|
1132
1815
|
Lens: ${input.lens.name}`
|
|
1133
1816
|
);
|
|
1134
1817
|
if (input.patterns.length > 0) {
|
|
1135
|
-
const canonical = input.patterns.filter((p) => p.type === "canonical");
|
|
1136
|
-
const candidates = input.patterns.filter((p) => p.type === "candidate");
|
|
1137
1818
|
let emergentBlock = "EMERGENT\n";
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
emergentBlock += `
|
|
1819
|
+
for (const p of input.patterns) {
|
|
1820
|
+
emergentBlock += `
|
|
1141
1821
|
${p.name}
|
|
1142
1822
|
`;
|
|
1143
|
-
|
|
1823
|
+
emergentBlock += ` ${p.description}
|
|
1144
1824
|
`;
|
|
1145
|
-
|
|
1146
|
-
}
|
|
1147
|
-
if (candidates.length > 0) {
|
|
1148
|
-
emergentBlock += "\n Emergent (candidates \u2014 not yet in worldmodel)\n";
|
|
1149
|
-
for (const p of candidates) {
|
|
1150
|
-
emergentBlock += `
|
|
1151
|
-
${p.name} (candidate)
|
|
1152
|
-
`;
|
|
1153
|
-
emergentBlock += ` ${p.description}
|
|
1825
|
+
if (p.evidence.cited_invariant) {
|
|
1826
|
+
emergentBlock += ` Cited invariant: ${p.evidence.cited_invariant}
|
|
1154
1827
|
`;
|
|
1155
|
-
if (p.evidence.cited_invariant) {
|
|
1156
|
-
emergentBlock += ` Cited invariant: ${p.evidence.cited_invariant}
|
|
1157
|
-
`;
|
|
1158
|
-
}
|
|
1159
1828
|
}
|
|
1160
1829
|
}
|
|
1161
1830
|
sections.push(emergentBlock.trimEnd());
|
|
@@ -1367,21 +2036,80 @@ function serializeYAML(obj, indent = 0) {
|
|
|
1367
2036
|
async function emergent(input) {
|
|
1368
2037
|
const lens = resolveLens2(input.lensId);
|
|
1369
2038
|
const windowDays = input.windowDays ?? 14;
|
|
2039
|
+
let worldStack;
|
|
2040
|
+
let worldmodelContent = input.worldmodelContent;
|
|
2041
|
+
if (!worldmodelContent || worldmodelContent.trim() === "") {
|
|
2042
|
+
worldStack = discoverWorlds({ explicitWorldsDir: input.worldPath });
|
|
2043
|
+
worldmodelContent = worldStack.combinedContent;
|
|
2044
|
+
}
|
|
1370
2045
|
let statedIntent;
|
|
1371
2046
|
let exocortexContext;
|
|
1372
2047
|
let priorReadContext = "";
|
|
1373
2048
|
if (input.exocortexPath) {
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
2049
|
+
const repoName = input.scope.type === "repo" ? input.scope.repo : void 0;
|
|
2050
|
+
exocortexContext = readExocortex(input.exocortexPath, repoName);
|
|
2051
|
+
const compressed = compressExocortex(exocortexContext);
|
|
2052
|
+
if (compressed) {
|
|
2053
|
+
statedIntent = `## Stated Intent (from exocortex, compressed)
|
|
2054
|
+
|
|
2055
|
+
${compressed}
|
|
2056
|
+
|
|
2057
|
+
Compare stated intent against actual GitHub activity. Gaps = drift.`;
|
|
2058
|
+
}
|
|
1377
2059
|
const priorReads = loadPriorReads(input.exocortexPath);
|
|
1378
2060
|
if (priorReads.length > 0) {
|
|
1379
|
-
priorReadContext =
|
|
2061
|
+
priorReadContext = compressPriorReads(priorReads);
|
|
1380
2062
|
}
|
|
1381
2063
|
}
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
2064
|
+
let events;
|
|
2065
|
+
let orgRepos;
|
|
2066
|
+
if (input.scope.type === "org") {
|
|
2067
|
+
const orgResult = await fetchGitHubOrgActivity(
|
|
2068
|
+
input.scope,
|
|
2069
|
+
input.githubToken,
|
|
2070
|
+
{ windowDays }
|
|
2071
|
+
);
|
|
2072
|
+
events = orgResult.events;
|
|
2073
|
+
orgRepos = orgResult.repos;
|
|
2074
|
+
} else {
|
|
2075
|
+
events = await fetchGitHubActivity(input.scope, input.githubToken, {
|
|
2076
|
+
windowDays
|
|
2077
|
+
});
|
|
2078
|
+
}
|
|
2079
|
+
let adapterSignals = "";
|
|
2080
|
+
const activeAdapters = ["github"];
|
|
2081
|
+
const discordToken = process.env.DISCORD_TOKEN;
|
|
2082
|
+
const discordGuild = process.env.DISCORD_GUILD_ID;
|
|
2083
|
+
if (discordToken && discordGuild) {
|
|
2084
|
+
try {
|
|
2085
|
+
const discord = await fetchDiscordActivity(discordGuild, discordToken, { windowDays });
|
|
2086
|
+
events.push(...discord.events);
|
|
2087
|
+
adapterSignals += "\n\n" + formatDiscordSignalsForPrompt(discord.signals);
|
|
2088
|
+
activeAdapters.push("discord");
|
|
2089
|
+
} catch {
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
const slackToken = process.env.SLACK_TOKEN;
|
|
2093
|
+
if (slackToken) {
|
|
2094
|
+
try {
|
|
2095
|
+
const slack = await fetchSlackActivity(slackToken, { windowDays });
|
|
2096
|
+
events.push(...slack.events);
|
|
2097
|
+
adapterSignals += "\n\n" + formatSlackSignalsForPrompt(slack.signals);
|
|
2098
|
+
activeAdapters.push("slack");
|
|
2099
|
+
} catch {
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
const notionToken = process.env.NOTION_TOKEN;
|
|
2103
|
+
if (notionToken) {
|
|
2104
|
+
try {
|
|
2105
|
+
const notion = await fetchNotionActivity(notionToken, { windowDays });
|
|
2106
|
+
events.push(...notion.events);
|
|
2107
|
+
adapterSignals += "\n\n" + formatNotionSignalsForPrompt(notion.signals);
|
|
2108
|
+
activeAdapters.push("notion");
|
|
2109
|
+
} catch {
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
1385
2113
|
const classified = classifyEvents(events);
|
|
1386
2114
|
const signals = extractSignals(classified);
|
|
1387
2115
|
const scores = computeScores(signals, input.worldmodelContent !== "");
|
|
@@ -1392,7 +2120,7 @@ async function emergent(input) {
|
|
|
1392
2120
|
lens,
|
|
1393
2121
|
ai: input.ai,
|
|
1394
2122
|
canonicalPatterns: input.canonicalPatterns,
|
|
1395
|
-
statedIntent: statedIntent
|
|
2123
|
+
statedIntent: [statedIntent, adapterSignals, priorReadContext].filter(Boolean).join("\n\n") || void 0
|
|
1396
2124
|
});
|
|
1397
2125
|
const rewrittenPatterns = patterns.map((p) => lens.rewrite(p));
|
|
1398
2126
|
const allDescriptions = rewrittenPatterns.map((p) => p.description).join("\n");
|
|
@@ -1441,7 +2169,9 @@ async function emergent(input) {
|
|
|
1441
2169
|
voiceClean: voiceViolations.length === 0,
|
|
1442
2170
|
signals,
|
|
1443
2171
|
scores,
|
|
1444
|
-
eventCount: events.length
|
|
2172
|
+
eventCount: events.length,
|
|
2173
|
+
activeAdapters,
|
|
2174
|
+
worldStack
|
|
1445
2175
|
};
|
|
1446
2176
|
}
|
|
1447
2177
|
function computeScores(signals, worldmodelLoaded) {
|
|
@@ -1534,15 +2264,31 @@ function createMockAI(fixedResponse) {
|
|
|
1534
2264
|
}
|
|
1535
2265
|
|
|
1536
2266
|
export {
|
|
2267
|
+
compressWorldmodel,
|
|
2268
|
+
compressExocortex,
|
|
2269
|
+
compressLens,
|
|
2270
|
+
compressPriorReads,
|
|
1537
2271
|
composeSystemPrompt,
|
|
1538
2272
|
checkForbiddenPhrases,
|
|
1539
2273
|
think,
|
|
2274
|
+
parseScope,
|
|
1540
2275
|
parseRepoScope,
|
|
1541
2276
|
formatScope,
|
|
1542
2277
|
fetchGitHubActivity,
|
|
2278
|
+
fetchGitHubOrgActivity,
|
|
1543
2279
|
createMockGitHubAdapter,
|
|
2280
|
+
fetchDiscordActivity,
|
|
2281
|
+
formatDiscordSignalsForPrompt,
|
|
2282
|
+
fetchSlackActivity,
|
|
2283
|
+
formatSlackSignalsForPrompt,
|
|
2284
|
+
fetchNotionActivity,
|
|
2285
|
+
formatNotionSignalsForPrompt,
|
|
2286
|
+
discoverWorlds,
|
|
2287
|
+
formatActiveWorlds,
|
|
1544
2288
|
readExocortex,
|
|
1545
2289
|
formatExocortexForPrompt,
|
|
2290
|
+
readTeamExocortices,
|
|
2291
|
+
formatTeamExocorticesForPrompt,
|
|
1546
2292
|
summarizeExocortex,
|
|
1547
2293
|
writeRead,
|
|
1548
2294
|
updateKnowledge,
|