vigiles 29.1.0 → 30.0.1
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/dist/adapter-conformance.d.ts +1 -1
- package/dist/adapter-conformance.js +106 -25
- package/dist/adapter-registry.d.ts +61 -14
- package/dist/adapter-registry.js +78 -10
- package/dist/adapter.d.ts +23 -2
- package/dist/adapter.js +13 -1
- package/dist/adapters/claude-code/adapter.d.ts +32 -2
- package/dist/adapters/claude-code/adapter.js +44 -23
- package/dist/adapters/claude-code/agent-runtime.js +3 -1
- package/dist/adapters/claude-code/dialect.js +87 -21
- package/dist/adapters/claude-code/effect-region.js +3 -1
- package/dist/adapters/claude-code/hook-protocol.js +16 -0
- package/dist/adapters/claude-code/instruction-chain.d.ts +25 -0
- package/dist/adapters/claude-code/instruction-chain.js +626 -0
- package/dist/adapters/claude-code/layout.d.ts +2 -2
- package/dist/adapters/claude-code/layout.js +42 -8
- package/dist/adapters/claude-code/model-access.d.ts +41 -0
- package/dist/adapters/claude-code/model-access.js +46 -0
- package/dist/adapters/claude-code/skill-reachability.d.ts +125 -0
- package/dist/adapters/claude-code/skill-reachability.js +111 -0
- package/dist/adapters/claude-code/skill-runtime.js +3 -1
- package/dist/adapters/codex/adapter.d.ts +39 -2
- package/dist/adapters/codex/adapter.js +29 -29
- package/dist/adapters/codex/dialect.js +11 -6
- package/dist/adapters/codex/eval.d.ts +10 -0
- package/dist/adapters/codex/eval.js +48 -1
- package/dist/adapters/codex/hook-protocol.d.ts +2 -1
- package/dist/adapters/codex/hook-protocol.js +10 -0
- package/dist/adapters/codex/instruction-chain.d.ts +40 -0
- package/dist/adapters/codex/instruction-chain.js +105 -0
- package/dist/adapters/codex/layout.d.ts +1 -1
- package/dist/adapters/codex/layout.js +41 -14
- package/dist/adapters/opencode/adapter.d.ts +33 -2
- package/dist/adapters/opencode/adapter.js +36 -36
- package/dist/adapters/opencode/dialect.js +2 -2
- package/dist/adapters/opencode/instruction-chain.d.ts +37 -0
- package/dist/adapters/opencode/instruction-chain.js +70 -0
- package/dist/adapters/opencode/layout.d.ts +19 -0
- package/dist/adapters/opencode/layout.js +34 -15
- package/dist/adoptability.d.ts +31 -1
- package/dist/adoptability.js +57 -0
- package/dist/cli-main.js +185 -102
- package/dist/core/adapter.d.ts +213 -61
- package/dist/core/compile.d.ts +2 -2
- package/dist/core/compile.js +57 -46
- package/dist/core/compose.d.ts +5 -3
- package/dist/core/compose.js +5 -3
- package/dist/core/config-schema.d.ts +14 -2
- package/dist/core/config-schema.js +20 -7
- package/dist/core/dialect.d.ts +54 -12
- package/dist/core/dialect.js +56 -0
- package/dist/core/eval-driver.d.ts +194 -0
- package/dist/core/eval-driver.js +3 -0
- package/dist/core/frontmatter-read.d.ts +10 -0
- package/dist/core/frontmatter-read.js +30 -3
- package/dist/core/guards.js +3 -1
- package/dist/core/hook-program.d.ts +27 -2
- package/dist/core/hook-program.js +29 -24
- package/dist/core/hook-protocol.d.ts +54 -0
- package/dist/core/install-reader.d.ts +18 -0
- package/dist/core/install-reader.js +88 -0
- package/dist/core/instruction-chain.d.ts +444 -0
- package/dist/core/instruction-chain.js +292 -0
- package/dist/core/instruction-weight.d.ts +96 -14
- package/dist/core/instruction-weight.js +65 -30
- package/dist/core/layout.d.ts +220 -33
- package/dist/core/layout.js +115 -1
- package/dist/core/lethal-trifecta.d.ts +12 -7
- package/dist/core/lethal-trifecta.js +13 -13
- package/dist/core/live-driver.d.ts +137 -0
- package/dist/core/live-driver.js +14 -0
- package/dist/core/markdown.d.ts +23 -0
- package/dist/core/markdown.js +77 -28
- package/dist/core/orphans.js +9 -7
- package/dist/core/settings-codec.d.ts +17 -0
- package/dist/core/settings-codec.js +56 -0
- package/dist/core/surface-discovery.d.ts +2 -2
- package/dist/core/surface-discovery.js +24 -12
- package/dist/core/surface-scopes.d.ts +26 -6
- package/dist/core/surface-scopes.js +52 -11
- package/dist/core/validate.js +16 -3
- package/dist/coverage-artifact.d.ts +3 -2
- package/dist/coverage-artifact.js +6 -5
- package/dist/eval-cache.d.ts +6 -1
- package/dist/eval-cache.js +11 -1
- package/dist/eval.d.ts +16 -108
- package/dist/eval.js +36 -2
- package/dist/harness-test.d.ts +3 -63
- package/dist/hook-install.d.ts +12 -1
- package/dist/hook-install.js +12 -1
- package/dist/hook-runtime.js +4 -2
- package/dist/hook-state-store.js +3 -1
- package/dist/local-files-tracked.d.ts +17 -0
- package/dist/local-files-tracked.js +70 -0
- package/dist/local-files.d.ts +62 -0
- package/dist/local-files.js +183 -0
- package/dist/observe.d.ts +3 -2
- package/dist/observe.js +7 -6
- package/dist/plugin-loader.d.ts +1 -1
- package/dist/plugin-loader.js +43 -36
- package/dist/scan-behavioral.d.ts +34 -25
- package/dist/scan-behavioral.js +122 -58
- package/dist/scan-core.js +37 -18
- package/dist/scan-files.d.ts +1 -1
- package/dist/scan-files.js +53 -33
- package/dist/scan-trigger-suggest.d.ts +0 -21
- package/dist/scan-trigger-suggest.js +0 -23
- package/dist/scan.d.ts +4 -4
- package/dist/scan.js +120 -84
- package/dist/skill-harness.d.ts +21 -5
- package/dist/skill-harness.js +29 -11
- package/dist/surface-discovery-fs.d.ts +2 -0
- package/dist/surface-discovery-fs.js +108 -6
- package/dist/test-coverage-files.js +24 -17
- package/dist/test-coverage.d.ts +9 -3
- package/dist/test-coverage.js +32 -22
- package/dist/verify-plugin-guards.js +1 -1
- package/package.json +1 -1
- package/dist/skill-reachability.d.ts +0 -68
- package/dist/skill-reachability.js +0 -205
- /package/dist/{dialect-drift.d.ts → adapters/claude-code/dialect-drift.d.ts} +0 -0
- /package/dist/{dialect-drift.js → adapters/claude-code/dialect-drift.js} +0 -0
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* not be re-read); pair with a behavioral/judged check for certainty.
|
|
28
28
|
*/
|
|
29
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
-
exports.codexEvalDriver = exports.CODEX_TRIGGER_RATE_EXPERIMENTAL = void 0;
|
|
30
|
+
exports.codexLiveDriver = exports.codexEvalDriver = exports.CODEX_TRIGGER_RATE_EXPERIMENTAL = void 0;
|
|
31
31
|
exports.parseCodexEvalRun = parseCodexEvalRun;
|
|
32
32
|
exports.codexRunError = codexRunError;
|
|
33
33
|
exports.codexSkillFired = codexSkillFired;
|
|
@@ -244,6 +244,53 @@ exports.codexEvalDriver = {
|
|
|
244
244
|
// Codex-only: the trigger-rate number is not validated (see the constant above).
|
|
245
245
|
experimental: exports.CODEX_TRIGGER_RATE_EXPERIMENTAL,
|
|
246
246
|
};
|
|
247
|
+
/**
|
|
248
|
+
* The Codex {@link HarnessLiveDriver} — the EXECUTING tiers' side of the
|
|
249
|
+
* adapter, reached through `codexAdapter.liveDriver()`.
|
|
250
|
+
*
|
|
251
|
+
* It is the object `scan-behavioral.ts:buildProbe` used to build from
|
|
252
|
+
* `harness === "codex"`: the same four answers, now carried by the adapter that
|
|
253
|
+
* knows them instead of switched on by a name in the application layer.
|
|
254
|
+
*/
|
|
255
|
+
exports.codexLiveDriver = {
|
|
256
|
+
evalDriver: exports.codexEvalDriver,
|
|
257
|
+
// 🔴 ANSWERED WITHOUT TOUCHING THE MACHINE, and that is a CONSTRAINT of the
|
|
258
|
+
// caller rather than a property of this harness. `access` is read on the
|
|
259
|
+
// AUDIT path, before `decideExecute` and `resolveExecution` have established
|
|
260
|
+
// consent — including `--json`, `--no-interactive` and a remembered "no" —
|
|
261
|
+
// and that path promises to execute nothing. A first draft probed the binary
|
|
262
|
+
// with `codexDriver.available()`, which spawns `codex --version`; harmless in
|
|
263
|
+
// itself, and still a process this run had no permission to start.
|
|
264
|
+
//
|
|
265
|
+
// Claude Code's `access` reads env only, so with this one the guarantee stops
|
|
266
|
+
// being a property of whichever adapter happens to be driving and becomes
|
|
267
|
+
// structural: NO adapter executes anything to answer it.
|
|
268
|
+
//
|
|
269
|
+
// What that costs, stated rather than hidden: a machine with no codex binary
|
|
270
|
+
// is told the tier is reachable and finds out at RUN time instead, where the
|
|
271
|
+
// probe self-reports unavailable and the tier reports a miss. That is exactly
|
|
272
|
+
// what shipped before the port existed (`adapter.name === "codex"` was true
|
|
273
|
+
// with no probe at all), so this is not a regression — it is the old answer
|
|
274
|
+
// with the reason written down. The remedy string that used to ride on
|
|
275
|
+
// `{ kind: "none", fix }` is deleted rather than parked: it had no reader
|
|
276
|
+
// left, and an exported constant nothing prints is a claim, not a feature.
|
|
277
|
+
//
|
|
278
|
+
// "Subscription" rather than "metered": the codex CLI carries its own auth
|
|
279
|
+
// (a ChatGPT plan or an API key) and nothing outside a run can tell which,
|
|
280
|
+
// so the CLI words this harness $0 metered, as it always has.
|
|
281
|
+
access: () => ({ kind: "subscription" }),
|
|
282
|
+
// NO skill-selection event exists here, so firing is INFERRED from the
|
|
283
|
+
// SKILL.md read — wrong in both directions (see the constant above). The
|
|
284
|
+
// caveat travels with the signal so a report can never print the number bare,
|
|
285
|
+
// and the selection-collision matrix refuses this driver at the type level.
|
|
286
|
+
firing: { kind: "inferred", caveat: exports.CODEX_TRIGGER_RATE_EXPERIMENTAL },
|
|
287
|
+
// No namespace: firing is the SKILL.md read, which carries the bare name.
|
|
288
|
+
firedFor: (skill) => (t) => codexSkillFired(t, skill),
|
|
289
|
+
// A MEASURED LIMITATION, not a capability: stubbing a Claude-shaped plugin
|
|
290
|
+
// for Codex is unvalidated, so the real skills are installed and firing is
|
|
291
|
+
// detected regardless of body.
|
|
292
|
+
installsStubs: false,
|
|
293
|
+
};
|
|
247
294
|
/**
|
|
248
295
|
* Spawn real `codex exec --json` for the eval tier (real model, the user's codex
|
|
249
296
|
* auth — NOT the mock). CONFIRMED flags (codex 0.139.0): `--json` for the event
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Finding: it is essentially IDENTICAL to Claude Code's (exit 2 / `decision:block`
|
|
4
4
|
* / `permissionDecision:deny`) — the thin `HookProtocol` port was the right call.
|
|
5
5
|
* The genuine deltas are the env vars a hook receives + the TOML config format
|
|
6
|
-
* (the
|
|
6
|
+
* (the ENCODING lives in PluginLayout.settings, a codec; the entry SHAPE is
|
|
7
|
+
* `registration`/`mergeRegistrations` below).
|
|
7
8
|
*
|
|
8
9
|
* Context injection (`hookSpecificOutput.additionalContext`) is ALSO shared — same
|
|
9
10
|
* shape, confirmed against the official Codex hooks docs
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.codexHookProtocol = void 0;
|
|
4
|
+
const hook_install_js_1 = require("../../hook-install.js");
|
|
4
5
|
exports.codexHookProtocol = {
|
|
5
6
|
name: "codex",
|
|
6
7
|
blockExitCode: 2,
|
|
@@ -27,5 +28,14 @@ exports.codexHookProtocol = {
|
|
|
27
28
|
"permission_mode",
|
|
28
29
|
"PLUGIN_ROOT",
|
|
29
30
|
],
|
|
31
|
+
// Codex is FLAT: `[[hooks.<event>]]` carries one `{matcher?, command}` per
|
|
32
|
+
// entry. Same fact `toTomlEntries` encodes on the merge side.
|
|
33
|
+
registration(on, matcher, command) {
|
|
34
|
+
const entry = matcher === undefined ? { command } : { matcher, command };
|
|
35
|
+
return { hooks: { [on]: [entry] } };
|
|
36
|
+
},
|
|
37
|
+
mergeRegistrations(existing, compiled, managedBy) {
|
|
38
|
+
return (0, hook_install_js_1.mergeHooksToml)(existing, compiled, managedBy);
|
|
39
|
+
},
|
|
30
40
|
};
|
|
31
41
|
//# sourceMappingURL=hook-protocol.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex's instruction chain — the one file a repo-root session loads, and why
|
|
3
|
+
* every other `AGENTS.md`-shaped file in the map does not.
|
|
4
|
+
*
|
|
5
|
+
* Verbatim from the vendor page (`https://developers.openai.com/codex/guides/agents-md`,
|
|
6
|
+
* fetched 2026-09-21 and recorded in zernie/vigiles#262): starting at the
|
|
7
|
+
* project root Codex walks DOWN to the current working directory, taking **at
|
|
8
|
+
* most one file per directory** along that path, checking `AGENTS.override.md`
|
|
9
|
+
* first, then `AGENTS.md`, then fallback names the repo itself declares via
|
|
10
|
+
* `project_doc_fallback_filenames` in `config.toml`. The files are concatenated
|
|
11
|
+
* root-down and truncated at a byte cap on file boundaries.
|
|
12
|
+
*
|
|
13
|
+
* 🔴 SO `"**\/AGENTS.md"` WAS NOT MERELY UNBOUNDED, IT WAS WRONG. At a repo-root
|
|
14
|
+
* session the chain is the ROOT DIRECTORY ALONE — cwd and root are the same
|
|
15
|
+
* directory, so there is no path to walk down. Summing every nested `AGENTS.md`
|
|
16
|
+
* added up files that never load together: a monorepo with twelve package-level
|
|
17
|
+
* files was told it was 12× over a budget no session ever approaches. Codex
|
|
18
|
+
* TRUNCATES silently over its budget, so this number is the only warning a user
|
|
19
|
+
* gets, and it was crying wolf on the harness where wolf means "your rules do
|
|
20
|
+
* not exist".
|
|
21
|
+
*
|
|
22
|
+
* And `AGENTS.override.md` is the mirror image of Claude Code's local file: it
|
|
23
|
+
* REPLACES the committed file rather than being appended after it. That is the
|
|
24
|
+
* difference the `replaced` reason exists to carry — the combination rule is the
|
|
25
|
+
* harness's, the fact that a file was hidden is reported to the core.
|
|
26
|
+
*/
|
|
27
|
+
import type { InstructionChain } from "../../core/instruction-chain.js";
|
|
28
|
+
/** `AGENTS.md` → `AGENTS.override.md`: the per-machine file that REPLACES it. */
|
|
29
|
+
export declare function overrideSiblingOf(instructionFile: string): string;
|
|
30
|
+
/** Inputs the layout supplies so this module names no path of its own. */
|
|
31
|
+
export interface CodexChainInput {
|
|
32
|
+
/** `AGENTS.md`. */
|
|
33
|
+
readonly instructionFile: string;
|
|
34
|
+
/** The settings files, in precedence order (repo, then the local sibling). */
|
|
35
|
+
readonly settingsPaths: readonly string[];
|
|
36
|
+
/** The layout's own codec — this module does not know the encoding. */
|
|
37
|
+
readonly parseSettings: (text: string) => Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
export declare function codexInstructionChain(files: Readonly<Record<string, string>>, input: CodexChainInput): InstructionChain;
|
|
40
|
+
//# sourceMappingURL=instruction-chain.d.ts.map
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.overrideSiblingOf = overrideSiblingOf;
|
|
4
|
+
exports.codexInstructionChain = codexInstructionChain;
|
|
5
|
+
const instruction_chain_js_1 = require("../../core/instruction-chain.js");
|
|
6
|
+
/** The `config.toml` key by which a repo declares its own instruction names. */
|
|
7
|
+
const FALLBACK_NAMES_KEY = "project_doc_fallback_filenames";
|
|
8
|
+
/** `AGENTS.md` → `AGENTS.override.md`: the per-machine file that REPLACES it. */
|
|
9
|
+
function overrideSiblingOf(instructionFile) {
|
|
10
|
+
return (0, instruction_chain_js_1.siblingNamed)(instructionFile, "override");
|
|
11
|
+
}
|
|
12
|
+
/** `project_doc_fallback_filenames`, in the order the repo declared them. */
|
|
13
|
+
function fallbackNames(files, input) {
|
|
14
|
+
const out = [];
|
|
15
|
+
for (const path of input.settingsPaths) {
|
|
16
|
+
const text = files[path];
|
|
17
|
+
if (text === undefined)
|
|
18
|
+
continue;
|
|
19
|
+
let value;
|
|
20
|
+
try {
|
|
21
|
+
value = input.parseSettings(text);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// A config mid-merge must not decide which instructions load; falling
|
|
25
|
+
// back to "the documented names only" is the conservative reading.
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const raw = value[FALLBACK_NAMES_KEY];
|
|
29
|
+
if (!Array.isArray(raw))
|
|
30
|
+
continue;
|
|
31
|
+
for (const name of raw) {
|
|
32
|
+
if (typeof name === "string" && name !== "" && !out.includes(name)) {
|
|
33
|
+
out.push(name);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
function codexInstructionChain(files, input) {
|
|
40
|
+
const override = overrideSiblingOf(input.instructionFile);
|
|
41
|
+
// The root directory's ONE slot, in the vendor's precedence order.
|
|
42
|
+
//
|
|
43
|
+
// 🔴 THE OVERRIDE IS A REPOSITORY FILE, NOT A PER-MACHINE ONE. It used to be
|
|
44
|
+
// `scope: "local"` on the reasoning "like Claude Code's, it is a per-machine
|
|
45
|
+
// file" — an analogy, and the vendor does not support it. Codex's guide:
|
|
46
|
+
// "In each directory along the path, it checks for `AGENTS.override.md`,
|
|
47
|
+
// then `AGENTS.md`". The one override it calls temporary is the GLOBAL
|
|
48
|
+
// `~/.codex/AGENTS.override.md`; the guide never mentions `.gitignore`.
|
|
49
|
+
// Measured on the browser engine, where every file is committed by
|
|
50
|
+
// construction: a repository holding both files published `committed 0`,
|
|
51
|
+
// while Codex loads the override's bytes. Same class as the invented
|
|
52
|
+
// `.codex/config.local.toml` earlier in this PR — a sibling modelled by
|
|
53
|
+
// analogy instead of by observation.
|
|
54
|
+
const candidates = [
|
|
55
|
+
{ path: override, role: "root", scope: "repo" },
|
|
56
|
+
{ path: input.instructionFile, role: "root", scope: "repo" },
|
|
57
|
+
...fallbackNames(files, input).map((name) => ({
|
|
58
|
+
path: name,
|
|
59
|
+
role: "fallback",
|
|
60
|
+
scope: "repo",
|
|
61
|
+
})),
|
|
62
|
+
];
|
|
63
|
+
const loaded = [];
|
|
64
|
+
const unloaded = [];
|
|
65
|
+
for (const candidate of candidates) {
|
|
66
|
+
if (files[candidate.path] === undefined)
|
|
67
|
+
continue;
|
|
68
|
+
const winner = loaded[0];
|
|
69
|
+
if (winner === undefined) {
|
|
70
|
+
loaded.push(candidate);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
// AT MOST ONE FILE PER DIRECTORY. Everything after the winner is present
|
|
74
|
+
// and does not load, and says by whom it was replaced — which is the whole
|
|
75
|
+
// content of the field #262 called `shadows`.
|
|
76
|
+
unloaded.push({
|
|
77
|
+
...candidate,
|
|
78
|
+
reason: { kind: "replaced", by: winner.path },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// A nested `AGENTS.md` is NOT loaded at a repo-root session: the walk goes
|
|
82
|
+
// root→cwd, and at the root those are the same directory. The domain's bound
|
|
83
|
+
// never enumerates one, so this branch is reached only by a caller holding a
|
|
84
|
+
// wider map — and then the honest answer is "only when the session runs in
|
|
85
|
+
// that directory", not "always".
|
|
86
|
+
const named = new Set([...loaded, ...unloaded].map((e) => e.path));
|
|
87
|
+
const leaves = new Set([input.instructionFile, override]);
|
|
88
|
+
for (const path of Object.keys(files).sort()) {
|
|
89
|
+
if (named.has(path) || !path.includes("/"))
|
|
90
|
+
continue;
|
|
91
|
+
if (!leaves.has(path.slice(path.lastIndexOf("/") + 1)))
|
|
92
|
+
continue;
|
|
93
|
+
unloaded.push({
|
|
94
|
+
path,
|
|
95
|
+
role: "root",
|
|
96
|
+
scope: "repo",
|
|
97
|
+
reason: { kind: "on-demand", when: "subdirectory" },
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
// No imports and no patterns: Codex's project doc has no include mechanism —
|
|
101
|
+
// an empty array here is a STATEMENT, not a stub, and the property tests hold
|
|
102
|
+
// over it the same way they hold over a populated one.
|
|
103
|
+
return { loaded, unloaded, imports: [], patterns: [], redirects: [] };
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=instruction-chain.js.map
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
* - MCP detection (`mcpConfigFile`/`mcpManifestKey`) is JSON-shaped, so it won't
|
|
53
53
|
* see Codex's `[mcp_servers]` TOML table — a known layout-port gap.
|
|
54
54
|
* 🔴 THE PATHS BELOW ARE DOCUMENTED IN `docs/configuration.md`. Change any of
|
|
55
|
-
* them — `instructionFile`, `
|
|
55
|
+
* them — `instructionFile`, `surfaces`, `userSurfaceRoot`, `rulesDir` — and
|
|
56
56
|
* that page is wrong until you edit it too. The page marks this symbol with
|
|
57
57
|
* `vigiles:symbol`, so RENAMING it turns `vigiles lint` red and forces the
|
|
58
58
|
* edit; changing a VALUE in place does not, and nothing today catches that.
|
|
@@ -1,30 +1,57 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.codexLayout = void 0;
|
|
4
|
+
const instruction_chain_js_1 = require("../../core/instruction-chain.js");
|
|
5
|
+
const settings_codec_js_1 = require("../../core/settings-codec.js");
|
|
6
|
+
const instruction_chain_js_2 = require("./instruction-chain.js");
|
|
4
7
|
exports.codexLayout = {
|
|
5
8
|
name: "codex",
|
|
6
9
|
manifestPath: ".codex/config.toml",
|
|
7
10
|
hooksConventionPath: ".codex/hooks.json",
|
|
8
11
|
settingsPath: ".codex/config.toml",
|
|
9
|
-
|
|
12
|
+
settings: settings_codec_js_1.tomlSettingsCodec,
|
|
10
13
|
instructionFile: "AGENTS.md",
|
|
11
|
-
// Surfaces carry their OWN prefix and
|
|
12
|
-
// style, not the Claude Code one. Codex's skills and its prompts do
|
|
13
|
-
// parent (`.agents/` vs `.codex/`), so no single
|
|
14
|
+
// Surfaces carry their OWN prefix and the materialize prefix is "" — the
|
|
15
|
+
// OpenCode style, not the Claude Code one. Codex's skills and its prompts do
|
|
16
|
+
// NOT share a parent (`.agents/` vs `.codex/`), so no single root can name
|
|
14
17
|
// both; spelling each dir in full is the only shape that keeps the reported key
|
|
15
18
|
// equal to the real on-disk path.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
surfaces: {
|
|
20
|
+
skill: ".agents/skills",
|
|
21
|
+
// No `agent` key: Codex `[agents]` is a TOML concurrency table, not a
|
|
22
|
+
// subagent dir. An ABSENT key is the only spelling of "this harness has no
|
|
23
|
+
// such surface" — it used to be `agentDir: ""`, a second spelling that every
|
|
24
|
+
// reader had to remember to test for.
|
|
25
|
+
//
|
|
26
|
+
// Custom prompts are documented ONLY at `~/.codex/prompts` (user-global,
|
|
27
|
+
// top-level `.md`, and marked deprecated in favour of skills). No repo-level
|
|
28
|
+
// location is documented, so this prototype's root-level `prompts/` is left
|
|
29
|
+
// as it was rather than moved on a guess.
|
|
30
|
+
command: "prompts",
|
|
31
|
+
},
|
|
32
|
+
// No `userSurfaceRoot`: the surfaces carry their own prefix, so the
|
|
33
|
+
// materialize prefix is "" and a file-map key equals the on-disk path. That
|
|
34
|
+
// used to be spelled twice, as `materializeRoot: ""` beside an absent
|
|
35
|
+
// `userSurfaceRoot`.
|
|
36
|
+
//
|
|
37
|
+
// ⚠️ `hookScriptsDir: "hooks"` carries over the value the hand-written
|
|
38
|
+
// `intraRefDirs` had. NOTHING in the vendor pages confirms a repo-level
|
|
39
|
+
// `hooks/` directory for Codex; it is kept as it was rather than "aligned"
|
|
40
|
+
// from a guess, the same stance `installCodexSkills` takes above.
|
|
41
|
+
hookScriptsDir: "hooks",
|
|
25
42
|
pluginRootToken: "${PLUGIN_ROOT}",
|
|
26
43
|
mcpConfigFile: ".mcp.json",
|
|
27
44
|
mcpManifestKey: "mcp_servers",
|
|
28
|
-
|
|
45
|
+
// The root directory's ONE slot — override, then the committed file, then the
|
|
46
|
+
// names the repo itself declares in `config.toml`. See ./instruction-chain.ts
|
|
47
|
+
// for the vendor wording and for why `"**/AGENTS.md"` was not merely unbounded
|
|
48
|
+
// but wrong about what a root session loads.
|
|
49
|
+
instructionChain(files) {
|
|
50
|
+
return (0, instruction_chain_js_2.codexInstructionChain)(files, {
|
|
51
|
+
instructionFile: exports.codexLayout.instructionFile,
|
|
52
|
+
settingsPaths: (0, instruction_chain_js_1.settingsSourcePaths)(exports.codexLayout),
|
|
53
|
+
parseSettings: (text) => exports.codexLayout.settings.parse(text),
|
|
54
|
+
});
|
|
55
|
+
},
|
|
29
56
|
};
|
|
30
57
|
//# sourceMappingURL=layout.js.map
|
|
@@ -1,3 +1,34 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* opencodeAdapter — EXPERIMENTAL, internal-only prototype `HarnessAdapter`. It
|
|
3
|
+
* exists to PROVE the kit generalizes to the optional-transport-port shape: a
|
|
4
|
+
* harness that does pillar 1 AND is mockable (openai-compatible) BUT whose hooks
|
|
5
|
+
* are in-process JS/TS plugin modules, not shell processes. So it declares
|
|
6
|
+
* `shellHooks: false`, ships NO `hookProtocol`, and the conformance kit must
|
|
7
|
+
* accept it without demanding a fake one. This is the pillar-1-only,
|
|
8
|
+
* no-shell-hooks shape the capability gating was built for.
|
|
9
|
+
*
|
|
10
|
+
* 🔴 AND IT IS THE PORT'S THIRD IMPLEMENTATION, which is why two of the port's
|
|
11
|
+
* illegal states were live HERE and nowhere else: a port validated against two
|
|
12
|
+
* implementations cannot see a state only the third can reach. The other one
|
|
13
|
+
* was in its layout (a skill dir outside `surfaceDirs`).
|
|
14
|
+
*
|
|
15
|
+
* It is deliberately NOT registered in `src/adapter-registry.ts` and NOT exported
|
|
16
|
+
* from any `vigiles/*` subpath, so the CLI never auto-detects OpenCode and
|
|
17
|
+
* consumers can't import it. Promote it (register + `vigiles/opencode` export +
|
|
18
|
+
* the deferred transport renderers) only when OpenCode support actually ships.
|
|
19
|
+
*/
|
|
20
|
+
import type { DetectSignal } from "../../core/adapter.js";
|
|
21
|
+
export declare const opencodeAdapter: {
|
|
22
|
+
readonly name: "opencode";
|
|
23
|
+
readonly harnessTesting: false;
|
|
24
|
+
readonly shellHooks: false;
|
|
25
|
+
readonly subagents: true;
|
|
26
|
+
readonly dialect: import("../claude-code/dialect.js").HarnessDialect;
|
|
27
|
+
readonly layout: import("../../adapter.js").PluginLayout;
|
|
28
|
+
readonly claims: (path: string) => boolean;
|
|
29
|
+
readonly detect: (exists: (repoRelative: string) => boolean) => DetectSignal;
|
|
30
|
+
/** Nothing to say about an opencode install — see the Codex adapter for why
|
|
31
|
+
* `[]` is the answer rather than an absent capability. */
|
|
32
|
+
readonly advisories: () => readonly string[];
|
|
33
|
+
};
|
|
3
34
|
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -1,55 +1,55 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.opencodeAdapter = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* opencodeAdapter — EXPERIMENTAL, internal-only prototype `HarnessAdapter`. It
|
|
6
|
-
* exists to PROVE the kit generalizes to the optional-transport-port shape: a
|
|
7
|
-
* harness that does pillar 1 AND is mockable (openai-compatible) BUT whose hooks
|
|
8
|
-
* are in-process JS/TS plugin modules, not shell processes. So it declares
|
|
9
|
-
* `shellHooks: false`, ships NO `hookProtocol`, and the conformance kit must
|
|
10
|
-
* accept it without demanding a fake one. This is the pillar-1-+-mockable-but-
|
|
11
|
-
* no-shell-hooks shape the capability gating was built for.
|
|
12
|
-
*
|
|
13
|
-
* It is deliberately NOT registered in `src/adapter-registry.ts` and NOT exported
|
|
14
|
-
* from any `vigiles/*` subpath, so the CLI never auto-detects OpenCode and
|
|
15
|
-
* consumers can't import it. Promote it (register + `vigiles/opencode` export +
|
|
16
|
-
* the deferred transport renderers) only when OpenCode support actually ships.
|
|
17
|
-
*/
|
|
18
|
-
const node_fs_1 = require("node:fs");
|
|
19
|
-
const node_path_1 = require("node:path");
|
|
20
4
|
const dialect_js_1 = require("./dialect.js");
|
|
21
5
|
const layout_js_1 = require("./layout.js");
|
|
22
6
|
const surface_discovery_js_1 = require("../../core/surface-discovery.js");
|
|
23
|
-
|
|
24
|
-
|
|
7
|
+
// `opencodeRuntime` / `opencodeModelMock` are NOT imported: with
|
|
8
|
+
// `harnessTesting: false` the type gives those fields `?: never`, so carrying
|
|
9
|
+
// them would be a compile error. The modules stay on disk for the commit that
|
|
10
|
+
// adds a driver and flips the flag back.
|
|
25
11
|
exports.opencodeAdapter = {
|
|
26
12
|
name: "opencode",
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
13
|
+
// 🔴 `harnessTesting: false`, AND IT USED TO SAY `true`. That declaration was
|
|
14
|
+
// the port's second live illegal state: the flag was set, `runtime` and
|
|
15
|
+
// `modelMock` were present, and there was NO `harnessTestDriver` behind them
|
|
16
|
+
// — so `runHarnessTest` threw "declares harnessTesting but carries no
|
|
17
|
+
// harnessTestDriver" at run time, and the conformance kit missed it because
|
|
18
|
+
// it checked the two ports it knew about and not the thunk.
|
|
19
|
+
//
|
|
20
|
+
// The declaration was also simply untrue, and the repo said so elsewhere:
|
|
21
|
+
// `docs/harnesses.md` records OpenCode's mockable tier as "declared but not
|
|
22
|
+
// yet built". So this is not a downgrade — it is the flag catching up with
|
|
23
|
+
// the tier, and `runtime`/`modelMock` come off with it because the `false`
|
|
24
|
+
// arm of `TestingPorts` types them `?: never`. Set it back to `true` in the
|
|
25
|
+
// same commit that adds a driver; the type will refuse anything else.
|
|
26
|
+
harnessTesting: false,
|
|
27
|
+
// Hooks are in-process JS/TS plugin modules — no shell-hook tier, hence
|
|
28
|
+
// shellHooks:false and NO hookProtocol (the `false` arm types it `?: never`,
|
|
29
|
+
// so shipping one is now an error rather than dead weight).
|
|
30
|
+
shellHooks: false,
|
|
31
|
+
subagents: true,
|
|
35
32
|
dialect: dialect_js_1.opencodeDialect,
|
|
36
33
|
layout: layout_js_1.opencodeLayout,
|
|
37
|
-
runtime: runtime_js_1.opencodeRuntime,
|
|
38
|
-
modelMock: model_mock_js_1.opencodeModelMock,
|
|
39
|
-
// No hookProtocol: OpenCode hooks are code modules, not shell processes.
|
|
40
34
|
// Derived from the layout, never listed again here — see `claims` on
|
|
41
35
|
// `HarnessAdapter` for why this method takes a PATH and not a root.
|
|
42
36
|
claims(path) {
|
|
43
37
|
return (0, surface_discovery_js_1.layoutClaims)(layout_js_1.opencodeLayout, path);
|
|
44
38
|
},
|
|
45
|
-
detect(
|
|
39
|
+
detect(exists) {
|
|
46
40
|
// An `opencode.json` is a strong signal; a bare AGENTS.md is weak (many
|
|
47
|
-
// harnesses read it). (Unused while unregistered — kept for symmetry
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
41
|
+
// harnesses read it). (Unused while unregistered — kept for symmetry, and
|
|
42
|
+
// so the property tests have a THIRD implementation to run against.)
|
|
43
|
+
if (exists(layout_js_1.opencodeLayout.manifestPath))
|
|
44
|
+
return { specificity: 3, via: "manifest" };
|
|
45
|
+
if (exists(layout_js_1.opencodeLayout.instructionFile))
|
|
46
|
+
return { specificity: 1, via: "instruction-file" };
|
|
47
|
+
return { specificity: 0, via: "instruction-file" };
|
|
48
|
+
},
|
|
49
|
+
/** Nothing to say about an opencode install — see the Codex adapter for why
|
|
50
|
+
* `[]` is the answer rather than an absent capability. */
|
|
51
|
+
advisories() {
|
|
52
|
+
return [];
|
|
53
53
|
},
|
|
54
54
|
};
|
|
55
55
|
//# sourceMappingURL=adapter.js.map
|
|
@@ -30,7 +30,7 @@ exports.opencodeDialect = {
|
|
|
30
30
|
instructionTargets: ["AGENTS.md", "CLAUDE.md"],
|
|
31
31
|
pluginRootToken: "${OPENCODE_PLUGIN_ROOT}",
|
|
32
32
|
// OpenCode reads the minimal cross-tool SKILL.md frontmatter (name +
|
|
33
|
-
// description); the
|
|
34
|
-
|
|
33
|
+
// description); the richer keys are not part of its format.
|
|
34
|
+
skillFrontmatterKeys: ["name", "description"],
|
|
35
35
|
};
|
|
36
36
|
//# sourceMappingURL=dialect.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode's instruction chain — EXPERIMENTAL, like the rest of this adapter.
|
|
3
|
+
*
|
|
4
|
+
* It exists because it is the port's THIRD implementation, and the third one is
|
|
5
|
+
* where a shape has room to go wrong: both of the layout port's live illegal
|
|
6
|
+
* states were here, in the implementation the contract suite did not range over.
|
|
7
|
+
* For `instructionChain` it earns its keep a second way — it is the only
|
|
8
|
+
* implementation in this repo that populates {@link InstructionChain.patterns},
|
|
9
|
+
* so without it that half of the interface would be typed, documented and
|
|
10
|
+
* exercised by nothing.
|
|
11
|
+
*
|
|
12
|
+
* ⚠️ SOURCE OF THE `instructions` FACT, NAMED RATHER THAN IMPLIED. The key and
|
|
13
|
+
* its glob shape (`instructions: ["packages/*\/AGENTS.md"]`) are taken from
|
|
14
|
+
* `docs/design/port-redesign-round-2-2026-09-21.md` §1, which is where this
|
|
15
|
+
* adapter's other shapes come from too. It is NOT a vendor page this session
|
|
16
|
+
* fetched, and the adapter is unregistered and unexported, so nothing a user
|
|
17
|
+
* runs depends on it being exactly right. Do not promote it to a registered
|
|
18
|
+
* adapter without re-reading the vendor.
|
|
19
|
+
*
|
|
20
|
+
* What it does NOT do is expand those globs. A pattern is reported so the weight
|
|
21
|
+
* can say "plus N pattern(s) not weighed" instead of printing a number that is
|
|
22
|
+
* quietly missing them — the same stance the Codex chain takes towards a nested
|
|
23
|
+
* file, and the opposite of what the glob list this replaced did, which was to
|
|
24
|
+
* expand an adapter's pattern by walking the user's repository.
|
|
25
|
+
*/
|
|
26
|
+
import type { InstructionChain } from "../../core/instruction-chain.js";
|
|
27
|
+
/** Inputs the layout supplies so this module names no path of its own. */
|
|
28
|
+
export interface OpencodeChainInput {
|
|
29
|
+
/** `AGENTS.md`. */
|
|
30
|
+
readonly instructionFile: string;
|
|
31
|
+
/** `opencode.json` — both the manifest and the settings file here. */
|
|
32
|
+
readonly settingsPaths: readonly string[];
|
|
33
|
+
/** The layout's own codec — this module does not know the encoding. */
|
|
34
|
+
readonly parseSettings: (text: string) => Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
export declare function opencodeInstructionChain(files: Readonly<Record<string, string>>, input: OpencodeChainInput): InstructionChain;
|
|
37
|
+
//# sourceMappingURL=instruction-chain.d.ts.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.opencodeInstructionChain = opencodeInstructionChain;
|
|
4
|
+
const instruction_chain_js_1 = require("../../core/instruction-chain.js");
|
|
5
|
+
/** The manifest key listing extra instruction files, as the design records it. */
|
|
6
|
+
const INSTRUCTIONS_KEY = "instructions";
|
|
7
|
+
/** A value the domain would have to WALK to expand, rather than look up. */
|
|
8
|
+
function isPattern(value) {
|
|
9
|
+
return /[*?[\]{}]/.test(value) || value.includes("://");
|
|
10
|
+
}
|
|
11
|
+
/** The `instructions` entries one settings file declares, in declared order. */
|
|
12
|
+
function declaredInstructions(files, input) {
|
|
13
|
+
const out = [];
|
|
14
|
+
for (const from of input.settingsPaths) {
|
|
15
|
+
const text = files[from];
|
|
16
|
+
if (text === undefined)
|
|
17
|
+
continue;
|
|
18
|
+
let value;
|
|
19
|
+
try {
|
|
20
|
+
value = input.parseSettings(text);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const raw = value[INSTRUCTIONS_KEY];
|
|
26
|
+
if (!Array.isArray(raw))
|
|
27
|
+
continue;
|
|
28
|
+
for (const entry of raw) {
|
|
29
|
+
if (typeof entry === "string" && entry !== "") {
|
|
30
|
+
// The token AS WRITTEN is the manifest string itself — OpenCode names
|
|
31
|
+
// a path directly rather than prefixing it, so token === path here.
|
|
32
|
+
out.push({ path: entry, token: entry, from });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
function opencodeInstructionChain(files, input) {
|
|
39
|
+
const loaded = [];
|
|
40
|
+
const imports = [];
|
|
41
|
+
const patterns = [];
|
|
42
|
+
if (files[input.instructionFile] !== undefined) {
|
|
43
|
+
loaded.push({ path: input.instructionFile, role: "root", scope: "repo" });
|
|
44
|
+
}
|
|
45
|
+
for (const entry of declaredInstructions(files, input)) {
|
|
46
|
+
if (isPattern(entry.path)) {
|
|
47
|
+
patterns.push({ pattern: entry.path, from: entry.from });
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (!(0, instruction_chain_js_1.isRepoRootedImport)(entry.path))
|
|
51
|
+
continue;
|
|
52
|
+
imports.push(entry);
|
|
53
|
+
if (files[entry.path] === undefined)
|
|
54
|
+
continue;
|
|
55
|
+
if (loaded.some((e) => e.path === entry.path))
|
|
56
|
+
continue;
|
|
57
|
+
loaded.push({
|
|
58
|
+
path: entry.path,
|
|
59
|
+
role: "import",
|
|
60
|
+
scope: "repo",
|
|
61
|
+
via: { from: entry.from, token: entry.token },
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// No `unloaded`, and no `redirects`: nothing in OpenCode's documented shape
|
|
65
|
+
// hides one file behind another, and its extra instruction files are named in
|
|
66
|
+
// the MANIFEST rather than inside the root file, so the root file cannot be a
|
|
67
|
+
// pure pointer. Empty arrays are the statement, not a gap.
|
|
68
|
+
return { loaded, unloaded: [], imports, patterns, redirects: [] };
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=instruction-chain.js.map
|
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
* OpenCode: `opencode.json` manifest/settings, `AGENTS.md`, `.opencode/`
|
|
4
4
|
* surfaces, `${OPENCODE_PLUGIN_ROOT}`. Validates the layout port against real
|
|
5
5
|
* OpenCode shapes. NOT exported / NOT registered.
|
|
6
|
+
*
|
|
7
|
+
* 🔴 IT IS THE PORT'S THIRD IMPLEMENTATION, AND IT SHIPPED BROKEN — which is
|
|
8
|
+
* exactly the job a third implementation has. Until 2026-09-21 this descriptor
|
|
9
|
+
* read:
|
|
10
|
+
*
|
|
11
|
+
* surfaceDirs: [".opencode/agent", ".opencode/command"],
|
|
12
|
+
* skillDir: ".opencode/skill",
|
|
13
|
+
*
|
|
14
|
+
* Two fields naming the same set of directories, disagreeing. Everything that
|
|
15
|
+
* materializes, counts or scans a surface ranged over `surfaceDirs`, so
|
|
16
|
+
* OpenCode's skills were NAMED by the port and READ by nothing: a repo with
|
|
17
|
+
* skills under `.opencode/skill` graded as having none. Nothing caught it,
|
|
18
|
+
* because a port validated against two implementations cannot see a state only
|
|
19
|
+
* the third can reach.
|
|
20
|
+
*
|
|
21
|
+
* It cannot be written now: `surfaces` is one record keyed by kind, so there is
|
|
22
|
+
* no second field to disagree with, and `surfaceDirs()` is derived from it.
|
|
23
|
+
* Listing the skill dir here is therefore also a BEHAVIOUR change for this
|
|
24
|
+
* prototype — its skills become readable for the first time.
|
|
6
25
|
*/
|
|
7
26
|
import type { PluginLayout } from "../../core/layout.js";
|
|
8
27
|
export declare const opencodeLayout: PluginLayout;
|