Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/advisor-model-plan-mode-default.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@roomote/feature-flags': minor
'@roomote/web': minor
'@roomote/worker': minor
---

Plan mode is now always on: the `PlanMode` feature flag has been removed entirely, so planning turns run read-only for every deployment with no opt-out. The model role that powers it is now called "Advisor" in the settings UI and docs — it keeps backing the planning workflow, and it also backs a new `advisor` subagent that the coding agent consults when it is stuck or needs a second opinion. The advisor uses the configured Advisor model when one is set and otherwise falls back to the active coding model at the advisor reasoning level, which defaults to high.
4 changes: 2 additions & 2 deletions apps/docs/environment-variables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,13 @@ as per-task auth tokens or workspace paths.
| `R_VISION_MODEL` | Optional | Vision-capable model for visual inspection and screenshot/image work. |
| `R_CODE_REVIEW_MODEL` | Optional | Model for initial PR/MR review and review-sync tasks. |
| `R_EXPLORE_MODEL` | Optional | Exploration/helper model for investigation-oriented subtasks. |
| `R_PLANNING_MODEL` | Optional | Planning model used for longer coding-task planning. |
| `R_PLANNING_MODEL` | Optional | Advisor model used for planning turns and advisor consultations inside coding tasks. |
| `R_MODEL_REASONING_EFFORT` | Optional | Reasoning level for the coding model: `low`, `medium`, `high`, or `xhigh`. |
| `R_SMALL_MODEL_REASONING_EFFORT` | Optional | Reasoning level for the helper model. |
| `R_VISION_MODEL_REASONING_EFFORT` | Optional | Reasoning level for the vision model. |
| `R_CODE_REVIEW_MODEL_REASONING_EFFORT` | Optional | Reasoning level for the code review model. |
| `R_EXPLORE_MODEL_REASONING_EFFORT` | Optional | Reasoning level for the exploration model. |
| `R_PLANNING_MODEL_REASONING_EFFORT` | Optional | Reasoning level for the planning model. |
| `R_PLANNING_MODEL_REASONING_EFFORT` | Optional | Reasoning level for the advisor model. |
| `R_MODEL_ENV_KEYS` | Optional | Comma- or space-separated list of extra provider key env vars to forward to task workers. |
| `OPENROUTER_API_KEY` | Provider key | OpenRouter API key. Can also be saved from **Settings > Models**. |
| `AI_GATEWAY_API_KEY` | Provider key | Vercel AI Gateway API key. |
Expand Down
7 changes: 4 additions & 3 deletions apps/docs/models.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ where you want more speed, quality, or cost control.
| Helper model | Lightweight routing, titles, summaries, and quick internal decisions | Low latency, low cost, and enough accuracy for short judgments |
| Vision model | Visual inspection, screenshots, UI review, and image-heavy work | Image understanding, layout reasoning, and concise visual feedback |
| Code review model | Initial PR or MR review tasks and review-sync work | Careful reasoning, bug finding, security awareness, and willingness to cite evidence |
| Planning model | Planning steps inside longer coding tasks | Deliberate reasoning, decomposition, and ability to keep constraints in mind |
| Advisor model | Planning turns and advisor consultations inside longer coding tasks | Deliberate reasoning, decomposition, and ability to keep constraints in mind |

You do not need a separate model for every role. Many teams start with one
strong default model, then add a cheaper helper model or a stronger review
Expand All @@ -178,7 +178,8 @@ A practical starting point:

- use **Medium** for the default coding model
- use **Low** for helper and vision work unless you see quality issues
- use **High** for code review and planning when you want more careful analysis
- use **High** for code review and advisor work when you want more careful
analysis
- reserve **Extra high** for models and workflows where the added cost is
justified

Expand Down Expand Up @@ -215,7 +216,7 @@ For code review, prioritize:
- good false-positive control
- attention to tests, regressions, security, and edge cases

For planning, prioritize:
For advisor work, prioritize:

