Commit graph

23 commits

Author SHA1 Message Date
Nick Tabeling
e396e6d163 feat(DIS-106): RunResult discriminated union replaces Promise<string>
Some checks failed
CI / build-and-test (ubuntu-latest) (pull_request) Has been cancelled
CI / build-and-test (windows-latest) (pull_request) Has been cancelled
CI / lint (pull_request) Has been cancelled
Add RunResult type in src/agent/types.ts (success|timeout|cli-error|parse-error).
Runner resolves instead of rejects for all error cases.
Router handles all four status variants explicitly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 10:18:19 +02:00
Nick Tabeling
62ea507974 fix(DIS-105): prevent fence closure from pushing chunks beyond 2000-char hard limit
Some checks failed
CI / build-and-test (ubuntu-latest) (pull_request) Has been cancelled
CI / build-and-test (windows-latest) (pull_request) Has been cancelled
CI / lint (pull_request) Has been cancelled
The flush() helper now enforces the Discord hard limit by hard-splitting
any chunk that exceeds 2000 chars (e.g. when "\n```" was appended after
the soft-limit check). Also reserves 4 chars of overhead in the
soft-limit check when inside a fenced code block.

Adds a regression test that verifies no chunk exceeds 2000 chars even
when the fence closure is appended at the limit boundary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 10:13:58 +02:00
Nick Tabeling
a85217f753 merge: integrate origin/main into phase-1/split-for-discord
Resolve conflict in src/router.ts: keep both splitForDiscord (DIS-105)
and sanitizeForDiscord (DIS-102). Apply splitForDiscord to the already
sanitized response text.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 10:11:13 +02:00
Nick Tabeling
714e0c01e5 fix(DIS-102): sanitize response before DB storage to break path feedback loop
Some checks failed
CI / build-and-test (ubuntu-latest) (pull_request) Has been cancelled
CI / build-and-test (windows-latest) (pull_request) Has been cancelled
CI / lint (pull_request) Has been cancelled
The root cause was twofold:
1. Raw (unsanitized) agent responses were stored in the conversation
   history DB. On subsequent calls, these paths were fed back to the
   agent as context, causing it to reproduce full paths regardless of
   Discord-side sanitization.
2. repoRoot (workspace_path) was not passed to sanitizeForDiscord,
   so workspace-specific paths were only partially masked via the
   home directory fallback.

