@orion-agents/orion-code 0.3.26 → 0.3.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -22,14 +22,115 @@ which is **not** a pass.
22
22
 
23
23
  ## [Unreleased]
24
24
 
25
- ## [0.3.26] — UNRELEASED
26
-
27
- > **Status: candidate.** Open. Structured-diff integrity and Session lifecycle: body
28
- > rows that look like file headers stay body rows (#274), a hunk split across
29
- > pagination keeps its rows, line numbers and identity (#275), permanent Session
30
- > deletion removes the v2 Thread records (#276), and exiting a cutover Session no
31
- > longer rewrites its summary from the frozen legacy JSONL (#277). Plan:
32
- > `docs/plan/v0.3.26-plan.md`. Not merged, tagged or published.
25
+ ## [0.3.28] — UNRELEASED
26
+
27
+ > **Status: candidate.** Open. The typed `/review` runtime & CLI slice
28
+ > (plan: `docs/plan/v0.3.28-plan.md`, PR2 core of the v0.3.27 review loop):
29
+ > `/review` arguments now parse into the frozen `ReviewSelectorV1` (worktree /
30
+ > `--base` / repeatable `--path`, back-compat single-path filter) and unknown
31
+ > input returns the usage text instead of silently continuing as an ordinary
32
+ > chat turn. The review runs against the admission-time frozen Git snapshot
33
+ > through a bounded single-pass model completion; the model's findings are
34
+ > validated against the contract and anchored to the snapshot manifest, with
35
+ > invalid or unanchored findings counted and surfaced rather than silently
36
+ > dropped. Every run produces a digest-verifiable `CodeReviewResultV1` receipt
37
+ > rendered by the shared formatter with honest state banners: an empty scope
38
+ > reports "no changes under review" without calling the model, and partial,
39
+ > failed, cancelled or inconclusive runs never render as a green pass.
40
+ > Web wiring (PR3) and the qualification benchmark (PR4) remain subsequent
41
+ > slices. Not merged, tagged or published.
42
+
43
+ ### Added
44
+
45
+ - `src/services/code-review/selector.ts` — the typed `/review` argument
46
+ parser: empty / `all` → worktree scope, `--base <ref>` → base scope,
47
+ repeatable `--path <path>` filters, single bare relative path kept as a
48
+ back-compat worktree filter; unknown options, missing values, repeated
49
+ `--base`, absolute paths and traversal are typed usage errors. Fourteen
50
+ tests pin every accepted shape and rejection path.
51
+ - `src/services/code-review/runner.ts` — the bounded single-pass review
52
+ runner: `resolveReviewBudgets` (tighten-only) → `captureReviewSnapshot`
53
+ (worktree contained reader) → frozen unified diff captured with the same
54
+ admission-time arguments → rules digest binding the read-only constraints
55
+ digest plus ancestor `AGENTS.md` contents → one model completion through an
56
+ injected adapter → contract validation + snapshot-membership anchoring +
57
+ `maxFindings` budget → `resolveReviewConclusion` → receipt digest
58
+ recomputation. Empty scope completes without a model call and is labeled
59
+ `empty_scope_no_changes`; cancellation, model errors and unparsable output
60
+ are typed failures, never green passes.
61
+ - `src/services/code-review/format.ts` — the shared TUI formatter: scope and
62
+ resolved target header, findings grouped P0→P3 with side:line locations,
63
+ invalid-finding counts, coverage / skips / budget exhaustion footer and a
64
+ receipt digest line; partial / failed / cancelled banners never render as a
65
+ clean pass.
66
+ - `/review` command wiring (`src/commands/workflow-commands.ts`,
67
+ `workflow-command-handlers.ts`): the command is now a read-only builtin
68
+ that runs the typed workflow; unrecognized arguments return usage, a
69
+ missing model returns an explicit error, and no path falls back to ordinary
70
+ chat.
71
+
72
+ ### Changed
73
+
74
+ - `src/services/code-review/snapshot.ts` exports `reviewUnifiedDiffArgs` so
75
+ the runner reuses the exact frozen diff arguments instead of re-deriving
76
+ OIDs after admission; no frozen semantics changed.
77
+
78
+ ### Not run
79
+
80
+ - Real-PTY CLI verification, Web E2E (no Web changes in this slice), and the
81
+ paid human-annotated review-quality benchmark remain `not_run`, matching
82
+ the v0.3.27 plan's PR3/PR4 sequencing.
83
+
84
+ ## [0.3.27] — 2026-10-22
85
+
86
+ > **Status: published, 2026-10-22.** Tagged `v0.3.27` (`0be5b18`), merged via
87
+ > #283, and visible on npm as registry `latest`. First slice of the
88
+ > professional code review loop
89
+ > (plan: `docs/plan/v0.3.27-plan.md`): the shared review contract (selectors,
90
+ > states, conclusions, findings, tighten-only budgets, receipt digests,
91
+ > stale-applicability) is frozen in `src/services/code-review` and bound by
92
+ > contract tests; the shared Git snapshot reader captures worktree (staged /
93
+ > unstaged / untracked, separate sources) and base (frozen merge-base..HEAD
94
+ > OIDs, rename tracking) scopes with typed scope errors; and the read-only
95
+ > workflow constraints deny every non-read tool by default with a dispatch-level
96
+ > guard whose digest binds receipts. Runtime / CLI wiring (PR2), the Web loop
97
+ > (PR3) and the qualification benchmark (PR4) are subsequent slices of the same
98
+ > plan and are not part of this release; PR2's core CLI slice ships in 0.3.28.
99
+
100
+ ### Added
101
+
102
+ - #277-plan PR0: `src/services/code-review/contract.ts` — the shared review
103
+ contract with the plan §4.1 conclusion mapping (a partial, failed, cancelled
104
+ or untrustworthy review can never surface as a green "no findings"),
105
+ tighten-only budgets from plan §4.3, finding validation, receipt digests
106
+ verified by recomputation, and stale-applicability annotation that never
107
+ rewrites history. Twelve contract tests pin the semantics.
108
+ - PR0: the human-annotation review-quality benchmark set (12 defect + 8 clean
109
+ samples across TypeScript/JavaScript/Python with content digests, expected
110
+ triggers, allowed locations and frozen scoring thresholds), awaiting
111
+ per-sample human sign-off before any paid model run (plan §9.3).
112
+ - PR1: `src/services/code-review/snapshot.ts` — the shared Git snapshot reader:
113
+ worktree scope records staged (index OIDs), unstaged (working content with
114
+ before/after digest re-checks) and untracked changes as separate sources;
115
+ base scope freezes ref/HEAD/merge-base into immutable OIDs with rename
116
+ tracking and typed `ref_unresolved` / `no_merge_base` errors; sensitive and
117
+ binary entries are skipped without being read; tightened budgets skip the
118
+ excess with typed reasons; the contained worktree reader refuses to leave the
119
+ repository root.
120
+ - PR1: `src/services/code-review/constraints.ts` — deny-by-default read-only
121
+ workflow constraints (only `read_file`, `list_files`, `glob`, `grep` pass the
122
+ dispatch-level guard) with a frozen digest that receipts bind.
123
+
124
+ ## [0.3.26] — 2026-10-17
125
+
126
+ > **Status: published.** Released as `@orion-agents/orion-code@0.3.26` from
127
+ > `refs/tags/v0.3.26`, and merged to `main` as `b810996` (#278). Structured-diff
128
+ > integrity and Session lifecycle: header-lookalike body rows stay body rows
129
+ > (#274), hunks split across pagination keep their rows, line numbers and
130
+ > identity (#275), permanent Session deletion removes the v2 Thread records
131
+ > (#276), and exiting a cutover Session no longer rewrites its summary from the
132
+ > frozen legacy JSONL (#277). Plan: `docs/plan/v0.3.26-plan.md`. GitHub Release:
133
+ > https://github.com/orion-agents/orion-code/releases/tag/v0.3.26
33
134
 
34
135
  ### Fixed
35
136
 
package/README.md CHANGED
@@ -2,12 +2,15 @@
2
2
 
3
3
  Local-first, goal-driven coding agent for the terminal and browser.
4
4
 
5
- > v0.3.26 candidate — open. Structured-diff integrity and Session lifecycle: header-lookalike
6
- > body rows stay body rows (#274), hunks split across pagination keep their rows and line
7
- > numbers (#275), permanent Session deletion removes the v2 Thread records (#276), and
8
- > exiting a cutover Session no longer regresses its summary from the legacy JSONL (#277).
9
- > The previous release 0.3.25 is on npm (registry `latest`), tagged `v0.3.25` (`c356323`,
10
- > #273) and released on GitHub.
5
+ > v0.3.28 candidate — open. The typed `/review` workflow: slash arguments are
6
+ > parsed into the frozen review selector (worktree / `--base` / `--path`, with
7
+ > usage errors instead of silent chat fallback), the review runs against the
8
+ > admission-time frozen Git snapshot through a bounded single-pass model call,
9
+ > model findings are validated against the contract and the snapshot manifest,
10
+ > and the run renders a digest-verifiable receipt with honest state banners
11
+ > (empty scope ≠ review pass; partial / failed / cancelled never green). The
12
+ > previous release 0.3.27 is on npm (registry `latest`), tagged `v0.3.27`
13
+ > (`0be5b18`, #283) and released on GitHub.
11
14
 
12
15
  [中文说明](README.zh-CN.md) ·
13
16
  [v0.3.24 plan](docs/plan/v0.3.24-plan.md) ·
@@ -170,10 +173,10 @@ user extension boundaries.
170
173
  Node.js 22.12+, 24, and 26 are supported. Use Node 24 LTS for production or Node 26 Current for
171
174
  current development environments. Node 20 is upstream EOL and is no longer a v0.3 runtime.
172
175
 
173
- To install the pinned v0.3.26 package when it is available on npm:
176
+ To install the pinned v0.3.28 package when it is available on npm:
174
177
 
175
178
  ```bash
176
- npm install -g @orion-agents/orion-code@0.3.26
179
+ npm install -g @orion-agents/orion-code@0.3.28
177
180
  orion --version
178
181
  orion doctor
179
182
  ```
package/README.zh-CN.md CHANGED
@@ -2,10 +2,12 @@
2
2
 
3
3
  面向终端与浏览器、本地优先的目标驱动 Coding Agent。
4
4
 
5
- > v0.3.26 候选版本:进行中。结构化 diff 完整性与会话生命周期:形似文件头的 body 行保持为
6
- > body 行(#274)、跨页拆分的 hunk 保留行与行号(#275)、永久删除会话同时清除 v2 Thread
7
- > 记录(#276)、退出已切换会话不再从旧 JSONL 回退 summary(#277)。上一版 0.3.25 已发布
8
- > npm(registry `latest`)、打 tag `v0.3.25`(`c356323`,#273)并发布 GitHub Release。
5
+ > v0.3.28 候选版本:进行中。类型化 `/review` 审查工作流:slash 参数解析为冻结的审查
6
+ > selector(worktree / `--base` / `--path`,未知参数返回用法而非静默转入普通聊天);审查
7
+ > 基于 admission 时冻结的 Git snapshot,经有界单轮模型补全执行;模型 finding 按契约与
8
+ > snapshot manifest 校验;输出 digest 可验证的 receipt 与如实状态横幅(空范围 ≠ 审查通过;
9
+ > partial / failed / cancelled 永远不会显示为绿色)。上一版 0.3.27 已发布 npm
10
+ > (registry `latest`)、打 tag `v0.3.27`(`0be5b18`,#283)并发布 GitHub Release。
9
11
 
10
12
  [English](README.md) ·
11
13
  [v0.3.24 方案](docs/plan/v0.3.24-plan.md) ·
@@ -100,10 +102,10 @@ MCP 仍是用户可见的扩展边界。
100
102
  支持 Node.js 22.12+、24 和 26。生产环境建议使用 Node 24 LTS,当前开发环境也支持 Node 26
101
103
  Current。Node 20 已结束上游维护,不再属于 v0.3 Runtime 合同。
102
104
 
103
- v0.3.26 在 npm 可用后,可安装固定版本:
105
+ v0.3.28 在 npm 可用后,可安装固定版本:
104
106
 
105
107
  ```bash
106
- npm install -g @orion-agents/orion-code@0.3.26
108
+ npm install -g @orion-agents/orion-code@0.3.28
107
109
  orion --version
108
110
  orion doctor
109
111
  ```
@@ -4,5 +4,13 @@ declare function handlePlan(ctx: CommandContext, args: string): Promise<CommandR
4
4
  declare function handleDiff(ctx: CommandContext, args: string): CommandResult;
5
5
  declare function handleCommitPlan(ctx: CommandContext, args: string): CommandResult;
6
6
  declare function continueAsSlashChat(name: string, args: string): CommandResult;
7
- export { handlePlan, handleDiff, handleCommitPlan, continueAsSlashChat };
7
+ /**
8
+ * v0.3.28 — the typed `/review` workflow (docs/plan/v0.3.28-plan.md S4).
9
+ * Arguments are parsed into a frozen selector; unrecognized input returns the
10
+ * usage text instead of silently continuing as an ordinary chat turn. The
11
+ * run goes through the bounded single-pass review runner against the frozen
12
+ * Git snapshot, and the receipt is rendered by the shared formatter.
13
+ */
14
+ declare function handleReview(ctx: CommandContext, args: string): Promise<CommandResult>;
15
+ export { handlePlan, handleDiff, handleCommitPlan, continueAsSlashChat, handleReview, };
8
16
  //# sourceMappingURL=workflow-command-handlers.d.ts.map
@@ -5,9 +5,13 @@ exports.handlePlan = handlePlan;
5
5
  exports.handleDiff = handleDiff;
6
6
  exports.handleCommitPlan = handleCommitPlan;
7
7
  exports.continueAsSlashChat = continueAsSlashChat;
8
+ exports.handleReview = handleReview;
8
9
  const workspace_diff_1 = require("../services/workspace-diff");
9
10
  const commit_plan_1 = require("../services/commit-plan");
10
11
  const agent_mode_1 = require("../framework/agent-mode");
12
+ const selector_1 = require("../services/code-review/selector");
13
+ const runner_1 = require("../services/code-review/runner");
14
+ const format_1 = require("../services/code-review/format");
11
15
  async function handlePlan(ctx, args) {
12
16
  const task = args.trim();
13
17
  const [subcommand = '', ...tail] = task.split(/\s+/);
@@ -92,4 +96,66 @@ function continueAsSlashChat(name, args) {
92
96
  chatInput: `/${name}${trimmed ? ` ${trimmed}` : ''}`,
93
97
  };
94
98
  }
99
+ /**
100
+ * v0.3.28 — the typed `/review` workflow (docs/plan/v0.3.28-plan.md S4).
101
+ * Arguments are parsed into a frozen selector; unrecognized input returns the
102
+ * usage text instead of silently continuing as an ordinary chat turn. The
103
+ * run goes through the bounded single-pass review runner against the frozen
104
+ * Git snapshot, and the receipt is rendered by the shared formatter.
105
+ */
106
+ async function handleReview(ctx, args) {
107
+ const parsed = (0, selector_1.parseReviewSelector)(args);
108
+ if (!parsed.ok)
109
+ return { success: false, error: parsed.usage };
110
+ if (!ctx.llm) {
111
+ return {
112
+ success: false,
113
+ error: 'Code review needs a configured model. Run /model first, then retry /review.',
114
+ };
115
+ }
116
+ const llm = ctx.llm;
117
+ const outcome = await (0, runner_1.runCodeReview)({
118
+ cwd: ctx.cwd,
119
+ selector: parsed.selector,
120
+ adapter: {
121
+ async complete(messages) {
122
+ const response = await llm.chat(messages.map(message => ({ role: message.role, content: message.content })));
123
+ return {
124
+ content: response.content,
125
+ model: response.model,
126
+ usage: {
127
+ inputTokens: response.usage?.promptTokens ?? 0,
128
+ outputTokens: response.usage?.completionTokens ?? 0,
129
+ },
130
+ };
131
+ },
132
+ },
133
+ identity: {
134
+ workspaceIdentity: ctx.cwd,
135
+ sessionId: ctx.sessionId ?? 'pending-session',
136
+ threadId: ctx.sessionId ?? 'pending-session',
137
+ turnId: String(ctx.turnId ?? 'ad-hoc'),
138
+ },
139
+ ...(ctx.abortSignal ? { abortSignal: ctx.abortSignal } : {}),
140
+ });
141
+ if (!outcome.ok) {
142
+ const failure = outcome.failure;
143
+ if (failure.kind === 'cancelled')
144
+ return { success: false, error: 'Review cancelled.' };
145
+ if (failure.kind === 'budget') {
146
+ return { success: false, error: `Review budget rejected: ${failure.field}` };
147
+ }
148
+ if (failure.kind === 'snapshot') {
149
+ return {
150
+ success: false,
151
+ error: `Review scope error (${failure.code}): ${failure.message}`,
152
+ };
153
+ }
154
+ if (failure.kind === 'model_error') {
155
+ return { success: false, error: `Review model call failed: ${failure.message}` };
156
+ }
157
+ return { success: false, error: `Review output could not be parsed: ${failure.message}` };
158
+ }
159
+ return { success: true, output: (0, format_1.formatReviewReport)(outcome.result) };
160
+ }
95
161
  //# sourceMappingURL=workflow-command-handlers.js.map
@@ -54,14 +54,14 @@ exports.WORKFLOW_COMMANDS = [
54
54
  },
55
55
  {
56
56
  name: 'review',
57
- description: 'Review the current change or requested files',
58
- argumentHint: '[scope]',
57
+ description: 'Run a bounded read-only code review of the current change or a base comparison',
58
+ argumentHint: '[all | --base <ref>] [--path <path>]',
59
59
  category: 'workflow',
60
60
  priority: 10,
61
- type: 'chat',
62
- execution: 'agent-workflow',
61
+ type: 'builtin',
62
+ execution: 'builtin',
63
63
  risk: 'read-only',
64
- execute: (_ctx, args) => (0, workflow_command_handlers_1.continueAsSlashChat)('review', args),
64
+ execute: (ctx, args) => (0, workflow_command_handlers_1.handleReview)(ctx, args),
65
65
  },
66
66
  {
67
67
  name: 'research',
@@ -0,0 +1,25 @@
1
+ /** Read-only tools the review turn may use. Only first-party core tools with
2
+ * `readOnly: true` in their descriptors qualify (plan §5.1); `edit_file`,
3
+ * `exec_command` and `write_file` are structurally excluded. */
4
+ export declare const REVIEW_ALLOWED_TOOLS: ReadonlySet<string>;
5
+ export interface ReviewWorkflowConstraintsV1 {
6
+ readonly schemaVersion: 1;
7
+ readonly workflow: 'code-review';
8
+ readonly allowedTools: readonly string[];
9
+ readonly constraintsDigest: string;
10
+ readonly policy: string;
11
+ }
12
+ /** The frozen constraint set with its digest. Receipts and capability
13
+ * compilations bind this digest so a policy change invalidates old reviews'
14
+ * rules provenance alongside the rules digest. */
15
+ export declare const REVIEW_WORKFLOW_CONSTRAINTS: ReviewWorkflowConstraintsV1;
16
+ export declare class ReviewToolPolicyError extends Error {
17
+ readonly code: 'review_tool_denied';
18
+ readonly toolName: string;
19
+ constructor(message: string, code: 'review_tool_denied', toolName: string);
20
+ }
21
+ /** Dispatch-level re-validation. Called on every tool invocation of a review
22
+ * turn — direct calls, batch calls, aliases and steering all funnel through
23
+ * here before the gateway executes anything. */
24
+ export declare function assertReviewToolAllowed(toolName: string): void;
25
+ //# sourceMappingURL=constraints.d.ts.map
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ReviewToolPolicyError = exports.REVIEW_WORKFLOW_CONSTRAINTS = exports.REVIEW_ALLOWED_TOOLS = void 0;
4
+ exports.assertReviewToolAllowed = assertReviewToolAllowed;
5
+ /**
6
+ * v0.3.27 PR1 — read-only workflow constraints for code review (plan §5.1).
7
+ *
8
+ * The review turn runs through the normal AgentLoop / ToolGateway, but under
9
+ * immutable workflow constraints: the model only sees bounded read/list/glob/
10
+ * grep capabilities, and every dispatch re-validates against this module so
11
+ * aliases, batch calls, steering or permission escalation (AUTO / ALLOW) can
12
+ * never widen the turn. The constraint set is deny-by-default: a tool that is
13
+ * not explicitly allowlisted is rejected, which covers long-tail tools, MCP,
14
+ * network, task derivation and future additions without maintaining a denylist.
15
+ *
16
+ * PR2 wires this guard into the review workflow's dispatch; PR1 ships the
17
+ * frozen model, its digest, and the negative tests.
18
+ */
19
+ const canonical_1 = require("../../runtime/protocol/canonical");
20
+ /** Read-only tools the review turn may use. Only first-party core tools with
21
+ * `readOnly: true` in their descriptors qualify (plan §5.1); `edit_file`,
22
+ * `exec_command` and `write_file` are structurally excluded. */
23
+ exports.REVIEW_ALLOWED_TOOLS = new Set([
24
+ 'read_file',
25
+ 'list_files',
26
+ 'glob',
27
+ 'grep',
28
+ ]);
29
+ const REVIEW_CONSTRAINT_CONTENT = Object.freeze({
30
+ schemaVersion: 1,
31
+ workflow: 'code-review',
32
+ mode: 'read-only',
33
+ allowedTools: [...exports.REVIEW_ALLOWED_TOOLS].sort(),
34
+ deniedByDefault: Object.freeze([
35
+ 'exec_command',
36
+ 'write_file',
37
+ 'edit_file',
38
+ 'git mutations',
39
+ 'mcp',
40
+ 'network tools',
41
+ 'dynamic skills',
42
+ 'task derivation',
43
+ 'settings mutation',
44
+ ]),
45
+ /** Rationale text is part of the digest: changing the policy changes every
46
+ * receipt that binds it. */
47
+ policy: 'The review turn may only inspect the frozen snapshot through the ' +
48
+ 'allowlisted read tools. Permission escalation (AUTO / ALLOW) may tighten ' +
49
+ 'or maintain but never widen this set. Test commands are not run: they ' +
50
+ 'can write caches and mutate the workspace under review.',
51
+ });
52
+ /** The frozen constraint set with its digest. Receipts and capability
53
+ * compilations bind this digest so a policy change invalidates old reviews'
54
+ * rules provenance alongside the rules digest. */
55
+ exports.REVIEW_WORKFLOW_CONSTRAINTS = Object.freeze({
56
+ schemaVersion: 1,
57
+ workflow: 'code-review',
58
+ allowedTools: REVIEW_CONSTRAINT_CONTENT.allowedTools,
59
+ constraintsDigest: (0, canonical_1.digestRuntimeValue)(REVIEW_CONSTRAINT_CONTENT),
60
+ policy: REVIEW_CONSTRAINT_CONTENT.policy,
61
+ });
62
+ class ReviewToolPolicyError extends Error {
63
+ constructor(message, code, toolName) {
64
+ super(message);
65
+ this.code = code;
66
+ this.toolName = toolName;
67
+ this.name = 'ReviewToolPolicyError';
68
+ }
69
+ }
70
+ exports.ReviewToolPolicyError = ReviewToolPolicyError;
71
+ /** Dispatch-level re-validation. Called on every tool invocation of a review
72
+ * turn — direct calls, batch calls, aliases and steering all funnel through
73
+ * here before the gateway executes anything. */
74
+ function assertReviewToolAllowed(toolName) {
75
+ if (!exports.REVIEW_ALLOWED_TOOLS.has(toolName)) {
76
+ throw new ReviewToolPolicyError(`Tool "${toolName}" is not available in a read-only code review turn.`, 'review_tool_denied', toolName);
77
+ }
78
+ }
79
+ //# sourceMappingURL=constraints.js.map
@@ -0,0 +1,172 @@
1
+ /** v0.3.27 §3.1 — what a review examines. Parsed once, frozen at admission. */
2
+ export type ReviewSelectorV1 = {
3
+ readonly kind: 'worktree';
4
+ readonly paths?: readonly string[];
5
+ } | {
6
+ readonly kind: 'base';
7
+ readonly ref: string;
8
+ readonly paths?: readonly string[];
9
+ };
10
+ /** v0.3.27 §4.1 — run states. `queued`/`running` never carry a conclusion. */
11
+ export type ReviewRunStateV1 = 'queued' | 'running' | 'completed' | 'partial' | 'failed' | 'cancelled';
12
+ /** v0.3.27 §4.1 — terminal conclusions. Absence can only be asserted when the
13
+ * state is `completed`, coverage is complete and the output contained no
14
+ * invalid findings; everything else maps to findings or inconclusive. */
15
+ export type ReviewConclusionV1 = 'findings' | 'no_findings' | 'inconclusive';
16
+ /** v0.3.27 §6.1 — a stored result's applicability after code or rules change.
17
+ * History is never rewritten; staleness is an annotation, not an edit. */
18
+ export type ReviewApplicabilityV1 = 'current' | 'stale';
19
+ /** Priority levels. P0 requires evidence that the defect blocks usage without
20
+ * special preconditions; the definition is frozen here and in PR0 tests. */
21
+ export type ReviewFindingPriorityV1 = 'P0' | 'P1' | 'P2' | 'P3';
22
+ /** v0.3.27 §4.1 — one review finding. `fileKey` is validated for shape here;
23
+ * membership against the frozen snapshot manifest happens at Host validation
24
+ * time (PR1+), never in the model's favor. */
25
+ export interface ReviewFindingV1 {
26
+ readonly findingId: string;
27
+ readonly priority: ReviewFindingPriorityV1;
28
+ readonly title: string;
29
+ /** The condition under which the defect manifests — not a style opinion. */
30
+ readonly trigger: string;
31
+ readonly impact: string;
32
+ readonly fileKey: string;
33
+ readonly side: 'old' | 'new';
34
+ readonly startLine: number;
35
+ readonly endLine: number;
36
+ readonly evidence: string;
37
+ }
38
+ /** Coverage is evidence, not a count: what was actually inspected, what was
39
+ * skipped and why, and where truncation or budget exhaustion occurred. */
40
+ export interface ReviewCoverageV1 {
41
+ readonly inspectedFiles: readonly string[];
42
+ readonly inspectedRanges: readonly {
43
+ readonly fileKey: string;
44
+ readonly startLine: number;
45
+ readonly endLine: number;
46
+ }[];
47
+ readonly skipped: readonly {
48
+ readonly fileKey: string;
49
+ readonly reason: 'over_budget_files' | 'over_budget_bytes' | 'binary' | 'sensitive' | 'unreadable' | 'changed_during_review' | 'other';
50
+ readonly detail?: string;
51
+ }[];
52
+ readonly truncated: boolean;
53
+ readonly budgetExhausted: readonly string[];
54
+ }
55
+ /** Whether the declared coverage is internally consistent and complete for the
56
+ * resolved selector — computed by the Host from the frozen manifest, never
57
+ * taken from the model's self-report (§4.1). */
58
+ export interface ReviewCoverageAssertionV1 {
59
+ readonly complete: boolean;
60
+ readonly reason?: string;
61
+ }
62
+ /** v0.3.27 §4.1 — the persisted receipt. `receiptDigest` covers the canonical
63
+ * form of everything above it and is recomputed on read. */
64
+ export interface CodeReviewResultV1 {
65
+ readonly schemaVersion: 1;
66
+ readonly reviewId: string;
67
+ readonly workspaceIdentity: string;
68
+ readonly sessionId: string;
69
+ readonly threadId: string;
70
+ readonly turnId: string;
71
+ readonly requestedSelector: ReviewSelectorV1;
72
+ /** The actually resolved target (commit OIDs, snapshot ids), not the request. */
73
+ readonly resolvedTarget: {
74
+ readonly kind: 'worktree' | 'base';
75
+ readonly baseOid?: string;
76
+ readonly headOid?: string;
77
+ readonly snapshotId: string;
78
+ };
79
+ /** Digest of the frozen input snapshot; a stored result is stale when this
80
+ * no longer matches the current snapshot digest for the same selector. */
81
+ readonly inputSnapshotDigest: string;
82
+ readonly rulesDigest: string;
83
+ readonly state: ReviewRunStateV1;
84
+ readonly conclusion: ReviewConclusionV1;
85
+ readonly findings: readonly ReviewFindingV1[];
86
+ /** Findings that failed validation — counted and surfaced, never silently
87
+ * dropped (dropping them must not turn the run into no_findings). */
88
+ readonly invalidFindingCount: number;
89
+ readonly coverage: ReviewCoverageV1;
90
+ readonly coverageAssertion: ReviewCoverageAssertionV1;
91
+ readonly limits: readonly string[];
92
+ readonly model: {
93
+ readonly provider: string;
94
+ readonly model: string;
95
+ };
96
+ readonly usage: {
97
+ readonly inputTokens: number;
98
+ readonly outputTokens: number;
99
+ };
100
+ readonly startedAt: number;
101
+ readonly endedAt: number;
102
+ readonly receiptDigest: string;
103
+ }
104
+ /** Budgets are ceilings that may only be tightened (§4.3): a provider with a
105
+ * smaller window takes the stricter value; nothing may widen them at runtime. */
106
+ export interface ReviewBudgetsV1 {
107
+ readonly maxFiles: number;
108
+ readonly maxBodyBytes: number;
109
+ readonly maxDiffBytes: number;
110
+ readonly maxReadToolCalls: number;
111
+ readonly maxDurationMs: number;
112
+ readonly maxFindings: number;
113
+ readonly maxResultBytes: number;
114
+ }
115
+ export declare const DEFAULT_REVIEW_BUDGETS: ReviewBudgetsV1;
116
+ /** Text size caps for finding fields, in characters. Chosen so 100 maximal
117
+ * findings stay well inside the 256 KiB result bound. */
118
+ export declare const FINDING_TEXT_LIMITS: Readonly<{
119
+ title: 200;
120
+ trigger: 2000;
121
+ impact: 2000;
122
+ evidence: 4000;
123
+ fileKey: 1024;
124
+ }>;
125
+ /** Validates one model-produced finding against the frozen shape. Returns the
126
+ * finding unchanged when valid; a typed reason when not. Membership against
127
+ * the snapshot manifest is a separate Host check (PR1+). */
128
+ export declare function validateReviewFinding(value: unknown): {
129
+ readonly valid: true;
130
+ readonly finding: ReviewFindingV1;
131
+ } | {
132
+ readonly valid: false;
133
+ readonly reason: 'not_an_object' | 'finding_id_invalid' | 'priority_invalid' | 'title_invalid' | 'trigger_invalid' | 'impact_invalid' | 'file_key_invalid' | 'side_invalid' | 'line_range_invalid' | 'evidence_invalid';
134
+ };
135
+ /**
136
+ * v0.3.27 §4.1 — the conclusion mapping, frozen as code so CLI and Web cannot
137
+ * diverge. The invariants:
138
+ * - `queued`/`running` results carry no conclusion (callers must not call this).
139
+ * - A state that is not `completed` can never conclude `no_findings`.
140
+ * - Invalid findings make the output untrustworthy: zero valid findings with
141
+ * invalid ones present is `inconclusive`, never a green pass.
142
+ * - Zero findings without complete coverage is `inconclusive`, never a green
143
+ * pass ("不静默截断后宣布全部完成").
144
+ */
145
+ export declare function resolveReviewConclusion(input: {
146
+ readonly state: ReviewRunStateV1;
147
+ readonly validFindingCount: number;
148
+ readonly invalidFindingCount: number;
149
+ readonly coverageComplete: boolean;
150
+ }): ReviewConclusionV1;
151
+ /** Applies the budgets with the tighten-only rule: every override must be a
152
+ * safe integer at or below the frozen default, otherwise the run is refused
153
+ * rather than silently widened. */
154
+ export declare function resolveReviewBudgets(overrides?: Partial<ReviewBudgetsV1>): {
155
+ readonly ok: true;
156
+ readonly budgets: ReviewBudgetsV1;
157
+ } | {
158
+ readonly ok: false;
159
+ readonly field: string;
160
+ };
161
+ /** Marks a stored result's applicability against the current input snapshot and
162
+ * rules digests. The receipt itself is never rewritten. */
163
+ export declare function reviewApplicability(result: Pick<CodeReviewResultV1, 'inputSnapshotDigest' | 'rulesDigest'>, current: {
164
+ readonly inputSnapshotDigest: string;
165
+ readonly rulesDigest: string;
166
+ }): ReviewApplicabilityV1;
167
+ /** The receipt digest covers the canonical form of the result with the digest
168
+ * field itself excluded, so verification is a recomputation, not a lookup. */
169
+ export declare function computeReceiptDigest(result: Omit<CodeReviewResultV1, 'receiptDigest'>): string;
170
+ /** Verifies a stored receipt by recomputing its digest. */
171
+ export declare function verifyReceiptDigest(result: CodeReviewResultV1): boolean;
172
+ //# sourceMappingURL=contract.d.ts.map