- structured reasoning
- ability to break work into practical steps
Expand Down
10 changes: 5 additions & 5 deletions apps/web/src/components/settings/ModelSettingsSection.test.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions apps/web/src/components/settings/ModelSettingsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -237,14 +237,14 @@ const TASK_MODEL_ROLE_CONFIGS: readonly TaskModelRoleConfig[] = [
},
{
role: 'planning',
label: 'Planning model',
label: 'Advisor model',
description:
'Used for plan-mode turns while the planning workflow is active.',
'Used for plan-mode turns in the planning workflow and for the advisor subagent that coding tasks consult when they need help.',
icon: ScrollText,
modelEnvVarName: 'R_PLANNING_MODEL',
reasoningEnvVarName: 'R_PLANNING_MODEL_REASONING_EFFORT',
placeholder: 'Select a planning model',
reasoningAriaLabel: 'Planning model reasoning level',
placeholder: 'Select an advisor model',
reasoningAriaLabel: 'Advisor model reasoning level',
allowSameAsCoding: true,
},
];
Expand Down
1 change: 0 additions & 1 deletion apps/web/src/lib/mock-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { FeatureFlag } from '@roomote/feature-flags';
import type { UserResource } from '@/types';

export const mockFeatureFlags: Record<FeatureFlag, boolean> = {
[FeatureFlag.PlanMode]: false,
[FeatureFlag.SlackEvalLauncher]: false,
[FeatureFlag.ShowDebugUISetting]: false,
[FeatureFlag.SlackProofAutoPost]: false,
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/trpc/commands/task-models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -864,7 +864,7 @@ export async function updateTaskModelSettingsCommand(
normalizedPlanningModelId !== null &&
!knownModelIds.has(normalizedPlanningModelId)
) {
fieldErrors.planningModelId = 'Choose a valid planning model.';
fieldErrors.planningModelId = 'Choose a valid advisor model.';
}

const codingManagedByEnv = isConfiguredEnvValue(process.env.R_MODEL);
Expand Down
97 changes: 0 additions & 97 deletions apps/worker/src/run-task/__tests__/run-task.test.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

95 changes: 95 additions & 0 deletions apps/worker/src/run-task/agent-home.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ const ROOMOTE_OPENCODE_VISUAL_AGENT_NAME = 'visual';

const ROOMOTE_OPENCODE_JUDGE_AGENT_NAME = 'judge';

const ROOMOTE_OPENCODE_ADVISOR_AGENT_NAME = 'advisor';

const ROOMOTE_OPENCODE_EXPLORE_AGENT_NAME = 'explore';
const OPENCODE_GENERAL_AGENT_NAME = 'general';

Expand All @@ -72,6 +74,9 @@ const ROOMOTE_OPENCODE_VISUAL_MODEL_INSTRUCTIONS_FILE_NAME =
const ROOMOTE_OPENCODE_JUDGE_MODEL_INSTRUCTIONS_FILE_NAME =
'roomote-opencode-judge-model-instructions.md';

const ROOMOTE_OPENCODE_ADVISOR_MODEL_INSTRUCTIONS_FILE_NAME =
'roomote-opencode-advisor-model-instructions.md';

const ROOMOTE_OPENCODE_PROOF_RUNNER_INSTRUCTIONS_FILE_NAME =
'roomote-opencode-proof-runner-instructions.md';

Expand Down Expand Up @@ -104,6 +109,20 @@ const ROOMOTE_OPENCODE_JUDGE_AGENT_PROMPT = [
'Do not edit files, run shell commands, launch other agents, or make final product decisions. Keep your response focused on review findings and verdicts for the parent agent.',
].join('\n');

const ROOMOTE_OPENCODE_ADVISOR_AGENT_PROMPT = [
'You are Roomote coding advisor support.',
'',
'The parent coding agent consults you when it is stuck or needs help: repeated failed attempts, a confusing bug, an uncertain approach or design decision, or conflicting constraints.',
'',
'Start from the context the parent provides: the goal, what was tried, exact errors or failing output, and the relevant files. Read additional repository files when needed to ground your advice, but keep exploration targeted to the question.',
'',
'Return concrete, actionable guidance: 1) your diagnosis or best hypotheses, 2) the recommended approach and why, 3) specific next steps the parent can execute, 4) any risks or alternatives worth considering. Prefer a single clear recommendation over a menu of options.',
'',
'If the provided context is insufficient to advise confidently, say exactly what evidence would disambiguate the situation.',
'',
'Do not edit files, run shell commands, launch other agents, or make final product decisions. Keep your response focused on advice the parent agent can act on.',
].join('\n');

const ROOMOTE_OPENCODE_ARCHITECT_AGENT_PROMPT = [
'You are Roomote planning support: a planning specialist for read-mostly plan-mode turns.',
'',
Expand Down Expand Up @@ -623,6 +642,36 @@ function createJudgeAgentConfig(
};
}

function createAdvisorAgentConfig(
model: string,
reasoningOptions?: Record<string, unknown> | null,
): Record<string, unknown> {
return {
description:
'Consulting advisor the coding agent can ask for help when it is stuck, has repeated failed attempts, or needs a second opinion on approach or debugging.',
mode: 'subagent',
model,
...(reasoningOptions ? { options: reasoningOptions } : {}),
prompt: ROOMOTE_OPENCODE_ADVISOR_AGENT_PROMPT,
permission: {
read: 'allow',
list: 'allow',
glob: 'allow',
grep: 'allow',
external_directory: 'allow',
webfetch: 'allow',
edit: 'deny',
bash: 'deny',
task: 'deny',
todowrite: 'deny',
lsp: 'deny',
skill: 'deny',
question: 'deny',
},
tools: { ...SLACK_POSTING_TOOL_EXCLUSIONS },
};
}

// Note: the architect agent deliberately keeps the Slack-posting tools. It is
// a primary (parent-session) agent that owns plan-mode turns end to end, so
// it must be able to reply to the originating Slack thread itself.
Expand Down Expand Up @@ -735,6 +784,22 @@ function createJudgeModelInstructions(): string {
].join('\n');
}

