Skip to content

refactor(connectors): consolidate V1 IPC, prune V0 taxonomy, unify compile pipeline#931

Merged
buremba merged 13 commits into
mainfrom
feat/simplify-connector-stack
May 19, 2026
Merged

refactor(connectors): consolidate V1 IPC, prune V0 taxonomy, unify compile pipeline#931
buremba merged 13 commits into
mainfrom
feat/simplify-connector-stack

Conversation

@buremba
Copy link
Copy Markdown
Member

@buremba buremba commented May 19, 2026

Summary

Multi-round simplification on packages/connector-sdk + packages/connector-worker + packages/connectors. Cumulative ~450 LOC net deletion across 45 files. Includes one runtime-break fix that make build-packages would have shipped to prod.

Key changes

  • Magic-key IPC protocol gone. Pre-spike, gateway packed V1 shapes into options.__action_key / options.__auth_mode / options.__feed_key / options.__entity_ids / options.__auth_config; child-runner unpacked via stripInternalOptions. Invisible to every type. Now: ExecutorJob is a discriminated union (mode: 'sync' | 'action' | 'authenticate') carrying V1 SDK shapes verbatim.
  • V0 parallel taxonomy deleted. connector-sdk/src/types.ts previously shipped a V0 Content / Checkpoint / FeedSyncResult / FeedOptions / SessionState family that nothing under packages/connectors/ actually used (31/31 connectors are V1). Deleted.
  • CLI runtime break fixed. connector-run-cmd.ts previously passed flat mode/config/checkpoint/... + old onContentChunk/collectContents and read result.contents — would have failed in production after the IPC migration. make build-packages was bundler-only and didn't catch it. Migrated to V1 contract + verified by built-dist round-trip test (synthetic 5-line connector with throwing sync(); asserted error round-trips through onEventChunk).
  • 3 esbuild compile pipelines → 1. connector-worker/src/compile-connector.ts, cli/src/commands/_lib/connector-loader.ts, server/src/utils/connector-catalog.ts previously each had their own LRU caches + esbuild config. Consolidated behind @lobu/connector-worker/compile.
  • 19 boilerplate execute() stubs deleted from packages/connectors/. ConnectorRuntime.execute() defaults to { success: false, error: 'Actions not supported' }.
  • Sync executor result is streaming-only. ExecutorResult advertised events for sync but child-runner always returned events: []. Removed.
  • Server→worker source leaks closed. server.ts + connector-compiler.ts previously reached into worker source via relative imports; now go through @lobu/connector-worker/compile.

Process

Built across 6 rounds of spike commits driven by codex review. Final codex verdict: ENOUGH. All wip(spike): commit history preserved on branch — squash on merge.

Test plan

  • make typecheck clean
  • bun test packages/connector-worker/integration-tests clean (8/8)
  • lobu connector run <key> against a compiled connector works (CLI runtime regression coverage)

Summary by CodeRabbit

  • Refactor

    • Centralized connector compilation into a shared compilation pipeline used by server, CLI, and workers.
    • Reworked connector execution to a unified, job-based protocol for sync/action/auth modes.
    • Connectors now inherit a default "actions not supported" handler unless they implement actions.
  • Documentation

    • Updated Quick Start and worker sandbox docs to reflect new compilation and runtime behavior.

Review Change Stack

@buremba buremba enabled auto-merge (squash) May 19, 2026 18:40
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 866707b6-5a4a-4d76-bd7e-59ac124b049b

📥 Commits

Reviewing files that changed from the base of the PR and between a3c18d3 and 8563dc3.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (45)
  • .github/workflows/ci.yml
  • packages/cli/src/commands/_lib/connector-loader.ts
  • packages/cli/src/commands/_lib/connector-run-cmd.ts
  • packages/connector-sdk/src/connector-runtime.ts
  • packages/connector-sdk/src/connector-types.ts
  • packages/connector-sdk/src/event-taxonomy.ts
  • packages/connector-sdk/src/index.ts
  • packages/connector-sdk/src/types.ts
  • packages/connector-worker/integration-tests/subprocess-source-mode.test.ts
  • packages/connector-worker/integration-tests/subprocess.test.ts
  • packages/connector-worker/package.json
  • packages/connector-worker/src/__tests__/executor-heartbeat.test.ts
  • packages/connector-worker/src/compile-connector.ts
  • packages/connector-worker/src/compile/index.ts
  • packages/connector-worker/src/daemon/executor.ts
  • packages/connector-worker/src/executor/child-runner.ts
  • packages/connector-worker/src/executor/interface.ts
  • packages/connector-worker/src/executor/runtime.ts
  • packages/connector-worker/src/executor/subprocess.ts
  • packages/connectors/src/README.md
  • packages/connectors/src/capterra.ts
  • packages/connectors/src/g2.ts
  • packages/connectors/src/glassdoor.ts
  • packages/connectors/src/gmaps.ts
  • packages/connectors/src/google_play.ts
  • packages/connectors/src/hackernews.ts
  • packages/connectors/src/ios_appstore.ts
  • packages/connectors/src/linkedin.ts
  • packages/connectors/src/microsoft_outlook.ts
  • packages/connectors/src/producthunt.ts
  • packages/connectors/src/reddit.ts
  • packages/connectors/src/revolut.ts
  • packages/connectors/src/rss.ts
  • packages/connectors/src/spotify.ts
  • packages/connectors/src/trustpilot.ts
  • packages/connectors/src/website.ts
  • packages/connectors/src/whatsapp.ts
  • packages/connectors/src/x.ts
  • packages/connectors/src/youtube.ts
  • packages/server/package.json
  • packages/server/src/lib/feed-sync.ts
  • packages/server/src/server.ts
  • packages/server/src/tools/admin/manage_operations.ts
  • packages/server/src/utils/connector-catalog.ts
  • packages/server/src/utils/connector-compiler.ts
💤 Files with no reviewable changes (21)
  • packages/connectors/src/trustpilot.ts
  • packages/connectors/src/microsoft_outlook.ts
  • packages/connectors/src/youtube.ts
  • packages/connectors/src/spotify.ts
  • packages/connectors/src/rss.ts
  • packages/connector-sdk/src/event-taxonomy.ts
  • packages/connectors/src/revolut.ts
  • packages/connectors/src/linkedin.ts
  • packages/connectors/src/glassdoor.ts
  • packages/connectors/src/reddit.ts
  • packages/connectors/src/website.ts
  • packages/connectors/src/whatsapp.ts
  • packages/connectors/src/google_play.ts
  • packages/connectors/src/producthunt.ts
  • packages/connectors/src/g2.ts
  • packages/connector-sdk/src/connector-types.ts
  • packages/connectors/src/hackernews.ts
  • packages/connectors/src/x.ts
  • packages/connectors/src/capterra.ts
  • packages/connectors/src/gmaps.ts
  • packages/connectors/src/ios_appstore.ts
🚧 Files skipped from review as they are similar to previous changes (20)
  • packages/server/package.json
  • packages/connector-worker/integration-tests/subprocess-source-mode.test.ts
  • packages/server/src/lib/feed-sync.ts
  • packages/server/src/server.ts
  • packages/server/src/tools/admin/manage_operations.ts
  • packages/connector-worker/package.json
  • packages/connector-sdk/src/types.ts
  • packages/connector-worker/integration-tests/subprocess.test.ts
  • packages/connector-sdk/src/connector-runtime.ts
  • packages/connector-worker/src/executor/runtime.ts
  • packages/cli/src/commands/_lib/connector-run-cmd.ts
  • packages/connectors/src/README.md
  • packages/connector-worker/src/executor/subprocess.ts
  • packages/connector-worker/src/executor/interface.ts
  • packages/server/src/utils/connector-catalog.ts
  • packages/connector-sdk/src/index.ts
  • packages/connector-worker/src/daemon/executor.ts
  • packages/connector-worker/src/tests/executor-heartbeat.test.ts
  • packages/connector-worker/src/executor/child-runner.ts
  • packages/connector-worker/src/compile/index.ts

📝 Walkthrough

Walkthrough

This PR centralizes connector compilation in a shared compile module, changes the executor contract from context/contents to mode-discriminated job/result with event streaming, updates CLI/server/daemon/subprocess/child-runner to the new contract, makes ConnectorRuntime.execute() provide a default, and prunes SDK exports and redundant connector action stubs.

Changes

Executor Contract Refactor & Shared Compilation Pipeline

