claude-spotter 1.7.2 → 1.7.3
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 +4 -0
- package/README.ja.md +0 -18
- package/README.md +1 -19
- package/bin/spotter.mjs +0 -10
- package/package.json +1 -1
- package/src/cli/auditor-cmd.mjs +1 -7
- package/src/cli/doctor.mjs +0 -40
- package/src/core/auditor-backend.mjs +1 -10
- package/src/core/hook-event-log.mjs +1 -1
- package/src/daemon/daemon.mjs +0 -39
- package/src/index.mjs +0 -32
- package/src/cli/codex-cmd.mjs +0 -254
- package/src/core/codex-risk-dispatch.mjs +0 -101
- package/src/core/codex-sidecar-auditor-backend.mjs +0 -333
- package/src/core/codex-sidecar-policy.mjs +0 -194
- package/src/core/codex-sidecar-runner.mjs +0 -761
- package/src/core/sidecar-context.mjs +0 -117
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
各節はそのversion公開時点の変更記録であり、後続versionにより置換された仕様を含む。
|
|
4
4
|
現行runtime契約は[`docs/00_overview.md`](https://github.com/kitepon/Spotter/blob/main/docs/00_overview.md)から辿る。
|
|
5
5
|
|
|
6
|
+
## 1.7.3 — 2026-09-24
|
|
7
|
+
|
|
8
|
+
- 退役するcodex-sidecarの明示CLI、追加監査dispatch、primary auditor backend指定、診断と関連コードを削除する。Jev、Codex CLI、Haikuの主監査は維持する。
|
|
9
|
+
|
|
6
10
|
## 1.7.2 — 2026-09-21
|
|
7
11
|
|
|
8
12
|
- Jevの判定を実験済みの短い質問+Noulへ変更し、肯定確率0.5超を提案する。
|
package/README.ja.md
CHANGED
|
@@ -245,13 +245,6 @@ spotter dashboard device --id mac --name Mac
|
|
|
245
245
|
# この端末の評価DBを127.0.0.1:53940で配信
|
|
246
246
|
spotter dashboard hub --config dashboard-hub.json --host 172.18.0.1
|
|
247
247
|
# 端末一覧と/devices/<id>/の端末別proxyを配信
|
|
248
|
-
spotter codex risk-check --findings findings.json --host-agent claude
|
|
249
|
-
# Spotter finding を codex-sidecar に渡して read-only risk analysis
|
|
250
|
-
spotter codex review|explore|opinion --findings findings.json --host-agent claude
|
|
251
|
-
# その他の read-only codex-sidecar second-pass workflow
|
|
252
|
-
spotter codex work --findings findings.json --instruction "docs 更新" --approve-work \
|
|
253
|
-
--allowed-path docs/ --preserve-worktree
|
|
254
|
-
# 承認済み codex-sidecar work を isolated worktree で実行
|
|
255
248
|
spotter codex-hook install
|
|
256
249
|
# Codex native hooks の修復 / 明示登録 (通常は spotter install が実行)
|
|
257
250
|
spotter codex-hook diagnostics
|
|
@@ -283,16 +276,6 @@ retry queueを作らず、その端末だけを切り離せる。
|
|
|
283
276
|
Windows同梱のTask Scheduler installerはnpm・SSH用の対話ユーザープロファイルを維持しつつ、
|
|
284
277
|
dashboardの2つのPowerShell actionを非対話・console非表示で起動する。
|
|
285
278
|
|
|
286
|
-
Codex risk dispatch を daemon から非同期に流す場合:
|
|
287
|
-
|
|
288
|
-
```bash
|
|
289
|
-
SPOTTER_CODEX_RISK_CHECK=1 spotter daemon start --session-id ... --project-root ...
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
有効時は daemon が `pass:false` finding を detached process の
|
|
293
|
-
`spotter codex risk-check` に渡します。hook 応答は Codex を待ちません。
|
|
294
|
-
配線だけ確認する場合は `SPOTTER_CODEX_RISK_CHECK_DRY_RUN=1` を併用します。
|
|
295
|
-
|
|
296
279
|
## 端末内runtime error集計
|
|
297
280
|
|
|
298
281
|
factory diagnosticsとruntime error集計は既定OFFです。canonicalなdotagents factory reporter設定で
|
|
@@ -320,7 +303,6 @@ Codex CLI auditor は versioned product policy を使い、production は反復
|
|
|
320
303
|
profile から production へ自動昇格しません。`latest` alias や
|
|
321
304
|
親 Codex の default を暗黙継承せず、失敗時に別 model へ retry しません。制御された実験では
|
|
322
305
|
`SPOTTER_CODEX_CLI_MODEL` / `SPOTTER_CODEX_CLI_REASONING_EFFORT` で上書きでき、diagnostics は unverified と表示します。
|
|
323
|
-
明示 smoke には `SPOTTER_AUDITOR_BACKEND=codex-sidecar` も使えます。
|
|
324
306
|
|
|
325
307
|
## 設計ドキュメント
|
|
326
308
|
|
package/README.md
CHANGED
|
@@ -249,13 +249,6 @@ spotter dashboard device --id mac --name Mac
|
|
|
249
249
|
# serve this terminal's local evaluation DB on 127.0.0.1:53940
|
|
250
250
|
spotter dashboard hub --config dashboard-hub.json --host 172.18.0.1
|
|
251
251
|
# list terminals and proxy /devices/<id>/ to their local servers
|
|
252
|
-
spotter codex risk-check --findings findings.json --host-agent claude
|
|
253
|
-
# run read-only codex-sidecar risk analysis for Spotter findings
|
|
254
|
-
spotter codex review|explore|opinion --findings findings.json --host-agent claude
|
|
255
|
-
# run other read-only codex-sidecar second-pass workflows
|
|
256
|
-
spotter codex work --findings findings.json --instruction "Update docs" --approve-work \
|
|
257
|
-
--allowed-path docs/ --preserve-worktree
|
|
258
|
-
# run approved codex-sidecar work in an isolated worktree
|
|
259
252
|
spotter codex-hook install
|
|
260
253
|
# repair / explicitly register Codex native hooks (normally handled by spotter install)
|
|
261
254
|
spotter codex-hook diagnostics
|
|
@@ -290,16 +283,6 @@ The reference four-terminal service, reverse-tunnel, and Caddy/Cloudflare layout
|
|
|
290
283
|
On Windows, the bundled Task Scheduler installer keeps the interactive user's profile for npm and
|
|
291
284
|
SSH while starting both dashboard PowerShell actions non-interactively with hidden console windows.
|
|
292
285
|
|
|
293
|
-
Optional async Codex risk dispatch:
|
|
294
|
-
|
|
295
|
-
```bash
|
|
296
|
-
SPOTTER_CODEX_RISK_CHECK=1 spotter daemon start --session-id ... --project-root ...
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
When enabled, the daemon dispatches `pass:false` findings to `spotter codex risk-check`
|
|
300
|
-
in a detached process. Hook responses do not wait for Codex. Add
|
|
301
|
-
`SPOTTER_CODEX_RISK_CHECK_DRY_RUN=1` to exercise the wiring without calling Codex.
|
|
302
|
-
|
|
303
286
|
Primary auditor backend policy: Claude hooks automatically select Codex CLI when it is available on PATH,
|
|
304
287
|
otherwise the Haiku-compatible path. Codex native hooks automatically select Codex CLI. An explicit
|
|
305
288
|
`SPOTTER_AUDITOR_BACKEND` override wins on either host; runtime failure never triggers a hidden fallback.
|
|
@@ -343,14 +326,13 @@ Codex CLI auditor child processes use a versioned product policy. The production
|
|
|
343
326
|
Spotter does not inherit a `latest` alias or the parent Codex default, and an invocation failure never retries another model.
|
|
344
327
|
`SPOTTER_CODEX_CLI_MODEL` and `SPOTTER_CODEX_CLI_REASONING_EFFORT` can override
|
|
345
328
|
the production values for controlled experiments; diagnostics mark overrides as unverified.
|
|
346
|
-
`SPOTTER_AUDITOR_BACKEND=codex-sidecar` is available for explicit sidecar auditor smoke.
|
|
347
329
|
|
|
348
330
|
## Design docs
|
|
349
331
|
|
|
350
332
|
- **Current design** (catalog, discovery, classification axes): [docs/01_catalog-design.md](https://github.com/kitepon/Spotter/blob/main/docs/01_catalog-design.md) — source of truth from v1.0.0
|
|
351
333
|
- **Open issues + unverified concerns**: [docs/open-issues.md](https://github.com/kitepon/Spotter/blob/main/docs/open-issues.md) — read this before starting new work
|
|
352
334
|
- **Runtime contract**: [docs/02_spotter-claude-contract.md](https://github.com/kitepon/Spotter/blob/main/docs/02_spotter-claude-contract.md) — Claude hook / daemon / Haiku contract plus Codex native hook policy
|
|
353
|
-
- **Implementation invariants (§0)**: [AGENTS.md](https://github.com/kitepon/Spotter/blob/main/AGENTS.md) — no fallbacks, no silent failures, no provisional code
|
|
335
|
+
- **Implementation invariants (§0)**: [AGENTS.md](https://github.com/kitepon/Spotter/blob/main/AGENTS.md) — no fallbacks, no silent failures, no provisional code
|
|
354
336
|
- **Archived plans and history**: [docs/archive/](https://github.com/kitepon/Spotter/tree/main/docs/archive) — completed Codex rollout plans, primary backend smoke logs, and the frozen v0.1 design discussion
|
|
355
337
|
|
|
356
338
|
## Known limitations
|
package/bin/spotter.mjs
CHANGED
|
@@ -7,7 +7,6 @@ import { runUninstall } from '../src/cli/uninstall.mjs';
|
|
|
7
7
|
import { runDoctor } from '../src/cli/doctor.mjs';
|
|
8
8
|
import { runStatus } from '../src/cli/status.mjs';
|
|
9
9
|
import { runDbList, runDbRefresh, runDbRebuild } from '../src/cli/db-cmd.mjs';
|
|
10
|
-
import { runCodexCommand } from '../src/cli/codex-cmd.mjs';
|
|
11
10
|
import { runCodexHookCommand } from '../src/cli/codex-hook-cmd.mjs';
|
|
12
11
|
import { runCursorHookCommand } from '../src/cli/cursor-hook-cmd.mjs';
|
|
13
12
|
import { runAuditorCommand } from '../src/cli/auditor-cmd.mjs';
|
|
@@ -62,12 +61,6 @@ Usage:
|
|
|
62
61
|
spotter dashboard device --id ID [--name NAME] [--host HOST] [--port PORT] [--db PATH]
|
|
63
62
|
spotter dashboard hub --config FILE [--host HOST] [--port PORT]
|
|
64
63
|
serve the local device-routed evaluation dashboard
|
|
65
|
-
spotter codex risk-check --findings FILE
|
|
66
|
-
run read-only codex-sidecar risk analysis
|
|
67
|
-
spotter codex review|explore|opinion --findings FILE
|
|
68
|
-
run read-only codex-sidecar second-pass workflows
|
|
69
|
-
spotter codex work --findings FILE --approve-work --allowed-path PATH
|
|
70
|
-
run approved codex-sidecar worktree workflow
|
|
71
64
|
spotter codex-hook install|uninstall|diagnostics
|
|
72
65
|
(experimental) manage Codex native hooks
|
|
73
66
|
spotter cursor-hook install|uninstall|diagnostics
|
|
@@ -128,9 +121,6 @@ async function main() {
|
|
|
128
121
|
case 'doctor':
|
|
129
122
|
await runDoctor();
|
|
130
123
|
return;
|
|
131
|
-
case 'codex':
|
|
132
|
-
await runCodexCommand({ argv: rest });
|
|
133
|
-
return;
|
|
134
124
|
case 'codex-hook':
|
|
135
125
|
await runCodexHookCommand({ argv: rest });
|
|
136
126
|
return;
|
package/package.json
CHANGED
package/src/cli/auditor-cmd.mjs
CHANGED
|
@@ -10,7 +10,7 @@ const AUDITOR_USAGE = `spotter auditor — experimental primary auditor smoke co
|
|
|
10
10
|
Usage:
|
|
11
11
|
spotter auditor judge --stage user_input|turn_end --input FILE
|
|
12
12
|
[--project DIR] [--host-agent claude|codex|automation|unknown]
|
|
13
|
-
[--backend jev|haiku|codex-cli|
|
|
13
|
+
[--backend jev|haiku|codex-cli|auto]
|
|
14
14
|
spotter auditor matrix --stage user_input|turn_end --input FILE [--project DIR]
|
|
15
15
|
spotter auditor model-matrix --fixtures FILE [--profile baseline|luna|terra|terra-medium]...
|
|
16
16
|
[--repeat N] [--project DIR] [--output FILE]
|
|
@@ -134,9 +134,7 @@ export async function runAuditorMatrixCommand({
|
|
|
134
134
|
|
|
135
135
|
const AUDITOR_MATRIX_ROWS = Object.freeze([
|
|
136
136
|
Object.freeze({ id: 'claude.codex-cli', hostAgent: 'claude', backend: 'codex-cli' }),
|
|
137
|
-
Object.freeze({ id: 'claude.codex-sidecar', hostAgent: 'claude', backend: 'codex-sidecar' }),
|
|
138
137
|
Object.freeze({ id: 'codex.codex-cli', hostAgent: 'codex', backend: 'codex-cli' }),
|
|
139
|
-
Object.freeze({ id: 'codex.codex-sidecar', hostAgent: 'codex', backend: 'codex-sidecar' }),
|
|
140
138
|
]);
|
|
141
139
|
|
|
142
140
|
async function runAuditorMatrixRow({
|
|
@@ -207,15 +205,11 @@ function summarizeMatrix(matrix) {
|
|
|
207
205
|
total: matrix.length,
|
|
208
206
|
success: matrix.filter((row) => row.status === 'success').length,
|
|
209
207
|
error: matrix.filter((row) => row.status === 'error').length,
|
|
210
|
-
sidecarPrimaryAuditorImplemented: matrix
|
|
211
|
-
.filter((row) => row.backend === 'codex-sidecar')
|
|
212
|
-
.some((row) => row.status === 'success'),
|
|
213
208
|
};
|
|
214
209
|
}
|
|
215
210
|
|
|
216
211
|
function recursionSafetyFor(backend) {
|
|
217
212
|
if (backend === 'codex-cli') return 'spotter_parent_pid_backend_env';
|
|
218
|
-
if (backend === 'codex-sidecar') return 'spotter_parent_pid_sidecar_env';
|
|
219
213
|
return 'unknown';
|
|
220
214
|
}
|
|
221
215
|
|
package/src/cli/doctor.mjs
CHANGED
|
@@ -76,10 +76,6 @@ export async function runDoctor() {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
if (projectRoot) {
|
|
79
|
-
const sidecar = await codexSidecarAuditorReadiness(projectRoot);
|
|
80
|
-
mark(sidecar.ok, `codex-sidecar auditor: ${sidecar.status}`, sidecar.detail);
|
|
81
|
-
if (!sidecar.ok) warnings += 1;
|
|
82
|
-
|
|
83
79
|
const auditorContext = await inspectAuditorContextConfiguration({ projectRoot });
|
|
84
80
|
mark(auditorContext.ok, `evaluation context: ${auditorContext.mode}`, auditorContext.detail);
|
|
85
81
|
if (!auditorContext.ok) warnings += 1;
|
|
@@ -232,42 +228,6 @@ async function checkLocalAuditDb({ projectRoot, hostAgent }) {
|
|
|
232
228
|
}
|
|
233
229
|
}
|
|
234
230
|
|
|
235
|
-
async function codexSidecarAuditorReadiness(projectRoot) {
|
|
236
|
-
const args = ['diagnostics', '--project', projectRoot, '--preset', 'auditor', '--json'];
|
|
237
|
-
const cliPath = process.env.SPOTTER_CODEX_SIDECAR_CLI_PATH;
|
|
238
|
-
const cmd = cliPath ? process.execPath : 'codex-sidecar';
|
|
239
|
-
const finalArgs = cliPath ? [cliPath, ...args] : args;
|
|
240
|
-
try {
|
|
241
|
-
const invocation = buildWindowsCompatibleInvocation({
|
|
242
|
-
command: cmd,
|
|
243
|
-
args: finalArgs,
|
|
244
|
-
env: process.env,
|
|
245
|
-
allowCmdFallback: false,
|
|
246
|
-
});
|
|
247
|
-
const { stdout } = await execFileP(invocation.command, invocation.args, {
|
|
248
|
-
timeout: 15_000,
|
|
249
|
-
windowsHide: true,
|
|
250
|
-
maxBuffer: 1024 * 1024,
|
|
251
|
-
});
|
|
252
|
-
const parsed = JSON.parse(stdout);
|
|
253
|
-
const ok = parsed?.status === 'ok' && parsed?.normalizedRequest?.workflow === 'auditor';
|
|
254
|
-
return {
|
|
255
|
-
ok,
|
|
256
|
-
status: ok ? 'available' : 'unavailable',
|
|
257
|
-
detail: ok
|
|
258
|
-
? `workflow=${parsed.normalizedRequest.workflow}, reasoning=${parsed.normalizedRequest.modelReasoningEffort ?? 'default'}`
|
|
259
|
-
: `unexpected diagnostics: status=${parsed?.status ?? 'unknown'}`,
|
|
260
|
-
};
|
|
261
|
-
} catch (err) {
|
|
262
|
-
const stderr = typeof err?.stderr === 'string' && err.stderr.trim() ? ` stderr=${err.stderr.trim().split('\n').slice(-1)[0]}` : '';
|
|
263
|
-
return {
|
|
264
|
-
ok: false,
|
|
265
|
-
status: 'unavailable',
|
|
266
|
-
detail: `${err.message}${stderr}`,
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
231
|
async function exists(path) {
|
|
272
232
|
try { await access(path); return true; }
|
|
273
233
|
catch { return false; }
|
|
@@ -11,7 +11,6 @@ import { filterCatalogMisses } from './auditor-response.mjs';
|
|
|
11
11
|
import { AuditorBackendError } from './auditor-error.mjs';
|
|
12
12
|
import { detectHostAgent } from './host-agent.mjs';
|
|
13
13
|
import { createCodexCliAuditorBackend } from './codex-cli-backend.mjs';
|
|
14
|
-
import { createCodexSidecarAuditorBackend } from './codex-sidecar-auditor-backend.mjs';
|
|
15
14
|
import { isCodexCliAvailable as defaultIsCodexCliAvailable } from './codex-cli-availability.mjs';
|
|
16
15
|
import { createJevAuditorBackend, resolveJevApiKey, jevSelection, assertJevNotConfigured } from './jev-backend.mjs';
|
|
17
16
|
|
|
@@ -22,7 +21,7 @@ export {
|
|
|
22
21
|
filterCatalogMisses,
|
|
23
22
|
} from './auditor-response.mjs';
|
|
24
23
|
|
|
25
|
-
const AUDITOR_BACKENDS = new Set(['jev', 'haiku', 'codex-cli', '
|
|
24
|
+
const AUDITOR_BACKENDS = new Set(['jev', 'haiku', 'codex-cli', 'auto']);
|
|
26
25
|
const AUDITOR_POLICIES = new Set(['current', 'next']);
|
|
27
26
|
export const DEFAULT_HAIKU_AUDITOR_TIMEOUT_MS = 45_000;
|
|
28
27
|
|
|
@@ -58,14 +57,6 @@ export function createAuditorBackend({
|
|
|
58
57
|
if (selected.backend === 'haiku') {
|
|
59
58
|
return createHaikuAuditorBackend({ catalog, logger, haikuCaller, timeoutMs, env });
|
|
60
59
|
}
|
|
61
|
-
if (selected.backend === 'codex-sidecar') {
|
|
62
|
-
return createCodexSidecarAuditorBackend({
|
|
63
|
-
catalog,
|
|
64
|
-
projectRoot,
|
|
65
|
-
env,
|
|
66
|
-
timeoutMs,
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
60
|
if (selected.backend === 'codex-cli') {
|
|
70
61
|
return createCodexCliAuditorBackend({
|
|
71
62
|
catalog,
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// host: "claude" | "codex",
|
|
16
16
|
// hook: "SessionStart" | "UserPromptSubmit" | "PreToolUse" | "Stop" | "SessionEnd",
|
|
17
17
|
// status: <hook-specific string>,
|
|
18
|
-
// backend?: "jev" | "haiku" | "codex-cli" |
|
|
18
|
+
// backend?: "jev" | "haiku" | "codex-cli" | null,
|
|
19
19
|
// pass?: boolean | null,
|
|
20
20
|
// missingTools?: string[],
|
|
21
21
|
// code?: string | null,
|
package/src/daemon/daemon.mjs
CHANGED
|
@@ -39,11 +39,6 @@ import {
|
|
|
39
39
|
createAuditorBackend,
|
|
40
40
|
DEFAULT_HAIKU_AUDITOR_TIMEOUT_MS,
|
|
41
41
|
} from '../core/auditor-backend.mjs';
|
|
42
|
-
import {
|
|
43
|
-
dispatchCodexRiskCheck,
|
|
44
|
-
isCodexRiskDispatchDryRun,
|
|
45
|
-
isCodexRiskDispatchEnabled,
|
|
46
|
-
} from '../core/codex-risk-dispatch.mjs';
|
|
47
42
|
import { homedir } from 'node:os';
|
|
48
43
|
import { join } from 'node:path';
|
|
49
44
|
import { writeFile, unlink } from 'node:fs/promises';
|
|
@@ -83,9 +78,6 @@ export async function startDaemon({
|
|
|
83
78
|
logFn = () => {},
|
|
84
79
|
haikuCallWindowMs = DEFAULT_HAIKU_CALL_WINDOW_MS,
|
|
85
80
|
heartbeatTimeoutMs = DEFAULT_HEARTBEAT_TIMEOUT_MS,
|
|
86
|
-
codexRiskCheckEnabled = isCodexRiskDispatchEnabled(),
|
|
87
|
-
codexRiskCheckDryRun = isCodexRiskDispatchDryRun(),
|
|
88
|
-
dispatchCodexRiskCheckFn = dispatchCodexRiskCheck,
|
|
89
81
|
// If a haikuCaller is explicitly injected (test path), default to haiku — the
|
|
90
82
|
// injection itself signals caller intent. Production callers never inject one,
|
|
91
83
|
// so they hit the `auto` branch which runs availability detection. Explicit
|
|
@@ -285,7 +277,6 @@ export async function startDaemon({
|
|
|
285
277
|
result.reason ? `, reason=${result.reason}` : ''
|
|
286
278
|
}`
|
|
287
279
|
);
|
|
288
|
-
maybeDispatchCodexRiskCheck('user_input', judgment);
|
|
289
280
|
return withEvaluationMeta(result, judgment.meta, auditorBackend.name, spotterVersion);
|
|
290
281
|
}
|
|
291
282
|
|
|
@@ -386,7 +377,6 @@ export async function startDaemon({
|
|
|
386
377
|
result.reason ? `, reason=${result.reason}` : ''
|
|
387
378
|
}`
|
|
388
379
|
);
|
|
389
|
-
maybeDispatchCodexRiskCheck('turn_end', judgment);
|
|
390
380
|
|
|
391
381
|
state.usedTools = [];
|
|
392
382
|
state.lastUserInput = null;
|
|
@@ -409,35 +399,6 @@ export async function startDaemon({
|
|
|
409
399
|
}
|
|
410
400
|
}
|
|
411
401
|
|
|
412
|
-
function maybeDispatchCodexRiskCheck(stage, judgment) {
|
|
413
|
-
if (auditorBackend.name === 'jev') return;
|
|
414
|
-
if (!codexRiskCheckEnabled) {
|
|
415
|
-
if (judgment.pass === false && judgment.findings.length > 0) {
|
|
416
|
-
logFn(`${stage}: codex_risk_check skipped: disabled`);
|
|
417
|
-
}
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
if (!projectRoot) {
|
|
421
|
-
logFn(`${stage}: codex_risk_check skipped: no projectRoot`);
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
if (judgment.pass === true || judgment.findings.length === 0) return;
|
|
425
|
-
dispatchCodexRiskCheckFn({
|
|
426
|
-
projectRoot,
|
|
427
|
-
judgment,
|
|
428
|
-
sessionId,
|
|
429
|
-
stage,
|
|
430
|
-
hostAgent: 'claude',
|
|
431
|
-
dryRun: codexRiskCheckDryRun,
|
|
432
|
-
}).then((dispatch) => {
|
|
433
|
-
if (dispatch?.dispatched) {
|
|
434
|
-
logFn(`${stage}: codex_risk_check dispatched pid=${dispatch.pid ?? 'unknown'} result=${dispatch.resultPath}`);
|
|
435
|
-
}
|
|
436
|
-
}).catch((err) => {
|
|
437
|
-
logFn(`${stage}: codex_risk_check dispatch failed: ${err.message}`);
|
|
438
|
-
});
|
|
439
|
-
}
|
|
440
|
-
|
|
441
402
|
const onErrorFn = (err, envelope) => {
|
|
442
403
|
const evt = envelope?.event ?? '(pre-parse)';
|
|
443
404
|
logFn(`handler error on ${evt}: ${err.code ?? 'E_INTERNAL'}: ${err.message}`);
|
package/src/index.mjs
CHANGED
|
@@ -36,11 +36,6 @@ export {
|
|
|
36
36
|
CodexAuditorModelPolicyError,
|
|
37
37
|
resolveCodexAuditorModelSelection,
|
|
38
38
|
} from './core/codex-auditor-model-policy.mjs';
|
|
39
|
-
export {
|
|
40
|
-
buildCodexSidecarAuditorCommand,
|
|
41
|
-
buildCodexSidecarAuditorPrompt,
|
|
42
|
-
createCodexSidecarAuditorBackend,
|
|
43
|
-
} from './core/codex-sidecar-auditor-backend.mjs';
|
|
44
39
|
export {
|
|
45
40
|
codexLastAssistantMessage,
|
|
46
41
|
codexToolInputText,
|
|
@@ -48,33 +43,6 @@ export {
|
|
|
48
43
|
readCodexUsedTools,
|
|
49
44
|
} from './core/codex-transcript.mjs';
|
|
50
45
|
export { assertHostAgent, detectHostAgent as detectNeutralHostAgent } from './core/host-agent.mjs';
|
|
51
|
-
export {
|
|
52
|
-
createSidecarResultRecord,
|
|
53
|
-
spotterFindingsToSidecarContextBlocks,
|
|
54
|
-
spotterFindingToSidecarContextBlock,
|
|
55
|
-
} from './core/sidecar-context.mjs';
|
|
56
|
-
export {
|
|
57
|
-
buildDiagnosticsCommand,
|
|
58
|
-
buildSidecarSpawnOptions,
|
|
59
|
-
classifySidecarAvailability,
|
|
60
|
-
decideCodexSidecarUse,
|
|
61
|
-
detectHostAgent,
|
|
62
|
-
workCapabilitySmokeFromDiagnostics,
|
|
63
|
-
} from './core/codex-sidecar-policy.mjs';
|
|
64
|
-
export {
|
|
65
|
-
dispatchCodexRiskCheck,
|
|
66
|
-
isCodexRiskDispatchDryRun,
|
|
67
|
-
isCodexRiskDispatchEnabled,
|
|
68
|
-
} from './core/codex-risk-dispatch.mjs';
|
|
69
|
-
export {
|
|
70
|
-
readFindingsJson,
|
|
71
|
-
runCodexExplore,
|
|
72
|
-
runCodexOpinion,
|
|
73
|
-
runCodexReadOnlyWorkflow,
|
|
74
|
-
runCodexReview,
|
|
75
|
-
runCodexRiskCheck,
|
|
76
|
-
runCodexWork,
|
|
77
|
-
} from './core/codex-sidecar-runner.mjs';
|
|
78
46
|
export {
|
|
79
47
|
defaultDaemonLogDir,
|
|
80
48
|
summarizeDaemonLogText,
|
package/src/cli/codex-cmd.mjs
DELETED
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
import { resolve } from 'node:path';
|
|
2
|
-
import { readFindingsJson, runCodexReadOnlyWorkflow, runCodexWork } from '../core/codex-sidecar-runner.mjs';
|
|
3
|
-
|
|
4
|
-
const CODEX_USAGE = `spotter codex — Codex sidecar workflows
|
|
5
|
-
|
|
6
|
-
Usage:
|
|
7
|
-
spotter codex risk-check --findings FILE [--project DIR] [--host-agent claude|codex|automation|unknown]
|
|
8
|
-
[--dry-run] [--turn-timeout-ms MS] [--out FILE] [--no-save]
|
|
9
|
-
spotter codex review --findings FILE [--project DIR] [--host-agent claude|codex|automation|unknown]
|
|
10
|
-
[--dry-run] [--turn-timeout-ms MS] [--out FILE] [--no-save]
|
|
11
|
-
spotter codex explore --findings FILE [--project DIR] [--host-agent claude|codex|automation|unknown]
|
|
12
|
-
[--dry-run] [--turn-timeout-ms MS] [--out FILE] [--no-save]
|
|
13
|
-
spotter codex opinion --findings FILE [--project DIR] [--host-agent claude|codex|automation|unknown]
|
|
14
|
-
[--dry-run] [--turn-timeout-ms MS] [--out FILE] [--no-save]
|
|
15
|
-
spotter codex work --findings FILE --instruction TEXT --approve-work --allowed-path PATH
|
|
16
|
-
(--preserve-worktree | --remove-worktree)
|
|
17
|
-
[--project DIR] [--host-agent claude|codex|automation|unknown]
|
|
18
|
-
[--dry-run] [--turn-timeout-ms MS] [--out FILE] [--no-save]
|
|
19
|
-
`;
|
|
20
|
-
|
|
21
|
-
const CLI_WORKFLOWS = {
|
|
22
|
-
'risk-check': 'codex_risk_check',
|
|
23
|
-
review: 'codex_review',
|
|
24
|
-
explore: 'codex_explore',
|
|
25
|
-
opinion: 'codex_opinion',
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export async function runCodexCommand({ argv = process.argv.slice(2) } = {}) {
|
|
29
|
-
const sub = argv[0];
|
|
30
|
-
if (!sub || sub === '--help' || sub === '-h') {
|
|
31
|
-
process.stdout.write(CODEX_USAGE);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
if (sub === 'work') {
|
|
35
|
-
await runCodexWorkCommand({ argv: argv.slice(1) });
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
const workflow = CLI_WORKFLOWS[sub];
|
|
39
|
-
if (workflow) {
|
|
40
|
-
await runCodexReadOnlyWorkflowCommand({ argv: argv.slice(1), workflow, commandName: sub });
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
process.stderr.write(`unknown codex subcommand: ${sub}\n${CODEX_USAGE}`);
|
|
44
|
-
process.exit(2);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export async function runCodexWorkCommand({
|
|
48
|
-
argv = [],
|
|
49
|
-
writeOutput = (text) => process.stdout.write(text),
|
|
50
|
-
writeError = (text) => process.stderr.write(text),
|
|
51
|
-
runCodexWorkFn = runCodexWork,
|
|
52
|
-
readFindingsJsonFn = readFindingsJson,
|
|
53
|
-
} = {}) {
|
|
54
|
-
if (argv.includes('--help') || argv.includes('-h')) {
|
|
55
|
-
writeOutput(CODEX_USAGE);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
const opts = parseWorkArgs(argv);
|
|
59
|
-
const missing = [];
|
|
60
|
-
if (!opts.findingsPath) missing.push('--findings FILE');
|
|
61
|
-
if (!opts.instruction) missing.push('--instruction TEXT');
|
|
62
|
-
if (!opts.approved) missing.push('--approve-work');
|
|
63
|
-
if (opts.allowedPaths.length === 0) missing.push('--allowed-path PATH');
|
|
64
|
-
if (!opts.cleanup) missing.push('--preserve-worktree or --remove-worktree');
|
|
65
|
-
if (missing.length > 0) {
|
|
66
|
-
writeError(`missing required codex work option(s): ${missing.join(', ')}\n${CODEX_USAGE}`);
|
|
67
|
-
process.exit(2);
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
const findings = await readFindingsJsonFn(opts.findingsPath);
|
|
71
|
-
const record = await runCodexWorkFn({
|
|
72
|
-
projectRoot: opts.projectRoot,
|
|
73
|
-
findings,
|
|
74
|
-
instruction: opts.instruction,
|
|
75
|
-
hostAgent: opts.hostAgent,
|
|
76
|
-
approved: opts.approved,
|
|
77
|
-
allowedPaths: opts.allowedPaths,
|
|
78
|
-
cleanup: opts.cleanup,
|
|
79
|
-
dryRun: opts.dryRun,
|
|
80
|
-
turnTimeoutMs: opts.turnTimeoutMs,
|
|
81
|
-
save: opts.save,
|
|
82
|
-
outPath: opts.outPath,
|
|
83
|
-
});
|
|
84
|
-
writeOutput(JSON.stringify(record, null, 2) + '\n');
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export async function runCodexReadOnlyWorkflowCommand({
|
|
88
|
-
argv = [],
|
|
89
|
-
workflow,
|
|
90
|
-
commandName,
|
|
91
|
-
writeOutput = (text) => process.stdout.write(text),
|
|
92
|
-
writeError = (text) => process.stderr.write(text),
|
|
93
|
-
runCodexReadOnlyWorkflowFn = runCodexReadOnlyWorkflow,
|
|
94
|
-
readFindingsJsonFn = readFindingsJson,
|
|
95
|
-
} = {}) {
|
|
96
|
-
if (argv.includes('--help') || argv.includes('-h')) {
|
|
97
|
-
writeOutput(CODEX_USAGE);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
const opts = parseReadOnlyArgs(argv, commandName);
|
|
101
|
-
if (!opts.findingsPath) {
|
|
102
|
-
writeError(`missing required --findings FILE\n${CODEX_USAGE}`);
|
|
103
|
-
process.exit(2);
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
const findings = await readFindingsJsonFn(opts.findingsPath);
|
|
107
|
-
const record = await runCodexReadOnlyWorkflowFn({
|
|
108
|
-
workflow,
|
|
109
|
-
projectRoot: opts.projectRoot,
|
|
110
|
-
findings,
|
|
111
|
-
hostAgent: opts.hostAgent,
|
|
112
|
-
dryRun: opts.dryRun,
|
|
113
|
-
turnTimeoutMs: opts.turnTimeoutMs,
|
|
114
|
-
save: opts.save,
|
|
115
|
-
outPath: opts.outPath,
|
|
116
|
-
});
|
|
117
|
-
writeOutput(JSON.stringify(record, null, 2) + '\n');
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function parseReadOnlyArgs(argv, commandName) {
|
|
121
|
-
const opts = {
|
|
122
|
-
projectRoot: process.cwd(),
|
|
123
|
-
hostAgent: null,
|
|
124
|
-
findingsPath: null,
|
|
125
|
-
dryRun: false,
|
|
126
|
-
turnTimeoutMs: null,
|
|
127
|
-
save: true,
|
|
128
|
-
outPath: null,
|
|
129
|
-
};
|
|
130
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
131
|
-
const arg = argv[index];
|
|
132
|
-
if (arg === '--findings') {
|
|
133
|
-
opts.findingsPath = resolve(requireValue(argv, (index += 1), '--findings'));
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
if (arg === '--project') {
|
|
137
|
-
opts.projectRoot = resolve(requireValue(argv, (index += 1), '--project'));
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
if (arg === '--host-agent') {
|
|
141
|
-
opts.hostAgent = requireValue(argv, (index += 1), '--host-agent');
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
if (arg === '--dry-run') {
|
|
145
|
-
opts.dryRun = true;
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
if (arg === '--turn-timeout-ms') {
|
|
149
|
-
opts.turnTimeoutMs = parsePositiveInteger(requireValue(argv, (index += 1), '--turn-timeout-ms'), '--turn-timeout-ms');
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
if (arg === '--out') {
|
|
153
|
-
opts.outPath = resolve(requireValue(argv, (index += 1), '--out'));
|
|
154
|
-
opts.save = true;
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
if (arg === '--no-save') {
|
|
158
|
-
opts.save = false;
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
process.stderr.write(`unknown codex ${commandName} option: ${arg}\n${CODEX_USAGE}`);
|
|
162
|
-
process.exit(2);
|
|
163
|
-
return opts;
|
|
164
|
-
}
|
|
165
|
-
return opts;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function parseWorkArgs(argv) {
|
|
169
|
-
const opts = {
|
|
170
|
-
projectRoot: process.cwd(),
|
|
171
|
-
hostAgent: null,
|
|
172
|
-
findingsPath: null,
|
|
173
|
-
instruction: null,
|
|
174
|
-
approved: false,
|
|
175
|
-
allowedPaths: [],
|
|
176
|
-
cleanup: null,
|
|
177
|
-
dryRun: false,
|
|
178
|
-
turnTimeoutMs: null,
|
|
179
|
-
save: true,
|
|
180
|
-
outPath: null,
|
|
181
|
-
};
|
|
182
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
183
|
-
const arg = argv[index];
|
|
184
|
-
if (arg === '--findings') {
|
|
185
|
-
opts.findingsPath = resolve(requireValue(argv, (index += 1), '--findings'));
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
if (arg === '--project') {
|
|
189
|
-
opts.projectRoot = resolve(requireValue(argv, (index += 1), '--project'));
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
if (arg === '--host-agent') {
|
|
193
|
-
opts.hostAgent = requireValue(argv, (index += 1), '--host-agent');
|
|
194
|
-
continue;
|
|
195
|
-
}
|
|
196
|
-
if (arg === '--instruction') {
|
|
197
|
-
opts.instruction = requireValue(argv, (index += 1), '--instruction');
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
if (arg === '--approve-work') {
|
|
201
|
-
opts.approved = true;
|
|
202
|
-
continue;
|
|
203
|
-
}
|
|
204
|
-
if (arg === '--allowed-path') {
|
|
205
|
-
opts.allowedPaths.push(requireValue(argv, (index += 1), '--allowed-path'));
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
if (arg === '--preserve-worktree') {
|
|
209
|
-
opts.cleanup = 'preserve';
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
if (arg === '--remove-worktree') {
|
|
213
|
-
opts.cleanup = 'remove';
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
if (arg === '--dry-run') {
|
|
217
|
-
opts.dryRun = true;
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
if (arg === '--turn-timeout-ms') {
|
|
221
|
-
opts.turnTimeoutMs = parsePositiveInteger(requireValue(argv, (index += 1), '--turn-timeout-ms'), '--turn-timeout-ms');
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
if (arg === '--out') {
|
|
225
|
-
opts.outPath = resolve(requireValue(argv, (index += 1), '--out'));
|
|
226
|
-
opts.save = true;
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
if (arg === '--no-save') {
|
|
230
|
-
opts.save = false;
|
|
231
|
-
continue;
|
|
232
|
-
}
|
|
233
|
-
process.stderr.write(`unknown codex work option: ${arg}\n${CODEX_USAGE}`);
|
|
234
|
-
process.exit(2);
|
|
235
|
-
return opts;
|
|
236
|
-
}
|
|
237
|
-
return opts;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function requireValue(argv, index, option) {
|
|
241
|
-
const value = argv[index];
|
|
242
|
-
if (!value || value.startsWith('--')) {
|
|
243
|
-
throw Object.assign(new Error(`${option} requires a value`), { exitCode: 2 });
|
|
244
|
-
}
|
|
245
|
-
return value;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function parsePositiveInteger(value, option) {
|
|
249
|
-
const parsed = Number(value);
|
|
250
|
-
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
251
|
-
throw Object.assign(new Error(`${option} must be a positive integer`), { exitCode: 2 });
|
|
252
|
-
}
|
|
253
|
-
return parsed;
|
|
254
|
-
}
|