Skip to content

feat: add native session goals#27163

Open
jorgitin02 wants to merge 8 commits into
anomalyco:devfrom
jorgitin02:feat/session-goals
Open

feat: add native session goals#27163
jorgitin02 wants to merge 8 commits into
anomalyco:devfrom
jorgitin02:feat/session-goals

Conversation

@jorgitin02
Copy link
Copy Markdown
Contributor

@jorgitin02 jorgitin02 commented May 12, 2026

Issue for this PR

Closes #27167

Type of change

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

What does this PR do?

Adds native per-session goals to OpenCode. Goals are persisted server-side, exposed through session HTTP APIs and the generated JS SDK, and shared by the TUI and app. The runtime injects defensive hidden goal context, accounts token and wall-clock usage, auto-continues active goals only when idle, pauses on abort, and budget-limits when token budget is exhausted. Model tools are limited to reading, explicit create, and marking complete.

How did you verify your code works?

  • cd packages/opencode && bun typecheck
  • cd packages/opencode && bun test --timeout 30000 test/session/goal.test.ts
  • cd packages/opencode && bun test --timeout 30000 test/session/goal.test.ts test/server/httpapi-session.test.ts test/session/prompt.test.ts --test-name-pattern 'SessionGoal|session goal routes|goal continuation|goal tools'
  • cd packages/opencode && bun test --timeout 30000 test/session/snapshot-tool-race.test.ts
  • cd packages/app && bun typecheck
  • cd packages/app && bun test --preload ./happydom.ts src/components/prompt-input/submit.test.ts src/context/global-sync/event-reducer.test.ts src/context/global-sync/session-cache.test.ts
  • cd packages/sdk/js && bun typecheck
  • cd packages/desktop && bun typecheck
  • git diff --check
  • Pre-push hook ran bun turbo typecheck
  • Ran codex review --base refs/remotes/upstream/dev; fixed two P2 budget-accounting findings and added regression tests.

Screenshots / recordings

Not attached. The UI changes are compact slash/status surfaces; app and TUI behavior is covered by targeted tests.

Checklist

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

@github-actions github-actions Bot added needs:compliance This means the issue will auto-close after 2 hours. contributor and removed needs:compliance This means the issue will auto-close after 2 hours. labels May 12, 2026
@github-actions
Copy link
Copy Markdown
Contributor

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

@jorgitin02 jorgitin02 force-pushed the feat/session-goals branch 9 times, most recently from 908a68b to 786043a Compare May 13, 2026 00:31
@jorgitin02 jorgitin02 force-pushed the feat/session-goals branch 3 times, most recently from 7bc88b9 to 71989f4 Compare May 13, 2026 01:34
@jorgitin02 jorgitin02 force-pushed the feat/session-goals branch 2 times, most recently from f9c9820 to 73109c9 Compare May 13, 2026 02:01
@jorgitin02 jorgitin02 marked this pull request as ready for review May 13, 2026 02:02
@jorgitin02 jorgitin02 requested a review from adamdotdevin as a code owner May 13, 2026 02:02
@jorgitin02 jorgitin02 force-pushed the feat/session-goals branch 2 times, most recently from 52cbe78 to 3857494 Compare May 13, 2026 16:04
@TaQuangKhoi
Copy link
Copy Markdown

so much of code changes

@jorgitin02
Copy link
Copy Markdown
Contributor Author

@codex review

@jorgitin02 jorgitin02 force-pushed the feat/session-goals branch from 4cbd99f to 8b458e9 Compare May 15, 2026 13:45
cmaloney111 added a commit to psi-oss/gpd-app that referenced this pull request May 15, 2026
Reverting in favor of upstream opencode#27163 ("feat: add native
session goals", opened 2026-05-12, still open). Upstream ships:

  - Token + wall-clock usage accounting with budget-exhausted state
  - Model-facing read/create/mark-complete tools (a real forcing
    function — model can't ignore a passive system-prompt block the
    way it could here)
  - Auto-continue-on-idle + pause-on-abort
  - TUI prompt-input / session-header / footer surfaces
  - Real SDK codegen output (vs the hand-patched sdk.gen.ts /
    types.gen.ts in this PR that next regen would clobber)
  - 1243+ lines of test coverage vs ~120 here

PR #31's own "Followups (deferred)" section explicitly listed three
items upstream already provides: SDK regen, footer/status-bar
surface, real budget/time tracking. The implementation here was
display-only prompt injection with no enforcement.

Plan: wait for upstream anomalyco#27163 to merge, then sync via the normal
upstream-merge path. The gpd-cli companion (get-physics-done#228)
is a distinct surface (CLI, not in-runtime) and can land on its
own merits once it consumes the upstream-merged shape.

Cameron's f6a9017 on top (extending the RES-904 reward-hacking
gate to gpt/gemini/codex/kimi/trinity) touches a disjoint file set
and is preserved unchanged.