Layer / File(s) Summary
Shared connector compilation module & exports
packages/connector-worker/src/compile/index.ts, packages/connector-worker/package.json
Adds shared compile utilities: key validation and directory search, npm: import plugin, createConnectorCompiler with esbuild bundling and mtime LRU caching; exposes ./compile export.
Worker/CLI/Server compile delegations
packages/connector-worker/src/compile-connector.ts, packages/cli/src/commands/_lib/connector-loader.ts, packages/server/src/utils/connector-catalog.ts
Delegates file resolution and compilation to shared compile factory; re-exports compileConnectorFromFile and configures onUnresolvedNpm handling.
Executor interface: job/result + hooks
packages/connector-worker/src/executor/interface.ts
Replaces SyncContext/FeedSyncResult with ExecutorJob/ExecutorResult unions; ExecutionHooks streams EventEnvelope[] via onEventChunk; removes collectContents.
Executor runtime entrypoint
packages/connector-worker/src/executor/runtime.ts
executeCompiledConnector now delegates executor.execute(compiledCode, job, hooks) and returns ExecutorResult; removes old helpers.
Subprocess executor IPC & job handling
packages/connector-worker/src/executor/subprocess.ts
Sends {compiledCode, job} to child, builds child env from job.env, handles event_chunk IPC via hooks.onEventChunk, resolves with ExecutorResult (tracks checkpoint for sync).
Child-side runtime dispatch
packages/connector-worker/src/executor/child-runner.ts
Unpacks {compiledCode, job} and routes authenticate/action/sync in executeConnectorRuntime; streams sync events via event_chunk and returns normalized results.
Daemon executor wiring & helpers
packages/connector-worker/src/daemon/executor.ts
Sends ExecutorJob objects for sync/action/authenticate; switches streaming hook to onEventChunk; introduces mergeEnv/processEvent helpers and validates result modes.
CLI run: collect events via onEventChunk
packages/cli/src/commands/_lib/connector-run-cmd.ts
Aggregates events via onEventChunk, calls executeCompiledConnector with job+hooks, asserts sync mode, and builds artifacts and summary from collected events.
Server feed-run & admin inline action
packages/server/src/lib/feed-sync.ts, packages/server/src/tools/admin/manage_operations.ts
Calls executeCompiledConnector from package entrypoint with job objects, counts items via event chunks, and reads result fields guarded by result.mode checks.
Integration & unit tests
packages/connector-worker/integration-tests/*, packages/connector-worker/src/__tests__/*
Tests updated to pass ExecutorJob and to expect ExecutorResult/action output shapes; mocks adjusted accordingly.

ConnectorRuntime Default Execute & SDK Export Pruning

Layer / File(s) Summary
ConnectorRuntime default execute
packages/connector-sdk/src/connector-runtime.ts
Makes execute() concrete, returning { success: false, error: 'Actions not supported' } by default; docs updated.
Remove redundant connector execute overrides
packages/connectors/src/*.ts
Removes per-connector execute() stubs and related ActionContext/ActionResult imports so connectors rely on base default when actions are unsupported.
SDK export surface narrowing
packages/connector-sdk/src/{connector-types.ts,event-taxonomy.ts,types.ts,index.ts}
Removes FeedMode, native event taxonomy helpers, and several platform model types; narrows index re-exports to minimal surface (e.g., Env).
Docs & CI
packages/connectors/src/README.md, .github/workflows/ci.yml
Docs updated to reflect default execute behavior and compile sourcing; CI build step extended to include embeddings and connector-worker.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • lobu-ai/lobu#772: Related earlier work on shared connector compilation and findBundledConnectorFile behavior.
  • lobu-ai/lobu#828: Changes to bundled-file resolution that touch the same resolver paths.
  • lobu-ai/lobu#820: Related refactor touching daemon executor job-mode execution flow.

Suggested labels

skip-size-check

Poem

"I hopped through code with careful paws,
Bundled compiles and job-mode laws.
Events streamed in tidy chunks,
Old context gone — no fragile trunks.
A rabbit cheers for tidy runs! 🐰"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title accurately describes the main changes: refactoring to consolidate the V1 IPC protocol, remove unused V0 taxonomy, and unify the compilation pipeline across connector packages.
Description check ✅ Passed The pull request description is comprehensive and well-structured, covering the summary, key changes, process, and test plan as required by the template.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/simplify-connector-stack

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter
Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/connector-worker/src/executor/child-runner.ts (1)

128-174: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use the streamed event count for the items_found fallback.

When syncResult.metadata?.items_found is absent, this falls back to trailingEvents.length. Any connector that only uses emitEvents() and returns no events array will therefore report 0 items found despite having streamed events successfully.

Suggested fix
   // mode === 'sync'
+  let emittedEventCount = 0;
   const emitEvents = async (events: EventEnvelope[]) => {
+    emittedEventCount += events.length;
     for (let index = 0; index < events.length; index += EVENT_CHUNK_SIZE) {
       await sendIPC({
         type: 'event_chunk',
         events: events.slice(index, index + EVENT_CHUNK_SIZE),
       });
@@
     metadata: {
       items_found:
         typeof syncResult?.metadata?.items_found === 'number'
           ? syncResult.metadata.items_found
-          : trailingEvents.length,
+          : emittedEventCount,
       items_skipped:
         typeof syncResult?.metadata?.items_skipped === 'number'
           ? syncResult.metadata.items_skipped
           : 0,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/connector-worker/src/executor/child-runner.ts` around lines 128 -
174, The fallback for metadata.items_found currently uses trailingEvents.length
which misses events streamed via emitEvents; declare a counter (e.g.,
streamedEventsCount = 0) in the same scope, increment it inside emitEvents by
events.length before sending chunks, and then change the items_found fallback to
use streamedEventsCount (or streamedEventsCount || trailingEvents.length) when
syncResult?.metadata?.items_found is absent; reference emitEvents,
trailingEvents, syncResult, and metadata in your change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/connector-worker/src/executor/subprocess.ts`:
- Around line 318-328: The handler currently trusts msg.result after a bare cast
and directly calls settle(() => resolve(result)); add explicit validation of the
child result object (the msg.type === 'result' branch) before resolving: ensure
the object has a valid mode string (e.g., 'sync' or 'async') and that required
fields for each mode are present (for example, when result.mode === 'sync'
validate result.checkpoint exists and is well-formed; validate any auth/token
fields if expected), and if validation fails, call settle(() => reject(new
Error(...))) or otherwise handle the malformed payload (log and ignore) instead
of resolving; implement this as a small type-guard/validator used right before
the code that sets latestCheckpoint and calls settle(() => resolve(result)) so
only verified ExecutorResult objects are accepted.

In `@packages/server/src/tools/admin/manage_operations.ts`:
- Around line 215-223: The job config currently builds only from envStrings and
connectionCredentials, which drops saved per-connection settings; update the job
config creation in manage_operations.ts (the job: { config: ... } block) to
merge connection.config into job.config using the same helper/merge/precedence
you use for feed execution so connection.config values are preserved (e.g.,
combine connection.config, envStrings, and connectionCredentials in the same
order/merge function you use elsewhere).

In `@packages/server/src/utils/connector-catalog.ts`:
- Around line 5-8: The imports createConnectorCompiler and
findBundledConnectorFile (aliased as findInDirs) from
'`@lobu/connector-worker/compile`' fail because the compile subpath dist artifacts
are missing; before merging, build the connector-worker package so that
packages/connector-worker/dist/compile exists by running the repo build target
(e.g., make build-packages) to compile TypeScript from src → dist, then verify
the compiled export map resolves so imports used in connector-catalog.ts (and
other consumers like connector-compiler.ts, server.ts, CLI) can be resolved at
runtime.

---

Outside diff comments:
In `@packages/connector-worker/src/executor/child-runner.ts`:
- Around line 128-174: The fallback for metadata.items_found currently uses
trailingEvents.length which misses events streamed via emitEvents; declare a
counter (e.g., streamedEventsCount = 0) in the same scope, increment it inside
emitEvents by events.length before sending chunks, and then change the
items_found fallback to use streamedEventsCount (or streamedEventsCount ||
trailingEvents.length) when syncResult?.metadata?.items_found is absent;
reference emitEvents, trailingEvents, syncResult, and metadata in your change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2fdeb497-de7c-437a-81bd-304eb60cf50b

📥 Commits

Reviewing files that changed from the base of the PR and between 57cd312 and a3c18d3.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (44)
  • packages/cli/src/commands/_lib/connector-loader.ts
  • packages/cli/src/commands/_lib/connector-run-cmd.ts
  • packages/connector-sdk/src/connector-runtime.ts
  • packages/connector-sdk/src/connector-types.ts
  • packages/connector-sdk/src/event-taxonomy.ts
  • packages/connector-sdk/src/index.ts
  • packages/connector-sdk/src/types.ts
  • packages/connector-worker/integration-tests/subprocess-source-mode.test.ts
  • packages/connector-worker/integration-tests/subprocess.test.ts
  • packages/connector-worker/package.json
  • packages/connector-worker/src/__tests__/executor-heartbeat.test.ts
  • packages/connector-worker/src/compile-connector.ts
  • packages/connector-worker/src/compile/index.ts
  • packages/connector-worker/src/daemon/executor.ts
  • packages/connector-worker/src/executor/child-runner.ts
  • packages/connector-worker/src/executor/interface.ts
  • packages/connector-worker/src/executor/runtime.ts
  • packages/connector-worker/src/executor/subprocess.ts
  • packages/connectors/src/README.md
  • packages/connectors/src/capterra.ts
  • packages/connectors/src/g2.ts
  • packages/connectors/src/glassdoor.ts
  • packages/connectors/src/gmaps.ts
  • packages/connectors/src/google_play.ts
  • packages/connectors/src/hackernews.ts
  • packages/connectors/src/ios_appstore.ts
  • packages/connectors/src/linkedin.ts
  • packages/connectors/src/microsoft_outlook.ts
  • packages/connectors/src/producthunt.ts
  • packages/connectors/src/reddit.ts
  • packages/connectors/src/revolut.ts
  • packages/connectors/src/rss.ts
  • packages/connectors/src/spotify.ts
  • packages/connectors/src/trustpilot.ts
  • packages/connectors/src/website.ts
  • packages/connectors/src/whatsapp.ts
  • packages/connectors/src/x.ts
  • packages/connectors/src/youtube.ts
  • packages/server/package.json
  • packages/server/src/lib/feed-sync.ts
  • packages/server/src/server.ts
  • packages/server/src/tools/admin/manage_operations.ts
  • packages/server/src/utils/connector-catalog.ts
  • packages/server/src/utils/connector-compiler.ts
💤 Files with no reviewable changes (21)
  • packages/connector-sdk/src/event-taxonomy.ts
  • packages/connectors/src/glassdoor.ts
  • packages/connectors/src/revolut.ts
  • packages/connectors/src/whatsapp.ts
  • packages/connectors/src/youtube.ts
  • packages/connectors/src/rss.ts
  • packages/connectors/src/google_play.ts
  • packages/connectors/src/capterra.ts
  • packages/connectors/src/hackernews.ts
  • packages/connector-sdk/src/connector-types.ts
  • packages/connectors/src/g2.ts
  • packages/connectors/src/ios_appstore.ts
  • packages/connectors/src/microsoft_outlook.ts
  • packages/connectors/src/trustpilot.ts
  • packages/connectors/src/spotify.ts
  • packages/connectors/src/x.ts
  • packages/connectors/src/producthunt.ts
  • packages/connectors/src/linkedin.ts
  • packages/connectors/src/reddit.ts
  • packages/connectors/src/gmaps.ts
  • packages/connectors/src/website.ts

Comment on lines 318 to +328
if (msg.type === 'result') {
terminalMessageReceived = true;
const result = msg.result as FeedSyncResult;
const result = msg.result as ExecutorResult;
queueTask(async () => {
finalMetadata = result.metadata;
finalAuthUpdate = result.auth_update;
finalAuthResult = result.auth_result;
latestCheckpoint = result.checkpoint;

if (Array.isArray(result.contents) && result.contents.length > 0) {
if (collectContents) {
collectedContents.push(...result.contents);
}
await hooks?.onContentChunk?.(result.contents);
// For sync results, surface the trailing checkpoint to callers
// through the result; in-flight `checkpoint_update` messages have
// already been forwarded via the hook.
if (result.mode === 'sync') {
latestCheckpoint = result.checkpoint;
}

settle(() =>
resolve({
contents: collectedContents,
checkpoint: latestCheckpoint,
metadata: finalMetadata,
auth_update: finalAuthUpdate,
auth_result: finalAuthResult,
})
);
settle(() => resolve(result));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate child result messages before resolving them.

This path now trusts msg.result after a bare cast. Since connector code shares the child process, it can send an arbitrary { type: 'result' } payload and hand the parent a malformed ExecutorResult or bogus checkpoint/auth data. Please hard-validate mode and its required fields at this boundary before calling resolve(...).

Suggested guard
         if (msg.type === 'result') {
           terminalMessageReceived = true;
-          const result = msg.result as ExecutorResult;
+          const result = msg.result;
           queueTask(async () => {
-            // For sync results, surface the trailing checkpoint to callers
-            // through the result; in-flight `checkpoint_update` messages have
-            // already been forwarded via the hook.
-            if (result.mode === 'sync') {
-              latestCheckpoint = result.checkpoint;
-            }
-            settle(() => resolve(result));
+            if (!result || typeof result !== 'object' || typeof result.mode !== 'string') {
+              throw new Error('Child returned an invalid executor result');
+            }
+            if (result.mode === 'sync') {
+              latestCheckpoint =
+                typeof result.checkpoint === 'object' || result.checkpoint === null
+                  ? result.checkpoint
+                  : null;
+            } else if (result.mode === 'action') {
+              if (!result.output || typeof result.output !== 'object') {
+                throw new Error('Child returned an invalid action result');
+              }
+            } else if (result.mode === 'authenticate') {
+              if (!result.auth || typeof result.auth !== 'object') {
+                throw new Error('Child returned an invalid auth result');
+              }
+            } else {
+              throw new Error(`Child returned unknown mode: ${String(result.mode)}`);
+            }
+            settle(() => resolve(result as ExecutorResult));
           });
           return;
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (msg.type === 'result') {
terminalMessageReceived = true;
const result = msg.result as FeedSyncResult;
const result = msg.result as ExecutorResult;
queueTask(async () => {
finalMetadata = result.metadata;
finalAuthUpdate = result.auth_update;
finalAuthResult = result.auth_result;
latestCheckpoint = result.checkpoint;
if (Array.isArray(result.contents) && result.contents.length > 0) {
if (collectContents) {
collectedContents.push(...result.contents);
}
await hooks?.onContentChunk?.(result.contents);
// For sync results, surface the trailing checkpoint to callers
// through the result; in-flight `checkpoint_update` messages have
// already been forwarded via the hook.
if (result.mode === 'sync') {
latestCheckpoint = result.checkpoint;
}
settle(() =>
resolve({
contents: collectedContents,
checkpoint: latestCheckpoint,
metadata: finalMetadata,
auth_update: finalAuthUpdate,
auth_result: finalAuthResult,
})
);
settle(() => resolve(result));
if (msg.type === 'result') {
terminalMessageReceived = true;
const result = msg.result;
queueTask(async () => {
if (!result || typeof result !== 'object' || typeof result.mode !== 'string') {
throw new Error('Child returned an invalid executor result');
}
if (result.mode === 'sync') {
latestCheckpoint =
typeof result.checkpoint === 'object' || result.checkpoint === null
? result.checkpoint
: null;
} else if (result.mode === 'action') {
if (!result.output || typeof result.output !== 'object') {
throw new Error('Child returned an invalid action result');
}
} else if (result.mode === 'authenticate') {
if (!result.auth || typeof result.auth !== 'object') {
throw new Error('Child returned an invalid auth result');
}
} else {
throw new Error(`Child returned unknown mode: ${String(result.mode)}`);
}
settle(() => resolve(result as ExecutorResult));
});
return;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/connector-worker/src/executor/subprocess.ts` around lines 318 - 328,
The handler currently trusts msg.result after a bare cast and directly calls
settle(() => resolve(result)); add explicit validation of the child result
object (the msg.type === 'result' branch) before resolving: ensure the object
has a valid mode string (e.g., 'sync' or 'async') and that required fields for
each mode are present (for example, when result.mode === 'sync' validate
result.checkpoint exists and is well-formed; validate any auth/token fields if
expected), and if validation fails, call settle(() => reject(new Error(...))) or
otherwise handle the malformed payload (log and ignore) instead of resolving;
implement this as a small type-guard/validator used right before the code that
sets latestCheckpoint and calls settle(() => resolve(result)) so only verified
ExecutorResult objects are accepted.

Comment on lines +215 to +223
job: {
mode: 'action',
actionKey:
operation.backend_config.backend === 'local_action'
? operation.backend_config.actionKey
: operation.operation_key,
actionInput,
config: { ...envStrings, ...connectionCredentials },
env: envStrings,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep connection.config in the action job config.

This path now sends only env strings plus resolved credentials. Any local action that reads saved per-connection settings from context.config will start running as if the connection were unconfigured. Merge connection.config into job.config here, ideally with the same helper/precedence you use for feed execution.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/server/src/tools/admin/manage_operations.ts` around lines 215 - 223,
The job config currently builds only from envStrings and connectionCredentials,
which drops saved per-connection settings; update the job config creation in
manage_operations.ts (the job: { config: ... } block) to merge connection.config
into job.config using the same helper/merge/precedence you use for feed
execution so connection.config values are preserved (e.g., combine
connection.config, envStrings, and connectionCredentials in the same order/merge
function you use elsewhere).

Comment thread packages/server/src/utils/connector-catalog.ts
buremba added 13 commits May 19, 2026 20:09
Removes re-exports/types with zero consumers across packages/, examples/,
scripts/, config/ outside of the SDK itself:

- FeedMode enum (`'sync' | 'virtual'`) — only ever lived in the SDK
- MatchStrategy — used internally inside identity-types but never imported
  externally; keep the value, drop the re-export
- isSourceNativeEventType / SOURCE_NATIVE_EVENT_TYPES + event-taxonomy.ts —
  whole file existed only to be re-exported, no consumers anywhere
- ReactionEntity — embedded in ReactionContext; nothing imports it directly
- Stealth/CDP browser re-exports (humanWait, randomScroll, getRandomDelay,
  testBotDetection, StealthBrowser, launchStealthBrowser, withErrorCapture,
  withHttpRetry, BrowserAuthCascadeError, BrowserNetworkConfig,
  discoverChromeListeningPorts, discoverChromeProcessCdpUrls, normalizeCdpUrl,
  tryWebSocketCdp) — kept internal to the SDK where launcher/acquire use them;
  the index just stops claiming they're public surface.

Rationale: SDK is the public surface third-party connectors consume. Every
re-export is a backwards-compat obligation. Verified zero external uses with
`rg -l "\bSYM\b" packages/ --type=ts | grep -v connector-sdk/src` per symbol
before deletion. Typecheck clean: packages/connector-sdk, packages/connectors
(modulo pre-existing whatsapp baileys-typings drift), packages/connector-worker,
packages/cli, packages/server (modulo pre-existing guardrails/aggregator.ts
SkillPreToolGuardrail breakage on main).
…r IPC

The connector-worker executor stack was a multi-hop adapter that
repackaged the V1 ConnectorRuntime contract (SyncContext/ActionContext/
AuthContext + EventEnvelope) into a legacy "feed/options" shape with
magic underscore-prefixed keys (__action_key, __auth_mode, __feed_key,
__entity_ids, __auth_config, __auth_previous_credentials), serialised
that over IPC, unpacked it back to V1 in the child runner, then packed
the result into a legacy `FeedSyncResult { contents: Content[] }`,
serialised that, unpacked it in the daemon executor, and re-normalised
each item a second time inside `processContent`.

Concretely deleted:

- `Content`, `Checkpoint`, `FeedOptions`, `FeedSyncResult`, `SessionState`
  from `packages/connector-sdk/src/types.ts` (only `Env` remains — it has
  20+ downstream consumers in the gateway). The SDK now exposes one
  taxonomy: `SyncContext` / `ActionContext` / `AuthContext` /
  `EventEnvelope` from `connector-types.ts`.
- `buildConnectorExecutionContext`, `mergeExecutionSessionState`,
  `mergeExecutionEnv`, `normalizeEventEnvelope`, `getActionOutput` from
  `connector-worker/src/executor/runtime.ts` (~120 LOC of pack/unpack).
- The `__action_key` / `__auth_mode` / `__feed_key` / `__entity_ids` /
  `__auth_config` / `__auth_previous_credentials` / `stripInternalOptions`
  protocol on the IPC boundary.
- The double `normalizeEventEnvelope` call (child runner already
  normalises, daemon's `processContent` then normalised again on the
  resulting `Content` — a no-op pass on already-normalised fields).

The new contract: `ExecutorJob` is a discriminated union (`mode: 'sync' |
'action' | 'authenticate'`) carrying the V1 SDK shapes verbatim, and
`ExecutorResult` mirrors `SyncResult` / `ActionResult` / `AuthResult`
the same way. `child-runner.ts` reads the job and dispatches; the
parent's `subprocess.ts` is mode-agnostic (it just plumbs events,
checkpoints, and auth artifacts through hooks).

Net diff: -242 LOC across the in-scope packages (sdk + worker), and the
result/return paths from `executeSyncRun`, `executeActionRun`,
`executeAuthRun` collapse from "consume legacy FeedSyncResult and unpack
the right slot" to mode-narrowed direct access.

Typecheck status:
- packages/connector-sdk:     clean
- packages/connector-worker:  clean
- packages/connectors:        clean (pre-existing whatsapp baileys-typings
                              drift in src/whatsapp.ts only)
- packages/cli:               clean
- packages/server:            two BREAKING call-sites under
                              packages/server (out of spike scope):
                                src/lib/feed-sync.ts (executeCompiledConnector
                                still passing the flat legacy params)
                                src/tools/admin/manage_operations.ts
                                (getActionOutput re-export gone)
                              These reach across the package boundary
                              into connector-worker internals via
                              relative path / dynamic import — flagged
                              as architectural leak in REPORT.md.
                              Updating them is a one-screen rewrite to
                              the new `{job: {mode, ...}}` signature plus
                              `result.output` / `result.events` mode
                              narrowing. Left for a focused follow-up
                              PR so the spike stays within the read-only
                              constraint on out-of-scope packages.

The legacy test (`executor-heartbeat.test.ts`) was updated to the new
shape; the underlying `bun test` integration cannot run in this worktree
(unrelated `@lobu/embeddings` module-resolution issue that reproduces on
the pre-spike commit too, recorded in REPORT.md).
…sites

Updates the two server callers that reached into `connector-worker`'s
executor internals via relative path + dynamic import (flagged as an
abstraction leak in REPORT.md spike 2):

- `packages/server/src/lib/feed-sync.ts:7` was importing from
  `'../../../connector-worker/src/executor/runtime'` — now goes through
  the proper `@lobu/connector-worker/executor/runtime` package export.
  The sync call switches to the new `{ compiledCode, job: { mode: 'sync',
  ... }, hooks }` signature; item-count tracking moves from
  `result.contents.length` to an `onEventChunk` counter (events are
  streamed via the hook now, not collected in the result).

- `packages/server/src/tools/admin/manage_operations.ts:207` used a
  dynamic `await import("../../../../connector-worker/...")` to grab
  `executeCompiledConnector` + `getActionOutput`. The latter no longer
  exists (spike 2 deleted it), so this call-site goes through
  `@lobu/connector-worker/executor/runtime` for `executeCompiledConnector`
  alone and mode-narrows directly on `result.output`.

Typecheck status:
- packages/connector-sdk:     clean
- packages/connector-worker:  clean
- packages/connectors:        clean (modulo pre-existing whatsapp/baileys
                              drift on main)
- packages/cli:               clean
- packages/server:            clean (modulo pre-existing
                              gateway/guardrails/aggregator.ts errors on
                              main)

`make build-packages` was run to refresh the connector-worker .d.ts so
the new exports resolve through the package boundary instead of through
the workspace .ts sources.
…te stubs

Most connectors don't expose actions — `rg -c "Actions not supported"
packages/connectors/src` returned 19 identical paste-jobs of

    async execute(_ctx: ActionContext): Promise<ActionResult> {
      return { success: false, error: 'Actions not supported' };
    }

(one minor variant: whatsapp's `'Actions not supported in v1.'`, and
revolut's tab-indented double-quoted version). Parallel to how
`authenticate()` already had a default-throws implementation, this turns
`execute()` from `abstract` into a default-rejecting method on the base
class. Connectors that don't declare any `actions` in their
`ConnectorDefinition` no longer need to write the stub.

Concrete deltas:
- `packages/connector-sdk/src/connector-runtime.ts`: `execute` becomes a
  non-abstract method returning `{ success: false, error: 'Actions not
  supported' }`. Doc comment updated to call out the new optionality.
- 19 connectors lose the 3-line stub and the dead
  `type ActionContext, type ActionResult` imports it required:
    capterra, g2, glassdoor, gmaps, google_play, hackernews,
    ios_appstore, linkedin, microsoft_outlook, producthunt, reddit,
    revolut, rss, spotify, trustpilot, website, whatsapp (kept Auth
    imports for `authenticate`), x, youtube.

Net: -114 LOC across packages/connectors, +10 LOC in the SDK base class.

Compatibility checks:
- `child-runner.ts findRuntimeClass` checks `prototype?.sync` AND
  `prototype?.execute`. The inherited default on `ConnectorRuntime.prototype`
  still satisfies `Foo.prototype.execute` access (prototype-chain lookup),
  so the discovery heuristic continues to accept these subclasses.
- The 8 device-only "throws BRIDGE_ONLY from execute()" stubs in
  apple_health, apple_photos, apple_screen_time, chrome,
  chrome_bookmarks, chrome_downloads, chrome_history, local_directory,
  whatsapp_local are NOT touched — those communicate "this connector
  only runs on a device worker" (a different semantic from "actions not
  supported") and the user's brief was specifically the 16+ "Actions not
  supported" pastes.

Typecheck status (after `bun run build` in connector-sdk to regenerate
.d.ts so connector-package consumers see the new default):
- packages/connector-sdk:    clean
- packages/connector-worker: clean
- packages/connectors:       126 errors — IDENTICAL count to pre-spike
                             baseline (verified via git stash + recount).
                             All are the pre-existing whatsapp baileys
                             typings drift plus the dom-lib / .ts-import-
                             extension / unknown-type drift in capterra,
                             g2, glassdoor, etc. on main. None are caused
                             by this spike.
- packages/cli:              clean
- packages/server:           clean (modulo pre-existing
                             gateway/guardrails/aggregator.ts errors).
Three packages each shipped their own near-identical copy of
`findBundledConnectorFile` + `compileConnectorFromFile` + the `npm:`
specifier esbuild plugin + the `lobu`/`@lobu/connector-sdk` aliasing +
the mtime-keyed LRU compile cache:

  - packages/connector-worker/src/compile-connector.ts  (~150 LOC)
  - packages/cli/src/commands/_lib/connector-loader.ts  (~120 LOC)
  - packages/server/src/utils/connector-catalog.ts      (~150 LOC of the
                                                          file, the rest
                                                          stays — metadata
                                                          extraction, URI
                                                          normalisation,
                                                          catalog iteration)

Comments in two of the three already said things like "single source of
truth lives in @lobu/connector-worker. Duplicated here as a flat string
array to keep this file self-contained" and "Mirror the worker-side
resolver in compile-connector.ts: try the subdir layout first … Keep
these in sync if either side changes." That's the drift surface this
spike closes.

New shared module: `packages/connector-worker/src/compile/index.ts`,
exported as `@lobu/connector-worker/compile`. (Originally `./build` but
the repo root `.gitignore` has `**/build/` which silently ate the new
directory; renamed to `./compile` so it tracks.)

Surface:

  - `EXTERNAL_RUNTIME_DEPS` — re-exported from `runtime-deps.ts`.
  - `findBundledConnectorFile(key, candidateDirs)` — pure helper; the
    per-environment candidate-dir list is the parameter that genuinely
    differs (worker image vs CLI dist-bundle vs gateway pod). No
    internal cache; callers that need one (`server/connector-catalog.ts`,
    `cli/connector-loader.ts`) wrap their own thin Map<key, path|null>.
  - `createNpmSpecifierPlugin({ onUnresolved })` — esbuild plugin
    parametrised on the warn hook (server wants to log via pino, CLI/
    worker stay silent).
  - `createConnectorCompiler({ cacheMax?, sdkEntry?, onUnresolvedNpm? })`
    → `{ compileConnectorFromFile }`. Each instance owns its own
    8-entry LRU; callers that want per-process isolation create their
    own.

Net diff: -381 / +296 (with the new 229-LOC shared module) = -85 LOC
across the three packages plus zero risk of the three implementations
drifting from each other.

Server now declares `@lobu/connector-worker: workspace:*` as a real dep
(it was already importing from it after the prior round-2 spike, but
without a corresponding package.json entry).

Typecheck status (`make typecheck` from worktree root): **clean.** The
pre-existing `gateway/guardrails/aggregator.ts` `SkillPreToolGuardrail`
errors that REPORT.md called out turned out to be stale dist artifacts
in this worktree — once `make build-packages` re-emitted `@lobu/core`'s
.d.ts, those resolved too. Whole repo typecheck is green.
Round-3 follow-up. Spike 9232088 deleted the legacy executor adapter
in `@lobu/connector-worker` and migrated `daemon/executor.ts` plus the
two `packages/server` call-sites. The CLI's `lobu connector run` path
(`packages/cli/src/commands/_lib/connector-run-cmd.ts:434`) was missed
and would have crashed at runtime on every invocation. The slip was
masked by an `(executeCompiledConnector as any)` cast on the call site
— typecheck couldn't see the contract mismatch. `make build-packages`
is bundler-only and didn't catch it either.

Concrete fixes:

- Remove the `as any` cast on the call site so future contract drift
  is a typecheck error.
- Replace the flat-shape call
  (`{ mode: 'sync', compiledCode, config, checkpoint, env,
  connectionCredentials, sessionState, credentials, feedKey, entityIds,
  apiType, hooks: { onContentChunk, collectContents } }`)
  with the V1 shape:
  `{ compiledCode, job: { mode: 'sync', config, checkpoint, env,
  sessionState, credentials, feedKey, entityIds }, hooks: { onEventChunk } }`.
- Switch `onContentChunk` → `onEventChunk` and accumulate events into a
  local `EventEnvelope[]` (the executor used to collect them into
  `result.contents` for the caller via `collectContents: true`; that
  path is gone, sync is streaming-only now).
- Replace all `result.contents.*` reads with `collectedEvents.*` for the
  artifact JSON, the summary line, and the `summarizeEvents` call.
- Mode-narrow on `result.mode === 'sync'` before reading
  `result.checkpoint` / `result.metadata`.

Verification (typecheck-alone-is-insufficient per the round-3 brief):

1. `bunx tsc --noEmit` in `packages/cli` — clean.
2. `make build-packages` — clean.
3. Runtime smoke test in `packages/cli`:
   - load `dist/commands/_lib/connector-run-cmd.js` and confirm the
     `connectorRun` export resolves;
   - dynamic-import `@lobu/connector-worker/executor/runtime` the same
     way the CLI does at runtime, confirm `executeCompiledConnector`
     resolves;
   - feed it a synthetic 5-line connector whose `sync()` throws
     `'intentional smoke-test failure'`, with the new V1 job shape;
   - assert the executor forks a subprocess, the subprocess loads the
     bundle, the connector throws, and the error round-trips back
     with the expected payload.
   Result: `OK: V1 contract end-to-end (subprocess threw expected
   payload)`. The OLD shape would have died with TypeError on
   `result.contents.length` or shape-validation in
   `buildConnectorExecutionContext`.
… comment

Round-3 follow-up. The `sync` arm of `ExecutorResult` advertised
`events: EventEnvelope[]` but `child-runner.ts` always returned
`events: []` because the V1 contract streams events via
`hooks.onEventChunk` and the executor doesn't collect on the result
side. The shape was lying. Picked the option the code already
implements: sync is streaming-only.

Concrete changes:

- `packages/connector-worker/src/executor/interface.ts`:
  - `ExecutorResult` sync arm loses the `events` field. New doc-block
    spells out the contract ("events leave via `hooks.onEventChunk`,
    never collected onto the result — callers that need a list build
    it themselves in the hook") and points at the CLI as the reference
    consumer.
  - Top-of-file comment no longer mentions the long-dead
    `__action_key` / `__feed_key` / `__auth_mode` magic-key protocol.
    `git grep` for those identifiers across the monorepo now returns
    zero hits (verified post-commit).

- `packages/connector-worker/src/executor/child-runner.ts`:
  - The post-`sync()` `events: []` field is gone from the returned
    `ExecutorResult`.
  - Renamed the local `events` variable to `trailingEvents` to make it
    obvious it captures the *return-value* path (connectors that build
    the full list before returning) as distinct from incremental
    `emitEvents()` calls during sync. Both paths still funnel through
    the same `event_chunk` IPC so the parent sees one uniform stream.

Verification:

- Monorepo grep `rg -n '__action_key|__feed_key|__auth_mode' packages/
  examples/ scripts/ config/`: zero hits.
- `rg -n 'result\.events' packages/connector-worker/src packages/cli/src
  packages/server/src`: the remaining hits are all unrelated (chat SSE
  `eventsUrl`, embedding-fetch JSON response, the child-runner's own
  read of `syncResult.events` from the SDK type — which is the
  connector's `SyncResult.events`, not the executor's). No consumer
  reads sync `result.events` from `ExecutorResult` anywhere.
- Typecheck: `connector-sdk`, `connector-worker`, `cli`, `server`
  clean; `connectors` has only pre-existing whatsapp/dom-lib drift on
  main.
Round-3 follow-up. Two server files still reached into
`packages/connector-worker/src/runtime-deps.ts` via relative path even
after round-2 spike `ad4e92d6` added `@lobu/connector-worker/compile`
and declared the package dep. Cross-package leaks like these silently
break the moment the worker's source layout shifts (dist-only consumer
runtimes, esm-vs-cjs flips, file relocations) and they're the exact
pattern round-3 was about closing.

- `packages/server/src/server.ts:44`: `import { assertExternalDepsResolvable }
  from '../../connector-worker/src/runtime-deps'`
- `packages/server/src/utils/connector-compiler.ts:8`: `import {
  EXTERNAL_RUNTIME_DEPS } from '../../../connector-worker/src/runtime-deps'`

Both now go through `@lobu/connector-worker/compile`. The compile module
already re-exported `EXTERNAL_RUNTIME_DEPS` (round-2 spike `ad4e92d6`);
this spike adds `assertExternalDepsResolvable` to the same export so
the boot-time helper has a public-export path too. Both belong in
`connector-worker` (not `@lobu/core`) because they describe deps the
worker's compile pipeline externalises — moving them out would split
that concern across two packages.

Verification:

- `rg -n "from ['\"]\\.\\./\\.\\./.*connector-worker|from ['\"]\\.\\./\\.\\./\\.\\./connector-worker"
  packages/server packages/cli --type=ts`: zero hits.
- `make build-packages`: clean.
- `bunx tsc --noEmit` in `packages/server`: clean.
Round-3 follow-up covering targets 4 and 5.

- `packages/connector-sdk/src/connector-runtime.ts:72`: dropped the `_`
  prefix on the `ctx` parameter of the base `execute()`. The repo
  convention (AGENTS.md: "When fixing unused-parameter errors, delete
  the parameter rather than prefixing with `_`") doesn't apply
  literally here because the parameter is contractually required:
  subclasses overriding `execute(ctx: ActionContext)` rely on the base
  signature accepting an `ActionContext`. Verified by deleting the
  param and watching every action-supporting connector
  (`google_calendar`, `google_gmail`, `chrome`, `revolut`) fail
  typecheck with `Target signature provides too few arguments.
  Expected 1 or more, but got 0.`. Restored the param without the
  underscore and added a single `// biome-ignore
  lint/correctness/noUnusedFunctionParameters` line with the rationale
  ("contract signature — subclasses receive the full ActionContext")
  so future readers see why the param looks unused but stays.

- `packages/connectors/src/README.md:353`: deleted the paragraph that
  told new connector authors to paste the 3-line "actions not
  supported" stub. Replaced with one sentence pointing at the new
  default impl on `ConnectorRuntime` ("do nothing — the base class
  ships a default `execute()` that returns `{ success: false, error:
  'Actions not supported' }`. Omit the method entirely.").

Verification:
- `make typecheck` from the worktree root: clean.
- 19 connectors that already lost the stub in spike 02f990f still
  compile against the new base.
Round-4 doc polish.

- `packages/connectors/src/README.md`: the Quick Start (top of file)
  still showed the deleted `execute(_ctx: ActionContext): Promise<ActionResult>
  { return { success: false, error: 'Actions not supported' }; }` stub
  on every new connector — contradicting the actions-not-supported
  guidance later in the same file ("omit the method entirely"). Removed
  the stub from the Quick Start class body and dropped the now-unused
  `ActionContext` / `ActionResult` imports from its import block. The
  later section that does show actions (the `github`-style example
  around line 331) still imports them locally and is unchanged.

- `packages/cli/src/commands/_lib/connector-run-cmd.ts:344`: comment
  said "Build the SyncContext shape that executeCompiledConnector
  expects" — but as of spike `76a4677c` we build an `ExecutorJob`, not
  a `SyncContext`. One-word edit.

Verification:
- `rg -n "execute\(_ctx|ActionContext|ActionResult" packages/connectors/src/README.md`
  returns one hit (line 331 — the actions-supported example), not the
  Quick Start or the no-actions guidance.
- `make typecheck`: clean.
Round-5 polish — final doc/fixture drift after the V1 IPC migration.

- `packages/connectors/src/README.md`: rewrote the "How connector code
  runs" section. Pre-spike it described the gateway shipping
  ~13 MB `compiled_code` inline in every worker-poll response and the
  worker loading it blindly. After spikes `ad4e92d6` (compile pipeline
  consolidation) and `92320888` (V1 IPC), fleet workers receive only
  `connector_key` and compile locally via the shared
  `@lobu/connector-worker/compile` module; the gateway only inlines
  `compiled_code` for device/DB-only workers without source on disk.
  Also updated the IPC step to name the live wire format
  (`ExecutorJob` / `ExecutorResult`, `event_chunk` for streaming).

- `packages/connector-worker/integration-tests/subprocess.test.ts` +
  `subprocess-source-mode.test.ts`: fixtures still constructed a
  `BASE_CONTEXT: SyncContext` shaped as the deleted V0
  `{ options, checkpoint, env, apiType }`. Tests still passed because
  the fixture was passed positionally and the diagnostic-capture tests
  trigger crash/throw paths before the executor inspects most fields,
  but the comment + type alias misled future readers. Migrated to
  `BASE_JOB: ExecutorJob` with the V1 shape (`mode: 'sync'`, `feedKey`,
  `config`, `checkpoint`, `entityIds`, `credentials`, `sessionState`,
  `env`). Also fixed the synthetic-connector `compiled()` template: the
  inline `execute()` returned `{ contents: [], checkpoint: null }`
  (V0 `FeedSyncResult` shape) — replaced with V1 `ActionResult`
  (`{ success: false, error: 'no actions' }`) so the fixture is
  internally consistent with what `findRuntimeClass` expects.

Verification:
- `bun test packages/connector-worker/integration-tests`: 8 pass, 0 fail.
- `rg -n "\\bSyncContext\\b" packages/connector-worker packages/cli/src`:
  remaining hits are all live SDK-type references (the executor
  interface comment names the SDK type, the integration test's comment
  explains the synthetic `sync(_ctx)` receives a V1 `SyncContext`) —
  no stale fixture references. README `SyncContext` references all
  document the live SDK type.
- `make typecheck`: clean.
Round-6 one-liner. compile-connector.ts:17 referenced './build' but
the shared module was renamed to './compile' in round-2 (the gitignore
gotcha — '\*\*/build/' silently swallowed the original dir name).
Comment now matches the actual import path.
The integration job built only `core` + `connector-sdk` before invoking
the server's vitest suite. After spike ad4e92d the server's
`packages/server/src/utils/connector-catalog.ts` imports
`@lobu/connector-worker/compile` — Node's package resolver follows the
`exports` field to `./dist/compile/index.js` and fails when dist is
absent. vitest's vite-node loader propagates the failure as a load
error, breaking every integration test whose import graph transitively
touches `queue-helpers` / `worker-api` / `connector-catalog` — all 30
files in the suite. The unit job already builds connector-worker (line
57-67); the integration job had a smaller list that pre-dated the
shared-compile-module refactor.

Root cause verification:
1. Reproduced locally by `rm -rf packages/connector-worker/dist` and
   running `LOBU_TEST_BACKEND=pglite vitest run` from packages/server:
   `Error: Failed to load url @lobu/connector-worker/compile … Does the
   file exist?` on every integration file — same symptom as the CI
   run #26117699107 attached to PR #931.
2. Rebuilt connector-worker (`cd packages/connector-worker && bun run
   build`), re-ran the same vitest invocation — 73 of 76 files load
   and pass. The 2 remaining failures are pre-existing on main
   (`run-script-runtime.test.ts` needs Node 22's isolated-vm abi127
   prebuild; `install-operator.test.ts` fails identically on
   origin/main when checked out and re-run, both unrelated to this
   fix).

Considered alternatives:
- `"bun": "./src/compile/index.ts"` conditional export — rejected: the
  integration job runs Node (line 142 of ci.yml: "We run them under
  Node (not bun) … the sandbox runtime requires isolated-vm which
  is a V8 native addon that bun cannot load"), so the `bun` condition
  wouldn't activate.
- `"node": "./src/compile/index.ts"` source-mapped export — rejected:
  the source is .ts; Node can't load .ts without a loader, and
  installing one for CI tests would also slow every Node consumer
  in production. The build-dist approach matches every other
  workspace package.
- Switching to relative imports — rejected: that's the leak round 3
  closed. The fix belongs in CI, not by undoing the package boundary.

The added embeddings build is required because connector-worker's
src/embeddings.ts imports `@lobu/embeddings` and resolves it via the
workspace symlink → dist. Same dependency order the unit job already
honors (line 66 of the unit job builds both in the same order).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants