@lyupro/codex-bridge 0.6.5 → 0.6.7
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 +60 -0
- package/README.md +5 -5
- package/bin/codex-bridge.mjs +181 -178
- package/cli/dispatcher-contract-record.mjs +42 -0
- package/cli/dispatcher-contract.mjs +89 -0
- package/cli/doctor-hooks.mjs +104 -114
- package/cli/doctor-runs.mjs +55 -55
- package/cli/doctor.mjs +39 -3
- package/cli/handback-witness-check.mjs +39 -0
- package/cli/hook-targets.mjs +14 -6
- package/cli/hook.mjs +32 -23
- package/cli/host-contract.mjs +136 -135
- package/cli/install.mjs +9 -14
- package/cli/launcher-probe.mjs +38 -0
- package/cli/probe-contract.mjs +31 -62
- package/cli/probe-rig.mjs +53 -0
- package/cli/run-launcher.mjs +17 -0
- package/cli/settings-merge.mjs +42 -35
- package/cli/uninstall.mjs +15 -3
- package/cli/update.mjs +45 -16
- package/package.json +4 -4
- package/src/agents/codex-advisor.md +3 -4
- package/src/agents/codex-build.md +5 -5
- package/src/agents/codex-review.md +3 -3
- package/src/agents/codex-scout.md +3 -4
- package/src/home/hooks/dispatcher-gate.mjs +119 -0
- package/src/home/hooks/guard-tries.mjs +5 -6
- package/src/home/hooks/live-runs.mjs +1 -0
- package/src/home/hooks/order-gate.mjs +20 -5
- package/src/home/hooks/reply-guard.mjs +75 -10
- package/src/home/hooks/reply-verdicts.mjs +5 -0
- package/src/home/hooks/worktree-lock.mjs +108 -107
- package/src/home/lib/atomic-json.mjs +21 -0
- package/src/home/lib/brand-home.mjs +6 -0
- package/src/home/lib/dispatcher-command.mjs +79 -0
- package/src/home/lib/dispatcher-gate.mjs +95 -0
- package/src/home/lib/dispatcher-order.mjs +30 -8
- package/src/home/lib/dispatcher-state.mjs +111 -0
- package/src/home/lib/dispatcher-stop.mjs +52 -0
- package/src/home/lib/handback-witness.mjs +65 -0
- package/src/home/lib/hook-definitions.mjs +10 -0
- package/src/home/lib/hook-entry.mjs +20 -0
- package/src/home/lib/meta/advice-status.mjs +3 -7
- package/src/home/lib/meta/advice-verdict.mjs +37 -21
- package/src/home/lib/required-inputs.mjs +1 -1
- package/src/home/lib/runner/advise-carry.mjs +50 -0
- package/src/home/lib/runner/attach.mjs +11 -7
- package/src/home/lib/runner/codex-cmd.mjs +3 -2
- package/src/home/lib/runner/continuation.mjs +3 -1
- package/src/home/lib/runner/git-state.mjs +1 -1
- package/src/home/lib/runner/launcher.mjs +14 -13
- package/src/home/lib/runner/preflight.mjs +6 -3
- package/src/home/lib/runner/prompts.mjs +9 -8
- package/src/home/lib/runner/schemas.mjs +33 -7
|
@@ -27,7 +27,7 @@ import { parseArgs, die } from './args.mjs';
|
|
|
27
27
|
import { settleTaskInput } from './task-input.mjs';
|
|
28
28
|
import { parseContinuationGrant } from '../required-inputs.mjs';
|
|
29
29
|
import { continuationRefusal } from './continuation.mjs';
|
|
30
|
-
import {
|
|
30
|
+
import { advisorTaskOrRefuse, adviseSection } from './advise-carry.mjs';
|
|
31
31
|
import { schemaFor } from './schemas.mjs';
|
|
32
32
|
import { INSTRUCTIONS } from './prompts.mjs';
|
|
33
33
|
import { git, headSha, branchName, worktreeSnapshot, reviewScope } from './git-state.mjs';
|
|
@@ -146,12 +146,18 @@ export async function launcher(argv = process.argv.slice(2)) {
|
|
|
146
146
|
const chain = chainRuns(projectRunsRoot, repoRoot, opts.slug, taskHash, opts.orderId);
|
|
147
147
|
const startedChain = startedRuns(projectRunsRoot, chain);
|
|
148
148
|
const continuationGrant = parseContinuationGrant(taskText);
|
|
149
|
+
const attachExistingRun = () => attach({
|
|
150
|
+
runsRoot: projectRunsRoot, repo: repoRoot, slug: opts.slug, taskHash, orderId: opts.orderId, chain,
|
|
151
|
+
grantRun: continuationGrant?.run, isContinue: opts.continue, noWait: opts.noWait,
|
|
152
|
+
});
|
|
153
|
+
let attachedExitCode = opts.continue ? await attachExistingRun() : null;
|
|
154
|
+
if (attachedExitCode !== null) return attachedExitCode;
|
|
149
155
|
|
|
150
156
|
// The 2026-08-10_220535_plan25-2-install-table-two-roots incident exposed this ordering:
|
|
151
157
|
// a grant without its flag must refuse before attach can print an older run's verdict.
|
|
152
158
|
// markAbandoned() ran before this gate, so a dead runner already has meta.json with FAIL.
|
|
153
159
|
// A run without a verdict can therefore only still be in flight; ordinary repeats go through
|
|
154
|
-
// attach()
|
|
160
|
+
// attach() after this gate. Repeated --continue calls attach first because their grant is spent.
|
|
155
161
|
const continuationError = continuationRefusal(
|
|
156
162
|
projectRunsRoot,
|
|
157
163
|
startedChain,
|
|
@@ -164,20 +170,13 @@ export async function launcher(argv = process.argv.slice(2)) {
|
|
|
164
170
|
const scopeRun = continuationGrant &&
|
|
165
171
|
scopeRunRefusal({ agent: opts.agent, phase: opts.phase, runsRoot: projectRunsRoot, grantRun: continuationGrant.run });
|
|
166
172
|
if (scopeRun) die(scopeRun, 1);
|
|
173
|
+
// Plan_59 D22: the advise snapshot is read before the folder exists, so a broken scope result refuses for free.
|
|
174
|
+
const advisorTask = opts.agent === 'codex-advisor' ? advisorTaskOrRefuse({ taskText, phase: opts.phase, runsRoot: projectRunsRoot, grantRun: continuationGrant?.run }) : null;
|
|
167
175
|
|
|
168
176
|
// One order produced six Codex runs on 2026-08-03 because the caller's time ceiling made it
|
|
169
177
|
// restart the synchronous launcher. A live same-order run is now the repeat target: attach
|
|
170
178
|
// before creating a folder, probing Codex or spending another token.
|
|
171
|
-
|
|
172
|
-
runsRoot: projectRunsRoot,
|
|
173
|
-
repo: repoRoot,
|
|
174
|
-
slug: opts.slug,
|
|
175
|
-
taskHash,
|
|
176
|
-
orderId: opts.orderId,
|
|
177
|
-
chain,
|
|
178
|
-
isContinue: opts.continue,
|
|
179
|
-
noWait: opts.noWait,
|
|
180
|
-
});
|
|
179
|
+
if (!opts.continue) attachedExitCode = await attachExistingRun();
|
|
181
180
|
if (attachedExitCode !== null) return attachedExitCode;
|
|
182
181
|
|
|
183
182
|
if (startedChain.length && !opts.continue) {
|
|
@@ -274,7 +273,7 @@ export async function launcher(argv = process.argv.slice(2)) {
|
|
|
274
273
|
if (opts.agent === 'codex-scout') {
|
|
275
274
|
fs.writeFileSync(path.join(runDir, 'questions.json'), `${JSON.stringify(questions, null, 2)}\n`);
|
|
276
275
|
}
|
|
277
|
-
if (
|
|
276
|
+
if (advisorTask) fs.writeFileSync(path.join(runDir, 'advisor-task.json'), `${JSON.stringify(advisorTask, null, 2)}\n`);
|
|
278
277
|
|
|
279
278
|
if (opts.agent === 'codex-build') {
|
|
280
279
|
fs.writeFileSync(path.join(runDir, 'scope.txt'), `${opts.scopePatterns.join('\n')}\n`);
|
|
@@ -289,6 +288,8 @@ export async function launcher(argv = process.argv.slice(2)) {
|
|
|
289
288
|
// and what may be edited. Both also go to disk as questions.json / scope.txt, so the verdict
|
|
290
289
|
// is computed from the same list Codex was handed, not from a second reading of the wording.
|
|
291
290
|
const sections = [`## Operator task (verbatim)\n\n${taskText}`];
|
|
291
|
+
const scopeResults = adviseSection(advisorTask);
|
|
292
|
+
if (scopeResults) sections.push(scopeResults);
|
|
292
293
|
if (questions.length) {
|
|
293
294
|
sections.push(
|
|
294
295
|
[
|
|
@@ -33,8 +33,10 @@ export function taskPreflight({ agent, taskText }) {
|
|
|
33
33
|
}
|
|
34
34
|
if (path.isAbsolute(advice)) {
|
|
35
35
|
try {
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
// Plan_59 D26: only a judged advice authorizes a construction. On 2026-09-24 the gate took
|
|
37
|
+
// any codex-advisor folder, so a scope run or a FAIL advice would have passed as a second opinion.
|
|
38
|
+
const meta = fs.statSync(advice).isDirectory() ? readJson(path.join(advice, 'meta.json')) : null;
|
|
39
|
+
if (meta?.agent === 'codex-advisor' && meta.phase === 'advise' && meta.status === 'OK') {
|
|
38
40
|
return { refusal: null, advice };
|
|
39
41
|
}
|
|
40
42
|
} catch {
|
|
@@ -43,7 +45,8 @@ export function taskPreflight({ agent, taskText }) {
|
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
47
|
return freeRefusal('codex-build requires exactly one advice: line: mechanical | revert | docs-only | test-only ' +
|
|
46
|
-
'or an absolute path to an existing advisor run directory
|
|
48
|
+
'or an absolute path to an existing advisor run directory whose meta.json says agent codex-advisor, ' +
|
|
49
|
+
'phase advise, status OK. ' +
|
|
47
50
|
'An order that invents a construction needs a second opinion first; only work with no design choice may skip it.');
|
|
48
51
|
}
|
|
49
52
|
|
|
@@ -16,10 +16,11 @@ const BODIES = {
|
|
|
16
16
|
return `Judge a DESIGN before code exists.
|
|
17
17
|
|
|
18
18
|
Rules for both phases:
|
|
19
|
-
- Read ONLY the paths listed under \`## Paths\`, plus \`git log\` / \`git show\` on those paths.
|
|
19
|
+
- Read ONLY the paths listed under \`## Paths\` and, in the advise phase, under \`## Scope phase results\`, plus \`git log\` / \`git show\` on those paths.
|
|
20
20
|
- Write nothing. Do your own reading and checks; do not spawn or delegate to other agents.
|
|
21
|
-
-
|
|
22
|
-
|
|
21
|
+
- Prose fields cite code as \`path:line\`; every \`evidence\` field is a list of
|
|
22
|
+
\`{file, line_start, line_end}\` objects, ONE location per object. Several places mean several
|
|
23
|
+
objects, never a list inside \`file\`. Locations are machine-checked for existence.
|
|
23
24
|
- Do not guess which option the caller prefers: the task carries no preference on purpose.
|
|
24
25
|
- Two failure modes are forbidden: agreeing without independent checks ("both options are good",
|
|
25
26
|
"you know better"), and disagreeing to look independent. A disagreement without an address is
|
|
@@ -32,18 +33,18 @@ Scope phase:
|
|
|
32
33
|
(facts accepted from the task without checking, never empty), together with predicted_risks.
|
|
33
34
|
` : `
|
|
34
35
|
Advise phase:
|
|
35
|
-
- Settle every predicted risk
|
|
36
|
-
confirmed or refuted,
|
|
36
|
+
- Settle every predicted risk listed under \`## Scope phase results\` in risk_outcomes with its
|
|
37
|
+
risk_id, an outcome of confirmed or refuted, a \`note\` explaining it, and evidence.
|
|
37
38
|
- Give ONE recommendation by task option_id, or none-of-these with a described unlisted_option.
|
|
38
39
|
Put every rejected task option in rejected with its cost; for none-of-these this means EVERY
|
|
39
40
|
task option. Set unlisted_option to "none" when recommending a listed option.
|
|
40
41
|
- Each field is a distinct lens: strongest_counterargument comes from a skeptic;
|
|
41
42
|
pre_mortem comes from the executor who has only this advice and the repository. Give each
|
|
42
|
-
scenario an early_check of kind test | command | inspect with a target; inspect requires
|
|
43
|
+
scenario an early_check of kind test | command | inspect with a target; inspect requires evidence.
|
|
43
44
|
- question_defect comes from the requester who needs the problem solved, not an option picked.
|
|
44
|
-
- Rate assumptions VERIFIED (requires
|
|
45
|
+
- Rate assumptions VERIFIED (requires evidence) | REASONABLE | FRAGILE. Anything the requester
|
|
45
46
|
could refute with knowledge you lack belongs in open_questions, not why.
|
|
46
|
-
- independent_checks lists what you verified yourself, with
|
|
47
|
+
- independent_checks lists what you verified yourself, with evidence. Ground why in those checks
|
|
47
48
|
and state confidence honestly.
|
|
48
49
|
`}`;
|
|
49
50
|
},
|
|
@@ -150,6 +150,30 @@ const REVIEW_SCHEMA = {
|
|
|
150
150
|
},
|
|
151
151
|
};
|
|
152
152
|
|
|
153
|
+
// The 2026-09-24_094352_plan62-g1-dispatcher-gate run folded 3-5 citations into one advisor
|
|
154
|
+
// address; like the 2026-07-31 build changes[].file precedent, constrain a single location here.
|
|
155
|
+
export const EVIDENCE_LOCATION = {
|
|
156
|
+
type: 'object',
|
|
157
|
+
additionalProperties: false,
|
|
158
|
+
required: ['file', 'line_start', 'line_end'],
|
|
159
|
+
properties: {
|
|
160
|
+
file: {
|
|
161
|
+
type: 'string',
|
|
162
|
+
minLength: 1,
|
|
163
|
+
pattern: '^[^:;,*?{}\\r\\n]+$',
|
|
164
|
+
description: 'Exactly one repository-root-relative path. Spaces are allowed; lists, globs, drive letters and line suffixes are not — use a separate location object for each place.',
|
|
165
|
+
},
|
|
166
|
+
line_start: { type: 'integer', minimum: 1 },
|
|
167
|
+
line_end: { type: 'integer', minimum: 1 },
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const EVIDENCE_LIST = (minItems) => ({
|
|
172
|
+
type: 'array',
|
|
173
|
+
...(minItems === undefined ? {} : { minItems }),
|
|
174
|
+
items: EVIDENCE_LOCATION,
|
|
175
|
+
});
|
|
176
|
+
|
|
153
177
|
/** Plan_59: execution schemas stay static; schemaFor selects the advisor contract by phase. */
|
|
154
178
|
export const SCHEMAS = {
|
|
155
179
|
'codex-scout': SCOUT_SCHEMA,
|
|
@@ -220,11 +244,11 @@ export const PHASE_SCHEMAS = {
|
|
|
220
244
|
items: {
|
|
221
245
|
type: 'object',
|
|
222
246
|
additionalProperties: false,
|
|
223
|
-
required: ['claim', 'rating', '
|
|
247
|
+
required: ['claim', 'rating', 'evidence'],
|
|
224
248
|
properties: {
|
|
225
249
|
claim: { type: 'string' },
|
|
226
250
|
rating: { type: 'string', enum: ['VERIFIED', 'REASONABLE', 'FRAGILE'] },
|
|
227
|
-
|
|
251
|
+
evidence: EVIDENCE_LIST(),
|
|
228
252
|
},
|
|
229
253
|
},
|
|
230
254
|
},
|
|
@@ -233,11 +257,12 @@ export const PHASE_SCHEMAS = {
|
|
|
233
257
|
items: {
|
|
234
258
|
type: 'object',
|
|
235
259
|
additionalProperties: false,
|
|
236
|
-
required: ['risk_id', 'outcome', '
|
|
260
|
+
required: ['risk_id', 'outcome', 'note', 'evidence'],
|
|
237
261
|
properties: {
|
|
238
262
|
risk_id: { type: 'string' },
|
|
239
263
|
outcome: { type: 'string', enum: ['confirmed', 'refuted'] },
|
|
240
|
-
|
|
264
|
+
note: { type: 'string' },
|
|
265
|
+
evidence: EVIDENCE_LIST(1),
|
|
241
266
|
},
|
|
242
267
|
},
|
|
243
268
|
},
|
|
@@ -254,10 +279,11 @@ export const PHASE_SCHEMAS = {
|
|
|
254
279
|
early_check: {
|
|
255
280
|
type: 'object',
|
|
256
281
|
additionalProperties: false,
|
|
257
|
-
required: ['kind', 'target'],
|
|
282
|
+
required: ['kind', 'target', 'evidence'],
|
|
258
283
|
properties: {
|
|
259
284
|
kind: { type: 'string', enum: ['test', 'command', 'inspect'] },
|
|
260
285
|
target: { type: 'string', minLength: 3 },
|
|
286
|
+
evidence: EVIDENCE_LIST(),
|
|
261
287
|
},
|
|
262
288
|
},
|
|
263
289
|
},
|
|
@@ -271,8 +297,8 @@ export const PHASE_SCHEMAS = {
|
|
|
271
297
|
items: {
|
|
272
298
|
type: 'object',
|
|
273
299
|
additionalProperties: false,
|
|
274
|
-
required: ['check', '
|
|
275
|
-
properties: { check: { type: 'string' },
|
|
300
|
+
required: ['check', 'evidence'],
|
|
301
|
+
properties: { check: { type: 'string' }, evidence: EVIDENCE_LIST(1) },
|
|
276
302
|
},
|
|
277
303
|
},
|
|
278
304
|
},
|