Skip to content

feat(tool): add automate_manage lifecycle actions#1278

Open
Astro-Han wants to merge 11 commits into
devfrom
codex/i1249-automate-manage
Open

feat(tool): add automate_manage lifecycle actions#1278
Astro-Han wants to merge 11 commits into
devfrom
codex/i1249-automate-manage

Conversation

@Astro-Han

@Astro-Han Astro-Han commented Jun 12, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a deferred automate_manage tool for listing, pausing, resuming, and deleting existing PawWork Automations by exact id.

Also tightens the permission surfaces exposed by this new delete flow: delete confirmation shows the automation title/id, one-time-only prompts no longer show an unavailable "allow always" choice in the app or ACP, and model routing now points existing automation management requests to automate_manage via tool_info.

Why

Users can create Automations from conversation with automate, but issue #1249 tracks the missing management side: seeing existing automation ids, reversible pause/resume, and deletion without falling back to shell or OS scheduler workflows.

Related Issue

Closes #1249

Human Review Status

Pending

Review Focus

Please focus on the management contract: list stays current-scope, pause/resume do not ask, delete asks through ctx.ask before removal, live active-run deletion preserves the existing 409-style behavior as a readable tool error, and once-only delete prompts do not offer persistent approval.

Risk Notes

Deletion is destructive, so the tool gates it through ctx.ask and exact ids. The delete permission is intentionally one-time-only because automate_manage delete passes no always patterns. The app permission dock and ACP now hide the persistent approval option whenever a request cannot persist approval.

Pause/resume are reversible and follow the existing server route behavior by publishing definition updates instead of adding a separate scheduler path. The visible UI change is limited to the permission dock copy/buttons. The platform/permissions impact is shared app renderer and ACP behavior only; no OS-specific packaging, updater, signing, installer, or filesystem path behavior changed.

How To Verify

Focused opencode tests: bun --cwd packages/opencode test test/tool/automate-manage.test.ts test/tool/tool-info.test.ts test/tool/registry.test.ts test/acp/event-subscription.test.ts -> 99 pass, 0 fail
Automation delete follow-up tests: bun --cwd packages/opencode test test/tool/automate-manage.test.ts test/server/automation-scheduler.test.ts -> 59 pass, 0 fail
Prompt-loop activation smoke: bun --cwd packages/opencode test test/session/prompt-effect.test.ts -t "loop activates automate_manage through tool_info before invoking it" -> 1 pass, verifies the model-facing tool surface goes from tool_info-only to automate_manage callable before executing list
Opencode typecheck: (cd packages/opencode && bun run typecheck) -> tsgo --noEmit passed
App permission helper test: (cd packages/app && bun test --preload ./happydom.ts ./src/pages/session/composer/session-permission-dock.test.ts) -> 3 pass, 0 fail
App typecheck: (cd packages/app && bun run typecheck) -> tsgo -b passed
Permission dock snap: (cd packages/app && bun run snap permission-dock) -> 1 pass, generated docs/design/preview/screenshots/permission-dock.png for local visual review
Diff check: git diff --check and git diff --cached --check -> no whitespace errors

Screenshots or Recordings

Local snap generated and visually reviewed at docs/design/preview/screenshots/permission-dock.png. It shows automate_manage delete confirmation with automation title/id and no Allow always button, plus a normal persistable shell permission that still shows Allow always.

Checklist

