@margay/ccl-core 1.4.5-rc.8 → 1.4.6-beta.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/sdk/API.md CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  } from '@margay/ccl-core/sdk'
18
18
  ```
19
19
 
20
- - **运行时**:Node ≥ 20(ESM)。SDK 本体零第三方依赖,只用 Node 内置模块。
20
+ - **运行时**:Node ≥ 22.19.0(ESM),依赖随 `@margay/ccl-core` 安装。
21
21
  - **CLI 定位**:SDK 以受管子进程方式运行 CCL CLI(全管道 stdio,无 TTY)。CLI 路径解析顺序:
22
22
  1. `options.cliPath`
23
23
  2. 环境变量 `CCL_SDK_CLI_PATH`
@@ -29,10 +29,16 @@ import {
29
29
 
30
30
  ## 2. 核心入口
31
31
 
32
- ### 2.1 `query(prompt, options?) → AsyncGenerator<SDKEvent>`
32
+ ### 2.1 `query({ prompt, options? }) → Query`
33
33
 
34
34
  发起一轮对话(一次 agentic 任务),异步迭代产出事件,直到终态事件(`result` / `suspended` / fatal `error`)后结束。
35
35
 
36
+ 上面的单轮语义适用于字符串输入;旧的双参数形式仍兼容。`prompt` 也可传
37
+ `AsyncIterable<SDKUserMessage>`:每条输入完成后再消费下一条,`result.turnId`
38
+ 关联输入的 `uuid`(未提供时自动生成)。输入结束、生成器错误、abort 或 close
39
+ 都会回收流式 query 的 CLI 进程。必须持续消费事件;流式输出积压超过 1024 项
40
+ 会明确报错并关闭,避免无限占用内存。
41
+
36
42
  ```ts
