Commit Graph

1442 Commits

Author SHA1 Message Date
glifocat
12719be6e1 feat(poll-loop): inject destination reminder after SDK auto-compaction
Closes qwibitai/nanoclaw#2325.

When the Claude Code SDK auto-compacts the conversation context, the
compaction summary tends to drop the agent's learned <message to="…">
wrapping discipline. The destinations table is still populated and the
system prompt still lists them, but the behavioral pattern degrades —
A2A sends and multi-channel routing silently revert to bare-text or
single-channel delivery for the rest of the session, until the next
/clear.

Three small changes wire a reminder back into the live query when this
fires:

- New `compacted` event on ProviderEvent. Distinct from `result` so it
  doesn't mark the turn completed or get dispatched as a chat message
  (which is also why "Context compacted (N tokens compacted)." stops
  appearing as noise in user-facing chats — it was a side-effect of
  reusing the result event path).
- ClaudeProvider yields `compacted` instead of `result` for the SDK's
  compact_boundary system event.
- Poll-loop's event handler reacts by pushing a system-tagged reminder
  back into the active query when there are >1 destinations. Single-
  destination groups skip the push since they have a fallback that
  works without wrapping.

Tests cover both branches (multi-destination → reminder fires;
single-destination → no reminder) using a CompactingProvider that
emits the new event mid-stream.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 17:11:25 +02:00
gavrielc
8d5d088108 Merge pull request #2315 from alipgoldberg/setup/imessage-handle-copy
setup: drop "E.164" jargon from iMessage handle card
2026-05-07 17:13:58 +03:00
Ali Goldberg
7e0c256fa0 setup: drop "E.164" jargon from iMessage handle card
Replace "full E.164, e.g. +15551234567" with plain-language guidance
mirroring the WhatsApp setup card: "start with + and your country code,
no spaces or dashes" plus a worked example. "E.164" is the technical
name for the format and means nothing to non-telecom users; the
explanation it stands in for is one sentence.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 08:29:07 +00:00
Daniel M
d8d6f6bd65 Merge pull request #2313 from alipgoldberg/setup/teams-step-gate-back
setup: add back-to-channels exit at every Teams step gate
2026-05-07 11:16:26 +03:00
exe.dev user
88ff54cf83 setup: add back-to-channels exit at every Teams step gate
Teams setup is 6+ Azure steps over 30+ minutes. Today, every
"Done / Stuck / Show again" gate forces continuation; the only escape
is Ctrl-C, which kills setup entirely. Add a fourth option at each gate
that returns to the channel picker so a stuck operator can pick a
different channel without losing the rest of setup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 08:05:26 +00:00
Gabi Simons
4d5fa0868b Merge pull request #2297 from alipgoldberg/setup/slack-card-link-position
setup: tidy Slack app-creation card
2026-05-07 10:49:07 +03:00
gavrielc
aff3f58bc8 Merge pull request #2309 from glifocat/fix/skills-drop-sqlite3-cli-dep
fix(skills): replace sqlite3 CLI with in-tree better-sqlite3 wrapper
2026-05-06 21:13:17 +03:00
gavrielc
18635e7c7d fix(scripts/q): use stmt.reader instead of keyword sniffing for SELECT detection
The first-keyword check (`WITH` → SELECT path) was wrong for CTEs that
precede mutations (e.g. `WITH stale AS (...) DELETE FROM t WHERE ...`).
These would be routed through `db.prepare().all()` instead of executing
the mutation.

Use better-sqlite3's `stmt.reader` property, which asks SQLite's own
parser whether the statement returns data. Single mutations go through
`stmt.run()`; compound statements (which `prepare()` rejects) fall back
to `db.exec()`.

Add a regression test for WITH...DELETE.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-06 21:12:25 +03:00
NanoClaw bot user
0d7458c6f3 fix(skills): replace sqlite3 CLI with in-tree better-sqlite3 wrapper
Setup deliberately avoids the sqlite3 CLI (`setup/verify.ts:5` calls
this out: "Uses better-sqlite3 directly (no sqlite3 CLI)") and never
installs or probes for the binary. Despite that, 13 skills shelled out
to `sqlite3 ...` directly, breaking on hosts where the CLI isn't
preinstalled — the same root cause as #2191 but spread across the
skill surface.