How to use this checklist:

  • Tick a box by replacing [ ] with [x]. Do not edit, add, or remove items.
  • The bot-applied label items can only be honestly ticked AFTER the PR is opened and the labeler / priority-triage bots have run — return to the PR description and tick them then.
  • Most items are required. The few that are conditional are explicitly marked (conditional); for those, leave unticked if they truly do not apply and explain why in Risk Notes. All other items must be ticked before requesting human review.
  • Type label — this PR carries exactly one of bug, enhancement, task, documentation. Type labels are author-added; the labeler bot does NOT assign them. Add the label in the GitHub UI, then tick this.
  • Routing labels — this PR carries at least one of app, ui, platform, harness, ci. The labeler bot assigns these on PR open based on changed paths. Confirm the bot's choice (or override if wrong), then tick this.
  • Priority label — this PR carries exactly one of P0, P1, P2, P3. The priority-triage bot suggests one on PR open. Confirm or override, then tick this.
  • Human Review Status above is set to Pending, Approved by @<reviewer>, or Not required: <reason> (default is Pending; "not required" is restricted to bot-authored low-risk PRs).
  • I linked the related issue, or stated in Summary why there is no issue.
  • I described the review focus and any meaningful risks.
  • I replaced the example block in How To Verify with the real verification steps and the key result for each.
  • I did not introduce unrelated refactors, dependencies, generated files, or file changes beyond the stated scope.
  • (conditional) I manually checked visible UI or copy changes when needed, with screenshots or recordings. Leave unticked only if no visible UI or copy changed.
  • (conditional) I considered macOS and Windows impact for platform, packaging, updater, signing, paths, shell, or permissions changes. Leave unticked only if no platform/packaging surface was touched.
  • (conditional) I called out docs, release notes, dependencies, permissions, credentials, deletion behavior, generated content, or local file changes when relevant. Leave unticked only if none of those surfaces was touched.
  • I reviewed the final diff for unrelated changes and suspicious dependency changes.
  • I am targeting dev, and my PR title and commit messages use Conventional Commits in English.

Summary by CodeRabbit

  • New Features

    • Introduced an automation management tool to list, pause, resume, and delete automations via conversation.
  • UI

    • Permission prompt now shows metadata lines for automation deletions and only offers "Allow always" when persistence is permitted.
    • Added a permission-dock fixture and snapshot for visual verification.
  • Localization

    • Added English and Chinese strings for automate-manage deletion prompts.
  • Tests

    • Expanded tests for automation management, scheduler behavior, registry/deferred-tool handling, and permission flows.

@Astro-Han Astro-Han added enhancement New feature or request P2 Medium priority harness Model harness, prompts, tool descriptions, and session mechanics labels Jun 12, 2026

@github-actions github-actions 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.

Suggested priority: P2 (includes non-doc, non-test paths outside the low-risk bucket).

P1/P0 are reserved for maintainer confirmation. Please relabel manually if this is a release blocker, security issue, data-loss risk, or updater/runtime failure.

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a new deferred tool, automate_manage, to list, pause, resume, and delete PawWork Automations; wires it into ToolRegistry and tool_info; implements parameter validation, readable errors, confirmation gating for deletes, and publishes scheduler/definition/run events; and includes tests, prompt/docs, i18n, and UI fixture/snap updates.

Changes

automate_manage Tool Implementation & Integration

