Skip to content

[Plan Mode AUTOMATION] Cron nudges + auto-enable + subagent follow-ups#70089

Closed
100yenadmin wants to merge 1 commit into
openclaw:mainfrom
electricsheephq:isolated/pm-automation-followups
Closed

[Plan Mode AUTOMATION] Cron nudges + auto-enable + subagent follow-ups#70089
100yenadmin wants to merge 1 commit into
openclaw:mainfrom
electricsheephq:isolated/pm-automation-followups

Conversation

@100yenadmin

@100yenadmin 100yenadmin commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

📋 Umbrella tracker: #70101 — master tracker for the 9-PR plan-mode rollout. See it for status of all parts + suggested merge order + carry-forward backlog.


📋 Stack position: This is [Plan Mode AUTOMATION], a thematic carve-out PR alongside the numbered [Plan Mode 1/6][Plan Mode 6/6] stack.

  • Why a separate PR: this work (cron-driven plan-mode automation, escalating-retry nudges, plan-mode debug log, subagent plan-snapshot persister, auto-enable) couldn't be cleanly isolated as a numbered per-part PR because its code references PlanMode type + MAX_CONCURRENT_SUBAGENTS_IN_PLAN_MODE constant from [Plan Mode 1/6] + [Plan Mode 2/6] + [Plan Mode 3/6]. To avoid that work being visible only inside the [Plan Mode FULL] 30k bundle, it's carved out here as a focused thematic PR.
  • CI expectation: ⚠️ RED — this PR's code references symbols from [Plan Mode 1/6] + [Plan Mode 2/6] + [Plan Mode 3/6] that aren't on main yet. Local pre-commit lint hook also fires for the same reason (the PlanMode type imports resolve to any until the foundational PRs land). CI will pass once 1/6 → 3/6 + INJECTIONS merge in order, OR review the green-CI integrated state in [Plan Mode FULL].
  • Includes the INJECTIONS foundation commit so the cherry-pick is self-contained as much as possible (without dragging in PR3's foundational plan-mode files, which would balloon the diff to ~10k+).

Related PRs:


Summary

Adds the plan-mode automation + subagent-follow-up layer: cron-driven escalating-retry nudges (1/3/5-min intervals), auto-enable (model-specific opt-in for plan mode without explicit /plan on), subagent plan-snapshot persister (so a subagent's plan state is captured + restorable on resume), plan-mode debug log (operator-visible debug trail of plan-mode lifecycle events), reference card prompt (compact plan-mode rules summary the agent sees in its system prompt), and the plan-execution-nudge crons (P2.12a imperative-step nudge text).

Also bundles the INJECTIONS foundation (70a6e4b23a) so this branch compiles standalone (modulo the PlanMode type deps). Without that, pending-injection.ts would have unresolved imports.

Carved out of #68939 (closed). Originally planned as [Plan Mode 4/7] in the numbered sequence; could not be cleanly isolated due to type-dep closure on PR3+PR4 foundational files. Lives here as a thematic PR + in [Plan Mode FULL] for integration testing.

Sub-themes for review navigation

This PR bundles substantively different work areas because they were originally split as four sub-PRs in the dev history but have to ship together to compile (each references symbols introduced by the others). External-review convention reads the diff in this order:

Theme A — Plan-mode automation (the headline work)

The cron + nudge + auto-enable / debug-log / reference-card surface that gives plan mode its escalating-retry behavior.

  • src/agents/plan-mode/plan-nudge-crons.{ts,test.ts} — escalating-retry nudges (1/3/5-min)
  • src/agents/plan-mode/auto-enable.{ts,test.ts} — model-specific opt-in
  • src/agents/plan-mode/plan-mode-debug-log.{ts,test.ts} — operator debug trail
  • src/agents/plan-mode/reference-card.ts — system-prompt reference card
  • src/cron/isolated-agent/run.{ts,plan-mode.test.ts} + src/cron/normalize.ts + src/cron/types.ts — cron executor + plan-mode-aware target resolution
  • src/infra/heartbeat-runner.{ts,plan-nudge.test.ts} — heartbeat-triggered nudge dispatch

Theme B — Subagent plan-snapshot persistence

Captures + restores a subagent's plan state so it survives parent restarts / web reconnects.

  • src/gateway/plan-snapshot-persister.{ts,test.ts}
  • src/gateway/sessions-patch.subagent-gate.test.ts

Theme C — Stale-state gate fix (fresh-session-entry)

Hardens the auto-reply pipeline so plan-mode state reads from a fresh session-store entry per turn (instead of a stale in-memory cached entry that would silently drift across long-running sessions). Codex called this out as plausibly tagalong; in practice it's a bug fix uncovered while wiring the cron-nudge dispatcher.

  • src/auto-reply/reply/fresh-session-entry.{ts,test.ts}

Theme D — Subagent gating + spawn-tool refinement

Threads runId + plan-mode awareness through sessions_spawn, with subagent-registry/announce wire-up so an exit_plan_mode from a parent doesn't approve while research children are still in flight.

  • src/agents/tools/sessions-spawn-tool.{ts,test.ts}
  • src/agents/subagent-{announce,registry-run-manager,registry.test,registry.steer-restart.test}.ts

Theme E — Runner / tool-call plumbing (cross-cutting)

Threads automation context (planMode, runId, scheduler-trigger metadata) into the embedded runner + tool-call hook chain so the above themes can fire without per-call session-store reads.

  • src/agents/pi-embedded-runner/run.{ts,overflow-compaction.test.ts} + run/{helpers,incomplete-turn,params}.ts
  • src/agents/pi-tools.{ts,before-tool-call.ts}

Theme F — Schema additions (foundational)

Config schema for planMode.autoEnableFor, planMode.approvalTimeoutSeconds, nudge cadence; cron and error-codes protocol additions.

  • src/config/types.agent-defaults.ts + zod-schema.agent-defaults.ts
  • src/config/types.agents.ts + zod-schema.agent-runtime.ts
  • src/config/schema.base.generated.ts (regenerated)
  • src/gateway/protocol/schema/{cron,error-codes}.ts + protocol/index.ts
  • src/gateway/server.impl.ts (wires the new schema)

Theme G — Apps tooling (passthrough)

  • apps/macos/Sources/OpenClawProtocol/GatewayModels.swift + apps/shared/.../GatewayModels.swift — generated Swift mirrors of the protocol additions

Why not split into 4 sub-PRs?

Codex review suggested splitting Themes A / B / C / D. We considered it but the PR-budget on the maintainer side is constrained (we're already at 9 plan-mode PRs); adding 3 more sub-PRs trades one cleanup problem for another. The themed structure above is provided so reviewers can navigate this PR as if it were 4 sub-PRs without the maintenance overhead of actually splitting it.

Overlap with [Plan Mode INJECTIONS] (#70088)

This PR includes the typed pending-injection queue foundation (cherry-picked commit 70a6e4b23a) so the branch compiles standalone. The 6 files involved are:

  • src/agents/plan-mode/injections.{ts,test.ts}
  • src/agents/pi-embedded-runner/pending-injection.ts
  • src/commands/sessions.ts (small change)
  • src/commands/status.summary.ts (small change)
  • src/config/sessions/types.ts (queue-type additions, ~240 lines)

For review purposes: read these files in #70088, not here. Once #70088 merges to main, this PR's diff vs main automatically loses these files.

What This PR Includes

Plan-mode automation (new files)

  • Plan nudge crons (src/agents/plan-mode/plan-nudge-crons.ts + test) — escalating-retry nudges at 1/3/5-min intervals when an agent appears stuck mid-plan. Idempotent against the cycleId.
  • Auto-enable (src/agents/plan-mode/auto-enable.ts + test) — model-specific opt-in to plan mode driven by agents.defaults.planMode.autoEnableFor config.
  • Plan-mode debug log (src/agents/plan-mode/plan-mode-debug-log.ts + test) — operator-visible debug trail of plan-mode lifecycle events (entered, approved, rejected, cycle restart, etc.).
  • Reference card (src/agents/plan-mode/reference-card.ts) — compact plan-mode rules summary added to the agent's system prompt.
  • Plan execution nudge crons (P2.12a) — imperative-step nudge text added to the cron-driven nudge body.

Subagent follow-ups

  • Plan-snapshot persister (src/gateway/plan-snapshot-persister.ts + test) — captures + restores a subagent's plan state across runs.
  • Subagent gate (src/gateway/sessions-patch.subagent-gate.test.ts) — gate for subagent-spawned sessions to inherit parent plan-mode context appropriately.
  • Subagent registry updates (src/agents/subagent-registry*) — wire-up changes for plan-mode-aware spawn lifecycle.

Heartbeat + runner integration

  • Heartbeat plan-nudge (src/infra/heartbeat-runner.plan-nudge.test.ts + impl changes in src/agents/pi-embedded-runner/run.ts + pending-injection.ts) — heartbeat-triggered nudge dispatch.
  • Pre-LLM injection plumbing (src/agents/pi-tools.before-tool-call.ts, pi-tools.ts, pi-embedded-runner/run/{params,attempt,incomplete-turn}.ts) — threads automation hooks through the runner.

Schema additions

  • src/config/types.agent-defaults.ts, src/config/zod-schema.agent-defaults.tsplanMode.autoEnableFor, planMode.approvalTimeoutSeconds (schema-reserved), nudge cadence config.

Foundational (bundled for compile only — same content as [Plan Mode INJECTIONS] #70088)

  • src/agents/plan-mode/injections.ts + test
  • src/agents/pi-embedded-runner/pending-injection.ts
  • Schema additions to src/config/sessions/types.ts for the queue

Files In Scope

Primary review targets (the actual automation work):

  • src/agents/plan-mode/plan-nudge-crons.ts + test
  • src/agents/plan-mode/auto-enable.ts + test
  • src/agents/plan-mode/plan-mode-debug-log.ts + test
  • src/gateway/plan-snapshot-persister.ts + test
  • src/agents/plan-mode/reference-card.ts

Supporting:

  • Runner plumbing in src/agents/pi-embedded-runner/
  • Schema additions in src/config/
  • Foundational queue (also in [Plan Mode INJECTIONS])

Reviewer Guide

  1. Start with: plan-nudge-crons.ts (15 min) — the escalating-retry semantics + cycleId idempotency
  2. Then: auto-enable.ts — model-specific opt-in logic
  3. Then: plan-mode-debug-log.ts — operator debug surface
  4. Then: plan-snapshot-persister.ts — subagent state continuity
  5. Then: reference-card.ts — system-prompt addition
  6. Skip: the bundled INJECTIONS files if you've already reviewed them in [Plan Mode INJECTIONS] Typed pending-injection queue foundation #70088

What This PR Does NOT Include

  • Foundational plan-mode files (plan-mode/index.ts, types.ts, mutation-gate.ts, approval.ts) → land in [Plan Mode 1/6] + [Plan Mode 2/6]. This PR's code references those types (which is why CI is red until those merge).
  • Executing-state lifecycle (3-state mode), executing-phase nudges, [PLAN_STATUS] auto-inject preamble → folded into [Plan Mode FULL] only (separate work)
  • Plan UI / channels / docs → numbered per-part PRs

Issue references

Test Status

  • Unit tests: passing for the new files (cron, auto-enable, debug-log, snapshot-persister)
  • Integration: heartbeat + plan-nudge integration smoke
  • ⚠️ Local pre-commit lint hook fails on PlanMode type resolution (resolves to any until [Plan Mode 1/6] + [Plan Mode 2/6] merge to main). Cherry-pick used git -c core.hooksPath=/dev/null cherry-pick --continue to bypass; this is the same red-CI-expected pattern as the numbered per-part PRs.
  • Full pre-commit lint will pass once foundational PRs merge to main.

Carry-forward / deferred

  • agents.defaults.planMode.approvalTimeoutSeconds — schema-reserved here; runtime wiring deferred to a follow-up cycle
  • Subagent plan-mode visibility into parent session's cycleId — initial implementation here; refinement may come in a follow-up

@greptile-apps

greptile-apps Bot commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds the plan-mode automation layer: escalating-retry nudge crons (10/30/60-min intervals), model-specific auto-enable matching, a structured plan-mode debug log, a reference-card system-prompt injection, and a subagent plan-snapshot persister. The work is carved out from the numbered [Plan Mode 1/6–6/6] stack because it depends on types/constants from those foundational PRs; CI is expectedly red until that stack lands.

The new files are well-structured and thoroughly tested. The persistSnapshot auto-close gate is carefully double-checked inside the store lock (preflight + locked re-evaluation) with good commentary. No correctness or security issues were found — all remaining findings are P2 style/cleanup items.

Confidence Score: 5/5

Safe to merge once the foundational Plan Mode PRs (#70031#70070) land; no correctness or security issues found.

All findings are P2 style/cleanup items. The critical auto-close gate logic, cycleId idempotency, and approval metadata persistence are sound and well-covered by unit tests.

No files require special attention beyond the three P2 items noted inline.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/agents/plan-mode/plan-nudge-crons.ts
Line: 112-122

Comment:
**Dynamic import re-executed inside the loop body**

`assertSafeCronSessionTargetId` is dynamically imported on every iteration of the `intervals` for-loop (once per nudge, up to 3 times with defaults). The `sessionKey` being validated doesn't change between iterations — if it is invalid the whole schedule call should be aborted before entering the loop at all. As written, the validation check also can't `break` the outer loop; it `continue`s, so the remaining intervals still schedule redundantly. Hoisting both the import and the validation outside the loop would short-circuit immediately on a bad key.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/gateway/plan-snapshot-persister.ts
Line: 726-730

Comment:
**Stale preflight `allowAutoClose` used in post-write logging branch**

The `else if` checks `!allowAutoClose` (the preflight value) rather than `!appliedAllowAutoClose` (the value actually committed to the store). When the store-lock check flips the decision from allow→deny, neither the line 675 branch nor this branch fires — so no "auto-close suppressed" log appears in the post-write section. The lock-flip warn at line 612-619 inside the callback captures the flip, but consider replacing `!allowAutoClose` with `!appliedAllowAutoClose` to keep the post-write log consistent with the actual applied outcome.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/agents/plan-mode/plan-mode-debug-log.ts
Line: 154-156

Comment:
**`isDebugEnabled` is a trivial one-liner wrapper with no added value**

`isDebugEnabled()` exists solely as `return isPlanModeDebugEnabled()`. Since `isPlanModeDebugEnabled` is now exported directly, the private wrapper adds a call frame without any encapsulation benefit. Calling `isPlanModeDebugEnabled()` directly in `logPlanModeDebug` would eliminate the indirection.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "test(plan-mode): add mandatory `title` a..." | Re-trigger Greptile

Comment on lines +112 to +122
const { assertSafeCronSessionTargetId } = await import("../../cron/session-target.js");
try {
assertSafeCronSessionTargetId(params.sessionKey);
} catch (validationErr) {
params.log?.warn?.(
`plan-nudge schedule skipped: sessionKey "${params.sessionKey}" fails cron sessionTarget validation: ${
validationErr instanceof Error ? validationErr.message : String(validationErr)
}`,
);
continue;
}

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.

P2 Dynamic import re-executed inside the loop body

assertSafeCronSessionTargetId is dynamically imported on every iteration of the intervals for-loop (once per nudge, up to 3 times with defaults). The sessionKey being validated doesn't change between iterations — if it is invalid the whole schedule call should be aborted before entering the loop at all. As written, the validation check also can't break the outer loop; it continues, so the remaining intervals still schedule redundantly. Hoisting both the import and the validation outside the loop would short-circuit immediately on a bad key.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/plan-mode/plan-nudge-crons.ts
Line: 112-122

Comment:
**Dynamic import re-executed inside the loop body**

`assertSafeCronSessionTargetId` is dynamically imported on every iteration of the `intervals` for-loop (once per nudge, up to 3 times with defaults). The `sessionKey` being validated doesn't change between iterations — if it is invalid the whole schedule call should be aborted before entering the loop at all. As written, the validation check also can't `break` the outer loop; it `continue`s, so the remaining intervals still schedule redundantly. Hoisting both the import and the validation outside the loop would short-circuit immediately on a bad key.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +726 to +730
} else if (params.closeOnComplete && !allowAutoClose) {
log.info(
`plan completed but auto-close suppressed (no approved state): sessionKey=${params.sessionKey} — ` +
"agent must call exit_plan_mode for explicit user approval before mutations unlock",
);

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.

P2 Stale preflight allowAutoClose used in post-write logging branch

The else if checks !allowAutoClose (the preflight value) rather than !appliedAllowAutoClose (the value actually committed to the store). When the store-lock check flips the decision from allow→deny, neither the line 675 branch nor this branch fires — so no "auto-close suppressed" log appears in the post-write section. The lock-flip warn at line 612-619 inside the callback captures the flip, but consider replacing !allowAutoClose with !appliedAllowAutoClose to keep the post-write log consistent with the actual applied outcome.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/plan-snapshot-persister.ts
Line: 726-730

Comment:
**Stale preflight `allowAutoClose` used in post-write logging branch**

The `else if` checks `!allowAutoClose` (the preflight value) rather than `!appliedAllowAutoClose` (the value actually committed to the store). When the store-lock check flips the decision from allow→deny, neither the line 675 branch nor this branch fires — so no "auto-close suppressed" log appears in the post-write section. The lock-flip warn at line 612-619 inside the callback captures the flip, but consider replacing `!allowAutoClose` with `!appliedAllowAutoClose` to keep the post-write log consistent with the actual applied outcome.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +154 to +156
function isDebugEnabled(): boolean {
return isPlanModeDebugEnabled();
}

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.

P2 isDebugEnabled is a trivial one-liner wrapper with no added value

isDebugEnabled() exists solely as return isPlanModeDebugEnabled(). Since isPlanModeDebugEnabled is now exported directly, the private wrapper adds a call frame without any encapsulation benefit. Calling isPlanModeDebugEnabled() directly in logPlanModeDebug would eliminate the indirection.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/plan-mode/plan-mode-debug-log.ts
Line: 154-156

Comment:
**`isDebugEnabled` is a trivial one-liner wrapper with no added value**

`isDebugEnabled()` exists solely as `return isPlanModeDebugEnabled()`. Since `isPlanModeDebugEnabled` is now exported directly, the private wrapper adds a call frame without any encapsulation benefit. Calling `isPlanModeDebugEnabled()` directly in `logPlanModeDebug` would eliminate the indirection.

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copilot AI 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.

Pull request overview

Adds the plan-mode automation + follow-up layer across runner/gateway/cron: plan continuation nudges (heartbeat + cron), model-based plan-mode auto-enable, plan-mode debug logging + reference card, subagent lifecycle gating/continuations, and the typed pending-injection queue foundation with schema + protocol updates.

Changes:

  • Introduces plan-mode automation primitives (nudge cron scheduling/guards, heartbeat plan nudge prefix, auto-enable matcher, debug log, reference card, plan-mode status tool).
  • Wires plan-mode/subagent state through gateway + runner + cron (session row surfaces, event subscriptions, subagent concurrency caps, plan snapshot persistence hooks).
  • Expands schemas/protocol for plan-mode execution (new error codes, cron payload planCycleId, session entry fields, config schema additions, test configs).

Reviewed changes

Copilot reviewed 66 out of 66 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/vitest/vitest.plan-mode.config.ts Adds a dedicated Vitest config/scope for plan-mode test/coverage runs.
src/plugins/contracts/plugin-sdk-runtime-api-guardrails.test.ts Updates runtime export allowlist for Telegram (sendDocumentTelegram, TelegramDocumentOpts).
src/infra/heartbeat-runner.ts Adds buildActivePlanNudge and prepends plan nudge to heartbeat prompt; simplifies plugin resolution.
src/infra/heartbeat-runner.plan-nudge.test.ts Unit tests for the heartbeat plan nudge prefix builder and suppression guards.
src/gateway/session-utils.types.ts Extends gateway session row shape to include exec/plan/pending-interaction fields.
src/gateway/session-utils.ts Refactors session loading/canonicalization and combined store merging; removes deleted-agent helper logic.
src/gateway/server.impl.ts Refactors startup/close wiring; introduces plan snapshot unsubscribe into close handler.
src/gateway/server-runtime-subscriptions.ts Wires plan snapshot persister + subagent gate persistence and broadcasts sessions.changed.
src/gateway/server-runtime-handles.ts Tracks planSnapshotUnsub in mutable runtime state.
src/gateway/server-methods/sessions.ts Extends sessions.changed payload and adjusts send/steer flow.
src/gateway/server-close.ts Ensures plan snapshot subscription is unsubscribed on shutdown.
src/gateway/server-close.test.ts Updates close-handler tests for new constructor shape and planSnapshotUnsub.
src/gateway/protocol/schema/error-codes.ts Adds plan-approval subagent gate error codes with structured details expectations.
src/gateway/protocol/schema/cron.ts Adds optional planCycleId to cron agent-turn payload schema.
src/gateway/protocol/index.ts Re-exports ErrorCode and removes unused channels.start validation exports.
src/gateway/plan-snapshot-persister.test.ts Adds guard-rail tests for approvalRunId defensive behavior.
src/cron/types.ts Adds planCycleId to CronAgentTurnPayloadFields.
src/cron/normalize.ts Consolidates/clarifies sessionTarget:"current" resolution semantics.
src/cron/isolated-agent/run.ts Adds plan-cycle-bound nudge skip guards and wires plan-mode auto-enable at cron turn start.
src/cron/isolated-agent/run.plan-mode.test.ts Tests for plan-cycle nudge guards + autoEnableFor runtime wiring in cron runs.
src/config/zod-schema.agent-runtime.ts Adds per-agent embeddedPi.autoContinue + maxIterations runtime schema.
src/config/zod-schema.agent-defaults.ts Adds defaults schema for embeddedPi autoContinue/maxIterations and planMode (enabled/autoEnableFor/debug).
src/config/types.agents.ts Adds per-agent embeddedPi.autoContinue + maxIterations types.
src/config/types.agent-defaults.ts Adds embeddedPi autoContinue/maxIterations and planMode autoEnableFor/debug types + docs.
src/config/sessions/types.ts Adds plan-mode session state fields, typed pending-injection queue types, and token-total helper rename.
src/config/schema.base.generated.ts Regenerates base config schema for new embeddedPi + planMode fields and updates compaction help text.
src/commands/status.summary.ts Switches to resolveFreshSessionTotalTokens for status cost display.
src/commands/sessions.ts Adjusts token total semantics (fresh vs preserved stale totals) for CLI + JSON output.
src/auto-reply/reply/fresh-session-entry.ts Adds helpers to read latest planMode/acceptEdits/session entry from disk (skip-cache) for freshness.
src/auto-reply/reply/agent-runner.misc.runreplyagent.test.ts Updates test expectations to reflect new skip-cache disk reads.
src/agents/tools/update-plan-tool.test.ts Expands tests for non-empty content, close-on-complete, and closure gate criteria.
src/agents/tools/sessions-spawn-tool.ts Adds plan-mode-aware concurrency cap, cleanup keep behavior, and parent openSubagent tracking.
src/agents/tools/sessions-spawn-tool.test.ts Updates mocks/tests and adds coverage for plan-mode subagent concurrency cap.
src/agents/tools/plan-mode-status-tool.ts Adds plan_mode_status tool for read-only plan-mode introspection (disk + run ctx).
src/agents/tools/cron-tool.ts Adds recipe documentation for sessionTarget:"current" resume-after-wait.
src/agents/tool-display-config.ts Adds display config for plan tools (enter/exit/status) and fixes ask_user_question detail keys.
src/agents/subagent-registry.test.ts Updates agent-events mocks and loosens outcome shape expectations.
src/agents/subagent-registry.steer-restart.test.ts Ensures agent-events mock includes actual exports; adds openSubagentRunIds remap test.
src/agents/subagent-registry-run-manager.ts Drains/replaces parent openSubagentRunIds on child completion/steer restart; outcome update logic tweak.
src/agents/subagent-announce.ts Adds plan-mode-aware announce instruction suffix and reads requester plan mode on announce.
src/agents/plan-mode/reference-card.ts Adds compact plan-mode reference card for system prompt injection.
src/agents/plan-mode/plan-nudge-crons.ts Implements scheduling + cleanup for plan nudge one-shot cron jobs.
src/agents/plan-mode/plan-nudge-crons.test.ts Unit tests for plan nudge cron schedule/cleanup behavior.
src/agents/plan-mode/plan-mode-debug-log.ts Adds opt-in plan-mode debug event logger with env/config gating and TTL caching.
src/agents/plan-mode/integration.test.ts Adds wiring smoke test for plan-mode tool enablement + mutation gate behavior.
src/agents/plan-mode/injections.ts Adds typed pending-injection queue (enqueue/consume/compose) with legacy migration.
src/agents/plan-mode/auto-enable.ts Adds model-id regex matching with compiled-pattern cache for plan-mode auto-enable.
src/agents/plan-mode/auto-enable.test.ts Unit tests for auto-enable matching + malformed-pattern handling.
src/agents/pi-tools.ts Threads planMode + live accessors into before-tool-call hook context.
src/agents/pi-tools.before-tool-call.ts Adds plan-mode mutation gate + post-approval acceptEdits constraint gate with live refresh semantics.
src/agents/pi-embedded-runner/skills-runtime.test.ts Updates snapshot reload logic tests to account for resolvedPlanTemplates presence.
src/agents/pi-embedded-runner/run/params.ts Threads planMode + live accessors through embedded runner params.
src/agents/pi-embedded-runner/run/helpers.ts Raises default retry iteration budget; adds user override and subagent cap constant.
src/agents/pi-embedded-runner/run.overflow-compaction.test.ts Keeps test fast by overriding maxIterations and updating expectations accordingly.
src/agents/pi-embedded-runner/pending-injection.ts Adds backward-compat shim over typed injection queue consumer + composer.
src/agents/pi-embedded-runner/pending-injection.test.ts Adds tests for pending injection consumption/composition with hermetic session store.
apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift Adds new plan-approval gate error codes to Swift client models.
apps/macos/Sources/OpenClawProtocol/GatewayModels.swift Adds new plan-approval gate error codes to macOS Swift client models.
Comments suppressed due to low confidence (1)

src/gateway/server.impl.ts:232

  • startupTrace is referenced (e.g. startupTrace.measure(...)) but is no longer declared since createGatewayStartupTrace() and const startupTrace = ... were removed. This will fail to compile; either restore a startupTrace implementation (even a no-op that provides mark/measure) or remove all startupTrace.* call sites in this file.

Comment on lines 990 to 993
resolvedDelivery: {
channel: prepared.context.resolvedDelivery.channel,
to: prepared.context.resolvedDelivery.to,
accountId: prepared.context.resolvedDelivery.accountId,
threadId: prepared.context.resolvedDelivery.threadId,
},

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

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

When calling executeCronRun, resolvedDelivery is now passed with only { channel, accountId }, dropping to and threadId. executeCronRun (and downstream cron message-tool executor) accepts/uses to/threadId for targeting; omitting them will lose explicit delivery targets for cron agent turns. Pass through to and threadId from prepared.context.resolvedDelivery here as well.

Copilot uses AI. Check for mistakes.
Comment thread src/gateway/session-utils.ts Outdated
Comment on lines +395 to +412
export function loadSessionEntry(sessionKey: string) {
const cfg = loadConfig();
const key = normalizeOptionalString(sessionKey) ?? "";
const target = resolveGatewaySessionStoreTarget({
const canonicalKey = resolveSessionStoreKey({ cfg, sessionKey });
const agentId = resolveSessionStoreAgentId(cfg, canonicalKey);
const { storePath, store } = resolveGatewaySessionStoreLookup({
cfg,
key,
key: normalizeOptionalString(sessionKey) ?? "",
canonicalKey,
agentId,
});
const storePath = target.storePath;
const store = loadSessionStore(storePath);
const freshestMatch = resolveFreshestSessionStoreMatchFromStoreKeys(store, target.storeKeys);
const legacyKey = freshestMatch?.key !== target.canonicalKey ? freshestMatch?.key : undefined;
return {
const target = resolveGatewaySessionStoreTarget({
cfg,
storePath,
key: normalizeOptionalString(sessionKey) ?? "",
store,
entry: freshestMatch?.entry,
canonicalKey: target.canonicalKey,
legacyKey,
};
});
const freshestMatch = resolveFreshestSessionStoreMatchFromStoreKeys(store, target.storeKeys);
const legacyKey = freshestMatch?.key !== canonicalKey ? freshestMatch?.key : undefined;
return { cfg, storePath, store, entry: freshestMatch?.entry, canonicalKey, legacyKey };

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

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

This refactor removes the exported resolveDeletedAgentIdFromSessionKey helper, but other gateway modules still import/use it (e.g. server-methods/chat.ts, sessions-resolve.ts, and related tests). As-is this will cause build failures and/or drop the deleted-agent guard behavior (#65524). Either keep exporting the helper from session-utils.ts or update all call sites to the new deleted-agent validation mechanism (and adjust tests accordingly).

Copilot uses AI. Check for mistakes.
Comment on lines -482 to 487
);
return;
}
const { entry, canonicalKey, storePath } = loadSessionEntry(key);
if (!entry?.sessionId) {

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

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

The deleted-agent guard for sessions.send/sessions.steer appears to have been removed (previously rejected sends targeting sessions whose owning agent was deleted, #65524). This changes behavior and will likely break the existing deleted-agent guard tests in src/gateway/server-methods/sessions.send-deleted-agent.test.ts. Consider reintroducing the resolveDeletedAgentIdFromSessionKey check here (matching chat.send) or moving the guard into loadSessionEntry/session resolution so both send paths consistently fail closed.

Copilot uses AI. Check for mistakes.
Comment on lines +37 to +41
* compatible): on store-write failure inside the underlying queue
* helper, the queue helper drops the captured entries and returns an
* empty array — favoring the once-and-only-once guarantee over
* caller-can-still-inject. Operators see the warn-log line for any
* disk failure path.

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

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

The docstring claims that on store-write failure the underlying queue consumer "drops the captured entries and returns an empty array". But consumePendingAgentInjections() is implemented to return any captured entries even if persisting the clear fails (best-effort delivery). Update this comment to match the actual error semantics so callers/operators aren't misled during debugging.

Suggested change
* compatible): on store-write failure inside the underlying queue
* helper, the queue helper drops the captured entries and returns an
* empty array favoring the once-and-only-once guarantee over
* caller-can-still-inject. Operators see the warn-log line for any
* disk failure path.
* compatible): if the underlying queue helper captures pending entries
* but then fails to persist the queue clear, it still returns the
* captured entries rather than an empty array. This favors
* caller-can-still-inject best-effort delivery, and operators see the
* warn-log line for any disk failure path.

Copilot uses AI. Check for mistakes.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 55a76b9012

ℹ️ 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 1673 to 1677
run({
reason: params.reason,
agentId: params.agentId,
sessionKey: params.sessionKey,
heartbeat: params.heartbeat,
});

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.

P1 Badge Preserve wake heartbeat overrides in targeted runs

startHeartbeatRunner no longer forwards params.heartbeat from the wake request, so targeted wakes now always run with targetAgent.heartbeat defaults. This drops requestHeartbeatNow({ heartbeat: { target: "last" } }) overrides used by cron/server wake paths, which can reroute or suppress heartbeat delivery when the agent default target differs from the requested one.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Real concern, acknowledged. Not fixed in this immediate cycle (we're in cleanup-mode focused on maintainer-takeover; see #70101 for context). Tracked in the umbrella's deferred-items checklist for the bot-triage follow-up cycle. The fix will land on this branch with a Fixed in {SHA} reply here per the standard pr-review-loop pattern. Maintainers can also opt to merge the per-part PR as-is and address in a post-merge polish cycle.

Comment on lines 990 to 993
resolvedDelivery: {
channel: prepared.context.resolvedDelivery.channel,
to: prepared.context.resolvedDelivery.to,
accountId: prepared.context.resolvedDelivery.accountId,
threadId: prepared.context.resolvedDelivery.threadId,
},

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.

P1 Badge Pass full resolved delivery target into cron executor

runCronIsolatedAgentTurn now strips to and threadId when calling executeCronRun, so downstream execution always sees those fields as undefined. The executor uses these values to compute currentChannelId and to pass messageTo/messageThreadId into runEmbeddedPiAgent; dropping them breaks explicit recipient/thread routing for cron turns (especially topic/threaded channels) and can cause tool-side messages to target the wrong place or fail policy checks.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Real concern, acknowledged. Not fixed in this immediate cycle (we're in cleanup-mode focused on maintainer-takeover; see #70101 for context). Tracked in the umbrella's deferred-items checklist for the bot-triage follow-up cycle. The fix will land on this branch with a Fixed in {SHA} reply here per the standard pr-review-loop pattern. Maintainers can also opt to merge the per-part PR as-is and address in a post-merge polish cycle.

@100yenadmin

Copy link
Copy Markdown
Contributor Author

Acknowledging Greptile / Copilot / Codex review feedback on this PR. Triage status:

  • Stack-coordination concerns (e.g. unresolved imports referencing symbols added by earlier per-part PRs in the stack) — expected by design. Red CI on this PR is per the rollout plan; CI turns green as the chain merges in sequence (1/6 → 6/6). See the stack-position header banner at the top of this PR body.
  • Real source-code issues flagged here (P1 bugs, P2 nits, etc.) — will be triaged + fixed in a focused follow-up cycle within ~24h. Fix SHAs will be posted as in-line replies on each individual comment per the standard pr-review-loop pattern.

For end-to-end integration testing in the meantime, see #70071 [Plan Mode FULL] — the green-CI integrated bundle of all parts.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0b1ed28085

ℹ️ 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 432 to 436
export const validateChannelsStatusParams = ajv.compile<ChannelsStatusParams>(
ChannelsStatusParamsSchema,
);
export const validateChannelsStartParams =
ajv.compile<ChannelsStartParams>(ChannelsStartParamsSchema);
export const validateChannelsLogoutParams = ajv.compile<ChannelsLogoutParams>(
ChannelsLogoutParamsSchema,

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.

P1 Badge Restore channels.start validator export from protocol index

validateChannelsStartParams is no longer defined/exported here, but src/gateway/server-methods/channels.ts still imports and calls it for the channels.start RPC. That leaves the handler with a missing symbol (ESM module-link failure at startup, or undefined at runtime depending build target), which breaks channel startup requests even though ChannelsStartParamsSchema still exists in the protocol schema.

Useful? React with 👍 / 👎.

Comment thread src/agents/pi-tools.ts Outdated
Comment on lines +422 to +424
const subagentPolicy =
options?.sessionKey &&
isSubagentEnvelopeSession(options.sessionKey, {
cfg: options.config,
store: subagentStore,
})
? resolveSubagentToolPolicyForSession(options.config, options.sessionKey, {
store: subagentStore,
})
isSubagentSessionKey(options?.sessionKey) && options?.sessionKey
? resolveSubagentToolPolicyForSession(options.config, options.sessionKey)

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.

P1 Badge Apply subagent policy to ACP envelope sessions

This now gates subagent policy only on isSubagentSessionKey(...), which excludes ACP session keys (agent:...:acp:...). ACP children are still treated as subagent envelopes via stored subagentRole/subagentControlScope in subagent-capabilities.ts, so this change skips resolveSubagentToolPolicyForSession for ACP runs and removes core-tool restrictions (e.g. leaf ACP children can access tools that should stay denied such as sessions_spawn/subagents).

Useful? React with 👍 / 👎.

…s Re-cut from merged-upstream state (v4)

Cherry-pick / file-copy of 66 files from feat/plan-channel-parity-merged-upstream
(merged-head 651a22d) onto current upstream/main. Branch: isolated/pm-automation-followups.

v4 absorbs the upstream merge into plan-mode dev (Option B; merge commit
b1ead74) plus the post-merge build/test fixes (commit 651a22d).
@100yenadmin
100yenadmin force-pushed the isolated/pm-automation-followups branch from 0b1ed28 to 21292e5 Compare April 22, 2026 14:37

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 21292e5e59

ℹ️ 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 759 to 761
groupChannel: params.groupChannel,
groupSpace: params.groupSpace,
memberRoleIds: params.memberRoleIds,
spawnedBy: params.spawnedBy,

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.

P1 Badge Preserve memberRoleIds when building attempt params

This call stopped forwarding memberRoleIds into runEmbeddedAttemptWithBackend, so downstream tool construction now always sees params.memberRoleIds as undefined. In group-channel runs (Discord/Slack/etc.), role-aware policy checks in createOpenClawCodingTools rely on those ids (src/agents/pi-embedded-runner/run/attempt.ts:511), so this regression can incorrectly deny/allow role-gated actions and break channel permission behavior for any session that depends on member roles.

Useful? React with 👍 / 👎.

@@ -261,10 +268,6 @@ export async function runEmbeddedPiAgent(
config: params.config,
});
const resolvedWorkspace = workspaceResolution.workspaceDir;

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.

P2 Badge Restore canonical workspace detection before attempt routing

The canonical-workspace derivation was removed here, and the attempt params no longer carry isCanonicalWorkspace; as a result, bootstrap routing falls back to its default true path (src/agents/pi-embedded-runner/run/attempt-bootstrap-routing.ts:44-46). For non-default workspaces where effectiveWorkspace === resolvedWorkspace, runs are now misclassified as canonical, which changes resolveBootstrapMode to full instead of limited (src/agents/bootstrap-mode.ts:23) and can inject/record canonical bootstrap behavior in the wrong workspace.

Useful? React with 👍 / 👎.

@prtags

prtags Bot commented Apr 23, 2026

Copy link
Copy Markdown

Related work from PRtags group ace-bullfrog-hw3e

Title: Open PR candidate: plan-mode carve-out overlaps integrated full bundle

Number Title
#70031 [Plan Mode 1/6] Plan-state foundation
#70066 [Plan Mode 2/6] Core backend MVP
#70067 [Plan Mode 3/6] Advanced plan interactions
#70068 [Plan Mode 4/6] Web UI + i18n
#70069 [Plan Mode 5/6] Text channels + Telegram
#70070 [Plan Mode 6/6] Docs, QA, and help
#70071 [Plan Mode FULL] Integrated bundle for testing (Parts 1\u20136 + automation + executing-state lifecycle)
#70088 [Plan Mode INJECTIONS] Typed pending-injection queue foundation
#70089* [Plan Mode AUTOMATION] Cron nudges + auto-enable + subagent follow-ups

* This PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling app: macos App: macos app: web-ui App: web-ui commands Command implementations gateway Gateway runtime size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants