@inetafrica/open-claudia 3.0.34 → 3.0.35

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 CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## v3.0.35 — Spaces comments are readable rich text, not a run-on wall
4
+
5
+ - **The bot's Spaces replies rendered as unreadable walls.** `buildCommentContent` posted the entire reply as one text node inside one paragraph. Spaces comments are TipTap/ProseMirror docs: inside a single text node newlines don't render, so paragraphs, numbered lists and `---` separators collapsed into one run-on blob — and the model's markdown (`**bold**`, `*italic*`, backticks) printed its asterisks literally.
6
+ - **Outgoing comments now convert markdown → a real ProseMirror doc.** Blank lines become paragraphs, single newlines hardBreaks; `**`/`*`/`` ` `` become bold/italic/code marks (word-bounded, so snake_case identifiers stay plain); `-`/`1.` runs become real bullet/ordered lists; `#`–`###` become headings; ``` fences become code blocks; `---` becomes a divider. Every emitted node type was verified against the Spaces frontend editor schema (StarterKit + HorizontalRule + CodeBlockLowlight), so nothing renders broken.
7
+ - **Mention chips unchanged.** The proven tag-chip shape still leads the first paragraph, so notification fan-out and the "@Name text…" lead-in behave exactly as before. `flattenContent` also learned `codeBlock`/`horizontalRule` so the bot reads its own comments back cleanly.
8
+ - **Agent-space / OpenClaw:** no new deps, no new env, no schema change. Covered by new `test-spaces-comment-content.js` in the pretest chain. Bots pick this up on their next upgrade, together with v3.0.34's /upgrade redelivery guard.
9
+
3
10
  ## v3.0.34 — /upgrade no longer triggers a rollout storm
4
11
 
5
12
  - **One `/upgrade` used to mean up to five rollouts.** On AgentSpace pods the control plane answers `/upgrade` by rolling the deployment, which SIGTERMs the bot mid-turn. Telegram long-polling keeps no persisted offset, so the still-unconfirmed `/upgrade` update was redelivered to the freshly booted pod — which triggered *another* rollout, and so on. Observed on rahilcoder 2026-07-17: deployment revisions 47–51 (five rollouts in three minutes) from a single command; every earlier `/upgrade` had silently double-fired the same way (revs 42/43, 45/46).
@@ -89,8 +89,9 @@ function flattenContent(node) {
89
89
  if (t === "text") return node.text || "";
90
90
  if (t === "mention") return `@${node.attrs?.label || node.attrs?.id || ""}`;
91
91
  if (t === "hardBreak") return "\n";
92
+ if (t === "horizontalRule") return "---\n";
92
93
  const inner = (node.content || []).map(flattenContent).join("");
93
- if (["paragraph", "heading", "listItem", "blockquote"].includes(t)) return inner + "\n";
94
+ if (["paragraph", "heading", "listItem", "blockquote", "codeBlock"].includes(t)) return inner + "\n";
94
95
  return inner;
95
96
  }
96
97
 
@@ -98,17 +99,126 @@ function contentText(raw) {
98
99
  return flattenContent(raw).trim();
99
100
  }
100
101
 
102
+ // Inline markdown → ProseMirror inline nodes. Supports `code`, **bold** and
103
+ // *italic*/_italic_ (word-bounded, so snake_case identifiers and stray
104
+ // asterisks in prose survive untouched). One level only — chat comments
105
+ // don't need nesting, and a wrong guess renders worse than plain text.
106
+ const INLINE_TOKEN_RE = /(`[^`\n]+`)|(\*\*(?=\S)[^*\n]*?\S\*\*)|(\*(?=\S)[^*\n]*?\S\*)|((?<![\w`])_(?=\S)[^_\n]*?\S_(?![\w`]))/g;
107
+ function inlineNodes(text) {
108
+ const out = [];
109
+ const push = (t, marks) => { if (t) out.push(marks ? { type: "text", text: t, marks } : { type: "text", text: t }); };
110
+ let last = 0;
111
+ let m;
112
+ INLINE_TOKEN_RE.lastIndex = 0;
113
+ while ((m = INLINE_TOKEN_RE.exec(text))) {
114
+ push(text.slice(last, m.index));
115
+ if (m[1]) push(m[1].slice(1, -1), [{ type: "code" }]);
116
+ else if (m[2]) push(m[2].slice(2, -2), [{ type: "bold" }]);
117
+ else if (m[3]) push(m[3].slice(1, -1), [{ type: "italic" }]);
118
+ else push(m[4].slice(1, -1), [{ type: "italic" }]);
119
+ last = m.index + m[0].length;
120
+ }
121
+ push(text.slice(last));
122
+ return out;
123
+ }
124
+
125
+ // Block-level markdown → ProseMirror blocks. The model writes markdown; Spaces
126
+ // renders TipTap docs — posting the raw text as one text node collapses every
127
+ // newline and prints the asterisks literally (the unreadable-wall bug). Only
128
+ // node types present in the Spaces editor schema (StarterKit + HorizontalRule
129
+ // + CodeBlockLowlight) are emitted, so nothing renders broken.
130
+ function markdownBlocks(text) {
131
+ const blocks = [];
132
+ const lines = String(text || "").replace(/\r\n/g, "\n").split("\n");
133
+ let para = [];
134
+ const flushPara = () => {
135
+ if (!para.length) return;
136
+ const content = [];
137
+ para.forEach((ln, idx) => {
138
+ if (idx > 0) content.push({ type: "hardBreak" });
139
+ content.push(...inlineNodes(ln));
140
+ });
141
+ para = [];
142
+ if (content.length) blocks.push({ type: "paragraph", content });
143
+ };
144
+ let i = 0;
145
+ while (i < lines.length) {
146
+ const trimmed = lines[i].trim();
147
+ if (!trimmed) { flushPara(); i += 1; continue; }
148
+ if (/^(-{3,}|\*{3,}|_{3,})$/.test(trimmed)) { flushPara(); blocks.push({ type: "horizontalRule" }); i += 1; continue; }
149
+ if (trimmed.startsWith("```")) {
150
+ flushPara();
151
+ const language = trimmed.slice(3).trim();
152
+ const buf = [];
153
+ i += 1;
154
+ while (i < lines.length && !lines[i].trim().startsWith("```")) { buf.push(lines[i]); i += 1; }
155
+ i += 1;
156
+ blocks.push({
157
+ type: "codeBlock",
158
+ attrs: { language: language || null },
159
+ ...(buf.length ? { content: [{ type: "text", text: buf.join("\n") }] } : {}),
160
+ });
161
+ continue;
162
+ }
163
+ if (/^[-*•]\s+/.test(trimmed)) {
164
+ flushPara();
165
+ const items = [];
166
+ while (i < lines.length && /^[-*•]\s+/.test(lines[i].trim())) {
167
+ const inner = inlineNodes(lines[i].trim().replace(/^[-*•]\s+/, ""));
168
+ items.push({ type: "listItem", content: [inner.length ? { type: "paragraph", content: inner } : { type: "paragraph" }] });
169
+ i += 1;
170
+ }
171
+ blocks.push({ type: "bulletList", content: items });
172
+ continue;
173
+ }
174
+ if (/^\d+[.)]\s+/.test(trimmed)) {
175
+ flushPara();
176
+ const start = parseInt(trimmed, 10);
177
+ const items = [];
178
+ while (i < lines.length && /^\d+[.)]\s+/.test(lines[i].trim())) {
179
+ const inner = inlineNodes(lines[i].trim().replace(/^\d+[.)]\s+/, ""));
180
+ items.push({ type: "listItem", content: [inner.length ? { type: "paragraph", content: inner } : { type: "paragraph" }] });
181
+ i += 1;
182
+ }
183
+ blocks.push({ type: "orderedList", attrs: { start: Number.isNaN(start) ? 1 : start }, content: items });
184
+ continue;
185
+ }
186
+ const heading = trimmed.match(/^(#{1,3})\s+(.*)$/);
187
+ if (heading) {
188
+ flushPara();
189
+ const inner = inlineNodes(heading[2]);
190
+ blocks.push({ type: "heading", attrs: { level: heading[1].length }, ...(inner.length ? { content: inner } : {}) });
191
+ i += 1;
192
+ continue;
193
+ }
194
+ para.push(lines[i]);
195
+ i += 1;
196
+ }
197
+ flushPara();
198
+ return blocks;
199
+ }
200
+
101
201
  // Build the stringified ProseMirror doc for an outgoing comment. Optional
