@inetafrica/open-claudia 3.0.25 → 3.0.26
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/CHANGELOG.md +7 -0
- package/channels/spaces/adapter.js +33 -8
- package/core/runner.js +7 -1
- package/package.json +1 -1
- package/test-provider-language.js +10 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v3.0.26 — Spaces replies actually post (and stop saying "Queued.")
|
|
4
|
+
|
|
5
|
+
- **An engaged Spaces reply no longer fails with 400 "Parent comment not found."** Core threads every agent reply under the triggering message id — a Telegram-ism. On Spaces that id is a *notification* id, not a comment id, so the backend rejected the whole post and the bot silently failed to answer any mention. `send()` now only threads under an explicit `comment:`-prefixed id and otherwise posts a top-level task comment; if a stale/deleted parent still 400s, it retries once at top level so the reply always lands.
|
|
6
|
+
- **The bot threads its reply under the exact comment it's answering.** When a notification names a specific comment (a reply's `replyId`, or a mention/comment that targets a comment), the reply is threaded under it instead of floating at the task root. Task-level mentions and assignments stay top-level, as before.
|
|
7
|
+
- **No more "Queued." noise on a task thread.** The transient queue/compaction acknowledgement is a chat-UX affordance for Telegram/Kazee. On a Spaces task it posted a throwaway "Queued." comment when a second turn stacked (and, pre-fix, that post itself 400'd). Spaces now stays silent while queued and simply posts the real reply when its turn runs. Per-identity run serialization is unchanged and intended — one being, one hand.
|
|
8
|
+
- **Agent-space / OpenClaw:** no new deps, no new env, no schema change; Docker image builds identically (FROM the pre-baked `:base`). Existing pods pick this up on their next approved upgrade.
|
|
9
|
+
|
|
3
10
|
## v3.0.25 — The bot types back on a Spaces task
|
|
4
11
|
|
|
5
12
|
- **"Open Claudia is typing…" now shows on a Spaces task thread while the bot composes.** Core already runs a typing heartbeat around every agent turn (calling `adapter.typing()` every ~4s and `adapter.typingStop()` at turn-end); on Spaces those were no-op stubs. They now re-emit the exact `comment:typing` event a human client sends over the adapter's existing authenticated socket, so a teammate watching a task sees the bot compose like any other member — the same three-dot indicator the web and mobile apps already render.
|
|
@@ -202,9 +202,22 @@ class SpacesAdapter {
|
|
|
202
202
|
actorName = actorName || `${actorId.firstName || ""} ${actorId.lastName || ""}`.trim() || actorId.email;
|
|
203
203
|
actorId = actorId.id || actorId._id;
|
|
204
204
|
}
|
|
205
|
+
// The specific comment to thread a reply under, when the event is about one:
|
|
206
|
+
// a reply → the reply comment (replyId); a comment/mention that targets a
|
|
207
|
+
// comment → that comment id. Absent for task-level mentions and assignments,
|
|
208
|
+
// where the reply is a top-level task comment.
|
|
209
|
+
const commentId = String(
|
|
210
|
+
data.replyId || inner.replyId
|
|
211
|
+
|| data.commentId || inner.commentId
|
|
212
|
+
|| (data.targetType === "comment" ? data.targetId : "")
|
|
213
|
+
|| (inner.targetType === "comment" ? inner.targetId : "")
|
|
214
|
+
|| (payload.targetType === "comment" ? payload.targetId : "")
|
|
215
|
+
|| "",
|
|
216
|
+
);
|
|
205
217
|
return {
|
|
206
218
|
type,
|
|
207
219
|
taskId,
|
|
220
|
+
commentId,
|
|
208
221
|
actorId: actorId ? String(actorId) : "",
|
|
209
222
|
actorName: actorName || "",
|
|
210
223
|
spaceId: payload.spaceId || inner.spaceId || null,
|
|
@@ -261,7 +274,10 @@ class SpacesAdapter {
|
|
|
261
274
|
userId: n.actorId || n.taskId,
|
|
262
275
|
type: "text",
|
|
263
276
|
text: n.content || `[${n.type} on task ${n.title || n.taskId}]`,
|
|
264
|
-
|
|
277
|
+
// Thread the bot's reply under the exact comment that triggered this engage
|
|
278
|
+
// (a reply, or a mention targeting a comment). Falls back to the
|
|
279
|
+
// notification id, which send() posts as a top-level task comment.
|
|
280
|
+
messageId: n.commentId ? `comment:${n.commentId}` : id,
|
|
265
281
|
from: { id: n.actorId, name: n.actorName },
|
|
266
282
|
spaces: { taskId: n.taskId, spaceId: n.spaceId, notificationType: n.type, deepLink: n.deepLink, title: n.title, mode: this._taskLoopMode() },
|
|
267
283
|
raw: payload,
|
|
@@ -473,15 +489,24 @@ class SpacesAdapter {
|
|
|
473
489
|
async send(channelId, text, opts = {}) {
|
|
474
490
|
const taskId = String(channelId || "").replace(/^task:/, "");
|
|
475
491
|
if (!taskId) { console.error("Spaces send: no taskId in channelId", channelId); return null; }
|
|
492
|
+
// Thread only under an EXPLICIT comment id (a "comment:"-prefixed replyTo,
|
|
493
|
+
// set on an engaged reply/comment-mention). Core's default replyTo is the
|
|
494
|
+
// triggering NOTIFICATION id, which is not a comment — threading under it
|
|
495
|
+
// 400s ("Parent comment not found"), so treat anything else as a top-level
|
|
496
|
+
// task comment.
|
|
497
|
+
const replyTo = String(opts.replyTo || "");
|
|
498
|
+
const parentId = replyTo.startsWith("comment:") ? replyTo.slice("comment:".length) : null;
|
|
499
|
+
const post = (parent) => this.client.createComment({ taskId, text, mentions: opts.mentions || [], parentId: parent });
|
|
500
|
+
const idOf = (c) => (c && (c._id || c.id) ? String(c._id || c.id) : true);
|
|
476
501
|
try {
|
|
477
|
-
|
|
478
|
-
taskId,
|
|
479
|
-
text,
|
|
480
|
-
mentions: opts.mentions || [],
|
|
481
|
-
parentId: opts.replyTo ? String(opts.replyTo).replace(/^comment:/, "") : null,
|
|
482
|
-
});
|
|
483
|
-
return comment && (comment._id || comment.id) ? String(comment._id || comment.id) : true;
|
|
502
|
+
return idOf(await post(parentId));
|
|
484
503
|
} catch (e) {
|
|
504
|
+
// A stale/deleted parent 400s; retry once at top level so the reply always
|
|
505
|
+
// lands on the task rather than being dropped.
|
|
506
|
+
if (parentId && /parent comment not found/i.test(e.message || "")) {
|
|
507
|
+
try { return idOf(await post(null)); }
|
|
508
|
+
catch (e2) { console.error("Spaces send error (top-level retry):", e2.message); return null; }
|
|
509
|
+
}
|
|
485
510
|
console.error("Spaces send error:", e.message);
|
|
486
511
|
return null;
|
|
487
512
|
}
|
package/core/runner.js
CHANGED
|
@@ -1982,7 +1982,13 @@ function runAgent(prompt, cwd, replyToMsgId, opts = {}) {
|
|
|
1982
1982
|
}
|
|
1983
1983
|
const deferred = deferredRunResult();
|
|
1984
1984
|
state.messageQueue.push({ runContext, store, deferred, queuedAt: Date.now() });
|
|
1985
|
-
|
|
1985
|
+
// Transient queue/compaction acks are a chat-UX affordance (Telegram/Kazee).
|
|
1986
|
+
// On a Spaces task thread they'd post a throwaway "Queued." comment on the
|
|
1987
|
+
// task — noise. Spaces stays quiet and just posts the real reply when its
|
|
1988
|
+
// turn runs.
|
|
1989
|
+
if (currentAdapter()?.type !== "spaces") {
|
|
1990
|
+
send(state.isCompacting ? "Compacting context, will pick this up next…" : "Queued.", { replyTo: replyToMsgId }).catch(() => {});
|
|
1991
|
+
}
|
|
1986
1992
|
return deferred.promise;
|
|
1987
1993
|
}
|
|
1988
1994
|
return executeAdmittedRun(runContext, state, store, false, true);
|
package/package.json
CHANGED
|
@@ -32,15 +32,16 @@ assert.doesNotMatch(setup, /Your Claude Code bot is connected|Description=Claude
|
|
|
32
32
|
|
|
33
33
|
const pkg = JSON.parse(read("package.json"));
|
|
34
34
|
assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
|
|
35
|
-
// 3.0.
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
|
|
35
|
+
// 3.0.26 approved by Sumeet 2026-07-15: fix Spaces reply posting. (1) An engaged
|
|
36
|
+
// reply no longer 400s — send() only threads under an explicit "comment:"-prefixed
|
|
37
|
+
// id (the triggering reply/comment-mention) and otherwise posts a top-level task
|
|
38
|
+
// comment, with a top-level retry if a stale parent 400s. (2) The bot threads its
|
|
39
|
+
// reply under the exact triggering comment when the notification names one
|
|
40
|
+
// (replyId / comment-target). (3) The transient "Queued." ack is suppressed on
|
|
41
|
+
// Spaces — a stacked turn stays silent and just posts the real reply when it runs,
|
|
42
|
+
// instead of dropping a throwaway "Queued." comment on the task. Builds on 3.0.25's
|
|
43
|
+
// comment:typing emit.
|
|
44
|
+
assert.strictEqual(pkg.version, "3.0.26", "release version must remain unchanged without explicit approval");
|
|
44
45
|
for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
|
|
45
46
|
assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
|
|
46
47
|
}
|