Skip to content
Open
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
8 changes: 7 additions & 1 deletion docs/guides/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,12 @@ See [config-surface.md](../references/config-surface.md) for the full override s
- `standard`: recommended default for most tasks.
- `intensive`: best for complex or high-risk tasks (large refactors, flaky tests, heavy verification).

For `glm run`, task intent is routed separately from prompt lane:

- review-style tasks automatically use a review overlay that prioritizes findings, regressions, and missing tests
- delivery tasks keep the normal change-oriented overlay
- verifier harness stays disabled unless `--loop` is enabled, even if `--mode intensive` is selected manually

Defaults:

- `glm chat`: `standard`
Expand All @@ -181,7 +187,7 @@ Defaults:

You can override these defaults via `glm config set taskLaneDefault <auto|direct|standard|intensive>`.

When `taskLaneDefault=auto`, `glm run` will pick `direct` for trivial tasks (docs/lint/format) and `standard` otherwise. `glm run --loop` still forces `intensive`.
When `taskLaneDefault=auto`, `glm run` will pick `direct` for trivial tasks (docs/lint/format), `standard` for normal delivery work, and a review overlay for review-style tasks. `glm run --loop` still forces `intensive` and enables the verifier harness.

## Approvals (`--yolo` and `/approval`)

Expand Down
8 changes: 7 additions & 1 deletion docs/guides/cli.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,12 @@ glm auth login
- `standard`:默认推荐;对一般开发任务会先做简短计划,再实现并在可行时验证。
- `intensive`:适合复杂或高风险任务(大范围重构、测试不稳定、需要更多自检),更强调明确计划和严格验证。

对 `glm run` 来说,任务意图会与 prompt lane 分开判断:

- review 类任务会自动切到 review overlay,更强调 findings、回归风险和缺失测试
- delivery 类任务仍使用面向改动交付的 overlay
- verifier harness 只有在启用 `--loop` 时才会开启;即使手动指定 `--mode intensive`,也不会隐式开启 verifier harness

默认行为:

- `glm chat` 默认 `standard`
Expand All @@ -181,7 +187,7 @@ glm auth login

你也可以通过 `glm config set taskLaneDefault <auto|direct|standard|intensive>` 覆盖上述默认值。

当 `taskLaneDefault=auto` 时,`glm run` 会对简单任务(如文档、lint、format)自动选择 `direct`,其他情况选择 `standard`;`glm run --loop` 仍会强制使用 `intensive`。
当 `taskLaneDefault=auto` 时,`glm run` 会对简单任务(如文档、lint、format)自动选择 `direct`,对普通交付任务选择 `standard`,对 review 类任务追加 review overlay;`glm run --loop` 仍会强制使用 `intensive`,并启用 verifier harness

## 审批(`--yolo` 与 `/approval`)