Reverts the 12 files PR #31 modified/added to their state at the
PR #30 merge (fb230ed):

  packages/opencode/migration/20260515120000_add_session_goal/migration.sql (deleted)
  packages/opencode/src/cli/cmd/tui/component/dialog-session-goal.tsx (deleted)
  packages/opencode/src/cli/cmd/tui/routes/session/index.tsx (restored)
  packages/opencode/src/server/instance/session.ts (restored)
  packages/opencode/src/session/index.ts (restored)
  packages/opencode/src/session/projectors.ts (restored)
  packages/opencode/src/session/prompt.ts (restored)
  packages/opencode/src/session/session.sql.ts (restored)
  packages/opencode/test/cli/tui/dialog-session-goal.test.ts (deleted)
  packages/opencode/test/session/goal.test.ts (deleted)
  packages/sdk/js/src/v2/gen/sdk.gen.ts (restored)
  packages/sdk/js/src/v2/gen/types.gen.ts (restored)
@Cheny5863
Copy link
Copy Markdown

⚠️ 请注意:此 PR 修改了系统 Prompt,这会导致 DeepSeek 的 Prompt 缓存(Context Caching)失效/命中失败。

English:
⚠️ Please note: This PR modifies the system prompt, which will cause DeepSeek's prompt caching to miss.
Screenshot from 2026-05-23 01-03-27

@Cheny5863
Copy link
Copy Markdown

There is another problem:

False “no progress” auto-pause stopped goal continuation prematurely

Summary

When a session had an active goal, autoContinueGoal could incorrectly decide that a synthetic continuation turn made no progress, immediately set the goal to paused, and stop injecting further continuation prompts. From the user’s perspective, the agent “stopped on its own” or /goal resume appeared to do nothing (goal went back to paused within ~200ms).

This was not an LLM/provider failure. The goal loop exited before a new LLM turn was enqueued.

Root cause

After a synthetic goal-continuation user message (Continue working toward the active session goal.), auto-pause only inspected the latest assistant message. Pure text responses (e.g. a status summary asking “need to continue?”) do not count as progress — only tools/patches/subtasks do:

    const assistantMadeGoalProgress = (message: MessageV2.WithParts) =>
      message.info.role === "assistant" &&
      message.parts.some((part) => {
        if (part.type === "patch" || part.type === "subtask") return true
        if (part.type !== "tool") return false
        if (part.tool === "get_goal") return false
        return part.state.status === "completed" || part.state.status === "running" || part.state.status === "pending"
      })

Original buggy condition (simplified):

// BEFORE fix — only looked at the latest assistant
if (
  isGoalContinuationMessage(latestUser) &&
  latestAssistant.finish &&
  !assistantMadeGoalProgress(latestAssistant)  // ← too narrow
) {
  await goals.update({ sessionID, status: "paused" })
  return
}

Failure scenario

Typical message sequence:

  1. Synthetic user: Continue working toward the active session goal.
  2. Assistant A: runs bash / other tools (real progress)
  3. Assistant B (latest): text-only summary, e.g. ## Status summary… need to continue?

On idle, autoContinueGoal (or /goal resumecontinueGoal) saw:

  • Latest user = old synthetic continuation message
  • Latest assistant = B (text only) → assistantMadeGoalProgress === false

So it logged auto_pause_no_progress and paused the goal — even though assistant A in the same stretch had already used tools.

Observed in logs:

goal.continue reason=start
goal.continue reason=auto_pause_no_progress latestAssistantProgress=false
goal.status from=active to=paused

No inject_synthetic_prompt, no new LLM call.

Why normal chat still worked

A real user message creates a new turn and does not hit this auto-pause branch the same way. The bug specifically affected the goal idle-continuation loop, not arbitrary user prompts.

Fix

Treat a continuation stretch as “no progress” only if none of the last N messages (N=5) contain tool/patch/subtask progress:

        if (
          Option.isSome(latestUser) &&
          Option.isSome(latestAssistant) &&
          latestAssistant.value.info.role === "assistant" &&
          isGoalContinuationMessage(latestUser.value) &&
          latestUser.value.info.id < latestAssistant.value.info.id &&
          latestAssistant.value.info.finish &&
          !recentProgress
        ) {
          yield* goalContinueLog(sessionID, "auto_pause_no_progress", messageContext)
          yield* goals.update({ sessionID, status: "paused" }).pipe(
            Effect.catchIf(NotFoundError.isInstance, () => Effect.void),
            Effect.ignore,
          )
          return
        }

Where recentProgress is derived from the last 5 messages:

    const recentMessagesMadeGoalProgress = (messages: MessageV2.WithParts[]) =>
      messages.some((message) => assistantMadeGoalProgress(message))

After the fix, resume logs show recentProgress=true and inject_synthetic_prompt instead of immediate re-pause.

@Cheny5863
Copy link
Copy Markdown

Used it for a bit and everything looks good so far—no other issues found. The program runs great, thanks for the PR!

@jorgitin02 jorgitin02 force-pushed the feat/session-goals branch from 9fc0904 to 76a2009 Compare May 23, 2026 12:08
@jorgitin02
Copy link
Copy Markdown
Contributor Author

⚠️ 请注意:此 PR 修改了系统 Prompt,这会导致 DeepSeek 的 Prompt 缓存(Context Caching)失效/命中失败。

English: ⚠️ Please note: This PR modifies the system prompt, which will cause DeepSeek's prompt caching to miss. Screenshot from 2026-05-23 01-03-27

Both issues should be fixed now.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: Add native session goals with /goal

3 participants