102
202
  // leading @mention chips (each {userId,label}) drive Spaces' notification
103
- // fan-out; the tool proved this exact node shape renders as tag chips.
203
+ // fan-out; the tool proved this exact node shape renders as tag chips. Chips
204
+ // are prepended into the first paragraph so the visual "@Name text…" lead-in
205
+ // stays identical to the pre-markdown behaviour.
104
206
  function buildCommentContent(text, mentions = []) {
105
- const inline = [];
207
+ const chips = [];
106
208
  for (const m of mentions) {
107
- inline.push({ type: "mention", attrs: { id: m.userId, label: m.label, mentionSuggestionChar: "@" } });
108
- inline.push({ type: "text", text: " " });
209
+ chips.push({ type: "mention", attrs: { id: m.userId, label: m.label, mentionSuggestionChar: "@" } });
210
+ chips.push({ type: "text", text: " " });
211
+ }
212
+ const blocks = markdownBlocks(text);
213
+ if (chips.length) {
214
+ if (blocks.length && blocks[0].type === "paragraph") {
215
+ blocks[0].content = [...chips, ...(blocks[0].content || [])];
216
+ } else {
217
+ blocks.unshift({ type: "paragraph", content: chips });
218
+ }
109
219
  }
110
- if (text) inline.push({ type: "text", text });
111
- return JSON.stringify({ type: "doc", content: [{ type: "paragraph", content: inline }] });
220
+ if (!blocks.length) blocks.push({ type: "paragraph" });
221
+ return JSON.stringify({ type: "doc", content: blocks });
112
222
  }
113
223
 
114
224
  class SpacesClient {
@@ -201,4 +311,4 @@ class SpacesClient {
201
311
  }
202
312
  }
203
313
 
204
- module.exports = { SpacesClient, flattenContent, contentText, buildCommentContent, asArray };
314
+ module.exports = { SpacesClient, flattenContent, contentText, buildCommentContent, markdownBlocks, inlineNodes, asArray };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "3.0.34",
3
+ "version": "3.0.35",
4
4
  "description": "An always-on, provider-agnostic coding-agent harness for Claude Code and OpenAI Codex via chat",
5
5
  "main": "bot.js",
