@coda-console/claude 1.7.8 → 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 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,13 +3,13 @@
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.7.8",
6
+ "version": "1.8.0",
7
7
  "categories": [
8
8
  "Agent"
9
9
  ],
10
10
  "dependencies": {
11
11
  "@agentclientprotocol/sdk": "1.4.0",
12
- "@anthropic-ai/claude-agent-sdk": "0.3.296"
12
+ "@anthropic-ai/claude-agent-sdk": "0.3.312"
13
13
  },
14
14
  "contributes": {
15
15
  "agents": [
@@ -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
  },