Expand Down
2 changes: 2 additions & 0 deletions docs/references/config-surface.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ The CLI influences runtime behavior via flags. `glm inspect --json` is the easie
- Selects the prompt lane overlay (execution style) used by `glm chat` and `glm run`.
- This affects how the model is instructed to work (plan-first vs. direct, verification emphasis).
- It does **not** enable/disable the loop. Use `--loop` and `loop.*` config keys for loop behavior.
- For `glm run`, task intent is resolved separately. Review-style tasks receive a review overlay, while delivery tasks receive the normal change-oriented overlay.
- Manual `--mode intensive` does not enable verifier harness by itself. Verifier harness is only active when `--loop` is enabled.
- Defaults:
- `glm chat`: `standard`
- `glm run`: `standard`
Expand Down
2 changes: 2 additions & 0 deletions docs/references/config-surface.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ CLI 会通过 flags 影响 runtime 行为。排查时建议直接运行 `glm ins
- 选择 `glm chat` / `glm run` 使用的 prompt lane(执行风格)。
- 会影响模型被如何引导工作(是否先计划、是否强调验证等)。
- 不会启用或关闭 loop。loop 仍由 `--loop` 与 `loop.*` 配置控制。
- 对 `glm run` 而言,任务意图会独立解析。review 类任务会使用 review overlay,delivery 类任务会继续使用面向改动交付的 overlay。
- 手动指定 `--mode intensive` 不会单独开启 verifier harness;只有启用 `--loop` 时 verifier harness 才会生效。
- 默认值:
- `glm chat`:`standard`
- `glm run`:`standard`
Expand Down
28 changes: 14 additions & 14 deletions src/commands/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
type SingleTaskExecutionResult,
type LoopTaskExecutionResult,
} from "../runtime/run-runtime.js";
import { routePromptModeForTask } from "../runtime/task-router.js";
import { routeTaskExecutionForRun } from "../runtime/task-router.js";
import {
createGlmRuntime,
withPreservedProcessCwd,
Expand Down Expand Up @@ -110,14 +110,12 @@ export async function runRunCommand(input: RunCommandInput): Promise<number> {
},
async () => {
const configuredLane = fileConfig.taskLaneDefault ?? "auto";
const promptMode =
input.promptMode ??
(configuredLane === "auto"
? routePromptModeForTask({
task: input.task,
loopEnabled: loopOptions.enabled,
}).mode
: (configuredLane as PromptMode));
const taskRoute = routeTaskExecutionForRun({
task: input.task,
loopEnabled: loopOptions.enabled,
promptModeOverride:
input.promptMode ?? (configuredLane === "auto" ? undefined : (configuredLane as PromptMode)),
});

const hooks = {
emit: outputFormat === "jsonl" ? emitJsonl : undefined,
Expand All @@ -131,7 +129,7 @@ export async function runRunCommand(input: RunCommandInput): Promise<number> {
const runtime = await createGlmRuntime({
cwd: input.cwd,
...runtimeConfig,
promptMode,
promptMode: taskRoute.promptMode,
});

const startedAt = new Date().toISOString();
Expand All @@ -143,12 +141,13 @@ export async function runRunCommand(input: RunCommandInput): Promise<number> {
provider: runtimeConfig.provider,
model: runtimeConfig.model,
loop: loopOptions.enabled,
promptMode,
promptMode: taskRoute.promptMode,
taskRoute,
});

const result: SingleTaskExecutionResult | LoopTaskExecutionResult = loopOptions.enabled
? await runTaskLoop(runtime, input.task, loopOptions, promptMode, hooks)
: await runSingleTask(runtime, input.task, promptMode, hooks);
? await runTaskLoop(runtime, input.task, loopOptions, taskRoute.promptMode, hooks)
: await runSingleTask(runtime, input.task, taskRoute, hooks);

hooks.emit?.({
type: "run.done",
Expand All @@ -168,7 +167,8 @@ export async function runRunCommand(input: RunCommandInput): Promise<number> {
task: input.task,
provider: runtimeConfig.provider,
model: runtimeConfig.model,
promptMode,
promptMode: taskRoute.promptMode,
taskRoute,
loop: loopOptions,
result,
});
Expand Down
6 changes: 5 additions & 1 deletion src/loop/profiles/code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ export function createCodeLoopProfile(promptMode: PromptMode = "intensive"): Loo
return {
name: "code",
buildLoopContract(task: string): string {
return composeTaskPrompt(task, promptMode);
return composeTaskPrompt(task, {
promptMode,
taskIntent: "delivery",
verifierHarness: "loop",
});
},
buildRepairPrompt(result: VerificationResult, nextRound: number): string {
return composeRepairPrompt(result, nextRound);
Expand Down
32 changes: 22 additions & 10 deletions src/prompt/task-overlay.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,39 @@
import type { PromptMode } from "./mode-overlays.js";
import type { TaskPromptProfile } from "./task-prompt-profile.js";

export function buildTaskOverlay(task: string, mode: PromptMode): string {
export function buildTaskOverlay(task: string, profile: TaskPromptProfile): string {
const trimmedTask = task.trim();
const { promptMode, taskIntent, verifierHarness } = profile;

const instructions =
mode === "direct"
taskIntent === "review"
? [
"- Review the requested scope before proposing changes.",
"- Prioritize concrete findings, regressions, risks, and missing tests.",
"- Do not make code changes unless the task explicitly asks for them.",
]
: promptMode === "direct"
? [
"- Work the bounded task directly.",
"- Keep the change minimal and report the concrete result.",
]
: mode === "intensive"
: promptMode === "intensive"
? [
"- Start with a short plan.",
"- Make the smallest coherent fix or change.",
"- Stop after the focused implementation so verification can run.",
]
: [
"- Start with a short plan when needed.",
"- Make the smallest coherent change that completes the task.",
"- Verify before claiming success when the repo offers a practical check.",
];
"- Make the smallest coherent change that completes the task.",
"- Verify before claiming success when the repo offers a practical check.",
];

return [`Task overlay (${mode}):`, trimmedTask, "", "Round instructions:", ...instructions].join(
"\n",
);
return [
`Task overlay (${promptMode}/${taskIntent}):`,
trimmedTask,
"",
`Verifier harness: ${verifierHarness}`,
"Round instructions:",
...instructions,
].join("\n");
}
10 changes: 10 additions & 0 deletions src/prompt/task-prompt-profile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { PromptMode } from "./mode-overlays.js";

export type TaskIntent = "delivery" | "review";
export type VerifierHarnessMode = "disabled" | "loop";

export type TaskPromptProfile = {
promptMode: PromptMode;
taskIntent: TaskIntent;
verifierHarness: VerifierHarnessMode;
};
5 changes: 3 additions & 2 deletions src/runtime/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getBaseContractPath, loadBaseContractPrompt } from "../prompt/base-cont
import { buildModeOverlay, type PromptMode } from "../prompt/mode-overlays.js";
import { buildRepoOverlay } from "../prompt/repo-overlay.js";
import { buildTaskOverlay } from "../prompt/task-overlay.js";
import type { TaskPromptProfile } from "../prompt/task-prompt-profile.js";
import { buildVerificationOverlay } from "../prompt/verification-overlay.js";
import type { VerificationResult } from "../loop/types.js";
import { buildRepoContextPack } from "./repo-context.js";
Expand Down Expand Up @@ -37,8 +38,8 @@ export async function buildRuntimePromptStack(args: {
};
}

export function composeTaskPrompt(task: string, mode: PromptMode): string {
return buildTaskOverlay(task, mode);
export function composeTaskPrompt(task: string, profile: TaskPromptProfile): string {
return buildTaskOverlay(task, profile);
}

export function composeRepairPrompt(result: VerificationResult, nextRound: number): string {
Expand Down
9 changes: 7 additions & 2 deletions src/runtime/run-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { runVerificationCommand } from "../loop/verify-runner.js";
import type { VerificationCommandResolution, VerificationResult } from "../loop/types.js";
import { composeTaskPrompt } from "./prompt.js";
import type { PromptMode } from "../prompt/mode-overlays.js";
import type { TaskPromptProfile } from "../prompt/task-prompt-profile.js";
import { patchRuntimeLoopStatus } from "../diagnostics/runtime-status.js";

export type RunRuntimeHooks = {
Expand Down Expand Up @@ -40,10 +41,14 @@ function defaultWriteHuman(text: string): void {
export async function runSingleTask(
runtime: AgentSessionRuntime,
task: string,
promptMode: PromptMode = "standard",
taskPromptProfile: TaskPromptProfile = {
promptMode: "standard",
taskIntent: "delivery",
verifierHarness: "disabled",
},
hooks?: RunRuntimeHooks,
): Promise<SingleTaskExecutionResult> {
return runPromptSequence(runtime, [composeTaskPrompt(task, promptMode)], hooks);
return runPromptSequence(runtime, [composeTaskPrompt(task, taskPromptProfile)], hooks);
}

type AgentAssistantMessage = {
Expand Down
69 changes: 61 additions & 8 deletions src/runtime/task-router.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import type { PromptMode } from "../prompt/mode-overlays.js";
import type { TaskPromptProfile } from "../prompt/task-prompt-profile.js";

export type TaskRouterDecision = {
mode: PromptMode;
promptMode: PromptMode;
taskIntent: TaskPromptProfile["taskIntent"];
verifierHarness: TaskPromptProfile["verifierHarness"];
reason: string;
};

Expand Down Expand Up @@ -57,28 +60,78 @@ function isLikelyTrivialTask(task: string): boolean {
return false;
}

export function routePromptModeForTask(args: {
function isLikelyReviewTask(task: string): boolean {
return matchesAnyKeyword(task, [
"review",
"audit",
"code review",
"pr review",
"regression review",
"security review",
"审查",
"评审",
"代码审查",
]);
}

export function routeTaskExecutionForRun(args: {
task: string;
loopEnabled: boolean;
promptModeOverride?: PromptMode;
}): TaskRouterDecision {
const task = normalizeTaskText(args.task);

if (args.loopEnabled) {
return {
mode: "intensive",
reason: "loop enabled",
promptMode: args.promptModeOverride ?? "intensive",
taskIntent: "delivery",
verifierHarness: "loop",
reason:
args.promptModeOverride === undefined ? "loop enabled" : "loop enabled; prompt mode override",
};
}

if (isLikelyReviewTask(task)) {
return {
promptMode: args.promptModeOverride ?? "standard",
taskIntent: "review",
verifierHarness: "disabled",
reason:
args.promptModeOverride === undefined
? "review task heuristic"
: "review task heuristic; prompt mode override",
};
}

if (isLikelyTrivialTask(task)) {
return {
mode: "direct",
reason: "trivial task heuristic",
promptMode: args.promptModeOverride ?? "direct",
taskIntent: "delivery",
verifierHarness: "disabled",
reason:
args.promptModeOverride === undefined
? "trivial task heuristic"
: "trivial task heuristic; prompt mode override",
};
}

return {
mode: "standard",
reason: "default lane",
promptMode: args.promptModeOverride ?? "standard",
taskIntent: "delivery",
verifierHarness: "disabled",
reason:
args.promptModeOverride === undefined ? "default lane" : "default lane; prompt mode override",
};
}

export function routePromptModeForTask(args: {
task: string;
loopEnabled: boolean;
promptModeOverride?: PromptMode;
}): { mode: PromptMode; reason: string } {
const decision = routeTaskExecutionForRun(args);
return {
mode: decision.promptMode,
reason: decision.reason,
};
}
Loading
Loading