Skip to content

feat: add /goal plugin for autonomous task completion#28610

Open
NathanKong76 wants to merge 1 commit into
anomalyco:devfrom
NathanKong76:feat/native-goal-command
Open

feat: add /goal plugin for autonomous task completion#28610
NathanKong76 wants to merge 1 commit into
anomalyco:devfrom
NathanKong76:feat/native-goal-command

Conversation

@NathanKong76
Copy link
Copy Markdown

@NathanKong76 NathanKong76 commented May 21, 2026

Issue for this PR

Closes #27167

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Adds a built-in plugin that enables multi-turn autonomous goal execution via /goal command. The plugin hooks into opencode's existing plugin system — no core code changes.

State is stored in .opencode/goals.json (atomic write via tmp+rename), not in the SQLite database.

How the continuation loop works:

  1. Model completes a turn → session goes idle
  2. Plugin receives session.status idle event via Hooks.event
  3. Checks goals.json — if goal is still active, injects a <system-reminder> continuation prompt via client.promptAsync()
  4. Server creates a new user message and starts a new LLM turn
  5. Loop repeats until goal is complete / paused / blocked

Commands:

  • /goal fix the bug — sets a new goal and sends template to LLM
  • /goal pause / /goal resume / /goal clear — subcommands handled inline, no LLM call

Tools registered via Hooks.tool:

  • create_goal — create or reset a goal
  • update_goal — mark as complete or blocked
  • get_goal — read current goal state

No token budget tracking — the model API doesn't expose per-turn token usage to plugins. The continuation prompt shows iteration count and elapsed time instead.

How did you verify your code works?

  • bun test test/plugin/goal.test.ts: 18/18 pass (storage, status logic, prompt generation, formatting)
  • bun test test/plugin/goal.integration.test.ts: 16/16 pass (all hooks exercised via mock PluginInput: event continuation triggering, tool CRUD, command handling, edge cases)
  • bun test test/session/prompt.test.ts: 38/13/3 (existing tests unaffected, 3 pre-existing shell timeout)
  • bun test test/tool/registry.test.ts: 14/14 pass
  • bun typecheck: 0 errors

Screenshots / recordings

N/A — CLI change only.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Copilot AI review requested due to automatic review settings May 21, 2026 08:15
@github-actions github-actions Bot added needs:compliance This means the issue will auto-close after 2 hours. and removed needs:compliance This means the issue will auto-close after 2 hours. labels May 21, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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.

Introduces an experimental “goals” capability that lets a session persist an objective, continue autonomously across turns, and update goal status via new tools and a /goal command.

Changes:

  • Added goal session storage + service layer and wired it into the app/runtime layers.
  • Introduced create_goal / update_goal tools and a new /goal command template.
  • Extended the session prompt loop to optionally auto-continue when a goal is active.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
