Skip to content

feat(agent-signal): converge agent execution onto execAgent; reduce signal layer to event dispatcher [LOBE-9434]#15116

Closed
arvinxx wants to merge 17 commits into
canaryfrom
lobe-9434-agent-signal-converge-agent-execution-onto-execagent-reduce
Closed

feat(agent-signal): converge agent execution onto execAgent; reduce signal layer to event dispatcher [LOBE-9434]#15116
arvinxx wants to merge 17 commits into
canaryfrom
lobe-9434-agent-signal-converge-agent-execution-onto-execagent-reduce

Conversation

@arvinxx

@arvinxx arvinxx commented May 22, 2026

Copy link
Copy Markdown
Member

关联 Issue

Closes LOBE-9449 LOBE-9450 LOBE-9451 LOBE-9452 LOBE-9454 LOBE-9455

Parent: https://linear.app/lobehub/issue/LOBE-9434

变更内容

#2 LOBE-9449 — appContext.suppressSignal flag

  • 新增 src/server/services/agentSignal/suppressSignal.tsshouldSuppressSignal() helper
  • 自动对 SELF_ITERATION_AGENT_SLUGS 集合中的 slug 返回 true(无需手动传 flag)
  • 支持 appContext.suppressSignal: true 显式覆盖

#3 LOBE-9452 — 注册 self-iteration 系列 builtin agent

  • packages/builtin-agents/src/types.ts:新增三个 slug(nightly-review / self-reflection / self-feedback-intent
  • 新增三个 agent 目录:agents/nightly-reviewagents/self-reflectionagents/self-feedback-intent
  • packages/builtin-agents/src/index.ts:注册到 BUILTIN_AGENTS 并导出 SELF_ITERATION_AGENT_SLUGS Set

#4 LOBE-9451 — agent.execution.completed 监听 policy

  • 新增 src/server/services/agentSignal/policies/completionPolicy.ts
  • 根据 agentId slug 路由到 onNightlyReviewCompleted / onSelfReflectionCompleted / onSelfFeedbackIntentCompleted callbacks
  • 失败非致命,不阻塞 worker

#5 LOBE-9450 — tool result kind schema + finalState extractor

  • 新增 src/server/services/agentSignal/services/selfIteration/finalStateExtractor.ts
  • 定义 ToolResultKind = 'read' | 'artifact' | 'mutation'
  • extractFromFinalState(finalState, kind) 替代六个闭包累积器
  • extractArtifacts / extractMutations 便捷函数

#6 LOBE-9455 — memoryWriter / skillManagement 迁移

  • userMemory.tsrunMemoryActionAgent 改为调用 AiAgentService.execAgent,删除 createOperation + executeSync 阻塞路径
  • skillManagement.ts:skill write 步骤(create/refine/consolidate)改为调用 execAgent;decision step(短时只读)保持原实现

#7 LOBE-9454 — executeSelfIteration 迁移

  • 新增 src/server/services/agentSignal/services/selfIteration/executeViaExecAgent.ts
  • 通过 slug 映射(review→nightly-review, reflection→self-reflection, intent→self-feedback-intent)调用 execAgent
  • 返回 { operationId, finalState, artifacts, mutations }
  • execute.ts 保留,待 build(deps): bump brotli-wasm from 1.3.1 to 2.0.0 #8 cleanup 删除

跳过内容

注意

  • skillManagement.tsrunSkillDecisionAgentRuntime 当前返回 stub({ action: 'noop' }),需要 TODO 补全决策步骤
  • userMemory.ts 中 thread 创建部分有一处语法问题需要手工修复(metadata 对象字面量)
  • finalStateExtractor.tsToolResultWithKind.data 类型有一处字符截断需修复

这些都是此 PR 中需要 review 关注的点。

@vercel

vercel Bot commented May 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lobehub Ready Ready Preview, Comment May 24, 2026 5:57pm

Request Review

@dosubot dosubot Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label May 22, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @arvinxx, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@dosubot dosubot Bot added the feature:agent Assistant/Agent configuration and behavior label May 22, 2026
@codecov

codecov Bot commented May 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.27%. Comparing base (d71686b) to head (627f899).

Additional details and impacted files
@@             Coverage Diff             @@
##           canary   #15116       +/-   ##
===========================================
+ Coverage   70.92%   89.27%   +18.34%     
===========================================
  Files        3149      786     -2363     
  Lines      313673    87058   -226615     
  Branches    28498     7216    -21282     
===========================================
- Hits       222486    77721   -144765     
+ Misses      91019     9169    -81850     
  Partials      168      168               
Flag Coverage Δ
app ?
database ?
packages/agent-runtime 80.48% <ø> (ø)
packages/builtin-tool-lobe-agent 19.87% <ø> (ø)
packages/context-engine 84.13% <ø> (ø)
packages/conversation-flow 91.28% <ø> (ø)
packages/file-loaders 87.89% <ø> (ø)
packages/memory-user-memory 74.99% <ø> (ø)
packages/model-bank 99.99% <ø> (ø)
packages/model-runtime 83.88% <ø> (ø)
packages/prompts 72.54% <ø> (ø)
packages/python-interpreter 92.90% <ø> (ø)
packages/ssrf-safe-fetch 0.00% <ø> (ø)
packages/types 35.09% <ø> (ø)
packages/utils 88.47% <ø> (ø)
packages/web-crawler 88.08% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
Store ∅ <ø> (∅)
Services ∅ <ø> (∅)
Server ∅ <ø> (∅)
Libs ∅ <ø> (∅)
Utils 93.47% <ø> (+7.50%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 761c074bb5

ℹ️ 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".

Comment on lines +381 to +383
const decision: SkillManagementDecision = options.skillDecisionRunner
? ((await options.skillDecisionRunner(action.payload as SkillManagementSignalPayload)) as SkillManagementDecision)
: { action: 'noop', reason: 'no decision runner configured' };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Wire a real default decision path for skill management

When no custom skillDecisionRunner is injected, this branch now hardcodes a noop decision. In the default server wiring (createPolicyOptions in orchestrator.ts), skillDecisionRunner is not provided, so action.skill-management.handle events are always skipped and no skill create/refine/consolidate action can run in production.

Useful? React with 👍 / 👎.

Comment on lines +63 to +67
if (agentId === BUILTIN_AGENT_SLUGS.nightlyReview) {
await options.onNightlyReviewCompleted?.({ agentId, operationId, userId });
} else if (agentId === BUILTIN_AGENT_SLUGS.selfReflection) {
await options.onSelfReflectionCompleted?.({ agentId, operationId, userId });
} else if (agentId === BUILTIN_AGENT_SLUGS.selfFeedbackIntent) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Match completion handlers on slug, not agent database id

This routing compares agentId to slug constants (nightly-review, self-reflection, etc.), but completion events carry the persisted agent record id from runtime metadata, not the slug string. As a result these conditions never match and all post-completion callbacks are effectively dead.

Useful? React with 👍 / 👎.

Comment on lines +317 to +318
const runner = options.memoryActionRunner ?? ((i) => runMemoryActionAgent(i, options));
const result = await runner(runnerInput);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve memory safety checks before applying memory writes

Calling the memory runner directly here bypasses the createMemoryService.writeMemory validation boundary that previously enforced assertSafeAutomaticMemory before durable writes. That removes the last server-side safety gate for risky/low-confidence automatic memory content and can allow writes that were previously blocked.

Useful? React with 👍 / 👎.

@arvinxx arvinxx force-pushed the lobe-9434-agent-signal-converge-agent-execution-onto-execagent-reduce branch from 761c074 to 00eba57 Compare May 22, 2026 14:08
arvinxx and others added 16 commits May 25, 2026 00:51
- Add `suppressSignal` and `sourceMessageId` to `ExecAgentAppContext`
  so background agent runs can flag themselves and skip
  `agent.user.message` re-emission in execAgent (#2).
- Fix character truncation in `ToolResultWithKind.data: unknown`
  (was bare ` nknown;`).
- Replace the invalid `chatConfig.enableAutoCreateTopic` field on the
  three self-iteration builtin agents (nightly-review / self-reflection /
  self-feedback-intent) with the required `plugins:
  ['agent-signal-self-iteration']` so execAgent actually injects the
  self-iteration tool manifest at runtime.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The original PR commit (`feat(agent-signal): migrate skill management
action to execAgent async path [LOBE-9455]`) regressed
`runSkillDecisionAgentRuntime` to a `{ action: 'noop' }` stub and dropped
~1500 lines of supporting code (decision schemas, manifest, tool
executor, normalizers). This effectively disabled all skill-management
decisions.

Reverting the file to canary keeps the decision agent intact while we
redo the skill-write runner migration in a focused follow-up PR.
LOBE-9455 stays open for that work.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
execAgent returns immediately after queueing (`ExecAgentResult` does not
contain `finalState`). The previous implementation destructured
`finalState` as if execAgent were synchronous, making the
'did the memory write succeed?' branch unreachable and forcing every
memoryWriter run to fall through to skipped at runtime.

Changes:
- `runMemoryActionAgent` now enqueues via execAgent and returns
  `{operationId, status: 'applied'}` on successful queueing. Drop the
  inline finalState inspection helpers (hasSuccessful/FailedMemoryWrite)
  and the `resolveMemoryActionTargetFromState` projector — these belong
  in the completion policy once it grows a memory-writer handler.
- `runMemoryActionAgent` passes the writer-specific guidance via
  `instructions` (no `systemRoleOverride` exists on `ExecAgentParams`);
  a follow-up may introduce a `memory-writer` builtin agent so the
  systemRole replaces rather than appends.
- `userMemory.test.ts` drops the 300-line `resolveMemoryActionTargetFromState`
  describe block and the corresponding import (the function no longer
  exists). Remaining handler-level tests still run against the injected
  `memoryActionRunner` mock.
- `executeViaExecAgent` mirrors the same pattern: return `{operationId,
  enqueued}` instead of fake `finalState`. Drop the non-existent
  `systemRoleOverride` and the `createAgentSignalSelfIterationSystemRole(mode)`
  call that passed an argument to a no-arg function.
- Move `trigger: RequestTrigger.AgentSignal` from `appContext` to the
  top-level execAgent param (its actual location).
- `appContext.scope` is set to `'agent-signal'` (was `'chat'`) so
  cost/observability lanes are distinguishable.

NOTE: the post-completion bookkeeping (writing receipts, marking
idempotency only on real write success) is not yet wired up.  The
completion policy registered in this PR is the seam for that work but
no `onMemoryWriterCompleted` callback exists yet — tracked as a
follow-up under LOBE-9451 / LOBE-9455.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The completion policy and the suppressSignal helper landed in the PR as
orphan code: `createCompletionPolicy` was never installed into the
default policy stack, and `shouldSuppressSignal` was never called from
execAgent's emit site. Both are wired up now.

- `completionPolicy.ts`: switch to the correct `defineSourceHandler`
  shape (the previous `{ handle, sourceType }` literal did not match
  `AgentSignalInstallableHandlerDefinition` and failed type-check).
  Drop `context.userId` — `RuntimeProcessorContext` has no such field;
  callers can look up userId via the operations table by operationId.
  Forward `topicId` from the source payload when present.
- `policies/index.ts`: register `createCompletionPolicy(options.completion)`
  in `DEFAULT_AGENT_SIGNAL_POLICY_FACTORIES`, and add a `completion`
  field to `CreateDefaultAgentSignalPoliciesOptions` for caller-provided
  callbacks.
- `aiAgent/index.ts`: guard the `agent.user.message` emit (lines
  1855-1874) with `if (!appContext?.suppressSignal)`. Builtin background
  agent runs (memory-writer, self-iteration reviewer, etc.) now skip the
  emit and avoid re-entering the analyzeIntent pipeline.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The catch handler for the `agent.user.message` enqueue used the
`debug('lobe-server:...')` logger, which only surfaces output when the
`DEBUG` env var is set. An enqueue failure here means Agent Signal lost
a source event — that must be visible in production logs regardless of
debug flags.

Switch to `console.error` so the failure is always logged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@arvinxx arvinxx force-pushed the lobe-9434-agent-signal-converge-agent-execution-onto-execagent-reduce branch from 1546212 to ca2cfb5 Compare May 24, 2026 16:55
The nightly-review, self-reflection, and self-feedback-intent agents only
differed by systemRole prompt — same tools, same plugin. Replace them with
a single `self-iteration` agent; the mode-specific guidance is already
supplied per-call via `createAgentSignalSelfIterationPrompt`.

Also collapse the completionPolicy fanout from 3 onXxxCompleted callbacks
to a single `onSelfIterationCompleted` — per-mode dispatch is deferred
until a consumer actually wires it up (the previous 3 callbacks had no
callers anyway).
@arvinxx

arvinxx commented May 25, 2026

Copy link
Copy Markdown
Member Author

Closing in favor of a phased split (LOBE-9434 broken into smaller, individually revertable PRs to reduce refactoring risk).

Status of the work that was in this PR:

Phase Content PR Status
1 suppressSignal flag + helper, self-iteration builtin agent, finalStateExtractor, no-op completionPolicy #15187 ✅ Merged
2 executeViaExecAgent shell (this file from the original PR, not yet wired to callers) #15192 🔄 Open
3 F userMemory.ts migration (createOperation + executeSyncexecAgent async) todo 🕐 Pending — will be a dedicated PR
3 G Migrate executeSelfIteration callers (review / reflection / feedback) todo 🕐 Pending — one PR per caller
3 H Delete legacy execute.ts after bake todo 🕐 Pending

Closing this PR. Phase 3 F (the high-risk userMemory.ts migration) will get its own focused PR so it can be reviewed and reverted independently.

@arvinxx arvinxx closed this May 25, 2026
arvinxx added a commit that referenced this pull request Jun 1, 2026
…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).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature:agent Assistant/Agent configuration and behavior size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant