Commit Graph

1587 Commits

Author SHA1 Message Date
Koshkoshinsk
0a18c1d21a Ensure user is in docker group before sg docker, revert workarounds
The root cause of broken keyboard navigation was sg docker prompting
for the (unset) group password when the user wasn't in the docker
group. Fix by running sudo usermod -aG docker before sg docker.

This makes the stty sane calls and p.confirm workaround unnecessary,
so revert those. Also remove the manual docker group instruction from
nanoclaw.sh since container.ts handles it automatically.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-30 10:31:30 +00:00
Koshkoshinsk
dec1be6adc Add clone step to root warning user-creation instructions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-30 10:31:30 +00:00
Koshkoshinsk
030ee8a46f Update root warning instructions: add root login step, fix ssh user
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-30 10:31:30 +00:00
Koshkoshinsk
c4f654083d Change root warning from y/N prompt to numbered menu options
Clearer UX: option 1 shows user creation instructions,
option 2 explicitly continues as root (not recommended).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-30 10:31:30 +00:00
Koshkoshinsk
7755082a4c Add root user warning gate to Linux setup pre-flight
Users running setup as root hit permission issues with containers,
services, and file ownership. Warn early with an interactive prompt
and provide step-by-step instructions to create a regular user.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-30 10:31:30 +00:00
gabi-simons
8a205808e0 fix(setup): wrap scratch agent cleanup in transaction, remove session data
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-30 08:19:18 +00:00
Gabi Simons
d7c76ac12b Merge branch 'main' into setup-scratch-agent-cleanup 2026-04-30 11:07:01 +03:00
github-actions[bot]
f828e2971c chore: bump version to 2.0.19 2026-04-30 07:40:21 +00:00
github-actions[bot]
43f49b988e docs: update token count to 135k tokens · 68% of context window 2026-04-30 07:40:16 +00:00
gavrielc
012292d063 Merge pull request #2115 from robbyczgw-cla/fix/session-manager-attachment-extensions
fix(session-manager): derive attachment extension from mimeType and att.type
2026-04-30 10:40:01 +03:00
gavrielc
d2151ae848 Merge branch 'main' into fix/session-manager-attachment-extensions 2026-04-30 10:39:50 +03:00
github-actions[bot]
15f286b73d chore: bump version to 2.0.18 2026-04-30 07:34:23 +00:00
gavrielc
6e5e568da1 sanitize agent sent file names to prevent path traversal 2026-04-30 10:33:46 +03:00
gavrielc
2a3be9ec7f extract attachment-naming, harden mimeType guard, add tests
Move the MIME/type-to-extension maps and derivation helpers out of
session-manager.ts into a dedicated attachment-naming module — keeps
session-manager focused on session lifecycle and gives the helpers
a natural home for unit tests alongside the existing attachment-safety
module.

Two small fixes alongside the extraction:

- extForMime now guards `typeof mime !== 'string'` before .split, so a
  buggy bridge passing `mimeType: { ... }` (object) no longer crashes
  the inbound write loop.
- deriveAttachmentName computes Date.now() once per call instead of
  twice, and tightens the explicit-name check to a string-and-truthy
  guard so non-string values fall through to derivation.

Adds attachment-naming.test.ts with 11 cases covering MIME normalization
(case + parameters), Telegram type fallback, the non-string defensive
guard, and the bare-timestamp fallback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 09:41:24 +03:00
Gabi Simons
2b1b138a44 Merge branch 'main' into feat/channel-approval-flow 2026-04-30 09:37:54 +03:00
Gabi Simons
3c7b971f1b Merge branch 'main' into setup-scratch-agent-cleanup 2026-04-30 09:37:39 +03:00
Mike Nolet
8dd004ca75 fix(scheduling): include routing in schedule_task content JSON
The schedule_task MCP tool wrote routing fields (platform_id, channel_type,
thread_id) onto the outbound system message's row columns, but
handleSystemAction (src/delivery.ts) parses content JSON and forwards only
that to handlers. handleScheduleTask (src/modules/scheduling/actions.ts)
reads content.platformId/channelType/threadId — which the writer never
populated — so every kind='task' row landed in messages_in with all-null
routing.

When host-sweep wakes a scheduled task, dispatchResultText's fast path
requires routing on the message and bails when it's null, falling through
to the "Routing recovery" retry prompt. End-user delivery still works
because the agent can pick a destination from its destinations table on
retry — so the bug went undetected, silently costing one extra LLM turn
per scheduled-task wake. Sessions whose destinations table has no channel
row (e.g. agent-only destinations) fail outright with a recovery loop.

Fix: add the routing fields to the content JSON so the writer matches the
contract handleScheduleTask already expects. cancel/pause/resume/update_task
operate by id alone and don't need routing.
2026-04-30 08:13:59 +02:00
github-actions[bot]
34f3612877 docs: update token count to 135k tokens · 67% of context window 2026-04-29 15:30:23 +00:00
github-actions[bot]
1452ed262b chore: bump version to 2.0.17 2026-04-29 15:30:20 +00:00
gavrielc
597e282f88 Merge pull request #2110 from qwibitai/fix/credential-failure-ux
fix(credentials): require OneCLI gateway for container spawn
2026-04-29 18:30:05 +03:00
gavrielc
33a03f25a9 Merge remote-tracking branch 'origin/main' into fix/credential-failure-ux 2026-04-29 18:28:57 +03:00
gavrielc
e31a6c7e34 revert(credentials): drop auth-required login-message handling
Removing the "Not logged in · Please run /login" detection and
substitution from this PR — narrowing scope to just the OneCLI
gateway transient-retry change. The login-message handling will be
addressed separately.

Reverts:
- AgentProvider.isAuthRequired / authRequiredMessage
- ClaudeProvider auth-required regex, classifier, and remediation text
- poll-loop writeAuthRequiredMessage helper + call sites
- claude.test.ts (auth-only test file)

OneCLI/wakeContainer changes (the remaining content of the PR) are
unaffected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:26:04 +03:00
github-actions[bot]
ee165d09c2 docs: update token count to 134k tokens · 67% of context window 2026-04-29 15:13:42 +00:00
github-actions[bot]
70cb35f58b chore: bump version to 2.0.16 2026-04-29 15:13:37 +00:00
gavrielc
d1a2505d20 Merge pull request #2116 from robbyczgw-cla/fix/compact-window-operator-override
fix(claude-provider): respect operator-set CLAUDE_CODE_AUTO_COMPACT_WINDOW (closes #1820)
2026-04-29 18:13:23 +03:00
robbyczgw-cla
9889848932 fix(claude-provider): respect operator-set CLAUDE_CODE_AUTO_COMPACT_WINDOW
Closes #1820.

The container agent-runner sets CLAUDE_CODE_AUTO_COMPACT_WINDOW
unconditionally on the container process env, with no way to override
it per-deployment without editing source. Read process.env first and
fall back to the existing 165000 literal when unset.

Default behavior is unchanged for installs that do not set the env
var. Operators running 1M-context models or emergency-tuning a live
deployment can now raise or lower the threshold from the host env.
2026-04-29 15:07:26 +00:00
gavrielc
594d1b4055 style(cli): apply prettier formatting
Pre-commit hook ran prettier on the prior commit but left the reformats
unstaged. Folding them in here so the branch is clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:03:47 +03:00
gavrielc
beb5e049ed fix(credentials): move auth-required remediation message into provider
Adds a paired `authRequiredMessage()` method to AgentProvider so
per-provider auth-failure remediation can differ. Claude returns the
Anthropic/`claude` instruction; future providers (Codex, OpenCode, …)
can return their own remediation text. The poll-loop calls
`provider.authRequiredMessage?.()` and falls back to a generic message
if a provider implements `isAuthRequired` without supplying its own
remediation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:03:25 +03:00
gavrielc
3a3d2ee644 feat(cli): scaffold nc CLI with list-groups command
Adds a transport-agnostic CLI control plane shared between three eventual
callers (host shell, Claude in project, container agent) — though only the
host-side socket transport is wired in this commit. Container DB transport
and approval flow land alongside the first risky command.

- src/cli/frame.ts:        wire format (RequestFrame, ResponseFrame, CallerContext)
- src/cli/registry.ts:     command registry with RiskClass
- src/cli/dispatch.ts:     transport-agnostic dispatcher
- src/cli/transport.ts:    Transport interface
- src/cli/socket-client.ts: SocketTransport against data/nc.sock
- src/cli/socket-server.ts: host-side listener (chmod 0600, line-delimited JSON)
- src/cli/format.ts:       human table / --json output modes
- src/cli/client.ts:       `nc` argv -> frame -> transport -> stdout
- src/cli/commands/list-groups.ts: first command (riskClass: safe)
- bin/nc:                  bash launcher (resolves project root via symlink)
- src/index.ts:            start/stop server + import command barrel

`data/nc.sock` is intentionally separate from `data/cli.sock` (which the
existing chat-style channel adapter still owns).

Verified end-to-end: `nc list-groups`, `nc list groups`, `--json`,
unknown-command error, host-down ENOENT message with start instructions.
typecheck clean; eslint reports only the same `no-catch-all` warnings the
rest of the codebase has.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:03:16 +03:00
robbyczgw-cla
b9d302524e fix(session-manager): derive attachment extension from mimeType and att.type
When a channel bridge passes an attachment without an explicit `name`,
extractAttachmentFiles fell back to `attachment-<ts>` with no extension.
Agents could not tell whether the file was a JPEG, PDF, or audio clip,
and tools keyed on extension (image viewers, exiftool, etc.) misbehaved.

Two cases are now covered:

1. Channels that set `mimeType` but no `name` (Discord/Slack documents,
   Telegram document uploads). A small MIME-to-extension table covers
   the common content types — image/*, audio/*, video/*, pdf, zip,
   txt, json. Unknown MIMEs fall back to the unsuffixed name.

2. Channels that set `att.type` but no `mimeType` (Telegram photos,
   stickers, voice, animations). The chat-sdk bridge sets a coarse
   media-class (`photo` / `sticker` / `voice` / `video` /
   `animation`) which is reliable enough to derive a canonical
   extension. Telegram GIFs are MP4 under the hood.

The existing isSafeAttachmentName security guard is preserved — the
derived name still passes through it before disk I/O. The new lookup
tables emit static values from internal maps and cannot construct a
path-traversal payload; attacker-controlled att.name continues to flow
through the same validator.
2026-04-29 15:01:09 +00:00
robbyczgw-cla
ef8e3aa1b8 fix(poll-loop): apply pre-task scripts to follow-up injections too
Tasks arriving during an active query were pushed into the stream as
follow-ups without running their `script` gate — so a wakeAgent=false
pre-script that was supposed to suppress the tick silently leaked
through and woke the agent every time. Evidence: monitoring cron
firing every 10 min with [task-script] log lines never showing.

Run applyPreTaskScripts on the follow-up batch too: wakeAgent=false
tasks get marked completed and dropped; wakeAgent=true tasks have
scriptOutput enriched exactly like the initial-batch path. Added a
pollInFlight guard to serialize async runs and avoid overlapping
script executions when the interval fires while one is still going.

Wrapped in a MODULE-HOOK:scheduling-pre-task-followup marker block
to match the existing initial-batch hook convention.
2026-04-29 14:55:47 +00:00
gavrielc
3c620bc8d0 Merge branch 'fix/credential-failure-ux' of https://github.com/qwibitai/nanoclaw into fix/credential-failure-ux 2026-04-29 17:52:17 +03:00
gavrielc
d5b48e4742 fix(credentials): address review feedback
- wakeContainer now never throws — returns Promise<boolean>, catches
  internally. Closes the regression risk for the 5 awaited callers in
  agent-to-agent, interactive, and approvals/response-handler that the
  previous version left unwrapped. Router uses the boolean to stop the
  typing indicator on transient failure; host-sweep just awaits.
- Tighten AUTH_REQUIRED_RE: anchor to start-of-string with the specific
  `·` (U+00B7) separator the CLI uses, so an agent that quotes the
  banner mid-sentence in a normal reply doesn't trip the classifier.
- Log a one-line note from writeAuthRequiredMessage so substitutions
  are visible when debugging "user got the credentials message but I
  don't see why."
- Add unit tests for ClaudeProvider.isAuthRequired covering both banner
  variants, trailing content, mid-sentence quoting, leading-prose
  quoting, alternate separators, and unrelated text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 17:51:32 +03:00
gabi-simons
8542c484f6 fix(setup): isolate scratch agent with hardcoded _ping-test folder
- Scratch agent uses fixed folder `_ping-test` so it can never collide
  with a real agent on re-runs
- Added --folder flag to init-cli-agent.ts and cli-agent step wrapper
- Delete always targets `_ping-test` exactly — no re-derivation needed
- Removed normalizeName coupling and FOLDER status field (no longer needed)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 14:45:42 +00:00
gavrielc
1dd8fabde9 Merge branch 'main' into fix/credential-failure-ux 2026-04-29 17:42:25 +03:00
gabi-simons
8c5d67cc78 fix(setup): dynamic FK cleanup, remove normalizeName coupling
- delete-cli-agent.ts discovers tables with agent_group_id dynamically
  instead of hardcoding a list
- cli-agent step emits FOLDER in its status block so setup/auto.ts
  reads it from the step result instead of re-deriving via normalizeName

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 14:27:03 +00:00
gabi-simons
d86051805b feat(setup): delete scratch agent after ping-pong, simplify flow
The "Terminal Agent" created for the connection test is now silently
deleted after a successful ping. If the user chooses to chat, a new
agent is auto-created as "{name}'s Terminal" — no name prompt needed.
Condensed the three-line ping section into a single "Connection verified."
status line.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 14:10:53 +00:00
gavrielc
5f34e26240 fix(credentials): translate auth errors and require OneCLI for spawn
Two related fixes for the case where credentials aren't usable:

1. Replace Claude Code's "Not logged in / Invalid API key · Please run
   /login" output with a host-aware message. The user can't run /login
   from chat, so the raw text is unhelpful. Provider gains an optional
   isAuthRequired() classifier; the poll-loop substitutes the message
   on both result-text and error paths.

2. Treat OneCLI gateway failure as a transient hard error instead of
   spawning a credential-less container. The catch in container-runner
   now propagates; router and host-sweep wrap wakeContainer to log and
   leave the inbound row pending so the next 60s sweep tick retries.
   Router also stops the typing indicator on failure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 17:02:15 +03:00
Gabi Simons
5dd15c0014 Merge branch 'main' into feat/channel-approval-flow 2026-04-29 16:34:31 +03:00
Gabi Simons
db19837740 feat(permissions): richer channel-approval flow with agent selection and free-text naming
Replace the hardcoded Approve/Ignore card with a multi-step flow:
- Single agent: "Connect to [name]" / "Connect new agent" / "Reject"
- Multiple agents: "Choose existing agent" (follow-up list) / "Connect new agent" / "Reject"
- "Connect new agent" prompts for a free-text name via DM, creates immediately on reply
- Add setMessageInterceptor router hook for capturing free-text replies
- Add resolveChannelName optional method to ChannelAdapter interface

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 13:34:10 +00:00
gavrielc
9e45845000 Merge pull request #2104 from alipgoldberg/setup-assistant-green
feat(setup): paint "assistant" green in the agent-name prompt
2026-04-29 15:36:26 +03:00
gavrielc
9a919f4148 Merge branch 'main' into setup-assistant-green 2026-04-29 15:36:14 +03:00
exe.dev user
4608836953 feat(setup): paint "assistant" green in the agent-name prompt
Wraps the word "assistant" in `accentGreen` (#3fba50, added in #2103)
across the six channel adapters that ask "What should your assistant
be called?" — Discord, iMessage, Signal, Slack, Telegram, WhatsApp.
Mirrors the green emphasis on "you" in the display-name prompt: the
green word names the subject of the question (assistant vs operator)
so the operator parses it at a glance.
2026-04-29 12:32:25 +00:00
gavrielc
1bf903a64d Merge pull request #2103 from alipgoldberg/setup-pronoun-green
feat(setup): paint "you" green in the display-name prompt
2026-04-29 15:25:12 +03:00
gavrielc
0044bba0e5 Merge branch 'main' into setup-pronoun-green 2026-04-29 15:25:02 +03:00
exe.dev user
26594d2c54 feat(setup): paint "you" green in the display-name prompt
Adds an `accentGreen` helper (#3fba50) with the same TTY/NO_COLOR/
truecolor gating as the rest of the palette, then wraps the word
"you" in the "What should your assistant call you?" prompt so the
operator parses at a glance who the question is about — the user,
not the assistant. The mirror prompt that asks for the assistant's
name ("What should your assistant be called?") is left for a
follow-up.
2026-04-29 12:16:15 +00:00
gavrielc
01131521ff Merge pull request #2102 from alipgoldberg/setup-color-choices
feat(setup): cyan highlight on active and submitted choices
2026-04-29 15:07:56 +03:00
gavrielc
3742165708 Merge branch 'main' into setup-color-choices 2026-04-29 15:07:00 +03:00
exe.dev user
4c791a41b2 feat(setup): cyan highlight on active and submitted choices
Customize `brightSelect`'s render function so the focused option's
label paints in brand cyan during selection and the submitted answer
paints in dim cyan after the user moves on. Inactive options keep
their default rendering — only the cursor and submitted state pick
up the color, matching the body-text emphasis added in #2101.

Also migrate the one remaining `p.select` call site (the "What next?"
prompt after the first chat) to `brightSelect` so every menu in the
setup flow goes through the same render path. The shape of the call
matches what `brightSelect` already supports — message + options
with value/label/hint — so no feature is lost in the swap.

Reuses `brandBody` from #2101 for the cyan, so the prompt highlight
and the body prose share one definition of the brand body color.
2026-04-29 12:01:35 +00:00
gavrielc
6ef147bc89 Merge pull request #2101 from alipgoldberg/setup-color-body
feat(setup): paint card and log bodies in brand cyan
2026-04-29 14:58:27 +03:00