@dungle-scrubs/harness-cli-normalizer 0.5.5 → 0.5.6
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 +3 -2
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +16 -8
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +11 -4
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/help.d.ts +3 -3
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +32 -23
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/inspect.d.ts.map +1 -1
- package/dist/cli/inspect.js +10 -7
- package/dist/cli/inspect.js.map +1 -1
- package/dist/cli/resume-guard.js +29 -2
- package/dist/cli/resume-guard.js.map +1 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +31 -32
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/session-json.d.ts +5 -2
- package/dist/cli/session-json.d.ts.map +1 -1
- package/dist/cli/session-json.js +5 -3
- package/dist/cli/session-json.js.map +1 -1
- package/dist/cli/session.d.ts.map +1 -1
- package/dist/cli/session.js +103 -36
- package/dist/cli/session.js.map +1 -1
- package/dist/execution/events.d.ts +8 -1
- package/dist/execution/events.d.ts.map +1 -1
- package/dist/execution/events.js.map +1 -1
- package/dist/execution/open-session.d.ts +7 -6
- package/dist/execution/open-session.d.ts.map +1 -1
- package/dist/execution/open-session.js +88 -59
- package/dist/execution/open-session.js.map +1 -1
- package/dist/execution/stream-turn.d.ts +4 -7
- package/dist/execution/stream-turn.d.ts.map +1 -1
- package/dist/execution/stream-turn.js +43 -22
- package/dist/execution/stream-turn.js.map +1 -1
- package/dist/interpretation/argv.d.ts +9 -5
- package/dist/interpretation/argv.d.ts.map +1 -1
- package/dist/interpretation/argv.js +2 -7
- package/dist/interpretation/argv.js.map +1 -1
- package/dist/interpretation/capabilities.d.ts +20 -0
- package/dist/interpretation/capabilities.d.ts.map +1 -1
- package/dist/interpretation/capabilities.js +27 -0
- package/dist/interpretation/capabilities.js.map +1 -1
- package/dist/interpretation/hints.js +1 -1
- package/dist/interpretation/hints.js.map +1 -1
- package/dist/interpretation/question.d.ts +6 -2
- package/dist/interpretation/question.d.ts.map +1 -1
- package/dist/interpretation/question.js +13 -4
- package/dist/interpretation/question.js.map +1 -1
- package/dist/interpretation/refusal.d.ts +1 -1
- package/dist/interpretation/refusal.d.ts.map +1 -1
- package/dist/interpretation/refusal.js +1 -1
- package/dist/interpretation/refusal.js.map +1 -1
- package/dist/interpretation/skills-selection.d.ts +18 -1
- package/dist/interpretation/skills-selection.d.ts.map +1 -1
- package/dist/interpretation/skills-selection.js +33 -6
- package/dist/interpretation/skills-selection.js.map +1 -1
- package/dist/knowledge/claude-code.d.ts.map +1 -1
- package/dist/knowledge/claude-code.js +21 -0
- package/dist/knowledge/claude-code.js.map +1 -1
- package/dist/knowledge/codex.d.ts.map +1 -1
- package/dist/knowledge/codex.js +16 -6
- package/dist/knowledge/codex.js.map +1 -1
- package/dist/knowledge/descriptor.d.ts +39 -12
- 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 +7 -0
- package/dist/knowledge/muse.js.map +1 -1
- package/dist/knowledge/pi.d.ts.map +1 -1
- package/dist/knowledge/pi.js +23 -4
- package/dist/knowledge/pi.js.map +1 -1
- package/package.json +3 -2
- package/src/cli/args.ts +16 -6
- package/src/cli/config.ts +13 -4
- package/src/cli/help.ts +32 -23
- package/src/cli/inspect.ts +8 -6
- package/src/cli/resume-guard.ts +26 -2
- package/src/cli/run.ts +32 -36
- package/src/cli/session-json.ts +11 -5
- package/src/cli/session.ts +122 -40
- package/src/execution/events.ts +9 -1
- package/src/execution/open-session.ts +99 -72
- package/src/execution/stream-turn.ts +53 -30
- package/src/interpretation/argv.ts +11 -12
- package/src/interpretation/capabilities.ts +50 -0
- package/src/interpretation/hints.ts +1 -1
- package/src/interpretation/question.ts +17 -7
- package/src/interpretation/refusal.ts +2 -1
- package/src/interpretation/skills-selection.ts +46 -8
- package/src/knowledge/claude-code.ts +21 -0
- package/src/knowledge/codex.ts +16 -6
- package/src/knowledge/descriptor.ts +40 -12
- package/src/knowledge/muse.ts +7 -0
- package/src/knowledge/pi.ts +23 -4
|
@@ -47,7 +47,15 @@ If and only if a genuine decision you cannot make defensibly blocks correct prog
|
|
|
47
47
|
|
|
48
48
|
Say nothing after the block and stop generating. The caller's user will answer, and the answer arrives as the next user message in this session - continue from it. For every choice you can make defensibly yourself, do not ask - decide, act, and state the decision you made.`;
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
export type QuestionMode = "ask" | "assume" | "none";
|
|
51
|
+
|
|
52
|
+
export const QUESTION_MODES: readonly QuestionMode[] = ["ask", "assume", "none"] as const;
|
|
53
|
+
|
|
54
|
+
export const isQuestionMode = (value: string): value is QuestionMode =>
|
|
55
|
+
(QUESTION_MODES as readonly string[]).includes(value);
|
|
56
|
+
|
|
57
|
+
/** Compose the transport preamble onto a prompt. `mode` selects which
|
|
58
|
+
* preamble to inject (ask/assume/none), and `channel` selects the
|
|
51
59
|
* contract wording: "turn" (exit-and-resume transport, hcn run) or
|
|
52
60
|
* "session" (live channel, hcn session). Idempotent: a prompt that
|
|
53
61
|
* already carries any preamble passes through unchanged - a turn-mode
|
|
@@ -55,12 +63,14 @@ Say nothing after the block and stop generating. The caller's user will answer,
|
|
|
55
63
|
* both; the already-composed contract stands). */
|
|
56
64
|
export const composeEscalatedPrompt = (
|
|
57
65
|
prompt: string,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
): string =>
|
|
61
|
-
prompt.startsWith(QUESTION_PREAMBLE_MARKER)
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
mode: QuestionMode,
|
|
67
|
+
channel: "turn" | "session" = "turn",
|
|
68
|
+
): string => {
|
|
69
|
+
if (prompt.startsWith(QUESTION_PREAMBLE_MARKER)) return prompt;
|
|
70
|
+
if (mode === "none") return prompt;
|
|
71
|
+
if (mode === "assume") return `${NO_ESCALATION_PREAMBLE}\n\n${prompt}`;
|
|
72
|
+
return `${channel === "session" ? SESSION_ESCALATION_PREAMBLE : ESCALATION_PREAMBLE}\n\n${prompt}`;
|
|
73
|
+
};
|
|
64
74
|
|
|
65
75
|
/** The structured question a worker asks (the block's fields). */
|
|
66
76
|
export interface QuestionBlock {
|
|
@@ -35,6 +35,7 @@ export type RefusalOption =
|
|
|
35
35
|
| "excludeTools"
|
|
36
36
|
| "skills"
|
|
37
37
|
| "autonomy"
|
|
38
|
+
| "questions"
|
|
38
39
|
| `discovery.${string}`;
|
|
39
40
|
|
|
40
41
|
/** One helper builds the message from the structured fields so message and
|
|
@@ -89,7 +90,7 @@ export const buildRefusalMessage = (
|
|
|
89
90
|
case "no-autonomy-mode":
|
|
90
91
|
return `${harness} has no unattended-run flag; ${supportedStr} - drop autonomy or route to a supporting harness (claude --dangerously-skip-permissions, codex/muse --yolo)`;
|
|
91
92
|
case "no-session-mode":
|
|
92
|
-
return `${harness} declares no persistent headless session mode; ${supportedStr} - use
|
|
93
|
+
return `${harness} declares no persistent headless session mode; ${supportedStr} - use hcn run --resume <id>`;
|
|
93
94
|
default: {
|
|
94
95
|
const exhaustive: never = issue;
|
|
95
96
|
return `${exhaustive as string} for ${harness}${optionPart}${detailSuffix}; ${supportedStr}`;
|
|
@@ -10,7 +10,15 @@
|
|
|
10
10
|
* OFF - `--settings '{"skillOverrides":{"<name>":"off",...}}'` for every
|
|
11
11
|
* known skill except the picks. Known set comes from the caller's root
|
|
12
12
|
* listing (same source that resolved the names).
|
|
13
|
-
* - codex
|
|
13
|
+
* - codex: per-skill disable via config-kv array `-c
|
|
14
|
+
* skills.config=[{path="...", enabled=false}]` for every known skill
|
|
15
|
+
* except the picks (complement-off, same inversion as claude). Uses
|
|
16
|
+
* `path` selector rather than `name` because a skill's frontmatter
|
|
17
|
+
* `name` need not equal its directory basename, and `path` is exact.
|
|
18
|
+
* The path for skill <n> under root <root> is <root>/<n>/SKILL.md.
|
|
19
|
+
* No global `skills.enabled` switch exists. Requires the known set
|
|
20
|
+
* and the resolved picks (root derived from picks via dirname).
|
|
21
|
+
* - muse: refuse (structural) with the standard hint shape.
|
|
14
22
|
*/
|
|
15
23
|
import type { HarnessDescriptor } from "../knowledge/descriptor.js";
|
|
16
24
|
import { ArgvRefusalError } from "./refusal.js";
|
|
@@ -20,6 +28,11 @@ export const basenameOf = (p: string): string => {
|
|
|
20
28
|
return i === -1 ? p : p.slice(i + 1);
|
|
21
29
|
};
|
|
22
30
|
|
|
31
|
+
const dirnameOf = (p: string): string => {
|
|
32
|
+
const i = p.lastIndexOf("/");
|
|
33
|
+
return i === -1 ? "" : p.slice(0, i);
|
|
34
|
+
};
|
|
35
|
+
|
|
23
36
|
export const renderSkillsSelection = (
|
|
24
37
|
h: HarnessDescriptor,
|
|
25
38
|
skills: readonly string[],
|
|
@@ -35,11 +48,9 @@ export const renderSkillsSelection = (
|
|
|
35
48
|
supportedBy: [
|
|
36
49
|
{ harness: "pi", spelling: "--skill" },
|
|
37
50
|
{ harness: "claude", spelling: "skillOverrides" },
|
|
51
|
+
{ harness: "codex", spelling: "-c skills.config" },
|
|
38
52
|
],
|
|
39
|
-
hint:
|
|
40
|
-
h.name === "codex"
|
|
41
|
-
? "codex discovers skills from its own directory with no call-time surface - stage the skills into $CODEX_HOME/skills or pass their content in the prompt"
|
|
42
|
-
: "muse scopes skills by workspace trust with no per-skill surface - include the skill content in the prompt or use --trust-workspace for the whole registry",
|
|
53
|
+
hint: "muse scopes skills by workspace trust with no per-skill surface - include the skill content in the prompt or use --trust-workspace for the whole registry",
|
|
43
54
|
});
|
|
44
55
|
}
|
|
45
56
|
|
|
@@ -50,9 +61,9 @@ export const renderSkillsSelection = (
|
|
|
50
61
|
return tokens;
|
|
51
62
|
}
|
|
52
63
|
|
|
53
|
-
// claude: complement-off via settings JSON
|
|
54
|
-
//
|
|
55
|
-
//
|
|
64
|
+
// claude and codex: complement-off via CLI layer (settings JSON / config
|
|
65
|
+
// array). The descriptor-level render returns [] and tokens append in
|
|
66
|
+
// stream-turn / CLI.
|
|
56
67
|
return [];
|
|
57
68
|
};
|
|
58
69
|
|
|
@@ -71,3 +82,30 @@ export const claudeSkillOverridesArg = (
|
|
|
71
82
|
const json = JSON.stringify({ skillOverrides: offs });
|
|
72
83
|
return ["--settings", json];
|
|
73
84
|
};
|
|
85
|
+
|
|
86
|
+
/** Codex complement form: every known skill except the picks gets
|
|
87
|
+
* `{path="<root>/<name>/SKILL.md>", enabled=false}` via `-c
|
|
88
|
+
* skills.config=[...]`. Uses `path` (exact) over `name` because
|
|
89
|
+
* frontmatter name may diverge from directory basename. Root is derived
|
|
90
|
+
* from the picks' dirname (all picks share the same root); if picks is
|
|
91
|
+
* empty the complement cannot be rooted and we return [] (caller picks
|
|
92
|
+
* nothing - no integration point needs this, and the CLI layer never
|
|
93
|
+
* calls with empty picks). Empty complement returns [] (no flag). */
|
|
94
|
+
export const codexSkillConfigArg = (
|
|
95
|
+
knownSkills: readonly string[],
|
|
96
|
+
pickedPaths: readonly string[],
|
|
97
|
+
): string[] => {
|
|
98
|
+
const picks = new Set(pickedPaths.map(basenameOf));
|
|
99
|
+
// Derive root from first pick's dirname; all picks are under same root
|
|
100
|
+
// (skills-root guarantees this). Fall back to "" if no picks.
|
|
101
|
+
const root = pickedPaths.length > 0 ? dirnameOf(pickedPaths[0] as string) : "";
|
|
102
|
+
const entries: string[] = [];
|
|
103
|
+
for (const name of knownSkills) {
|
|
104
|
+
if (!picks.has(name)) {
|
|
105
|
+
const absPath = root ? `${root}/${name}/SKILL.md` : `${name}/SKILL.md`;
|
|
106
|
+
entries.push(`{path=${JSON.stringify(absPath)}, enabled=false}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (entries.length === 0) return [];
|
|
110
|
+
return ["-c", `skills.config=[${entries.join(", ")}]`];
|
|
111
|
+
};
|
|
@@ -51,6 +51,19 @@ export const claudeCode: HarnessDescriptor = deepFreeze({
|
|
|
51
51
|
// hooks (D-025). Token deltas require this exact output flag set.
|
|
52
52
|
// sessionMode.flags is the complete flag list after the binary, so -p
|
|
53
53
|
// lives here rather than being inherited from launch.baseFlags.
|
|
54
|
+
// claude has two session-id flags with opposite unknown-id behaviour:
|
|
55
|
+
// `--session-id` NAMES a fresh session (creates if missing, silent) while
|
|
56
|
+
// `--resume` RESTORES an existing one (refuses unknown id, exit 1
|
|
57
|
+
// "No conversation found" - verified phase11,
|
|
58
|
+
// test/fixtures/phase11-claude-session-resume/resume-unknown-id.stderr.txt).
|
|
59
|
+
// `idFlag` is the naming flag (`--session-id`); `resumeFlag` is the
|
|
60
|
+
// restoring flag (`--resume`) - distinct as data so the renderer cannot
|
|
61
|
+
// pick the wrong one. Unknown-id behaviour reuses `resume.onMissing:
|
|
62
|
+
// "error"` as the single source; sessionMode does not restate it.
|
|
63
|
+
// Verified phase11 (test/fixtures/phase11-claude-session-resume):
|
|
64
|
+
// `session-id-establish.ndjson` creates with `--session-id`, and
|
|
65
|
+
// `resume-restores.ndjson` restores the same id with `--resume` and
|
|
66
|
+
// recalls codeword "pomegranate".
|
|
54
67
|
flags: [
|
|
55
68
|
"-p",
|
|
56
69
|
"--input-format",
|
|
@@ -63,6 +76,7 @@ export const claudeCode: HarnessDescriptor = deepFreeze({
|
|
|
63
76
|
"project",
|
|
64
77
|
],
|
|
65
78
|
idFlag: "--session-id",
|
|
79
|
+
resumeFlag: "--resume",
|
|
66
80
|
input: { kind: "claude-sdk-user-message" },
|
|
67
81
|
turnEnd: { type: "result" },
|
|
68
82
|
identityProbe: null,
|
|
@@ -140,6 +154,13 @@ export const claudeCode: HarnessDescriptor = deepFreeze({
|
|
|
140
154
|
},
|
|
141
155
|
session: true,
|
|
142
156
|
},
|
|
157
|
+
// Escalation provenance transcribed from test/fixtures/phase7-questions/,
|
|
158
|
+
// committed 2026-08-19. `model` is empty because no fixture on that stream
|
|
159
|
+
// records a model id - absence of evidence, not an unset field.
|
|
160
|
+
escalation: {
|
|
161
|
+
supported: true,
|
|
162
|
+
observedOn: { harness: "claude", model: "", version: "2.1.235", date: "2026-08-19" },
|
|
163
|
+
},
|
|
143
164
|
turnOptions: {
|
|
144
165
|
effort: { kind: "effort", render: { kind: "flag-value", flag: "--effort" } },
|
|
145
166
|
// issue #48, live-verified 2.1.235: --system-prompt replaces the built-in
|
package/src/knowledge/codex.ts
CHANGED
|
@@ -101,6 +101,13 @@ export const codexCli: HarnessDescriptor = deepFreeze({
|
|
|
101
101
|
},
|
|
102
102
|
session: false,
|
|
103
103
|
},
|
|
104
|
+
// Escalation provenance transcribed from test/fixtures/phase7-questions/,
|
|
105
|
+
// committed 2026-08-19. `model` is empty because no fixture on that stream
|
|
106
|
+
// records a model id - absence of evidence, not an unset field.
|
|
107
|
+
escalation: {
|
|
108
|
+
supported: true,
|
|
109
|
+
observedOn: { harness: "codex", model: "", version: "0.146.1", date: "2026-08-19" },
|
|
110
|
+
},
|
|
104
111
|
turnOptions: {
|
|
105
112
|
effort: {
|
|
106
113
|
kind: "effort",
|
|
@@ -129,12 +136,15 @@ export const codexCli: HarnessDescriptor = deepFreeze({
|
|
|
129
136
|
render: { kind: "flag-value", flag: "--sandbox" },
|
|
130
137
|
},
|
|
131
138
|
},
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
// (
|
|
137
|
-
skills
|
|
139
|
+
// Tools: no built-in name lists; control is feature booleans
|
|
140
|
+
// (reachable per-call via -c key=value), sandbox, and approval policy.
|
|
141
|
+
// MCP servers do have per-tool keys (mcp_servers.<id>.tools.<tool>)
|
|
142
|
+
// but built-ins do not.
|
|
143
|
+
// Skills (verified 2026-08-22, codex 0.147.0, --strict-config probes):
|
|
144
|
+
// per-skill disable via config-kv array -c skills.config=[{path=...,
|
|
145
|
+
// enabled=false}] (selector path or name, validated per entry); also
|
|
146
|
+
// -c skills.bundled.enabled=false. No global skills.enabled switch.
|
|
147
|
+
skills: { loadFlag: null, overridesVia: "config-skills-array" },
|
|
138
148
|
tools: {
|
|
139
149
|
includeFlag: null,
|
|
140
150
|
excludeFlag: null,
|
|
@@ -52,6 +52,9 @@ export interface SessionInputContract {
|
|
|
52
52
|
readonly kind: SessionInputKind;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
export const SESSION_RESUME_FLAGS = ["--resume", "--session-id"] as const;
|
|
56
|
+
export type SessionResumeFlag = (typeof SESSION_RESUME_FLAGS)[number];
|
|
57
|
+
|
|
55
58
|
/** Consumers branch on these (session-limit: wait for reset; weekly-limit:
|
|
56
59
|
* route elsewhere), so the vocabulary is closed - a descriptor cannot invent
|
|
57
60
|
* a code a consumer has no arm for. */
|
|
@@ -302,19 +305,26 @@ export interface HarnessDescriptor {
|
|
|
302
305
|
* the binary to open one lucid-owned process serving many turns, or null
|
|
303
306
|
* when the harness has no such mode. This is the entire argv prefix after
|
|
304
307
|
* the binary - no launch flags are prepended. `idFlag` pins the
|
|
305
|
-
* caller-assigned session identity
|
|
306
|
-
* delimits one turn (claude: the `result`
|
|
307
|
-
* `agent_settled`). `identityProbe`, when present, names
|
|
308
|
-
* runner writes at spawn whose response carries the session
|
|
309
|
-
* is identity-silent at startup (spike evidence:
|
|
308
|
+
* caller-assigned session identity for a FRESH session (naming). `turnEnd`
|
|
309
|
+
* is the stdout record that delimits one turn (claude: the `result`
|
|
310
|
+
* record; pi rpc: `agent_settled`). `identityProbe`, when present, names
|
|
311
|
+
* a command the runner writes at spawn whose response carries the session
|
|
312
|
+
* id - pi rpc is identity-silent at startup (spike evidence:
|
|
310
313
|
* test/fixtures/pi-rpc-spike), so identity needs a round trip. */
|
|
311
314
|
readonly sessionMode: {
|
|
312
315
|
readonly flags: readonly string[];
|
|
313
|
-
/**
|
|
314
|
-
*
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
316
|
+
/** The flag that names a fresh session (caller-assigned identity at
|
|
317
|
+
* spawn). Null only when the harness mints the id itself. */
|
|
318
|
+
readonly idFlag: SessionResumeFlag | null;
|
|
319
|
+
/** The flag that carries an EXISTING session id to resume it in
|
|
320
|
+
* session mode, distinct from `idFlag` which names a fresh session.
|
|
321
|
+
* Closed vocabulary: pi reuses `--session-id` for both (verified
|
|
322
|
+
* phase10, test/fixtures/phase10-pi-rpc-resume); claude uses
|
|
323
|
+
* `--resume` to restore vs `--session-id` to name (verified phase11,
|
|
324
|
+
* test/fixtures/phase11-claude-session-resume). Unknown-id behavior
|
|
325
|
+
* is not duplicated here; it reuses `resume.onMissing` as the single
|
|
326
|
+
* source. */
|
|
327
|
+
readonly resumeFlag: SessionResumeFlag;
|
|
318
328
|
readonly input: SessionInputContract;
|
|
319
329
|
readonly turnEnd: Readonly<Record<string, string>>;
|
|
320
330
|
readonly identityProbe: { readonly command: string } | null;
|
|
@@ -407,6 +417,23 @@ export interface HarnessDescriptor {
|
|
|
407
417
|
readonly streamingByMode: Readonly<Record<HarnessMode, StreamingGranularity>>;
|
|
408
418
|
readonly session: boolean;
|
|
409
419
|
};
|
|
420
|
+
/** Escalation provenance: whether this harness and model were observed to
|
|
421
|
+
* emit the structured hcn-question block when instructed, and when that
|
|
422
|
+
* observation was made. `supported` is documented as "this harness and
|
|
423
|
+
* model were observed to emit the structured block when instructed", NOT
|
|
424
|
+
* as "this harness can ask" - live probes show models ask unprompted, so
|
|
425
|
+
* a capability-to-ask name would be false. `observedOn` is optional;
|
|
426
|
+
* absence means no probe exists for this harness/model. Staleness is
|
|
427
|
+
* derived from version comparison at read time, not stored. */
|
|
428
|
+
readonly escalation: {
|
|
429
|
+
readonly supported: boolean;
|
|
430
|
+
readonly observedOn?: {
|
|
431
|
+
readonly harness: string;
|
|
432
|
+
readonly model: string;
|
|
433
|
+
readonly version: string;
|
|
434
|
+
readonly date: string;
|
|
435
|
+
};
|
|
436
|
+
};
|
|
410
437
|
/** Per-call turn options this harness can express, keyed by the closed
|
|
411
438
|
* `TurnOptionKey` vocabulary. Absent keys are unexpressible on this
|
|
412
439
|
* harness; a call passing them must refuse. Discovery is a table of
|
|
@@ -430,10 +457,11 @@ export interface HarnessDescriptor {
|
|
|
430
457
|
* pi model registry, D-008). */
|
|
431
458
|
/** Caller-directed skills allowlist surface (issue #38). pi: repeatable
|
|
432
459
|
* load flag. claude: per-name "off" overrides via settings JSON.
|
|
433
|
-
* codex
|
|
460
|
+
* codex: per-skill "off" via config-kv array (-c skills.config).
|
|
461
|
+
* muse: null (structural gap - trust/config scoped only). */
|
|
434
462
|
readonly skills: {
|
|
435
463
|
readonly loadFlag: string | null;
|
|
436
|
-
readonly overridesVia: "settings-skilloverrides" | null;
|
|
464
|
+
readonly overridesVia: "settings-skilloverrides" | "config-skills-array" | null;
|
|
437
465
|
} | null;
|
|
438
466
|
readonly tools: {
|
|
439
467
|
readonly includeFlag: string | null;
|
package/src/knowledge/muse.ts
CHANGED
|
@@ -91,6 +91,13 @@ export const museCode: HarnessDescriptor = deepFreeze({
|
|
|
91
91
|
},
|
|
92
92
|
session: false,
|
|
93
93
|
},
|
|
94
|
+
// Escalation provenance transcribed from test/fixtures/phase7-questions/,
|
|
95
|
+
// committed 2026-08-19. `model` is empty because no fixture on that stream
|
|
96
|
+
// records a model id - absence of evidence, not an unset field.
|
|
97
|
+
escalation: {
|
|
98
|
+
supported: true,
|
|
99
|
+
observedOn: { harness: "muse", model: "", version: "0.2.1", date: "2026-08-19" },
|
|
100
|
+
},
|
|
94
101
|
turnOptions: {
|
|
95
102
|
effort: { kind: "effort", render: { kind: "flag-value", flag: "--reasoning-effort" } },
|
|
96
103
|
write: {
|
package/src/knowledge/pi.ts
CHANGED
|
@@ -55,10 +55,22 @@ export const piCli: HarnessDescriptor = deepFreeze({
|
|
|
55
55
|
// slice remains the proven vertical (D-003); this entry is the second.
|
|
56
56
|
sessionMode: {
|
|
57
57
|
flags: ["--mode", "rpc"],
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
|
|
58
|
+
// pi has TWO flags with similar names and opposite unknown-id behaviour:
|
|
59
|
+
// `--session <path|id>` requires an EXISTING id (live-verified
|
|
60
|
+
// "No session found" on a fresh uuid) while `--session-id <id>` creates
|
|
61
|
+
// it if missing. Session mode uses `--session-id`, the flag the one-shot
|
|
62
|
+
// resume grammar also uses (resume.flag). That is why `idFlag` is
|
|
63
|
+
// `--session-id` here and not null - the earlier `idFlag: null` comment
|
|
64
|
+
// described `--session`, a different flag. Verified phase10
|
|
65
|
+
// (test/fixtures/phase10-pi-rpc-resume): a second `pi --mode rpc
|
|
66
|
+
// --session-id <id>` against the same id restored the codeword
|
|
67
|
+
// "pomegranate" (rpc-resume.ndjson), while the first established it
|
|
68
|
+
// and warned on stderr "creating a new session with that id"
|
|
69
|
+
// (rpc-establish.stderr.txt). An unknown id is therefore created with
|
|
70
|
+
// a stderr warning, which is what `resume.onMissing: "create"` already
|
|
71
|
+
// records for the one-shot grammar - no duplication in sessionMode.
|
|
72
|
+
idFlag: "--session-id",
|
|
73
|
+
resumeFlag: "--session-id",
|
|
62
74
|
input: { kind: "pi-rpc-prompt" },
|
|
63
75
|
turnEnd: { type: "agent_settled" },
|
|
64
76
|
identityProbe: { command: "get_state" },
|
|
@@ -119,6 +131,13 @@ export const piCli: HarnessDescriptor = deepFreeze({
|
|
|
119
131
|
},
|
|
120
132
|
session: true,
|
|
121
133
|
},
|
|
134
|
+
// Escalation provenance transcribed from test/fixtures/phase7-questions/,
|
|
135
|
+
// committed 2026-08-19. `model` is empty because no fixture on that stream
|
|
136
|
+
// records a model id - absence of evidence, not an unset field.
|
|
137
|
+
escalation: {
|
|
138
|
+
supported: true,
|
|
139
|
+
observedOn: { harness: "pi", model: "", version: "0.84.2", date: "2026-08-19" },
|
|
140
|
+
},
|
|
122
141
|
turnOptions: {
|
|
123
142
|
effort: { kind: "effort", render: { kind: "flag-value", flag: "--thinking" } },
|
|
124
143
|
provider: { kind: "selector", render: { kind: "flag-value", flag: "--provider" } },
|