-
Notifications
You must be signed in to change notification settings - Fork 20
feat(apply): watcher admin-only fields + lobu export + examples roll-out #829
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
8063037
wip: feat/apply-watchers-full CLI work in progress
buremba ac6583d
feat(apply,examples): demonstrate new watcher fields across all 12 ex…
buremba 3b84cc1
fix(apply): tighten reaction_script validation + export skip-emit guard
buremba 5cedfca
fix(deps): refresh bun.lock after packages/web → packages/owletto rename
buremba 5657b6e
fix(server): register watcher scalar columns in QUERYABLE_SCHEMA
buremba File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
38 changes: 38 additions & 0 deletions
38
examples/agent-community/models/reactions/opportunity-matcher.reaction.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| /** | ||
| * Reaction for the `opportunity-matcher` watcher. | ||
| * | ||
| * Runs every 12h after the LLM scans member activity and produces a list of | ||
| * suggested matches. Persists each match as a `community_match` event so | ||
| * downstream consumers (intro-drafting agents, weekly digest, audit log) can | ||
| * iterate over a single source of truth instead of re-running the matcher. | ||
| */ | ||
| import type { ReactionContext } from "@lobu/connector-sdk"; | ||
|
|
||
| interface MatchData { | ||
| signals?: Array<{ | ||
| member_a: string; | ||
| member_b: string; | ||
| reason: string; | ||
| confidence?: number; | ||
| }>; | ||
| } | ||
|
|
||
| export default async (ctx: ReactionContext, client: any): Promise<void> => { | ||
| const data = ctx.extracted_data as MatchData; | ||
| const signals = data.signals ?? []; | ||
| if (signals.length === 0) return; | ||
|
|
||
| for (const s of signals) { | ||
| await client.knowledge.save({ | ||
| entity_ids: ctx.entities.map((e) => e.id), | ||
| content: `Match: ${s.member_a} ↔ ${s.member_b} — ${s.reason}`, | ||
| semantic_type: "community_match", | ||
| metadata: { | ||
| member_a: s.member_a, | ||
| member_b: s.member_b, | ||
| confidence: s.confidence ?? null, | ||
| window_id: ctx.window.id, | ||
| }, | ||
| }); | ||
| } | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
examples/atlas/models/reactions/catalog-staleness-checker.reaction.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| /** | ||
| * Reaction for atlas's `catalog-staleness-checker` watcher. | ||
| * | ||
| * Writes a `catalog_stale` event per stale entry the LLM identified. Atlas is | ||
| * a long-lived reference catalog — entries that haven't been re-verified in | ||
| * 90+ days are flagged so a curator can decide whether to refresh, retire, or | ||
| * leave them. | ||
| */ | ||
| import type { ReactionContext } from "@lobu/connector-sdk"; | ||
|
|
||
| interface StaleData { | ||
| stale_entries?: Array<{ | ||
| entity_type: string; | ||
| slug: string; | ||
| last_updated: string; | ||
| suggested_action: string; | ||
| }>; | ||
| } | ||
|
|
||
| export default async (ctx: ReactionContext, client: any): Promise<void> => { | ||
| const data = ctx.extracted_data as StaleData; | ||
| const stale = data.stale_entries ?? []; | ||
| if (stale.length === 0) return; | ||
|
|
||
| for (const s of stale) { | ||
| await client.knowledge.save({ | ||
| entity_ids: ctx.entities.map((e) => e.id), | ||
| content: `Stale ${s.entity_type}/${s.slug} — last updated ${s.last_updated}\n→ ${s.suggested_action}`, | ||
| semantic_type: "catalog_stale", | ||
| metadata: { | ||
| entity_type: s.entity_type, | ||
| slug: s.slug, | ||
| last_updated: s.last_updated, | ||
| window_id: ctx.window.id, | ||
| }, | ||
| }); | ||
| } | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
examples/finance/models/reactions/reconciliation-monitor.reaction.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| /** | ||
| * Reaction for the `reconciliation-monitor` watcher. | ||
| * | ||
| * Persists any variance flagged during the daily 6am sweep as a durable | ||
| * `variance_flag` event tied to the affected account. Downstream agents | ||
| * (close-of-month rollup, audit prep) consume these events instead of | ||
| * re-extracting variances from the raw transaction stream. | ||
| */ | ||
| import type { ReactionContext } from "@lobu/connector-sdk"; | ||
|
|
||
| interface ReconciliationData { | ||
| variances?: Array<{ | ||
| account: string; | ||
| amount: number; | ||
| direction: "over" | "under"; | ||
| reason: string; | ||
| }>; | ||
| unreconciled_count?: number; | ||
| } | ||
|
|
||
| export default async (ctx: ReactionContext, client: any): Promise<void> => { | ||
| const data = ctx.extracted_data as ReconciliationData; | ||
| const variances = data.variances ?? []; | ||
| if (variances.length === 0) return; | ||
|
|
||
| for (const v of variances) { | ||
| await client.knowledge.save({ | ||
| entity_ids: ctx.entities.map((e) => e.id), | ||
| content: `Variance ${v.direction} on ${v.account}: ${v.amount} — ${v.reason}`, | ||
| semantic_type: "variance_flag", | ||
| metadata: { | ||
| account: v.account, | ||
| amount: v.amount, | ||
| direction: v.direction, | ||
| window_id: ctx.window.id, | ||
| unreconciled_count: data.unreconciled_count ?? null, | ||
| }, | ||
| }); | ||
| } | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
examples/lobu-crm/models/reactions/funnel-digest.reaction.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| /** | ||
| * Reaction for the `funnel-digest` watcher. | ||
| * | ||
| * Runs after the weekly Monday-9am window completes. `ctx.extracted_data` is | ||
| * whatever the watcher's `extraction_schema` produced — funnel snapshot, top | ||
| * action, stale leads, etc. We persist the digest as a `funnel_digest` event | ||
| * linked to every lead the watcher knows about so the next digest can compare | ||
| * stage_counts week-over-week without re-running classification. | ||
| * | ||
| * Pair with `notification_priority: high` on the watcher — the OS notification | ||
| * fires regardless of whether this script succeeds; this just produces durable | ||
| * knowledge. | ||
| */ | ||
| import type { ReactionContext } from "@lobu/connector-sdk"; | ||
|
|
||
| interface DigestData { | ||
| top_action?: string; | ||
| stage_counts?: Record<string, number>; | ||
| conversations_this_week?: number; | ||
| gap?: string; | ||
| } | ||
|
|
||
| export default async (ctx: ReactionContext, client: any): Promise<void> => { | ||
| const data = ctx.extracted_data as DigestData; | ||
| const stageSummary = Object.entries(data.stage_counts ?? {}) | ||
| .map(([stage, n]) => `${stage}: ${n}`) | ||
| .join(", "); | ||
| const content = [ | ||
| `Weekly funnel digest — ${ctx.window.window_end.slice(0, 10)}`, | ||
| `Top action: ${data.top_action ?? "(none)"}`, | ||
| `Stages: ${stageSummary || "(empty)"}`, | ||
| `Conversations this week: ${data.conversations_this_week ?? 0}`, | ||
| data.gap ? `Gap: ${data.gap}` : null, | ||
| ] | ||
| .filter(Boolean) | ||
| .join("\n"); | ||
|
|
||
| await client.knowledge.save({ | ||
| // Attaching to the whole watcher's entity set keeps the digest scoped to | ||
| // CRM data and discoverable from any lead the watcher already touches. | ||
| entity_ids: ctx.entities.map((e) => e.id), | ||
| content, | ||
| semantic_type: "funnel_digest", | ||
| metadata: { | ||
| window_id: ctx.window.id, | ||
| watcher_slug: ctx.watcher.slug, | ||
| stage_counts: data.stage_counts ?? {}, | ||
| top_action: data.top_action ?? null, | ||
| }, | ||
| }); | ||
| }; |
44 changes: 44 additions & 0 deletions
44
examples/lobu-crm/models/reactions/inbound-triage.reaction.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| /** | ||
| * Reaction for the `inbound-triage` watcher. | ||
| * | ||
| * Fires every 2h after the watcher LLM extracts new and enriched leads from | ||
| * GitHub/X/HN signals. The script writes a `lead_interaction` event per | ||
| * recommended action so the next digest can count them — the watcher itself | ||
| * already creates the `lead` rows, so we don't duplicate that here. | ||
| */ | ||
| import type { ReactionContext } from "@lobu/connector-sdk"; | ||
|
|
||
| interface TriageData { | ||
| new_leads?: Array<{ | ||
| name: string; | ||
| source: string; | ||
| stage: string; | ||
| why?: string; | ||
| }>; | ||
| recommended_actions?: string[]; | ||
| notable?: boolean; | ||
| } | ||
|
|
||
| export default async (ctx: ReactionContext, client: any): Promise<void> => { | ||
| const data = ctx.extracted_data as TriageData; | ||
| // Nothing notable → nothing to persist. The watcher's prompt is explicit | ||
| // about not manufacturing noise; we mirror that here. | ||
| if (!data.notable) return; | ||
|
|
||
| const actions = data.recommended_actions ?? []; | ||
| if (actions.length === 0) return; | ||
|
|
||
| await client.knowledge.save({ | ||
| entity_ids: ctx.entities.map((e) => e.id), | ||
| content: [ | ||
| `Triage run ${ctx.window.window_end.slice(0, 16)} — ${actions.length} action(s)`, | ||
| ...actions.map((a, i) => `${i + 1}. ${a}`), | ||
| ].join("\n"), | ||
| semantic_type: "lead_interaction", | ||
| metadata: { | ||
| window_id: ctx.window.id, | ||
| new_lead_count: data.new_leads?.length ?? 0, | ||
| action_count: actions.length, | ||
| }, | ||
| }); | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
examples/market/models/reactions/founder-activity-tracker.reaction.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| /** | ||
| * Reaction for the `founder-activity-tracker` watcher. | ||
| * | ||
| * Records notable public activity (tweets, blog posts, hiring posts, fundraise | ||
| * rumors) as `founder_activity` events. The opportunity-matcher watcher reads | ||
| * these events to suggest cross-portfolio introductions. | ||
| */ | ||
| import type { ReactionContext } from "@lobu/connector-sdk"; | ||
|
|
||
| interface FounderActivityData { | ||
| signals?: Array<{ | ||
| founder: string; | ||
| activity_type: string; | ||
| summary: string; | ||
| importance?: "low" | "medium" | "high"; | ||
| }>; | ||
| } | ||
|
|
||
| export default async (ctx: ReactionContext, client: any): Promise<void> => { | ||
| const data = ctx.extracted_data as FounderActivityData; | ||
| const signals = data.signals ?? []; | ||
| // High-importance only — low-noise channel for the intel feed. | ||
| const notable = signals.filter((s) => s.importance === "high"); | ||
| if (notable.length === 0) return; | ||
|
|
||
| for (const s of notable) { | ||
| await client.knowledge.save({ | ||
| entity_ids: ctx.entities.map((e) => e.id), | ||
| content: `${s.founder} — ${s.activity_type}: ${s.summary}`, | ||
| semantic_type: "founder_activity", | ||
| metadata: { | ||
| founder: s.founder, | ||
| activity_type: s.activity_type, | ||
| importance: s.importance, | ||
| window_id: ctx.window.id, | ||
| }, | ||
| }); | ||
| } | ||
| }; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Align reaction payload fields with the watcher extraction schema.
MatchDataexpectsmember_a/member_b, but theopportunity-matcherwatcher schema emitsinterested_members+suggested_action. This will persist records with missing participant names at runtime.Suggested alignment patch
interface MatchData { signals?: Array<{ - member_a: string; - member_b: string; + interested_members?: string[]; reason: string; - confidence?: number; + suggested_action?: string; }>; } export default async (ctx: ReactionContext, client: any): Promise<void> => { @@ for (const s of signals) { + const [member_a, member_b] = s.interested_members ?? []; + if (!member_a || !member_b) continue; + await client.knowledge.save({ entity_ids: ctx.entities.map((e) => e.id), - content: `Match: ${s.member_a} ↔ ${s.member_b} — ${s.reason}`, + content: `Match: ${member_a} ↔ ${member_b} — ${s.reason}`, semantic_type: "community_match", metadata: { - member_a: s.member_a, - member_b: s.member_b, - confidence: s.confidence ?? null, + member_a, + member_b, + suggested_action: s.suggested_action ?? null, window_id: ctx.window.id, }, }); } };Also applies to: 28-33
🤖 Prompt for AI Agents