✨ feat(agent-signal): add execAgent plumbing for self-iteration migration#15187
Conversation
…tion Phase 1 of LOBE-9434: introduces dormant plumbing for converging agent execution onto execAgent. No behavior changes for any existing caller — every piece is a no-op until later phases wire it up. - Add `ExecAgentAppContext.suppressSignal` flag and `sourceMessageId` - Add `shouldSuppressSignal` helper; gate the `agent.user.message` re-emission in `aiAgent.execAgent` so future builtin/background runs cannot recurse into the analyzeIntent pipeline - Register `self-iteration` builtin agent + `SELF_ITERATION_AGENT_SLUGS` - Add `finalStateExtractor` (`extractFromFinalState` / `extractMutations` / `extractArtifacts`) for reading tool-result kind partitions off a persisted AgentState snapshot - Register a no-op `completionPolicy` listener on `agent.execution.completed` with an optional `onSelfIterationCompleted` callback (undefined by default) Tests: 17 new unit tests across suppressSignal, finalStateExtractor, and completionPolicy.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5d483c4f9b
ℹ️ 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".
|
|
||
| results.push({ | ||
| apiName: typeof message.apiName === 'string' ? message.apiName : undefined, | ||
| data: contentRecord ?? content, |
There was a problem hiding this comment.
Preserve pluginState payload for extracted tool results
When kind is discovered via pluginState.kind (because message.content is plain text/non-JSON), extractFromFinalState still sets data to contentRecord ?? content, which drops the structured pluginState fields (e.g., IDs needed for mutation/artifact projection). In that scenario downstream self-iteration completion handlers will receive only a string message instead of the actual tool result object, making final-state reconstruction incomplete.
Useful? React with 👍 / 👎.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## canary #15187 +/- ##
=========================================
Coverage 70.92% 70.93%
=========================================
Files 3149 3152 +3
Lines 313896 313971 +75
Branches 33311 28558 -4753
=========================================
+ Hits 222628 222705 +77
+ Misses 91100 91098 -2
Partials 168 168
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
…tion (lobehub#15187) Phase 1 of LOBE-9434: introduces dormant plumbing for converging agent execution onto execAgent. No behavior changes for any existing caller — every piece is a no-op until later phases wire it up. - Add `ExecAgentAppContext.suppressSignal` flag and `sourceMessageId` - Add `shouldSuppressSignal` helper; gate the `agent.user.message` re-emission in `aiAgent.execAgent` so future builtin/background runs cannot recurse into the analyzeIntent pipeline - Register `self-iteration` builtin agent + `SELF_ITERATION_AGENT_SLUGS` - Add `finalStateExtractor` (`extractFromFinalState` / `extractMutations` / `extractArtifacts`) for reading tool-result kind partitions off a persisted AgentState snapshot - Register a no-op `completionPolicy` listener on `agent.execution.completed` with an optional `onSelfIterationCompleted` callback (undefined by default) Tests: 17 new unit tests across suppressSignal, finalStateExtractor, and completionPolicy.
…nt slugs (#15202) The Phase 1 consolidation into a single `self-iteration` slug (PR #15187, inheriting commit 627f899 from the closed #15116) conflated three distinct background flows that have: - Independent receipt tables and idempotency Redis namespaces - Different preflight / brief projection paths - Different audit pipelines `one identifier = one behavior` is a load-bearing contract once these agents are routed through the standard execAgent plugin lookup. Restore the 3 mode-specific slugs so each agent declares its own tool surface: | slug | future plugin identifier | | ----------------------- | ------------------------------- | | `nightly-review` | `agent-signal-review` | | `self-reflection` | `agent-signal-reflection` | | `self-feedback-intent` | `agent-signal-feedback-intent` | `SELF_ITERATION_AGENT_SLUGS` now contains all three; `completionPolicy` dispatches on slug membership rather than equality; callback receives the resolved `agentId` so mode-specific bookkeeping can route from it. Plugin arrays reference the future identifiers but the tool packages are not yet registered — invoking any of these agents today runs the LLM with no tools (dormant by design). Tool-package registration follows in a separate PR. No behavior change for existing callers (none invoke these slugs yet).
💻 Change Type
🔗 Related Issue
Part of LOBE-9434 (parent).
Relates to #15116 — this is Phase 1 (dormant plumbing) of the staged split for that PR.
🔀 Description of Change
Phase 1 of LOBE-9434: introduce dormant infrastructure for converging
agent execution onto
execAgent. Every piece in this PR is a no-op for everyexisting caller — later phases will start using these primitives.
The goal of splitting Phase 1 out is to let the high-risk pieces of #15116
(switching
userMemoryto asyncexecAgent, deletingexecuteSync,migrating
executeSelfIteration) land in much smaller, individuallyrevertable PRs once this foundation has baked.
Contents:
ExecAgentAppContext.suppressSignalflag +sourceMessageId;shouldSuppressSignalhelper; gate theagent.user.messagere-emission insideAiAgentService.execAgentsuppressSignal: trueand no caller passes theself-iterationslug yet — current behaviour identical.self-iterationbuiltin agent +SELF_ITERATION_AGENT_SLUGSsetshouldSuppressSignal.finalStateExtractor(extractFromFinalState/extractMutations/extractArtifacts)completionPolicyregistered onagent.execution.completed,onSelfIterationCompletedcallback isundefinedby defaultDifferences vs. the original #15116:
shouldSuppressSignalis actually wired intoAiAgentService.execAgent(the original PR added the helper but never called it).packages/builtin-agents/src/types.tsorpackages/builtin-agents/src/index.ts.log(…)debug logger inaiAgent/index.tsfor the enqueue catch handler (original PR switched toconsole.error).completionPolicyshort-circuits before thetry { … }block when no callback is provided, instead of always entering it.🧪 How to Test
bun run type-checkclean for everything Phase 1 touches (remaining errors are pre-existing cloud-business issues unrelated to this PR)src/server/services/agentSignal/__tests__/suppressSignal.test.ts(5 tests)src/server/services/agentSignal/services/selfIteration/__test__/finalStateExtractor.test.ts(6 tests)src/server/services/agentSignal/policies/completionPolicy.test.ts(6 tests)packages/builtin-agents,policies/, andservices/selfIteration/: 300 tests pass.📝 Additional Information
No behaviour change. Safe to merge first; subsequent phases:
executeViaExecAgent(still not wired into any caller)userMemory.ts(the high-risk piece), then migrateexecuteSelfIterationcallers one at a timeonSelfIterationCompletedcallback and clean up legacyexecute.tsCloud impact: none —
aiAgent/index.tsis an open-source path; the suppress check is gated byappContext?.suppressSignal === trueor membership inSELF_ITERATION_AGENT_SLUGS, neither of which is reachable by any current cloud caller.