@margay/ccl-core 1.4.5-rc.9 → 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/QUICKSTART.md +6 -5
- package/README.md +7 -6
- package/dist/cli.js +9363 -6637
- package/dist/sdk/index.d.ts +3 -1
- package/dist/sdk/index.js +1 -0
- package/dist/sdk/query.d.ts +2 -29
- package/dist/sdk/query.js +52 -14
- package/dist/sdk/queryHandle.d.ts +4 -4
- package/dist/sdk/queryHandle.js +44 -9
- package/dist/sdk/queryInput.d.ts +13 -0
- package/dist/sdk/queryInput.js +70 -0
- package/dist/sdk/requestProgress.d.ts +14 -0
- package/dist/sdk/requestProgress.js +26 -0
- package/dist/sdk/session.d.ts +48 -0
- package/dist/sdk/session.js +167 -0
- package/dist/sdk/types.d.ts +24 -1
- package/docs/ccl-duo-agent.md +2 -0
- package/docs/cli-installation.md +86 -0
- package/docs/context-checkpoints-and-daemon.md +54 -0
- package/docs/gateway-quickstart.md +52 -0
- package/docs/releases/1.4.5-rc.10.md +50 -0
- package/docs/releases/1.4.6-beta.0.md +54 -0
- package/package.json +9 -4
- package/sdk/API.md +35 -3
- package/sdk/README.md +94 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { queryHandleWithTransport } from './queryHandle.js';
|
|
3
|
+
import { spawnForOptions } from './query.js';
|
|
4
|
+
/** One CLI process, sequential turns, and a bounded host-side input queue. */
|
|
5
|
+
export class Session {
|
|
6
|
+
pid;
|
|
7
|
+
/** Existing SDK controls, including initialization, model and task controls. */
|
|
8
|
+
controls;
|
|
9
|
+
handle;
|
|
10
|
+
limit;
|
|
11
|
+
options;
|
|
12
|
+
queue = [];
|
|
13
|
+
active;
|
|
14
|
+
wake;
|
|
15
|
+
accepting = true;
|
|
16
|
+
closed = false;
|
|
17
|
+
drainWaiters = [];
|
|
18
|
+
pumping;
|
|
19
|
+
constructor(options = {}, transport) {
|
|
20
|
+
this.limit = options.maxPendingTurns ?? 32;
|
|
21
|
+
if (!Number.isSafeInteger(this.limit) || this.limit < 1) {
|
|
22
|
+
throw new RangeError('ccl-sdk: maxPendingTurns must be a positive integer');
|
|
23
|
+
}
|
|
24
|
+
if (options.pendingDecision)
|
|
25
|
+
throw new Error('ccl-sdk: resume a pending decision before creating a Session');
|
|
26
|
+
this.options = options;
|
|
27
|
+
const owned = transport ?? spawnForOptions(options);
|
|
28
|
+
this.pid = owned.pid;
|
|
29
|
+
this.handle = queryHandleWithTransport(this.inputs(), options, owned);
|
|
30
|
+
this.controls = this.handle;
|
|
31
|
+
this.pumping = this.consume();
|
|
32
|
+
}
|
|
33
|
+
prompt(content) {
|
|
34
|
+
return this.enqueue(content, false);
|
|
35
|
+
}
|
|
36
|
+
followUp(content) {
|
|
37
|
+
return this.enqueue(content, false);
|
|
38
|
+
}
|
|
39
|
+
/** Interrupt the active turn; correction runs before queued follow-ups. */
|
|
40
|
+
steer(content) {
|
|
41
|
+
const turn = this.enqueue(content, true);
|
|
42
|
+
if (this.active)
|
|
43
|
+
void this.interrupt().catch(error => this.fail(error));
|
|
44
|
+
return turn;
|
|
45
|
+
}
|
|
46
|
+
enqueue(content, priority) {
|
|
47
|
+
if (!this.accepting || this.closed)
|
|
48
|
+
throw new Error('ccl-sdk: session input is closed');
|
|
49
|
+
if (this.queue.length + (this.active ? 1 : 0) >= this.limit) {
|
|
50
|
+
throw new Error(`ccl-sdk: session queue is full (${this.limit} pending turns)`);
|
|
51
|
+
}
|
|
52
|
+
const id = randomUUID();
|
|
53
|
+
let resolve;
|
|
54
|
+
let reject;
|
|
55
|
+
const result = new Promise((yes, no) => { resolve = yes; reject = no; });
|
|
56
|
+
// It is valid to enqueue follow-ups and observe their results later.
|
|
57
|
+
void result.catch(() => { });
|
|
58
|
+
const turn = { id, result, cancel: () => this.cancel(id) };
|
|
59
|
+
const entry = { turn, resolve, reject, input: {
|
|
60
|
+
type: 'user', uuid: id, message: { role: 'user', content },
|
|
61
|
+
} };
|
|
62
|
+
if (priority)
|
|
63
|
+
this.queue.unshift(entry);
|
|
64
|
+
else
|
|
65
|
+
this.queue.push(entry);
|
|
66
|
+
this.wake?.();
|
|
67
|
+
return turn;
|
|
68
|
+
}
|
|
69
|
+
async *inputs() {
|
|
70
|
+
while (!this.closed) {
|
|
71
|
+
if (this.active) {
|
|
72
|
+
await new Promise(resolve => { this.wake = resolve; });
|
|
73
|
+
this.wake = undefined;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const entry = this.queue.shift();
|
|
77
|
+
if (entry) {
|
|
78
|
+
this.active = entry;
|
|
79
|
+
yield entry.input;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
await new Promise(resolve => { this.wake = resolve; });
|
|
83
|
+
this.wake = undefined;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async consume() {
|
|
88
|
+
try {
|
|
89
|
+
for await (const event of this.handle) {
|
|
90
|
+
const correlated = { ...event, turnId: event.turnId ?? this.active?.turn.id };
|
|
91
|
+
// Observability callbacks must not terminate the agent or strand work.
|
|
92
|
+
try {
|
|
93
|
+
this.options.onEvent?.(correlated);
|
|
94
|
+
}
|
|
95
|
+
catch { /* host observer */ }
|
|
96
|
+
if (event.type === 'result' && this.active) {
|
|
97
|
+
if (event.turnId !== this.active.turn.id)
|
|
98
|
+
throw new Error('ccl-sdk: mismatched turn result');
|
|
99
|
+
this.active.resolve(event);
|
|
100
|
+
this.active = undefined;
|
|
101
|
+
this.wake?.();
|
|
102
|
+
this.notifyDrained();
|
|
103
|
+
}
|
|
104
|
+
else if (event.type === 'error' && event.fatal) {
|
|
105
|
+
throw new Error(event.message);
|
|
106
|
+
}
|
|
107
|
+
else if (event.type === 'suspended') {
|
|
108
|
+
throw new Error(`ccl-sdk: session suspended; resumeWithDecision(${event.sessionId}) is required`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (!this.closed)
|
|
112
|
+
this.fail(new Error('ccl-sdk: session transport ended'));
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
this.fail(error);
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
await this.controls.close();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
fail(reason) {
|
|
122
|
+
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
123
|
+
this.closed = true;
|
|
124
|
+
this.accepting = false;
|
|
125
|
+
this.active?.reject(error);
|
|
126
|
+
this.active = undefined;
|
|
127
|
+
for (const entry of this.queue.splice(0))
|
|
128
|
+
entry.reject(error);
|
|
129
|
+
this.wake?.();
|
|
130
|
+
this.notifyDrained();
|
|
131
|
+
void this.controls.close().catch(() => { });
|
|
132
|
+
}
|
|
133
|
+
notifyDrained() {
|
|
134
|
+
if (this.active || this.queue.length)
|
|
135
|
+
return;
|
|
136
|
+
for (const resolve of this.drainWaiters.splice(0))
|
|
137
|
+
resolve();
|
|
138
|
+
}
|
|
139
|
+
async interrupt() {
|
|
140
|
+
if (this.active)
|
|
141
|
+
await this.controls.interrupt();
|
|
142
|
+
}
|
|
143
|
+
async cancel(id) {
|
|
144
|
+
if (this.active?.turn.id === id)
|
|
145
|
+
return this.interrupt();
|
|
146
|
+
const index = this.queue.findIndex(entry => entry.turn.id === id);
|
|
147
|
+
if (index < 0)
|
|
148
|
+
return;
|
|
149
|
+
this.queue.splice(index, 1)[0].reject(new Error('ccl-sdk: queued turn cancelled'));
|
|
150
|
+
this.notifyDrained();
|
|
151
|
+
}
|
|
152
|
+
/** Stop accepting inputs and drain accepted turns, keeping controls alive. */
|
|
153
|
+
async endInput() {
|
|
154
|
+
this.accepting = false;
|
|
155
|
+
if (!this.active && !this.queue.length)
|
|
156
|
+
return;
|
|
157
|
+
await new Promise(resolve => this.drainWaiters.push(resolve));
|
|
158
|
+
}
|
|
159
|
+
async close() {
|
|
160
|
+
this.fail(new Error('ccl-sdk: session closed'));
|
|
161
|
+
await this.controls.close();
|
|
162
|
+
await this.pumping;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
export function createSession(options = {}) {
|
|
166
|
+
return new Session(options);
|
|
167
|
+
}
|
package/dist/sdk/types.d.ts
CHANGED
|
@@ -142,6 +142,8 @@ export type QueryOptions = {
|
|
|
142
142
|
* suggestions; it is neither a result nor an instruction for the host to retry.
|
|
143
143
|
*/
|
|
144
144
|
includeErrorDiagnostics?: boolean;
|
|
145
|
+
/** Opt-in finite, nonterminal gateway status events. Defaults to false. */
|
|
146
|
+
includeRequestProgress?: boolean;
|
|
145
147
|
/**
|
|
146
148
|
* slice sdk-json-schema-output (G4): a JSON Schema for structured output. When set, the CLI
|
|
147
149
|
* validates the final result against it (retrying the model) and returns `structuredOutput` on
|
|
@@ -297,10 +299,30 @@ export type Usage = {
|
|
|
297
299
|
* minimal set from the issue (P0-1) plus `permission_resolved` (audit trail)
|
|
298
300
|
* and optional `raw` passthrough.
|
|
299
301
|
*/
|
|
300
|
-
export type
|
|
302
|
+
export type SDKUserMessage = {
|
|
303
|
+
type: 'user';
|
|
304
|
+
message: {
|
|
305
|
+
role: 'user';
|
|
306
|
+
content: string | Array<{
|
|
307
|
+
type: string;
|
|
308
|
+
[key: string]: unknown;
|
|
309
|
+
}>;
|
|
310
|
+
};
|
|
311
|
+
uuid?: string;
|
|
312
|
+
parent_tool_use_id?: null;
|
|
313
|
+
session_id?: string;
|
|
314
|
+
};
|
|
315
|
+
export type SDKEvent = SDKEventPayload & {
|
|
316
|
+
turnId?: string;
|
|
317
|
+
};
|
|
318
|
+
type SDKEventPayload = {
|
|
301
319
|
type: 'assistant_text';
|
|
302
320
|
text: string;
|
|
303
321
|
sessionId?: string;
|
|
322
|
+
} | {
|
|
323
|
+
type: 'request_progress';
|
|
324
|
+
progress: import('./requestProgress.js').GatewayRequestProgress;
|
|
325
|
+
sessionId?: string;
|
|
304
326
|
} | {
|
|
305
327
|
type: 'error_diagnostic';
|
|
306
328
|
diagnostic: import('./errorDiagnostics.js').RecoveryDiagnostic;
|
|
@@ -452,3 +474,4 @@ export type Transport = {
|
|
|
452
474
|
* rewritten). Absent for in-memory test transports. */
|
|
453
475
|
pid?: number;
|
|
454
476
|
};
|
|
477
|
+
export {};
|
package/docs/ccl-duo-agent.md
CHANGED
|
@@ -100,6 +100,8 @@ Duo 让一个 Agent 执行任务,另一个独立检查结果。双方各有上
|
|
|
100
100
|
|
|
101
101
|
本地修复候选在模型查询失败终止后将对应 Duo 任务置为暂停,保留材料和审查记录。先检查错误、凭据或模型可用性,再使用 `/duo resume`;失败不会被标为审核完成。
|
|
102
102
|
|
|
103
|
+
2026-10-15 本地候选修复了超时后“显示已恢复、任务仍停在暂停阶段”的问题。恢复时根据当前要求、材料和有效审核记录重新确定工作阶段,再允许双方继续;暂停期间改过要求时,旧 PASS 不能恢复为可接受状态。此修复尚未发布。
|
|
104
|
+
|
|
103
105
|
Duo 退出和任务完成不是一回事。任务被接受后 Duo 回到待命,可直接输入下一项任务;即使旧材料后来变化,仍可接新任务或使用 `/duo exit`。清理失败会显示原因,但不能锁住输入。普通 `/exit` 退出 CCL 会话。
|
|
104
106
|
|
|
105
107
|
### 必要时由用户裁决
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# CLI installation and upgrades
|
|
2
|
+
|
|
3
|
+
For `1.4.5-rc.10`, use Node.js `>=22.19.0`. This matches the declared minimum of
|
|
4
|
+
the pinned `undici 8.0.2` runtime dependency; the earlier Node 20 declaration no
|
|
5
|
+
longer applies. macOS Node/Bun verification does not imply Windows verification.
|
|
6
|
+
|
|
7
|
+
Starting with `1.4.5-rc.10`, CCL declares `ccl` and `margay` as names for the same
|
|
8
|
+
`dist/cli.js`. Both names must report the package version and provide the same
|
|
9
|
+
options. The `1.4.5-rc.9` package declares only
|
|
10
|
+
`margay`. Installing that existing version will not add `ccl`.
|
|
11
|
+
|
|
12
|
+
## Global and project installs
|
|
13
|
+
|
|
14
|
+
For a terminal command available outside a project, install the desired released
|
|
15
|
+
version globally:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install --global @margay/ccl-core@latest
|
|
19
|
+
margay --version
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
After installing a release with both entries, also run `ccl --version`. Both
|
|
23
|
+
outputs must match the installed package's version. Global installation does not
|
|
24
|
+
replace a CLI embedded in Margay Desktop or already loaded by a running process.
|
|
25
|
+
|
|
26
|
+
Without `--global`, npm installs a project dependency. Run that copy explicitly:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
npm install @margay/ccl-core@latest
|
|
30
|
+
./node_modules/.bin/margay --version
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Releases with both entries also provide `./node_modules/.bin/ccl`. A successful
|
|
34
|
+
project install does not upgrade commands elsewhere on the shell's PATH.
|
|
35
|
+
|
|
36
|
+
## Migrating old custom launchers (macOS/Linux)
|
|
37
|
+
|
|
38
|
+
Some earlier local deployments created shell scripts that pin a release path.
|
|
39
|
+
These are separate from npm-managed links. Inspect resolution before and after
|
|
40
|
+
upgrading:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
type -a ccl margay
|
|
44
|
+
npm prefix --global
|
|
45
|
+
npm ls --global @margay/ccl-core --depth=0
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
There are two distinct cases:
|
|
49
|
+
|
|
50
|
+
- **npm reports `EEXIST`:** an unmanaged launcher occupies the intended npm bin
|
|
51
|
+
path. Inspect the named file and confirm it belongs to the old CCL deployment.
|
|
52
|
+
Move that specific file to an unused backup path, then repeat the installation.
|
|
53
|
+
Preserve unrelated executables; a blanket `--force` is not a migration plan.
|
|
54
|
+
- **Installation succeeds, but the command reports an old version:** an alias,
|
|
55
|
+
shell function or an earlier PATH directory is taking precedence. Inspect the
|
|
56
|
+
resolved entry. Back up the specific obsolete CCL wrapper, or update an explicit
|
|
57
|
+
alias/function to use the npm entry. Ensure the npm prefix's `bin` directory is
|
|
58
|
+
on PATH. Refresh the shell command cache (`rehash` in zsh, `hash -r` in bash),
|
|
59
|
+
then verify both commands again from outside the source checkout.
|
|
60
|
+
|
|
61
|
+
Keep each backup until the new commands work. To roll back a custom wrapper,
|
|
62
|
+
restore its saved bytes at the original path after inspecting what currently
|
|
63
|
+
occupies that path. For an npm-managed installation, install the previous exact
|
|
64
|
+
version. Future npm upgrades manage both links once the custom wrappers have
|
|
65
|
+
been migrated.
|
|
66
|
+
|
|
67
|
+
## Checking a release tarball
|
|
68
|
+
|
|
69
|
+
From a built checkout, pack the release and test its actual npm installation:
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
npm pack --ignore-scripts --pack-destination /path/to/artifacts
|
|
73
|
+
node tests/package-cli-install.mjs /path/to/artifacts/candidate.tgz /path/to/previous.tgz
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The smoke test uses isolated install prefixes, HOME/configuration and cache. It
|
|
77
|
+
checks fresh global and project installs, upgrade from the previous tarball,
|
|
78
|
+
both commands through PATH, matching executable/version/help, an unmanaged-file
|
|
79
|
+
collision, backup migration and PATH shadowing. Dependency lifecycle scripts are
|
|
80
|
+
disabled; this verifies CLI installation and startup, not native dependency or
|
|
81
|
+
model behavior. It currently runs on POSIX; Windows `.cmd`/PowerShell launchers
|
|
82
|
+
require a Windows run. It cleans its temporary installations even on failure.
|
|
83
|
+
|
|
84
|
+
Use the final tarball as the test input. A metadata edit after publishing rc.9
|
|
85
|
+
does not change rc.9 on npm; allocate a new version, rebuild, pack and verify
|
|
86
|
+
before publishing the corrected release.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Completed-step checkpoints and background scheduling
|
|
2
|
+
|
|
3
|
+
For a long task, `ContextCheckpoint` can save selected completed tool results with
|
|
4
|
+
a summary and artifact references. Subsequent requests carry that summary and
|
|
5
|
+
retrieval references. Full original outputs, including image blocks, remain in
|
|
6
|
+
the session's transcript-adjacent `.checkpoints` directory with SHA-256 integrity
|
|
7
|
+
checks. `list` locates saved records; `restore` makes the original evidence
|
|
8
|
+
available again, including after transcript compaction or resume.
|
|
9
|
+
|
|
10
|
+
At a completed phase, omit `tool_use_ids` to save all eligible completed results.
|
|
11
|
+
For a subset, `list` returns available result IDs with their tool names and inputs.
|
|
12
|
+
Use the `checkpoint_id` returned by `save` or `list` when restoring; invented
|
|
13
|
+
protocol IDs are rejected.
|
|
14
|
+
|
|
15
|
+
The model chooses completed work; the tool rejects missing results, unresolved
|
|
16
|
+
permission interactions, errors, background launches, and duplicate archives.
|
|
17
|
+
User messages, corrections and user-supplied images are never projected away.
|
|
18
|
+
Checkpoint storage failures leave the incoming context intact. This path uses the
|
|
19
|
+
existing full compaction and result-budget machinery; it does not enable the
|
|
20
|
+
unimplemented provider cache-editing features. Checkpointing can change cache
|
|
21
|
+
reuse and adds tool calls, so savings depend on the actual task and provider.
|
|
22
|
+
|
|
23
|
+
`ccl daemon start --cwd /path/to/project` starts a detached local owner for the
|
|
24
|
+
project's existing `.ccl/scheduled_tasks.json`. The launching CLI may exit; the
|
|
25
|
+
owner remains. `ccl daemon run` is the foreground equivalent for an external
|
|
26
|
+
service manager. This command does not install a login or reboot service.
|
|
27
|
+
|
|
28
|
+
The owner uses the existing cron scheduler and project lock. It records an
|
|
29
|
+
occurrence before acknowledging it, then records execution and local delivery
|
|
30
|
+
separately under `.ccl/daemon/`. `status` shows these records. Completed output is
|
|
31
|
+
saved in `results/`; tool/events evidence is retained in `events/`. There is no
|
|
32
|
+
automatic email, chat or other external delivery.
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
ccl daemon status --cwd /path/to/project
|
|
36
|
+
ccl daemon cancel OCCURRENCE_ID --cwd /path/to/project
|
|
37
|
+
ccl daemon approve OCCURRENCE_ID allow --cwd /path/to/project
|
|
38
|
+
ccl daemon approve OCCURRENCE_ID deny --cwd /path/to/project
|
|
39
|
+
ccl daemon run-missed OCCURRENCE_ID --cwd /path/to/project
|
|
40
|
+
ccl daemon deliver OCCURRENCE_ID --cwd /path/to/project
|
|
41
|
+
ccl daemon stop --cwd /path/to/project
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The CLI's existing permission rules apply. An unresolved permission suspends
|
|
45
|
+
execution until an explicit `approve` decision. Missed one-shot tasks are retained
|
|
46
|
+
as `missed` and need `run-missed`; recurring timing follows the shared scheduler.
|
|
47
|
+
On restart, accepted but not started work can continue. Work interrupted after
|
|
48
|
+
execution began is recorded as `interrupted` and never automatically replayed:
|
|
49
|
+
inspect its events and external effects before scheduling new work. `deliver`
|
|
50
|
+
retries saving an already successful result, without rerunning its tools.
|
|
51
|
+
|
|
52
|
+
`cancel` stops an occurrence; delete the durable cron definition as well when
|
|
53
|
+
future recurring occurrences should cease. A permission wait and an interrupted
|
|
54
|
+
run remain visible across restarts. Stop does not erase schedules or evidence.
|
|
@@ -190,8 +190,60 @@ by endpoint switching.
|
|
|
190
190
|
Output/context limits do not mean the account is out of credit. See the
|
|
191
191
|
[recovery guide](./error-recovery-usage.md) for consent and external-blocker UX.
|
|
192
192
|
|
|
193
|
+
## Request Deadlines And Slow Waits
|
|
194
|
+
|
|
195
|
+
The source candidate adds connection and response deadlines while preserving
|
|
196
|
+
the existing total request setup budget. These values are milliseconds:
|
|
197
|
+
|
|
198
|
+
| Variable | Default | Applies until |
|
|
199
|
+
| --- | --- | --- |
|
|
200
|
+
| `CCL_GATEWAY_REQUEST_TIMEOUT_MS` | `300000` | A response stream is established, including retry delays |
|
|
201
|
+
| `CCL_GATEWAY_CONNECT_TIMEOUT_MS` | Total setup budget | A pooled/new connection can send the request |
|
|
202
|
+
| `CCL_GATEWAY_RESPONSE_TIMEOUT_MS` | Total setup budget | Response headers arrive after sending |
|
|
203
|
+
| `CCL_GATEWAY_SLOW_WAIT_MS` | `30000` | A single slow-status notice becomes eligible |
|
|
204
|
+
|
|
205
|
+
Values must be whole numbers from 1 to 3600000. Invalid values fall back to the
|
|
206
|
+
default and produce a one-time stderr notice. A phase deadline cannot extend
|
|
207
|
+
the remaining total budget, and retries do not receive a new total budget.
|
|
208
|
+
Defaults therefore do not shorten the existing five-minute setup allowance.
|
|
209
|
+
|
|
210
|
+
For example, allow up to 30 seconds to connect and 180 seconds for headers:
|
|
211
|
+
|
|
212
|
+
```sh
|
|
213
|
+
CCL_GATEWAY_CONNECT_TIMEOUT_MS=30000 CCL_GATEWAY_RESPONSE_TIMEOUT_MS=180000 margay
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
After streaming starts, the separate model-progress idle watchdog applies;
|
|
217
|
+
the setup deadline does not impose a maximum duration on healthy output.
|
|
218
|
+
The TUI shows the current request phase and a slow-wait notice. Escape cancels
|
|
219
|
+
through the existing task cancellation path. Status notices do not reset the
|
|
220
|
+
model-progress watchdog or become assistant text.
|
|
221
|
+
|
|
222
|
+
A local timeout reports `gateway_request_timeout` with the observed phase.
|
|
223
|
+
It cannot establish whether the remote request completed. Check task state
|
|
224
|
+
before repeating work that may have side effects. SDK hosts can opt into
|
|
225
|
+
[structured request status](../sdk/README.md#gateway-request-status-source-candidate).
|
|
226
|
+
|
|
193
227
|
## Troubleshooting
|
|
194
228
|
|
|
229
|
+
In the source candidate, headless runs with `CI=1` can authenticate using an
|
|
230
|
+
admitted gateway configuration alone. A separate Anthropic API key is not
|
|
231
|
+
required for that route. Missing or rejected gateway credentials still require
|
|
232
|
+
a valid login or another supported credential source. This fixes a startup
|
|
233
|
+
failure present in rc.9; it does not change the gateway's 401 handling.
|
|
234
|
+
|
|
235
|
+
The source candidate also reports a failed in-process teammate request to its
|
|
236
|
+
team leader with the visible failure reason. The teammate remains available for
|
|
237
|
+
explicit new work after the failure; a timeout does not automatically retry the
|
|
238
|
+
task or mark it complete. Check any uncertain side effects before resuming.
|
|
239
|
+
|
|
240
|
+
In the source candidate, headless background completions belong to the input
|
|
241
|
+
that launched them. They share that input's `--max-turns` ceiling and cannot
|
|
242
|
+
restart it after an error or interrupt. Successful background work is collected
|
|
243
|
+
before its final result. A new SDK input can start fresh work; late notifications
|
|
244
|
+
from the previous input do not start model calls for it. Closing an SDK query
|
|
245
|
+
also waits for background shell termination during the CLI's bounded shutdown.
|
|
246
|
+
|
|
195
247
|
| Symptom | Action |
|
|
196
248
|
| --- | --- |
|
|
197
249
|
| Website offers no login entry | Confirm that login is enabled for that website/environment; a 200 page alone does not prove it is enabled. Cancel the CLI wait if disabled |
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# 1.4.5-rc.10
|
|
2
|
+
|
|
3
|
+
本版本包含双命令入口和 Mac Phase B 稳定性修复。包同时注册 `ccl` 和
|
|
4
|
+
`margay`,两个命令指向同一份运行程序,功能、参数和版本相同。
|
|
5
|
+
CLI 与 SDK 随同一个 `@margay/ccl-core` 包交付。
|
|
6
|
+
|
|
7
|
+
## 相对 rc.9 的更新
|
|
8
|
+
|
|
9
|
+
- 网关请求区分连接、等待响应和流式读取阶段,阶段期限共用请求总期限。
|
|
10
|
+
CLI/SDK 可选择接收等待进度;通知异常不阻塞请求,不混入模型上下文。
|
|
11
|
+
- 无头执行的一次输入只返回一个终态。任务失败后,旧后台通知不会重新唤起模型、
|
|
12
|
+
执行工具或重复返回结果;取消及 SDK 关闭会清理所属后台任务。
|
|
13
|
+
- 自动继续与压缩累计使用剩余任务预算,避免重置额度后不断运行。
|
|
14
|
+
- 修复 CI 网关凭据识别、Duo 超时后的恢复,以及进程内/独立进程 Swarm 的失败通知。
|
|
15
|
+
- 保留 rc.9 的输出预算、慢请求等待和有界断流恢复;已完成工具结果继续保留。
|
|
16
|
+
- 将外部运行依赖 `undici` 固定为本候选验证过的 `8.0.2`,避免全新 npm 安装
|
|
17
|
+
自动选择不同版本。Node 声明要求调整为 `>=22.19.0`,与该依赖声明一致;
|
|
18
|
+
原先的 Node 20 支持声明不再适用。
|
|
19
|
+
|
|
20
|
+
## 安装与升级
|
|
21
|
+
|
|
22
|
+
全局安装后核对两个入口:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
npm install --global @margay/ccl-core@1.4.5-rc.10
|
|
26
|
+
ccl --version
|
|
27
|
+
margay --version
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
两者应输出 `1.4.5-rc.10 (Margay)`。不带 `--global` 的安装只更新项目依赖,
|
|
31
|
+
可通过 `./node_modules/.bin/ccl` 或 `./node_modules/.bin/margay` 使用。
|
|
32
|
+
|
|
33
|
+
旧的自定义启动脚本、alias 或 PATH 排序可能继续选择旧版本。若安装报告
|
|
34
|
+
`EEXIST` 或运行版本不一致,按[安装与升级说明](../cli-installation.md)检查并
|
|
35
|
+
备份迁移对应入口。npm 全局安装不会替换 Margay Desktop 内嵌的运行包。
|
|
36
|
+
|
|
37
|
+
## 验证与已知边界
|
|
38
|
+
|
|
39
|
+
Phase B 候选已在 Mac 验证 Node/Bun 下 CLI、SDK 的成功、超时、取消和后续输入,
|
|
40
|
+
以及 TUI、Duo、Swarm 和 Margay.app 私有副本的消费链路。CLI 构建 SHA256:
|
|
41
|
+
`92a070720228fe35b80608e751fb59ea0116ac7c9e9115c009da98e1063a39df`。
|
|
42
|
+
包的安装检查另外验证 POSIX 全新全局/项目安装、rc.9 升级、双入口、旧脚本冲突和 PATH 遮挡。
|
|
43
|
+
|
|
44
|
+
- 本版不包含“轮次耗尽返回退出码 2,由上层自动续接”的新方案;到上限仍报告
|
|
45
|
+
`error_max_turns`。真实视频验证的首个 48 轮调用失败,另一次明确输入才完成交付。
|
|
46
|
+
- DeepSeek thinking 400 的根因及针对性修复尚未收口;不能承诺本版消除该错误,
|
|
47
|
+
也不能仅凭 `blocked_external` 认定连接/超时问题已修复。
|
|
48
|
+
- Windows 实机未验证;全仓 typecheck 的 11 条既有诊断未解决。
|
|
49
|
+
- 获批 reactive compact 与 taskBudget 组合在当前公开入口不可达,仅作源码核对。
|
|
50
|
+
运营接口 401 及 shell 失败文案与 `is_error:false` 不一致的线索仍待定位。
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# 1.4.6-beta.0
|
|
2
|
+
|
|
3
|
+
本版将已在本机完成验证和独立审核的修复发布到 npm。基线为
|
|
4
|
+
`1.4.5-rc.10`,实现与本地候选 `1.4.5-rc.10.local-issues-38-42.1`
|
|
5
|
+
一致;本次只调整发布版本、重新构建并补充版本说明。
|
|
6
|
+
|
|
7
|
+
## 相对 npm rc.10 的变化
|
|
8
|
+
|
|
9
|
+
- #22:失败的 Bash 命令保留 stdout/stderr,较长的错误输出明确标记截断。
|
|
10
|
+
- #35:显式 thinking 设置通过 CLI、SDK 正确传给兼容 Messages 网关;
|
|
11
|
+
原生模型继续遵循各自能力适配。
|
|
12
|
+
- #36:bare 模式显式请求 Write 等不可用工具时,在请求模型前明确报错,
|
|
13
|
+
避免静默过滤。bare 默认仍为 Bash、Read、Edit。
|
|
14
|
+
- #38:SDK 支持异步输入序列,逐轮返回结果,正确处理输入结束、取消和关闭。
|
|
15
|
+
- #39:中文等不依赖空格分词的请求可以进入已有相关记忆选择流程。
|
|
16
|
+
- #40:新增 SDK Session/Turn 接口,支持常驻进程、排队输入、优先纠正和逐轮取消。
|
|
17
|
+
- #41:新增 ContextCheckpoint,支持保存、压缩请求中的历史工具结果及恢复原证据。
|
|
18
|
+
- #42:新增独立 daemon 执行持久任务,记录执行与本地结果交付,避免交付重试重复执行。
|
|
19
|
+
- GLM 服务端工具历史切换到其他模型时,转换已完成且配对明确的扩展结果;
|
|
20
|
+
保留原始会话记录和普通客户端工具调用。
|
|
21
|
+
- 保留已验证的流式工具调用、压缩恢复和会话并发写入修复,以及 rc.10 的
|
|
22
|
+
请求期限、取消、后台任务清理和唯一终态处理。
|
|
23
|
+
|
|
24
|
+
## 安装与兼容
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
npm install --global @margay/ccl-core@1.4.6-beta.0
|
|
28
|
+
ccl --version
|
|
29
|
+
margay --version
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Node 要求仍为 `>=22.19.0`。CLI 和 SDK 随同一个包发布。
|
|
33
|
+
全局升级不会替换 Margay Desktop 内置的运行包;桌面端需单独更新其依赖和构建。
|
|
34
|
+
本版不自动安装系统启动服务、不自动重启已有会话,也不迁移用户数据。
|
|
35
|
+
|
|
36
|
+
目标 npm 标签:`latest` 指向本版,`next` 与 `stable` 指向 `1.4.5-rc.10`。
|
|
37
|
+
`rc` 标签保持原值。
|
|
38
|
+
|
|
39
|
+
## 验证与仍需处理的问题
|
|
40
|
+
|
|
41
|
+
核心实现的独立审核和执行证据见仓库中的
|
|
42
|
+
`docs/issues-22-35-36-fix-2026-10-18.md` 与
|
|
43
|
+
`docs/issues-38-42-fix-2026-10-19.md`。本次发布的构建、安装及协议验证记录
|
|
44
|
+
保存在 `.rll/records/release-1.4.6-beta.0/`,发布包与验证包使用同一份 tarball。
|
|
45
|
+
|
|
46
|
+
- 此版仍有 AI 指令审核发现的定时语义问题:相对时间、精确时刻和未来开始可能
|
|
47
|
+
生成不符合要求的计划;“关闭终端继续运行”也可能只保存任务而未启动 daemon。
|
|
48
|
+
- HTTP 400/401 的部分具体原因展示和父子任务重复错误恢复用例仍有既有失败。
|
|
49
|
+
- 任务达到配置的轮次上限仍会失败,不保证自动续接;提高输出预算不突破模型或网关上限。
|
|
50
|
+
- 检查点验证证明保存/恢复可用,不代表性能或 token 成本改善。
|
|
51
|
+
- 全仓类型检查存在 11 条已记录的基线错误;Windows 实机未验证。
|
|
52
|
+
|
|
53
|
+
需要回退时可安装 `@margay/ccl-core@1.4.5-rc.10`。回退不会撤销已经执行的任务或文件写入;
|
|
54
|
+
依赖本版新 SDK 接口的宿主应同步回退其调用代码。
|
package/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@margay/ccl-core",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.6-beta.0",
|
|
4
4
|
"description": "Core runtime extracted from Claude Code (ccl) for Margay integration",
|
|
5
5
|
"license": "SEE LICENSE IN README.md",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
+
"ccl": "dist/cli.js",
|
|
8
9
|
"margay": "dist/cli.js"
|
|
9
10
|
},
|
|
10
11
|
"files": [
|
|
11
12
|
"dist",
|
|
12
13
|
"README.md",
|
|
13
14
|
"QUICKSTART.md",
|
|
15
|
+
"docs/cli-installation.md",
|
|
14
16
|
"INTENT-GATEWAY-GUIDE.md",
|
|
15
17
|
"docs/index.md",
|
|
16
18
|
"docs/error-recovery-usage.md",
|
|
@@ -37,10 +39,13 @@
|
|
|
37
39
|
"docs/features/business-facts.md",
|
|
38
40
|
"docs/releases/1.4.5-rc.8.md",
|
|
39
41
|
"docs/features/copy-on-select.md",
|
|
40
|
-
"docs/releases/1.4.5-rc.9.md"
|
|
42
|
+
"docs/releases/1.4.5-rc.9.md",
|
|
43
|
+
"docs/releases/1.4.5-rc.10.md",
|
|
44
|
+
"docs/context-checkpoints-and-daemon.md",
|
|
45
|
+
"docs/releases/1.4.6-beta.0.md"
|
|
41
46
|
],
|
|
42
47
|
"engines": {
|
|
43
|
-
"node": ">=
|
|
48
|
+
"node": ">=22.19.0"
|
|
44
49
|
},
|
|
45
50
|
"scripts": {
|
|
46
51
|
"typecheck": "tsc --noEmit",
|
|
@@ -128,7 +133,7 @@
|
|
|
128
133
|
"tree-kill": "^1.2.2",
|
|
129
134
|
"turndown": "^7.2.4",
|
|
130
135
|
"type-fest": "^5.5.0",
|
|
131
|
-
"undici": "
|
|
136
|
+
"undici": "8.0.2",
|
|
132
137
|
"usehooks-ts": "^3.1.1",
|
|
133
138
|
"vscode-jsonrpc": "^8.2.1",
|
|
134
139
|
"vscode-languageserver-protocol": "^3.17.5",
|
package/sdk/API.md
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
} from '@margay/ccl-core/sdk'
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
- **运行时**: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?) →
|
|
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
|
-
|
|
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.
|