♻️ refactor(agent-signal): execAgent migration — serverRuntime bridge + completion projection + async memoryWriter + executeSelfIteration removal#15392
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## canary #15392 +/- ##
===========================================
- Coverage 89.74% 70.91% -18.83%
===========================================
Files 862 3221 +2359
Lines 105577 319260 +213683
Branches 10216 28093 +17877
===========================================
+ Hits 94746 226405 +131659
- Misses 10656 92680 +82024
Partials 175 175
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
8a1aeae to
a69bc27
Compare
a69bc27 to
9ca7012
Compare
55e3525 to
b923389
Compare
…no side-channel Rewrite all three self-iteration execAgent tool surfaces (review / reflection / feedback-intent) as static, named primitives instead of reusing the dynamic createServerToolSet / createToolSet factory (which carries the legacy reserveOperation / receipt / completeOperation side channel the migration removes). Package (builtin-tool-agent-signal): - AgentSignalToolService.invoke (generic bag) → AgentSignalRuntimeService, a narrow named DB-primitive seam (skillManagement precedent). Artifact recorders echo their input; reads/mutations route to one primitive each. The runtime carries no dedupe / receipt / operation-state side channel — idempotency and receipt projection live on the completion path, not the tool call. Server primitives (pure live-DB reads + writes, keyed to api names): - review/server.ts createReviewRuntimePrimitives — proposal lifecycle + resource tools, parameterized by window scalars from the operation marker, reusing the existing snapshot/preflight/projection/brief helpers. - tools/runtimePrimitives.ts createResourceRuntimePrimitives — the skill-read / skill-write / writeMemory surface shared by reflection and feedback-intent. - No context blob and no getEvidenceDigest: evidence is embedded in the agent prompt, so tools only touch live state. serverRuntimes: agentSignalReview / agentSignalReflection / agentSignalFeedbackIntent thin factories wiring ToolExecutionContext → primitives → package runtime, all registered. createServerToolSet / createToolSet left untouched (legacy executeSelfIteration path, removed in S4). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…alState Replace the in-runtime receipt accumulator with finalState-driven projection on the completion path. finalState is only in hand inside the completion lifecycle (S3 final snapshots are write-only — get() is a null stub; the operation row has no messages; prod webhook hooks strip finalState), so receipts must be projected from the one point state exists. - CompletionLifecycle.emitSignalEvents: extract the compact, kind-tagged tool outcomes from the terminal state (extractSelfIterationCompletionPayload) and carry them on the agent.execution.completed payload — only for marked self-iteration runs, never the full message history. - completionPolicy: forward the payload to onSelfIterationCompleted. - completion/buildSelfIterationReceipts: project mutations + artifacts into user-visible receipts, mirroring the legacy createReceipts kind/status/target mapping. Deterministic receipt ids (sourceId + tool call id) → idempotent re-projection; the store dedupes by id. - completion/selfIterationCompletionHandler: build + persist receipts. - orchestrator: wire the handler into createDefaultAgentSignalPolicies. - agent-signal source type: add an opaque selfIteration field to the agent.execution.completed payload. Inert until the dispatch side stamps the operation marker (S3 / S4): without a marker the extractor returns undefined and the handler no-ops. Tests: buildSelfIterationReceipts (5) + extractCompletionPayload (4); completion policy + CompletionLifecycle + orchestrator suites green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rt (inert) Foundation for migrating the memory writer to the async execAgent path: teach the completion path to project a memory receipt from a finished memory-writer run. Inert until the dispatch side stamps a kind:'memory' marker (part 2). - completion routing is now keyed on the operation MARKER (the selfIteration payload), not the agent slug — a memory writer runs as the user's own agent, so a slug check would miss it. completionPolicy gates on payload presence; agentId loosened to string. - extractCompletionPayload: for a kind:'memory' run, synthesize a writeMemory mutation from the run's finalState (the memory builtin tool results are not kind-tagged, so extractMutations finds nothing) via resolveMemoryActionResultFromState. - buildSelfIterationReceipts: a memory run surfaces as just its action receipt, no aggregate review summary. - extract the pure memory finalState parsers into a dependency-light ./memoryActionResult module so the completion lifecycle can reuse them without dragging the heavy memory-runner module (ModelRuntime/AgentService/…) into its graph. userMemory re-exports them for backward compat. - bump a too-tight (5s) timeout on the real-orchestration integration test. Tests: completion (12) + completionPolicy (8) + userMemory (12) green; agentSignal policies + orchestrator suites (138) green; type-check clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nt + completion receipt Flip the memory-writer action from a blocking executeSync run to an async queued operation (autoStart) stamped with an agent-signal `memory` marker. The user-visible "memory saved" receipt is no longer projected synchronously from the action result — it is projected on the completion path from the run's finalState (extractMemoryMutations → buildSelfIterationReceipts), so the receipt appears a few seconds later once the run completes. - userMemory.ts: add `dispatch` path enqueuing via createOperation(autoStart), stamping appContext.agentSignal so completion can project the receipt. - receiptService.ts: drop the synchronous memory receipt projection (would duplicate the async one, with a premature empty target). - types.ts: add `agentSignal` marker to OperationCreationParams.appContext. - tests: cover the memory-kind completion loop end-to-end (single memory receipt, correct target + anchor, no aggregate summary). Note: the memory run uses createOperation (not execAgent), so it never synthesises a user message and cannot recurse into analyzeIntent — no suppressSignal needed on this path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…xecAgent Foundation for migrating self-iteration onto execAgent: let a background run carry its agent-signal marker so the S2 completion path can project receipts. - Move AgentSignalOperationMarker / AgentSignalOperationKind into @lobechat/types (ExecAgentAppContext can now reference it); operationMarker.ts re-exports the type and keeps the runtime parse/validate helpers. - ExecAgentAppContext: add `agentSignal?` field. - execAgent: forward `appContext.agentSignal` into createOperation's appContext (it was dropped by the curated passthrough), so it lands in state.metadata.agentSignal — the key the completion extractor reads. No behaviour change yet: nothing sets appContext.agentSignal on the execAgent path until the self-iteration dispatch helper lands. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…elper Shared primitive for migrating the 3 self-iteration modes off the hand-rolled runtime onto async execAgent (used by reflection/feedback/nightly-review next). - enqueueSelfIterationRun(): create an isolated thread (when anchored), then execAgent the builtin slug with suppressSignal + the agent-signal marker on appContext, autoStart, headless. Returns immediately (fire-and-forget). - marker: add `agentId` (the reviewed user agent). A slug run resolves the operation agentId to the builtin agent, so receipts must attribute to the reviewed agent carried on the marker. - buildSelfIterationReceipts: attribute to `marker.agentId ?? agentId` (memory runs leave it unset and fall back to the run agentId — unchanged). Not wired into the mode handlers yet. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
716aeea to
72eb59b
Compare
… execAgent Replace the hand-rolled `executeSelfIteration` runtime (new AgentRuntime + custom call_llm executor + 6 closure side-channels) with the standard async `execAgent` queue path. nightly-review / self-reflection / self-feedback-intent now enqueue via `enqueueSelfIterationRun → execAgent` and project their receipts/briefs on the `agent.execution.completed` completion path. - Delete `execute.ts` (1500 lines) + `execute.test.ts`; gut the three server adapters (review/reflection/feedback) to drop the synchronous run path and the legacy receipt/runtime wiring. - `aiAgent`: background runs execute under a builtin slug but attribute their resource tools + receipts to the *reviewed* user agent via the run marker. - Drop the orchestrator's `writeDailyBrief` default — nightly review writes its brief in-run via the builtin review serverRuntime primitive. - Add `ReviewRunStatus.Dispatched` for enqueued background runs. - Completion-path debug logging across CompletionLifecycle / completionPolicy / completion handler. Part of LOBE-9434 (S4 · LOBE-9876).
…-iteration agents a mini model
Live-testing the S4 self-iteration → execAgent path surfaced two gaps that kept
background runs (nightly-review / self-reflection / self-feedback-intent) from
ever dispatching:
- execAgent threw `Agent not found: <slug>` when addressed purely by a builtin
slug (the self-iteration dispatch path) because getAgentConfig only resolves
persisted rows. Lazily materialize the virtual builtin row via
AgentModel.getBuiltinAgent — mirrors the inbox/task precedent — then re-resolve.
- The three self-iteration builtin agents had no `persist` model, so runs fell
back to the user's default chat model. Give them `persist: { DEFAULT_MINI_MODEL,
DEFAULT_MINI_PROVIDER }` (gpt-5.4-mini), matching the legacy executeSelfIteration
behavior.
Verified live: self-reflection now dispatches, the async operation reaches `done`,
and a `review` completion receipt is projected on the completion path. Adds two
execAgent.builtinRuntime tests (builtin-slug materialization + unknown-id still
throws).
Part of LOBE-9434 (S4).
✅ S4 live-validated (local dev server, real LLM)Ran the self-iteration path end-to-end via
Result: Also learned (for reviewers): the agent-signal marker is not persisted on the BDD acceptance harness for this lives in the cloud repo (lobehub-biz/lobehub-cloud#671). Tests:
|
There was a problem hiding this comment.
Sorry @arvinxx, your pull request is larger than the review limit of 150000 diff characters
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b0ffa0395d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const operation = await new AgentOperationModel(serverDB, userId).findById(operationId); | ||
| const marker = readAgentSignalMarker(operation?.metadata); |
There was a problem hiding this comment.
Read the marker from a field that is actually persisted
Nightly-review tools depend on this marker for reviewWindowStart, reviewWindowEnd, localDate, and stable sourceId, but AgentRuntimeService.recordStart never writes appContext.agentSignal into agent_operations.metadata (it only persists a trimmed appContext, and no later update writes this metadata). In live runs marker is therefore always undefined here, so proposal/brief writes fall back to the 1970 window and operationId source, breaking the nightly review date/window and idempotent source ids.
Useful? React with 👍 / 👎.
…riefWriter After the S4 gutting, review/server.ts only uses createServerSelfReviewBriefWriter in a `ReturnType<typeof ...>` position — split it into a type-only import to satisfy @typescript-eslint/consistent-type-imports (the lone lint:ts error).
…eceipts project The agent runtime persists tool messages with only content/role/tool_call_id (no message-level apiName), so the completion extractor's `message.apiName` read was always undefined in live runs — buildSelfIterationReceipts then dropped every mutation via `if (!apiName) return []`, so durable skill/proposal writes produced no action receipt (only the summary survived; memory was exempt via a hard-coded apiName). Fix the extraction channel, not the shared runtime: - ExecutionRuntime stamps `apiName` into the result content alongside `kind`. - extractFromFinalState reads apiName from the content (message.apiName fallback). Tests reworked to the real persisted shape (apiName in content, no message-level apiName) — the prior mocks hid the bug. Part of LOBE-9434 (S4).
…ver tools
Self-iteration server tools (nightly-review etc.) read the run marker from
`agent_operations.metadata` via readAgentSignalMarker, but recordStart only
persisted a trimmed appContext and never wrote metadata — so in live runs the
marker was always undefined and review/proposal writes fell back to a 1970
window/localDate + operationId source (non-idempotent).
recordStart now persists `metadata: { agentSignal }` from appContext.agentSignal,
so the tool path matches the completion path (which reads it from finalState).
Part of LOBE-9434 (S4).
# 🚀 LobeHub Release (20260604) **Release Date:** June 4, 2026 **Since v2.2.1:** 88 merged PRs · 11 contributors > This week brings Execution Devices out of the lab — run agents and Claude Code on any configured local or remote machine — alongside Claude Opus 4.8, token-usage analytics, and Page sharing. --- ## ✨ Highlights - **Execution Devices** — Pick where an agent runs. Desktop and CLI devices auto-register with a stable machine ID, route through the gateway by channel, and surface a device switcher in the chat input. Run remote Claude Code on a configured device, with a recent-directory picker you can drag to reorder. (#15300, #15315, #15322, #15343, #15351, #15371) - **Claude Opus 4.8** — Day-one support for Anthropic's latest model. (#15314) - **Token-usage analytics** — A new token-usage mode on the activity heatmap, backed by a denormalized topic usage/cost rollup so totals stay accurate without recomputing from messages. (#15365, #15417, #15425) - **Page sharing** — Share a Page through a dedicated document share flow, plus new Workspace and Agent share tables. (#15309, #15439) - **Self-iteration agents** — Agent Signal's execAgent migration lands a server-runtime bridge, async memory writer, and a registered self-iteration tool package, with a CLI trigger command for testing. (#15360, #15364, #15392) - **Knowledge search** — BM25 search now extends to file-backed documents, and the portal ships an editable CodeMirror viewer for local files with document highlighting. (#15247, #15298) --- ## 🏗️ Core Agent & Architecture ### Agent Signal & Runtime - **execAgent migration** — Server-runtime bridge, completion projection, async memory writer, and removal of the legacy `executeSelfIteration` path. (#15392) - Registered the self-iteration builtin tool package and restored the three mode-specific self-iteration agent slugs. (#15202, #15364) - Added a CLI trigger command with a golden-snapshot fixture for Agent Signal. (#15360) - **Skill priority** — Agent Builder now emits a skill-priority instruction with matching server runtime. (#15409) - Retry empty LLM completions instead of silently finishing the turn. (#15355) - Classify topic/agent/session foreign-key violations as `ConversationParentMissing` for clearer recovery. (#15408) - Persist canonical nested usage/performance on assistant messages, and re-link orphan tool messages at the raw bucket write boundary. (#15359, #15438) - Guard `createAgent` against LLM double-encoded array fields. (#15381) --- ## 🖥️ Execution Devices & Gateway - Auto-register desktop and CLI devices with a stable machine ID, and add the `@lobechat/device-identity` package. (#15300, #15321) - New Devices settings page behind the Execution Device Switcher lab, with a device switcher shown for all agents in the chat input. (#15315, #15371) - `connectionId` + channel routing across the gateway client and device list; preset the local device on the first LLM request for the 本机 target. (#15322, #15435) - Run remote Claude Code on a configured device, with drag-to-reorder recent-directory management and client renders for device tool results. (#15343, #15351, #15437) - Preserve content and state across gateway tool calls, and prevent duplicate streaming from stale reconnects. (#15114, #15354) --- ## 🖥️ CLI & Desktop - Preserve content/state for connect local file and shell tools; render the `runCommand` tool result card. (#15441, #15442) - New `lh topic view` command; CLI now auto-registers its device on login, matching desktop. (#15340, #15377) - Resolve CLI tools from the shell `PATH`, and clarify local command session handling. (#15368, #15389) - Relocate visual-ref helpers to `@lobechat/const` to fix a renderer crash; upload `.blockmap` files to S3 for differential updates. (#15326, #15369) - Fix a market OAuth expiry that triggered the wrong re-login modal, and kill dev child processes on parent shutdown. (#15246, #15290) --- ## 🗂️ Pages, Library & Knowledge - Document share flow with business slot stubs, plus Workspace and Agent share tables. (#15309, #15439) - Export Agent profiles as Markdown, preserving an empty agent prompt on export. (#15312, #15316) - Editable CodeMirror viewer for local files with document highlighting; BM25 search extended to file-backed documents. (#15247, #15298) - Default new Agent-doc files to `.md` and preserve IME composition; refresh folder data on slug switch and dedupe breadcrumb fetches. (#15335, #15427) --- ## 💬 Chat & User Experience - Group-by-status mode for the Topic sidebar; dropped the legacy session→agentId compatibility path from Topic queries. (#15366, #15378) - Restore editor focus after the file picker closes, and close the skill dropdown before navigating to settings. (#15391, #15394) - Strip markdown tokens from fallback Topic titles; keep an open ActionBar popup when hovering another message. (#15303, #15372) - Stabilize home starter loading and stop transliterating model names in the home starter; show artifact source while streaming. (#15310, #15324, #15386) - Group the sidebar spacer with recents and agents. (#15373) --- ## 📊 Analytics, Tasks & Notifications - Token-usage mode on the activity heatmap, backed by a denormalized topic usage/cost rollup. (#15365, #15417, #15425) - Push: new `PushChannel`, receipt cron, and `pushToken` tRPC API. (#15233) - Tasks now support file and image attachments. (#15141) --- ## 🧩 Models & Providers - Support Claude Opus 4.8 and configurable model routing with starters. (#15314, #15384) - MiniMax M3: new model entry and an Anthropic video runtime. (#15380, #15403) - Add `intern-s2-preview` with `thinking_mode`, and `step-3.7-flash` support. (#15308, #15317) - Block disabling the official provider; fix default provider setup in business mode. (#15379, #15382) --- ## 🎨 UI & Modals - Migrate modals to `@lobehub/ui/base-ui` (LOBE-9711 + eval batch), including the create-custom-model and feedback/changelog modals. (#15401, #15416) - Restructure confirmModal title and content across deletion flows; polish the service-model form and migrate its Switch to base-ui. (#15426, #15440) - Wrap the BlueBubbles bridge config into a connection card; update `@lobehub/ui` to v5.15.5. (#15325, #15342) --- ## 🔒 Reliability - Replace hardcoded `session_context` values with template variables in credentials. (#15352) - Point `CHANGELOG_URL` to `/changelog`. (#15428) --- ## 👥 Contributors Huge thanks to **11 contributors** who shipped **88 merged PRs** this cycle. @hezhijie0327 · @qybaihe · @sxjeru · @arvinxx · @Innei · @tjx666 · @lijian · @sudongyuer · @cy948 · @rivertwilight · @AmAzing129 Plus @lobehubbot and renovate[bot] for maintenance. --- **Full Changelog**: v2.2.1...release/weekly-20260604
💻 Change Type
🔗 Related
LOBE-9434 — S1 (LOBE-9873) · S2 (LOBE-9874) · S3 (LOBE-9875) · S4 (LOBE-9876). Follows the merged builtin-tool package (#15364).
close LOBE-9873 LOBE-9874 LOBE-9875 LOBE-9876
🔀 What's actually on this branch now
S1 — server execution bridge (
@lobechat/builtin-tool-agent-signal→ server runtimes)AgentSignalRuntimeService, skillManagement precedent) instead of the dynamiccreateServerToolSetfactory — the runtime carries noreserveOperation/ receipt /completeOperationside channel; idempotency + receipt projection move to the completion path.operationMarker.ts— run-scoped marker (kind / sourceId / localDate / review window / message anchors) carried on the operation row, read at tool-call & completion time (theagent.execution.completedpayload only carriesagentId/operationId/topicId).serverRuntimes/agentSignal{Review,Reflection,FeedbackIntent}.ts— thin factories wiringToolExecutionContext→ primitives → package runtime. All three registered inserverRuntimes/index.ts.createServerToolSet/createToolSetleft untouched (legacyexecuteSelfIterationpath, removed in S4).S2 — completion → outcome framework (finalState → receipts)
CompletionLifecycle.emitSignalEventsextracts the compact, kind-tagged tool outcomes from terminal state (extractSelfIterationCompletionPayload) and carries them on theagent.execution.completedpayload — only for marker-stamped runs.completionPolicyforwards the payload toonSelfIterationCompleted;buildSelfIterationReceiptsprojects mutations + artifacts into receipts (mirrors the legacycreateReceiptskind/status/target mapping). Deterministic receipt ids → idempotent re-projection; store dedupes by id.createDefaultAgentSignalPoliciesvia the orchestrator. Inert for self-iteration kinds until S4 stamps their markers.S3 —⚠️ the one production-path change here
memoryWriter→ asyncexecAgent+ completion receiptuserMemory.ts: the memory-writer action flips from a blockingexecuteSyncrun to an async queued operation (createOperation(autoStart)) stamped with akind:'memory'marker.receiptService.ts) — it is projected on the completion path from the run's finalState (extractMemoryMutations → buildSelfIterationReceipts), so it now appears a few seconds later once the run completes (accepted in LOBE-9875).types.ts:agentSignalmarker added toOperationCreationParams.appContext.createOperation(notexecAgent), so it never synthesises a user message and cannot recurse intoanalyzeIntent— nosuppressSignalneeded on this path.🧪 Verification
bun run type-checkclean (agent-signal scope; pre-existing cloudrouter-runtime-options.tserrors are unrelated).buildSelfIterationReceipts,extractCompletionPayload,completionLoopw/ memory-kind e2e case),completionPolicy,userMemory,receiptService,orchestrator.lh agent-signal triggerrun + golden-snapshot structural assertion — requires a running dev server + real LLM.🔀 S4 —
executeSelfIteration→ asyncexecAgent(now landed)execute.ts(~1500 lines) +execute.test.ts; gut the three server adapters (review/reflection/feedback×server.ts+handler.ts) to drop the synchronous run path and the legacy receipt/runtime wiring. The 6 closure side-channels are gone.enqueueSelfIterationRun → execAgentand project receipts/briefs on theagent.execution.completedcompletion path.aiAgent: background runs execute under a builtin slug but attribute resource tools + receipts to the reviewed user agent via the run marker (appContext.agentSignal.agentId ?? resolvedAgentId).writeDailyBriefdefault — nightly review writes its brief in-run via the builtin review serverRuntime primitive.ReviewRunStatus.Dispatchedadded for enqueued background runs; completion-path debug logging acrossCompletionLifecycle/completionPolicy/ completion handler.📝 Notes
Cloud impact: none (cloud repo unchanged). PR moves out of draft once S4 is coherent and live-verified.
🤖 Generated with Claude Code