Layer / File(s) Summary
Tool parameters and internal helpers
packages/opencode/src/tool/automate-manage.ts
Defines action literal and AutomateManageParameters, output/metadata types, schedule/item formatters, requireID, and readable automation error mapping.
Tool execution and export
packages/opencode/src/tool/automate-manage.ts
Implements execute branching for list, pause/resume (state update + conditional publish), and delete (ctx.ask confirmation, remove with readable errors, publish run/deleted events), and exports AutomateManageTool.
Registry and tool-info wiring
packages/opencode/src/tool/registry.ts, packages/opencode/src/tool/tool-info.ts
Imports and instantiates AutomateManageTool in ToolRegistry, registers it in init/builtin lists, and adds automate_manage to the DEFERRED catalog with parameter schema and descriptions.
Test runtime and helpers
packages/opencode/test/tool/automate-manage.test.ts
Bootstraps Effect runtime with Automation.defaultLayer, resolves Automation.Service, installs controllable AutomationScheduler stubs, and provides deterministic tool contexts with ask recording.
Action-specific behavior tests
packages/opencode/test/tool/automate-manage.test.ts
Tests schema/contract decoding, list output shape and non-settling behavior, pause/resume transitions and revisions, delete confirmation, tombstone output and event publishing, stale-id and active-run error cases, and denied-confirmation behavior.
Tool-info & registry tests
packages/opencode/test/tool/tool-info.test.ts, packages/opencode/test/tool/registry.test.ts
Updates deferred-tool expectations to include automate_manage, card/list assertions, canonical id normalization, activation target resolution, and registry surface/activation assertions across prompt surfaces.
UI/i18n, fixture & snap
packages/app/src/i18n/*, packages/app/e2e/snap/fixtures/*, packages/app/e2e/snap/*, packages/app/src/pages/session/composer/*
Adds ui.permission.automateManageDelete i18n keys, permission-dock Solid fixture and Playwright snapshot test, and session permission dock helpers/rendering changes to surface metadata lines and conditional persist button.
ACP and subscription tests
packages/opencode/src/acp/agent.ts, packages/opencode/test/acp/event-subscription.test.ts
Conditionally omit the always option in ACP permission requests when none exist and add tests verifying permission.asked option lists reflect always presence.
Scheduler and prompt-effect tests
packages/opencode/test/server/automation-scheduler.test.ts, packages/opencode/test/session/prompt-effect.test.ts
Adds a scheduler regression test ensuring DefinitionDeleted cancels scheduled work, imports AutomationScheduler for prompt-effect cleanup, and adds a live test verifying tool_info activation precedes automate_manage invocation.

Sequence Diagram

sequenceDiagram
  participant User
  participant Tool as automate_manage Tool
  participant AutomationService as Automation.Service
  participant Context as ctx.ask
  participant Scheduler as AutomationScheduler

  User->>Tool: delete { action: "delete", id: "automation-id" }
  activate Tool
  Tool->>Scheduler: settle owner
  Tool->>AutomationService: get(id)
  AutomationService-->>Tool: automation
  Tool->>Context: ask permission for delete (title,id)
  activate Context
  User-->>Context: approve
  Context-->>Tool: permission granted
  Tool->>AutomationService: remove(id)
  AutomationService-->>Tool: { tombstone, stoppedRun? }
  Tool->>Scheduler: cancel scheduled work for id
  Scheduler-->>Tool: cancelled
  Tool->>Tool: publishDefinitionDeleted / publishRunUpdated
  Tool-->>User: { tombstone, metadata }
  deactivate Tool
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐇 With nimble paws I patch the code today,

List, pause, resume, or delete — just say the way.
I ask first for permission, I tidy with care,
No fuzzy ids, no surprises — your automations, fair.
Hoppity hooray, management now in-chat play!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(tool): add automate_manage lifecycle actions' directly and concisely describes the main change: a new tool providing lifecycle management actions for automations.
Description check ✅ Passed The PR description comprehensively addresses all required sections: Summary describes the automate_manage tool additions, Why explains the user need from issue #1249, Related Issue links the issue, Human Review Status is set to Pending, Review Focus articulates key contract points, Risk Notes detail deletion safety and permission boundaries, How To Verify provides concrete test results, and all checklist items are addressed.
Linked Issues check ✅ Passed The PR fully implements the coding requirements from #1249: automate_manage tool with list/pause/resume/delete actions, flat id-based schema, delete gated through ctx.ask, pause/resume without confirmation, error handling for stale ids and active-run conflicts, deferred tool activation via tool_info, and model routing updates. Tests cover tool contract, activation, and permission behavior.
Out of Scope Changes check ✅ Passed All changes directly support the automate_manage tool implementation and associated permission/routing updates. No unrelated refactors, unscoped UI changes, edit/update verbs, bulk operations, or cross-session administration were introduced; the scope remains precisely bounded as defined in issue #1249.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/i1249-automate-manage

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces the automate_manage tool, which allows listing, pausing, resuming, and deleting existing PawWork Automations. It includes the tool definition, integration into the tool registry and info modules, and comprehensive unit tests. The feedback suggests improving error handling in the tool's execution flow by leveraging the Effect ecosystem's error channel (using Effect.fail and Effect.succeed instead of throwing synchronous errors).

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/opencode/src/tool/automate-manage.ts
Comment thread packages/opencode/src/tool/automate-manage.ts Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/opencode/src/tool/tool-info.ts`:
- Around line 69-75: The tool_info entry for the automate_manage tool does not
fully document the activation-time contract: update the description string for
the automate_manage object (the one with id "automate_manage" and parameters
cast to Tool.Def["parameters"]) to explicitly state that "list" operations are
limited to the current context only and that pause, resume, and delete
operations require an exact automation id (no fuzzy matching); keep the existing
user-facing guidance but extend the deferred description to enumerate these
constraints so the pre-activation model cannot assume global listing or allow
fuzzy id targets.

In `@packages/opencode/test/tool/automate-manage.test.ts`:
- Around line 1-196: The suite currently creates a manual ManagedRuntime
(ManagedRuntime, runtime, and the await runtime.runPromise(...) that yields
automation) and uses plain bun:test test(...) cases; replace that wiring by
converting tests to use the repository test harness (testEffect) and run
OS-backed scenarios with it.live(...), remove the
ManagedRuntime/runtime/automation manual setup and instead obtain the Automation
service via the testEffect provided layer (use Automation.defaultLayer or the
harness's layer composition), and update each test case to run as testEffect +
it.live so Effect services (e.g., createAutomateManageDefinition,
AutomationScheduler interactions, Flock/tmpdir) are executed under the standard
Effect test environment rather than custom runtime.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4c3ad70b-c5f8-45d3-b4dd-bdfb747c3037

📥 Commits

Reviewing files that changed from the base of the PR and between a143f92 and 8393bf4.

📒 Files selected for processing (6)
  • packages/opencode/src/tool/automate-manage.ts
  • packages/opencode/src/tool/registry.ts
  • packages/opencode/src/tool/tool-info.ts
  • packages/opencode/test/tool/automate-manage.test.ts
  • packages/opencode/test/tool/registry.test.ts
  • packages/opencode/test/tool/tool-info.test.ts

Comment thread packages/opencode/src/tool/tool-info.ts
Comment thread packages/opencode/test/tool/automate-manage.test.ts
@github-actions github-actions Bot added app Application behavior and product flows ui Design system and user interface labels Jun 12, 2026

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/opencode/src/tool/automate-manage.ts`:
- Around line 101-103: The update path for pause/resume currently calls
automation.update(id, { paused: ... }) after reading previous and can surface a
raw NotFound; wrap or replace that call with the same readable stale-id mapping
used elsewhere so a removed definition produces the friendly "automate_manage
list" guidance instead of a raw error—i.e., intercept errors from
automation.update (in automate-manage.ts around the params.action ===
"pause"/"resume" branch), detect the not-found/stale-id case and re-throw or
return the mapped, human-friendly error message used elsewhere (preserving the
existing revision check against previous.revision).

In `@packages/opencode/test/session/prompt-effect.test.ts`:
- Around line 1040-1044: Add an explicit assertion on the number of emitted
requests before indexing into requests: after collecting const requests = yield*
llm.inputs, assert the expected count (e.g., expect(requests).toHaveLength(2) or
expect(requests.length).toBeGreaterThanOrEqual(2)) so the subsequent positional
checks that call requestToolNames(requests[0]) and requestToolNames(requests[1])
fail with a clear message if fewer requests were emitted.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c8f27aba-d8cb-4d92-8b42-541de9f443e4

📥 Commits

Reviewing files that changed from the base of the PR and between 8393bf4 and d3bb3d1.

📒 Files selected for processing (16)
  • packages/app/e2e/snap/fixtures/permission-dock-fixture.tsx
  • packages/app/e2e/snap/permission-dock.snap.ts
  • packages/app/src/i18n/en.ts
  • packages/app/src/i18n/zh.ts
  • packages/app/src/pages/session/composer/session-permission-dock.test.ts
  • packages/app/src/pages/session/composer/session-permission-dock.tsx
  • packages/opencode/src/acp/agent.ts
  • packages/opencode/src/session/prompt/pawwork.txt
  • packages/opencode/src/tool/automate-manage.ts
  • packages/opencode/src/tool/shell.txt
  • packages/opencode/src/tool/tool-info.ts
  • packages/opencode/test/acp/event-subscription.test.ts
  • packages/opencode/test/server/automation-scheduler.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/test/tool/automate-manage.test.ts
  • packages/opencode/test/tool/registry.test.ts
✅ Files skipped from review due to trivial changes (3)
  • packages/opencode/src/tool/shell.txt
  • packages/app/src/i18n/zh.ts
  • packages/app/src/i18n/en.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/opencode/src/tool/tool-info.ts
  • packages/opencode/test/tool/registry.test.ts

Comment on lines +101 to +103
if (params.action === "pause" || params.action === "resume") {
const definition = yield* automation.update(id, { paused: params.action === "pause" })
if (definition.revision !== previous.revision) {

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Apply readable stale-id mapping to the pause/resume update call.

Line 102 performs automation.update(...) after a separate read on Line 100. If the definition is removed between those calls, the update path can leak a raw not-found error instead of the automate_manage list guidance used elsewhere.

💡 Suggested fix
-          const definition = yield* automation.update(id, { paused: params.action === "pause" })
+          const definition = yield* readableAutomationEffect(
+            automation.update(id, { paused: params.action === "pause" }),
+            id,
+          )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/src/tool/automate-manage.ts` around lines 101 - 103, The
update path for pause/resume currently calls automation.update(id, { paused: ...
}) after reading previous and can surface a raw NotFound; wrap or replace that
call with the same readable stale-id mapping used elsewhere so a removed
definition produces the friendly "automate_manage list" guidance instead of a
raw error—i.e., intercept errors from automation.update (in automate-manage.ts
around the params.action === "pause"/"resume" branch), detect the
not-found/stale-id case and re-throw or return the mapped, human-friendly error
message used elsewhere (preserving the existing revision check against
previous.revision).

Comment on lines +1040 to +1044
const requests = yield* llm.inputs
expect(requestToolNames(requests[0])).toContain("tool_info")
expect(requestToolNames(requests[0])).not.toContain("automate_manage")
expect(requestToolNames(requests[1])).toContain("automate_manage")

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add an explicit request-count assertion before positional tool-list checks.

Lines 1041-1044 index requests[0] / requests[1] directly. If the loop emits fewer requests in a regression, this throws before asserting the activation contract and makes failures harder to diagnose.

💡 Suggested fix
         const requests = yield* llm.inputs
-        expect(requestToolNames(requests[0])).toContain("tool_info")
-        expect(requestToolNames(requests[0])).not.toContain("automate_manage")
-        expect(requestToolNames(requests[1])).toContain("automate_manage")
+        expect(requests.length).toBeGreaterThanOrEqual(2)
+        const [firstRequest, secondRequest] = requests
+        expect(requestToolNames(firstRequest)).toContain("tool_info")
+        expect(requestToolNames(firstRequest)).not.toContain("automate_manage")
+        expect(requestToolNames(secondRequest)).toContain("automate_manage")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const requests = yield* llm.inputs
expect(requestToolNames(requests[0])).toContain("tool_info")
expect(requestToolNames(requests[0])).not.toContain("automate_manage")
expect(requestToolNames(requests[1])).toContain("automate_manage")
const requests = yield* llm.inputs
expect(requests.length).toBeGreaterThanOrEqual(2)
const [firstRequest, secondRequest] = requests
expect(requestToolNames(firstRequest)).toContain("tool_info")
expect(requestToolNames(firstRequest)).not.toContain("automate_manage")
expect(requestToolNames(secondRequest)).toContain("automate_manage")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/test/session/prompt-effect.test.ts` around lines 1040 -
1044, Add an explicit assertion on the number of emitted requests before
indexing into requests: after collecting const requests = yield* llm.inputs,
assert the expected count (e.g., expect(requests).toHaveLength(2) or
expect(requests.length).toBeGreaterThanOrEqual(2)) so the subsequent positional
checks that call requestToolNames(requests[0]) and requestToolNames(requests[1])
fail with a clear message if fewer requests were emitted.

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

Labels

app Application behavior and product flows enhancement New feature or request harness Model harness, prompts, tool descriptions, and session mechanics P2 Medium priority ui Design system and user interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] automate_manage tool: list, pause, resume, and delete Automations from conversation

1 participant