@coda-console/claude 1.7.9 → 1.8.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/agent/models.mjs +5 -2
- package/agent/session.mjs +21 -5
- package/package.json +3 -2
package/agent/models.mjs
CHANGED
|
@@ -53,10 +53,13 @@ function modelSelect(currentValue, models) {
|
|
|
53
53
|
// The effort that leaves it to the model, marked as the agent's own default (docs/agent-protocol.md): what Coda goes back to
|
|
54
54
|
// when the session's effort is taken off.
|
|
55
55
|
export const MODEL_EFFORT = 'default';
|
|
56
|
-
// `current`: { mode, model, effort }. An effort not chosen is null: the model's own default.
|
|
56
|
+
// `current`: { mode, model, effort, ultracode }. An effort not chosen is null: the model's own default. Ultracode is null
|
|
57
|
+
// where the model cannot run it.
|
|
57
58
|
export function configOptions(models, current) {
|
|
58
59
|
const efforts = models.find(m => m.id === current.model)?.efforts ?? [];
|
|
59
60
|
return [select('mode', '审批模式', 'mode', current.mode, MODES), modelSelect(current.model, models),
|
|
60
61
|
...(efforts.length ? [select('effort', '推理强度', 'thought_level', efforts.includes(current.effort) ? current.effort : MODEL_EFFORT,
|
|
61
|
-
[{ id: MODEL_EFFORT, name: '模型默认', _meta: { coda: { default: true } } }, ...efforts.map(id => ({ id, name: id }))])] : [])
|
|
62
|
+
[{ id: MODEL_EFFORT, name: '模型默认', _meta: { coda: { default: true } } }, ...efforts.map(id => ({ id, name: id }))])] : []),
|
|
63
|
+
...(current.ultracode !== null ? [{ id: 'ultracode', name: 'Ultracode', description: '每个任务都用工作流派多个子 Agent 完成,用量明显更大',
|
|
64
|
+
category: 'model_config', type: 'boolean', currentValue: current.ultracode }] : [])];
|
|
62
65
|
}
|
package/agent/session.mjs
CHANGED
|
@@ -80,7 +80,7 @@ export class ClaudeSession {
|
|
|
80
80
|
this.steering = new Set(); this.held = null; this.ending = null; this.turning = false;
|
|
81
81
|
// The model services chosen in Coda (providers.mjs), and the service's model the query runs on: null on Claude's own.
|
|
82
82
|
this.services = chosenServices(env); this.serviceModel = null;
|
|
83
|
-
this.models = []; this.commands = []; this.terminal = null; this.current = { mode: 'default', model: undefined, effort: null };
|
|
83
|
+
this.models = []; this.commands = []; this.terminal = null; this.current = { mode: 'default', model: undefined, effort: null, ultracode: null };
|
|
84
84
|
// Streamed text by API message, so that the complete message adds only what was not streamed; tool calls said.
|
|
85
85
|
this.streams = new Map(); this.streamed = new Map(); this.tools = new ToolCallCache(); this.children = new Set(); this.early = new Map(); this.compaction = null;
|
|
86
86
|
this.waits = new ClaudeWaits({ say: (params, sessionId = this.sessionId) => this.notify('_coda/wait', { sessionId, ...params }),
|
|
@@ -189,7 +189,7 @@ export class ClaudeSession {
|
|
|
189
189
|
const email = initialization.account?.email;
|
|
190
190
|
this.account = typeof email === 'string' && email.trim() ? accountHash(email) : null;
|
|
191
191
|
if (this.env.CODA_PLUGIN_STATE) new ClaudeSessionLimits({ path: join(this.env.CODA_PLUGIN_STATE, 'limits.json') }).startedAs(this.account);
|
|
192
|
-
this.current.model = this.models[0]?.id;
|
|
192
|
+
this.current.model = this.models[0]?.id; this.current.ultracode = await this.ultracode();
|
|
193
193
|
this.phase = 'running';
|
|
194
194
|
return { ...(resume ? {} : { sessionId: this.sessionId }), configOptions: this.options() };
|
|
195
195
|
} catch (error) {
|
|
@@ -204,6 +204,7 @@ export class ClaudeSession {
|
|
|
204
204
|
this.input = new InputStream();
|
|
205
205
|
const entry = this.served(model);
|
|
206
206
|
const settings = { ...this.launch.settings, ...(this.current.effort ? { effortLevel: this.current.effort } : {}),
|
|
207
|
+
...(this.current.ultracode === true ? { ultracode: true } : {}),
|
|
207
208
|
...(entry ? serviceSettings(this.services.find(service => service.ref === entry.service), entry.model) : {}) };
|
|
208
209
|
this.serviceModel = entry ? model : null; this.cacheUse.restarted();
|
|
209
210
|
// A compaction in the query moved off ends with it.
|
|
@@ -219,6 +220,11 @@ export class ClaudeSession {
|
|
|
219
220
|
new Promise((_, reject) => { timer = setTimeout(() => reject(failure('Claude 初始化超时', -32001)), this.initializationTimeoutMs); })])
|
|
220
221
|
.finally(() => clearTimeout(timer));
|
|
221
222
|
}
|
|
223
|
+
// Whether the query's model can run Ultracode (sdk Settings.ultracode), and whether it does; null where it cannot.
|
|
224
|
+
async ultracode() {
|
|
225
|
+
const { applied } = await this.query.getSettings();
|
|
226
|
+
return applied.ultracodeAvailable ? applied.ultracode === true : null;
|
|
227
|
+
}
|
|
222
228
|
// The model chosen is of another service than the query's (Claude's own, or a chosen service's): the session goes on in a
|
|
223
229
|
// query started for it, resuming what the last one said. Before a turn, as Coda sets a session's model before each.
|
|
224
230
|
async reroute() {
|
|
@@ -233,11 +239,18 @@ export class ClaudeSession {
|
|
|
233
239
|
this.start(said ? { resume: this.sessionId } : { sessionId: this.sessionId }, this.current.model);
|
|
234
240
|
if (this.title) this.title.query = this.query;
|
|
235
241
|
await this.initialized();
|
|
242
|
+
const ultracode = await this.ultracode();
|
|
243
|
+
if (ultracode !== this.current.ultracode) {
|
|
244
|
+
this.current.ultracode = ultracode;
|
|
245
|
+
this.update(this.sessionId, { sessionUpdate: 'config_option_update', configOptions: this.options() });
|
|
246
|
+
}
|
|
236
247
|
} catch (error) {
|
|
237
248
|
this.end(this.query, error);
|
|
238
249
|
throw error instanceof RequestError ? error : failure(error?.message || 'Claude 会话启动失败');
|
|
239
250
|
}
|
|
240
251
|
}
|
|
252
|
+
// A change of effort turns Ultracode off unless the same settings keep it on.
|
|
253
|
+
keptUltracode() { return this.current.ultracode === true ? { ultracode: true } : {}; }
|
|
241
254
|
// The chosen services' model that `model` is; null for one of Claude's own.
|
|
242
255
|
served(model) { return this.models.find(m => m.id === model && m.model) ?? null; }
|
|
243
256
|
// Whether the next turn runs in another query than this one: on another service's model, or back on Claude's own.
|
|
@@ -253,16 +266,19 @@ export class ClaudeSession {
|
|
|
253
266
|
// what this one still runs would end with it.
|
|
254
267
|
const switching = (this.served(value)?.id ?? null) !== this.serviceModel;
|
|
255
268
|
if (switching && (this.prompts.size || this.waits.pending)) throw failure('后台任务、子 Agent 或定时唤醒还没结束,结束后才能换到这个模型', -32602);
|
|
256
|
-
if (!switching && !this.serviceModel) await this.query.setModel(value);
|
|
269
|
+
if (!switching && !this.serviceModel) { await this.query.setModel(value); this.current.ultracode = await this.ultracode(); }
|
|
257
270
|
this.current.model = value;
|
|
258
271
|
// An effort chosen for the last model that this one does not have is no choice of it: the model's own goes on.
|
|
259
272
|
if (this.current.effort !== null && !this.models.find(model => model.id === value).efforts.includes(this.current.effort)) {
|
|
260
|
-
await this.query.applyFlagSettings({ effortLevel: null }); this.current.effort = null;
|
|
273
|
+
await this.query.applyFlagSettings({ effortLevel: null, ...this.keptUltracode() }); this.current.effort = null;
|
|
261
274
|
}
|
|
262
275
|
} else if (configId === 'effort') {
|
|
263
276
|
const effort = value === MODEL_EFFORT ? null : value;
|
|
264
277
|
if (effort !== null && !this.models.find(model => model.id === this.current.model)?.efforts.includes(effort)) throw failure('推理强度不适用于当前模型', -32602);
|
|
265
|
-
await this.query.applyFlagSettings({ effortLevel: effort }); this.current.effort = effort;
|
|
278
|
+
await this.query.applyFlagSettings({ effortLevel: effort, ...this.keptUltracode() }); this.current.effort = effort;
|
|
279
|
+
} else if (configId === 'ultracode') {
|
|
280
|
+
if (typeof value !== 'boolean' || this.current.ultracode === null) throw failure('Ultracode 不可用', -32602);
|
|
281
|
+
await this.query.applyFlagSettings({ ultracode: value }); this.current.ultracode = value;
|
|
266
282
|
} else throw failure(`没有设置项 ${configId}`, -32602);
|
|
267
283
|
return { configOptions: this.options() };
|
|
268
284
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"id": "claude",
|
|
4
4
|
"displayName": "Claude",
|
|
5
5
|
"description": "Claude SDK Agent:Coda 基于 Claude Agent SDK 写的 ACP 入口,一个进程一个 SDK 会话;资源页的「Claude」面板显示会话报告的额度,可从 Coda 登录。",
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.8.0",
|
|
7
7
|
"categories": [
|
|
8
8
|
"Agent"
|
|
9
9
|
],
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"command": "exec \"$CODA_NODE\" \"$CODA_PLUGIN_PATH/agent/entry.mjs\"",
|
|
23
23
|
"modes": {
|
|
24
24
|
"title": "审批模式",
|
|
25
|
-
"unset": "
|
|
25
|
+
"unset": "Agent 默认",
|
|
26
|
+
"note": "未选择时从人工审批开始;Claude 自己切换的模式保持不变。",
|
|
26
27
|
"auto": "auto",
|
|
27
28
|
"full": "bypassPermissions"
|
|
28
29
|
},
|