@neuroverseos/governance 0.8.0 → 0.9.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/AGENTS.md +8 -0
- package/README.md +180 -10
- package/dist/{chunk-MC6O5GV5.js → chunk-3ZWU7C43.js} +1056 -135
- package/dist/{chunk-VGFDMPVB.js → chunk-TCGGED4G.js} +283 -1
- package/dist/cli/neuroverse.cjs +2000 -460
- package/dist/cli/radiant.cjs +1452 -237
- package/dist/cli/radiant.js +62 -24
- package/dist/cli/worldmodel.cjs +1157 -823
- package/dist/cli/worldmodel.js +116 -1
- package/dist/{lenses-K5FVSALR.js → lenses-XDWK6ZKI.js} +5 -3
- package/dist/radiant/index.cjs +953 -130
- package/dist/radiant/index.d.cts +271 -10
- package/dist/radiant/index.d.ts +271 -10
- package/dist/radiant/index.js +41 -397
- package/dist/{server-DFNY5N5A.js → server-JKUBUK5H.js} +2 -2
- package/dist/worldmodel-create-5SWHVNMQ.js +195 -0
- package/examples/radiant-weekly-workflow.yml +81 -0
- package/package.json +2 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getLens
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-TCGGED4G.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}
|
|
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)
|
|
33
140
|
|
|
34
|
-
|
|
141
|
+
${compressedWorld}`,
|
|
142
|
+
// Section 2: Analytical frame (evaluation questions + rubric)
|
|
143
|
+
`## How to Think
|
|
35
144
|
|
|
36
|
-
${
|
|
145
|
+
${cl.scoringRubric}
|
|
37
146
|
|
|
38
|
-
|
|
147
|
+
Questions:
|
|
148
|
+
${cl.evaluationQuestions}
|
|
39
149
|
|
|
40
|
-
${overlapsBlock}
|
|
150
|
+
Overlaps: ${overlapsBlock}
|
|
151
|
+
Center: ${lens.primary_frame.center_identity}
|
|
41
152
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
);
|
|
46
|
-
const vocabPreferred = Object.entries(lens.vocabulary.preferred).map(([generic, native]) => `- "${generic}" \u2192 **${native}**`).join("\n");
|
|
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
|
|
@@ -390,18 +465,758 @@ function createMockGitHubAdapter(fixedEvents) {
|
|
|
390
465
|
return async () => fixedEvents;
|
|
391
466
|
}
|
|
392
467
|
|
|
393
|
-
// src/radiant/adapters/
|
|
394
|
-
|
|
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/git-remote.ts
|
|
863
|
+
import { existsSync, readFileSync, statSync } from "fs";
|
|
395
864
|
import { join, resolve } from "path";
|
|
396
|
-
function
|
|
397
|
-
const
|
|
865
|
+
function resolveGitConfigPath(repoDir) {
|
|
866
|
+
const dotGit = join(repoDir, ".git");
|
|
867
|
+
if (!existsSync(dotGit)) return null;
|
|
868
|
+
try {
|
|
869
|
+
const stat = statSync(dotGit);
|
|
870
|
+
if (stat.isDirectory()) {
|
|
871
|
+
return join(dotGit, "config");
|
|
872
|
+
}
|
|
873
|
+
if (stat.isFile()) {
|
|
874
|
+
const content = readFileSync(dotGit, "utf-8");
|
|
875
|
+
const match = /^gitdir:\s*(.+)$/m.exec(content);
|
|
876
|
+
if (!match) return null;
|
|
877
|
+
const gitDir = resolve(repoDir, match[1].trim());
|
|
878
|
+
const configPath = join(gitDir, "config");
|
|
879
|
+
return existsSync(configPath) ? configPath : null;
|
|
880
|
+
}
|
|
881
|
+
} catch {
|
|
882
|
+
return null;
|
|
883
|
+
}
|
|
884
|
+
return null;
|
|
885
|
+
}
|
|
886
|
+
function readOriginRemote(repoDir) {
|
|
887
|
+
const configPath = resolveGitConfigPath(repoDir);
|
|
888
|
+
if (!configPath) return null;
|
|
889
|
+
try {
|
|
890
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
891
|
+
const sectionRe = /\[remote "origin"\]\s*\n((?:(?!\[)[^\n]*\n?)*)/;
|
|
892
|
+
const section = sectionRe.exec(raw);
|
|
893
|
+
if (!section) return null;
|
|
894
|
+
const urlRe = /^\s*url\s*=\s*(.+?)\s*$/m;
|
|
895
|
+
const url = urlRe.exec(section[1]);
|
|
896
|
+
return url ? url[1] : null;
|
|
897
|
+
} catch {
|
|
898
|
+
return null;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
function parseRemoteUrl(url) {
|
|
902
|
+
const trimmed = url.trim();
|
|
903
|
+
if (!trimmed) return null;
|
|
904
|
+
const ssh = /^git@([^:]+):([^/]+)\/(.+?)(?:\.git)?\/?$/.exec(trimmed);
|
|
905
|
+
if (ssh) return { host: ssh[1], owner: ssh[2], repo: ssh[3] };
|
|
906
|
+
const sshProto = /^ssh:\/\/git@([^/]+)\/([^/]+)\/(.+?)(?:\.git)?\/?$/.exec(trimmed);
|
|
907
|
+
if (sshProto) return { host: sshProto[1], owner: sshProto[2], repo: sshProto[3] };
|
|
908
|
+
const https = /^https?:\/\/(?:[^@/]+@)?([^/]+)\/([^/]+)\/(.+?)(?:\.git)?\/?$/.exec(trimmed);
|
|
909
|
+
if (https) return { host: https[1], owner: https[2], repo: https[3] };
|
|
910
|
+
return null;
|
|
911
|
+
}
|
|
912
|
+
function getRepoOrigin(repoDir) {
|
|
913
|
+
const url = readOriginRemote(repoDir);
|
|
914
|
+
if (!url) return null;
|
|
915
|
+
return parseRemoteUrl(url);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// src/radiant/core/extends.ts
|
|
919
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, rmSync, statSync as statSync2, writeFileSync } from "fs";
|
|
920
|
+
import { join as join2, resolve as resolve2, isAbsolute } from "path";
|
|
921
|
+
import { homedir } from "os";
|
|
922
|
+
import { createHash } from "crypto";
|
|
923
|
+
import { execFileSync } from "child_process";
|
|
924
|
+
function loadExtendsConfig(repoDir) {
|
|
925
|
+
const configPath = join2(repoDir, ".neuroverse", "config.json");
|
|
926
|
+
if (!existsSync2(configPath)) return null;
|
|
927
|
+
try {
|
|
928
|
+
const raw = readFileSync2(configPath, "utf-8");
|
|
929
|
+
const parsed = JSON.parse(raw);
|
|
930
|
+
return parsed;
|
|
931
|
+
} catch {
|
|
932
|
+
return null;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
function parseExtendsSpec(raw) {
|
|
936
|
+
const trimmed = raw.trim();
|
|
937
|
+
if (!trimmed) return null;
|
|
938
|
+
if (trimmed.startsWith("github:")) {
|
|
939
|
+
const rest = trimmed.slice("github:".length);
|
|
940
|
+
const match = /^([^/]+)\/([^@:]+)(?:@([^:]+))?(?::(.+))?$/.exec(rest);
|
|
941
|
+
if (!match) return null;
|
|
942
|
+
return {
|
|
943
|
+
raw: trimmed,
|
|
944
|
+
kind: "github",
|
|
945
|
+
owner: match[1],
|
|
946
|
+
repo: match[2],
|
|
947
|
+
ref: match[3] ?? "HEAD",
|
|
948
|
+
subpath: match[4] ?? ""
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
if (trimmed.startsWith("./") || trimmed.startsWith("../") || isAbsolute(trimmed)) {
|
|
952
|
+
return { raw: trimmed, kind: "local", path: trimmed };
|
|
953
|
+
}
|
|
954
|
+
return null;
|
|
955
|
+
}
|
|
956
|
+
var DEFAULT_TTL_MS = 60 * 60 * 1e3;
|
|
957
|
+
function getCacheDir(spec, baseCacheDir) {
|
|
958
|
+
const root = baseCacheDir ?? join2(homedir(), ".neuroverse", "cache", "extends");
|
|
959
|
+
const key = createHash("sha256").update(spec.raw).digest("hex").slice(0, 16);
|
|
960
|
+
return join2(root, key);
|
|
961
|
+
}
|
|
962
|
+
function isCacheFresh(cacheDir, ttlMs) {
|
|
963
|
+
const stampPath = join2(cacheDir, ".neuroverse-fetched");
|
|
964
|
+
if (!existsSync2(stampPath)) return false;
|
|
965
|
+
try {
|
|
966
|
+
const stamp = statSync2(stampPath);
|
|
967
|
+
return Date.now() - stamp.mtimeMs < ttlMs;
|
|
968
|
+
} catch {
|
|
969
|
+
return false;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
function markCacheFresh(cacheDir) {
|
|
973
|
+
const stampPath = join2(cacheDir, ".neuroverse-fetched");
|
|
974
|
+
try {
|
|
975
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
976
|
+
writeFileSync(stampPath, (/* @__PURE__ */ new Date()).toISOString());
|
|
977
|
+
} catch {
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
var defaultGitFetcher = (spec, destDir) => {
|
|
981
|
+
if (spec.kind !== "github") return;
|
|
982
|
+
const url = `https://github.com/${spec.owner}/${spec.repo}.git`;
|
|
983
|
+
const parent = resolve2(destDir, "..");
|
|
984
|
+
mkdirSync(parent, { recursive: true });
|
|
985
|
+
if (existsSync2(destDir)) {
|
|
986
|
+
rmSync(destDir, { recursive: true, force: true });
|
|
987
|
+
}
|
|
988
|
+
const args = ["clone", "--depth", "1", "--filter=blob:none"];
|
|
989
|
+
if (spec.ref && spec.ref !== "HEAD") {
|
|
990
|
+
args.push("--branch", spec.ref);
|
|
991
|
+
}
|
|
992
|
+
args.push(url, destDir);
|
|
993
|
+
execFileSync("git", args, { stdio: "pipe" });
|
|
994
|
+
};
|
|
995
|
+
function resolveExtendsSpec(spec, repoDir, options) {
|
|
996
|
+
if (spec.kind === "local") {
|
|
997
|
+
const full = isAbsolute(spec.path) ? spec.path : resolve2(repoDir, spec.path);
|
|
998
|
+
if (!existsSync2(full)) {
|
|
999
|
+
return { spec, dir: null, warning: `local extends path not found: ${full}` };
|
|
1000
|
+
}
|
|
1001
|
+
return { spec, dir: full };
|
|
1002
|
+
}
|
|
1003
|
+
const cacheRoot = options?.cacheDir;
|
|
1004
|
+
const ttl = options?.ttlMs ?? DEFAULT_TTL_MS;
|
|
1005
|
+
const cacheDir = getCacheDir(spec, cacheRoot);
|
|
1006
|
+
const fresh = isCacheFresh(cacheDir, ttl);
|
|
1007
|
+
const needsFetch = options?.forceRefresh || !fresh || !existsSync2(cacheDir);
|
|
1008
|
+
if (needsFetch && options?.noFetch) {
|
|
1009
|
+
if (existsSync2(cacheDir) && existsSync2(join2(cacheDir, ".neuroverse-fetched"))) {
|
|
1010
|
+
return resolveSubpath(spec, cacheDir);
|
|
1011
|
+
}
|
|
1012
|
+
return options?.silentOnMissing ? { spec, dir: null } : { spec, dir: null, warning: `NEUROVERSE_NO_FETCH set and no cache for ${spec.raw}` };
|
|
1013
|
+
}
|
|
1014
|
+
if (needsFetch) {
|
|
1015
|
+
const fetcher = options?.fetcher ?? defaultGitFetcher;
|
|
1016
|
+
try {
|
|
1017
|
+
fetcher(spec, cacheDir);
|
|
1018
|
+
markCacheFresh(cacheDir);
|
|
1019
|
+
} catch (err) {
|
|
1020
|
+
if (existsSync2(cacheDir) && existsSync2(join2(cacheDir, ".neuroverse-fetched"))) {
|
|
1021
|
+
const result = resolveSubpath(spec, cacheDir);
|
|
1022
|
+
return options?.silentOnMissing ? result : { ...result, warning: `fetch failed for ${spec.raw}, using stale cache: ${err.message}` };
|
|
1023
|
+
}
|
|
1024
|
+
return options?.silentOnMissing ? { spec, dir: null } : { spec, dir: null, warning: `fetch failed for ${spec.raw}: ${err.message}` };
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
return resolveSubpath(spec, cacheDir);
|
|
1028
|
+
}
|
|
1029
|
+
function resolveSubpath(spec, cacheDir) {
|
|
1030
|
+
const target = spec.subpath ? join2(cacheDir, spec.subpath) : cacheDir;
|
|
1031
|
+
if (!existsSync2(target)) {
|
|
1032
|
+
return { spec, dir: null, warning: `subpath not found in ${spec.raw}: ${spec.subpath}` };
|
|
1033
|
+
}
|
|
1034
|
+
if (!spec.subpath) {
|
|
1035
|
+
const candidates = [
|
|
1036
|
+
join2(target, "worlds"),
|
|
1037
|
+
join2(target, ".neuroverse", "worlds")
|
|
1038
|
+
];
|
|
1039
|
+
for (const c of candidates) {
|
|
1040
|
+
if (existsSync2(c)) return { spec, dir: c };
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
return { spec, dir: target };
|
|
1044
|
+
}
|
|
1045
|
+
function detectOrgExtendsSpec(repoDir) {
|
|
1046
|
+
const origin = getRepoOrigin(repoDir);
|
|
1047
|
+
if (!origin) return null;
|
|
1048
|
+
if (origin.host !== "github.com") return null;
|
|
1049
|
+
if (origin.repo === "worlds") return null;
|
|
1050
|
+
return {
|
|
1051
|
+
raw: `github:${origin.owner}/worlds`,
|
|
1052
|
+
kind: "github",
|
|
1053
|
+
owner: origin.owner,
|
|
1054
|
+
repo: "worlds",
|
|
1055
|
+
ref: "HEAD",
|
|
1056
|
+
subpath: ""
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
function resolveAllExtends(repoDir, options) {
|
|
1060
|
+
const config = options?.config !== void 0 ? options.config : loadExtendsConfig(repoDir);
|
|
1061
|
+
if (!config?.extends || config.extends.length === 0) return [];
|
|
1062
|
+
const results = [];
|
|
1063
|
+
for (const raw of config.extends) {
|
|
1064
|
+
const spec = parseExtendsSpec(raw);
|
|
1065
|
+
if (!spec) {
|
|
1066
|
+
results.push({
|
|
1067
|
+
spec: { raw, kind: "local" },
|
|
1068
|
+
dir: null,
|
|
1069
|
+
warning: `unparseable extends spec: ${raw}`
|
|
1070
|
+
});
|
|
1071
|
+
continue;
|
|
1072
|
+
}
|
|
1073
|
+
results.push(resolveExtendsSpec(spec, repoDir, options));
|
|
1074
|
+
}
|
|
1075
|
+
return results;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
// src/radiant/core/discovery.ts
|
|
1079
|
+
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
|
|
1080
|
+
import { join as join3, resolve as resolve3, basename } from "path";
|
|
1081
|
+
import { homedir as homedir2 } from "os";
|
|
1082
|
+
function discoverWorlds(options) {
|
|
1083
|
+
const worlds = [];
|
|
1084
|
+
const warnings = [];
|
|
1085
|
+
const forceRefresh = process.env.NEUROVERSE_REFRESH === "1";
|
|
1086
|
+
const noFetch = process.env.NEUROVERSE_NO_FETCH === "1";
|
|
1087
|
+
const noOrg = options?.disableOrg || process.env.NEUROVERSE_NO_ORG === "1";
|
|
1088
|
+
const userDir = options?.userWorldsDir ?? join3(homedir2(), ".neuroverse", "worlds");
|
|
1089
|
+
if (existsSync3(userDir)) {
|
|
1090
|
+
worlds.push(...loadWorldsFromDir(userDir, "user"));
|
|
1091
|
+
}
|
|
1092
|
+
if (!noOrg && !options?.explicitWorldsDir) {
|
|
1093
|
+
const specs = [];
|
|
1094
|
+
if (options?.repoDir) {
|
|
1095
|
+
const fromGit = detectOrgExtendsSpec(options.repoDir);
|
|
1096
|
+
if (fromGit) specs.push(fromGit);
|
|
1097
|
+
}
|
|
1098
|
+
if (options?.scopeOwner) {
|
|
1099
|
+
const already = specs.some(
|
|
1100
|
+
(s) => s.owner?.toLowerCase() === options.scopeOwner.toLowerCase()
|
|
1101
|
+
);
|
|
1102
|
+
if (!already) {
|
|
1103
|
+
specs.push({
|
|
1104
|
+
raw: `github:${options.scopeOwner}/worlds`,
|
|
1105
|
+
kind: "github",
|
|
1106
|
+
owner: options.scopeOwner,
|
|
1107
|
+
repo: "worlds"
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
const baseDir = options?.repoDir ?? process.cwd();
|
|
1112
|
+
for (const spec of specs) {
|
|
1113
|
+
const result = resolveExtendsSpec(spec, baseDir, {
|
|
1114
|
+
cacheDir: options?.extendsCacheDir,
|
|
1115
|
+
fetcher: options?.extendsFetcher,
|
|
1116
|
+
ttlMs: options?.extendsTtlMs,
|
|
1117
|
+
forceRefresh,
|
|
1118
|
+
noFetch,
|
|
1119
|
+
silentOnMissing: true
|
|
1120
|
+
});
|
|
1121
|
+
worlds.push(...loadExtendsWorlds(result, "org"));
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
if (options?.repoDir && !options.disableExtends && !options.explicitWorldsDir) {
|
|
1125
|
+
const results = resolveAllExtends(options.repoDir, {
|
|
1126
|
+
cacheDir: options.extendsCacheDir,
|
|
1127
|
+
fetcher: options.extendsFetcher,
|
|
1128
|
+
ttlMs: options.extendsTtlMs,
|
|
1129
|
+
forceRefresh,
|
|
1130
|
+
noFetch
|
|
1131
|
+
});
|
|
1132
|
+
for (const result of results) {
|
|
1133
|
+
worlds.push(...loadExtendsWorlds(result, "extends"));
|
|
1134
|
+
if (result.warning) warnings.push(result.warning);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
if (options?.explicitWorldsDir) {
|
|
1138
|
+
worlds.push(...loadWorldsFromDir(options.explicitWorldsDir, "repo"));
|
|
1139
|
+
} else if (options?.repoDir) {
|
|
1140
|
+
const repoPaths = [
|
|
1141
|
+
join3(options.repoDir, "worlds"),
|
|
1142
|
+
join3(options.repoDir, ".neuroverse", "worlds")
|
|
1143
|
+
];
|
|
1144
|
+
for (const p of repoPaths) {
|
|
1145
|
+
if (existsSync3(p)) {
|
|
1146
|
+
worlds.push(...loadWorldsFromDir(p, "repo"));
|
|
1147
|
+
break;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
const combinedContent = worlds.map((w) => {
|
|
1152
|
+
const tag = w.extendsFrom ? `<!-- world: ${w.name} (${w.source} ${w.extendsFrom}) -->` : `<!-- world: ${w.name} (${w.source}) -->`;
|
|
1153
|
+
return `${tag}
|
|
1154
|
+
${w.content}`;
|
|
1155
|
+
}).join("\n\n---\n\n");
|
|
1156
|
+
const summary = worlds.length === 0 ? "no worlds discovered" : worlds.map((w) => `${w.name} (${w.source})`).join(", ");
|
|
1157
|
+
return { worlds, combinedContent, summary, warnings };
|
|
1158
|
+
}
|
|
1159
|
+
function formatActiveWorlds(stack) {
|
|
1160
|
+
if (stack.worlds.length === 0) return "No worlds loaded.";
|
|
1161
|
+
const lines = ["ACTIVE WORLDS", ""];
|
|
1162
|
+
for (const w of stack.worlds) {
|
|
1163
|
+
const sourceLabel = w.source === "base" ? "universal" : w.source === "user" ? "personal" : w.source === "org" ? `org (${w.extendsFrom ?? "auto"})` : w.source === "extends" ? `shared (${w.extendsFrom ?? "extends"})` : "this repo";
|
|
1164
|
+
lines.push(` ${w.name} (${sourceLabel})`);
|
|
1165
|
+
}
|
|
1166
|
+
if (stack.warnings.length > 0) {
|
|
1167
|
+
lines.push("", "WARNINGS");
|
|
1168
|
+
for (const w of stack.warnings) lines.push(` ${w}`);
|
|
1169
|
+
}
|
|
1170
|
+
return lines.join("\n");
|
|
1171
|
+
}
|
|
1172
|
+
function loadExtendsWorlds(result, source) {
|
|
1173
|
+
if (!result.dir) return [];
|
|
1174
|
+
const loaded = loadWorldsFromDir(result.dir, source);
|
|
1175
|
+
return loaded.map((w) => ({ ...w, extendsFrom: result.spec.raw }));
|
|
1176
|
+
}
|
|
1177
|
+
function loadWorldsFromDir(dirPath, source) {
|
|
1178
|
+
const dir = resolve3(dirPath);
|
|
1179
|
+
if (!existsSync3(dir)) return [];
|
|
1180
|
+
const stat = statSync3(dir);
|
|
1181
|
+
if (stat.isFile() && dir.endsWith(".md")) {
|
|
1182
|
+
try {
|
|
1183
|
+
return [{
|
|
1184
|
+
name: basename(dir).replace(/\.worldmodel\.md$/, "").replace(/\.nv-world\.md$/, ""),
|
|
1185
|
+
source,
|
|
1186
|
+
path: dir,
|
|
1187
|
+
content: readFileSync3(dir, "utf-8")
|
|
1188
|
+
}];
|
|
1189
|
+
} catch {
|
|
1190
|
+
return [];
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
if (!stat.isDirectory()) return [];
|
|
1194
|
+
const files = readdirSync(dir).filter(
|
|
1195
|
+
(f) => f.endsWith(".worldmodel.md") || f.endsWith(".nv-world.md")
|
|
1196
|
+
).sort();
|
|
1197
|
+
return files.map((f) => {
|
|
1198
|
+
const fullPath = join3(dir, f);
|
|
1199
|
+
return {
|
|
1200
|
+
name: f.replace(/\.worldmodel\.md$/, "").replace(/\.nv-world\.md$/, ""),
|
|
1201
|
+
source,
|
|
1202
|
+
path: fullPath,
|
|
1203
|
+
content: readFileSync3(fullPath, "utf-8")
|
|
1204
|
+
};
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// src/radiant/adapters/exocortex.ts
|
|
1209
|
+
import { readFileSync as readFileSync4, existsSync as existsSync4, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
|
|
1210
|
+
import { join as join4, resolve as resolve4 } from "path";
|
|
1211
|
+
function readExocortex(dirPath, repoName) {
|
|
1212
|
+
const dir = resolve4(dirPath);
|
|
398
1213
|
let filesLoaded = 0;
|
|
399
1214
|
function tryRead(...paths) {
|
|
400
1215
|
for (const p of paths) {
|
|
401
|
-
const full =
|
|
402
|
-
if (
|
|
1216
|
+
const full = join4(dir, p);
|
|
1217
|
+
if (existsSync4(full)) {
|
|
403
1218
|
try {
|
|
404
|
-
const content =
|
|
1219
|
+
const content = readFileSync4(full, "utf-8").trim();
|
|
405
1220
|
if (content) {
|
|
406
1221
|
filesLoaded++;
|
|
407
1222
|
return content;
|
|
@@ -412,16 +1227,64 @@ function readExocortex(dirPath) {
|
|
|
412
1227
|
}
|
|
413
1228
|
return null;
|
|
414
1229
|
}
|
|
1230
|
+
const attention = tryRead("attention.md");
|
|
1231
|
+
const goals = tryRead("goals.md");
|
|
1232
|
+
const identity = tryRead("identity.md", "user.md");
|
|
1233
|
+
const organization = tryRead("org/organization.md", "org/src/organization.md");
|
|
1234
|
+
const methods = tryRead("org/methods.md", "org/src/methods.md");
|
|
1235
|
+
let sprint = null;
|
|
1236
|
+
let projectContext = null;
|
|
1237
|
+
if (repoName) {
|
|
1238
|
+
const projectPaths = [
|
|
1239
|
+
repoName,
|
|
1240
|
+
repoName.toLowerCase(),
|
|
1241
|
+
repoName.replace(/-/g, "_")
|
|
1242
|
+
];
|
|
1243
|
+
for (const projectDir of projectPaths) {
|
|
1244
|
+
const projectSprint = tryRead(
|
|
1245
|
+
`${projectDir}/src/sprint.md`,
|
|
1246
|
+
`${projectDir}/sprint.md`
|
|
1247
|
+
);
|
|
1248
|
+
if (projectSprint) {
|
|
1249
|
+
sprint = projectSprint;
|
|
1250
|
+
break;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
for (const projectDir of projectPaths) {
|
|
1254
|
+
const roadmap = tryRead(
|
|
1255
|
+
`${projectDir}/roadmap.md`,
|
|
1256
|
+
`${projectDir}/src/roadmap.md`
|
|
1257
|
+
);
|
|
1258
|
+
if (roadmap) {
|
|
1259
|
+
projectContext = roadmap;
|
|
1260
|
+
break;
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
if (!sprint) {
|
|
1265
|
+
sprint = tryRead("sprint.md", "src/sprint.md");
|
|
1266
|
+
}
|
|
415
1267
|
const ctx = {
|
|
416
|
-
attention
|
|
417
|
-
goals
|
|
418
|
-
identity
|
|
419
|
-
sprint
|
|
420
|
-
organization
|
|
421
|
-
methods
|
|
1268
|
+
attention,
|
|
1269
|
+
goals,
|
|
1270
|
+
identity,
|
|
1271
|
+
sprint,
|
|
1272
|
+
organization,
|
|
1273
|
+
methods,
|
|
422
1274
|
source: dir,
|
|
423
1275
|
filesLoaded
|
|
424
1276
|
};
|
|
1277
|
+
if (projectContext && ctx.sprint) {
|
|
1278
|
+
ctx.sprint = `${ctx.sprint}
|
|
1279
|
+
|
|
1280
|
+
---
|
|
1281
|
+
Project roadmap:
|
|
1282
|
+
${projectContext}`;
|
|
1283
|
+
} else if (projectContext) {
|
|
1284
|
+
ctx.sprint = `Project roadmap:
|
|
1285
|
+
${projectContext}`;
|
|
1286
|
+
ctx.filesLoaded++;
|
|
1287
|
+
}
|
|
425
1288
|
return ctx;
|
|
426
1289
|
}
|
|
427
1290
|
function formatExocortexForPrompt(ctx) {
|
|
@@ -463,14 +1326,14 @@ ${ctx.methods}`);
|
|
|
463
1326
|
return sections.join("\n\n");
|
|
464
1327
|
}
|
|
465
1328
|
function readTeamExocortices(teamDir) {
|
|
466
|
-
const dir =
|
|
467
|
-
if (!
|
|
468
|
-
const entries =
|
|
1329
|
+
const dir = resolve4(teamDir);
|
|
1330
|
+
if (!existsSync4(dir)) return [];
|
|
1331
|
+
const entries = readdirSync2(dir);
|
|
469
1332
|
const results = [];
|
|
470
1333
|
for (const entry of entries) {
|
|
471
|
-
const entryPath =
|
|
1334
|
+
const entryPath = join4(dir, entry);
|
|
472
1335
|
try {
|
|
473
|
-
const stat =
|
|
1336
|
+
const stat = statSync4(entryPath);
|
|
474
1337
|
if (stat.isDirectory()) {
|
|
475
1338
|
const ctx = readExocortex(entryPath);
|
|
476
1339
|
if (ctx.filesLoaded > 0) {
|
|
@@ -515,25 +1378,25 @@ function summarizeExocortex(ctx) {
|
|
|
515
1378
|
}
|
|
516
1379
|
|
|
517
1380
|
// src/radiant/memory/palace.ts
|
|
518
|
-
import { readFileSync as
|
|
519
|
-
import { join as
|
|
1381
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, readdirSync as readdirSync3, existsSync as existsSync5 } from "fs";
|
|
1382
|
+
import { join as join5, resolve as resolve5 } from "path";
|
|
520
1383
|
function writeRead(exocortexDir, frontmatter, text) {
|
|
521
|
-
const dir =
|
|
522
|
-
|
|
1384
|
+
const dir = resolve5(exocortexDir, "radiant", "reads");
|
|
1385
|
+
mkdirSync2(dir, { recursive: true });
|
|
523
1386
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
524
1387
|
const filename = `${date}.md`;
|
|
525
|
-
const filepath =
|
|
1388
|
+
const filepath = join5(dir, filename);
|
|
526
1389
|
const content = `${frontmatter}
|
|
527
1390
|
|
|
528
1391
|
${text}
|
|
529
1392
|
`;
|
|
530
|
-
|
|
1393
|
+
writeFileSync2(filepath, content, "utf-8");
|
|
531
1394
|
return filepath;
|
|
532
1395
|
}
|
|
533
1396
|
function updateKnowledge(exocortexDir, persistence, options) {
|
|
534
|
-
const dir =
|
|
535
|
-
|
|
536
|
-
const filepath =
|
|
1397
|
+
const dir = resolve5(exocortexDir, "radiant");
|
|
1398
|
+
mkdirSync2(dir, { recursive: true });
|
|
1399
|
+
const filepath = join5(dir, "knowledge.md");
|
|
537
1400
|
const totalReads = options?.totalReads ?? 0;
|
|
538
1401
|
const existingUntriggered = loadUntriggeredCounts(filepath);
|
|
539
1402
|
const lines = [
|
|
@@ -620,14 +1483,14 @@ function updateKnowledge(exocortexDir, persistence, options) {
|
|
|
620
1483
|
lines.push(`${name}=${count}`);
|
|
621
1484
|
}
|
|
622
1485
|
lines.push("-->");
|
|
623
|
-
|
|
1486
|
+
writeFileSync2(filepath, lines.join("\n"), "utf-8");
|
|
624
1487
|
return filepath;
|
|
625
1488
|
}
|
|
626
1489
|
function loadUntriggeredCounts(filepath) {
|
|
627
1490
|
const counts = /* @__PURE__ */ new Map();
|
|
628
|
-
if (!
|
|
1491
|
+
if (!existsSync5(filepath)) return counts;
|
|
629
1492
|
try {
|
|
630
|
-
const content =
|
|
1493
|
+
const content = readFileSync5(filepath, "utf-8");
|
|
631
1494
|
const match = content.match(
|
|
632
1495
|
/<!-- untriggered_counts[\s\S]*?-->/
|
|
633
1496
|
);
|
|
@@ -645,13 +1508,13 @@ function loadUntriggeredCounts(filepath) {
|
|
|
645
1508
|
return counts;
|
|
646
1509
|
}
|
|
647
1510
|
function loadPriorReads(exocortexDir) {
|
|
648
|
-
const dir =
|
|
649
|
-
if (!
|
|
650
|
-
const files =
|
|
1511
|
+
const dir = resolve5(exocortexDir, "radiant", "reads");
|
|
1512
|
+
if (!existsSync5(dir)) return [];
|
|
1513
|
+
const files = readdirSync3(dir).filter((f) => f.endsWith(".md")).sort();
|
|
651
1514
|
const reads = [];
|
|
652
1515
|
for (const filename of files) {
|
|
653
|
-
const filepath =
|
|
654
|
-
const content =
|
|
1516
|
+
const filepath = join5(dir, filename);
|
|
1517
|
+
const content = readFileSync5(filepath, "utf-8");
|
|
655
1518
|
const date = filename.replace(".md", "");
|
|
656
1519
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
657
1520
|
const frontmatter = fmMatch ? fmMatch[1] : "";
|
|
@@ -1028,15 +1891,17 @@ function buildInterpretationPrompt(input) {
|
|
|
1028
1891
|
const eventSample = formatEventSample(input.events, 30);
|
|
1029
1892
|
const canonicalList = (input.canonicalPatterns ?? []).length > 0 ? `Patterns the organization has already named (use these names if you see them):
|
|
1030
1893
|
${input.canonicalPatterns.map((p) => `- ${p}`).join("\n")}` : "No patterns have been named yet. Everything you observe is new.";
|
|
1894
|
+
const compressedWorld = compressWorldmodel(input.worldmodelContent);
|
|
1895
|
+
const cl = compressLens(input.lens);
|
|
1031
1896
|
const frame = input.lens.primary_frame;
|
|
1032
1897
|
const evalQuestions = frame.evaluation_questions.map((q, i) => `${i + 1}. ${q}`).join("\n");
|
|
1033
|
-
const forbiddenList =
|
|
1034
|
-
const jargonTable =
|
|
1898
|
+
const forbiddenList = cl.forbiddenPhrases;
|
|
1899
|
+
const jargonTable = cl.jargonTranslations;
|
|
1035
1900
|
return `You are a behavioral intelligence system reading team activity and producing a read for the reader who needs to act on it.
|
|
1036
1901
|
|
|
1037
|
-
##
|
|
1902
|
+
## Worldmodel (compressed)
|
|
1038
1903
|
|
|
1039
|
-
${
|
|
1904
|
+
${compressedWorld}
|
|
1040
1905
|
|
|
1041
1906
|
## What happened this window
|
|
1042
1907
|
|
|
@@ -1227,30 +2092,16 @@ Window: last ${input.windowDays} days \xB7 ${input.eventCount} events
|
|
|
1227
2092
|
Lens: ${input.lens.name}`
|
|
1228
2093
|
);
|
|
1229
2094
|
if (input.patterns.length > 0) {
|
|
1230
|
-
const canonical = input.patterns.filter((p) => p.type === "canonical");
|
|
1231
|
-
const candidates = input.patterns.filter((p) => p.type === "candidate");
|
|
1232
2095
|
let emergentBlock = "EMERGENT\n";
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
emergentBlock += `
|
|
2096
|
+
for (const p of input.patterns) {
|
|
2097
|
+
emergentBlock += `
|
|
1236
2098
|
${p.name}
|
|
1237
2099
|
`;
|
|
1238
|
-
|
|
1239
|
-
`;
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
if (candidates.length > 0) {
|
|
1243
|
-
emergentBlock += "\n Emergent (candidates \u2014 not yet in worldmodel)\n";
|
|
1244
|
-
for (const p of candidates) {
|
|
1245
|
-
emergentBlock += `
|
|
1246
|
-
${p.name} (candidate)
|
|
1247
|
-
`;
|
|
1248
|
-
emergentBlock += ` ${p.description}
|
|
2100
|
+
emergentBlock += ` ${p.description}
|
|
1249
2101
|
`;
|
|
1250
|
-
|
|
1251
|
-
|
|
2102
|
+
if (p.evidence.cited_invariant) {
|
|
2103
|
+
emergentBlock += ` Cited invariant: ${p.evidence.cited_invariant}
|
|
1252
2104
|
`;
|
|
1253
|
-
}
|
|
1254
2105
|
}
|
|
1255
2106
|
}
|
|
1256
2107
|
sections.push(emergentBlock.trimEnd());
|
|
@@ -1462,16 +2313,29 @@ function serializeYAML(obj, indent = 0) {
|
|
|
1462
2313
|
async function emergent(input) {
|
|
1463
2314
|
const lens = resolveLens2(input.lensId);
|
|
1464
2315
|
const windowDays = input.windowDays ?? 14;
|
|
2316
|
+
let worldStack;
|
|
2317
|
+
let worldmodelContent = input.worldmodelContent;
|
|
2318
|
+
if (!worldmodelContent || worldmodelContent.trim() === "") {
|
|
2319
|
+
worldStack = discoverWorlds({ explicitWorldsDir: input.worldPath });
|
|
2320
|
+
worldmodelContent = worldStack.combinedContent;
|
|
2321
|
+
}
|
|
1465
2322
|
let statedIntent;
|
|
1466
2323
|
let exocortexContext;
|
|
1467
2324
|
let priorReadContext = "";
|
|
1468
2325
|
if (input.exocortexPath) {
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
2326
|
+
const repoName = input.scope.type === "repo" ? input.scope.repo : void 0;
|
|
2327
|
+
exocortexContext = readExocortex(input.exocortexPath, repoName);
|
|
2328
|
+
const compressed = compressExocortex(exocortexContext);
|
|
2329
|
+
if (compressed) {
|
|
2330
|
+
statedIntent = `## Stated Intent (from exocortex, compressed)
|
|
2331
|
+
|
|
2332
|
+
${compressed}
|
|
2333
|
+
|
|
2334
|
+
Compare stated intent against actual GitHub activity. Gaps = drift.`;
|
|
2335
|
+
}
|
|
1472
2336
|
const priorReads = loadPriorReads(input.exocortexPath);
|
|
1473
2337
|
if (priorReads.length > 0) {
|
|
1474
|
-
priorReadContext =
|
|
2338
|
+
priorReadContext = compressPriorReads(priorReads);
|
|
1475
2339
|
}
|
|
1476
2340
|
}
|
|
1477
2341
|
let events;
|
|
@@ -1489,6 +2353,40 @@ async function emergent(input) {
|
|
|
1489
2353
|
windowDays
|
|
1490
2354
|
});
|
|
1491
2355
|
}
|
|
2356
|
+
let adapterSignals = "";
|
|
2357
|
+
const activeAdapters = ["github"];
|
|
2358
|
+
const discordToken = process.env.DISCORD_TOKEN;
|
|
2359
|
+
const discordGuild = process.env.DISCORD_GUILD_ID;
|
|
2360
|
+
if (discordToken && discordGuild) {
|
|
2361
|
+
try {
|
|
2362
|
+
const discord = await fetchDiscordActivity(discordGuild, discordToken, { windowDays });
|
|
2363
|
+
events.push(...discord.events);
|
|
2364
|
+
adapterSignals += "\n\n" + formatDiscordSignalsForPrompt(discord.signals);
|
|
2365
|
+
activeAdapters.push("discord");
|
|
2366
|
+
} catch {
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
const slackToken = process.env.SLACK_TOKEN;
|
|
2370
|
+
if (slackToken) {
|
|
2371
|
+
try {
|
|
2372
|
+
const slack = await fetchSlackActivity(slackToken, { windowDays });
|
|
2373
|
+
events.push(...slack.events);
|
|
2374
|
+
adapterSignals += "\n\n" + formatSlackSignalsForPrompt(slack.signals);
|
|
2375
|
+
activeAdapters.push("slack");
|
|
2376
|
+
} catch {
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
const notionToken = process.env.NOTION_TOKEN;
|
|
2380
|
+
if (notionToken) {
|
|
2381
|
+
try {
|
|
2382
|
+
const notion = await fetchNotionActivity(notionToken, { windowDays });
|
|
2383
|
+
events.push(...notion.events);
|
|
2384
|
+
adapterSignals += "\n\n" + formatNotionSignalsForPrompt(notion.signals);
|
|
2385
|
+
activeAdapters.push("notion");
|
|
2386
|
+
} catch {
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
1492
2390
|
const classified = classifyEvents(events);
|
|
1493
2391
|
const signals = extractSignals(classified);
|
|
1494
2392
|
const scores = computeScores(signals, input.worldmodelContent !== "");
|
|
@@ -1499,7 +2397,7 @@ async function emergent(input) {
|
|
|
1499
2397
|
lens,
|
|
1500
2398
|
ai: input.ai,
|
|
1501
2399
|
canonicalPatterns: input.canonicalPatterns,
|
|
1502
|
-
statedIntent: statedIntent
|
|
2400
|
+
statedIntent: [statedIntent, adapterSignals, priorReadContext].filter(Boolean).join("\n\n") || void 0
|
|
1503
2401
|
});
|
|
1504
2402
|
const rewrittenPatterns = patterns.map((p) => lens.rewrite(p));
|
|
1505
2403
|
const allDescriptions = rewrittenPatterns.map((p) => p.description).join("\n");
|
|
@@ -1548,7 +2446,9 @@ async function emergent(input) {
|
|
|
1548
2446
|
voiceClean: voiceViolations.length === 0,
|
|
1549
2447
|
signals,
|
|
1550
2448
|
scores,
|
|
1551
|
-
eventCount: events.length
|
|
2449
|
+
eventCount: events.length,
|
|
2450
|
+
activeAdapters,
|
|
2451
|
+
worldStack
|
|
1552
2452
|
};
|
|
1553
2453
|
}
|
|
1554
2454
|
function computeScores(signals, worldmodelLoaded) {
|
|
@@ -1641,6 +2541,10 @@ function createMockAI(fixedResponse) {
|
|
|
1641
2541
|
}
|
|
1642
2542
|
|
|
1643
2543
|
export {
|
|
2544
|
+
compressWorldmodel,
|
|
2545
|
+
compressExocortex,
|
|
2546
|
+
compressLens,
|
|
2547
|
+
compressPriorReads,
|
|
1644
2548
|
composeSystemPrompt,
|
|
1645
2549
|
checkForbiddenPhrases,
|
|
1646
2550
|
think,
|
|
@@ -1650,6 +2554,23 @@ export {
|
|
|
1650
2554
|
fetchGitHubActivity,
|
|
1651
2555
|
fetchGitHubOrgActivity,
|
|
1652
2556
|
createMockGitHubAdapter,
|
|
2557
|
+
fetchDiscordActivity,
|
|
2558
|
+
formatDiscordSignalsForPrompt,
|
|
2559
|
+
fetchSlackActivity,
|
|
2560
|
+
formatSlackSignalsForPrompt,
|
|
2561
|
+
fetchNotionActivity,
|
|
2562
|
+
formatNotionSignalsForPrompt,
|
|
2563
|
+
readOriginRemote,
|
|
2564
|
+
parseRemoteUrl,
|
|
2565
|
+
getRepoOrigin,
|
|
2566
|
+
loadExtendsConfig,
|
|
2567
|
+
parseExtendsSpec,
|
|
2568
|
+
getCacheDir,
|
|
2569
|
+
resolveExtendsSpec,
|
|
2570
|
+
detectOrgExtendsSpec,
|
|
2571
|
+
resolveAllExtends,
|
|
2572
|
+
discoverWorlds,
|
|
2573
|
+
formatActiveWorlds,
|
|
1653
2574
|
readExocortex,
|
|
1654
2575
|
formatExocortexForPrompt,
|
|
1655
2576
|
readTeamExocortices,
|