@dungle-scrubs/harness-cli-normalizer 0.6.1 → 0.6.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/README.md +15 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +5 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +2 -0
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/help.d.ts +2 -2
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +4 -0
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/plan-turn.d.ts.map +1 -1
- package/dist/cli/plan-turn.js +3 -1
- package/dist/cli/plan-turn.js.map +1 -1
- package/dist/cli/session.d.ts.map +1 -1
- package/dist/cli/session.js +11 -0
- package/dist/cli/session.js.map +1 -1
- package/dist/interpretation/argv.d.ts +1 -0
- package/dist/interpretation/argv.d.ts.map +1 -1
- package/dist/interpretation/argv.js +2 -0
- package/dist/interpretation/argv.js.map +1 -1
- package/dist/interpretation/isolation.d.ts +10 -0
- package/dist/interpretation/isolation.d.ts.map +1 -0
- package/dist/interpretation/isolation.js +30 -0
- package/dist/interpretation/isolation.js.map +1 -0
- package/dist/interpretation/resolve-options.d.ts.map +1 -1
- package/dist/interpretation/resolve-options.js +15 -1
- package/dist/interpretation/resolve-options.js.map +1 -1
- package/dist/interpretation/support.d.ts.map +1 -1
- package/dist/interpretation/support.js +2 -0
- package/dist/interpretation/support.js.map +1 -1
- package/dist/interpretation/turn-options.d.ts.map +1 -1
- package/dist/interpretation/turn-options.js +2 -0
- package/dist/interpretation/turn-options.js.map +1 -1
- package/dist/knowledge/claude-code.d.ts +3 -1
- package/dist/knowledge/claude-code.d.ts.map +1 -1
- package/dist/knowledge/claude-code.js +23 -3
- package/dist/knowledge/claude-code.js.map +1 -1
- package/dist/knowledge/descriptor.d.ts +1 -1
- package/dist/knowledge/descriptor.d.ts.map +1 -1
- package/dist/knowledge/descriptor.js +1 -0
- package/dist/knowledge/descriptor.js.map +1 -1
- package/dist/knowledge/muse.d.ts.map +1 -1
- package/dist/knowledge/muse.js +6 -1
- package/dist/knowledge/muse.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/args.ts +4 -0
- package/src/cli/config.ts +2 -0
- package/src/cli/help.ts +4 -0
- package/src/cli/plan-turn.ts +5 -1
- package/src/cli/session.ts +17 -0
- package/src/interpretation/argv.ts +3 -0
- package/src/interpretation/isolation.ts +39 -0
- package/src/interpretation/resolve-options.ts +13 -1
- package/src/interpretation/support.ts +2 -0
- package/src/interpretation/turn-options.ts +2 -0
- package/src/knowledge/claude-code.ts +23 -3
- package/src/knowledge/descriptor.ts +1 -0
- package/src/knowledge/muse.ts +6 -1
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
StreamingGranularity,
|
|
11
11
|
} from "../knowledge/descriptor.js";
|
|
12
12
|
import { defaultDescriptors } from "../knowledge/overrides.js";
|
|
13
|
+
import { assertIsolationCombination } from "./isolation.js";
|
|
13
14
|
import { ArgvRefusalError } from "./refusal.js";
|
|
14
15
|
import { assertUsableSessionId, SESSION_ID_MAX, SessionIdRefusalError } from "./session-id.js";
|
|
15
16
|
import { renderSkillsSelection, type SkillsSelection } from "./skills-selection.js";
|
|
@@ -61,6 +62,7 @@ export interface DiscoveryOptions {
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
export interface TurnOptions {
|
|
65
|
+
readonly isolation?: "tool-free";
|
|
64
66
|
readonly prompt: Prompt;
|
|
65
67
|
readonly tools?: readonly string[];
|
|
66
68
|
readonly excludeTools?: readonly string[];
|
|
@@ -205,6 +207,7 @@ export interface SpawnArgvOptions extends TurnOptions {
|
|
|
205
207
|
* passthrough tail after a bare separator. One owner, so the CLI's preview
|
|
206
208
|
* and the runner's spawn agree by construction (RFC-02 change 10). */
|
|
207
209
|
export const buildSpawnArgv = (h: HarnessDescriptor, opts: SpawnArgvOptions): string[] => {
|
|
210
|
+
assertIsolationCombination(h, opts);
|
|
208
211
|
const base =
|
|
209
212
|
opts.resume === undefined
|
|
210
213
|
? buildLaunchArgv(h, opts)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { HarnessDescriptor } from "../knowledge/descriptor.js";
|
|
2
|
+
import type { TurnOptions } from "./argv.js";
|
|
3
|
+
import { ArgvRefusalError } from "./refusal.js";
|
|
4
|
+
|
|
5
|
+
/** Isolation owns these dimensions for the whole invocation. Config defaults
|
|
6
|
+
* yield to it; explicit competing selections refuse before registry reads. */
|
|
7
|
+
export const ISOLATION_OVERRIDES = [
|
|
8
|
+
"tools",
|
|
9
|
+
"excludeTools",
|
|
10
|
+
"skills",
|
|
11
|
+
"access",
|
|
12
|
+
"autonomy",
|
|
13
|
+
"discovery",
|
|
14
|
+
] as const;
|
|
15
|
+
export const assertIsolationCombination = (
|
|
16
|
+
h: HarnessDescriptor,
|
|
17
|
+
opts: Partial<TurnOptions> & {
|
|
18
|
+
readonly skillNames?: readonly string[];
|
|
19
|
+
readonly passthrough?: readonly string[];
|
|
20
|
+
},
|
|
21
|
+
): void => {
|
|
22
|
+
if (opts.isolation === undefined) return;
|
|
23
|
+
const conflict =
|
|
24
|
+
ISOLATION_OVERRIDES.find((key) =>
|
|
25
|
+
key === "autonomy" ? opts[key] === true : opts[key] !== undefined,
|
|
26
|
+
) ??
|
|
27
|
+
(opts.skillNames !== undefined ? "skills" : undefined) ??
|
|
28
|
+
(opts.passthrough?.length ? "native passthrough" : undefined);
|
|
29
|
+
if (conflict === undefined) return;
|
|
30
|
+
throw new ArgvRefusalError({
|
|
31
|
+
issue: "mutually-exclusive-options",
|
|
32
|
+
harness: h.name,
|
|
33
|
+
option: "isolation",
|
|
34
|
+
detail: conflict,
|
|
35
|
+
supported: [
|
|
36
|
+
"tool-free without explicit tools, skills, access, autonomy, discovery, or native passthrough",
|
|
37
|
+
],
|
|
38
|
+
});
|
|
39
|
+
};
|
|
@@ -11,6 +11,7 @@ import type { HarnessDescriptor } from "../knowledge/descriptor.js";
|
|
|
11
11
|
import { defaultDescriptors } from "../knowledge/overrides.js";
|
|
12
12
|
import { DEFAULT_TURN_PROFILE, type ProfileKey } from "../knowledge/profile.js";
|
|
13
13
|
import type { TurnOptions } from "./argv.js";
|
|
14
|
+
import { assertIsolationCombination, ISOLATION_OVERRIDES } from "./isolation.js";
|
|
14
15
|
import type { QuestionMode } from "./question.js";
|
|
15
16
|
import { ArgvRefusalError } from "./refusal.js";
|
|
16
17
|
import type { ToolMapConfig } from "./tool-vocabulary.js";
|
|
@@ -152,10 +153,17 @@ export const resolveEffectiveOptions = (
|
|
|
152
153
|
args: TurnOptions,
|
|
153
154
|
tiers: ConfigTiers = {},
|
|
154
155
|
): ResolvedOptions => {
|
|
156
|
+
assertIsolationCombination(h, args);
|
|
155
157
|
const provenance: ProvenanceEntry[] = [];
|
|
158
|
+
if (args.isolation !== undefined)
|
|
159
|
+
provenance.push({ key: "isolation", value: args.isolation, tier: "arg" });
|
|
156
160
|
const unrenderable: string[] = [];
|
|
157
|
-
const config = effectiveConfig(tiers);
|
|
161
|
+
const config = { ...effectiveConfig(tiers) };
|
|
162
|
+
if (args.isolation !== undefined) {
|
|
163
|
+
for (const key of ISOLATION_OVERRIDES) delete config[key];
|
|
164
|
+
}
|
|
158
165
|
const sourceTier = (key: string): ProvenanceTier | undefined => {
|
|
166
|
+
if (config[key as keyof TurnOptions] === undefined) return undefined;
|
|
159
167
|
if (tiers.project?.[key as keyof TurnOptions] !== undefined) return "project-config";
|
|
160
168
|
if (tiers.user?.[key as keyof TurnOptions] !== undefined) return "user-config";
|
|
161
169
|
return undefined;
|
|
@@ -288,6 +296,10 @@ export const resolveEffectiveOptions = (
|
|
|
288
296
|
|
|
289
297
|
// Profile is the floor: apply only where nothing above it set the key.
|
|
290
298
|
for (const [key, value] of Object.entries(DEFAULT_TURN_PROFILE)) {
|
|
299
|
+
if (args.isolation !== undefined && ISOLATION_OVERRIDES.some((owned) => owned === key)) {
|
|
300
|
+
provenance.push({ key, value: "disabled (tool-free isolation)", tier: "arg" });
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
291
303
|
const argsSet = effectiveArgs[key as keyof TurnOptions] !== undefined;
|
|
292
304
|
const tier = sourceTier(key);
|
|
293
305
|
if (argsSet) {
|
|
@@ -51,6 +51,7 @@ const spellingOf = (h: HarnessDescriptor, option: RefusalOption): string | null
|
|
|
51
51
|
}
|
|
52
52
|
case "autonomy":
|
|
53
53
|
return h.autonomy?.flag ?? null;
|
|
54
|
+
case "isolation":
|
|
54
55
|
case "effort":
|
|
55
56
|
case "sandbox":
|
|
56
57
|
case "contextWindow":
|
|
@@ -124,6 +125,7 @@ export const recognizeNativeSpelling = (
|
|
|
124
125
|
"tools",
|
|
125
126
|
"excludeTools",
|
|
126
127
|
"autonomy",
|
|
128
|
+
"isolation",
|
|
127
129
|
"effort",
|
|
128
130
|
"sandbox",
|
|
129
131
|
"contextWindow",
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
import { defaultDescriptors } from "../knowledge/overrides.js";
|
|
25
25
|
import type { DiscoveryOptions, TurnOptions } from "./argv.js";
|
|
26
26
|
import { hintFor } from "./hints.js";
|
|
27
|
+
import { assertIsolationCombination } from "./isolation.js";
|
|
27
28
|
import { ArgvRefusalError } from "./refusal.js";
|
|
28
29
|
import { supportedBy } from "./support.js";
|
|
29
30
|
import { renderToolSelection } from "./tool-selection.js";
|
|
@@ -41,6 +42,7 @@ export const renderTurnOptions = (
|
|
|
41
42
|
opts: TurnOptions,
|
|
42
43
|
phase: "launch" | "resume",
|
|
43
44
|
): string[] => {
|
|
45
|
+
assertIsolationCombination(h, opts);
|
|
44
46
|
const sequences: string[][] = [];
|
|
45
47
|
// The turn option the access preset displaces when set (codex: sandbox),
|
|
46
48
|
// read from the descriptor so no arm below branches on a harness name.
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* `ANTHROPIC_API_KEY` / `apiKeyHelper` (OAuth never read) - so a caller
|
|
10
10
|
* authenticated via OAuth would break. The descriptor therefore offers no
|
|
11
11
|
* `instructionFiles` facet and a call passing it must refuse. Likewise
|
|
12
|
-
* `tools` has no discovery flag on claude and refuses.
|
|
12
|
+
* `tools` has no discovery flag on claude and refuses. The explicit
|
|
13
|
+
* tool-free isolation composite below accepts the bare-mode auth cost and
|
|
14
|
+
* disables native tools too. It is separate from a granular discovery facet.
|
|
13
15
|
*
|
|
14
16
|
* Effort: A-002 showed `--effort bogus` warns on stderr and runs at DEFAULT
|
|
15
17
|
* effort, exit 0, with nothing echoed in the stream. The library-side
|
|
@@ -115,9 +117,16 @@ export const claudeCode: HarnessDescriptor = deepFreeze({
|
|
|
115
117
|
autonomy: { flag: "--dangerously-skip-permissions" },
|
|
116
118
|
vocabulary: {
|
|
117
119
|
modelFlag: "--model",
|
|
118
|
-
|
|
120
|
+
// https://platform.claude.com/docs/en/models/fable-5-1/overview
|
|
121
|
+
models: [
|
|
122
|
+
"claude-fable-5-1",
|
|
123
|
+
"claude-fable-5",
|
|
124
|
+
"claude-opus-5",
|
|
125
|
+
"claude-sonnet-5",
|
|
126
|
+
"claude-haiku-4-5-20251001",
|
|
127
|
+
],
|
|
119
128
|
aliases: {
|
|
120
|
-
fable: "claude-fable-5",
|
|
129
|
+
fable: "claude-fable-5-1",
|
|
121
130
|
opus: "claude-opus-5",
|
|
122
131
|
sonnet: "claude-sonnet-5",
|
|
123
132
|
haiku: "claude-haiku-4-5-20251001",
|
|
@@ -161,6 +170,17 @@ export const claudeCode: HarnessDescriptor = deepFreeze({
|
|
|
161
170
|
observedOn: { harness: "claude", model: "", version: "2.1.235", date: "2026-08-19" },
|
|
162
171
|
},
|
|
163
172
|
turnOptions: {
|
|
173
|
+
// Native CLI reference: bare removes discovery; the empty built-in list and
|
|
174
|
+
// MCP deny remove tool access. Resume and native overrides are refused.
|
|
175
|
+
isolation: {
|
|
176
|
+
kind: "enum",
|
|
177
|
+
values: ["tool-free"],
|
|
178
|
+
resumeRender: null,
|
|
179
|
+
render: {
|
|
180
|
+
kind: "flag-list",
|
|
181
|
+
flags: ["--bare", "--tools", "", "--disallowedTools", "mcp__*", "--strict-mcp-config"],
|
|
182
|
+
},
|
|
183
|
+
},
|
|
164
184
|
effort: { kind: "effort", render: { kind: "flag-value", flag: "--effort" } },
|
|
165
185
|
// issue #48, live-verified 2.1.235: --system-prompt replaces the built-in
|
|
166
186
|
// prompt; --exclude-dynamic-system-prompt-sections strips the dynamic
|
|
@@ -146,6 +146,7 @@ export type UnavailableMatcher = PhraseMatcher;
|
|
|
146
146
|
* arm would be dead data that can only drift. Render order is the tuple
|
|
147
147
|
* order, so argv is deterministic regardless of caller field order. */
|
|
148
148
|
export const TURN_OPTION_KEYS = deepFreeze([
|
|
149
|
+
"isolation",
|
|
149
150
|
"effort",
|
|
150
151
|
"sandbox",
|
|
151
152
|
"contextWindow",
|
package/src/knowledge/muse.ts
CHANGED
|
@@ -59,7 +59,12 @@ export const museCode: HarnessDescriptor = deepFreeze({
|
|
|
59
59
|
autonomy: { flag: "--yolo" },
|
|
60
60
|
vocabulary: {
|
|
61
61
|
modelFlag: "--model",
|
|
62
|
-
models: [
|
|
62
|
+
models: [
|
|
63
|
+
"muse-spark-1.3-contributor",
|
|
64
|
+
"muse-spark-1.2-contributor",
|
|
65
|
+
"muse-spark-1.2",
|
|
66
|
+
"muse-spark-1.1",
|
|
67
|
+
],
|
|
63
68
|
aliases: {},
|
|
64
69
|
efforts: ["none", "minimal", "low", "medium", "high", "xhigh"],
|
|
65
70
|
extensible: false,
|