Fix: sanitize the full response once before both sending to Discord
and storing in the DB. Pass workspace_path as repoRoot for precise
path replacement. Add Windows path tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 10:02:17 +02:00
Nick Tabeling
57cd3d4a43 feat(DIS-102): sanitizeForDiscord strips paths and tokens before Discord send
Add sanitizeForDiscord() in src/runtime/sanitize.ts.
Router applies sanitizer to all channel.send() calls.
Prevents path/token leaks into Discord channels.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 09:50:37 +02:00
dev
67433d63fa Merge pull request 'refactor(DIS-108): extract DB schema to src/db/schema.sql (SSOT)' (#50) from phase-1/db-schema-ssot into main
Some checks are pending
CI / build-and-test (ubuntu-latest) (push) Waiting to run
CI / build-and-test (windows-latest) (push) Waiting to run
CI / lint (push) Waiting to run
Reviewed-on: #50
2026-04-09 16:01:48 +00:00
Nick Tabeling
3dd75ddc30 refactor(DIS-108): extract DB schema to src/db/schema.sql (SSOT)
Some checks failed
CI / build-and-test (ubuntu-latest) (pull_request) Has been cancelled
CI / build-and-test (windows-latest) (pull_request) Has been cancelled
CI / lint (pull_request) Has been cancelled
Move all CREATE TABLE/INDEX DDL from database.ts to schema.sql.
database.ts loads and executes schema.sql via db.exec().
Add integration test verifying idempotent schema init.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 17:50:14 +02:00
Nick Tabeling
5718284518 feat(DIS-107): Zod schemas for disclaw.yaml, agent.yaml, CLI output
Some checks failed
CI / build-and-test (ubuntu-latest) (pull_request) Has been cancelled
CI / build-and-test (windows-latest) (pull_request) Has been cancelled
CI / lint (pull_request) Has been cancelled
Add DisclawConfigSchema and AgentYamlSchema with Zod validation.
loader.ts and identity.ts validate external data at parse time.
Clear error messages with field name and expected type.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 17:49:44 +02:00
Nick Tabeling
00132c6d22 feat(DIS-105): codeblock-aware message splitting for Discord 2000-char limit
Some checks failed
CI / build-and-test (ubuntu-latest) (pull_request) Has been cancelled
CI / build-and-test (windows-latest) (pull_request) Has been cancelled
CI / lint (pull_request) Has been cancelled
Add splitForDiscord() in src/discord/split.ts.
Fences are properly closed and reopened across chunk boundaries.
Router uses splitForDiscord instead of internal splitMessage().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 17:48:58 +02:00
dev
6039e992bd Merge pull request 'feat(DIS-101): introduce pino structured logger, remove console.*' (#45) from phase-1/pino-logger into main
Some checks are pending
CI / build-and-test (ubuntu-latest) (push) Waiting to run
CI / build-and-test (windows-latest) (push) Waiting to run
CI / lint (push) Waiting to run
Reviewed-on: #45
2026-04-09 15:40:46 +00:00
Nick Tabeling
1cafaabe2a feat(DIS-101): introduce pino structured logger, remove console.*
Some checks failed
CI / build-and-test (ubuntu-latest) (pull_request) Has been cancelled
CI / build-and-test (windows-latest) (pull_request) Has been cancelled
CI / lint (pull_request) Has been cancelled
Replace all console.* calls in src/ with pino child-loggers.
Add rootLogger + childLogger(component) in src/runtime/logger.ts.
pino-pretty used as dev transport only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 17:09:37 +02:00
Nick Tabeling
04450bb6d9 feat(DIS-104): global semaphore for max_concurrent_agents cap
Some checks failed
CI / build-and-test (ubuntu-latest) (pull_request) Has been cancelled
CI / build-and-test (windows-latest) (pull_request) Has been cancelled
CI / lint (pull_request) Has been cancelled
Add Semaphore class in src/runtime/concurrency.ts.
Runner acquires/releases slot around claude spawn.
Default cap: 4, configurable via disclaw.yaml max_concurrent_agents.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 17:08:58 +02:00
Nick Tabeling
aad45b9637 fix: remove --bare flag to restore OAuth login for Claude Pro/Max
Some checks failed
CI / build-and-test (ubuntu-latest) (pull_request) Has been cancelled
CI / build-and-test (windows-latest) (pull_request) Has been cancelled
CI / lint (pull_request) Has been cancelled
--bare disables OAuth/keychain auth and requires ANTHROPIC_API_KEY,
breaking Claude Pro/Max subscriptions. CLAUDE.md isolation is now
achieved structurally: workspaces live under ~/.disclaw/workspaces/
(outside the repo) so walk-up never reaches DisClaw's CLAUDE.md.

Update integration test to assert --bare is absent and verify correct
args (-p, --output-format json). Fix sanitize-env tests to reflect
CI=true removal (was breaking OAuth headless-mode detection).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 13:48:45 +02:00
Nick Tabeling
dc4a1ef241 feat(runtime): add startup lockfile to prevent double-start
Implements acquireLock() in src/runtime/lockfile.ts that writes a PID-stamped
lock file under workspaces_root, detects live vs stale locks via process.kill(pid, 0),
and returns a release function. src/index.ts acquires the lock before DB init and
releases it in the graceful-shutdown handler. Four unit tests cover acquire, duplicate-
acquire rejection, stale-lock recovery, and release.
2026-04-09 11:49:17 +02:00
dev
0c512a0cbc Merge pull request 'DIS-005: Path-Traversal-Check in /new-agent' (#25) from phase-0/path-traversal-check into main
Reviewed-on: #25
2026-04-09 09:44:54 +00:00
dev
90e7cf37ab Merge pull request 'DIS-006: settings.json-Template haerten + CLAUDE.md Injection-Klausel' (#24) from phase-0/harden-settings-and-claudemd into main
Reviewed-on: #24
2026-04-09 09:44:33 +00:00
dev
613758e912 Merge branch 'main' into phase-0/path-traversal-check 2026-04-09 09:43:41 +00:00
Nick Tabeling
a54918c3be feat(runner): add --bare and --append-system-prompt-file flags
Prevents CLAUDE.md walk-up through parent directories by passing --bare
to every Claude CLI invocation, and explicitly injects the workspace
CLAUDE.md via --append-system-prompt-file so agent identity is isolated.
Adds fake-claude fixture and integration test to verify flags are present.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 11:22:44 +02:00
Nick Tabeling
b4cc00e93c fix(new-agent): add path traversal containment check
Extracts validateAgentName() with path.resolve containment guard after
regex validation; returns clean ephemeral Discord reply on violation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 11:19:28 +02:00
Nick Tabeling
028ea0bf28 feat(identity): harden settings.json template, add injection-resistance clause
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 11:19:19 +02:00
Nick Tabeling
d71081d2b1 feat(config): move workspaces_root to home dir, add tilde expansion
- disclaw.yaml: change default workspaces_root to ~/.disclaw/workspaces
- src/config/loader.ts: add expandWorkspacesRoot() with ~ expansion via
  os.homedir(), path.resolve() normalisation, and in-repo warning via
  console.warn (explains CLAUDE.md walk-up leak risk)
- tests/unit/workspace-root-resolve.test.ts: Vitest unit tests (a-d)
- README.md: add Migration bestehender Workspaces section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 10:40:34 +02:00
Nick Tabeling
510969b2d1 fix(runner): add sanitizedEnv, remove process.env passthrough
Introduces src/runtime/env.ts with a pure sanitizedEnv() function that
strips DISCORD_BOT_TOKEN, DISCORD_CLIENT_SECRET, DISCORD_PUBLIC_KEY, and
all keys matching /^(DISCLAW_SECRET_|SECRET_|TOKEN_|API_KEY_?)/i before
passing the environment to spawned Claude Code child processes. Adds
optional env_blocklist field to DisclawConfig. Runner now passes
DISCLAW_AGENT_NAME and respects the blocklist.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 10:39:06 +02:00
Nick Tabeling
bade6d07f6 fix(runner): replace shell:true with cross-spawn, add resolveClaude
- Remove shell:true from runner.ts (command injection vector closed)
- Import cross-spawn instead of child_process.spawn; add windowsHide:true
- Add src/runtime/resolve-claude.ts: CLAUDE_PATH env override, where/which
  PATH fallback, in-memory cache, clear error message on not-found
- Add tests/unit/resolve-claude.test.ts: 5 Vitest tests covering env override,
  PATH fallback, multi-line output trimming, error case, and cache behaviour
- Add cross-spawn + @types/cross-spawn to package.json; add vitest + test script
2026-04-09 10:29:58 +02:00