@qwen-code/qwen-code 0.15.12-preview.2 → 0.16.0-nightly.20260522.48b0a8bfc
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/bundled/qc-helper/docs/configuration/settings.md +69 -71
- package/bundled/qc-helper/docs/features/_meta.ts +2 -0
- package/bundled/qc-helper/docs/features/approval-mode.md +119 -2
- package/bundled/qc-helper/docs/features/auto-mode.md +263 -0
- package/bundled/qc-helper/docs/features/commands.md +11 -10
- package/bundled/qc-helper/docs/features/lsp.md +87 -10
- package/bundled/qc-helper/docs/features/skills.md +3 -0
- package/bundled/qc-helper/docs/features/structured-output.md +309 -0
- package/bundled/qc-helper/docs/features/sub-agents.md +47 -5
- package/bundled/qc-helper/docs/qwen-serve.md +179 -24
- package/bundled/qc-helper/docs/reference/keyboard-shortcuts.md +11 -11
- package/bundled/review/SKILL.md +12 -3
- package/bundled/stuck/SKILL.md +124 -0
- package/chunks/{agent-UQY6A6OS.js → agent-GRCSD6XI.js} +15 -17
- package/chunks/{anthropicContentGenerator-4QE6LTVV.js → anthropicContentGenerator-RQJNXJIY.js} +7 -4
- package/chunks/{askUserQuestion-QFSCBTUO.js → askUserQuestion-PQPMPNM3.js} +2 -2
- package/chunks/{ca-VQSV6JHA.js → ca-UZ7BANMN.js} +29 -3
- package/chunks/{chunk-B7ZL7HUA.js → chunk-4AOCVI6J.js} +2 -1
- package/chunks/{chunk-PCL3EJGY.js → chunk-4U3O4VDA.js} +4722 -3823
- package/chunks/{chunk-AEJ2DKLP.js → chunk-C6WMLUNB.js} +1 -1
- package/chunks/{chunk-FSYKVGER.js → chunk-C7PY32RJ.js} +115 -23
- package/chunks/{chunk-JMZQICAL.js → chunk-CAVZVZX6.js} +2 -2
- package/chunks/{chunk-CAWKL3UC.js → chunk-CSWBPY3P.js} +2 -2
- package/chunks/{chunk-G27O2LD2.js → chunk-D5NTAHYL.js} +1 -1
- package/chunks/{chunk-BXNCPI75.js → chunk-DMIMF3CG.js} +2 -2
- package/chunks/{chunk-OCC4MZRS.js → chunk-F23NCRJ2.js} +1 -1
- package/chunks/{chunk-SOIEFHIK.js → chunk-G7YTSRES.js} +1 -100
- package/chunks/{chunk-SQNQIOD5.js → chunk-GGNTZ2NH.js} +92 -21
- package/chunks/{chunk-FKVKVE6N.js → chunk-KXZ4TJB4.js} +1 -1
- package/chunks/{chunk-CBVB66WY.js → chunk-L5E26RN6.js} +2 -2
- package/chunks/{chunk-UXW7MYAW.js → chunk-MAY32HXD.js} +376 -1
- package/chunks/{chunk-2WFU3IUH.js → chunk-OZO6R4KI.js} +11796 -4086
- package/chunks/{chunk-CM2IESUE.js → chunk-PR4T27R7.js} +1 -1
- package/chunks/{chunk-FYMSCRHM.js → chunk-PVVL5Q3W.js} +32 -1
- package/chunks/{chunk-MXBWOU2L.js → chunk-TM4RMEYN.js} +17 -349
- package/chunks/{chunk-YHEAJFCI.js → chunk-USE2VQ5P.js} +3 -0
- package/chunks/chunk-UY2AYRZF.js +19126 -0
- package/chunks/{chunk-GJXIKCKL.js → chunk-XP27SJMH.js} +76 -5
- package/chunks/chunk-XTYIPAUG.js +379 -0
- package/chunks/{chunk-TPGOGCWM.js → chunk-YJLGXDQJ.js} +1 -1
- package/chunks/{contextCommand-MQRG6RMG.js → contextCommand-O2OLCMOG.js} +17 -19
- package/chunks/{cron-create-WUTD5ZTH.js → cron-create-IGYXQVG4.js} +28 -2
- package/chunks/{cron-delete-N3UQYCRA.js → cron-delete-ETKIZCWT.js} +2 -2
- package/chunks/{cron-list-Z6RJJ4YH.js → cron-list-BVCUSWRU.js} +2 -2
- package/chunks/{de-M2IPQRBS.js → de-V4IE2OOZ.js} +29 -3
- package/chunks/{dist-RRYNPBOE.js → dist-4L54HRX2.js} +2 -2
- package/chunks/{dist-WP4AH3VK.js → dist-BXDUQ2QY.js} +1 -1
- package/chunks/{dist-M6GFCZ7S.js → dist-MN2PDDPR.js} +1 -1
- package/chunks/{edit-3KCBTA25.js → edit-NRL5ZYWP.js} +32 -18
- package/chunks/{en-N5GMPCVT.js → en-HGJ2SPLM.js} +32 -3
- package/chunks/{enter-worktree-VWS5QZTU.js → enter-worktree-TLT6EXVN.js} +41 -17
- package/chunks/{exit-worktree-RVXFWAPD.js → exit-worktree-PE7RZMLE.js} +41 -17
- package/chunks/{exitPlanMode-UL5DILDG.js → exitPlanMode-JYMKAHYD.js} +15 -17
- package/chunks/{fr-BTHRYEXO.js → fr-CJULI7ZX.js} +29 -3
- package/chunks/{geminiContentGenerator-O2OPGHJG.js → geminiContentGenerator-GYIOVT4A.js} +3 -3
- package/chunks/{glob-57BSREPN.js → glob-T7KIVIWT.js} +15 -17
- package/chunks/{grep-XO5JOC7T.js → grep-4KI7ZJR5.js} +15 -17
- package/chunks/{ja-D63TAEBO.js → ja-L7CHRQEW.js} +29 -3
- package/chunks/{keychain-token-storage-DMFP5IJM.js → keychain-token-storage-335UOLJ6.js} +2 -2
- package/chunks/{ls-SUILOZZB.js → ls-7HD6XG3V.js} +3 -3
- package/chunks/{lsp-6TQBWVMZ.js → lsp-ZZSFCIWD.js} +2 -2
- package/chunks/{monitor-BECPGO3K.js → monitor-L5YPPGVO.js} +45 -26
- package/chunks/notebook-edit-HYXLIC3D.js +756 -0
- package/chunks/{openaiContentGenerator-KEZQHIRM.js → openaiContentGenerator-XRP5JM6P.js} +12 -11
- package/chunks/{pt-XUV7FSKC.js → pt-M6JULLEQ.js} +29 -3
- package/chunks/{qwenContentGenerator-RPMRXTNH.js → qwenContentGenerator-EOX3HZXX.js} +17 -19
- package/chunks/{qwenOAuth2-JSQ7EPR3.js → qwenOAuth2-EEJGROP7.js} +9 -3
- package/chunks/{read-file-LGHEIQNH.js → read-file-3XOYPTIB.js} +7 -8
- package/chunks/{ripGrep-6SFSXZ2G.js → ripGrep-5QUURWGU.js} +15 -17
- package/chunks/{ru-7KHWMN3A.js → ru-QILM4HBC.js} +29 -3
- package/chunks/{send-message-Q2JRAC3J.js → send-message-ULK4MQXJ.js} +23 -2
- package/chunks/{serve-27O2AFE3.js → serve-AZ44ZNWF.js} +8864 -1351
- package/chunks/{shell-J7K5KYCH.js → shell-O6OTTSIP.js} +15 -17
- package/chunks/{skill-2R7P4ATS.js → skill-MUGS3H4M.js} +23 -10
- package/chunks/{src-CGEDVW67.js → src-YCQBAYCP.js} +292 -28
- package/chunks/{syntheticOutput-S4DRGMQM.js → syntheticOutput-IS2X5OZ2.js} +3 -3
- package/chunks/{task-stop-7THHVAQS.js → task-stop-7QSJGSSP.js} +2 -2
- package/chunks/{todoWrite-WKUGUTPX.js → todoWrite-7CVACFUX.js} +3 -3
- package/chunks/{tool-search-XOH3ZWVS.js → tool-search-UFDQJOXT.js} +7 -8
- package/chunks/{web-fetch-OZE6ZQUF.js → web-fetch-ENQ2I5JA.js} +7 -4
- package/chunks/{write-file-74NQ27Q2.js → write-file-GBLDDYQT.js} +29 -18
- package/chunks/{zh-VGHU6XBB.js → zh-PWL2NKY3.js} +32 -3
- package/chunks/{zh-TW-O36Q4V7E.js → zh-TW-S3YGWICZ.js} +32 -3
- package/cli.js +61338 -76795
- package/locales/ca.js +44 -5
- package/locales/de.js +44 -5
- package/locales/en.js +49 -5
- package/locales/fr.js +45 -5
- package/locales/ja.js +43 -5
- package/locales/pt.js +43 -5
- package/locales/ru.js +43 -5
- package/locales/zh-TW.js +46 -4
- package/locales/zh.js +46 -4
- package/package.json +2 -2
- package/chunks/chunk-5P5XGNYH.js +0 -93
- package/chunks/chunk-SYCJMSIJ.js +0 -82
- package/chunks/chunk-Y6Z2O3WR.js +0 -33
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# Structured Output (`--json-schema`)
|
|
2
|
+
|
|
3
|
+
Constrain the model's final answer to a JSON Schema you supply. Qwen
|
|
4
|
+
Code registers a synthetic terminal tool the model is required to call,
|
|
5
|
+
parses the call's arguments against your schema, and exposes the
|
|
6
|
+
validated payload on stdout (or in the JSON / stream-json result
|
|
7
|
+
envelope). The first valid call ends the run.
|
|
8
|
+
|
|
9
|
+
Headless only — works with `qwen -p`, a positional prompt, or a prompt
|
|
10
|
+
piped via stdin.
|
|
11
|
+
|
|
12
|
+
## Quick start
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
qwen --prompt "Summarize the changes in HEAD with risk_level" \
|
|
16
|
+
--json-schema '{
|
|
17
|
+
"type": "object",
|
|
18
|
+
"properties": {
|
|
19
|
+
"summary": { "type": "string" },
|
|
20
|
+
"risk_level": { "type": "string", "enum": ["low", "medium", "high"] }
|
|
21
|
+
},
|
|
22
|
+
"required": ["summary", "risk_level"],
|
|
23
|
+
"additionalProperties": false
|
|
24
|
+
}'
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Output on stdout (default `--output-format text`):
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{ "summary": "…", "risk_level": "low" }
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The line is exactly the JSON-stringified payload + newline — no
|
|
34
|
+
envelope, no event log. Pipe it straight into `jq` or another consumer.
|
|
35
|
+
|
|
36
|
+
In **text** mode, stdout is reserved for the JSON payload on success
|
|
37
|
+
and is empty on failure; error messages and log lines go to stderr.
|
|
38
|
+
That makes `$(qwen --json-schema …) || exit 1` capture patterns safe
|
|
39
|
+
under text mode — failures land in stderr, not mixed into the captured
|
|
40
|
+
variable. The model's incidental prose during planning is **not**
|
|
41
|
+
mirrored to stderr either — text mode discards it; reach for
|
|
42
|
+
`--output-format json` or `stream-json` if you need to see it.
|
|
43
|
+
|
|
44
|
+
In `--output-format json` and `stream-json`, the failure result
|
|
45
|
+
message is emitted on **stdout** alongside the success path (as the
|
|
46
|
+
final element of the JSON array, or the terminating `result` line on
|
|
47
|
+
the JSONL stream). Not all failure modes emit a result to stdout —
|
|
48
|
+
max-session-turns (exit 53) and signal interrupts (exit 130) exit with
|
|
49
|
+
stderr output only. Check the exit code first; `is_error` on the
|
|
50
|
+
result object disambiguates within the subset of failures that do
|
|
51
|
+
produce a result event.
|
|
52
|
+
|
|
53
|
+
> **Empty schema:** Passing `{}` produces `{}` (an empty JSON object)
|
|
54
|
+
> on stdout. The model calls `structured_output` with no arguments;
|
|
55
|
+
> the upstream argument-normalisation path turns the empty function
|
|
56
|
+
> call into an empty-object payload, which passes validation against
|
|
57
|
+
> the empty schema and is emitted verbatim.
|
|
58
|
+
|
|
59
|
+
## Supplying the schema
|
|
60
|
+
|
|
61
|
+
Two equivalent forms:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Inline JSON literal
|
|
65
|
+
qwen -p "…" --json-schema '{"type":"object", "properties":{…}}'
|
|
66
|
+
|
|
67
|
+
# Read from a file
|
|
68
|
+
qwen -p "…" --json-schema @./schemas/summary.json
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The `@path` form expands `~`, normalizes the path, and reads the file
|
|
72
|
+
with `utf8` encoding.
|
|
73
|
+
|
|
74
|
+
> **Latency note:** Successful runs incur a shutdown holdback **capped
|
|
75
|
+
> at ~500 ms** while in-flight background agents flush their final
|
|
76
|
+
> notifications before the result is emitted. The holdback exits early
|
|
77
|
+
> if no background tasks are pending, so simple runs barely notice it;
|
|
78
|
+
> batch pipelines that fan out hundreds of `--json-schema` invocations
|
|
79
|
+
> against busy agents should account for this upper bound.
|
|
80
|
+
|
|
81
|
+
> **Security note:** Schemas may contain user-supplied regular
|
|
82
|
+
> expressions in `pattern` keywords. Ajv compiles these with the
|
|
83
|
+
> ECMAScript regex engine, which is vulnerable to catastrophic
|
|
84
|
+
> backtracking. Because tool arguments are always objects, the
|
|
85
|
+
> `pattern` keyword only fires inside string properties — a malicious
|
|
86
|
+
> schema like
|
|
87
|
+
> `{"type":"object","properties":{"value":{"type":"string","pattern":"(a+)+b"}}}`
|
|
88
|
+
> can hang the CLI when the model supplies a moderately long
|
|
89
|
+
> matching value. Only run `--json-schema` with schemas from sources
|
|
90
|
+
> you trust.
|
|
91
|
+
|
|
92
|
+
Validation at parse time:
|
|
93
|
+
|
|
94
|
+
- The file must be a regular file (no FIFOs, character devices, or
|
|
95
|
+
directories).
|
|
96
|
+
- File size is capped at 4 MiB. Real-world JSON schemas are well under
|
|
97
|
+
this; multi-MiB files almost always indicate a wrong-path mistake.
|
|
98
|
+
- The schema must be valid JSON. For `@path` input, the parse error is
|
|
99
|
+
generic ("content of `<path>` is not valid JSON") rather than echoing
|
|
100
|
+
the SyntaxError detail, so a wrapping process that surfaces stderr
|
|
101
|
+
can't read a prefix of the file's contents back from the error.
|
|
102
|
+
- The schema must compile under the strict Ajv configuration —
|
|
103
|
+
typos like `propertees` are surfaced, but spec-valid patterns
|
|
104
|
+
(e.g. `required` without listing every key in `properties`) are
|
|
105
|
+
accepted.
|
|
106
|
+
- The schema root must accept object-typed values. Function-calling
|
|
107
|
+
APIs (Gemini, OpenAI, Anthropic) all require tool arguments to be
|
|
108
|
+
JSON objects, so a non-object root would register an unusable tool.
|
|
109
|
+
|
|
110
|
+
The root-acceptance check walks `type`, `const`, `enum`, `anyOf`,
|
|
111
|
+
`oneOf`, `allOf`, `not`, and `if`/`then`/`else` (best-effort for the
|
|
112
|
+
decidable cases). When in doubt it defers to Ajv at runtime.
|
|
113
|
+
|
|
114
|
+
> **Root `$ref` is rejected** by the parse-time check. If your schema
|
|
115
|
+
> reuses a definition via `$ref`, wrap it in `allOf`:
|
|
116
|
+
>
|
|
117
|
+
> ```jsonc
|
|
118
|
+
> // Rejected:
|
|
119
|
+
> { "$ref": "#/$defs/MyObj", "$defs": { "MyObj": { "type": "object", "properties": { "name": { "type": "string" } } } } }
|
|
120
|
+
>
|
|
121
|
+
> // Accepted (root accepts objects via the allOf branch):
|
|
122
|
+
> { "allOf": [{ "$ref": "#/$defs/MyObj" }], "$defs": { "MyObj": { "type": "object", "properties": { "name": { "type": "string" } } } } }
|
|
123
|
+
> ```
|
|
124
|
+
>
|
|
125
|
+
> `$ref` inside `anyOf` / `oneOf` / `allOf` is deferred to Ajv at
|
|
126
|
+
> runtime, so the wrapped form passes the root-acceptance check.
|
|
127
|
+
|
|
128
|
+
## Output shape per format
|
|
129
|
+
|
|
130
|
+
| `--output-format` | What goes to stdout |
|
|
131
|
+
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
132
|
+
| `text` (default) | `JSON.stringify(payload) + "\n"` — one line, the validated object. |
|
|
133
|
+
| `json` | A single JSON **array** of message objects (the full event log). The final element is the `type: "result"` message, which carries both `result` (`JSON.stringify(payload)`) and `structured_result` (the raw object). |
|
|
134
|
+
| `stream-json` | Each event on its own line as JSONL. The terminating `result` line carries `result` (stringified) and `structured_result` (raw object). |
|
|
135
|
+
|
|
136
|
+
In both JSON formats, prefer reading `structured_result` over `result`
|
|
137
|
+
when you want the object; `result` is the stringified form provided for
|
|
138
|
+
consumers that always expect a string in that field. For `--output-format
|
|
139
|
+
json`, read the last element of the array and pull `structured_result`
|
|
140
|
+
from there (e.g. `jq '.[-1].structured_result'`); for `stream-json`,
|
|
141
|
+
read the final `type: "result"` line on the stream.
|
|
142
|
+
|
|
143
|
+
## Restrictions
|
|
144
|
+
|
|
145
|
+
| Combination | Behavior |
|
|
146
|
+
| ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
147
|
+
| `--json-schema` + `-i` / `--prompt-interactive` | Rejected at parse time. The synthetic tool's "session ends now" message has no terminator in the TUI loop. |
|
|
148
|
+
| `--json-schema` + `--input-format stream-json` | Rejected at parse time. The single-shot terminal contract is incompatible with the long-lived stream-json input protocol. |
|
|
149
|
+
| `--json-schema` + `--acp` / `--experimental-acp` | Rejected at parse time. ACP runs its own turn loop that doesn't honor the synthetic-tool terminal contract. |
|
|
150
|
+
| `--json-schema` with no prompt and no piped stdin | Rejected at parse time. Headless mode needs a prompt — pass `-p`, a positional argument, or pipe one in. |
|
|
151
|
+
| `--bare` + `--json-schema` | Supported. The synthetic tool is registered alongside the bare three (`read_file`, `edit`, `run_shell_command`). |
|
|
152
|
+
| `--json-schema` inside a subagent | Tool is NOT registered. Only the main / drain turns of the top-level run honor the terminal contract; a subagent calling the tool would receive "session ends now" and then keep running because its loop has no terminator. |
|
|
153
|
+
|
|
154
|
+
## Retry and failure modes
|
|
155
|
+
|
|
156
|
+
> **Cost note.** Two things multiply token spend in a `--json-schema`
|
|
157
|
+
> run, both worth designing for:
|
|
158
|
+
>
|
|
159
|
+
> - **Schema embedded in every turn.** The schema ships as the
|
|
160
|
+
> `structured_output` function declaration's `parameters` block on
|
|
161
|
+
> every model request, not just the first. Large schemas (up to the
|
|
162
|
+
> 4 MiB parse cap) proportionally increase per-turn input tokens
|
|
163
|
+
> for the entire run.
|
|
164
|
+
> - **Each validation retry is a full model turn.** A schema the
|
|
165
|
+
> model misses repeatedly is multiplied per failure (request +
|
|
166
|
+
> inference + response). Keep schemas constrained enough to guide
|
|
167
|
+
> the model and simple enough to nail on the first try; raise
|
|
168
|
+
> `--max-session-turns` when retries are expected.
|
|
169
|
+
|
|
170
|
+
The session ends on the first valid call. Until then:
|
|
171
|
+
|
|
172
|
+
- **Args fail validation.** `structured_output` returns a tool-result
|
|
173
|
+
error with Ajv's message, the model sees it on the next turn, and
|
|
174
|
+
may correct the arguments and call again.
|
|
175
|
+
- **Model calls a side-effecting tool in the same turn as
|
|
176
|
+
`structured_output`.** The pre-scan suppresses the sibling — it
|
|
177
|
+
never runs, regardless of whether the structured call ultimately
|
|
178
|
+
validates. The two paths split on what the model sees next:
|
|
179
|
+
- **Validation succeeds:** the run ends immediately, and the model
|
|
180
|
+
never gets another turn — the suppressed sibling is silently
|
|
181
|
+
discarded.
|
|
182
|
+
- **Validation fails:** the model gets another turn and sees a
|
|
183
|
+
synthesised "Skipped:" `tool_result` for the suppressed call,
|
|
184
|
+
so it can re-issue that call in a **separate turn** (one that
|
|
185
|
+
does not include `structured_output`).
|
|
186
|
+
- **Model emits plain text instead of calling
|
|
187
|
+
`structured_output`.** Exit code `1`. The error message includes
|
|
188
|
+
the turn count and a truncated preview of the model's output so
|
|
189
|
+
you can see what it actually said.
|
|
190
|
+
- **Run reaches `maxSessionTurns`.** Exit code `53`. Standard
|
|
191
|
+
"Reached max session turns" exit, plus a `--json-schema`-specific
|
|
192
|
+
hint that points at the three common stuck-run causes: model never
|
|
193
|
+
called the tool, `structured_output` is denied by permission rules,
|
|
194
|
+
or the schema is unsatisfiable.
|
|
195
|
+
- **Run is interrupted (SIGINT / Ctrl-C).** Exit code `130`. The
|
|
196
|
+
structured result is normally not emitted, but the shutdown
|
|
197
|
+
holdback loop does not poll the abort signal, so a SIGINT that
|
|
198
|
+
arrives after a successful call has been captured but before the
|
|
199
|
+
result reaches stdout may still land on stdout. Treat the exit
|
|
200
|
+
code as the source of truth.
|
|
201
|
+
|
|
202
|
+
## Privacy
|
|
203
|
+
|
|
204
|
+
The args you submit through `structured_output` ARE the structured
|
|
205
|
+
payload — already emitted on stdout. To avoid persisting the same
|
|
206
|
+
payload a second time into on-device surfaces that may be exported off
|
|
207
|
+
the machine, args are redacted with the placeholder
|
|
208
|
+
`{ __redacted: 'structured_output payload (see stdout result)' }` on:
|
|
209
|
+
|
|
210
|
+
- The `ToolCallEvent` telemetry path (OTLP exports, QwenLogger,
|
|
211
|
+
ui-telemetry stream, chat-recording UI event mirror).
|
|
212
|
+
- The on-disk chat-recording JSONL at
|
|
213
|
+
`~/.qwen/projects/<sanitized-cwd>/chats/<sessionId>.jsonl` (re-fed
|
|
214
|
+
into model context on `--continue` / `--resume`), including every
|
|
215
|
+
validation-failure retry.
|
|
216
|
+
|
|
217
|
+
Tool-call metrics (duration, success, decision) and surrounding event
|
|
218
|
+
metadata are preserved.
|
|
219
|
+
|
|
220
|
+
> **Schema is sent to the model provider.** Redaction covers the
|
|
221
|
+
> _call arguments_ on local surfaces only. The schema itself rides
|
|
222
|
+
> on every model request as the `structured_output` function
|
|
223
|
+
> declaration's `parameters` block — so any literal values you put
|
|
224
|
+
> inside it (`enum`, `const`, `default`, `examples`, `description`,
|
|
225
|
+
> `$comment`, etc.) reach the provider in cleartext just like prompt
|
|
226
|
+
> text. Schemas should describe shape and constraints; treat them as
|
|
227
|
+
> public toward the provider and keep secrets, customer records, and
|
|
228
|
+
> other sensitive payloads out of the schema body.
|
|
229
|
+
|
|
230
|
+
> **Hooks see raw args.** The redaction described above only applies
|
|
231
|
+
> to telemetry and chat-recording. `PreToolUse`, `PostToolUse`, and
|
|
232
|
+
> `PostToolUseFailure` hooks (including HTTP hooks that can forward
|
|
233
|
+
> payloads off-device) receive the unredacted `tool_input` for
|
|
234
|
+
> `structured_output`, since the hook contract is "see what the tool
|
|
235
|
+
> sees." If you operate audit-style catch-all hooks, either disable
|
|
236
|
+
> them for `structured_output` (filter on `tool_name`) or add
|
|
237
|
+
> hook-side redaction before running `--json-schema` against
|
|
238
|
+
> sensitive data.
|
|
239
|
+
|
|
240
|
+
## Session resumption (`--continue` / `--resume`)
|
|
241
|
+
|
|
242
|
+
`--json-schema` is a per-run flag, not a per-session property. The
|
|
243
|
+
synthetic tool is registered when the CLI parses its arguments, so:
|
|
244
|
+
|
|
245
|
+
- Re-pass `--json-schema` on every `--continue` / `--resume` you want
|
|
246
|
+
the terminal contract to apply to. The same schema as the original
|
|
247
|
+
run is the safe default — a mid-session schema swap is allowed but
|
|
248
|
+
changes the contract the model is being held to.
|
|
249
|
+
- If you `--continue` without `--json-schema`, the resumed run is an
|
|
250
|
+
ordinary headless session: `structured_output` simply doesn't
|
|
251
|
+
exist as a tool, and the model will respond in free-form text.
|
|
252
|
+
- The `__redacted` placeholder in the resumed chat-recording does
|
|
253
|
+
not affect resumability in practice. A successful `structured_output`
|
|
254
|
+
call terminates the session immediately, so the only redacted args
|
|
255
|
+
a resumed run could see are from failed attempts. The model still
|
|
256
|
+
has each attempt's Ajv validation error in the recorded `tool_result`
|
|
257
|
+
and the live parameter schema (re-registered from `--json-schema`),
|
|
258
|
+
which is enough to retry.
|
|
259
|
+
|
|
260
|
+
## Permission gating
|
|
261
|
+
|
|
262
|
+
`structured_output` deliberately bypasses the `--core-tools` allowlist:
|
|
263
|
+
the tool only exists when `--json-schema` is set, so excluding it
|
|
264
|
+
would leave the run with no terminal contract.
|
|
265
|
+
|
|
266
|
+
Explicit `permissions.deny` rules and `--exclude-tools` settings DO
|
|
267
|
+
take effect — both use the same deny mechanism and both prevent
|
|
268
|
+
`structured_output` from being registered, so the model never sees
|
|
269
|
+
the tool declaration. The typical result is that the model answers in
|
|
270
|
+
plain text (exit 1). If the model loops through other tools without
|
|
271
|
+
ever producing text, it will eventually hit `maxSessionTurns`
|
|
272
|
+
(exit 53) and the `--json-schema` hint in the error message tells you
|
|
273
|
+
where to look.
|
|
274
|
+
|
|
275
|
+
> **`--bare` caveat.** Bare mode ignores most settings-derived inputs,
|
|
276
|
+
> including settings-level `permissions.deny` and `tools.exclude`. The
|
|
277
|
+
> synthetic tool stays registered, so a settings-only deny of
|
|
278
|
+
> `structured_output` will silently no-op under `--bare`. Argv-level
|
|
279
|
+
> `--exclude-tools structured_output` still applies in bare mode — use
|
|
280
|
+
> the flag rather than settings if you need to lock down a bare run.
|
|
281
|
+
|
|
282
|
+
## Conflict with MCP tools
|
|
283
|
+
|
|
284
|
+
If an MCP server registers a tool literally named `structured_output`,
|
|
285
|
+
the tool-registry collision check renames the MCP tool to
|
|
286
|
+
`mcp__<server-name>__structured_output` so the synthetic tool keeps
|
|
287
|
+
the bare name. The user-supplied schema is always the one the model
|
|
288
|
+
sees.
|
|
289
|
+
|
|
290
|
+
## Example: gating a multi-step run on the structured output
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
RESULT=$(qwen --prompt "Audit this diff and rate its risk." \
|
|
294
|
+
--json-schema @./schemas/audit.json) || exit 1
|
|
295
|
+
|
|
296
|
+
risk=$(jq -r '.risk_level' <<<"$RESULT")
|
|
297
|
+
if [ "$risk" = "high" ]; then
|
|
298
|
+
echo "High-risk diff; pausing pipeline." >&2
|
|
299
|
+
exit 2
|
|
300
|
+
fi
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## See also
|
|
304
|
+
|
|
305
|
+
- [Headless Mode](headless.md) — the `-p`-based flow `--json-schema`
|
|
306
|
+
builds on.
|
|
307
|
+
- [Dual Output](dual-output.md) — records a JSON-event sidecar
|
|
308
|
+
alongside the TUI (a different approach to machine-readable output;
|
|
309
|
+
does not require `--json-schema`).
|
|
@@ -134,7 +134,7 @@ Subagents are configured using Markdown files with YAML frontmatter. This format
|
|
|
134
134
|
---
|
|
135
135
|
name: agent-name
|
|
136
136
|
description: Brief description of when and how to use this agent
|
|
137
|
-
model: inherit # Optional: inherit or
|
|
137
|
+
model: inherit # Optional: inherit, fast, modelId, or authType:modelId
|
|
138
138
|
approvalMode: auto-edit # Optional: default, plan, auto-edit, yolo
|
|
139
139
|
tools: # Optional: allowlist of tools
|
|
140
140
|
- tool1
|
|
@@ -151,10 +151,48 @@ Multiple paragraphs are supported.
|
|
|
151
151
|
|
|
152
152
|
Use the optional `model` frontmatter field to control which model a subagent uses:
|
|
153
153
|
|
|
154
|
-
- `inherit`: Use the same model as the main conversation
|
|
155
|
-
- Omit the field: Same as `inherit
|
|
156
|
-
- `
|
|
157
|
-
|
|
154
|
+
- `inherit`: Use the same model as the main conversation.
|
|
155
|
+
- Omit the field: Same as `inherit`.
|
|
156
|
+
- `fast`: Use the configured `fastModel`. If no valid fast model is configured,
|
|
157
|
+
the subagent falls back to `inherit`.
|
|
158
|
+
- `glm-5`: Use that model ID. Qwen Code first checks the main conversation's
|
|
159
|
+
auth type; if the model is not available there, it can resolve the model from
|
|
160
|
+
another configured provider.
|
|
161
|
+
- `openai:gpt-4o`: Use an explicit provider and model ID. This is useful when a
|
|
162
|
+
subagent should run on a model registered under a different auth type from the
|
|
163
|
+
main conversation.
|
|
164
|
+
|
|
165
|
+
For example:
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
---
|
|
169
|
+
name: fast-reviewer
|
|
170
|
+
description: Reviews small diffs with the configured fast model
|
|
171
|
+
model: fast
|
|
172
|
+
tools:
|
|
173
|
+
- read_file
|
|
174
|
+
- grep_search
|
|
175
|
+
---
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
---
|
|
180
|
+
name: openai-researcher
|
|
181
|
+
description: Uses an OpenAI-compatible provider for research tasks
|
|
182
|
+
model: openai:gpt-4o
|
|
183
|
+
tools:
|
|
184
|
+
- read_file
|
|
185
|
+
- grep_search
|
|
186
|
+
- glob
|
|
187
|
+
---
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The `fast` selector uses the same `fastModel` setting configured in
|
|
191
|
+
`settings.json` or with `/model --fast`. That setting may itself refer to a
|
|
192
|
+
model under another configured auth type, such as `openai:deepseek-v4-flash`.
|
|
193
|
+
When the selector resolves to another auth type, Qwen Code creates a dedicated
|
|
194
|
+
runtime provider for that subagent request and sends the provider only the bare
|
|
195
|
+
model ID.
|
|
158
196
|
|
|
159
197
|
#### Permission Mode
|
|
160
198
|
|
|
@@ -620,6 +658,10 @@ Always follow these standards:
|
|
|
620
658
|
|
|
621
659
|
- **Tool Restrictions**: Use `tools` to limit which tools a subagent can access, or `disallowedTools` to block specific tools while inheriting everything else
|
|
622
660
|
- **Permission Mode**: Subagents inherit their parent's permission mode by default. Plan-mode sessions cannot escalate to auto-edit through delegated agents. Privileged modes (auto-edit, yolo) are blocked in untrusted folders.
|
|
661
|
+
- **Provider Selection**: A subagent with `model: authType:modelId`, or
|
|
662
|
+
`model: fast` where `fastModel` resolves to another auth type, sends that
|
|
663
|
+
subagent's model requests to the selected provider. Make sure that provider is
|
|
664
|
+
appropriate for the subagent's task and data.
|
|
623
665
|
- **Sandboxing**: All tool execution follows the same security model as direct tool use
|
|
624
666
|
- **Audit Trail**: All Subagents actions are logged and visible in real-time
|
|
625
667
|
- **Access Control**: Project and user-level separation provides appropriate boundaries
|