37
43
  for await (const event of query('检查收件箱并起草回复', {
38
44
  cwd: '/srv/cos-workspace',
@@ -73,6 +79,20 @@ for await (const event of resumeWithDecision(sessionId, { behavior: 'allow' }))
73
79
 
74
80
  `query(newPrompt, { resume: sessionId })` —— 在已有会话上继续新一轮对话(非审批场景的普通 resume)。
75
81
 
82
+ ### 2.5 `createSession(options?) → Session`
83
+
84
+ 在同一个 CLI 进程中连续交互。`prompt(content)`、`followUp(content)` 返回
85
+ `Turn { id, result, cancel() }`,按顺序执行;`steer(content)` 中断当前轮,将纠偏
86
+ 放到等待队列前面。已产生的工具副作用不会回滚。
87
+
88
+ `maxPendingTurns` 默认 32,包含正在执行的轮次,队满时同步拒绝新输入。
89
+ `onEvent(event)` 接收即时事件,Session 不保存事件历史。`controls` 提供原有
90
+ Query 控制方法,`pid` 用于宿主监控。
91
+
92
+ `endInput()` 停止接受新输入并等待已接受轮次,但保留进程和控制通道;`close()`
93
+ 终止进程并拒绝未完成轮次。权限回调可等待宿主决定,interrupt/close 会取消
94
+ 等待;返回 `pending` 仍表示持久挂起,后续用 `resumeWithDecision` 恢复。
95
+
76
96
  ---
77
97
 
78
98
  ## 3. 事件参考(SDKEvent)
@@ -92,7 +112,9 @@ for await (const event of resumeWithDecision(sessionId, { behavior: 'allow' }))
92
112
  | `error_diagnostic` | `diagnostic: RecoveryDiagnostic`, `sessionId?` | 可选过程事件,仅 `includeErrorDiagnostics:true` 时输出;不是终态 |
93
113
  | `raw` | `message` | 透传子进程原始消息(仅 `includeRaw:true` 时产出,调试用) |
94
114
 
95
- **终态约定**:每次迭代**恰好一个**终态——`result`、`suspended`、或 fatal `error`。宿主状态机据此收敛。
115
+ **终态约定**:字符串 query 的 `result` 结束该次输入。流式 query / Session 的
116
+ `result` 只结束对应 `turnId`,后续仍可能有输入。`suspended`、fatal `error`、
117
+ 关闭或输入 EOF 结束流式 query;不要把首个 result 当作常驻 Session 已关闭。
96
118
 
97
119
  ### 恢复结果与用户介入
98
120
 
@@ -390,3 +412,13 @@ query('用 filesystem server 列目录', {
390
412
 
391
413
  新增事件:`{ type:'stream_event', event, sessionId? }`;result 事件新增 `structuredOutput?`。
392
414
  新增 hooks:`onSessionStart` / `onUserPromptSubmit`;`onTurnEnd` 事件新增 `usage?`。
415
+
416
+
417
+ ### Optional gateway request progress (source candidate)
418
+
419
+ `QueryOptions.includeRequestProgress?: boolean` defaults to false. When enabled,
420
+ `SDKEvent` additionally includes `{ type: 'request_progress', progress }`, where
421
+ `progress` is the exported `GatewayRequestProgress` type. See the
422
+ [request status guide](./README.md#gateway-request-status-source-candidate) for
423
+ phases, bounded delivery and host lifecycle rules. This nonterminal event must
424
+ not be used as an assistant message, a successful result, or a retry instruction.
package/sdk/README.md CHANGED
@@ -66,6 +66,43 @@ process events. Those events do not replace or add a terminal result.
66
66
  See the [API contract](./API.md) and [recovery guide](../docs/error-recovery-usage.md)
67
67
  for consent, external blockers, and continuation rules.
68
68
 
69
+ ## Gateway request status (source candidate)
70
+
71
+ Set `includeRequestProgress: true` to receive nonterminal `request_progress`
72
+ events. This is off by default, independent of raw/partial/error diagnostic
73
+ options. It does not change `result`, cancellation, or `onSessionEnd`.
74
+
75
+ ```ts
76
+ for await (const event of query('Summarize this project.', {
77
+ includeRequestProgress: true,
78
+ })) {
79
+ if (event.type === 'request_progress') {
80
+ const { requestId, phase, attempt, elapsedMs, idleMs, slow } = event.progress
81
+ // Update a transient status indicator, keyed by requestId.
82
+ }
83
+ if (event.type === 'result') {
84
+ // End the turn using the existing success/error contract.
85
+ }
86
+ }
87
+ ```
88
+
89
+ Phases are `preparing`, `connecting`, `sending`, `waiting_response`,
90
+ `response_headers`, `retry_wait`, `streaming`, and `finished`. `connecting`
91
+ includes pool waiting, DNS, TCP, TLS and proxy negotiation; it does not identify
92
+ which of those substeps is slow. `idleMs` measures time without effective model
93
+ progress. Events contain no prompt, headers, credentials or endpoint URL.
94
+
95
+ Events are finite (at most 32 per model request), not periodic heartbeats.
96
+ A slow notice never counts as model progress or grants permission to retry.
97
+ `finished` ends a request, not necessarily the turn; clear status on the existing
98
+ result/abort/disconnect path as well. Hosts should keep their own deadlines
99
+ and avoid replaying tools when the remote completion state is unknown.
100
+
101
+ CLI users can opt in with `--print --output-format stream-json
102
+ --include-request-progress`; each wire event is `system/request_progress`.
103
+ The flag is invalid with text or JSON output. See
104
+ [request deadlines](../docs/gateway-quickstart.md#request-deadlines-and-slow-waits).
105
+
69
106
  ## Architecture
70
107
 
71
108
  The SDK is the **host side** of the stream-json control protocol that
@@ -146,3 +183,60 @@ provider mapping, skills-dir wrapping, hooks, usage accumulation, file-store
146
183
  round-trip. The issue's live end-to-end DoD slice (real model + mock outbound
147
184
  endpoint + real process exit) is CoS-side per the issue's division of labor —
148
185
  the SDK-side seams it needs are all covered above.
186
+ # Minimal mode and explicit tools
187
+
188
+ When passing `--bare` through `extraArgs`, the built-in tool set is Bash, Read,
189
+ and Edit (REPL/coordinator modes have their own subset). `--tools default` uses
190
+ that mode's available set; an explicit incompatible selection such as
191
+ `--bare --tools Write` fails with a diagnostic before a model request. Remove
192
+ `--bare` and unset `CLAUDE_CODE_SIMPLE` to request Write. Allow/deny permissions
193
+ still apply; `--allowedTools` does not add tools to the available set.
194
+
195
+ Use `extraArgs: ['--thinking', 'disabled']` to explicitly disable thinking on
196
+ Anthropic Messages compatible routes. Omitting the option retains the route's
197
+ default. The selection persists across requests; a query handle's
198
+ `setMaxThinkingTokens(0)` explicitly disables it, and `null` restores the default.
199
+ # Persistent sessions and streamed input
200
+
201
+ `query({ prompt: asyncIterable, options })` accepts `SDKUserMessage` values. It
202
+ submits one message at a time, waits for its result before pulling the next,
203
+ and adds the input UUID as `result.turnId` (generating a UUID if omitted).
204
+ Input EOF closes a streamed query. Invalid messages or producer failures reject
205
+ the event iterator. Consume events as they arrive: an unread streamed query fails
206
+ explicitly at 1024 buffered events instead of retaining unlimited output.
207
+
208
+ Use `createSession` when the host owns a longer conversation:
209
+
210
+ ```ts
211
+ import { createSession } from '@margay/ccl-core/sdk'
212
+
213
+ const session = createSession({ cwd: projectDir, maxPendingTurns: 32 })
214
+ try {
215
+ const first = session.prompt('Inspect the project and describe the failing test.')
216
+ console.log(await first.result)
217
+ const next = session.followUp('Fix that failure and run the relevant test.')
218
+ console.log(await next.result)
219
+ await session.endInput()
220
+ // The same child and controls remain alive until close().
221
+ console.log(session.pid, await session.controls.initializationResult())
222
+ } finally {
223
+ await session.close()
224
+ }
225
+ ```
226
+
227
+ `prompt` and `followUp` enqueue FIFO; exceeding `maxPendingTurns` throws before
228
+ acceptance. Each `Turn` has a stable `id`, a `result` promise and `cancel()`.
229
+ Cancelling a queued turn rejects its promise. Cancelling an active turn interrupts
230
+ the CLI and returns its interrupted result; already executed tool effects remain.
231
+ `steer(text)` interrupts the active turn and puts the correction ahead of waiting
232
+ follow-ups. It does not undo earlier effects. `endInput()` rejects new inputs and
233
+ waits for accepted turns while preserving controls; `close()` terminates the child
234
+ and rejects unfinished turns. `onEvent` observes events without retaining history.
235
+
236
+ Permission callbacks can wait for a host decision while the process stays alive.
237
+ Interrupt and close cancel that wait. Returning `{ behavior: 'pending' }` retains
238
+ the existing durable suspend contract: it ends this process and requires
239
+ `resumeWithDecision(sessionId, decision)`; it does not imply automatic approval.
240
+ Uncooperative application iterators/callbacks cannot be forcibly stopped, but do
241
+ not prevent CLI cleanup. One-shot string queries and post-result `rewindFiles`
242
+ retain their existing behavior.