function createAdvisorModelInstructions(): string {
return [
`An OpenCode \`${ROOMOTE_OPENCODE_ADVISOR_AGENT_NAME}\` subagent is always configured as consulting support for the active coding agent.`,
'',
'When `R_PLANNING_MODEL` is configured, the advisor uses that advisor model. Otherwise it falls back to the active coding model, running at the advisor reasoning level (defaults to high) for deeper analysis.',
'',
`When you are stuck or need help — repeated failed fix attempts, a confusing bug, an uncertain approach or design decision, or conflicting constraints — delegate one focused consultation to the \`${ROOMOTE_OPENCODE_ADVISOR_AGENT_NAME}\` subagent with the Task tool before grinding through more blind attempts.`,
'',
'Give the advisor a complete brief: the goal, what you already tried, the exact errors or failing output, and the relevant file paths. The advisor can read the repository but cannot run commands, so include runtime evidence it cannot gather itself.',
'',
'Treat the advisor response as internal guidance for the parent workflow. Verify its recommendations against the repository before acting on them, and keep orchestration, code changes, and final user-facing decisions in the parent agent.',
'',
"Do not paste the advisor's full output into chat or any user-facing reply. Surface at most a brief, parent-authored summary of the direction taken.",
].join('\n');
}

function createExploreAgentConfig(options: {
model: string;
reasoningOptions?: Record<string, unknown> | null;
Expand Down Expand Up @@ -872,6 +937,22 @@ function resolveModelBackedOpenCodeConfig(
: null,
),
};
// The advisor subagent shares the planning/advisor model role. It defaults
// to the coding model when no advisor model is configured, and the advisor
// reasoning level (default high) applies in either case so consultations
// run with deeper reasoning than ordinary coding turns.
const advisorModel = planningModel ?? effectiveCodingModel;
const advisorAgent = {
[ROOMOTE_OPENCODE_ADVISOR_AGENT_NAME]: createAdvisorAgentConfig(
advisorModel,
planningModelReasoningEffort
? buildOpenCodeModelReasoningOptions(
advisorModel,
planningModelReasoningEffort,
)
: null,
),
};
const exploreEffectiveModel = exploreModel ?? effectiveCodingModel;
const exploreAgent =
exploreModel || exploreModelReasoningEffort
Expand Down Expand Up @@ -917,6 +998,7 @@ function resolveModelBackedOpenCodeConfig(
const agent = {
...(visualAgent ?? {}),
...judgeAgent,
...advisorAgent,
...(exploreAgent ?? {}),
...architectAgent,
...generalAgent,
Expand Down Expand Up @@ -1124,6 +1206,19 @@ export function generateOpenCodeConfig({
instructions.push(judgeModelInstructionsPath);
}

if (operatorAgent[ROOMOTE_OPENCODE_ADVISOR_AGENT_NAME]) {
const advisorModelInstructionsPath = path.join(
openCodeConfigDir,
ROOMOTE_OPENCODE_ADVISOR_MODEL_INSTRUCTIONS_FILE_NAME,
);
fs.writeFileSync(
advisorModelInstructionsPath,
createAdvisorModelInstructions(),
'utf8',
);
instructions.push(advisorModelInstructionsPath);
}

const proofBrowserTarget = runtimeEnv.ROOMOTE_PROOF_BROWSER_TARGET?.trim();
delete runtimeEnv.ROOMOTE_PROOF_BROWSER_TARGET;

Expand Down
Loading