packages/opencode/src/tool/registry.ts Registers goal tools and provides the goal service layer.
packages/opencode/src/tool/goal.ts Implements create_goal and update_goal tools.
packages/opencode/src/session/session.sql.ts Adds GoalStatus and a new goal SQLite table.
packages/opencode/src/session/prompt.ts Adds experimental auto-continue behavior based on active goals.
packages/opencode/src/session/goal.ts New SessionGoal service for CRUD + iteration/token tracking.
packages/opencode/src/effect/app-runtime.ts Provides SessionGoal layer at the app level.
packages/opencode/src/config/config.ts Adds experimental.goals config flag.
packages/opencode/src/command/template/goal.txt Adds /goal command prompt template.
packages/opencode/src/command/index.ts Registers /goal command when experimental.goals is enabled.
packages/core/src/session-message-updater.ts Adds no-op handlers for new goal events.
packages/core/src/session-event.ts Defines new goal-related session events and includes them in the union.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/opencode/src/session/prompt.ts Outdated
Comment on lines +1477 to +1491
if (outcome === "break") {
const goalSvc = yield* SessionGoal.Service
const configSvc = yield* Config.Service
const cfg = yield* configSvc.get()
const goalsEnabled = cfg.experimental?.goals === true
if (goalsEnabled) {
const goal = yield* goalSvc.get(sessionID)
if (goal && goalSvc.isGoalContinueNeeded(goal)) {
yield* goalSvc.incrementIteration(sessionID)
const updatedGoal = yield* goalSvc.get(sessionID)
if (updatedGoal && goalSvc.isGoalContinueNeeded(updatedGoal)) {
const contPrompt = updatedGoal.status === "budget_limited"
? goalSvc.getBudgetLimitPrompt(updatedGoal)
: goalSvc.getContinuationPrompt(updatedGoal)
const msgId = MessageID.ascending()
Comment thread packages/opencode/src/session/goal.ts Outdated
})

const isGoalContinueNeeded: Interface["isGoalContinueNeeded"] = (state: GoalState) => {
return state.status === "active"
Comment thread packages/opencode/src/session/prompt.ts Outdated
})

const runLoop: (sessionID: SessionID) => Effect.Effect<MessageV2.WithParts> = Effect.fn("SessionPrompt.run")(
const runLoop: (sessionID: SessionID) => Effect.Effect<MessageV2.WithParts, never, SessionGoal.Service | Config.Service> = Effect.fn("SessionPrompt.run")(
Comment thread packages/opencode/src/session/prompt.ts Outdated
input: LoopInput,
) {
return yield* state.ensureRunning(input.sessionID, lastAssistant(input.sessionID), runLoop(input.sessionID))
return yield* state.ensureRunning(input.sessionID, lastAssistant(input.sessionID), runLoop(input.sessionID) as Effect.Effect<MessageV2.WithParts>)
Comment thread packages/opencode/src/tool/goal.ts Outdated
Comment on lines +14 to +18
const UpdateGoalParams = Schema.Struct({
status: Schema.String.annotate({
description: 'New status: "complete" (goal achieved), "blocked" (cannot proceed)',
}),
})
Comment thread packages/opencode/src/session/goal.ts Outdated
@@ -0,0 +1,230 @@
import { Effect, Layer, Context, Schema } from "effect"
Comment thread packages/opencode/src/session/goal.ts Outdated
Comment on lines +6 to +9
import * as Log from "@opencode-ai/core/util/log"

const DEFAULT_MAX_ITERATIONS = 50
const log = Log.create({ service: "session.goal" })
Comment thread packages/opencode/src/session/goal.ts Outdated
timeUpdated: number
}

export class GoalNotFoundError extends Error {
Comment thread packages/opencode/src/session/goal.ts Outdated
}
}

export class GoalAlreadyExistsError extends Error {
Comment on lines +412 to +415
"session.next.goal.set": () => {},
"session.next.goal.updated": () => {},
"session.next.goal.cleared": () => {},
"session.next.goal.completed": () => {},
@NathanKong76 NathanKong76 force-pushed the feat/native-goal-command branch 3 times, most recently from 3d912fc to 33076c5 Compare May 21, 2026 13:45
@NathanKong76 NathanKong76 force-pushed the feat/native-goal-command branch from 4728f09 to b2c696b Compare May 27, 2026 02:31
@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label May 27, 2026
@NathanKong76 NathanKong76 changed the title feat: add native /goal command for autonomous task completion feat: add /goal plugin for autonomous task completion May 27, 2026
@NathanKong76
Copy link
Copy Markdown
Author

Rewritten as a plugin per @aiden Cline feedback — zero core code changes, just 2 files.

Architecture

  • Tools: create_goal / update_goal / get_goal registered via Hooks.tool
  • Commands: /goal pause|resume|clear and --token-budget handled via Hooks["command.execute.before"]
  • Continuation: subscribes to session.status idle event via Hooks.event, injects <system-reminder> prompt via client.prompt() to trigger next LLM turn
  • Storage: .opencode/goals.json (single file, atomic tmp+rename write)

Design

  • No SQLite migration, no core schema changes
  • <system-reminder> tags keep continuation prompts invisible in chat
  • 30s client.prompt() timeout + inFlight set prevents duplicate continuations
  • Budget uses iteration×4000 token estimate

Open to feedback — happy to adjust anything. If this pattern works, future goal features (time limits, subtask goals, etc.) can be added to the plugin without touching core.

A built-in plugin that enables multi-turn autonomous goal execution:

- Tools: create_goal, update_goal, get_goal
- Commands: /goal pause|resume|clear, --token-budget <N>
- Continuation: listens to session.status idle event, checks active
  goal via .opencode/goals.json, injects <system-reminder> prompt via
  client.prompt() to trigger next LLM turn
- Storage: single goals.json per directory, atomic write (tmp+rename)
- Budget: iteration-based estimation, auto-transitions to budget_limited
- Guards: 30s timeout on client.prompt(), inFlight set prevents duplicates
- Zero core schema changes: all data in the plugin's own json file
@NathanKong76 NathanKong76 force-pushed the feat/native-goal-command branch from 0030ef4 to 1268d72 Compare May 27, 2026 03:06
@github-actions github-actions Bot removed the needs:compliance This means the issue will auto-close after 2 hours. label May 27, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: Add native session goals with /goal

2 participants