Skip to content

[Plan Mode 6/8] Text channels + Telegram attachments#70018

Closed
100yenadmin wants to merge 8 commits into
openclaw:mainfrom
electricsheephq:restack/68939-pr7-8-channels
Closed

[Plan Mode 6/8] Text channels + Telegram attachments#70018
100yenadmin wants to merge 8 commits into
openclaw:mainfrom
electricsheephq:restack/68939-pr7-8-channels

Conversation

@100yenadmin

Copy link
Copy Markdown
Contributor

Summary

[Plan Mode 6/8] — part of the 8-part decomposition of #68939, stacking on #69449 (Part 1).

Stacks on: previous part (see [Plan Mode 5/8] / [Plan Mode 1/9 #69449]).

Universal /plan slash commands across text channels (Telegram, Discord, Slack, WhatsApp) + Telegram markdown attachment delivery via sendDocumentTelegram SDK facade. Combines Parts 7+8 since Part 8 was <1000 lines. Stacks on Part 5/8.

Lines: ~3401

Diff note

This PR is opened cross-repo (head: 100yenadmin:restack/68939-pr7-8-channels) against openclaw:main. Because GitHub doesn't support stacked-PR bases that point at fork refs, the displayed diff cumulatively includes all previous parts in the stack. Reviewers should focus on the changes specific to this part — see commit list for the new content beyond what previous parts established.

Test status

After post-v2026.4.21 rebase: all targeted plan-mode tests pass (53/53 in unit-fast for approval + integration suites). The full suite has pre-existing vitest workspace project-name conflicts unrelated to this work.

Related

Eva added 8 commits April 22, 2026 12:39
…ion tests (rebase fixup)

Pure rebase fixup. The `exit_plan_mode` tool gained a mandatory `title`
parameter in upstream main between Part 5's original base and current
HEAD. After rebasing Part 5 onto upstream/main, four integration tests
(in `src/agents/plan-mode/integration.test.ts`) were failing with
`ToolInputError: exit_plan_mode requires a title field` instead of
the originally-asserted error messages.

This commit just adds `title: "..."` to the four failing test calls
so they exercise the validation paths they were originally testing
(empty plan, multiple in_progress, unknown status).

No production code changes.
Copilot AI review requested due to automatic review settings April 22, 2026 06:20
@greptile-apps

greptile-apps Bot commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Too many files changed for review. (173 files found, 100 file limit)

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

Note

Copilot was unable to run its full agentic suite in this review.

This PR expands plan-mode support across text channels and adds Telegram document attachment delivery, while hardening plan-mode runtime state, cron wake-ups, and subagent gating/diagnostics.

Changes:

  • Adds plan-mode protocol fields (planMode, planApproval, lastPlanSteps) plus new plan-approval error codes for subagent gating.
  • Introduces plan-mode runtime tools, mutation gating, cron “plan nudge” wake-ups, and skill plan-template seeding.
  • Adds Telegram sendDocumentTelegram helper and plumbing for plan markdown attachment delivery.

Reviewed changes

Copilot reviewed 107 out of 173 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/gateway/protocol/schema/sessions.ts Adds plan-mode patch fields and discriminated union for approval actions.
src/gateway/protocol/schema/error-codes.ts Adds plan-approval subagent gating error codes.
src/gateway/protocol/schema/cron.ts Adds optional planCycleId to cron agent-turn payload schema.
src/gateway/protocol/index.ts Removes ChannelsStart artifacts; re-exports ErrorCode type.
src/gateway/plan-snapshot-persister.test.ts Adds tests for approvalRunId guard in plan snapshot persister.
src/cron/types.ts Adds planCycleId type to cron payload.
src/cron/normalize.ts Consolidates sessionTarget="current" normalization logic and improves docs.
src/cron/isolated-agent/run.ts Adds plan-mode autoEnableFor + planCycleId nudge guards; tweaks finalize/run context.
src/cron/isolated-agent/run.plan-mode.test.ts Adds tests for plan-cycle nudge guards + plan-mode autoEnableFor behavior.
src/config/zod-schema.ts Documents/adds skills.limits.maxPlanTemplateSteps.
src/config/zod-schema.agent-runtime.ts Adds embeddedPi autoContinue + maxIterations per-agent overrides.
src/config/zod-schema.agent-defaults.ts Adds embeddedPi defaults + planMode defaults schema.
src/config/types.skills.ts Adds maxPlanTemplateSteps config type + docs.
src/config/types.agents.ts Adds per-agent embeddedPi autoContinue and maxIterations types.
src/config/types.agent-defaults.ts Adds defaults types/docs for embeddedPi + planMode + compaction notice wording.
src/config/schema.base.generated.ts Regenerates base config schema for new embeddedPi/planMode/skills limits docs.
src/commands/status.summary.ts Switches to fresh token totals resolver.
src/commands/sessions.ts Preserves stale totals in JSON output; uses fresh totals for display list.
src/auto-reply/reply/fresh-session-entry.ts Adds disk-fresh helpers for planMode and acceptEdits and fresh entry reads.
src/auto-reply/reply/commands-handlers.runtime.ts Registers universal /plan command handler.
src/auto-reply/reply/agent-runner.misc.runreplyagent.test.ts Updates assertion for skip-cache store load now expected by plan-mode freshness path.
src/auto-reply/commands-registry.shared.ts Registers /plan command definition for all channels.
src/agents/transport-message-transform.ts Improves missing tool_result placeholder text + caps log volume/memory.
src/agents/tools/update-plan-tool.test.ts Updates update_plan content assertions; adds close-on-complete + closure gate tests.
src/agents/tools/sessions-spawn-tool.ts Adds plan-mode subagent concurrency cap, parent tracking, and cleanup keep override in plan mode.
src/agents/tools/sessions-spawn-tool.test.ts Removes role-forwarding expectations; adds concurrency-cap regression tests.
src/agents/tools/plan-mode-status-tool.ts Adds plan_mode_status read-only introspection tool.
src/agents/tools/enter-plan-mode-tool.ts Adds enter_plan_mode tool.
src/agents/tools/cron-tool.ts Documents “resume after wait” recipe using sessionTarget="current".
src/agents/tools/ask-user-question-tool.ts Adds ask_user_question tool for approval-card question flows.
src/agents/tools/ask-user-question-tool.test.ts Adds tests for ask_user_question validation and deterministic IDs.
src/agents/tool-display-config.ts Adds display config for new plan-mode tools + fixes ask_user_question detailKeys.
src/agents/tool-description-presets.ts Adds descriptions/summaries for plan tools and strengthens plan-mode lifecycle docs.
src/agents/tool-catalog.ts Registers plan-mode tools in the core tool catalog.
src/agents/test-helpers/fast-openclaw-tools-sessions.ts Updates update_plan tool mock signature and exports statuses.
src/agents/subagent-registry.test.ts Mocks new drain helper; adjusts outcome expectations.
src/agents/subagent-registry.steer-restart.test.ts Uses partial actual agent-events mock; adds test for parent openSubagentRunIds remap.
src/agents/subagent-registry-run-manager.ts Drains completed/killed subagents from parents; replaces outcomes-equality logic and remaps IDs on steer restart.
src/agents/subagent-announce.ts Adds plan-mode-aware suffix so announce turns don’t stall in plan mode.
src/agents/skills/workspace.ts Carries resolved plan templates into snapshots.
src/agents/skills/types.ts Adds plan-template step types and snapshot field for resolved templates.
src/agents/skills/skill-planner.ts Adds plan-template normalization/building logic with caps/dedup diagnostics.
src/agents/skills/frontmatter.ts Parses plan templates from skill frontmatter with kebab/camel aliasing and content alias.
src/agents/skills/frontmatter.test.ts Adds tests for planTemplate parsing and fallback behavior.
src/agents/plan-mode/types.ts Introduces plan-mode types, approval ID generation, and decision injection builder.
src/agents/plan-mode/reference-card.ts Adds persistent plan-mode reference card content.
src/agents/plan-mode/plan-nudge-crons.ts Schedules/cleans up plan nudge cron jobs with validation + best-effort semantics.
src/agents/plan-mode/plan-mode-debug-log.ts Adds opt-in debug logging with env/config gate and TTL caching.
src/agents/plan-mode/plan-archetype-prompt.ts Adds decision-complete plan prompt + plan filename helpers.
src/agents/plan-mode/plan-archetype-prompt.test.ts Tests plan prompt contents and filename/slug behavior.
src/agents/plan-mode/plan-archetype-bridge.ts Adds plan-mode approval → markdown persist + Telegram attachment dispatch.
src/agents/plan-mode/mutation-gate.ts Adds plan-mode mutation gate for tool calls.
src/agents/plan-mode/mutation-gate.test.ts Adds tests for mutation gate allow/block rules and exec whitelist.
src/agents/plan-mode/index.ts Adds plan-mode barrel exports.
src/agents/plan-mode/auto-enable.ts Adds model regex matching for plan-mode auto-enable.
src/agents/plan-mode/auto-enable.test.ts Tests auto-enable matcher and cache behavior.
src/agents/plan-mode/approval.ts Adds approval resolution state machine and stale-event guard.
src/agents/plan-hydration.ts Adds active-plan hydration formatting for post-compaction injection.
src/agents/plan-hydration.test.ts Adds tests for plan hydration formatting/filtering.
src/agents/pi-tools.ts Threads planMode + live accessors into before-tool-call hook context.
src/agents/pi-tools.before-tool-call.ts Enforces plan-mode mutation gate + post-approval acceptEdits constraints.
src/agents/pi-embedded-runner/skills-runtime.test.ts Updates snapshot behavior tests to account for resolvedPlanTemplates.
src/agents/pi-embedded-runner/run/params.ts Adds planMode + live accessor params to embedded runner interface.
src/agents/pi-embedded-runner/run/helpers.ts Raises default max iterations; adds user override support + subagent cap constant.
src/agents/pi-embedded-runner/run/attempt.spawn-workspace.test-support.ts Stubs plan-template seeder for workspace spawn tests.
src/agents/pi-embedded-runner/run.overflow-compaction.test.ts Uses config override to keep retry-limit test fast under new maxIterations defaults.
src/agents/pi-embedded-runner/run.incomplete-turn.test.ts Updates retry counts; adds auto-continue tests; disables planning-only retry in plan mode.
src/agents/pi-embedded-runner/pending-injection.ts Adds compatibility shim for pending injection queue consumer/composer.
src/agents/pi-embedded-runner/pending-injection.test.ts Adds tests for pending injection composition + once-only clearing behavior.
src/agents/openclaw-tools.ts Registers plan-mode tools; threads runId into update_plan and spawn tool.
src/agents/openclaw-tools.registration.ts Adds plan-mode tools feature gate helper.
extensions/telegram/src/send.ts Adds sendDocumentTelegram with size checks, caption handling, threading + retry/diag.
extensions/telegram/runtime-api.ts Re-exports sendDocumentTelegram and opts type from runtime API.
docs/tools/slash-commands.md Documents universal /plan command.
apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift Adds plan-mode fields and new error codes; removes ChannelsStartParams model.
apps/shared/OpenClawKit/Sources/OpenClawKit/Resources/tool-display.json Adds display metadata for plan-mode tools.
apps/macos/Sources/OpenClawProtocol/GatewayModels.swift Adds plan-mode fields and new error codes; removes ChannelsStartParams model.
Comments suppressed due to low confidence (1)

src/cron/isolated-agent/run.ts:1

  • resolvedDelivery no longer passes to and threadId. If downstream delivery and/or logging relies on those fields (typical for channel sends), this can cause cron agent turns to lose their delivery target or threading context. If this removal is intentional, ensure the consuming code path can operate without to/threadId; otherwise, restore the fields here to preserve behavior.
import { hasAnyAuthProfileStoreSource } from "../../agents/auth-profiles/source-check.js";

Comment on lines +4 to +7
import { getAgentRunContext, trackOpenSubagentForParent } from "../../infra/agent-events.js";
import { normalizeDeliveryContext } from "../../utils/delivery-context.js";
import type { GatewayMessageChannel } from "../../utils/message-channel.js";
import { MAX_CONCURRENT_SUBAGENTS_IN_PLAN_MODE } from "../plan-mode/index.js";

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.

sessions-spawn-tool.ts imports MAX_CONCURRENT_SUBAGENTS_IN_PLAN_MODE from ../plan-mode/index.js, but the new barrel file src/agents/plan-mode/index.ts in this diff does not export that symbol. This will fail at build time. Fix by exporting MAX_CONCURRENT_SUBAGENTS_IN_PLAN_MODE from the plan-mode barrel, or import it directly from the module where it’s defined.

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +32
const MUTATION_TOOL_BLOCKLIST = new Set([
"apply_patch",
"bash",
"edit",
"exec",
"gateway",
"message",
"nodes",
"process",
"sessions_send",
"sessions_spawn",
"subagents",
"write",
]);

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 mutation gate currently blocks sessions_spawn in plan mode, but the plan-mode docs/reference card in this PR explicitly describe spawning research subagents during plan-mode investigation. As written, sessions_spawn will be rejected in plan mode, breaking that workflow. Consider (mandatory) removing sessions_spawn from MUTATION_TOOL_BLOCKLIST and explicitly allowing it (and likely ask_user_question, plan_mode_status, and enter_plan_mode) in PLAN_MODE_ALLOWED_TOOLS to match the documented plan-mode lifecycle.

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +50
const PLAN_MODE_ALLOWED_TOOLS = new Set([
"read",
"web_search",
"web_fetch",
"memory_search",
"memory_get",
"update_plan",
"exit_plan_mode",
"session_status",
]);

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 mutation gate currently blocks sessions_spawn in plan mode, but the plan-mode docs/reference card in this PR explicitly describe spawning research subagents during plan-mode investigation. As written, sessions_spawn will be rejected in plan mode, breaking that workflow. Consider (mandatory) removing sessions_spawn from MUTATION_TOOL_BLOCKLIST and explicitly allowing it (and likely ask_user_question, plan_mode_status, and enter_plan_mode) in PLAN_MODE_ALLOWED_TOOLS to match the documented plan-mode lifecycle.

Copilot uses AI. Check for mistakes.
Comment on lines +242 to +243
* - `reject`: optional `feedback` (capped to 8 KiB to bound
* the prompt-cache hash explosion vector — PR-11 H4).

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 doc comment says reject has optional feedback, but the discriminated union below makes feedback required (minLength: 1). Update the comment to reflect the actual schema contract so client implementers don’t build against a mismatched API.

Suggested change
* - `reject`: optional `feedback` (capped to 8 KiB to bound
* the prompt-cache hash explosion vector PR-11 H4).
* - `reject`: REQUIRES non-empty `feedback` (capped to 8 KiB
* to bound the prompt-cache hash explosion vector PR-11
* H4).

Copilot uses AI. Check for mistakes.
PluginApprovalRequestParamsSchema,
type PluginApprovalResolveParams,
PluginApprovalResolveParamsSchema,
type ErrorCode,

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.

type ErrorCode is imported here but (based on this diff) not used, while the file later re-exports ErrorCode directly from ./schema/error-codes.js. If noUnusedLocals is enabled, this will fail compilation. Remove the unused type ErrorCode import, or use it directly instead of the re-export.

Suggested change
type ErrorCode,

Copilot uses AI. Check for mistakes.
Comment on lines +80 to +86
for (const minutes of intervals) {
if (minutes <= 0 || !Number.isFinite(minutes)) {
continue;
}
const fireAtMs = now + Math.floor(minutes * 60_000);
const fireAtIso = new Date(fireAtMs).toISOString();
try {

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.

assertSafeCronSessionTargetId is dynamically imported inside the loop, causing repeated module-loading work when multiple intervals are scheduled. Import it once outside the loop (or cache it in a local variable) and reuse it per interval to keep scheduling overhead bounded.

Copilot uses AI. Check for mistakes.
// assertion time. Belt-and-suspenders here keeps the failure
// local + actionable instead of a generic cron-validator
// error 60 seconds later.
const { assertSafeCronSessionTargetId } = await import("../../cron/session-target.js");

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.

assertSafeCronSessionTargetId is dynamically imported inside the loop, causing repeated module-loading work when multiple intervals are scheduled. Import it once outside the loop (or cache it in a local variable) and reuse it per interval to keep scheduling overhead bounded.

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: 6f5aa4eb5e

ℹ️ 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 +176 to +179
const sendResult = await sendDocumentTelegram(dctx.to, absPath, {
caption,
parseMode: "HTML",
});

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 Telegram accountId when sending plan attachments

dispatchPlanArchetypeAttachment resolves a delivery context from the session (including the originating Telegram account), but the sendDocumentTelegram call omits accountId. In multi-account setups this makes the SDK fall back to the default bot token, so attachments for sessions owned by a non-default Telegram account can fail with chat-not-found or be sent from the wrong bot, which silently drops the plan file for the user.

Useful? React with 👍 / 👎.

Comment on lines +176 to +179
const sendResult = await sendDocumentTelegram(dctx.to, absPath, {
caption,
parseMode: "HTML",
});

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 Forward stored threadId on Telegram plan attachments

The bridge only passes dctx.to into sendDocumentTelegram, but does not forward dctx.threadId as messageThreadId. When session routing stores topic/thread separately (base chat id in to, thread id in threadId), the attachment is posted to the root chat instead of the active topic thread, so plan approvals appear in the wrong conversation context.

Useful? React with 👍 / 👎.

@100yenadmin

Copy link
Copy Markdown
Contributor Author

Closed for sequential rollout — please disregard cumulative diff.

This PR was opened with --base main against the upstream repo. Cross-repo PRs can't reference fork branches as bases, so opening all 8 stack parts simultaneously produced cumulative diffs (each PR included all prior parts, 10k → 30k lines). That's review-hostile and reproduces exactly the problem the original umbrella #68939 had.

The fix: sequential rollout. Each [Plan Mode N/9] PR opens only AFTER the previous one merges to main, so its diff is incremental.

Currently open:

Next: when 1/9 merges, [Plan Mode 2/9] Core backend MVP opens with a clean ~2,000-line diff. Etc.

This PR's branch is preserved on the fork and will be reopened with a clean diff in its turn.

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 channel: telegram Channel integration: telegram commands Command implementations docs Improvements or additions to documentation gateway Gateway runtime size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants