Reviewers on #2933 asked for tests on the button-style plumbing. Two layers:
- src/channels/ask-question.test.ts (new): normalizeOption/normalizeOptions
style whitelist — primary/danger/default pass through; unknown strings,
case variants, and non-string values drop to undefined; string-shorthand
options carry no style; style coexists with the label/selectedLabel/value
defaulting. This is the load-bearing gate: an invalid style reaching
Slack Block Kit fails the whole card with invalid_blocks, which in the
approval flow is an effective auto-deny.
- src/channels/chat-sdk-bridge.test.ts: ask_question delivery passes each
normalized option style into Button() and omits it when unset; an invalid
style in the raw payload is stripped before the card is built.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
These config exports in src/config.ts have zero readers and advertise
controls that v2 removed:
- CONTAINER_TIMEOUT / CONTAINER_MAX_OUTPUT_SIZE / IDLE_TIMEOUT /
MAX_CONCURRENT_CONTAINERS / MAX_MESSAGES_PER_PROMPT
- the trigger block (escapeRegex, buildTriggerPattern, DEFAULT_TRIGGER,
getTriggerPattern, TRIGGER_PATTERN)
Also remove the `auth` script from package.json — it pointed at the
deleted src/whatsapp-auth.ts, so `pnpm run auth` only errored — and fix
the stale MAX_MESSAGES_PER_PROMPT reference in the container's
messages-in.ts comment (the value comes from container.json's
maxMessagesPerPrompt).
Grep confirms no importers in src/, container/, scripts/, setup/, or
tests. Precedent: 0283391.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Egress-lockdown and CPU/memory limits were read from process.env only, but
the shipped launchd/systemd service sets just PATH+HOME and the host never
loads .env into process.env — so these knobs could not be turned on the
supported way. Route them through the same readEnvFile path as the other
config keys, keeping process.env precedence so dev-mode/nohup installs are
unaffected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The dispatcher trimmed exactly one trailing dash-segment to recover the
target id, so any generated id containing dashes (UUIDs, sess-*, appr-*)
never matched a command and failed unknown-command; only --id worked.
Resolve by longest registered prefix instead: split the dash-joined
command, take the longest prefix that lookup() resolves as the command,
and treat the re-joined remainder as args.id. Server-side only, no wire
or client change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add '/start' back to the host FILTERED set (a Telegram fix from 866b791
was silently undone when host gating was added) so it is dropped instead
of reaching the agent as a normal message. Replace the inline isAdmin
query and its hasTable('user_roles') fail-open guard with a call to
hasAdminPrivilege; user_roles always exists (core migration 001-initial),
so the guard only ever masked a missing check. Add a focused command-gate
test covering both.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Approval cards render every button with the same neutral style, so Approve
and Reject read identically at a glance. The chat SDK's Button already
accepts style ('primary' | 'danger' | 'default') and @chat-adapter/slack
maps primary→green and danger→red Block Kit styles (Telegram ignores it),
but the ask_question pipeline dropped the field.
- ask-question.ts: add OptionStyle and an optional style field to
OptionInput/NormalizedOption; normalizeOption whitelists the value.
Bare-string options stay unstyled.
- chat-sdk-bridge.ts: forward opt.style into Button() on ask_question
cards. Persisted options_json tolerates the extra key
(getAskQuestionRender only reads label/selectedLabel/value).
- Producers: module approvals (Approve/Reject), sender approvals
(Allow/Deny), channel approvals (Connect/Reject), and OneCLI credential
cards (Approve/Reject) annotate primary/danger. Multi-choice picker
options stay unstyled — a list of equals.
Purely additive: options without style render exactly as before.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The container self-registration barrel imported mock.js, so every container
registered a 'mock' provider returning canned text. A typo'd --provider mock
would silently 'work' instead of failing loudly. Drop the import (mock.ts stays
for direct test use) and tidy the now-stale host comments and docs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The hosted OneCLI gateway (api.onecli.sh) sends a structured summary field
on ApprovalRequest — { action, details: [{label, value}] } — that the SDK's
TypeScript type doesn't declare yet. When present, render it as the approval
card body (*Action:* plus labeled fields, fencing multi-line values) instead
of the raw METHOD host/path + bodyPreview, so approvers see what the agent
is doing (e.g. To / Subject / Body of an email send) rather than an HTTP
trace.
Rendering is defensive: non-string values are coerced via JSON.stringify (a
render throw would turn into a deny via handleRequest's catch), each value is
capped at 900 chars, and a 2600-char running budget keeps the card under
Slack's 3000-char section block limit — overflow is reported with an
explicit "…N field(s) omitted for length" line instead of a failed
delivery. Without a summary, the old bodyPreview fallback remains, now
capped and with the method/host/path as a footnote.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Address review feedback on PR #2795:
- SKILL.md Step 1 now creates tools/ before cp -R (cp does not create
intermediate parents; tools/ is not a standard NanoClaw dir, so the copy
failed on a fresh checkout).
- package.json engines bumped to >=22.5 — server.js statically imports
activity.js -> node:sqlite (DatabaseSync), which lands in Node 22.5, so
the server crashed at module load on Node 20/21 LTS.
- Doc references (SKILL.md, README.md) updated to Node >=22.5 for consistency.
Verified 87/87 tests pass on Node 22.14.
Agent templates: folder-only templates under templates/ (context/instructions.md +
optional context extras, .mcp.json, skills/). Stamping via ncl groups create
--template writes the provider-neutral instructions.prepend.md (inlined at the top
of CLAUDE.md/AGENTS.md every spawn), copies context extras preserving their
template-relative layout, writes MCP servers to container config, and installs the
per-group skills overlay. Includes docs (docs/templates.md, templates/README.md).
Setup-wizard wiring ships separately on top of this.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>