Skip to content

feat: add native /goal command for autonomous task completion#28610

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

feat: add native /goal command 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 native /goal command that enables multi-turn autonomous goal execution, similar to Codex CLI's /goal command. Feature gated behind experimental.goals config flag (default off).

Implementation approach:

The goal runtime is backed by a new GoalTable in SQLite (via existing Drizzle ORM). A SessionGoal.Service state machine handles CRUD, token budget tracking, and iteration limits. Two model tools (create_goal, update_goal) let the model manage goals autonomously.

The key design challenge was avoiding InstanceRef not provided errors — config.get() needs InstanceRef which isn't available during layer resolution. The fix: SessionGoal.Service and config.get() are accessed lazily inside runLoop (after outcome === "break"), not at the layer effect level.

Goal continuation works by injecting a synthetic user message with a continuation prompt when the goal is still active after a turn. The loop continues automatically until the goal is marked complete or the iteration limit (default 50) is reached.

/goal command registration is conditional on cfg.experimental?.goals === true.

How did you verify your code works?

  • typecheck: clean
  • test/tool/registry.test.ts: 15/15 pass
  • test/session/prompt.test.ts: 37/3 (3 pre-existing failures unrelated to this change — confirmed same failures exist on dev branch)

Screenshots / recordings

N/A — CLI change only.

Checklist

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

Add a native /goal command that enables multi-turn autonomous goal
execution, matching the implementation approach of Codex CLI.

Key changes:
- New GoalTable schema (thread_id PK, objective, status, token budget,
  iteration count) and GoalStatus union type in session.sql.ts
- Goal runtime state machine in session/goal.ts with CRUD operations,
  continuation prompt generation, budget tracking, and iteration limits
- create_goal and update_goal model tools in tool/goal.ts, with
  SessionGoal.Service resolved lazily inside execute (not at define time)
- Goal tools registered unconditionally in tool/registry.ts, feature
  gated only at runtime via experimental.goals config check
- /goal command registered in command/index.ts behind
  experimental.goals feature flag
- Goal continuation logic in prompt.ts runLoop: after outcome is
  'break', checks for active goal and injects continuation prompt as
  a synthetic user message before continuing the loop
- SessionGoal.defaultLayer merged into AppLayer in app-runtime.ts
- Goal event types (GoalSet, GoalUpdated, GoalCleared, GoalCompleted)
  in session-event.ts with no-op handlers in session-message-updater.ts

Fixes anomalyco#27162
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 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()
})

const isGoalContinueNeeded: Interface["isGoalContinueNeeded"] = (state: GoalState) => {
return state.status === "active"
})

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")(
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 on lines +14 to +18
const UpdateGoalParams = Schema.Struct({
status: Schema.String.annotate({
description: 'New status: "complete" (goal achieved), "blocked" (cannot proceed)',
}),
})
@@ -0,0 +1,230 @@
import { Effect, Layer, Context, Schema } from "effect"
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" })
timeUpdated: number
}

export class GoalNotFoundError extends Error {
}
}

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": () => {},
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