@kylecheng3146/agent-ops 0.1.13 → 0.1.15
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/README.md +22 -17
- package/dist/packages/cli/src/args.js +5 -0
- package/dist/packages/cli/src/bin.js +28 -27
- package/dist/packages/cli/src/commands/init.js +36 -9
- package/dist/packages/cli/src/commands/review.js +51 -32
- package/dist/packages/cli/src/commands/trust.js +28 -0
- package/dist/packages/cli/src/commands/uninstall.js +36 -9
- package/dist/packages/cli/src/commands/update.js +36 -9
- package/dist/packages/cli/src/context.js +13 -8
- package/dist/packages/cli/src/public-plan.js +8 -6
- package/dist/runtime/src/adapters/claude/config.js +33 -17
- package/dist/runtime/src/adapters/claude/output.js +13 -0
- package/dist/runtime/src/hooks/stop-service.js +45 -6
- package/dist/runtime/src/install/codex-loop.js +33 -3
- package/dist/runtime/src/install/harness.js +6 -3
- package/dist/runtime/src/install/hooks.js +1 -1
- package/dist/runtime/src/install/ownership.js +25 -9
- package/dist/runtime/src/install/plan.js +11 -7
- package/dist/runtime/src/install/probes.js +2 -2
- package/dist/runtime/src/review/attestation.js +65 -0
- package/dist/runtime/src/review/execute.js +3 -0
- package/docs/en/guides/configuration.md +20 -15
- package/docs/en/spec/README.md +2 -1
- package/docs/en/spec/harness-adapters.md +2 -1
- package/docs/zh-TW/guides/configuration.md +17 -11
- package/docs/zh-TW/spec/README.md +2 -2
- package/docs/zh-TW/spec/harness-adapters.md +2 -2
- package/package.json +1 -1
- package/templates/common/CLAUDE.block.md +3 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { AgentOpsError } from "../fs/paths.js";
|
|
3
|
+
import { readPrivateFile, writePrivateFile } from "../security/permissions.js";
|
|
4
|
+
const FINGERPRINT_PATTERN = /^[a-f0-9]{64}$/u;
|
|
5
|
+
const TASK_ID_PATTERN = /^[A-Za-z0-9._-]{1,128}$/u;
|
|
6
|
+
export const REVIEW_ATTESTATION_DIRECTORY = ".agent-ops/reviews";
|
|
7
|
+
function attestationPath(root, fingerprint) {
|
|
8
|
+
return join(root, ...REVIEW_ATTESTATION_DIRECTORY.split("/"), `${fingerprint}.json`);
|
|
9
|
+
}
|
|
10
|
+
function parseAttestation(value) {
|
|
11
|
+
if (typeof value !== "object" || value === null) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const record = value;
|
|
15
|
+
if (record.schemaVersion !== 1 ||
|
|
16
|
+
record.status !== "PASS" ||
|
|
17
|
+
(record.taskId !== undefined &&
|
|
18
|
+
(typeof record.taskId !== "string" ||
|
|
19
|
+
!TASK_ID_PATTERN.test(record.taskId))) ||
|
|
20
|
+
typeof record.harness !== "string" ||
|
|
21
|
+
!TASK_ID_PATTERN.test(record.harness) ||
|
|
22
|
+
typeof record.sourceFingerprint !== "string" ||
|
|
23
|
+
!FINGERPRINT_PATTERN.test(record.sourceFingerprint) ||
|
|
24
|
+
typeof record.createdAt !== "string" ||
|
|
25
|
+
!Number.isFinite(Date.parse(record.createdAt))) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
schemaVersion: 1,
|
|
30
|
+
...(record.taskId === undefined ? {} : { taskId: record.taskId }),
|
|
31
|
+
harness: record.harness,
|
|
32
|
+
status: "PASS",
|
|
33
|
+
sourceFingerprint: record.sourceFingerprint,
|
|
34
|
+
createdAt: record.createdAt
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export async function saveReviewAttestation(root, attestation) {
|
|
38
|
+
const validated = parseAttestation(attestation);
|
|
39
|
+
if (validated === null) {
|
|
40
|
+
throw new AgentOpsError("REVIEW_ATTESTATION_INVALID", "Review attestation is invalid.");
|
|
41
|
+
}
|
|
42
|
+
const relativePath = `${REVIEW_ATTESTATION_DIRECTORY}/${validated.sourceFingerprint}.json`;
|
|
43
|
+
await writePrivateFile(attestationPath(root, validated.sourceFingerprint), `${JSON.stringify(validated, null, 2)}\n`, root);
|
|
44
|
+
return relativePath;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Returns the attestation recorded for this exact source state, or null. A
|
|
48
|
+
* malformed file reads as absent: a gate must fail closed on garbage, never
|
|
49
|
+
* treat it as a passing review.
|
|
50
|
+
*/
|
|
51
|
+
export async function findReviewAttestation(root, sourceFingerprint) {
|
|
52
|
+
if (!FINGERPRINT_PATTERN.test(sourceFingerprint)) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const source = await readPrivateFile(attestationPath(root, sourceFingerprint), root);
|
|
56
|
+
if (source === null) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
return parseAttestation(JSON.parse(source));
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -140,6 +140,9 @@ export function createReviewExecutor(options) {
|
|
|
140
140
|
if (spawned.stdoutTruncated || spawned.stderrTruncated) {
|
|
141
141
|
return { status: "NOT_RUN", reason: "output-too-large", harness: target };
|
|
142
142
|
}
|
|
143
|
+
if (spawned.failureClass === "nonzero-exit") {
|
|
144
|
+
return { status: "NOT_RUN", reason: "login-required", harness: target };
|
|
145
|
+
}
|
|
143
146
|
const payload = extractReviewObject(target, spawned.stdout);
|
|
144
147
|
const parsed = payload === undefined
|
|
145
148
|
? undefined
|
|
@@ -47,9 +47,10 @@ Enable it during `agent-ops init`, or by hand:
|
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
`targets` is an **ordered fallback chain**. Every review locks to the
|
|
50
|
-
staged/unstaged/untracked surface (or a clean `--base <ref>...HEAD` range)
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
staged/unstaged/untracked surface (or a clean `--base <ref>...HEAD` range).
|
|
51
|
+
A bare review uses the built-in `change-quality` criterion; `--task` uses the
|
|
52
|
+
task criteria and requires fresh PASS evidence for required checks. The full
|
|
53
|
+
report is printed, and PASS persists only a source-fingerprint attestation.
|
|
53
54
|
|
|
54
55
|
Every attempt starts from a fresh temporary cwd with a narrow environment. The
|
|
55
56
|
following target identities may be configured; only Claude currently meets the
|
|
@@ -102,20 +103,25 @@ interactive OAuth, so there is no `--fix`. Run `<target> login` yourself.
|
|
|
102
103
|
### Project-local loop profile
|
|
103
104
|
|
|
104
105
|
`--profile loop` is an opt-in project-scope profile. Select `codex`, `claude`,
|
|
105
|
-
or both (for example, `--harness codex,claude`)
|
|
106
|
-
|
|
107
|
-
a dry run:
|
|
106
|
+
or both (for example, `--harness codex,claude`). Claude Code supports native
|
|
107
|
+
Windows through a generated PowerShell launcher; Codex's loop launcher still
|
|
108
|
+
requires POSIX-compatible `bash`. Start with a dry run:
|
|
108
109
|
|
|
109
110
|
```bash
|
|
110
111
|
agent-ops init --dry-run --scope project --harness codex,claude --profile loop --json
|
|
111
112
|
agent-ops init --scope project --harness codex,claude --profile loop --yes
|
|
112
113
|
```
|
|
113
114
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
On native Windows, select `--harness claude` unless Codex is running in a
|
|
116
|
+
POSIX environment; the Codex loop still invokes `bash`.
|
|
117
|
+
|
|
118
|
+
For each selected supported harness, agent-ops owns the minimal native
|
|
119
|
+
launchers: `.codex/hooks/agent-ops-loop.sh` for Codex, and
|
|
120
|
+
`.claude/hooks/agent-ops-loop.sh` plus `.claude/hooks/agent-ops-loop.ps1` for
|
|
121
|
+
Claude Code. Both Claude launchers delegate to the same installed Node
|
|
122
|
+
runtime, so they do not copy a project-specific loop script. On Windows,
|
|
123
|
+
Claude's generated settings select the PowerShell launcher. Codex also gets
|
|
124
|
+
`.codex/config.toml` only when it is absent. First installation seeds, without replacing existing content,
|
|
119
125
|
`loop-goal.md`, `loop-state.md`, and `loop-telemetry.jsonl` under the selected
|
|
120
126
|
harness directory. A hash-commented `.gitignore` block ignores those local
|
|
121
127
|
files.
|
|
@@ -194,7 +200,6 @@ Changing this feature changes native registration. Run:
|
|
|
194
200
|
|
|
195
201
|
```bash
|
|
196
202
|
agent-ops update
|
|
197
|
-
agent-ops trust grant
|
|
198
203
|
```
|
|
199
204
|
|
|
200
205
|
Without `update`, doctor can report `UPDATE_REQUIRED` for registration drift.
|
|
@@ -202,14 +207,14 @@ Separately, after a toolkit upgrade or effective profile or capability change
|
|
|
202
207
|
alters an intact path-independent managed rules artifact,
|
|
203
208
|
`artifact-staleness` reports `DEGRADED` with `UPDATE_REQUIRED`. `agent-ops
|
|
204
209
|
update` regenerates the artifact and clears that result; a missing or
|
|
205
|
-
hash-mismatched artifact remains an `artifacts` `FAIL`.
|
|
206
|
-
|
|
210
|
+
hash-mismatched artifact remains an `artifacts` `FAIL`. Confirmed project
|
|
211
|
+
updates automatically replace the stale trust binding when verifiers exist.
|
|
207
212
|
|
|
208
213
|
Doctor never writes, and some findings have no fix: a surface outside the
|
|
209
214
|
installation root, or a capability a harness only partially supports by
|
|
210
215
|
descriptor declaration (opencode's `lifecycle-summary`, for example), report
|
|
211
216
|
`UNKNOWN` or `DEGRADED` permanently and exit 0. CI that wants automatic
|
|
212
|
-
repair calls `agent-ops update`
|
|
217
|
+
repair calls `agent-ops update` or the manual `agent-ops trust grant` directly rather
|
|
213
218
|
than parsing doctor's output.
|
|
214
219
|
|
|
215
220
|
Stop is report-only: it continues the
|
package/docs/en/spec/README.md
CHANGED
|
@@ -7,7 +7,8 @@ integration is a generated local plugin; it does not manage `opencode.json`.
|
|
|
7
7
|
|
|
8
8
|
Configuration is versioned independently from the manifest. Config v1 migrates
|
|
9
9
|
to config v2 with Stop verification disabled; changing the capability requires
|
|
10
|
-
`agent-ops update
|
|
10
|
+
a confirmed project `agent-ops update`, which also refreshes trust when
|
|
11
|
+
verifiers exist. Stop verification is
|
|
11
12
|
explicit, trusted, report-only, and never completes a task. Dry-run plans keep
|
|
12
13
|
foreign settings opaque, and the routing migration is one-way once applied.
|
|
13
14
|
|
|
@@ -85,7 +85,8 @@ policy into project-specific scripts or alter an ordinary permission request.
|
|
|
85
85
|
|
|
86
86
|
- Trigger: A project selects `loop` with Codex, Claude Code, or both.
|
|
87
87
|
- Action: Generate only the selected `.codex/hooks/agent-ops-loop.sh` and/or
|
|
88
|
-
`.claude/hooks/agent-ops-loop.sh`
|
|
88
|
+
Claude's `.claude/hooks/agent-ops-loop.sh` plus
|
|
89
|
+
`.claude/hooks/agent-ops-loop.ps1` launchers, register the documented loop
|
|
89
90
|
lifecycle events except `Stop`, and preserve foreign hook groups. Block only
|
|
90
91
|
high-confidence literal credentials at `UserPromptSubmit` or Bash
|
|
91
92
|
`PreToolUse`, and dangerous Bash commands at `PreToolUse`, using the documented native denial shape. Emit no
|
|
@@ -42,8 +42,9 @@ Claude 與 Codex lifecycle support 為 `supported`,OpenCode 從 app initializa
|
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
`targets` 是**有序的後備鏈**。每次 review 都鎖定 staged/unstaged/untracked
|
|
45
|
-
變更(或乾淨的 `--base <ref>...HEAD
|
|
46
|
-
|
|
45
|
+
變更(或乾淨的 `--base <ref>...HEAD`)。裸跑使用內建 `change-quality` 準則;
|
|
46
|
+
`--task` 使用 task criteria,並要求必要驗證的最新 PASS evidence。完整 report
|
|
47
|
+
會顯示給人看,PASS 後只持久化 source-fingerprint attestation。
|
|
47
48
|
|
|
48
49
|
每次嘗試都從新的暫存 cwd 與最小環境啟動。目前只有 Claude 具備完整的
|
|
49
50
|
context-isolation 合約,能自動執行:
|
|
@@ -90,18 +91,24 @@ agent-ops doctor --check-auth # 每個目標一次真實 print 呼叫
|
|
|
90
91
|
### Project-local loop profile
|
|
91
92
|
|
|
92
93
|
`--profile loop` 是明確 opt-in 的 project-scope profile。請選擇 `codex`、
|
|
93
|
-
`claude` 或兩者(例如 `--harness codex,claude
|
|
94
|
-
|
|
94
|
+
`claude` 或兩者(例如 `--harness codex,claude`)。Claude Code 已支援原生
|
|
95
|
+
Windows,會使用產生的 PowerShell launcher;Codex 的 loop launcher 仍需要
|
|
96
|
+
POSIX-compatible `bash`。建議先 dry run:
|
|
95
97
|
|
|
96
98
|
```bash
|
|
97
99
|
agent-ops init --dry-run --scope project --harness codex,claude --profile loop --json
|
|
98
100
|
agent-ops init --scope project --harness codex,claude --profile loop --yes
|
|
99
101
|
```
|
|
100
102
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
在原生 Windows 上,除非 Codex 是在 POSIX environment 執行,請選擇
|
|
104
|
+
`--harness claude`;Codex loop 仍會呼叫 `bash`。
|
|
105
|
+
|
|
106
|
+
對每個選定且支援的 harness,agent-ops 擁有最小的 native launcher:Codex 是
|
|
107
|
+
`.codex/hooks/agent-ops-loop.sh`;Claude Code 是
|
|
108
|
+
`.claude/hooks/agent-ops-loop.sh` 與 `.claude/hooks/agent-ops-loop.ps1`。兩個
|
|
109
|
+
Claude launcher 都委派給同一個已安裝的 Node runtime,因此不會複製
|
|
110
|
+
project-specific loop script。Windows 產生的 settings 會選用 PowerShell
|
|
111
|
+
launcher。Codex 只會在 `.codex/config.toml` 不存在時建立它。首次安裝會在不
|
|
105
112
|
覆寫既有內容的前提下,於選定 harness directory 建立 `loop-goal.md`、
|
|
106
113
|
`loop-state.md` 與 `loop-telemetry.jsonl`;並以 hash-commented `.gitignore`
|
|
107
114
|
block 忽略這些 local file。
|
|
@@ -175,7 +182,6 @@ config v2 feature,必須明確啟用且至少提供一個已確認的 command
|
|
|
175
182
|
|
|
176
183
|
```bash
|
|
177
184
|
agent-ops update
|
|
178
|
-
agent-ops trust grant
|
|
179
185
|
```
|
|
180
186
|
|
|
181
187
|
未執行 `update` 時,doctor 可因 registration drift 回報 `UPDATE_REQUIRED`。另
|
|
@@ -183,12 +189,12 @@ agent-ops trust grant
|
|
|
183
189
|
path-independent managed rules artifact 改變時,`artifact-staleness` 會回報帶有
|
|
184
190
|
`UPDATE_REQUIRED` 的 `DEGRADED`。`agent-ops update` 會重新產生 artifact 並清除
|
|
185
191
|
這個結果;artifact 缺失或 hash 不符時,`artifacts` check 仍為 `FAIL`。未重新
|
|
186
|
-
|
|
192
|
+
project update 經確認後,若有 verifier 會自動更新 stale trust binding。
|
|
187
193
|
|
|
188
194
|
doctor 從不寫入檔案,部分結果本來就無法修復:安裝根目錄以外的 surface,
|
|
189
195
|
或 harness 依 descriptor 宣告只部分支援的 capability(例如 opencode 的
|
|
190
196
|
`lifecycle-summary`),會永久回報 `UNKNOWN` 或 `DEGRADED` 且 exit 0。若 CI
|
|
191
|
-
需要自動修復,請直接呼叫 `agent-ops update
|
|
197
|
+
需要自動修復,請直接呼叫 `agent-ops update`;也可手動呼叫 `agent-ops trust grant`,
|
|
192
198
|
不要解析 doctor 的輸出。
|
|
193
199
|
|
|
194
200
|
Stop 是 report-only:`PASS`、`FAIL`
|
|
@@ -6,8 +6,8 @@ Harness adapter 規則涵蓋 Codex、Claude Code 與 opencode。opencode 整合
|
|
|
6
6
|
產生的 local plugin,不管理 `opencode.json`。
|
|
7
7
|
|
|
8
8
|
Configuration 與 manifest 分開版本化。Config v1 會遷移為預設 disabled Stop
|
|
9
|
-
verification 的 config v2;變更 capability
|
|
10
|
-
|
|
9
|
+
verification 的 config v2;變更 capability 後必須執行經確認的 project
|
|
10
|
+
`agent-ops update`,有 verifier 時會一併更新 trust。Stop verification 必須明確啟用、具備 trust、
|
|
11
11
|
為 report-only,且永遠不會完成 task。Dry-run plan 會隱藏 foreign settings
|
|
12
12
|
內容;routing migration 一旦套用即為單向。
|
|
13
13
|
|
|
@@ -72,8 +72,8 @@ Claude Code launcher 後使用同一個 shared runtime。它 MUST NOT 將 policy
|
|
|
72
72
|
project-specific script,也不得改變一般 permission request。
|
|
73
73
|
|
|
74
74
|
- Trigger: Project 以 Codex、Claude Code 或兩者選擇 `loop`。
|
|
75
|
-
- Action: 只產生選定的 `.codex/hooks/agent-ops-loop.sh` 與/或
|
|
76
|
-
`.claude/hooks/agent-ops-loop.sh` launcher,註冊文件化的 loop lifecycle event
|
|
75
|
+
- Action: 只產生選定的 `.codex/hooks/agent-ops-loop.sh` 與/或 Claude 的
|
|
76
|
+
`.claude/hooks/agent-ops-loop.sh`、`.claude/hooks/agent-ops-loop.ps1` launcher,註冊文件化的 loop lifecycle event
|
|
77
77
|
(不含 `Stop`),並保留 foreign hook group。只在 `UserPromptSubmit` 或 Bash
|
|
78
78
|
`PreToolUse` 的 high-confidence literal credential,以及 `PreToolUse` 的危險 Bash command 時,使用
|
|
79
79
|
文件化的 native denial shape 進行 blocking。對 `PermissionRequest`(包括
|
package/package.json
CHANGED