@orion-agents/orion-code 0.3.22 → 0.3.24
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 +50 -3
- package/README.md +6 -5
- package/README.zh-CN.md +5 -4
- package/dist/harness/failure-attribution.d.ts +23 -0
- package/dist/harness/failure-attribution.js +103 -0
- package/dist/harness/verified-repair.d.ts +71 -0
- package/dist/harness/verified-repair.js +212 -0
- package/dist/product/paths.d.ts +20 -1
- package/dist/product/paths.js +293 -10
- package/dist/runtime/release-receipts.d.ts +3 -3
- package/dist/runtime/release-receipts.js +29 -0
- package/dist/services/config-dir.d.ts +3 -2
- package/dist/services/config-dir.js +6 -29
- package/dist/services/repo-intelligence/index.d.ts +11 -0
- package/dist/services/repo-intelligence/index.js +20 -0
- package/dist/services/repo-intelligence/recall.d.ts +44 -0
- package/dist/services/repo-intelligence/recall.js +115 -0
- package/dist/services/repo-intelligence/symbol-index.d.ts +52 -0
- package/dist/services/repo-intelligence/symbol-index.js +258 -0
- package/dist/services/session-storage.d.ts +18 -2
- package/dist/services/session-storage.js +196 -60
- package/dist/web/protocol.d.ts +8 -0
- package/dist/web/workbench-controller.js +46 -2
- package/dist/web-client/assets/{FilesPanel-CKeXPofZ.js → FilesPanel-BjEDjE7l.js} +1 -1
- package/dist/web-client/assets/{GitPanel-DollPxv0.js → GitPanel-DvgAZDRA.js} +1 -1
- package/dist/web-client/assets/{ReviewPanel-CNADacIn.js → ReviewPanel-BMPaYKDY.js} +1 -1
- package/dist/web-client/assets/{TerminalPanel-Cyns4uJt.js → TerminalPanel-Da1ZEhGF.js} +1 -1
- package/dist/web-client/assets/index-DZOlUgt6.js +20 -0
- package/dist/web-client/assets/{useAutoNotice-BLADeFFj.js → useAutoNotice-Ylj5bfSH.js} +1 -1
- package/dist/web-client/index.html +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/dist/services/auto-fix/autoFixConfig.d.ts +0 -30
- package/dist/services/auto-fix/autoFixConfig.js +0 -78
- package/dist/services/auto-fix/autoFixHook.d.ts +0 -27
- package/dist/services/auto-fix/autoFixHook.js +0 -99
- package/dist/services/auto-fix/autoFixRunner.d.ts +0 -60
- package/dist/services/auto-fix/autoFixRunner.js +0 -207
- package/dist/services/auto-fix/index.d.ts +0 -7
- package/dist/services/auto-fix/index.js +0 -17
- package/dist/web-client/assets/index-BhGhORAX.js +0 -20
package/CHANGELOG.md
CHANGED
|
@@ -22,12 +22,59 @@ which is **not** a pass.
|
|
|
22
22
|
|
|
23
23
|
## [Unreleased]
|
|
24
24
|
|
|
25
|
-
## [0.3.
|
|
25
|
+
## [0.3.24]
|
|
26
26
|
|
|
27
|
-
> **Status: candidate.**
|
|
27
|
+
> **Status: candidate.** Implementation on `codex/v0.3.24`, based on `main@6ed47fd`.
|
|
28
|
+
> Plan: `docs/plan/v0.3.24-plan.md`. Registry and release status must be
|
|
29
|
+
> verified from their own immutable receipts, not inferred from this changelog.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- Session command and Composer requests track only their originating Session;
|
|
34
|
+
sending no longer blocks another Session's Composer or session creation.
|
|
35
|
+
- Same-Context snapshots across a live event stream are reused when unchanged;
|
|
36
|
+
warm selection does not re-fetch or disable input. Background changes and
|
|
37
|
+
disconnections still require synchronization before writes.
|
|
38
|
+
- Foreground display caches retain streamed messages, tool cards and loaded
|
|
39
|
+
history instead of restoring an older HTTP tail when switching back.
|
|
40
|
+
- Creation is single-flight and respects a later foreground selection. Late
|
|
41
|
+
send responses do not clear, focus or replace another Session's draft.
|
|
42
|
+
- Web creation bounds asynchronous Git branch probes; project storage identity
|
|
43
|
+
resolution reuses an invalidatable bounded cache instead of repeatedly
|
|
44
|
+
spawning synchronous Git on every storage helper call.
|
|
45
|
+
- Cross-Workspace navigation remains usable while another Session is running;
|
|
46
|
+
the most recent navigation intent wins when Context activation is in flight.
|
|
47
|
+
- Per-Session scroll position and loaded history survive navigation, while
|
|
48
|
+
foreground Snapshot and SSE reconciliation no longer skips background events.
|
|
49
|
+
- Web Session creation waits for file locks without freezing the Host event
|
|
50
|
+
loop, and cold Catalog scans yield to unrelated HTTP and SSE traffic.
|
|
51
|
+
|
|
52
|
+
## [0.3.23] — UNRELEASED
|
|
53
|
+
|
|
54
|
+
> **Status: candidate.** Open. Coding Intelligence & Verified Execution — wires the live
|
|
55
|
+
> coding-task evaluation to the production runtime (isolated workspaces, machine-verified
|
|
56
|
+
> success, five-outcome classification), adds repo-intelligence symbol indexing and
|
|
57
|
+
> recall, and consolidates the bounded verification/repair loop into the HarnessKernel
|
|
58
|
+
> completion audit. Plan: `docs/plan/v0.3.23-plan.md`. Not merged, tagged or published.
|
|
59
|
+
|
|
60
|
+
### Added (planned)
|
|
61
|
+
|
|
62
|
+
- Live Coding Eval: `LiveTaskExecutor` + `compare-receipts` A/B reporting; five-outcome
|
|
63
|
+
classification with orthogonal falseComplete; `command_exit_zero` / `invariant` checks.
|
|
64
|
+
- Repo Intelligence: lightweight TS/JS symbol index with incremental maintenance and
|
|
65
|
+
sourced recall (Recall@K evaluated).
|
|
66
|
+
- Verified Repair: structured failure attribution, bounded repair loop (feature-flagged),
|
|
67
|
+
five-state completion audit; the orphaned auto-fix / verification-profile modules merge
|
|
68
|
+
into the wired stack and are removed.
|
|
69
|
+
|
|
70
|
+
## [0.3.22] — 2026-10-09
|
|
71
|
+
|
|
72
|
+
> **Status: published.** Released as `@orion-agents/orion-code@0.3.22` from
|
|
73
|
+
> `refs/tags/v0.3.22`, and merged to `main` as `9f09e0a` (#265). Hardens ToolGateway
|
|
28
74
|
> in-flight request identity (T22-01), adds the durability fault-injection matrix (T22-02),
|
|
29
75
|
> the four-state release verification gate (T22-03), and a real coding-task evaluation
|
|
30
|
-
> harness (T22-04). Plan: `docs/plan/v0.3.22-plan.md`.
|
|
76
|
+
> harness (T22-04). Plan: `docs/plan/v0.3.22-plan.md`. GitHub Release:
|
|
77
|
+
> https://github.com/orion-agents/orion-code/releases/tag/v0.3.22。
|
|
31
78
|
|
|
32
79
|
### Fixed (product)
|
|
33
80
|
|
package/README.md
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Local-first, goal-driven coding agent for the terminal and browser.
|
|
4
4
|
|
|
5
|
-
> v0.3.
|
|
6
|
-
>
|
|
7
|
-
>
|
|
5
|
+
> v0.3.24 development focus — Web session responsiveness: Session-scoped request admission,
|
|
6
|
+
> cached foreground switching, retained live views, and bounded Git probes during creation.
|
|
7
|
+
> Built on the merged v0.3.23 source (`6ed47fd`, #266).
|
|
8
8
|
|
|
9
9
|
[中文说明](README.zh-CN.md) ·
|
|
10
|
+
[v0.3.24 plan](docs/plan/v0.3.24-plan.md) ·
|
|
10
11
|
[v0.3.7 plan](docs/plan/v0.3.7-left-rail-improvement-plan.md) ·
|
|
11
12
|
[v0.3.6 plan](docs/plan/v0.3.6-plan.md) ·
|
|
12
13
|
[v0.3.5 plan](docs/plan/v0.3.5-plan.md) ·
|
|
@@ -166,10 +167,10 @@ user extension boundaries.
|
|
|
166
167
|
Node.js 22.12+, 24, and 26 are supported. Use Node 24 LTS for production or Node 26 Current for
|
|
167
168
|
current development environments. Node 20 is upstream EOL and is no longer a v0.3 runtime.
|
|
168
169
|
|
|
169
|
-
|
|
170
|
+
To install the pinned v0.3.24 package when it is available on npm:
|
|
170
171
|
|
|
171
172
|
```bash
|
|
172
|
-
npm install -g @orion-agents/orion-code@0.3.
|
|
173
|
+
npm install -g @orion-agents/orion-code@0.3.24
|
|
173
174
|
orion --version
|
|
174
175
|
orion doctor
|
|
175
176
|
```
|
package/README.zh-CN.md
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
面向终端与浏览器、本地优先的目标驱动 Coding Agent。
|
|
4
4
|
|
|
5
|
-
> v0.3.
|
|
6
|
-
>
|
|
5
|
+
> v0.3.24 开发重点:Web 会话响应性。发送按会话等待、有效缓存即时切换、保留实时显示内容,
|
|
6
|
+
> 新建使用有界 Git 探测。基于已合并的 v0.3.23 源码(`6ed47fd`,#266)。
|
|
7
7
|
|
|
8
8
|
[English](README.md) ·
|
|
9
|
+
[v0.3.24 方案](docs/plan/v0.3.24-plan.md) ·
|
|
9
10
|
[v0.3.7 方案](docs/plan/v0.3.7-left-rail-improvement-plan.md) ·
|
|
10
11
|
[v0.3.6 方案](docs/plan/v0.3.6-plan.md) ·
|
|
11
12
|
[v0.3.4 稳定化方案](docs/plan/v0.3.4-stabilization-plan.md) ·
|
|
@@ -97,10 +98,10 @@ MCP 仍是用户可见的扩展边界。
|
|
|
97
98
|
支持 Node.js 22.12+、24 和 26。生产环境建议使用 Node 24 LTS,当前开发环境也支持 Node 26
|
|
98
99
|
Current。Node 20 已结束上游维护,不再属于 v0.3 Runtime 合同。
|
|
99
100
|
|
|
100
|
-
|
|
101
|
+
v0.3.24 在 npm 可用后,可安装固定版本:
|
|
101
102
|
|
|
102
103
|
```bash
|
|
103
|
-
npm install -g @orion-agents/orion-code@0.3.
|
|
104
|
+
npm install -g @orion-agents/orion-code@0.3.24
|
|
104
105
|
orion --version
|
|
105
106
|
orion doctor
|
|
106
107
|
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type FailureKind = 'typescript' | 'lint' | 'build' | 'unit-test' | 'integration-test' | 'environment' | 'provider' | 'unknown';
|
|
2
|
+
export interface FailureEntryV1 {
|
|
3
|
+
readonly file?: string;
|
|
4
|
+
readonly line?: number;
|
|
5
|
+
readonly code?: string;
|
|
6
|
+
readonly message: string;
|
|
7
|
+
}
|
|
8
|
+
export interface StructuredFailureEvidenceV1 {
|
|
9
|
+
readonly kind: FailureKind;
|
|
10
|
+
readonly summary: string;
|
|
11
|
+
readonly entries: readonly FailureEntryV1[];
|
|
12
|
+
/** Stable over identical failures; changes when the failure changes. */
|
|
13
|
+
readonly digest: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function parseTypeScriptDiagnostics(output: string): readonly FailureEntryV1[];
|
|
16
|
+
export declare function parseEsLintOutput(output: string): readonly FailureEntryV1[];
|
|
17
|
+
export declare function parseJestFailures(output: string): readonly FailureEntryV1[];
|
|
18
|
+
/**
|
|
19
|
+
* Attribute a verification failure to a structured kind. Detection order:
|
|
20
|
+
* TypeScript → test failure → lint → build → provider → environment → unknown.
|
|
21
|
+
*/
|
|
22
|
+
export declare function attributeFailure(output: string, command?: string): StructuredFailureEvidenceV1;
|
|
23
|
+
//# sourceMappingURL=failure-attribution.d.ts.map
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseTypeScriptDiagnostics = parseTypeScriptDiagnostics;
|
|
4
|
+
exports.parseEsLintOutput = parseEsLintOutput;
|
|
5
|
+
exports.parseJestFailures = parseJestFailures;
|
|
6
|
+
exports.attributeFailure = attributeFailure;
|
|
7
|
+
/**
|
|
8
|
+
* v0.3.23 T23-C2 — structured failure attribution.
|
|
9
|
+
*
|
|
10
|
+
* Parses verification output (tsc, jest, eslint, build tooling, environment
|
|
11
|
+
* errors) into bounded structured evidence instead of feeding raw logs back to
|
|
12
|
+
* the model. Each attribution carries a stable digest so the repair loop can
|
|
13
|
+
* detect no-progress rounds objectively.
|
|
14
|
+
*/
|
|
15
|
+
const crypto_1 = require("crypto");
|
|
16
|
+
const TSC_PATTERN = /^(.+?)\((\d+),(\d+)\):\s+(?:error|warning)\s+(TS\d+):\s+(.+)$/gm;
|
|
17
|
+
const ESLINT_PATTERN = /^(.+?):(\d+):(\d+)\s+(error|warning)\s+(.+)$/gm;
|
|
18
|
+
const JEST_FAIL_PATTERN = /✕\s+(.+?)(?:\s*\(\d+\s*m?s\))?$/gm;
|
|
19
|
+
function digestOf(kind, entries) {
|
|
20
|
+
const hash = (0, crypto_1.createHash)('sha256');
|
|
21
|
+
hash.update(kind);
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
hash.update(`${entry.file ?? ''}|${entry.line ?? ''}|${entry.code ?? ''}|${entry.message}`);
|
|
24
|
+
}
|
|
25
|
+
return hash.digest('hex').slice(0, 32);
|
|
26
|
+
}
|
|
27
|
+
function evidence(kind, entries, summary) {
|
|
28
|
+
return {
|
|
29
|
+
kind,
|
|
30
|
+
summary,
|
|
31
|
+
entries: Object.freeze(entries),
|
|
32
|
+
digest: digestOf(kind, entries),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function parseTypeScriptDiagnostics(output) {
|
|
36
|
+
const entries = [];
|
|
37
|
+
for (const match of output.matchAll(TSC_PATTERN)) {
|
|
38
|
+
entries.push({
|
|
39
|
+
file: match[1],
|
|
40
|
+
line: Number(match[2]),
|
|
41
|
+
code: match[4],
|
|
42
|
+
message: match[5].trim(),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return entries;
|
|
46
|
+
}
|
|
47
|
+
function parseEsLintOutput(output) {
|
|
48
|
+
const entries = [];
|
|
49
|
+
for (const match of output.matchAll(ESLINT_PATTERN)) {
|
|
50
|
+
entries.push({
|
|
51
|
+
file: match[1],
|
|
52
|
+
line: Number(match[2]),
|
|
53
|
+
code: match[4],
|
|
54
|
+
message: match[5].trim(),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return entries;
|
|
58
|
+
}
|
|
59
|
+
function parseJestFailures(output) {
|
|
60
|
+
const entries = [];
|
|
61
|
+
for (const match of output.matchAll(JEST_FAIL_PATTERN)) {
|
|
62
|
+
entries.push({ message: match[1].trim() });
|
|
63
|
+
}
|
|
64
|
+
const summary = /Tests:\s+(\d+)\s+failed/.exec(output);
|
|
65
|
+
if (summary && entries.length === 0) {
|
|
66
|
+
entries.push({ message: `${summary[1]} test(s) failed` });
|
|
67
|
+
}
|
|
68
|
+
return entries;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Attribute a verification failure to a structured kind. Detection order:
|
|
72
|
+
* TypeScript → test failure → lint → build → provider → environment → unknown.
|
|
73
|
+
*/
|
|
74
|
+
function attributeFailure(output, command = '') {
|
|
75
|
+
const commandLower = command.toLowerCase();
|
|
76
|
+
const tsEntries = parseTypeScriptDiagnostics(output);
|
|
77
|
+
if (tsEntries.length > 0) {
|
|
78
|
+
return evidence('typescript', tsEntries.slice(0, 50), `${tsEntries.length} TypeScript diagnostic(s); first: ${tsEntries[0].file}:${tsEntries[0].line} ${tsEntries[0].code}`);
|
|
79
|
+
}
|
|
80
|
+
if (/✕|●.*›|Tests:\s+\d+\s+failed|FAIL\s+\S+test/i.test(output)) {
|
|
81
|
+
const jestEntries = parseJestFailures(output);
|
|
82
|
+
const kind = /integrat|e2e|playwright/i.test(output) || /e2e/i.test(commandLower)
|
|
83
|
+
? 'integration-test'
|
|
84
|
+
: 'unit-test';
|
|
85
|
+
return evidence(kind, jestEntries.slice(0, 50), `${jestEntries.length} failed test(s)`);
|
|
86
|
+
}
|
|
87
|
+
const lintEntries = parseEsLintOutput(output);
|
|
88
|
+
if (lintEntries.length > 0 || /eslint/i.test(commandLower)) {
|
|
89
|
+
return evidence('lint', lintEntries.slice(0, 50), `${lintEntries.length} lint problem(s)`);
|
|
90
|
+
}
|
|
91
|
+
if (/build failed|rollup|webpack|vite build|tsc && |error during build/i.test(output)) {
|
|
92
|
+
return evidence('build', [], 'build tooling reported a failure');
|
|
93
|
+
}
|
|
94
|
+
if (/provider|api key|rate limit|429|status 5|econnrefused|enotfound|fetch failed|timed out/i.test(output) ||
|
|
95
|
+
/provider/i.test(commandLower)) {
|
|
96
|
+
return evidence('provider', [], 'provider or network failure during verification');
|
|
97
|
+
}
|
|
98
|
+
if (/eacces|eperm|enoent|enoentries|no such file|permission denied|disk space|enospc/i.test(output)) {
|
|
99
|
+
return evidence('environment', [], 'environment or filesystem failure during verification');
|
|
100
|
+
}
|
|
101
|
+
return evidence('unknown', [], 'verification failed without a recognizable pattern');
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=failure-attribution.js.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { StructuredFailureEvidenceV1 } from './failure-attribution';
|
|
2
|
+
export interface VerificationRunV1 {
|
|
3
|
+
readonly ok: boolean;
|
|
4
|
+
readonly output: string;
|
|
5
|
+
readonly command: string;
|
|
6
|
+
readonly durationMs: number;
|
|
7
|
+
}
|
|
8
|
+
export interface RepairGuardSnapshotV1 {
|
|
9
|
+
/** Workspace-relative paths of test files present BEFORE the repair. */
|
|
10
|
+
readonly testFiles: readonly string[];
|
|
11
|
+
/** Path → count of assertion tokens (`expect(`) per test file. */
|
|
12
|
+
readonly assertionTokens: Readonly<Record<string, number>>;
|
|
13
|
+
/** Path → digest of acceptance/verification criteria files. */
|
|
14
|
+
readonly acceptanceDigests: Readonly<Record<string, string>>;
|
|
15
|
+
}
|
|
16
|
+
export declare class ForbiddenRepairError extends Error {
|
|
17
|
+
readonly guard: 'test_deleted' | 'assertion_weakened' | 'acceptance_modified';
|
|
18
|
+
constructor(message: string, guard: 'test_deleted' | 'assertion_weakened' | 'acceptance_modified');
|
|
19
|
+
}
|
|
20
|
+
export declare function snapshotTestFiles(workspace: string, testFilePaths: readonly string[]): RepairGuardSnapshotV1;
|
|
21
|
+
/**
|
|
22
|
+
* C4 — forbidden repair detection. Throws ForbiddenRepairError when the
|
|
23
|
+
* after-snapshot shows a test deleted, assertions materially weakened
|
|
24
|
+
* (>20% fewer `expect(` calls in a file), or an acceptance file modified.
|
|
25
|
+
*/
|
|
26
|
+
export declare function assertNoForbiddenRepair(before: RepairGuardSnapshotV1, after: RepairGuardSnapshotV1): void;
|
|
27
|
+
export type VerifiedRepairTerminal = 'completed' | 'failed' | 'blocked' | 'cancelled' | 'unverified';
|
|
28
|
+
export interface RepairRoundV1 {
|
|
29
|
+
readonly attempt: number;
|
|
30
|
+
readonly verification: VerificationRunV1;
|
|
31
|
+
readonly attribution: StructuredFailureEvidenceV1;
|
|
32
|
+
readonly repairRan: boolean;
|
|
33
|
+
readonly repairError?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface VerifiedRepairOutcomeV1 {
|
|
36
|
+
readonly terminal: VerifiedRepairTerminal;
|
|
37
|
+
readonly attempts: number;
|
|
38
|
+
readonly rounds: readonly RepairRoundV1[];
|
|
39
|
+
readonly noProgress: boolean;
|
|
40
|
+
readonly failureReason?: string;
|
|
41
|
+
readonly lastAttribution?: StructuredFailureEvidenceV1;
|
|
42
|
+
}
|
|
43
|
+
export interface VerifiedRepairOptionsV1 {
|
|
44
|
+
/** Runs the (change-aware) verification command. */
|
|
45
|
+
readonly verify: () => Promise<VerificationRunV1>;
|
|
46
|
+
/** Performs the bounded repair for the given attribution. */
|
|
47
|
+
readonly repair: (attribution: StructuredFailureEvidenceV1, attempt: number) => Promise<void>;
|
|
48
|
+
/** Compares guard snapshots after each repair; throws on forbidden repairs. */
|
|
49
|
+
readonly guard?: (previous: RepairGuardSnapshotV1 | undefined) => Promise<RepairGuardSnapshotV1> | RepairGuardSnapshotV1;
|
|
50
|
+
readonly maxAttempts?: number;
|
|
51
|
+
readonly noProgressThreshold?: number;
|
|
52
|
+
readonly signal?: AbortSignal;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The bounded verified-repair loop (C3). The first verification is the
|
|
56
|
+
* change-aware check; each failed round attributes the failure structurally
|
|
57
|
+
* (C2), runs one bounded repair, re-verifies, and evaluates progress.
|
|
58
|
+
*/
|
|
59
|
+
export declare function runVerifiedRepairLoop(options: VerifiedRepairOptionsV1): Promise<VerifiedRepairOutcomeV1>;
|
|
60
|
+
/**
|
|
61
|
+
* C5 — five-state completion mapping. A turn may only be `completed` with
|
|
62
|
+
* trusted verification evidence; no evidence maps to `unverified`.
|
|
63
|
+
*/
|
|
64
|
+
export declare function completionStateFromVerification(state: {
|
|
65
|
+
readonly cancelled: boolean;
|
|
66
|
+
readonly blockedReason?: string;
|
|
67
|
+
readonly verificationRan: boolean;
|
|
68
|
+
readonly verificationOk: boolean;
|
|
69
|
+
readonly agentClaimedSuccess: boolean;
|
|
70
|
+
}): VerifiedRepairTerminal;
|
|
71
|
+
//# sourceMappingURL=verified-repair.d.ts.map
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ForbiddenRepairError = void 0;
|
|
4
|
+
exports.snapshotTestFiles = snapshotTestFiles;
|
|
5
|
+
exports.assertNoForbiddenRepair = assertNoForbiddenRepair;
|
|
6
|
+
exports.runVerifiedRepairLoop = runVerifiedRepairLoop;
|
|
7
|
+
exports.completionStateFromVerification = completionStateFromVerification;
|
|
8
|
+
/**
|
|
9
|
+
* v0.3.23 T23-C3/C4/C5 — bounded verified-repair loop and the five-state
|
|
10
|
+
* completion audit.
|
|
11
|
+
*
|
|
12
|
+
* The loop alternates `verify` → structured attribution → caller-supplied
|
|
13
|
+
* `repair` with hard bounds: `maxAttempts` (default 3, from the archived
|
|
14
|
+
* AutoFixConfig semantics), an objective no-progress detector (identical
|
|
15
|
+
* attribution digests on consecutive rounds), a wall-clock/cancel signal, and
|
|
16
|
+
* an anti-cheat guard that maps forbidden repair behaviors (deleting tests,
|
|
17
|
+
* weakening assertions, touching acceptance criteria) to a hard `failed`.
|
|
18
|
+
*
|
|
19
|
+
* Completion states: completed / failed / blocked / cancelled / unverified.
|
|
20
|
+
* `unverified` is used when no trusted verification ran and nothing else
|
|
21
|
+
* explains the stop — a task can never be marked completed without
|
|
22
|
+
* verification evidence.
|
|
23
|
+
*/
|
|
24
|
+
const crypto_1 = require("crypto");
|
|
25
|
+
const failure_attribution_1 = require("./failure-attribution");
|
|
26
|
+
class ForbiddenRepairError extends Error {
|
|
27
|
+
constructor(message, guard) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.guard = guard;
|
|
30
|
+
this.name = 'ForbiddenRepairError';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.ForbiddenRepairError = ForbiddenRepairError;
|
|
34
|
+
const hash = (value) => (0, crypto_1.createHash)('sha256').update(value).digest('hex').slice(0, 32);
|
|
35
|
+
function snapshotTestFiles(workspace, testFilePaths) {
|
|
36
|
+
const { existsSync, readFileSync } = require('fs');
|
|
37
|
+
const assertionTokens = {};
|
|
38
|
+
const acceptanceDigests = {};
|
|
39
|
+
for (const relative of testFilePaths) {
|
|
40
|
+
const target = joinWorkspace(workspace, relative);
|
|
41
|
+
if (!existsSync(target))
|
|
42
|
+
continue;
|
|
43
|
+
const content = readFileSync(target, 'utf8');
|
|
44
|
+
assertionTokens[relative] = (content.match(/expect\s*\(/g) ?? []).length;
|
|
45
|
+
acceptanceDigests[relative] = hash(content);
|
|
46
|
+
}
|
|
47
|
+
return { testFiles: testFilePaths, assertionTokens, acceptanceDigests };
|
|
48
|
+
}
|
|
49
|
+
function joinWorkspace(workspace, relative) {
|
|
50
|
+
return `${workspace.replace(/[\\/]+$/, '')}/${relative}`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* C4 — forbidden repair detection. Throws ForbiddenRepairError when the
|
|
54
|
+
* after-snapshot shows a test deleted, assertions materially weakened
|
|
55
|
+
* (>20% fewer `expect(` calls in a file), or an acceptance file modified.
|
|
56
|
+
*/
|
|
57
|
+
function assertNoForbiddenRepair(before, after) {
|
|
58
|
+
for (const testFile of before.testFiles) {
|
|
59
|
+
const beforeTokens = before.assertionTokens[testFile] ?? 0;
|
|
60
|
+
const afterExists = after.testFiles.includes(testFile) && after.assertionTokens[testFile] !== undefined;
|
|
61
|
+
if (!afterExists) {
|
|
62
|
+
throw new ForbiddenRepairError(`a test file was deleted during repair: ${testFile}`, 'test_deleted');
|
|
63
|
+
}
|
|
64
|
+
const afterTokens = after.assertionTokens[testFile] ?? 0;
|
|
65
|
+
if (beforeTokens > 0 && afterTokens < Math.ceil(beforeTokens * 0.8)) {
|
|
66
|
+
throw new ForbiddenRepairError(`assertions were weakened during repair: ${testFile} (${beforeTokens} → ${afterTokens} expect calls)`, 'assertion_weakened');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
for (const [file, beforeDigest] of Object.entries(before.acceptanceDigests)) {
|
|
70
|
+
const afterDigest = after.acceptanceDigests[file];
|
|
71
|
+
if (afterDigest !== undefined && afterDigest !== beforeDigest) {
|
|
72
|
+
throw new ForbiddenRepairError(`acceptance criteria were modified during repair: ${file}`, 'acceptance_modified');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The bounded verified-repair loop (C3). The first verification is the
|
|
78
|
+
* change-aware check; each failed round attributes the failure structurally
|
|
79
|
+
* (C2), runs one bounded repair, re-verifies, and evaluates progress.
|
|
80
|
+
*/
|
|
81
|
+
async function runVerifiedRepairLoop(options) {
|
|
82
|
+
const maxAttempts = options.maxAttempts ?? 3;
|
|
83
|
+
const noProgressThreshold = options.noProgressThreshold ?? 2;
|
|
84
|
+
const rounds = [];
|
|
85
|
+
const seenDigests = [];
|
|
86
|
+
let previousGuard;
|
|
87
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
88
|
+
if (options.signal?.aborted) {
|
|
89
|
+
return {
|
|
90
|
+
terminal: 'cancelled',
|
|
91
|
+
attempts: attempt - 1,
|
|
92
|
+
rounds,
|
|
93
|
+
noProgress: false,
|
|
94
|
+
failureReason: 'cancelled before verification',
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
let verification;
|
|
98
|
+
try {
|
|
99
|
+
verification = await options.verify();
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
return {
|
|
103
|
+
terminal: 'blocked',
|
|
104
|
+
attempts: attempt - 1,
|
|
105
|
+
rounds,
|
|
106
|
+
noProgress: false,
|
|
107
|
+
failureReason: `verification infrastructure failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const attribution = (0, failure_attribution_1.attributeFailure)(verification.output, verification.command);
|
|
111
|
+
rounds.push({
|
|
112
|
+
attempt,
|
|
113
|
+
verification,
|
|
114
|
+
attribution,
|
|
115
|
+
repairRan: false,
|
|
116
|
+
});
|
|
117
|
+
if (verification.ok) {
|
|
118
|
+
return {
|
|
119
|
+
terminal: 'completed',
|
|
120
|
+
attempts: attempt,
|
|
121
|
+
rounds,
|
|
122
|
+
noProgress: false,
|
|
123
|
+
lastAttribution: attribution,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// Guard FIRST (before no-progress): a forbidden repair from the previous
|
|
127
|
+
// round must surface as failed, never as a soft no-progress block.
|
|
128
|
+
try {
|
|
129
|
+
previousGuard = options.guard ? await options.guard(previousGuard) : undefined;
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
if (error instanceof ForbiddenRepairError) {
|
|
133
|
+
return {
|
|
134
|
+
terminal: 'failed',
|
|
135
|
+
attempts: attempt,
|
|
136
|
+
rounds,
|
|
137
|
+
noProgress: false,
|
|
138
|
+
failureReason: `forbidden repair: ${error.message}`,
|
|
139
|
+
lastAttribution: attribution,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
terminal: 'blocked',
|
|
144
|
+
attempts: attempt,
|
|
145
|
+
rounds,
|
|
146
|
+
noProgress: false,
|
|
147
|
+
failureReason: `repair guard failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
148
|
+
lastAttribution: attribution,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
// No-progress detection: identical failure digests on consecutive rounds.
|
|
152
|
+
if (seenDigests.length >= noProgressThreshold) {
|
|
153
|
+
seenDigests.shift();
|
|
154
|
+
}
|
|
155
|
+
seenDigests.push(attribution.digest);
|
|
156
|
+
const stuck = seenDigests.length >= noProgressThreshold &&
|
|
157
|
+
seenDigests.every(digest => digest === seenDigests[0]);
|
|
158
|
+
if (stuck || attempt === maxAttempts) {
|
|
159
|
+
return {
|
|
160
|
+
terminal: stuck ? 'blocked' : 'failed',
|
|
161
|
+
attempts: attempt,
|
|
162
|
+
rounds,
|
|
163
|
+
noProgress: stuck,
|
|
164
|
+
failureReason: stuck
|
|
165
|
+
? `no progress: the same failure persisted across ${seenDigests.length} rounds`
|
|
166
|
+
: `verification still failing after ${maxAttempts} attempt(s)`,
|
|
167
|
+
lastAttribution: attribution,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
await options.repair(attribution, attempt);
|
|
172
|
+
rounds[rounds.length - 1] = { ...rounds[rounds.length - 1], repairRan: true };
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
rounds[rounds.length - 1] = {
|
|
176
|
+
...rounds[rounds.length - 1],
|
|
177
|
+
repairRan: true,
|
|
178
|
+
repairError: error instanceof Error ? error.message : String(error),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (options.signal?.aborted) {
|
|
182
|
+
return {
|
|
183
|
+
terminal: 'cancelled',
|
|
184
|
+
attempts: attempt,
|
|
185
|
+
rounds,
|
|
186
|
+
noProgress: false,
|
|
187
|
+
failureReason: 'cancelled during repair',
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
terminal: 'failed',
|
|
193
|
+
attempts: maxAttempts,
|
|
194
|
+
rounds,
|
|
195
|
+
noProgress: false,
|
|
196
|
+
failureReason: `verification still failing after ${maxAttempts} attempt(s)`,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* C5 — five-state completion mapping. A turn may only be `completed` with
|
|
201
|
+
* trusted verification evidence; no evidence maps to `unverified`.
|
|
202
|
+
*/
|
|
203
|
+
function completionStateFromVerification(state) {
|
|
204
|
+
if (state.cancelled)
|
|
205
|
+
return 'cancelled';
|
|
206
|
+
if (state.blockedReason)
|
|
207
|
+
return 'blocked';
|
|
208
|
+
if (!state.verificationRan)
|
|
209
|
+
return 'unverified';
|
|
210
|
+
return state.verificationOk ? 'completed' : 'failed';
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=verified-repair.js.map
|
package/dist/product/paths.d.ts
CHANGED
|
@@ -29,8 +29,25 @@ export declare function getProjectsDir(): string;
|
|
|
29
29
|
export declare function getGlobalEnvPath(): string;
|
|
30
30
|
/** Encode an absolute project path into a stable directory key. */
|
|
31
31
|
export declare function encodeProjectPath(projectPath: string): string;
|
|
32
|
-
/**
|
|
32
|
+
/** Interactive Hosts must not persist an unverified Git project identity. */
|
|
33
|
+
export declare class ProjectStorageIdentityUnavailableError extends Error {
|
|
34
|
+
readonly code = "project_storage_identity_unavailable";
|
|
35
|
+
constructor(message?: string);
|
|
36
|
+
}
|
|
37
|
+
/** An operation-local verified identity; never a permanent external-env cache. */
|
|
38
|
+
export interface ProjectStorageIdentity {
|
|
39
|
+
readonly requestedPath: string;
|
|
40
|
+
readonly canonicalPath: string;
|
|
41
|
+
readonly projectKey: string;
|
|
42
|
+
readonly fingerprint: string;
|
|
43
|
+
}
|
|
44
|
+
export declare function resolveProjectStorageIdentityAsync(projectPath: string): Promise<ProjectStorageIdentity>;
|
|
45
|
+
/** Cheap lock-admission validation: filesystem/environment only, no Git spawn. */
|
|
46
|
+
export declare function assertProjectStorageIdentity(identity: ProjectStorageIdentity): void;
|
|
47
|
+
/** Resolve a project path for storage identity with bounded, invalidatable reuse. */
|
|
33
48
|
export declare function resolveProjectStoragePath(projectPath: string): string;
|
|
49
|
+
/** Pre-resolve interactive Host identities without blocking its event loop. */
|
|
50
|
+
export declare function resolveProjectStoragePathAsync(projectPath: string): Promise<string>;
|
|
34
51
|
export declare function getCanonicalProjectKey(projectPath: string): string;
|
|
35
52
|
export declare function getProjectDir(projectPath: string): string;
|
|
36
53
|
export declare function getProjectMetadataPath(projectPath: string): string;
|
|
@@ -59,6 +76,8 @@ export declare function getMemoryPath(type: MemoryType, cwd?: string): string;
|
|
|
59
76
|
export declare function getExistingMemoryPaths(cwd?: string): string[];
|
|
60
77
|
export declare function ensureConfigDir(): void;
|
|
61
78
|
export declare function ensureProjectDir(projectPath: string): void;
|
|
79
|
+
/** Use the already-verified identity; do not re-probe Git under explicit env. */
|
|
80
|
+
export declare function ensureResolvedProjectDir(identity: ProjectStorageIdentity): string;
|
|
62
81
|
/** Ordered project config candidate files (relative to project root). */
|
|
63
82
|
export declare const PROJECT_CONFIG_CANDIDATES: readonly [".orion-code.yaml", ".orion-code.yml", ".orion-code.json", "orion-code.yaml", "orion-code.yml", "orion.json"];
|
|
64
83
|
/** Project instructions file (relative to project root). */
|