@tacuchi/agent-workflow-cli 21.1.0 → 21.3.0
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 +2 -2
- package/dist/adapters/git-cli.js +12 -7
- package/dist/adapters/git-cli.js.map +1 -1
- package/dist/application/capability/wrapper.js +11 -3
- package/dist/application/capability/wrapper.js.map +1 -1
- package/dist/application/markdown.js +16 -2
- package/dist/application/markdown.js.map +1 -1
- package/dist/application/self/hooks-toml.js +137 -17
- package/dist/application/self/hooks-toml.js.map +1 -1
- package/dist/application/self/host-states.js +87 -1
- package/dist/application/self/host-states.js.map +1 -1
- package/dist/application/self/install-hooks.js +62 -6
- package/dist/application/self/install-hooks.js.map +1 -1
- package/dist/application/self/install-skill.js +27 -7
- package/dist/application/self/install-skill.js.map +1 -1
- package/dist/application/workline-index-service.js +15 -2
- package/dist/application/workline-index-service.js.map +1 -1
- package/dist/cli/tui/components/host-admin-section.js +14 -2
- package/dist/cli/tui/components/host-admin-section.js.map +1 -1
- package/dist/cli/tui/data/workflow-content.js +8 -1
- package/dist/cli/tui/data/workflow-content.js.map +1 -1
- package/dist/cli/tui/tabs/workflow-tab.js +4 -1
- package/dist/cli/tui/tabs/workflow-tab.js.map +1 -1
- package/dist/domain/flow/authority.js +62 -0
- package/dist/domain/flow/authority.js.map +1 -1
- package/dist/domain/harnesses.js +103 -4
- package/dist/domain/harnesses.js.map +1 -1
- package/dist/domain/host-verification.js +8 -8
- package/dist/domain/structured-choice-stamp.js +106 -0
- package/dist/domain/structured-choice-stamp.js.map +1 -0
- package/package.json +1 -1
- package/skills/w/context/MANIFEST.json +5 -1
- package/skills/w/harness/HARNESS.md +10 -4
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-host structured-choice binding, rendered as the text an installed
|
|
3
|
+
* surface carries.
|
|
4
|
+
*
|
|
5
|
+
* Why a stamp at all: the canonical bundle is host-neutral on purpose, and the CLI
|
|
6
|
+
* does not present — it emits directives and a surface shows them. So the only
|
|
7
|
+
* thing that ever knew which host it was talking about was the INSTALL, and it was
|
|
8
|
+
* throwing that away: every wrapper on every host shipped the same neutral
|
|
9
|
+
* sentence, and an agent reading it had nothing telling it which mechanism to
|
|
10
|
+
* reach for. Runtime detection is not the answer either — `aw harness` legitimately
|
|
11
|
+
* answers `unknown` inside Kimi Code, which exports no env marker.
|
|
12
|
+
*
|
|
13
|
+
* So the binding is stamped at install, when the target IS known, and it is
|
|
14
|
+
* generated from {@link HarnessSpec.structuredChoice} and nothing else — a second
|
|
15
|
+
* hand-written copy per host is exactly the drift the catalog exists to prevent.
|
|
16
|
+
*
|
|
17
|
+
* It stays short by design. Every byte here lands in EVERY installed wrapper on
|
|
18
|
+
* that host and is read on every invocation, so this is a context cost, not free
|
|
19
|
+
* documentation: it says which mechanism, its ceilings, where the sentence goes,
|
|
20
|
+
* and when to fall back. The reasoning behind the rule lives in `HARNESS.md`.
|
|
21
|
+
*/
|
|
22
|
+
import { harnessByInstallTarget } from "./harnesses.js";
|
|
23
|
+
/**
|
|
24
|
+
* The universal floor, spelled the same way on every host — including the ones
|
|
25
|
+
* that reach it as a fallback. Kept in one constant so a host that degrades and a
|
|
26
|
+
* host that never had a mechanism describe the same thing identically: two
|
|
27
|
+
* wordings would read as two different fallbacks.
|
|
28
|
+
*/
|
|
29
|
+
const LABELED_MARKDOWN = "labeled markdown — every option as `Label — functional sentence`, the `flow` control (`Compactar`/`Cerrar`) always among them, answered by label or `Aceptar recomendaciones`";
|
|
30
|
+
const CONTENT_RULE = "Degrade the mechanism, never the content: no alternative is merged, truncated or dropped to fit, and any loss is declared as a degradation.";
|
|
31
|
+
/**
|
|
32
|
+
* The stamp for one host, as a markdown blockquote (no trailing newline).
|
|
33
|
+
*
|
|
34
|
+
* A blockquote because it is inserted into documents whose own body is markdown —
|
|
35
|
+
* a command wrapper, a synthesized skill, a capability skill — and a quote block
|
|
36
|
+
* reads as "this is about your host", not as another step of the procedure.
|
|
37
|
+
*/
|
|
38
|
+
export function renderStructuredChoiceStamp(spec) {
|
|
39
|
+
const binding = spec.structuredChoice;
|
|
40
|
+
const lines = [
|
|
41
|
+
`**Structured-choice on this host (\`${spec.installTarget}\`, stamped at install).**`,
|
|
42
|
+
];
|
|
43
|
+
if (binding.state === "native" && binding.tool !== null) {
|
|
44
|
+
lines[0] += ` Present every human and authorization boundary with \`${binding.tool}\`${ceilingClause(binding.ceilings)}.`;
|
|
45
|
+
lines.push(sentenceClause(binding.sentence, binding.sentenceMaxChars));
|
|
46
|
+
if (binding.customAnswer) {
|
|
47
|
+
lines.push("It already offers a free-text answer, so do not add an `Other` option of your own.");
|
|
48
|
+
}
|
|
49
|
+
// The condition leads: a reader who stops at the comma still knows WHEN this
|
|
50
|
+
// applies. Trailing it after the fallback's own long description buried it.
|
|
51
|
+
lines.push(`When ${binding.fallbackReason}, fall back to ${LABELED_MARKDOWN}.`);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
lines[0] += ` Present every human and authorization boundary as ${LABELED_MARKDOWN}.`;
|
|
55
|
+
lines.push(binding.tool === null
|
|
56
|
+
? `This host exposes no native selection surface: ${binding.fallbackReason}.`
|
|
57
|
+
: `Its \`${binding.tool}\` is not reachable: ${binding.fallbackReason}.`);
|
|
58
|
+
}
|
|
59
|
+
lines.push(CONTENT_RULE);
|
|
60
|
+
return lines.map((line) => `> ${line}`).join("\n");
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The stamp for an install target, host or shared destination.
|
|
64
|
+
*
|
|
65
|
+
* A shared skills dir is read by several hosts at once, so stamping one host's tool
|
|
66
|
+
* into it would name the wrong mechanism for every other reader. It gets the
|
|
67
|
+
* guaranteed floor plus where to resolve its own column — which is the honest
|
|
68
|
+
* answer, and the only one that cannot be wrong.
|
|
69
|
+
*/
|
|
70
|
+
export function stampForInstallTarget(target) {
|
|
71
|
+
const spec = harnessByInstallTarget(target);
|
|
72
|
+
if (spec !== null)
|
|
73
|
+
return renderStructuredChoiceStamp(spec);
|
|
74
|
+
return [
|
|
75
|
+
`> **Structured-choice on this host (\`${target}\` is a shared skills dir, stamped at install).**`,
|
|
76
|
+
"> Several hosts read this directory, so no single native mechanism can be named here.",
|
|
77
|
+
`> Present every human and authorization boundary as ${LABELED_MARKDOWN}; if the host you`,
|
|
78
|
+
"> are actually running in has a native surface, its column in `harness/HARNESS.md` §",
|
|
79
|
+
"> *Harness binding matrix* is the one to follow.",
|
|
80
|
+
`> ${CONTENT_RULE}`,
|
|
81
|
+
].join("\n");
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* A ceiling the host declares, or the explicit absence of one.
|
|
85
|
+
*
|
|
86
|
+
* The absent case does not fall silent: with no host ceiling the chassis' own
|
|
87
|
+
* `≤3 content questions` is what applies, and an agent told nothing would either
|
|
88
|
+
* invent a limit or ignore both.
|
|
89
|
+
*/
|
|
90
|
+
function ceilingClause(ceilings) {
|
|
91
|
+
const flowSlot = ", always reserving one question slot for the `flow` control (`Compactar`/`Cerrar`)";
|
|
92
|
+
if (ceilings === null) {
|
|
93
|
+
return `, whose per-call ceilings this host does not declare — keep the chassis' ≤3 content questions${flowSlot}`;
|
|
94
|
+
}
|
|
95
|
+
return `, at most ${ceilings.questions} questions per call and ${ceilings.options} options each${flowSlot}`;
|
|
96
|
+
}
|
|
97
|
+
/** Where the option's functional sentence goes, and whether the host caps it. */
|
|
98
|
+
function sentenceClause(sentence, maxChars) {
|
|
99
|
+
const cap = maxChars === null
|
|
100
|
+
? ""
|
|
101
|
+
: ` This host caps that sentence at ${maxChars} characters: a consequence that does not fit is a degradation to declare, never a sentence to trim.`;
|
|
102
|
+
return sentence === "field"
|
|
103
|
+
? `The option's label and its functional sentence go in their own fields.${cap}`
|
|
104
|
+
: `This host shows one visible option string, so render \`Label — functional sentence\`.${cap}`;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=structured-choice-stamp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structured-choice-stamp.js","sourceRoot":"","sources":["../../src/domain/structured-choice-stamp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAwC,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAE9F;;;;;GAKG;AACH,MAAM,gBAAgB,GACpB,+KAA+K,CAAC;AAElL,MAAM,YAAY,GAChB,6IAA6I,CAAC;AAEhJ;;;;;;GAMG;AACH,MAAM,UAAU,2BAA2B,CAAC,IAAiB;IAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC;IACtC,MAAM,KAAK,GAAG;QACZ,uCAAuC,IAAI,CAAC,aAAa,4BAA4B;KACtF,CAAC;IAEF,IAAI,OAAO,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QACxD,KAAK,CAAC,CAAC,CAAC,IAAI,0DAA0D,OAAO,CAAC,IAAI,KAAK,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;QAC1H,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC;QACvE,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YACzB,KAAK,CAAC,IAAI,CACR,oFAAoF,CACrF,CAAC;QACJ,CAAC;QACD,6EAA6E;QAC7E,4EAA4E;QAC5E,KAAK,CAAC,IAAI,CAAC,QAAQ,OAAO,CAAC,cAAc,kBAAkB,gBAAgB,GAAG,CAAC,CAAC;IAClF,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,CAAC,CAAC,IAAI,sDAAsD,gBAAgB,GAAG,CAAC;QACtF,KAAK,CAAC,IAAI,CACR,OAAO,CAAC,IAAI,KAAK,IAAI;YACnB,CAAC,CAAC,kDAAkD,OAAO,CAAC,cAAc,GAAG;YAC7E,CAAC,CAAC,SAAS,OAAO,CAAC,IAAI,wBAAwB,OAAO,CAAC,cAAc,GAAG,CAC3E,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAqB;IACzD,MAAM,IAAI,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,2BAA2B,CAAC,IAAI,CAAC,CAAC;IAC5D,OAAO;QACL,yCAAyC,MAAM,mDAAmD;QAClG,uFAAuF;QACvF,uDAAuD,gBAAgB,mBAAmB;QAC1F,sFAAsF;QACtF,kDAAkD;QAClD,KAAK,YAAY,EAAE;KACpB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,SAAS,aAAa,CAAC,QAAuD;IAC5E,MAAM,QAAQ,GACZ,oFAAoF,CAAC;IACvF,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,OAAO,gGAAgG,QAAQ,EAAE,CAAC;IACpH,CAAC;IACD,OAAO,aAAa,QAAQ,CAAC,SAAS,2BAA2B,QAAQ,CAAC,OAAO,gBAAgB,QAAQ,EAAE,CAAC;AAC9G,CAAC;AAED,iFAAiF;AACjF,SAAS,cAAc,CAAC,QAA8B,EAAE,QAAuB;IAC7E,MAAM,GAAG,GACP,QAAQ,KAAK,IAAI;QACf,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,oCAAoC,QAAQ,qGAAqG,CAAC;IACxJ,OAAO,QAAQ,KAAK,OAAO;QACzB,CAAC,CAAC,yEAAyE,GAAG,EAAE;QAChF,CAAC,CAAC,wFAAwF,GAAG,EAAE,CAAC;AACpG,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tacuchi/agent-workflow-cli",
|
|
3
|
-
"version": "21.
|
|
3
|
+
"version": "21.3.0",
|
|
4
4
|
"description": "Runtime CLI for Workline — the stages + loops + artifacts system for agent work. Bundles the universal `w` skill set under `skills/w/` (slash commands `/w:*`: spec-new/spec-refine, plan-new/plan-exec, quick, persist, workspace-init, export-*); `self install --target <host>` copies SKILL + commands + hooks into the host. Pluggable capability skills via `.workflow/skills.toml`. Multi-empresa parametrization via `profile.json` cascade. Namespace auto-detected from any `.<ns>/sessions/` dir in CWD; default `workflow`.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -50,7 +50,11 @@
|
|
|
50
50
|
"plan.entry-gap-structural": "phases, contracts or journey are missing, or temporary behavior leaves its boundary undeclared",
|
|
51
51
|
"plan.deviation-structural": "the change that appeared while implementing touches a contract, the participating components, the phase order or the simulation boundary",
|
|
52
52
|
"plan.deviation-functional": "the change that appeared while implementing touches the expected result, the scope, a business rule or an acceptance criterion",
|
|
53
|
-
"chassis.context-pressure": "a fresh reader would need the CHECKPOINT to continue this run"
|
|
53
|
+
"chassis.context-pressure": "a fresh reader would need the CHECKPOINT to continue this run",
|
|
54
|
+
"plan.tasks-to-mark": "at least one task of the batch finished its local work and its checkbox is still open",
|
|
55
|
+
"plan.plan-closable": "every phase of the plan will be validated once this batch closes, so the plan can be sealed done",
|
|
56
|
+
"plan.commit-pending": "at least one affected source carries uncommitted changes from this batch",
|
|
57
|
+
"quick.db-touched": "the task read from or wrote to a database, so there is a statement to derive into the session script"
|
|
54
58
|
},
|
|
55
59
|
"commands": {
|
|
56
60
|
"quick": {
|
|
@@ -47,13 +47,13 @@ The capabilities the harness layer depends on, with their universal fallback (wh
|
|
|
47
47
|
|
|
48
48
|
## Harness binding matrix
|
|
49
49
|
|
|
50
|
-
Concrete mechanism per harness (matrix base verified **Jul-2026**; the `structured-choice` row
|
|
50
|
+
Concrete mechanism per harness (matrix base verified **Jul-2026**; the `structured-choice` row re-verified **2026-08-04** and the Codex `hooks` row **2026-08-05**, both against the INSTALLED runtimes plus real runs — not docs; `~` partial). Antigravity CLI reuses Gemini's surfaces (`~/.gemini/`); Oz reuses Warp's (they share the **Warp / Oz** column, with MCP via flag — see the note under the matrix).
|
|
51
51
|
|
|
52
52
|
| Capability | Claude Code | Codex | Kimi Code | Gemini / Antigravity | OpenCode | Crush | Warp / Oz | Generic |
|
|
53
53
|
|---|---|---|---|---|---|---|---|---|
|
|
54
54
|
| command-invocation | `.claude/commands/` (slash) | skills only (`$` mention; no commands dir, prompts removed) | skills only, as `/skill:<name>` (no commands dir) | skills only in agy (system slash commands; `.gemini/commands/*.toml` = legacy Gemini CLI) | `.opencode/command/` | `.crush/commands` (palette) + user-invocable skills | skills as `/name` | text |
|
|
55
55
|
| procedure-loading (skills) | `SKILL.md` `.claude/skills` | `SKILL.md` `.agents/skills` | `SKILL.md` `.kimi-code/skills`+`.agents/skills` (user and project tiers) | `SKILL.md` (agentskills) | `SKILL.md` `.opencode`+`.claude`+`.agents` | `SKILL.md` `~/.config/crush`+`.agents`+`.claude` (`.crush/skills` is project-only) | `SKILL.md` `.agents`+`.warp`+`.claude` | read-and-follow `.md` |
|
|
56
|
-
| structured-choice | `AskUserQuestion` (**main-agent only**; 1–4 questions, 2–4 options; label + description) | `request_user_input`
|
|
56
|
+
| structured-choice | `AskUserQuestion` (**main-agent only**; 1–4 questions, 2–4 options; label + description; free-text always offered) | `request_user_input` **not reachable** (~): its router refuses it in Default mode and exec mode never offers it; opt-in `default_mode_request_user_input` still *under development* → labeled markdown | `AskUserQuestion` (1–4 questions, 2–4 options; label + description; free-text offered) — **not called in `auto`/non-interactive mode** by the host's own rule → labeled markdown | `AskQuestion` (Antigravity/`agy`, the live binary: option `text` only, **no description field** → `Label — sentence`; write-in; no ceiling declared). `ask_user` is the retired Gemini CLI's and is **absent** from `agy` | `question` (label + description as separate fields; `custom` free-text on by default; no ceiling declared) — **denied in a non-interactive run** → labeled markdown | `question` (≤5 questions, ≤5 choices; description required per question <300 chars and **per choice <100 chars**; automatic fill-in) | no structured-choice surface → labeled markdown | labeled markdown (label + sentence) |
|
|
57
57
|
| compaction | `/compact` | Pre/PostCompact hooks | `/compact` + Pre/PostCompact hooks | ~ | `session.compacted` | ~ | ~ | CHECKPOINT + resume |
|
|
58
58
|
| subagent-dispatch | `Task` (parallel) | `SubagentStart` / agents | sub-agents (`SubagentStart`/`SubagentStop`) | agents (`.gemini/agents`) | `.opencode/agent/*.md` | ~ | ~ (cloud agents) | inline |
|
|
59
59
|
| persistent-context | `CLAUDE.md` (does **not** read AGENTS.md → symlink) | `AGENTS.md` | `AGENTS.md` (hierarchical) | `GEMINI.md` + `AGENTS.md` | `AGENTS.md` | `CRUSH.md` + `AGENTS.md` | `AGENTS.md` (auto) | `AGENTS.md` |
|
|
@@ -65,11 +65,17 @@ Concrete mechanism per harness (matrix base verified **Jul-2026**; the `structur
|
|
|
65
65
|
|
|
66
66
|
> **Kimi Code caveats** (verified 2026-07-29 vs the shipped v0.29.2 binary + live probes): it exports **no env markers** to its subprocesses, so `aw harness` legitimately answers `unknown` inside it and detection goes through binary + config dir. Its hooks live **only** in the user-global `config.toml` — there is no project-level config — and their schema is `event`/`matcher`/`command`/`timeout`, so the bundled JSON template is *transformed*, not copied: `type: "prompt"` hooks cannot be expressed and are reported as skipped, and matchers are carried only for the tool-name events.
|
|
67
67
|
|
|
68
|
+
> **Codex hooks caveats** (verified **2026-08-05** vs codex-cli 0.146.0 + real runs). Its user-level hooks are **not** in `config.toml`: they live in **`~/.codex/hooks.json`** with the **same JSON shape as Claude's** — `{"hooks": {"<Event>": [{"matcher": …, "hooks": [{"type": "command", "command": …, "timeout": N}]}]}}`. The event enum (`HookEventsToml`) is `PreToolUse` · `PermissionRequest` · `PostToolUse` · `PreCompact` · `PostCompact` · `SessionStart` · `SessionEnd` · `UserPromptSubmit` · `SubagentStart` · `SubagentStop` · `Stop`, so **all 5 events of the bundled template fit**, and its handlers admit `command`, **`prompt`** and `agent` — the `type: "prompt"` hook kimi cannot express, codex can. One observed limit: `SessionEnd` clamps its timeout to **3 s**.
|
|
69
|
+
>
|
|
70
|
+
> **But writing that file does not arm it, and that is why Workline does not manage hooks here.** Codex requires an **interactive human review per new or changed hook** (`New hook - review required`, `Modified since last trusted - review required`) and persists the decision as `trusted_hash` under `[hooks.state]`, keyed `"<file>:<event_snake_case>:<i>:<j>"`; the hash pins the command, so any edit re-requires review. Probe: in a clean `CODEX_HOME` a freshly written `hooks.json` was **read and validated** (it clamped a timeout) yet **no hook ran** across two consecutive runs, and codex recorded no trust entry of its own. Forging that hash would forge the person's security approval, so the surfaces say **available, not armed** and name this reason. `--dangerously-bypass-hook-trust` is per-invocation and self-describing. Plugin-bundled hooks skip the review (`Managed hooks are always on`), which is the route to take if this is ever revisited.
|
|
71
|
+
|
|
68
72
|
> **Notes (field research Aug-2026):** **`SKILL.md` skills** are the **universal** portable unit — **every harness in the matrix** supports them (Codex added them Dec-2025; **`.agents/skills` is the cross-host anchor**, read by Codex/OpenCode/Crush/Warp/Oz/**Kimi Code** — every host except Claude Code, which reads only `.claude/skills`). The **enforcement layer** is **not Claude-exclusive**: Codex + Gemini use a near-identical protocol (`permissionDecision:deny` / exit 2) and OpenCode blocks via `throw` in a JS plugin; Crush/Warp only offer **coarse** allow/deny (no custom per-command logic) → there, conventions stay **advisory** + allow/deny lists. Enforced **plan mode** is never trusted for safety; git-safe (invariant #5) is our own — though a host-planner's *output* (the plan it built) is adoptable input (`../commands/plan-new.md` § *Input resolution*, mode 4). **MCP** is universal (each host its file/key). The **guaranteed floor** (last column) runs the full model.
|
|
69
73
|
|
|
70
74
|
> **structured-choice routing.** A native binding qualifies only when the current client exposes it and can display the option's functional sentence without loss. When it has separate fields, map the semantic label and sentence to them; when it exposes one visible option string, render `Label — functional sentence`. Otherwise use labeled markdown. Respect the per-call ceilings in the row and reserve one question slot for `flow`; carry overflow into a later call. If the native tool already injects a custom/free-text option, do not add a duplicate `Other` option.
|
|
71
75
|
|
|
72
|
-
> **structured-choice evidence (
|
|
76
|
+
> **structured-choice evidence — what a RUN proved (2026-08-04).** Probed against the runtimes installed on the verification machine: claude 2.1.222 · codex-cli 0.146.0 · kimi 0.31.1 · opencode 1.18.5 · crush v0.87.0 · agy 1.0.16 · oz v0.2026.07.29. **Codex**: a real run hit `codex_core::tools::router: error=request_user_input is unavailable in Default mode` and the model listed its own tool set without it; `codex features list` reports the opt-in as `under development false`, and `[tools] experimental_request_user_input` is not a boolean but a table. **Kimi**: the tool is in its default agent's list, and in `--prompt` the host's own rule (`Do NOT call AskUserQuestion while auto mode is active`) made it degrade to labeled markdown by itself, options intact. **OpenCode**: the exported session of a real `opencode run` carries `{"permission":"question","action":"deny"}`. **Crush**: its ceilings and the per-choice 100-char cap were read from the installed binary; the run could not be verified (expired auth). **Antigravity (`agy`)**: its shipped proto declares `AskQuestionEntry {options, is_multi_select, write_in_response}` and `AskQuestionOption {id, text}` — no description field — while `ask_user` does not appear in the binary at all. **Warp** ships no CLI, so its row rests on docs; **Oz**'s launcher is a 122-byte Bash shim inside Warp.app with no tool surface of its own.
|
|
77
|
+
>
|
|
78
|
+
> **Doc references (checked 2026-08-02):** [Claude Code](https://code.claude.com/docs/en/agent-sdk/user-input) · [Codex App Server](https://learn.chatgpt.com/docs/app-server.md) · [Kimi Code](https://moonshotai.github.io/kimi-code/en/reference/tools.html) · [Gemini CLI](https://geminicli.com/docs/tools/ask-user/) · [Antigravity changelog](https://github.com/google-antigravity/antigravity-cli/blob/main/CHANGELOG.md) · [OpenCode](https://dev.opencode.ai/docs/tools/) · [Crush source](https://github.com/charmbracelet/crush) · [Warp agents](https://docs.warp.dev/agent-platform/getting-started/agents-in-warp) / [Oz CLI](https://docs.warp.dev/reference/cli). Public docs do not expose Antigravity's full question schema or a dedicated Warp/Oz structured-choice schema; the row says so instead of inferring one.
|
|
73
79
|
|
|
74
80
|
> **Oz (Warp's cloud sibling).** `oz agent run` is a cloud agent orchestrator that **reuses Warp's surfaces**: same skills (`.agents/skills`, top-level dirs like Warp) and `AGENTS.md`. No dedicated structured-choice schema is documented for Oz itself, so a direct Oz run uses labeled markdown; if Oz delegates to another harness and exposes that harness's native question surface, follow that harness's own binding. Oz differs from Warp in three points: **detection** via `OZ_RUN_ID` (takes priority over Warp when both markers coexist); **MCP without a config file** — the JSON is passed via the `--mcp` flag of `oz agent run` (or the `OZ_MCP_CONFIG` env), it never writes `.warp/.mcp.json`; and **no plugin or hooks** (advisory enforcement, like Warp). Hence it shares the **Warp / Oz** column with those caveats.
|
|
75
81
|
|
|
@@ -134,7 +140,7 @@ Each command's **contract** (Flow, Trigger, Input, Mode, …) is agnostic. The *
|
|
|
134
140
|
|
|
135
141
|
## Status
|
|
136
142
|
|
|
137
|
-
Capability model + binding matrix **defined** and **validated** with field research (base **Jul-2026**; `structured-choice`
|
|
143
|
+
Capability model + binding matrix **defined** and **validated** with field research (base **Jul-2026**; the `structured-choice` row re-verified **2026-08-04** and the Codex `hooks` row **2026-08-05** against the INSTALLED runtimes plus real runs — see the evidence note under the matrix for which claims rest on a run and which still rest on a doc).
|
|
138
144
|
|
|
139
145
|
The catalog counts **8 hosts** — `claude-code`, `codex`, `oz`, `warp`, `gemini`, `opencode`, `crush`, `kimi` — each with its own entry in `domain/harnesses.ts`. The columns above group two pairs that share a config surface (Warp/Oz, Gemini/Antigravity), which is a presentation choice, not a second taxonomy: the host set is whatever `HARNESSES` says. Anti-drift guards cover the CODE projections (TUI, install targets, doctor, detection); `chassis-consistency.test.ts` additionally parses the `structured-choice` row and asserts every host binding or explicit limitation. Support levels: **official** — Claude Code, Codex, Warp, Gemini/Antigravity, Kimi Code; **best-effort** — Oz, OpenCode, Crush. `agents` (`~/.agents/skills`) is a **shared destination**, never a host.
|
|
140
146
|
|