@coda-console/claude 1.6.1 → 1.6.2
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/session.mjs +7 -2
- package/agent/waits.mjs +19 -3
- package/package.json +1 -1
package/agent/session.mjs
CHANGED
|
@@ -42,6 +42,8 @@ const denied = { behavior: 'deny', message: 'Coda 没有在请求结束前给出
|
|
|
42
42
|
// A child session's output that comes before its task, kept until the task names that session: all of it, since what streamed
|
|
43
43
|
// counts as said, a message's streamed pieces as one.
|
|
44
44
|
const CHUNKS = new Set(['agent_message_chunk', 'agent_thought_chunk']);
|
|
45
|
+
// What a turn says or does, as against what the session is (its settings, commands, title, usage).
|
|
46
|
+
const SAYING = new Set([...CHUNKS, 'tool_call', 'tool_call_update', 'plan']);
|
|
45
47
|
// How many messages sent into a prompt (steer) may be waiting to be taken at once.
|
|
46
48
|
const STEERING_LIMIT = 32;
|
|
47
49
|
|
|
@@ -109,6 +111,8 @@ export class ClaudeSession {
|
|
|
109
111
|
else early.push(update);
|
|
110
112
|
return;
|
|
111
113
|
}
|
|
114
|
+
// What a turn of Claude's own says comes after what it handles (`claim`).
|
|
115
|
+
if (sessionId === this.sessionId && SAYING.has(update.sessionUpdate)) this.waits.claim();
|
|
112
116
|
this.notify('session/update', { sessionId, update });
|
|
113
117
|
}
|
|
114
118
|
registered(sessionId) {
|
|
@@ -306,13 +310,14 @@ export class ClaudeSession {
|
|
|
306
310
|
this.begin();
|
|
307
311
|
// A subagent's request is in its session.
|
|
308
312
|
const sessionId = this.waits.childSession(context.agentID) ?? this.sessionId;
|
|
313
|
+
if (sessionId === this.sessionId) this.waits.claim();
|
|
309
314
|
return toolName === 'AskUserQuestion'
|
|
310
315
|
? this.ask('elicitation/create', questionForm({ sessionId, toolCallId: context.toolUseID, input }), signal, { behavior: 'deny', message: '问答已失效' })
|
|
311
316
|
: this.ask('session/request_permission', permissionRequest({ sessionId, toolName, input, context }), signal, denied);
|
|
312
317
|
}
|
|
313
318
|
elicitation(request, { signal }) {
|
|
314
319
|
if (!['starting', 'running'].includes(this.phase)) return Promise.resolve({ action: 'cancel' });
|
|
315
|
-
this.begin();
|
|
320
|
+
this.begin(); this.waits.claim();
|
|
316
321
|
return this.ask('elicitation/create', elicitationForm({ sessionId: this.sessionId, request }), signal, { action: 'cancel' });
|
|
317
322
|
}
|
|
318
323
|
// Asked of Coda until it answers; withdrawn (`$/cancel_request`) once the SDK no longer waits for the answer.
|
|
@@ -517,7 +522,7 @@ export class ClaudeSession {
|
|
|
517
522
|
const outcome = this.ending ?? (failed ? { failure: error } : { stopReason: cancelled ? 'cancelled' : 'end_turn' });
|
|
518
523
|
if (this.ending || !this.steering.size) this.settle(id, outcome);
|
|
519
524
|
else { this.held = id; if (cancelled || failed) this.dropRest(outcome); }
|
|
520
|
-
} else if (this.waits.handling) {
|
|
525
|
+
} else if (this.waits.handling || this.waits.unclaimed) {
|
|
521
526
|
// A turn of Claude's own has no RPC reply to fail. End its own activity as failed with the reason, rather than
|
|
522
527
|
// saying the error as an answer and marking the work complete. The task that woke it retains its own outcome.
|
|
523
528
|
this.waits.finished(error ? { error: error.message, title: error.data?._meta?.coda?.failure?.kind === 'quota_exhausted' ? '额度不足' : undefined } : {});
|
package/agent/waits.mjs
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
// What a subagent launches (`parent_task_id`) is told in that subagent's session, once a wait has told Coda that session.
|
|
5
5
|
// A task that ends while no turn goes will wake Claude for its result: until a turn takes it up, or the session says idle,
|
|
6
6
|
// it is still waited on. A turn Claude begins on its own handles what woke it (`takeUp`): what its opening message names,
|
|
7
|
-
// else the tasks that ended, else the one wakeup due, else a wait of the turn's own
|
|
7
|
+
// else the tasks that ended, else the one wakeup due, else a wait of the turn's own, told only once the turn says or asks
|
|
8
|
+
// something (`claim`); what it handles ends with that turn.
|
|
8
9
|
const FINAL = { completed: 'done', failed: 'failed', killed: 'cancelled', stopped: 'cancelled', ended: 'done' };
|
|
9
10
|
const open = state => state === 'waiting' || state === 'handling';
|
|
10
11
|
const cut = (value, limit) => typeof value === 'string' && value ? value.slice(0, limit) : undefined;
|
|
@@ -146,6 +147,12 @@ export class ClaudeWaits {
|
|
|
146
147
|
let handled = [];
|
|
147
148
|
if (origin?.kind === 'task-notification') handled = origin.subkind === 'scheduled-trigger' ? crons.filter(cron => cron.prompt && cron.prompt === text)
|
|
148
149
|
: [...this.tasks.values()].filter(task => origin.runId && task.runId === origin.runId && open(task.state));
|
|
150
|
+
// A message a subagent sent (SendMessage: its report handed back, or word of how it goes) is that subagent's. Taken up for
|
|
151
|
+
// what it said, not for its end (`messaged`): the subagent ends as its end is told, not with this turn.
|
|
152
|
+
if (origin?.kind === 'peer') {
|
|
153
|
+
handled = [...this.tasks.values()].filter(task => origin.senderTaskId && task.taskId === origin.senderTaskId && open(task.state));
|
|
154
|
+
for (const task of handled) task.messaged = true;
|
|
155
|
+
}
|
|
149
156
|
if (!handled.length) handled = pending.sort((a, b) => a.endedAt - b.endedAt);
|
|
150
157
|
if (!handled.length) {
|
|
151
158
|
// Which came due is not told: the one due, or else the only one there is.
|
|
@@ -153,8 +160,11 @@ export class ClaudeWaits {
|
|
|
153
160
|
if ((due.length || crons.length) === 1) handled = due.length ? due : crons;
|
|
154
161
|
}
|
|
155
162
|
if (handled.length) for (const wait of handled) this.report(wait, 'handling');
|
|
156
|
-
|
|
163
|
+
// Nothing it names: told as a wait of its own once it says or asks something. One that says nothing is not told at all,
|
|
164
|
+
// as Claude Code's turn for the notice of a subagent whose report an earlier turn took in, which reaches no model.
|
|
165
|
+
else this.unclaimed = true;
|
|
157
166
|
}
|
|
167
|
+
claim() { if (this.unclaimed) { this.unclaimed = false; this.hold(); } }
|
|
158
168
|
hold() {
|
|
159
169
|
const own = { waitId: `turn-${++this.turns}` };
|
|
160
170
|
this.own.add(own);
|
|
@@ -164,8 +174,14 @@ export class ClaudeWaits {
|
|
|
164
174
|
finished({ error, title } = {}) {
|
|
165
175
|
// Processing a successful task's result can fail. Give that failure its own activity instead of changing the task's
|
|
166
176
|
// outcome, and leave recurring wakeups scheduled. There may already be a turn-owned activity (hold).
|
|
177
|
+
this.unclaimed = false;
|
|
167
178
|
if (error && !this.own.size) this.hold();
|
|
168
|
-
for (const task of this.tasks.values())
|
|
179
|
+
for (const task of this.tasks.values()) {
|
|
180
|
+
if (task.state !== 'handling') continue;
|
|
181
|
+
const messaged = task.messaged; task.messaged = false;
|
|
182
|
+
if (messaged && task.final === undefined) { this.report(task, 'waiting'); continue; }
|
|
183
|
+
this.report(task, task.final ?? 'done'); task.final = undefined;
|
|
184
|
+
}
|
|
169
185
|
for (const cron of this.crons.values()) {
|
|
170
186
|
if (cron.state !== 'handling') continue;
|
|
171
187
|
if (cron.final !== undefined || !cron.recurring) { this.report(cron, 'done'); continue; }
|
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.
|
|
6
|
+
"version": "1.6.2",
|
|
7
7
|
"categories": ["Agent"],
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@agentclientprotocol/sdk": "1.4.0",
|