Add `scripts/q.ts`, a ~30-LOC wrapper over the `better-sqlite3` dep
that setup already installs and verifies. Default output matches
`sqlite3 -list` (pipe-separated, no header) so existing skill text
reads identically — only the binary changes. SELECT/WITH queries go
through `db.prepare().all()`; everything else (INSERT/UPDATE/DELETE,
including compound statements) goes through `db.exec()`.

Migrate every in-tree caller:

- 17 hardcoded invocations across 8 SKILL.md files (init-first-agent,
  add-deltachat, add-signal, add-emacs, add-whatsapp, add-ollama-provider,
  debug, add-parallel) plus add-deltachat/VERIFY.md.
- `manage-channels/SKILL.md` shows canonical SQL but never prescribed
  a tool, so the assistant defaulted to `sqlite3` and silently failed.
  Add a one-line wrapper hint above the SQL block.
- `migrate-v2.sh` schema/count probes (was the original #2191 case).
  Replace `.tables` with `SELECT name FROM sqlite_master`.
- Document the wrapper convention in root `CLAUDE.md` under "Central DB".

Add `scripts/q.test.ts` with 6 vitest cases covering both modes,
NULL rendering, empty-result, compound mutations, and arg validation.
Wire `scripts/**/*.test.ts` into `vitest.config.ts`.

Out of scope (flagged for follow-up):
- `debug` and `add-parallel` still reference the v1-only path
  `store/messages.db`. Routing through the wrapper now produces a
  cleaner "no such file" error, but the surrounding sections are
  v1-era throughout — a v1-content cleanup is its own PR.
- `cleanup-sessions.sh` is being addressed in #1889 (different style,
  hard-fail rather than wrap); left untouched here to avoid stepping
  on that author's work.

Closes #2191.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 19:38:33 +02:00
exe.dev user
a36acd3413 setup: tidy Slack app-creation card
- Move the "Get started: …" URL above the numbered instructions and
  render it in bright white so it pops against the brand-cyan body.
  (Headless-only — interactive runs still auto-open the URL in a
  browser, no card line.)
- Group the OAuth scope list vertically by family (im, channels,
  groups, chat, users, reactions) instead of one comma-run wall.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 09:27:09 +00:00
gavrielc
f2d2ce9aed Merge pull request #2290 from glifocat/fix/manage-channels-canonical-queries
fix(manage-channels): include canonical SQL queries in SKILL.md
2026-05-06 01:52:19 +03:00
gavrielc
22715c163a Update README.md 2026-05-06 01:36:13 +03:00
Ethan Munoz
eacb93c4e5 fix(manage-channels): include canonical SQL queries in SKILL.md
The skill's "Assess Current State" step said only "query agent_groups,
messaging_groups, ..." without specifying columns. The `register` CLI
takes `--assistant-name "<name>"` (mentioned three times in the same
SKILL.md), but the schema column is `name`, not `assistant_name` — and
the SKILL.md never linked the two.

When the agent had to compose a SELECT against `agent_groups` from the
SKILL.md vocabulary alone, it extrapolated `--assistant-name` into a
column name and produced:

  SELECT id, folder, assistant_name FROM agent_groups;
  -> Error: in prepare, no such column: assistant_name

Replace the prose pointer with canonical SQL queries that match the
real schema. The `name AS assistant_name` alias preserves the familiar
term in the agent's output.

Verified locally as a drop-in: `/manage-channels` runs clean from end
to end with this version, no further inference needed.