6
6
  "bin": {
@@ -9,7 +9,7 @@
9
9
  "scripts": {
10
10
  "setup": "node setup.js",
11
11
  "start": "node bot.js",
12
- "pretest": "node test-provider-fixture.js && node test-provider-core-prompt.js && node test-provider-prompt-parity.js && node test-provider-env-isolation.js && node test-provider-resume-parity.js && node test-provider-bootstrap.js && node test-provider-registry.js && node test-provider-stream-decoder.js && node test-provider-events.js && node test-provider-claude.js && node test-provider-codex.js && node test-provider-runner.js && node test-provider-run-lifecycle.js && node test-provider-gateway.js && node test-provider-side-chat.js && node test-single-runtime.js && node test-provider-migration-backup.js && node test-provider-state-migration.js && node test-provider-session-history.js && node test-provider-session-commands.js && node test-provider-compaction.js && node test-provider-scheduler.js && node test-cursor-removal.js && node test-utility-provider-policy.js && node test-provider-subagent.js && node test-provider-recall.js && node test-provider-pack-review.js && node test-memory-mutation-queue.js && node test-provider-dream.js && node test-provider-enforcer.js && node test-provider-tool-hooks.js && node test-provider-boot-matrix.js && node test-provider-capabilities.js && node test-provider-language.js && node test-provider-coupling-audit.js && node test-provider-parity-e2e.js && node test-kazee-channel-health.js && node test-prompt-budget.js && node test-agency-ledger.js && node test-agency-deliberate.js && node test-guardrail.js",
12
+ "pretest": "node test-provider-fixture.js && node test-provider-core-prompt.js && node test-provider-prompt-parity.js && node test-provider-env-isolation.js && node test-provider-resume-parity.js && node test-provider-bootstrap.js && node test-provider-registry.js && node test-provider-stream-decoder.js && node test-provider-events.js && node test-provider-claude.js && node test-provider-codex.js && node test-provider-runner.js && node test-provider-run-lifecycle.js && node test-provider-gateway.js && node test-provider-side-chat.js && node test-single-runtime.js && node test-provider-migration-backup.js && node test-provider-state-migration.js && node test-provider-session-history.js && node test-provider-session-commands.js && node test-provider-compaction.js && node test-provider-scheduler.js && node test-cursor-removal.js && node test-utility-provider-policy.js && node test-provider-subagent.js && node test-provider-recall.js && node test-provider-pack-review.js && node test-memory-mutation-queue.js && node test-provider-dream.js && node test-provider-enforcer.js && node test-provider-tool-hooks.js && node test-provider-boot-matrix.js && node test-provider-capabilities.js && node test-provider-language.js && node test-provider-coupling-audit.js && node test-provider-parity-e2e.js && node test-kazee-channel-health.js && node test-prompt-budget.js && node test-agency-ledger.js && node test-agency-deliberate.js && node test-guardrail.js && node test-spaces-comment-content.js",
13
13
  "test": "OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node -e \"require('./vault'); console.log('OK')\" && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-runner-watchdog-static.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-usage-accounting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-engine.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-discoverer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-project-transcripts-smoke.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-abilities.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-extraction.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-couse.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-transfer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-tiers.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-merge-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-learning-e2e.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-nesting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-dedupe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-signal.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tools.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tooling-mode.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-manifest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-evolution.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-async.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-unified-identity.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-queue-routing.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-packs.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-persona.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-pipeline.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-relationship-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-relationship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-enforcer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-identity-prune.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-message-id.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-delivery-contract.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-lock.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-web-sessions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-poll-recovery.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-409-grace.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-conflict-heal-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-single-instance.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-streaming-split.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-spaces-autoenable.js"
14
14
  },
15
15
  "files": [
@@ -32,15 +32,15 @@ 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.34 approved by Sumeet 2026-07-17 (Rahil's triple-reboot screenshot +
36
- // "explain and fix"): /upgrade redelivery guard. On AgentSpace pods the
37
- // rollout SIGTERMs the bot mid-turn, Telegram long-polling holds no persisted
38
- // offset, and the unconfirmed /upgrade update is redelivered to the rebooted
39
- // pod — each replay triggered ANOTHER rollout (one /upgrade → five rollouts,
40
- // deployment revisions 47-51). Fix: upgrade-in-flight marker in CONFIG_DIR
41
- // guards the handler, and the ack is sent BEFORE the trigger so it can't be
42
- // lost to the SIGTERM.
43
- assert.strictEqual(pkg.version, "3.0.34", "release version must remain unchanged without explicit approval");
35
+ // 3.0.35 approved by Sumeet 2026-07-17 ("yes will this also ship the issue of
36
+ // new lines not showing properly?"): Spaces comments become real ProseMirror
37
+ // docs. buildCommentContent used to post the whole reply as ONE text node in
38
+ // ONE paragraph, so TipTap collapsed every newline and printed markdown
39
+ // asterisks literally — the unreadable-wall screenshot from the euvy bot
40
+ // thread. Now markdown converts to paragraphs/hardBreaks, bold/italic/code
41
+ // marks, bullet/ordered lists, headings, code blocks and dividers — all node
42
+ // types verified present in the Spaces editor schema.
43
+ assert.strictEqual(pkg.version, "3.0.35", "release version must remain unchanged without explicit approval");
44
44
  for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
45
45
  assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
46
46
  }