@neuroverseos/governance 0.11.0 → 0.12.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.
@@ -46,6 +46,11 @@ async function main(argv = process.argv.slice(2)) {
46
46
  let world;
47
47
  try {
48
48
  const resolved = resolveWorldPath(args.worldPath);
49
+ if (!resolved) {
50
+ process.stderr.write(`Failed to resolve world path: ${args.worldPath}
51
+ `);
52
+ process.exit(1);
53
+ }
49
54
  world = await loadWorld(resolved);
50
55
  } catch (err) {
51
56
  process.stderr.write(`Failed to load world: ${err instanceof Error ? err.message : err}
@@ -7702,6 +7702,11 @@ async function main12(argv = process.argv.slice(2)) {
7702
7702
  let world;
7703
7703
  try {
7704
7704
  const resolved = resolveWorldPath(args.worldPath);
7705
+ if (!resolved) {
7706
+ process.stderr.write(`Failed to resolve world path: ${args.worldPath}
7707
+ `);
7708
+ process.exit(1);
7709
+ }
7705
7710
  world = await loadWorld(resolved);
7706
7711
  } catch (err) {
7707
7712
  process.stderr.write(`Failed to load world: ${err instanceof Error ? err.message : err}
@@ -101,7 +101,7 @@ async function main() {
101
101
  return guardMain(subArgs);
102
102
  }
103
103
  case "audit": {
104
- const { main: auditMain } = await import("../audit-CRJOB4CP.js");
104
+ const { main: auditMain } = await import("../audit-JYNN3MOQ.js");
105
105
  return auditMain(subArgs);
106
106
  }
107
107
  case "test": {
@@ -56,16 +56,20 @@ __export(radiant_exports, {
56
56
  fetchDiscordActivity: () => fetchDiscordActivity,
57
57
  fetchGitHubActivity: () => fetchGitHubActivity,
58
58
  fetchGitHubOrgActivity: () => fetchGitHubOrgActivity,
59
+ fetchGoogleWorkspaceActivity: () => fetchGoogleWorkspaceActivity,
59
60
  fetchLinearActivity: () => fetchLinearActivity,
60
61
  fetchNotionActivity: () => fetchNotionActivity,
62
+ fetchSalesforceActivity: () => fetchSalesforceActivity,
61
63
  fetchSlackActivity: () => fetchSlackActivity,
62
64
  filterEventsByUser: () => filterEventsByUser,
63
65
  formatActiveWorlds: () => formatActiveWorlds,
64
66
  formatDiscordSignalsForPrompt: () => formatDiscordSignalsForPrompt,
65
67
  formatExocortexForPrompt: () => formatExocortexForPrompt,
68
+ formatGoogleWorkspaceSignalsForPrompt: () => formatGoogleWorkspaceSignalsForPrompt,
66
69
  formatLinearSignalsForPrompt: () => formatLinearSignalsForPrompt,
67
70
  formatNotionSignalsForPrompt: () => formatNotionSignalsForPrompt,
68
71
  formatPriorReadsForPrompt: () => formatPriorReadsForPrompt,
72
+ formatSalesforceSignalsForPrompt: () => formatSalesforceSignalsForPrompt,
69
73
  formatScope: () => formatScope,
70
74
  formatSlackSignalsForPrompt: () => formatSlackSignalsForPrompt,
71
75
  formatTeamExocorticesForPrompt: () => formatTeamExocorticesForPrompt,
@@ -2296,6 +2300,405 @@ async function fetchLinearGraphQL(apiKey, query, variables) {
2296
2300
  return json.data;
2297
2301
  }
2298
2302
 
2303
+ // src/radiant/adapters/google-workspace.ts
2304
+ async function fetchGoogleWorkspaceActivity(options) {
2305
+ const windowDays = options.windowDays ?? 14;
2306
+ const maxEmails = options.maxEmails ?? 100;
2307
+ const maxEvents = options.maxEvents ?? 100;
2308
+ const leaderEmail = options.leaderEmail?.toLowerCase();
2309
+ const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1e3);
2310
+ const [emailEvents, emailSignals] = await fetchGmailSent(
2311
+ options.accessToken,
2312
+ since,
2313
+ maxEmails,
2314
+ leaderEmail
2315
+ );
2316
+ const [calendarEvents, calendarSignals] = await fetchCalendarEvents(
2317
+ options.accessToken,
2318
+ since,
2319
+ maxEvents,
2320
+ leaderEmail
2321
+ );
2322
+ const events = [...emailEvents, ...calendarEvents].sort(
2323
+ (a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp)
2324
+ );
2325
+ return {
2326
+ events,
2327
+ signals: {
2328
+ ...emailSignals,
2329
+ ...calendarSignals
2330
+ }
2331
+ };
2332
+ }
2333
+ function formatGoogleWorkspaceSignalsForPrompt(signals) {
2334
+ if (signals.emailsSent === 0 && signals.meetingsHeld === 0) return "";
2335
+ const lines = [
2336
+ "## Google Workspace Activity (external coordination + meeting load)",
2337
+ ""
2338
+ ];
2339
+ if (signals.emailsSent > 0) {
2340
+ lines.push(
2341
+ `${signals.emailsSent} emails sent in window, to ${signals.uniqueRecipients} unique recipients.`
2342
+ );
2343
+ if (signals.topRecipients.length > 0) {
2344
+ lines.push(`Most-emailed: ${signals.topRecipients.slice(0, 5).join(", ")}.`);
2345
+ }
2346
+ }
2347
+ if (signals.meetingsHeld > 0) {
2348
+ const hours = Math.round(signals.totalMeetingMinutes / 60 * 10) / 10;
2349
+ lines.push(
2350
+ `${signals.meetingsHeld} meetings held (${signals.meetingsOrganized} organized by the leader), ${hours}h total.`
2351
+ );
2352
+ if (signals.avgMeetingAttendees !== null) {
2353
+ lines.push(
2354
+ `Average ${signals.avgMeetingAttendees} attendees per meeting, ${signals.uniqueAttendees} unique attendees total.`
2355
+ );
2356
+ }
2357
+ }
2358
+ lines.push("");
2359
+ lines.push(
2360
+ "Gmail sent volume + Calendar meeting load reveal a leader's external coordination shape."
2361
+ );
2362
+ lines.push(
2363
+ "Compare against shipped work (GitHub, Linear) to find the stated-strategy-vs-actual-time gap."
2364
+ );
2365
+ return lines.join("\n");
2366
+ }
2367
+ async function fetchGmailSent(token, since, maxEmails, leaderEmail) {
2368
+ const sinceUnix = Math.floor(since.getTime() / 1e3);
2369
+ const listUrl = new URL("https://gmail.googleapis.com/gmail/v1/users/me/messages");
2370
+ listUrl.searchParams.set("q", `in:sent after:${sinceUnix}`);
2371
+ listUrl.searchParams.set("maxResults", String(Math.min(maxEmails, 100)));
2372
+ const listRes = await fetch(listUrl.toString(), {
2373
+ headers: { Authorization: `Bearer ${token}` }
2374
+ });
2375
+ if (!listRes.ok) {
2376
+ throw new Error(`Gmail list failed ${listRes.status}: ${(await listRes.text()).slice(0, 200)}`);
2377
+ }
2378
+ const listJson = await listRes.json();
2379
+ const ids = (listJson.messages ?? []).slice(0, maxEmails);
2380
+ const events = [];
2381
+ const recipientCounts = /* @__PURE__ */ new Map();
2382
+ const PARALLEL = 5;
2383
+ for (let i = 0; i < ids.length; i += PARALLEL) {
2384
+ const chunk = ids.slice(i, i + PARALLEL);
2385
+ const results = await Promise.all(
2386
+ chunk.map(
2387
+ (m) => fetch(
2388
+ `https://gmail.googleapis.com/gmail/v1/users/me/messages/${m.id}?format=metadata&metadataHeaders=From&metadataHeaders=To&metadataHeaders=Subject&metadataHeaders=Date`,
2389
+ { headers: { Authorization: `Bearer ${token}` } }
2390
+ ).then((r) => r.ok ? r.json() : null).catch(() => null)
2391
+ )
2392
+ );
2393
+ for (const msg of results) {
2394
+ if (!msg) continue;
2395
+ const headers = msg.payload?.headers || [];
2396
+ const header = (n) => headers.find((h) => h.name.toLowerCase() === n.toLowerCase())?.value ?? "";
2397
+ const subject = header("Subject") || "(no subject)";
2398
+ const to = header("To");
2399
+ const dateHdr = header("Date");
2400
+ const timestamp = dateHdr ? new Date(dateHdr).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
2401
+ const snippet = msg.snippet ?? "";
2402
+ const recipients = parseAddressList(to);
2403
+ for (const r of recipients) {
2404
+ recipientCounts.set(r, (recipientCounts.get(r) ?? 0) + 1);
2405
+ }
2406
+ events.push({
2407
+ id: `gmail-${msg.id}`,
2408
+ timestamp,
2409
+ actor: {
2410
+ id: leaderEmail ?? "leader",
2411
+ kind: "human",
2412
+ name: leaderEmail ?? "Leader"
2413
+ },
2414
+ kind: "email_sent",
2415
+ content: `${subject} \u2014 ${snippet.slice(0, 200)}`,
2416
+ metadata: {
2417
+ to: recipients.slice(0, 5),
2418
+ subject
2419
+ }
2420
+ });
2421
+ }
2422
+ }
2423
+ const topRecipients = [...recipientCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 10).map(([addr]) => addr);
2424
+ return [
2425
+ events,
2426
+ {
2427
+ emailsSent: events.length,
2428
+ uniqueRecipients: recipientCounts.size,
2429
+ topRecipients
2430
+ }
2431
+ ];
2432
+ }
2433
+ function parseAddressList(raw) {
2434
+ if (!raw) return [];
2435
+ return raw.split(",").map((a) => {
2436
+ const match = a.match(/<([^>]+)>/);
2437
+ const addr = (match ? match[1] : a).trim().toLowerCase();
2438
+ return addr;
2439
+ }).filter((a) => a.includes("@") && a.length < 100);
2440
+ }
2441
+ async function fetchCalendarEvents(token, since, maxEvents, leaderEmail) {
2442
+ const timeMin = since.toISOString();
2443
+ const timeMax = (/* @__PURE__ */ new Date()).toISOString();
2444
+ const url = new URL("https://www.googleapis.com/calendar/v3/calendars/primary/events");
2445
+ url.searchParams.set("timeMin", timeMin);
2446
+ url.searchParams.set("timeMax", timeMax);
2447
+ url.searchParams.set("singleEvents", "true");
2448
+ url.searchParams.set("orderBy", "startTime");
2449
+ url.searchParams.set("maxResults", String(Math.min(maxEvents, 250)));
2450
+ const res = await fetch(url.toString(), {
2451
+ headers: { Authorization: `Bearer ${token}` }
2452
+ });
2453
+ if (!res.ok) {
2454
+ throw new Error(`Calendar list failed ${res.status}: ${(await res.text()).slice(0, 200)}`);
2455
+ }
2456
+ const json = await res.json();
2457
+ const events = [];
2458
+ const attendeeSet = /* @__PURE__ */ new Set();
2459
+ let totalMinutes = 0;
2460
+ let meetingsOrganized = 0;
2461
+ const attendeeCounts = [];
2462
+ for (const ev of json.items ?? []) {
2463
+ if (ev.status === "cancelled") continue;
2464
+ const start = ev.start?.dateTime || ev.start?.date;
2465
+ const end = ev.end?.dateTime || ev.end?.date;
2466
+ if (!start) continue;
2467
+ const startMs = new Date(start).getTime();
2468
+ const endMs = end ? new Date(end).getTime() : startMs;
2469
+ const minutes = Math.max(0, Math.round((endMs - startMs) / 6e4));
2470
+ totalMinutes += minutes;
2471
+ const attendees = (ev.attendees ?? []).map((a) => a.email?.toLowerCase()).filter((e) => !!e && e.includes("@"));
2472
+ for (const a of attendees) attendeeSet.add(a);
2473
+ attendeeCounts.push(attendees.length);
2474
+ const organizedByLeader = leaderEmail && ev.organizer?.email?.toLowerCase() === leaderEmail;
2475
+ if (organizedByLeader) meetingsOrganized++;
2476
+ events.push({
2477
+ id: `gcal-${ev.id}`,
2478
+ timestamp: start,
2479
+ actor: {
2480
+ id: leaderEmail ?? "leader",
2481
+ kind: "human",
2482
+ name: leaderEmail ?? "Leader"
2483
+ },
2484
+ kind: organizedByLeader ? "meeting_organized" : "meeting_attended",
2485
+ content: `${ev.summary ?? "(no title)"} \u2014 ${minutes}min, ${attendees.length} attendees`,
2486
+ metadata: {
2487
+ attendees: attendees.slice(0, 10),
2488
+ minutes,
2489
+ organizer: ev.organizer?.email
2490
+ }
2491
+ });
2492
+ }
2493
+ const avgAttendees = attendeeCounts.length > 0 ? Math.round(
2494
+ attendeeCounts.reduce((a, b) => a + b, 0) / attendeeCounts.length
2495
+ ) : null;
2496
+ return [
2497
+ events,
2498
+ {
2499
+ meetingsHeld: events.length,
2500
+ meetingsOrganized,
2501
+ uniqueAttendees: attendeeSet.size,
2502
+ totalMeetingMinutes: totalMinutes,
2503
+ avgMeetingAttendees: avgAttendees
2504
+ }
2505
+ ];
2506
+ }
2507
+
2508
+ // src/radiant/adapters/salesforce.ts
2509
+ async function fetchSalesforceActivity(options) {
2510
+ const windowDays = options.windowDays ?? 14;
2511
+ const maxRecords = Math.min(options.maxRecords ?? 200, 200);
2512
+ const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1e3);
2513
+ const sinceSoql = since.toISOString();
2514
+ const events = [];
2515
+ const signals = {
2516
+ opportunitiesMoved: 0,
2517
+ stageTransitions: 0,
2518
+ amountChanges: 0,
2519
+ closedWon: 0,
2520
+ closedLost: 0,
2521
+ tasksLogged: 0,
2522
+ callsLogged: 0,
2523
+ feedPostsByLeader: 0,
2524
+ totalPipelineAmount: 0,
2525
+ topStages: []
2526
+ };
2527
+ const opps = await soqlQuery(
2528
+ options,
2529
+ `SELECT Id, Name, StageName, Amount, CloseDate, IsClosed, IsWon, LastModifiedDate, Owner.Name
2530
+ FROM Opportunity
2531
+ WHERE LastModifiedDate >= ${sinceSoql}
2532
+ ORDER BY LastModifiedDate DESC
2533
+ LIMIT ${maxRecords}`
2534
+ );
2535
+ const stageCounts = /* @__PURE__ */ new Map();
2536
+ for (const opp of opps) {
2537
+ signals.opportunitiesMoved++;
2538
+ signals.totalPipelineAmount += opp.Amount ?? 0;
2539
+ stageCounts.set(opp.StageName, (stageCounts.get(opp.StageName) ?? 0) + 1);
2540
+ if (opp.IsClosed && opp.IsWon) signals.closedWon++;
2541
+ if (opp.IsClosed && !opp.IsWon) signals.closedLost++;
2542
+ events.push({
2543
+ id: `sf-opp-${opp.Id}`,
2544
+ timestamp: opp.LastModifiedDate,
2545
+ actor: {
2546
+ id: opp.Owner?.Name ?? "unknown",
2547
+ kind: "human",
2548
+ name: opp.Owner?.Name ?? "unknown"
2549
+ },
2550
+ kind: opp.IsClosed ? opp.IsWon ? "deal_won" : "deal_lost" : "deal_updated",
2551
+ content: `${opp.Name} \u2014 ${opp.StageName}${opp.Amount ? ` \xB7 $${Math.round(opp.Amount).toLocaleString()}` : ""} \xB7 close ${opp.CloseDate}`,
2552
+ metadata: {
2553
+ opportunityId: opp.Id,
2554
+ stage: opp.StageName,
2555
+ amount: opp.Amount,
2556
+ closeDate: opp.CloseDate
2557
+ }
2558
+ });
2559
+ }
2560
+ signals.topStages = [...stageCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([stage, count]) => ({ stage, count }));
2561
+ const history = await soqlQuery(
2562
+ options,
2563
+ `SELECT Id, OpportunityId, Field, OldValue, NewValue, CreatedDate, CreatedBy.Name
2564
+ FROM OpportunityFieldHistory
2565
+ WHERE CreatedDate >= ${sinceSoql} AND Field IN ('StageName', 'Amount', 'CloseDate')
2566
+ ORDER BY CreatedDate DESC
2567
+ LIMIT ${maxRecords}`
2568
+ ).catch(() => []);
2569
+ for (const h of history) {
2570
+ if (h.Field === "StageName") signals.stageTransitions++;
2571
+ if (h.Field === "Amount") signals.amountChanges++;
2572
+ events.push({
2573
+ id: `sf-hist-${h.Id}`,
2574
+ timestamp: h.CreatedDate,
2575
+ actor: {
2576
+ id: h.CreatedBy?.Name ?? "unknown",
2577
+ kind: "human",
2578
+ name: h.CreatedBy?.Name ?? "unknown"
2579
+ },
2580
+ kind: `deal_${h.Field.toLowerCase()}_changed`,
2581
+ content: `${h.Field}: ${String(h.OldValue)} \u2192 ${String(h.NewValue)}`,
2582
+ metadata: {
2583
+ opportunityId: h.OpportunityId,
2584
+ field: h.Field
2585
+ }
2586
+ });
2587
+ }
2588
+ const tasks = await soqlQuery(
2589
+ options,
2590
+ `SELECT Id, Subject, Status, Type, CreatedDate, Owner.Name
2591
+ FROM Task
2592
+ WHERE CreatedDate >= ${sinceSoql}
2593
+ ORDER BY CreatedDate DESC
2594
+ LIMIT ${maxRecords}`
2595
+ ).catch(() => []);
2596
+ for (const t of tasks) {
2597
+ signals.tasksLogged++;
2598
+ if (t.Type === "Call" || (t.Subject || "").toLowerCase().includes("call")) {
2599
+ signals.callsLogged++;
2600
+ }
2601
+ events.push({
2602
+ id: `sf-task-${t.Id}`,
2603
+ timestamp: t.CreatedDate,
2604
+ actor: {
2605
+ id: t.Owner?.Name ?? "unknown",
2606
+ kind: "human",
2607
+ name: t.Owner?.Name ?? "unknown"
2608
+ },
2609
+ kind: t.Type === "Call" ? "call_logged" : "task_logged",
2610
+ content: `${t.Subject} \u2014 ${t.Status}`,
2611
+ metadata: { taskId: t.Id, type: t.Type, status: t.Status }
2612
+ });
2613
+ }
2614
+ if (options.leaderName) {
2615
+ const feed = await soqlQuery(
2616
+ options,
2617
+ `SELECT Id, Body, CreatedDate, CreatedBy.Name, ParentId
2618
+ FROM FeedItem
2619
+ WHERE CreatedDate >= ${sinceSoql} AND CreatedBy.Name = '${escapeSoql(options.leaderName)}'
2620
+ ORDER BY CreatedDate DESC
2621
+ LIMIT ${Math.min(maxRecords, 50)}`
2622
+ ).catch(() => []);
2623
+ for (const f of feed) {
2624
+ signals.feedPostsByLeader++;
2625
+ events.push({
2626
+ id: `sf-feed-${f.Id}`,
2627
+ timestamp: f.CreatedDate,
2628
+ actor: {
2629
+ id: f.CreatedBy?.Name ?? "unknown",
2630
+ kind: "human",
2631
+ name: f.CreatedBy?.Name ?? "unknown"
2632
+ },
2633
+ kind: "feed_post",
2634
+ content: (f.Body || "").slice(0, 280),
2635
+ metadata: { feedId: f.Id, parent: f.ParentId }
2636
+ });
2637
+ }
2638
+ }
2639
+ events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
2640
+ return { events, signals };
2641
+ }
2642
+ function formatSalesforceSignalsForPrompt(signals) {
2643
+ if (signals.opportunitiesMoved === 0 && signals.tasksLogged === 0 && signals.stageTransitions === 0) {
2644
+ return "";
2645
+ }
2646
+ const lines = [
2647
+ "## Salesforce Activity (stated pipeline vs observed motion)",
2648
+ ""
2649
+ ];
2650
+ if (signals.opportunitiesMoved > 0) {
2651
+ const pipelineM = Math.round(signals.totalPipelineAmount / 1e4) / 100;
2652
+ lines.push(
2653
+ `${signals.opportunitiesMoved} opportunities touched in window ($${pipelineM}M total pipeline represented).`
2654
+ );
2655
+ lines.push(
2656
+ `${signals.stageTransitions} stage changes, ${signals.amountChanges} amount changes.`
2657
+ );
2658
+ if (signals.closedWon + signals.closedLost > 0) {
2659
+ lines.push(`${signals.closedWon} closed won, ${signals.closedLost} closed lost.`);
2660
+ }
2661
+ if (signals.topStages.length > 0) {
2662
+ lines.push(
2663
+ `Most active stages: ${signals.topStages.map((s) => `${s.stage} (${s.count})`).join(", ")}.`
2664
+ );
2665
+ }
2666
+ }
2667
+ if (signals.tasksLogged > 0) {
2668
+ lines.push(
2669
+ `${signals.tasksLogged} tasks logged (${signals.callsLogged} calls).`
2670
+ );
2671
+ }
2672
+ if (signals.feedPostsByLeader > 0) {
2673
+ lines.push(`${signals.feedPostsByLeader} feed posts by the leader.`);
2674
+ }
2675
+ lines.push("");
2676
+ lines.push(
2677
+ "Salesforce shows what the team said would happen (stages, forecast) vs. what is actually happening (movement, activity, close)."
2678
+ );
2679
+ lines.push(
2680
+ "Compare stage transitions against stated strategy \u2014 are the deals the leader said would close actually moving, or is the pipeline drifting?"
2681
+ );
2682
+ return lines.join("\n");
2683
+ }
2684
+ async function soqlQuery(opts, query) {
2685
+ const url = new URL(`${opts.instanceUrl}/services/data/v59.0/query`);
2686
+ url.searchParams.set("q", query);
2687
+ const res = await fetch(url.toString(), {
2688
+ headers: { Authorization: `Bearer ${opts.accessToken}` }
2689
+ });
2690
+ if (!res.ok) {
2691
+ throw new Error(
2692
+ `Salesforce SOQL ${res.status}: ${(await res.text()).slice(0, 200)}`
2693
+ );
2694
+ }
2695
+ const json = await res.json();
2696
+ return json.records ?? [];
2697
+ }
2698
+ function escapeSoql(raw) {
2699
+ return raw.replace(/['\\]/g, "");
2700
+ }
2701
+
2299
2702
  // src/radiant/core/vocabulary.ts
2300
2703
  function extractDeclaredVocabulary(worldmodelContent) {
2301
2704
  const aligned = extractSection(worldmodelContent, "Aligned Behaviors").map(
@@ -5049,16 +5452,20 @@ var RADIANT_PACKAGE_VERSION = "0.0.0";
5049
5452
  fetchDiscordActivity,
5050
5453
  fetchGitHubActivity,
5051
5454
  fetchGitHubOrgActivity,
5455
+ fetchGoogleWorkspaceActivity,
5052
5456
  fetchLinearActivity,
5053
5457
  fetchNotionActivity,
5458
+ fetchSalesforceActivity,
5054
5459
  fetchSlackActivity,
5055
5460
  filterEventsByUser,
5056
5461
  formatActiveWorlds,
5057
5462
  formatDiscordSignalsForPrompt,
5058
5463
  formatExocortexForPrompt,
5464
+ formatGoogleWorkspaceSignalsForPrompt,
5059
5465
  formatLinearSignalsForPrompt,
5060
5466
  formatNotionSignalsForPrompt,
5061
5467
  formatPriorReadsForPrompt,
5468
+ formatSalesforceSignalsForPrompt,
5062
5469
  formatScope,
5063
5470
  formatSlackSignalsForPrompt,
5064
5471
  formatTeamExocorticesForPrompt,
@@ -1244,6 +1244,136 @@ declare function fetchLinearActivity(apiKey: string, options?: LinearFetchOption
1244
1244
  */
1245
1245
  declare function formatLinearSignalsForPrompt(signals: LinearSignals): string;
1246
1246
 
1247
+ /**
1248
+ * @neuroverseos/governance/radiant — Google Workspace adapter
1249
+ *
1250
+ * Reads the leader's Gmail (sent messages) + Calendar (events in the
1251
+ * window) and normalizes both into Event[] for the Radiant pipeline.
1252
+ *
1253
+ * Why Google Workspace matters:
1254
+ * - It's the most universal source. Every non-MS-shop leader has
1255
+ * Gmail + Calendar. Non-engineering leaders who never touch GitHub
1256
+ * or Linear still live in these two apps.
1257
+ * - Gmail sent folder = external coordination signal. What the
1258
+ * leader's actually pushing out into the world. Drift between
1259
+ * stated strategy and actual outreach shows up here first.
1260
+ * - Calendar = meeting load vs. output. Heavy meetings + thin
1261
+ * shipping = the drift pattern leaders most want to catch.
1262
+ *
1263
+ * v1 scope:
1264
+ * - Gmail: list the leader's own sent messages in the window.
1265
+ * Subject + snippet + recipients + timestamp. No body (privacy +
1266
+ * payload size).
1267
+ * - Calendar: list events in the window from the primary calendar.
1268
+ * Title + attendees count + duration + whether the leader
1269
+ * organized.
1270
+ *
1271
+ * Auth: Google OAuth access token with scopes
1272
+ * https://www.googleapis.com/auth/gmail.readonly
1273
+ * https://www.googleapis.com/auth/calendar.readonly
1274
+ * Token lifecycle (refresh) is the caller's job — this adapter takes
1275
+ * a valid access token as input.
1276
+ *
1277
+ * Deno / browser / Node all work — uses native fetch only.
1278
+ */
1279
+
1280
+ interface GoogleWorkspaceFetchOptions {
1281
+ /** Access token with gmail.readonly + calendar.readonly scopes. */
1282
+ accessToken: string;
1283
+ /** How many days of history to fetch. Default: 14. */
1284
+ windowDays?: number;
1285
+ /** Max emails to include. Default: 100. */
1286
+ maxEmails?: number;
1287
+ /** Max calendar events to include. Default: 100. */
1288
+ maxEvents?: number;
1289
+ /** The leader's own email address — used to identify their events. */
1290
+ leaderEmail?: string;
1291
+ }
1292
+ interface GoogleWorkspaceSignals {
1293
+ emailsSent: number;
1294
+ uniqueRecipients: number;
1295
+ topRecipients: string[];
1296
+ meetingsHeld: number;
1297
+ meetingsOrganized: number;
1298
+ uniqueAttendees: number;
1299
+ totalMeetingMinutes: number;
1300
+ avgMeetingAttendees: number | null;
1301
+ }
1302
+ declare function fetchGoogleWorkspaceActivity(options: GoogleWorkspaceFetchOptions): Promise<{
1303
+ events: Event[];
1304
+ signals: GoogleWorkspaceSignals;
1305
+ }>;
1306
+ /**
1307
+ * Format Google Workspace signals for the AI interpretation prompt.
1308
+ */
1309
+ declare function formatGoogleWorkspaceSignalsForPrompt(signals: GoogleWorkspaceSignals): string;
1310
+
1311
+ /**
1312
+ * @neuroverseos/governance/radiant — Salesforce adapter
1313
+ *
1314
+ * Reads the leader's Salesforce activity and normalizes it into Event[]
1315
+ * for the Radiant pipeline. Opens the entire sales-leader market —
1316
+ * every sales org of any scale runs on Salesforce, and "is the pipeline
1317
+ * drifting from what we declared we'd close" is the exact job Radiant
1318
+ * does for that audience.
1319
+ *
1320
+ * v1 scope:
1321
+ * - Recent Opportunity changes: stage transitions, amount updates,
1322
+ * close-date changes (the signal set for deal-drift detection)
1323
+ * - Recent Tasks + logged Calls (activity per rep)
1324
+ * - Chatter FeedItems if accessible (how the team talks about deals)
1325
+ *
1326
+ * Auth: Salesforce OAuth — access token + instance URL. Each org has
1327
+ * its own `my.salesforce.com` endpoint; the token alone isn't enough,
1328
+ * the caller must also pass the instance URL.
1329
+ *
1330
+ * Individual-rep or individual-manager OAuth works without admin
1331
+ * consent — this adapter assumes one Salesforce user's view. For
1332
+ * enterprise admin-consent + org-wide access, a later adapter
1333
+ * variant can use the Reports API or a service-user token.
1334
+ *
1335
+ * Deno / browser / Node — native fetch only.
1336
+ */
1337
+
1338
+ interface SalesforceFetchOptions {
1339
+ /** OAuth access token with api + refresh_token scopes. */
1340
+ accessToken: string;
1341
+ /**
1342
+ * Salesforce instance URL — e.g. https://acme.my.salesforce.com.
1343
+ * Comes back in the token response; caller must persist it.
1344
+ */
1345
+ instanceUrl: string;
1346
+ /** How many days of history to fetch. Default: 14. */
1347
+ windowDays?: number;
1348
+ /** Max records per entity. Default: 200 (hard cap on SOQL page). */
1349
+ maxRecords?: number;
1350
+ /** The leader's own Salesforce user name / email, if known. */
1351
+ leaderName?: string;
1352
+ }
1353
+ interface SalesforceSignals {
1354
+ opportunitiesMoved: number;
1355
+ stageTransitions: number;
1356
+ amountChanges: number;
1357
+ closedWon: number;
1358
+ closedLost: number;
1359
+ tasksLogged: number;
1360
+ callsLogged: number;
1361
+ feedPostsByLeader: number;
1362
+ totalPipelineAmount: number;
1363
+ topStages: Array<{
1364
+ stage: string;
1365
+ count: number;
1366
+ }>;
1367
+ }
1368
+ declare function fetchSalesforceActivity(options: SalesforceFetchOptions): Promise<{
1369
+ events: Event[];
1370
+ signals: SalesforceSignals;
1371
+ }>;
1372
+ /**
1373
+ * Format Salesforce signals for the AI interpretation prompt.
1374
+ */
1375
+ declare function formatSalesforceSignalsForPrompt(signals: SalesforceSignals): string;
1376
+
1247
1377
  /**
1248
1378
  * @neuroverseos/governance/radiant — declared vocabulary extraction
1249
1379
  *
@@ -1962,4 +2092,4 @@ declare function filterEventsByUser(events: readonly Event[], username: string):
1962
2092
  */
1963
2093
  declare const RADIANT_PACKAGE_VERSION = "0.0.0";
1964
2094
 
1965
- export { type Actor, type ActorDomain, type ActorKind, type AlignmentStatus, type BridgingComponent, type BridgingComponentScore, type ClassifiedEvent, type CyberCapability, type CyberDimension, DEFAULT_EVIDENCE_GATE, DEFAULT_SIGNAL_EXTRACTORS, type DeclaredPattern, type DeclaredVocabulary, type DiscordFetchOptions, type DiscordSignals, type DiscoveredWorld, type EmergentInput, type EmergentResult, type Event, type EventReference, type EvidenceGate, type ExemplarRef, type ExocortexContext, type ExtendsConfig, type ExtendsSpec, type ExtractionResult, type Fetcher, type GitHubFetchOptions, type GovernanceAudit, type GovernanceVerdict, type InterpretInput, type InterpretResult, LENSES, type LensVocabulary, type LifeCapability, type LifeDimension, type LinearFetchOptions, type LinearSignals, type NotionFetchOptions, type NotionSignals, type ObservedPattern, type OrgScope, type OverlapDef, type ParsedRemote, type PatternEvidence, type PatternPersistence, type PrimaryFrame, type PriorRead, RADIANT_PACKAGE_VERSION, type RadiantAI, type RenderInput, type RenderOutput, type RenderingLens, type RepoScope, type ResolveResult, type Scope, type Score, type ScoreSentinel, type ScoredObservation, type Signal, type SignalExtractor, type SignalMatrix, type SlackFetchOptions, type SlackSignals, type ThinkInput, type ThinkResult, type ViewLevel, type VoiceDirectives, type VoiceViolation, type WorldStack, type WorldmodelItem, auditGovernance, aukiBuilderLens, checkForbiddenPhrases, classifyActorDomain, classifyEvents, composeSystemPrompt, compressExocortex, compressLens, compressPriorReads, compressWorldmodel, computePersistence, createAnthropicAI, createMockAI, createMockGitHubAdapter, detectOrgExtendsSpec, discoverWorlds, emergent, extractDeclaredVocabulary, extractSignals, fetchDiscordActivity, fetchGitHubActivity, fetchGitHubOrgActivity, fetchLinearActivity, fetchNotionActivity, fetchSlackActivity, filterEventsByUser, formatActiveWorlds, formatDiscordSignalsForPrompt, formatExocortexForPrompt, formatLinearSignalsForPrompt, formatNotionSignalsForPrompt, formatPriorReadsForPrompt, formatScope, formatSlackSignalsForPrompt, formatTeamExocorticesForPrompt, getCacheDir, getLens, getRepoOrigin, interpretPatterns, isPresent, isScored, isSentinel, listLenses, loadExtendsConfig, loadPriorReads, matchDeclaredPattern, parseExtendsSpec, parseRemoteUrl, parseRepoScope, parseScope, presenceAverage, readExocortex, readOriginRemote, readTeamExocortices, render, resolveAllExtends, resolveExtendsSpec, scoreComposite, scoreCyber, scoreLife, scoreNeuroVerse, sovereignConduitLens, summarizeExocortex, think, updateKnowledge, writeRead };
2095
+ export { type Actor, type ActorDomain, type ActorKind, type AlignmentStatus, type BridgingComponent, type BridgingComponentScore, type ClassifiedEvent, type CyberCapability, type CyberDimension, DEFAULT_EVIDENCE_GATE, DEFAULT_SIGNAL_EXTRACTORS, type DeclaredPattern, type DeclaredVocabulary, type DiscordFetchOptions, type DiscordSignals, type DiscoveredWorld, type EmergentInput, type EmergentResult, type Event, type EventReference, type EvidenceGate, type ExemplarRef, type ExocortexContext, type ExtendsConfig, type ExtendsSpec, type ExtractionResult, type Fetcher, type GitHubFetchOptions, type GoogleWorkspaceFetchOptions, type GoogleWorkspaceSignals, type GovernanceAudit, type GovernanceVerdict, type InterpretInput, type InterpretResult, LENSES, type LensVocabulary, type LifeCapability, type LifeDimension, type LinearFetchOptions, type LinearSignals, type NotionFetchOptions, type NotionSignals, type ObservedPattern, type OrgScope, type OverlapDef, type ParsedRemote, type PatternEvidence, type PatternPersistence, type PrimaryFrame, type PriorRead, RADIANT_PACKAGE_VERSION, type RadiantAI, type RenderInput, type RenderOutput, type RenderingLens, type RepoScope, type ResolveResult, type SalesforceFetchOptions, type SalesforceSignals, type Scope, type Score, type ScoreSentinel, type ScoredObservation, type Signal, type SignalExtractor, type SignalMatrix, type SlackFetchOptions, type SlackSignals, type ThinkInput, type ThinkResult, type ViewLevel, type VoiceDirectives, type VoiceViolation, type WorldStack, type WorldmodelItem, auditGovernance, aukiBuilderLens, checkForbiddenPhrases, classifyActorDomain, classifyEvents, composeSystemPrompt, compressExocortex, compressLens, compressPriorReads, compressWorldmodel, computePersistence, createAnthropicAI, createMockAI, createMockGitHubAdapter, detectOrgExtendsSpec, discoverWorlds, emergent, extractDeclaredVocabulary, extractSignals, fetchDiscordActivity, fetchGitHubActivity, fetchGitHubOrgActivity, fetchGoogleWorkspaceActivity, fetchLinearActivity, fetchNotionActivity, fetchSalesforceActivity, fetchSlackActivity, filterEventsByUser, formatActiveWorlds, formatDiscordSignalsForPrompt, formatExocortexForPrompt, formatGoogleWorkspaceSignalsForPrompt, formatLinearSignalsForPrompt, formatNotionSignalsForPrompt, formatPriorReadsForPrompt, formatSalesforceSignalsForPrompt, formatScope, formatSlackSignalsForPrompt, formatTeamExocorticesForPrompt, getCacheDir, getLens, getRepoOrigin, interpretPatterns, isPresent, isScored, isSentinel, listLenses, loadExtendsConfig, loadPriorReads, matchDeclaredPattern, parseExtendsSpec, parseRemoteUrl, parseRepoScope, parseScope, presenceAverage, readExocortex, readOriginRemote, readTeamExocortices, render, resolveAllExtends, resolveExtendsSpec, scoreComposite, scoreCyber, scoreLife, scoreNeuroVerse, sovereignConduitLens, summarizeExocortex, think, updateKnowledge, writeRead };
@@ -1244,6 +1244,136 @@ declare function fetchLinearActivity(apiKey: string, options?: LinearFetchOption
1244
1244
  */
1245
1245
  declare function formatLinearSignalsForPrompt(signals: LinearSignals): string;
1246
1246
 
1247
+ /**
1248
+ * @neuroverseos/governance/radiant — Google Workspace adapter
1249
+ *
1250
+ * Reads the leader's Gmail (sent messages) + Calendar (events in the
1251
+ * window) and normalizes both into Event[] for the Radiant pipeline.
1252
+ *
1253
+ * Why Google Workspace matters:
1254
+ * - It's the most universal source. Every non-MS-shop leader has
1255
+ * Gmail + Calendar. Non-engineering leaders who never touch GitHub
1256
+ * or Linear still live in these two apps.
1257
+ * - Gmail sent folder = external coordination signal. What the
1258
+ * leader's actually pushing out into the world. Drift between
1259
+ * stated strategy and actual outreach shows up here first.
1260
+ * - Calendar = meeting load vs. output. Heavy meetings + thin
1261
+ * shipping = the drift pattern leaders most want to catch.
1262
+ *
1263
+ * v1 scope:
1264
+ * - Gmail: list the leader's own sent messages in the window.
1265
+ * Subject + snippet + recipients + timestamp. No body (privacy +
1266
+ * payload size).
1267
+ * - Calendar: list events in the window from the primary calendar.
1268
+ * Title + attendees count + duration + whether the leader
1269
+ * organized.
1270
+ *
1271
+ * Auth: Google OAuth access token with scopes
1272
+ * https://www.googleapis.com/auth/gmail.readonly
1273
+ * https://www.googleapis.com/auth/calendar.readonly
1274
+ * Token lifecycle (refresh) is the caller's job — this adapter takes
1275
+ * a valid access token as input.
1276
+ *
1277
+ * Deno / browser / Node all work — uses native fetch only.
1278
+ */
1279
+
1280
+ interface GoogleWorkspaceFetchOptions {
1281
+ /** Access token with gmail.readonly + calendar.readonly scopes. */
1282
+ accessToken: string;
1283
+ /** How many days of history to fetch. Default: 14. */
1284
+ windowDays?: number;
1285
+ /** Max emails to include. Default: 100. */
1286
+ maxEmails?: number;
1287
+ /** Max calendar events to include. Default: 100. */
1288
+ maxEvents?: number;
1289
+ /** The leader's own email address — used to identify their events. */
1290
+ leaderEmail?: string;
1291
+ }
1292
+ interface GoogleWorkspaceSignals {
1293
+ emailsSent: number;
1294
+ uniqueRecipients: number;
1295
+ topRecipients: string[];
1296
+ meetingsHeld: number;
1297
+ meetingsOrganized: number;
1298
+ uniqueAttendees: number;
1299
+ totalMeetingMinutes: number;
1300
+ avgMeetingAttendees: number | null;
1301
+ }
1302
+ declare function fetchGoogleWorkspaceActivity(options: GoogleWorkspaceFetchOptions): Promise<{
1303
+ events: Event[];
1304
+ signals: GoogleWorkspaceSignals;
1305
+ }>;
1306
+ /**
1307
+ * Format Google Workspace signals for the AI interpretation prompt.
1308
+ */
1309
+ declare function formatGoogleWorkspaceSignalsForPrompt(signals: GoogleWorkspaceSignals): string;
1310
+
1311
+ /**
1312
+ * @neuroverseos/governance/radiant — Salesforce adapter
1313
+ *
1314
+ * Reads the leader's Salesforce activity and normalizes it into Event[]
1315
+ * for the Radiant pipeline. Opens the entire sales-leader market —
1316
+ * every sales org of any scale runs on Salesforce, and "is the pipeline
1317
+ * drifting from what we declared we'd close" is the exact job Radiant
1318
+ * does for that audience.
1319
+ *
1320
+ * v1 scope:
1321
+ * - Recent Opportunity changes: stage transitions, amount updates,
1322
+ * close-date changes (the signal set for deal-drift detection)
1323
+ * - Recent Tasks + logged Calls (activity per rep)
1324
+ * - Chatter FeedItems if accessible (how the team talks about deals)
1325
+ *
1326
+ * Auth: Salesforce OAuth — access token + instance URL. Each org has
1327
+ * its own `my.salesforce.com` endpoint; the token alone isn't enough,
1328
+ * the caller must also pass the instance URL.
1329
+ *
1330
+ * Individual-rep or individual-manager OAuth works without admin
1331
+ * consent — this adapter assumes one Salesforce user's view. For
1332
+ * enterprise admin-consent + org-wide access, a later adapter
1333
+ * variant can use the Reports API or a service-user token.
1334
+ *
1335
+ * Deno / browser / Node — native fetch only.
1336
+ */
1337
+
1338
+ interface SalesforceFetchOptions {
1339
+ /** OAuth access token with api + refresh_token scopes. */
1340
+ accessToken: string;
1341
+ /**
1342
+ * Salesforce instance URL — e.g. https://acme.my.salesforce.com.
1343
+ * Comes back in the token response; caller must persist it.
1344
+ */
1345
+ instanceUrl: string;
1346
+ /** How many days of history to fetch. Default: 14. */
1347
+ windowDays?: number;
1348
+ /** Max records per entity. Default: 200 (hard cap on SOQL page). */
1349
+ maxRecords?: number;
1350
+ /** The leader's own Salesforce user name / email, if known. */
1351
+ leaderName?: string;
1352
+ }
1353
+ interface SalesforceSignals {
1354
+ opportunitiesMoved: number;
1355
+ stageTransitions: number;
1356
+ amountChanges: number;
1357
+ closedWon: number;
1358
+ closedLost: number;
1359
+ tasksLogged: number;
1360
+ callsLogged: number;
1361
+ feedPostsByLeader: number;
1362
+ totalPipelineAmount: number;
1363
+ topStages: Array<{
1364
+ stage: string;
1365
+ count: number;
1366
+ }>;
1367
+ }
1368
+ declare function fetchSalesforceActivity(options: SalesforceFetchOptions): Promise<{
1369
+ events: Event[];
1370
+ signals: SalesforceSignals;
1371
+ }>;
1372
+ /**
1373
+ * Format Salesforce signals for the AI interpretation prompt.
1374
+ */
1375
+ declare function formatSalesforceSignalsForPrompt(signals: SalesforceSignals): string;
1376
+
1247
1377
  /**
1248
1378
  * @neuroverseos/governance/radiant — declared vocabulary extraction
1249
1379
  *
@@ -1962,4 +2092,4 @@ declare function filterEventsByUser(events: readonly Event[], username: string):
1962
2092
  */
1963
2093
  declare const RADIANT_PACKAGE_VERSION = "0.0.0";
1964
2094
 
1965
- export { type Actor, type ActorDomain, type ActorKind, type AlignmentStatus, type BridgingComponent, type BridgingComponentScore, type ClassifiedEvent, type CyberCapability, type CyberDimension, DEFAULT_EVIDENCE_GATE, DEFAULT_SIGNAL_EXTRACTORS, type DeclaredPattern, type DeclaredVocabulary, type DiscordFetchOptions, type DiscordSignals, type DiscoveredWorld, type EmergentInput, type EmergentResult, type Event, type EventReference, type EvidenceGate, type ExemplarRef, type ExocortexContext, type ExtendsConfig, type ExtendsSpec, type ExtractionResult, type Fetcher, type GitHubFetchOptions, type GovernanceAudit, type GovernanceVerdict, type InterpretInput, type InterpretResult, LENSES, type LensVocabulary, type LifeCapability, type LifeDimension, type LinearFetchOptions, type LinearSignals, type NotionFetchOptions, type NotionSignals, type ObservedPattern, type OrgScope, type OverlapDef, type ParsedRemote, type PatternEvidence, type PatternPersistence, type PrimaryFrame, type PriorRead, RADIANT_PACKAGE_VERSION, type RadiantAI, type RenderInput, type RenderOutput, type RenderingLens, type RepoScope, type ResolveResult, type Scope, type Score, type ScoreSentinel, type ScoredObservation, type Signal, type SignalExtractor, type SignalMatrix, type SlackFetchOptions, type SlackSignals, type ThinkInput, type ThinkResult, type ViewLevel, type VoiceDirectives, type VoiceViolation, type WorldStack, type WorldmodelItem, auditGovernance, aukiBuilderLens, checkForbiddenPhrases, classifyActorDomain, classifyEvents, composeSystemPrompt, compressExocortex, compressLens, compressPriorReads, compressWorldmodel, computePersistence, createAnthropicAI, createMockAI, createMockGitHubAdapter, detectOrgExtendsSpec, discoverWorlds, emergent, extractDeclaredVocabulary, extractSignals, fetchDiscordActivity, fetchGitHubActivity, fetchGitHubOrgActivity, fetchLinearActivity, fetchNotionActivity, fetchSlackActivity, filterEventsByUser, formatActiveWorlds, formatDiscordSignalsForPrompt, formatExocortexForPrompt, formatLinearSignalsForPrompt, formatNotionSignalsForPrompt, formatPriorReadsForPrompt, formatScope, formatSlackSignalsForPrompt, formatTeamExocorticesForPrompt, getCacheDir, getLens, getRepoOrigin, interpretPatterns, isPresent, isScored, isSentinel, listLenses, loadExtendsConfig, loadPriorReads, matchDeclaredPattern, parseExtendsSpec, parseRemoteUrl, parseRepoScope, parseScope, presenceAverage, readExocortex, readOriginRemote, readTeamExocortices, render, resolveAllExtends, resolveExtendsSpec, scoreComposite, scoreCyber, scoreLife, scoreNeuroVerse, sovereignConduitLens, summarizeExocortex, think, updateKnowledge, writeRead };
2095
+ export { type Actor, type ActorDomain, type ActorKind, type AlignmentStatus, type BridgingComponent, type BridgingComponentScore, type ClassifiedEvent, type CyberCapability, type CyberDimension, DEFAULT_EVIDENCE_GATE, DEFAULT_SIGNAL_EXTRACTORS, type DeclaredPattern, type DeclaredVocabulary, type DiscordFetchOptions, type DiscordSignals, type DiscoveredWorld, type EmergentInput, type EmergentResult, type Event, type EventReference, type EvidenceGate, type ExemplarRef, type ExocortexContext, type ExtendsConfig, type ExtendsSpec, type ExtractionResult, type Fetcher, type GitHubFetchOptions, type GoogleWorkspaceFetchOptions, type GoogleWorkspaceSignals, type GovernanceAudit, type GovernanceVerdict, type InterpretInput, type InterpretResult, LENSES, type LensVocabulary, type LifeCapability, type LifeDimension, type LinearFetchOptions, type LinearSignals, type NotionFetchOptions, type NotionSignals, type ObservedPattern, type OrgScope, type OverlapDef, type ParsedRemote, type PatternEvidence, type PatternPersistence, type PrimaryFrame, type PriorRead, RADIANT_PACKAGE_VERSION, type RadiantAI, type RenderInput, type RenderOutput, type RenderingLens, type RepoScope, type ResolveResult, type SalesforceFetchOptions, type SalesforceSignals, type Scope, type Score, type ScoreSentinel, type ScoredObservation, type Signal, type SignalExtractor, type SignalMatrix, type SlackFetchOptions, type SlackSignals, type ThinkInput, type ThinkResult, type ViewLevel, type VoiceDirectives, type VoiceViolation, type WorldStack, type WorldmodelItem, auditGovernance, aukiBuilderLens, checkForbiddenPhrases, classifyActorDomain, classifyEvents, composeSystemPrompt, compressExocortex, compressLens, compressPriorReads, compressWorldmodel, computePersistence, createAnthropicAI, createMockAI, createMockGitHubAdapter, detectOrgExtendsSpec, discoverWorlds, emergent, extractDeclaredVocabulary, extractSignals, fetchDiscordActivity, fetchGitHubActivity, fetchGitHubOrgActivity, fetchGoogleWorkspaceActivity, fetchLinearActivity, fetchNotionActivity, fetchSalesforceActivity, fetchSlackActivity, filterEventsByUser, formatActiveWorlds, formatDiscordSignalsForPrompt, formatExocortexForPrompt, formatGoogleWorkspaceSignalsForPrompt, formatLinearSignalsForPrompt, formatNotionSignalsForPrompt, formatPriorReadsForPrompt, formatSalesforceSignalsForPrompt, formatScope, formatSlackSignalsForPrompt, formatTeamExocorticesForPrompt, getCacheDir, getLens, getRepoOrigin, interpretPatterns, isPresent, isScored, isSentinel, listLenses, loadExtendsConfig, loadPriorReads, matchDeclaredPattern, parseExtendsSpec, parseRemoteUrl, parseRepoScope, parseScope, presenceAverage, readExocortex, readOriginRemote, readTeamExocortices, render, resolveAllExtends, resolveExtendsSpec, scoreComposite, scoreCyber, scoreLife, scoreNeuroVerse, sovereignConduitLens, summarizeExocortex, think, updateKnowledge, writeRead };
@@ -76,6 +76,405 @@ import "../chunk-MBOW6YXN.js";
76
76
  import "../chunk-QLPTHTVB.js";
77
77
  import "../chunk-QWGCMQQD.js";
78
78
 
79
+ // src/radiant/adapters/google-workspace.ts
80
+ async function fetchGoogleWorkspaceActivity(options) {
81
+ const windowDays = options.windowDays ?? 14;
82
+ const maxEmails = options.maxEmails ?? 100;
83
+ const maxEvents = options.maxEvents ?? 100;
84
+ const leaderEmail = options.leaderEmail?.toLowerCase();
85
+ const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1e3);
86
+ const [emailEvents, emailSignals] = await fetchGmailSent(
87
+ options.accessToken,
88
+ since,
89
+ maxEmails,
90
+ leaderEmail
91
+ );
92
+ const [calendarEvents, calendarSignals] = await fetchCalendarEvents(
93
+ options.accessToken,
94
+ since,
95
+ maxEvents,
96
+ leaderEmail
97
+ );
98
+ const events = [...emailEvents, ...calendarEvents].sort(
99
+ (a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp)
100
+ );
101
+ return {
102
+ events,
103
+ signals: {
104
+ ...emailSignals,
105
+ ...calendarSignals
106
+ }
107
+ };
108
+ }
109
+ function formatGoogleWorkspaceSignalsForPrompt(signals) {
110
+ if (signals.emailsSent === 0 && signals.meetingsHeld === 0) return "";
111
+ const lines = [
112
+ "## Google Workspace Activity (external coordination + meeting load)",
113
+ ""
114
+ ];
115
+ if (signals.emailsSent > 0) {
116
+ lines.push(
117
+ `${signals.emailsSent} emails sent in window, to ${signals.uniqueRecipients} unique recipients.`
118
+ );
119
+ if (signals.topRecipients.length > 0) {
120
+ lines.push(`Most-emailed: ${signals.topRecipients.slice(0, 5).join(", ")}.`);
121
+ }
122
+ }
123
+ if (signals.meetingsHeld > 0) {
124
+ const hours = Math.round(signals.totalMeetingMinutes / 60 * 10) / 10;
125
+ lines.push(
126
+ `${signals.meetingsHeld} meetings held (${signals.meetingsOrganized} organized by the leader), ${hours}h total.`
127
+ );
128
+ if (signals.avgMeetingAttendees !== null) {
129
+ lines.push(
130
+ `Average ${signals.avgMeetingAttendees} attendees per meeting, ${signals.uniqueAttendees} unique attendees total.`
131
+ );
132
+ }
133
+ }
134
+ lines.push("");
135
+ lines.push(
136
+ "Gmail sent volume + Calendar meeting load reveal a leader's external coordination shape."
137
+ );
138
+ lines.push(
139
+ "Compare against shipped work (GitHub, Linear) to find the stated-strategy-vs-actual-time gap."
140
+ );
141
+ return lines.join("\n");
142
+ }
143
+ async function fetchGmailSent(token, since, maxEmails, leaderEmail) {
144
+ const sinceUnix = Math.floor(since.getTime() / 1e3);
145
+ const listUrl = new URL("https://gmail.googleapis.com/gmail/v1/users/me/messages");
146
+ listUrl.searchParams.set("q", `in:sent after:${sinceUnix}`);
147
+ listUrl.searchParams.set("maxResults", String(Math.min(maxEmails, 100)));
148
+ const listRes = await fetch(listUrl.toString(), {
149
+ headers: { Authorization: `Bearer ${token}` }
150
+ });
151
+ if (!listRes.ok) {
152
+ throw new Error(`Gmail list failed ${listRes.status}: ${(await listRes.text()).slice(0, 200)}`);
153
+ }
154
+ const listJson = await listRes.json();
155
+ const ids = (listJson.messages ?? []).slice(0, maxEmails);
156
+ const events = [];
157
+ const recipientCounts = /* @__PURE__ */ new Map();
158
+ const PARALLEL = 5;
159
+ for (let i = 0; i < ids.length; i += PARALLEL) {
160
+ const chunk = ids.slice(i, i + PARALLEL);
161
+ const results = await Promise.all(
162
+ chunk.map(
163
+ (m) => fetch(
164
+ `https://gmail.googleapis.com/gmail/v1/users/me/messages/${m.id}?format=metadata&metadataHeaders=From&metadataHeaders=To&metadataHeaders=Subject&metadataHeaders=Date`,
165
+ { headers: { Authorization: `Bearer ${token}` } }
166
+ ).then((r) => r.ok ? r.json() : null).catch(() => null)
167
+ )
168
+ );
169
+ for (const msg of results) {
170
+ if (!msg) continue;
171
+ const headers = msg.payload?.headers || [];
172
+ const header = (n) => headers.find((h) => h.name.toLowerCase() === n.toLowerCase())?.value ?? "";
173
+ const subject = header("Subject") || "(no subject)";
174
+ const to = header("To");
175
+ const dateHdr = header("Date");
176
+ const timestamp = dateHdr ? new Date(dateHdr).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
177
+ const snippet = msg.snippet ?? "";
178
+ const recipients = parseAddressList(to);
179
+ for (const r of recipients) {
180
+ recipientCounts.set(r, (recipientCounts.get(r) ?? 0) + 1);
181
+ }
182
+ events.push({
183
+ id: `gmail-${msg.id}`,
184
+ timestamp,
185
+ actor: {
186
+ id: leaderEmail ?? "leader",
187
+ kind: "human",
188
+ name: leaderEmail ?? "Leader"
189
+ },
190
+ kind: "email_sent",
191
+ content: `${subject} \u2014 ${snippet.slice(0, 200)}`,
192
+ metadata: {
193
+ to: recipients.slice(0, 5),
194
+ subject
195
+ }
196
+ });
197
+ }
198
+ }
199
+ const topRecipients = [...recipientCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 10).map(([addr]) => addr);
200
+ return [
201
+ events,
202
+ {
203
+ emailsSent: events.length,
204
+ uniqueRecipients: recipientCounts.size,
205
+ topRecipients
206
+ }
207
+ ];
208
+ }
209
+ function parseAddressList(raw) {
210
+ if (!raw) return [];
211
+ return raw.split(",").map((a) => {
212
+ const match = a.match(/<([^>]+)>/);
213
+ const addr = (match ? match[1] : a).trim().toLowerCase();
214
+ return addr;
215
+ }).filter((a) => a.includes("@") && a.length < 100);
216
+ }
217
+ async function fetchCalendarEvents(token, since, maxEvents, leaderEmail) {
218
+ const timeMin = since.toISOString();
219
+ const timeMax = (/* @__PURE__ */ new Date()).toISOString();
220
+ const url = new URL("https://www.googleapis.com/calendar/v3/calendars/primary/events");
221
+ url.searchParams.set("timeMin", timeMin);
222
+ url.searchParams.set("timeMax", timeMax);
223
+ url.searchParams.set("singleEvents", "true");
224
+ url.searchParams.set("orderBy", "startTime");
225
+ url.searchParams.set("maxResults", String(Math.min(maxEvents, 250)));
226
+ const res = await fetch(url.toString(), {
227
+ headers: { Authorization: `Bearer ${token}` }
228
+ });
229
+ if (!res.ok) {
230
+ throw new Error(`Calendar list failed ${res.status}: ${(await res.text()).slice(0, 200)}`);
231
+ }
232
+ const json = await res.json();
233
+ const events = [];
234
+ const attendeeSet = /* @__PURE__ */ new Set();
235
+ let totalMinutes = 0;
236
+ let meetingsOrganized = 0;
237
+ const attendeeCounts = [];
238
+ for (const ev of json.items ?? []) {
239
+ if (ev.status === "cancelled") continue;
240
+ const start = ev.start?.dateTime || ev.start?.date;
241
+ const end = ev.end?.dateTime || ev.end?.date;
242
+ if (!start) continue;
243
+ const startMs = new Date(start).getTime();
244
+ const endMs = end ? new Date(end).getTime() : startMs;
245
+ const minutes = Math.max(0, Math.round((endMs - startMs) / 6e4));
246
+ totalMinutes += minutes;
247
+ const attendees = (ev.attendees ?? []).map((a) => a.email?.toLowerCase()).filter((e) => !!e && e.includes("@"));
248
+ for (const a of attendees) attendeeSet.add(a);
249
+ attendeeCounts.push(attendees.length);
250
+ const organizedByLeader = leaderEmail && ev.organizer?.email?.toLowerCase() === leaderEmail;
251
+ if (organizedByLeader) meetingsOrganized++;
252
+ events.push({
253
+ id: `gcal-${ev.id}`,
254
+ timestamp: start,
255
+ actor: {
256
+ id: leaderEmail ?? "leader",
257
+ kind: "human",
258
+ name: leaderEmail ?? "Leader"
259
+ },
260
+ kind: organizedByLeader ? "meeting_organized" : "meeting_attended",
261
+ content: `${ev.summary ?? "(no title)"} \u2014 ${minutes}min, ${attendees.length} attendees`,
262
+ metadata: {
263
+ attendees: attendees.slice(0, 10),
264
+ minutes,
265
+ organizer: ev.organizer?.email
266
+ }
267
+ });
268
+ }
269
+ const avgAttendees = attendeeCounts.length > 0 ? Math.round(
270
+ attendeeCounts.reduce((a, b) => a + b, 0) / attendeeCounts.length
271
+ ) : null;
272
+ return [
273
+ events,
274
+ {
275
+ meetingsHeld: events.length,
276
+ meetingsOrganized,
277
+ uniqueAttendees: attendeeSet.size,
278
+ totalMeetingMinutes: totalMinutes,
279
+ avgMeetingAttendees: avgAttendees
280
+ }
281
+ ];
282
+ }
283
+
284
+ // src/radiant/adapters/salesforce.ts
285
+ async function fetchSalesforceActivity(options) {
286
+ const windowDays = options.windowDays ?? 14;
287
+ const maxRecords = Math.min(options.maxRecords ?? 200, 200);
288
+ const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1e3);
289
+ const sinceSoql = since.toISOString();
290
+ const events = [];
291
+ const signals = {
292
+ opportunitiesMoved: 0,
293
+ stageTransitions: 0,
294
+ amountChanges: 0,
295
+ closedWon: 0,
296
+ closedLost: 0,
297
+ tasksLogged: 0,
298
+ callsLogged: 0,
299
+ feedPostsByLeader: 0,
300
+ totalPipelineAmount: 0,
301
+ topStages: []
302
+ };
303
+ const opps = await soqlQuery(
304
+ options,
305
+ `SELECT Id, Name, StageName, Amount, CloseDate, IsClosed, IsWon, LastModifiedDate, Owner.Name
306
+ FROM Opportunity
307
+ WHERE LastModifiedDate >= ${sinceSoql}
308
+ ORDER BY LastModifiedDate DESC
309
+ LIMIT ${maxRecords}`
310
+ );
311
+ const stageCounts = /* @__PURE__ */ new Map();
312
+ for (const opp of opps) {
313
+ signals.opportunitiesMoved++;
314
+ signals.totalPipelineAmount += opp.Amount ?? 0;
315
+ stageCounts.set(opp.StageName, (stageCounts.get(opp.StageName) ?? 0) + 1);
316
+ if (opp.IsClosed && opp.IsWon) signals.closedWon++;
317
+ if (opp.IsClosed && !opp.IsWon) signals.closedLost++;
318
+ events.push({
319
+ id: `sf-opp-${opp.Id}`,
320
+ timestamp: opp.LastModifiedDate,
321
+ actor: {
322
+ id: opp.Owner?.Name ?? "unknown",
323
+ kind: "human",
324
+ name: opp.Owner?.Name ?? "unknown"
325
+ },
326
+ kind: opp.IsClosed ? opp.IsWon ? "deal_won" : "deal_lost" : "deal_updated",
327
+ content: `${opp.Name} \u2014 ${opp.StageName}${opp.Amount ? ` \xB7 $${Math.round(opp.Amount).toLocaleString()}` : ""} \xB7 close ${opp.CloseDate}`,
328
+ metadata: {
329
+ opportunityId: opp.Id,
330
+ stage: opp.StageName,
331
+ amount: opp.Amount,
332
+ closeDate: opp.CloseDate
333
+ }
334
+ });
335
+ }
336
+ signals.topStages = [...stageCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([stage, count]) => ({ stage, count }));
337
+ const history = await soqlQuery(
338
+ options,
339
+ `SELECT Id, OpportunityId, Field, OldValue, NewValue, CreatedDate, CreatedBy.Name
340
+ FROM OpportunityFieldHistory
341
+ WHERE CreatedDate >= ${sinceSoql} AND Field IN ('StageName', 'Amount', 'CloseDate')
342
+ ORDER BY CreatedDate DESC
343
+ LIMIT ${maxRecords}`
344
+ ).catch(() => []);
345
+ for (const h of history) {
346
+ if (h.Field === "StageName") signals.stageTransitions++;
347
+ if (h.Field === "Amount") signals.amountChanges++;
348
+ events.push({
349
+ id: `sf-hist-${h.Id}`,
350
+ timestamp: h.CreatedDate,
351
+ actor: {
352
+ id: h.CreatedBy?.Name ?? "unknown",
353
+ kind: "human",
354
+ name: h.CreatedBy?.Name ?? "unknown"
355
+ },
356
+ kind: `deal_${h.Field.toLowerCase()}_changed`,
357
+ content: `${h.Field}: ${String(h.OldValue)} \u2192 ${String(h.NewValue)}`,
358
+ metadata: {
359
+ opportunityId: h.OpportunityId,
360
+ field: h.Field
361
+ }
362
+ });
363
+ }
364
+ const tasks = await soqlQuery(
365
+ options,
366
+ `SELECT Id, Subject, Status, Type, CreatedDate, Owner.Name
367
+ FROM Task
368
+ WHERE CreatedDate >= ${sinceSoql}
369
+ ORDER BY CreatedDate DESC
370
+ LIMIT ${maxRecords}`
371
+ ).catch(() => []);
372
+ for (const t of tasks) {
373
+ signals.tasksLogged++;
374
+ if (t.Type === "Call" || (t.Subject || "").toLowerCase().includes("call")) {
375
+ signals.callsLogged++;
376
+ }
377
+ events.push({
378
+ id: `sf-task-${t.Id}`,
379
+ timestamp: t.CreatedDate,
380
+ actor: {
381
+ id: t.Owner?.Name ?? "unknown",
382
+ kind: "human",
383
+ name: t.Owner?.Name ?? "unknown"
384
+ },
385
+ kind: t.Type === "Call" ? "call_logged" : "task_logged",
386
+ content: `${t.Subject} \u2014 ${t.Status}`,
387
+ metadata: { taskId: t.Id, type: t.Type, status: t.Status }
388
+ });
389
+ }
390
+ if (options.leaderName) {
391
+ const feed = await soqlQuery(
392
+ options,
393
+ `SELECT Id, Body, CreatedDate, CreatedBy.Name, ParentId
394
+ FROM FeedItem
395
+ WHERE CreatedDate >= ${sinceSoql} AND CreatedBy.Name = '${escapeSoql(options.leaderName)}'
396
+ ORDER BY CreatedDate DESC
397
+ LIMIT ${Math.min(maxRecords, 50)}`
398
+ ).catch(() => []);
399
+ for (const f of feed) {
400
+ signals.feedPostsByLeader++;
401
+ events.push({
402
+ id: `sf-feed-${f.Id}`,
403
+ timestamp: f.CreatedDate,
404
+ actor: {
405
+ id: f.CreatedBy?.Name ?? "unknown",
406
+ kind: "human",
407
+ name: f.CreatedBy?.Name ?? "unknown"
408
+ },
409
+ kind: "feed_post",
410
+ content: (f.Body || "").slice(0, 280),
411
+ metadata: { feedId: f.Id, parent: f.ParentId }
412
+ });
413
+ }
414
+ }
415
+ events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
416
+ return { events, signals };
417
+ }
418
+ function formatSalesforceSignalsForPrompt(signals) {
419
+ if (signals.opportunitiesMoved === 0 && signals.tasksLogged === 0 && signals.stageTransitions === 0) {
420
+ return "";
421
+ }
422
+ const lines = [
423
+ "## Salesforce Activity (stated pipeline vs observed motion)",
424
+ ""
425
+ ];
426
+ if (signals.opportunitiesMoved > 0) {
427
+ const pipelineM = Math.round(signals.totalPipelineAmount / 1e4) / 100;
428
+ lines.push(
429
+ `${signals.opportunitiesMoved} opportunities touched in window ($${pipelineM}M total pipeline represented).`
430
+ );
431
+ lines.push(
432
+ `${signals.stageTransitions} stage changes, ${signals.amountChanges} amount changes.`
433
+ );
434
+ if (signals.closedWon + signals.closedLost > 0) {
435
+ lines.push(`${signals.closedWon} closed won, ${signals.closedLost} closed lost.`);
436
+ }
437
+ if (signals.topStages.length > 0) {
438
+ lines.push(
439
+ `Most active stages: ${signals.topStages.map((s) => `${s.stage} (${s.count})`).join(", ")}.`
440
+ );
441
+ }
442
+ }
443
+ if (signals.tasksLogged > 0) {
444
+ lines.push(
445
+ `${signals.tasksLogged} tasks logged (${signals.callsLogged} calls).`
446
+ );
447
+ }
448
+ if (signals.feedPostsByLeader > 0) {
449
+ lines.push(`${signals.feedPostsByLeader} feed posts by the leader.`);
450
+ }
451
+ lines.push("");
452
+ lines.push(
453
+ "Salesforce shows what the team said would happen (stages, forecast) vs. what is actually happening (movement, activity, close)."
454
+ );
455
+ lines.push(
456
+ "Compare stage transitions against stated strategy \u2014 are the deals the leader said would close actually moving, or is the pipeline drifting?"
457
+ );
458
+ return lines.join("\n");
459
+ }
460
+ async function soqlQuery(opts, query) {
461
+ const url = new URL(`${opts.instanceUrl}/services/data/v59.0/query`);
462
+ url.searchParams.set("q", query);
463
+ const res = await fetch(url.toString(), {
464
+ headers: { Authorization: `Bearer ${opts.accessToken}` }
465
+ });
466
+ if (!res.ok) {
467
+ throw new Error(
468
+ `Salesforce SOQL ${res.status}: ${(await res.text()).slice(0, 200)}`
469
+ );
470
+ }
471
+ const json = await res.json();
472
+ return json.records ?? [];
473
+ }
474
+ function escapeSoql(raw) {
475
+ return raw.replace(/['\\]/g, "");
476
+ }
477
+
79
478
  // src/radiant/index.ts
80
479
  var RADIANT_PACKAGE_VERSION = "0.0.0";
81
480
  export {
@@ -105,16 +504,20 @@ export {
105
504
  fetchDiscordActivity,
106
505
  fetchGitHubActivity,
107
506
  fetchGitHubOrgActivity,
507
+ fetchGoogleWorkspaceActivity,
108
508
  fetchLinearActivity,
109
509
  fetchNotionActivity,
510
+ fetchSalesforceActivity,
110
511
  fetchSlackActivity,
111
512
  filterEventsByUser,
112
513
  formatActiveWorlds,
113
514
  formatDiscordSignalsForPrompt,
114
515
  formatExocortexForPrompt,
516
+ formatGoogleWorkspaceSignalsForPrompt,
115
517
  formatLinearSignalsForPrompt,
116
518
  formatNotionSignalsForPrompt,
117
519
  formatPriorReadsForPrompt,
520
+ formatSalesforceSignalsForPrompt,
118
521
  formatScope,
119
522
  formatSlackSignalsForPrompt,
120
523
  formatTeamExocorticesForPrompt,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neuroverseos/governance",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "Deterministic governance engine for AI agents — enforce worlds (permanent rules) and plans (mission constraints) with full audit trace",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",