Closes #2289
2026-05-06 00:29:54 +02:00
github-actions[bot]
2db5173f07 chore: bump version to 2.0.33 2026-05-05 21:56:17 +00:00
gavrielc
9b4860dd48 Merge pull request #2288 from glifocat/fix/host-sweep-tz-utc-parsing
fix(host-sweep): parse SQLite timestamps as UTC, not local time
2026-05-06 00:55:59 +03:00
Ethan Munoz
ec23bd7a7e fix(host-sweep): parse SQLite timestamps as UTC, not local time
SQLite TIMESTAMP columns store UTC without a zone marker. `Date.parse`
treats timezoneless ISO strings as local time, so on any non-UTC host
every claim and processAfter looks (TZ offset) hours stale. That makes
fresh claims trip the kill-claim path on the first sweep tick — every
container gets killed within seconds of spawn.

Two affected sites in host-sweep.ts:

  - decideStuckAction reads claim.status_changed and computes claimAge.
    On a TZ=Europe/Madrid host (UTC+2), a claim made 5s ago looks
    7205s old and exceeds CLAIM_STUCK_MS (60s).

  - The orphan retry loop reads msg.processAfter and skips messages
    rescheduled into the future. On the same host, future timestamps
    look (TZ offset) hours in the past, so the skip is missed and
    tries gets bumped on every tick.

Fix: introduce parseSqliteUtc(s) which appends "Z" only when no zone
marker is present, then call it from both sites. Behavior under
TZ=UTC is unchanged.

Verified on a production v2 install on TZ=Europe/Madrid: with the
patch applied, an idle container survived 30+ minutes without being
killed (previously: killed within 60s of spawn).

Tests: 5 new cases covering the bare/Z/+offset/invalid input matrix
and a TZ-independence check. All 19 host-sweep tests pass and tsc
clears against main.
2026-05-05 23:49:18 +02:00
gavrielc
61caac0a04 Merge pull request #2287 from glifocat/fix/migrate-v2-health-endpoint
fix(migrate-v2): probe correct OneCLI health endpoint
2026-05-06 00:48:33 +03:00
Ethan Munoz
4d5af78d35 fix(migrate-v2): probe correct OneCLI health endpoint (/api/health)
migrate-v2.sh probes ${ONECLI_URL_CHECK}/health (with ONECLI_URL_CHECK
defaulting to http://127.0.0.1:10254, the OneCLI web port). That path
returns 404, so the detection branch never matches an already-running
OneCLI instance and the script falls through to the install path.

The web app's health endpoint is /api/health
(apps/web/src/app/api/health/route.ts) and has been since the OneCLI
repo was made public. /health was never exposed by the web on :10254
nor by the gateway on :10255 (the gateway uses /healthz).

Verified against a running OneCLI v1.21.0:
  GET :10254/api/health  -> 200 {"status":"ok","version":"1.21.0",...}
  GET :10254/health      -> 404 (Next.js fallback HTML)
  GET :10255/healthz     -> 200
  GET :10255/health      -> 400 (gateway parses non-/healthz as CONNECT)

Closes #2285
2026-05-05 23:34:14 +02:00
gavrielc
863c224d9e Merge pull request #2249 from alipgoldberg/setup-telegram-no-telegram-fallback
feat(setup): clearer "Open Telegram" card with mobile fallback
2026-05-05 23:40:59 +03:00
gavrielc
87f75eed79 Merge branch 'main' into setup-telegram-no-telegram-fallback 2026-05-05 23:40:48 +03:00
gavrielc
fc09b900ef Merge pull request #2274 from alipgoldberg/setup-channel-back-nav-pr5-signal
setup: add ← Back option to Signal channel flow
2026-05-05 23:37:26 +03:00
gavrielc
1a2d004bad Merge pull request #2273 from alipgoldberg/setup-channel-back-nav-pr4-teams
setup: add ← Back option to Teams channel flow
2026-05-05 23:37:12 +03:00
gavrielc
e25eae7e57 Merge pull request #2272 from alipgoldberg/setup-channel-back-nav-pr3-slack
setup: add ← Back option to Slack channel flow
2026-05-05 23:36:30 +03:00
gavrielc
4a10a455f9 Merge pull request #2271 from alipgoldberg/setup-channel-back-nav-pr2-telegram
setup: add ← Back option to Telegram channel flow
2026-05-05 23:36:14 +03:00
gavrielc
eefbf4f61d Merge pull request #2269 from alipgoldberg/setup-channel-back-nav-pr1
setup: add ← Back option to Discord, WhatsApp, iMessage channel flows
2026-05-05 23:34:33 +03:00
gavrielc
a9c8c841f6 Merge pull request #2275 from alipgoldberg/whatsapp-linked-devices-copy
setup: update WhatsApp link instructions to "You / Settings"
2026-05-05 23:33:32 +03:00
gavrielc
3d42ba6e3d Merge pull request #2281 from alipgoldberg/setup-signal-cli-auto-install
setup: auto-install signal-cli when missing
2026-05-05 23:32:49 +03:00
gavrielc
5277e12a48 Merge pull request #2284 from glifocat/fix/baileys-v7-pin-install-scripts
fix(setup): pin Baileys to 7.0.0-rc.9 in install-whatsapp scripts
2026-05-05 21:49:52 +03:00
glifocat
a8e0a7f011 fix(setup): pin Baileys to 7.0.0-rc.9 in install-whatsapp scripts
PR #2259 (Baileys v6→v7) was merged into the channels branch instead of
main. PR #2260 was merged into main 28s later assuming v7 was already
in place. The v6 pin survived in three sites while the WhatsApp adapter
copied from origin/channels at install time was already on the v7 LID
API, breaking every fresh migrate-v2.sh run at 2c-install-whatsapp with
TS errors on remoteJidAlt/participantAlt/lid-mapping.update.

Bumps the pin to 7.0.0-rc.9 (the version v1 has been running on for
months) in:

- setup/install-whatsapp.sh
- setup/add-whatsapp.sh
- .claude/skills/add-whatsapp/SKILL.md (install instruction)

package.json + pnpm-lock.yaml are not touched here — install-whatsapp.sh
mutates them at runtime via pnpm install with the corrected pin.

Closes #2283
2026-05-05 20:47:36 +02:00
exe.dev user
291a1fc8a4 update Signal intro copy to reflect auto-install
Today's copy says "Check that signal-cli is installed (we'll guide
you if not)" but the auto-install PR (#2281) makes that misleading —
we don't guide, we just install. Update the intro list to match what
will actually happen, and add a "no input needed for any of it" lead
so users know to expect a hands-off run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 17:09:39 +00:00
exe.dev user
92a2347dc5 setup: auto-install signal-cli when missing
When a user picks Signal in setup and signal-cli isn't on PATH, today
NanoClaw bails with a GitHub releases link and tells them to re-run.
That's a hard wall for non-technical users — GitHub releases pages
are intimidating, and the Linux native build / Java decision isn't
obvious.

Replace the bail-out with a real install: a new install-signal-cli.sh
script that does `brew install signal-cli` on macOS or downloads the
native Linux release into ~/.local/bin (no Java, no sudo). Wired into
ensureSignalCli with a spinner; probe again after, fall back to the
original manual-install copy if anything fails.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 17:04:53 +00:00
github-actions[bot]
73d45f8097 docs: update token count to 141k tokens · 71% of context window 2026-05-05 15:07:07 +00:00
github-actions[bot]
395139ce63 chore: bump version to 2.0.32 2026-05-05 15:04:19 +00:00
glifocat
644ad2f017 Merge pull request #2265 from glifocat/fix/send-card-bridge
fix(channels): support display cards (send_card) in Chat SDK bridge
2026-05-05 17:03:56 +02:00
glifocat
824f311e31 Merge pull request #2266 from glifocat/fix/bump-chat-adapter-cohort-4-27
fix(skills): bump @chat-adapter/* cohort to 4.27.0 (Discord card duplication)
2026-05-05 17:03:25 +02:00
glifocat
c93e611228 Merge branch 'main' into fix/bump-chat-adapter-cohort-4-27 2026-05-05 15:35:19 +02:00
glifocat
4fc3273889 Merge branch 'main' into fix/send-card-bridge 2026-05-05 15:32:24 +02:00
gavrielc
fa6f2da83e Merge pull request #2260 from qwibitai/fix/drop-whatsapp-lid-migration
fix(migrate): drop WhatsApp LID dual-row migration step
2026-05-05 16:16:20 +03:00
gavrielc
34982eaf31 Merge branch 'main' into fix/drop-whatsapp-lid-migration 2026-05-05 16:16:02 +03:00
github-actions[bot]
9df6a91b32 docs: update token count to 141k tokens · 70% of context window 2026-05-05 13:04:29 +00:00
gavrielc
81b2364336 Merge pull request #2182 from mnolet/fix/test-infra-openInboundDb
fix(test-infra): openInboundDb honors in-memory test DB
2026-05-05 16:04:13 +03:00
gavrielc
144c65e32d Merge branch 'main' into fix/test-infra-openInboundDb 2026-05-05 16:03:16 +03:00
gavrielc
6d6584d120 fix(test-infra): openInboundDb honors in-memory test DB
openInboundDb() always opened /workspace/inbound.db which doesn't exist
in CI. In test mode, return a thin wrapper over the in-memory singleton
that delegates prepare/exec but no-ops close(), so callers' try/finally
cleanup doesn't destroy the shared DB mid-test.

One flag (_testMode), no monkey-patching, no saved-close bookkeeping.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-05 16:02:10 +03:00
github-actions[bot]
9ac1e6fd7b chore: bump version to 2.0.31 2026-05-05 12:57:49 +00:00
gavrielc
24d719fb88 Merge pull request #2209 from cfis/fix/host-sweep-test-uses-in-memory-db
fix(host-sweep): orphan-claim delete missed in tests (regression from #2183)
2026-05-05 15:57:31 +03:00
gavrielc
a870e7ebf2 fix: keep resetStuckProcessingRows private, restore test wrapper
The test wrapper forwards the in-memory outDb as the writable handle,
avoiding the filesystem reopen that fails in CI. The function stays
private — the optional writableOutDb param is an internal detail, not
a public API.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-05 15:56:08 +03:00
exe.dev user
7fdd7eaa1c setup: update WhatsApp link instructions to "You / Settings"
WhatsApp's mobile UI calls the menu "You" on iOS and "Settings" on
Android (depending on platform/version). Both QR-scan and pairing-code
captions only mentioned "Settings", so iOS users had to figure out the
iOS-specific path on their own.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 10:14:12 +00:00
exe.dev user
decf18049f setup: add ← Back option to Signal channel flow
Stacked on #2269 (back-nav scaffolding) plus the Telegram, Slack, and
Teams PRs. They share the same scaffolding file from #2269 — they
don't compile without it, so they have to stack.

Signal had no user-facing prompt before the install kicked off, so
there was nothing to attach a Back option to. This adds a brief "Set
up Signal" info card (what's about to happen, no new phone number
needed) followed by a Continue/Back brightSelect. The card serves
double duty — context for the install plus the Back gate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 09:51:21 +00:00
exe.dev user
c44c7a6669 setup: add ← Back option to Teams channel flow
Stacked on #2269 (back-nav scaffolding) plus the Telegram and Slack
PRs. They share the same scaffolding file from #2269 — they don't
compile without it, so they have to stack.

Both Teams paths already had a brightSelect at the right place, so we
just extend each with a Back option — no new prompts:

- Existing-credentials path: Yes/No confirm becomes Yes/No/Back
- Fresh-setup path: the very first stepGate ("How did that go?") gets
  a 4th option. Subsequent stepGates keep the original 3 options so
  we never lose mid-flow state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 09:47:17 +00:00
exe.dev user
6a54b69912 setup: add ← Back option to Slack channel flow
Stacked on the back-nav scaffolding from #2269 and the Telegram PR.

Slack's first prompt was already a single-purpose "Press Enter to open
Slack app settings" confirm. Replacing it with a 2-option brightSelect
(Open / ← Back) folds the Back gate into the existing screen — net
same number of prompts as before, just with a way out. The redundant
confirmThenOpen Press-Enter step is dropped; openUrl is called inline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 09:32:34 +00:00