diff --git a/.agent/.DS_Store b/.agent/.DS_Store deleted file mode 100644 index 1f2c43e08..000000000 Binary files a/.agent/.DS_Store and /dev/null differ diff --git a/.detect-secrets.cfg b/.detect-secrets.cfg index 66ed5236e..38912567c 100644 --- a/.detect-secrets.cfg +++ b/.detect-secrets.cfg @@ -7,6 +7,10 @@ [exclude-files] # pnpm lockfiles contain lots of high-entropy package integrity blobs. pattern = (^|/)pnpm-lock\.yaml$ +# Generated output and vendored assets. +pattern = (^|/)(dist|vendor)/ +# Local config file with allowlist patterns. +pattern = (^|/)\.detect-secrets\.cfg$ [exclude-lines] # Fastlane checks for private key marker; not a real key. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..f6fca8c5e --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: ['https://github.com/sponsors/steipete'] diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml new file mode 100644 index 000000000..bcfdefcdd --- /dev/null +++ b/.github/actionlint.yaml @@ -0,0 +1,17 @@ +# actionlint configuration +# https://github.com/rhysd/actionlint/blob/main/docs/config.md + +self-hosted-runner: + labels: + # Blacksmith CI runners + - blacksmith-4vcpu-ubuntu-2404 + - blacksmith-4vcpu-windows-2025 + +# Ignore patterns for known issues +paths: + .github/workflows/**/*.yml: + ignore: + # Ignore shellcheck warnings (we run shellcheck separately) + - 'shellcheck reported issue.+' + # Ignore intentional if: false for disabled jobs + - 'constant expression "false" in condition' diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..829604b4c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,113 @@ +# Dependabot configuration +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 + +registries: + npm-npmjs: + type: npm-registry + url: https://registry.npmjs.org + replaces-base: true + +updates: + # npm dependencies (root) + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + cooldown: + default-days: 7 + groups: + production: + dependency-type: production + update-types: + - minor + - patch + development: + dependency-type: development + update-types: + - minor + - patch + open-pull-requests-limit: 10 + registries: + - npm-npmjs + + # GitHub Actions + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + cooldown: + default-days: 7 + groups: + actions: + patterns: + - "*" + update-types: + - minor + - patch + open-pull-requests-limit: 5 + + # Swift Package Manager - macOS app + - package-ecosystem: swift + directory: /apps/macos + schedule: + interval: weekly + cooldown: + default-days: 7 + groups: + swift-deps: + patterns: + - "*" + update-types: + - minor + - patch + open-pull-requests-limit: 5 + + # Swift Package Manager - shared MoltbotKit + - package-ecosystem: swift + directory: /apps/shared/MoltbotKit + schedule: + interval: weekly + cooldown: + default-days: 7 + groups: + swift-deps: + patterns: + - "*" + update-types: + - minor + - patch + open-pull-requests-limit: 5 + + # Swift Package Manager - Swabble + - package-ecosystem: swift + directory: /Swabble + schedule: + interval: weekly + cooldown: + default-days: 7 + groups: + swift-deps: + patterns: + - "*" + update-types: + - minor + - patch + open-pull-requests-limit: 5 + + # Gradle - Android app + - package-ecosystem: gradle + directory: /apps/android + schedule: + interval: weekly + cooldown: + default-days: 7 + groups: + android-deps: + patterns: + - "*" + update-types: + - minor + - patch + open-pull-requests-limit: 5 diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 000000000..5c19fa418 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,222 @@ +"channel: bluebubbles": + - changed-files: + - any-glob-to-any-file: + - "extensions/bluebubbles/**" + - "docs/channels/bluebubbles.md" +"channel: discord": + - changed-files: + - any-glob-to-any-file: + - "src/discord/**" + - "extensions/discord/**" + - "docs/channels/discord.md" +"channel: googlechat": + - changed-files: + - any-glob-to-any-file: + - "extensions/googlechat/**" + - "docs/channels/googlechat.md" +"channel: imessage": + - changed-files: + - any-glob-to-any-file: + - "src/imessage/**" + - "extensions/imessage/**" + - "docs/channels/imessage.md" +"channel: line": + - changed-files: + - any-glob-to-any-file: + - "extensions/line/**" + - "docs/channels/line.md" +"channel: matrix": + - changed-files: + - any-glob-to-any-file: + - "extensions/matrix/**" + - "docs/channels/matrix.md" +"channel: mattermost": + - changed-files: + - any-glob-to-any-file: + - "extensions/mattermost/**" + - "docs/channels/mattermost.md" +"channel: msteams": + - changed-files: + - any-glob-to-any-file: + - "extensions/msteams/**" + - "docs/channels/msteams.md" +"channel: nextcloud-talk": + - changed-files: + - any-glob-to-any-file: + - "extensions/nextcloud-talk/**" + - "docs/channels/nextcloud-talk.md" +"channel: nostr": + - changed-files: + - any-glob-to-any-file: + - "extensions/nostr/**" + - "docs/channels/nostr.md" +"channel: signal": + - changed-files: + - any-glob-to-any-file: + - "src/signal/**" + - "extensions/signal/**" + - "docs/channels/signal.md" +"channel: slack": + - changed-files: + - any-glob-to-any-file: + - "src/slack/**" + - "extensions/slack/**" + - "docs/channels/slack.md" +"channel: telegram": + - changed-files: + - any-glob-to-any-file: + - "src/telegram/**" + - "extensions/telegram/**" + - "docs/channels/telegram.md" +"channel: tlon": + - changed-files: + - any-glob-to-any-file: + - "extensions/tlon/**" + - "docs/channels/tlon.md" +"channel: voice-call": + - changed-files: + - any-glob-to-any-file: + - "extensions/voice-call/**" +"channel: whatsapp-web": + - changed-files: + - any-glob-to-any-file: + - "src/web/**" + - "extensions/whatsapp/**" + - "docs/channels/whatsapp.md" +"channel: zalo": + - changed-files: + - any-glob-to-any-file: + - "extensions/zalo/**" + - "docs/channels/zalo.md" +"channel: zalouser": + - changed-files: + - any-glob-to-any-file: + - "extensions/zalouser/**" + - "docs/channels/zalouser.md" + +"app: android": + - changed-files: + - any-glob-to-any-file: + - "apps/android/**" + - "docs/platforms/android.md" +"app: ios": + - changed-files: + - any-glob-to-any-file: + - "apps/ios/**" + - "docs/platforms/ios.md" +"app: macos": + - changed-files: + - any-glob-to-any-file: + - "apps/macos/**" + - "docs/platforms/macos.md" + - "docs/platforms/mac/**" +"app: web-ui": + - changed-files: + - any-glob-to-any-file: + - "ui/**" + - "src/gateway/control-ui.ts" + - "src/gateway/control-ui-shared.ts" + - "src/gateway/protocol/**" + - "src/gateway/server-methods/chat.ts" + - "src/infra/control-ui-assets.ts" + +"gateway": + - changed-files: + - any-glob-to-any-file: + - "src/gateway/**" + - "src/daemon/**" + - "docs/gateway/**" + +"docs": + - changed-files: + - any-glob-to-any-file: + - "docs/**" + - "docs.acp.md" + +"cli": + - changed-files: + - any-glob-to-any-file: + - "src/cli/**" + +"commands": + - changed-files: + - any-glob-to-any-file: + - "src/commands/**" + +"scripts": + - changed-files: + - any-glob-to-any-file: + - "scripts/**" + +"docker": + - changed-files: + - any-glob-to-any-file: + - "Dockerfile" + - "Dockerfile.*" + - "docker-compose.yml" + - "docker-setup.sh" + - ".dockerignore" + - "scripts/**/*docker*" + - "scripts/**/Dockerfile*" + - "scripts/sandbox-*.sh" + - "src/agents/sandbox*.ts" + - "src/commands/sandbox*.ts" + - "src/cli/sandbox-cli.ts" + - "src/docker-setup.test.ts" + - "src/config/**/*sandbox*" + - "docs/cli/sandbox.md" + - "docs/gateway/sandbox*.md" + - "docs/install/docker.md" + - "docs/multi-agent-sandbox-tools.md" + +"agents": + - changed-files: + - any-glob-to-any-file: + - "src/agents/**" + +"security": + - changed-files: + - any-glob-to-any-file: + - "docs/cli/security.md" + - "docs/gateway/security.md" + +"extensions: copilot-proxy": + - changed-files: + - any-glob-to-any-file: + - "extensions/copilot-proxy/**" +"extensions: diagnostics-otel": + - changed-files: + - any-glob-to-any-file: + - "extensions/diagnostics-otel/**" +"extensions: google-antigravity-auth": + - changed-files: + - any-glob-to-any-file: + - "extensions/google-antigravity-auth/**" +"extensions: google-gemini-cli-auth": + - changed-files: + - any-glob-to-any-file: + - "extensions/google-gemini-cli-auth/**" +"extensions: llm-task": + - changed-files: + - any-glob-to-any-file: + - "extensions/llm-task/**" +"extensions: lobster": + - changed-files: + - any-glob-to-any-file: + - "extensions/lobster/**" +"extensions: memory-core": + - changed-files: + - any-glob-to-any-file: + - "extensions/memory-core/**" +"extensions: memory-lancedb": + - changed-files: + - any-glob-to-any-file: + - "extensions/memory-lancedb/**" +"extensions: open-prose": + - changed-files: + - any-glob-to-any-file: + - "extensions/open-prose/**" +"extensions: qwen-portal-auth": + - changed-files: + - any-glob-to-any-file: + - "extensions/qwen-portal-auth/**" diff --git a/.github/workflows/auto-response.yml b/.github/workflows/auto-response.yml new file mode 100644 index 000000000..b610e1718 --- /dev/null +++ b/.github/workflows/auto-response.yml @@ -0,0 +1,65 @@ +name: Auto response + +on: + issues: + types: [labeled] + pull_request_target: + types: [labeled] + +permissions: + issues: write + pull-requests: write + +jobs: + auto-response: + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: "2729701" + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + - name: Handle labeled items + uses: actions/github-script@v7 + with: + github-token: ${{ steps.app-token.outputs.token }} + script: | + const rules = [ + { + label: "skill-clawdhub", + close: true, + message: + "Thanks for the contribution! New skills should be published to Clawdhub for everyone to use. We’re keeping the core lean on skills, so I’m closing this out.", + }, + ]; + + const labelName = context.payload.label?.name; + if (!labelName) { + return; + } + + const rule = rules.find((item) => item.label === labelName); + if (!rule) { + return; + } + + const issueNumber = context.payload.issue?.number ?? context.payload.pull_request?.number; + if (!issueNumber) { + return; + } + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + body: rule.message, + }); + + if (rule.close) { + await github.rest.issues.update({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + state: "closed", + }); + } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be0d8926f..885d87fcb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,20 +32,29 @@ jobs: node-version: 22.x check-latest: true + - name: Setup pnpm (corepack retry) + run: | + set -euo pipefail + corepack enable + for attempt in 1 2 3; do + if corepack prepare pnpm@10.23.0 --activate; then + pnpm -v + exit 0 + fi + echo "corepack prepare failed (attempt $attempt/3). Retrying..." + sleep $((attempt * 10)) + done + exit 1 + - name: Runtime versions run: | node -v npm -v + pnpm -v - name: Capture node path run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV" - - name: Enable corepack and pin pnpm - run: | - corepack enable - corepack prepare pnpm@10.23.0 --activate - pnpm -v - - name: Install dependencies (frozen) env: CI: true @@ -67,7 +76,7 @@ jobs: command: pnpm lint - runtime: node task: test - command: pnpm test + command: pnpm canvas:a2ui:bundle && pnpm test - runtime: node task: build command: pnpm build @@ -79,7 +88,7 @@ jobs: command: pnpm format - runtime: bun task: test - command: bunx vitest run + command: pnpm canvas:a2ui:bundle && bunx vitest run - runtime: bun task: build command: bunx tsc -p tsconfig.json @@ -108,6 +117,20 @@ jobs: node-version: 22.x check-latest: true + - name: Setup pnpm (corepack retry) + run: | + set -euo pipefail + corepack enable + for attempt in 1 2 3; do + if corepack prepare pnpm@10.23.0 --activate; then + pnpm -v + exit 0 + fi + echo "corepack prepare failed (attempt $attempt/3). Retrying..." + sleep $((attempt * 10)) + done + exit 1 + - name: Setup Bun uses: oven-sh/setup-bun@v2 with: @@ -118,16 +141,11 @@ jobs: node -v npm -v bun -v + pnpm -v - name: Capture node path run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV" - - name: Enable corepack and pin pnpm - run: | - corepack enable - corepack prepare pnpm@10.23.0 --activate - pnpm -v - - name: Install dependencies env: CI: true @@ -168,6 +186,9 @@ jobs: checks-windows: runs-on: blacksmith-4vcpu-windows-2025 + env: + NODE_OPTIONS: --max-old-space-size=4096 + CLAWDBOT_TEST_WORKERS: 1 defaults: run: shell: bash @@ -180,7 +201,7 @@ jobs: command: pnpm lint - runtime: node task: test - command: pnpm test + command: pnpm canvas:a2ui:bundle && pnpm test - runtime: node task: build command: pnpm build @@ -212,6 +233,20 @@ jobs: node-version: 22.x check-latest: true + - name: Setup pnpm (corepack retry) + run: | + set -euo pipefail + corepack enable + for attempt in 1 2 3; do + if corepack prepare pnpm@10.23.0 --activate; then + pnpm -v + exit 0 + fi + echo "corepack prepare failed (attempt $attempt/3). Retrying..." + sleep $((attempt * 10)) + done + exit 1 + - name: Setup Bun uses: oven-sh/setup-bun@v2 with: @@ -222,16 +257,11 @@ jobs: node -v npm -v bun -v + pnpm -v - name: Capture node path run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV" - - name: Enable corepack and pin pnpm - run: | - corepack enable - corepack prepare pnpm@10.23.0 --activate - pnpm -v - - name: Install dependencies env: CI: true @@ -279,20 +309,29 @@ jobs: node-version: 22.x check-latest: true + - name: Setup pnpm (corepack retry) + run: | + set -euo pipefail + corepack enable + for attempt in 1 2 3; do + if corepack prepare pnpm@10.23.0 --activate; then + pnpm -v + exit 0 + fi + echo "corepack prepare failed (attempt $attempt/3). Retrying..." + sleep $((attempt * 10)) + done + exit 1 + - name: Runtime versions run: | node -v npm -v + pnpm -v - name: Capture node path run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV" - - name: Enable corepack and pin pnpm - run: | - corepack enable - corepack prepare pnpm@10.23.0 --activate - pnpm -v - - name: Install dependencies env: CI: true @@ -304,6 +343,8 @@ jobs: pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true || pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true - name: Run ${{ matrix.task }} + env: + NODE_OPTIONS: --max-old-space-size=4096 run: ${{ matrix.command }} macos-app: @@ -590,6 +631,8 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 + with: + gradle-version: 8.11.1 - name: Install Android SDK packages run: | diff --git a/.github/workflows/install-smoke.yml b/.github/workflows/install-smoke.yml index 84d1b7f32..0347c7810 100644 --- a/.github/workflows/install-smoke.yml +++ b/.github/workflows/install-smoke.yml @@ -13,12 +13,19 @@ jobs: - name: Checkout CLI uses: actions/checkout@v4 - - name: Setup pnpm - uses: pnpm/action-setup@v3 - with: - version: 10 - - name: Enable Corepack - run: corepack enable + - name: Setup pnpm (corepack retry) + run: | + set -euo pipefail + corepack enable + for attempt in 1 2 3; do + if corepack prepare pnpm@10.23.0 --activate; then + pnpm -v + exit 0 + fi + echo "corepack prepare failed (attempt $attempt/3). Retrying..." + sleep $((attempt * 10)) + done + exit 1 - name: Install pnpm deps (minimal) run: pnpm install --ignore-scripts --frozen-lockfile @@ -30,5 +37,5 @@ jobs: CLAWDBOT_NO_ONBOARD: "1" CLAWDBOT_INSTALL_SMOKE_SKIP_CLI: "1" CLAWDBOT_INSTALL_SMOKE_SKIP_NONROOT: ${{ github.event_name == 'pull_request' && '1' || '0' }} - CLAWDBOT_INSTALL_SMOKE_PREVIOUS: "2026.1.11-4" + CLAWDBOT_INSTALL_SMOKE_SKIP_PREVIOUS: "1" run: pnpm test:install:smoke diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 000000000..2b2f80130 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,24 @@ +name: Labeler + +on: + pull_request_target: + types: [opened, synchronize, reopened] + +permissions: + contents: read + pull-requests: write + +jobs: + label: + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: "2729701" + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + - uses: actions/labeler@v5 + with: + configuration-path: .github/labeler.yml + repo-token: ${{ steps.app-token.outputs.token }} + sync-labels: true diff --git a/.gitignore b/.gitignore index b2c1de9a2..9dc547c9c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,14 +19,14 @@ ui/test-results/ # Bun build artifacts *.bun-build apps/macos/.build/ -apps/shared/ClawdbotKit/.build/ +apps/shared/MoltbotKit/.build/ **/ModuleCache/ bin/ bin/clawdbot-mac bin/docs-list apps/macos/.build-local/ apps/macos/.swiftpm/ -apps/shared/ClawdbotKit/.swiftpm/ +apps/shared/MoltbotKit/.swiftpm/ Core/ apps/ios/*.xcodeproj/ apps/ios/*.xcworkspace/ @@ -40,6 +40,8 @@ apps/ios/*.xcfilelist # Vendor build artifacts vendor/a2ui/renderers/lit/dist/ +src/canvas-host/a2ui/*.bundle.js +src/canvas-host/a2ui/*.map .bundle.hash # fastlane (iOS) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..80813a0d3 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,105 @@ +# Pre-commit hooks for clawdbot +# Install: prek install +# Run manually: prek run --all-files +# +# See https://pre-commit.com for more information + +repos: + # Basic file hygiene + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: trailing-whitespace + exclude: '^(docs/|dist/|vendor/|.*\.snap$)' + - id: end-of-file-fixer + exclude: '^(docs/|dist/|vendor/|.*\.snap$)' + - id: check-yaml + args: [--allow-multiple-documents] + - id: check-added-large-files + args: [--maxkb=500] + - id: check-merge-conflict + + # Secret detection (same as CI) + - repo: https://github.com/Yelp/detect-secrets + rev: v1.5.0 + hooks: + - id: detect-secrets + args: + - --baseline + - .secrets.baseline + - --exclude-files + - '(^|/)(dist/|vendor/|pnpm-lock\.yaml$|\.detect-secrets\.cfg$)' + - --exclude-lines + - 'key_content\.include\?\("BEGIN PRIVATE KEY"\)' + - --exclude-lines + - 'case \.apiKeyEnv: "API key \(env var\)"' + - --exclude-lines + - 'case apikey = "apiKey"' + - --exclude-lines + - '"gateway\.remote\.password"' + - --exclude-lines + - '"gateway\.auth\.password"' + - --exclude-lines + - '"talk\.apiKey"' + - --exclude-lines + - '=== "string"' + - --exclude-lines + - 'typeof remote\?\.password === "string"' + + # Shell script linting + - repo: https://github.com/koalaman/shellcheck-precommit + rev: v0.11.0 + hooks: + - id: shellcheck + args: [--severity=error] # Only fail on errors, not warnings/info + # Exclude vendor and scripts with embedded code or known issues + exclude: '^(vendor/|scripts/e2e/)' + + # GitHub Actions linting + - repo: https://github.com/rhysd/actionlint + rev: v1.7.10 + hooks: + - id: actionlint + + # GitHub Actions security audit + - repo: https://github.com/zizmorcore/zizmor-pre-commit + rev: v1.22.0 + hooks: + - id: zizmor + args: [--persona=regular, --min-severity=medium, --min-confidence=medium] + exclude: '^(vendor/|Swabble/)' + + # Project checks (same commands as CI) + - repo: local + hooks: + # oxlint --type-aware src test + - id: oxlint + name: oxlint + entry: scripts/pre-commit/run-node-tool.sh oxlint --type-aware src test + language: system + pass_filenames: false + types_or: [javascript, jsx, ts, tsx] + + # oxfmt --check src test + - id: oxfmt + name: oxfmt + entry: scripts/pre-commit/run-node-tool.sh oxfmt --check src test + language: system + pass_filenames: false + types_or: [javascript, jsx, ts, tsx] + + # swiftlint (same as CI) + - id: swiftlint + name: swiftlint + entry: swiftlint --config .swiftlint.yml + language: system + pass_filenames: false + types: [swift] + + # swiftformat --lint (same as CI) + - id: swiftformat + name: swiftformat + entry: swiftformat --lint apps/macos/Sources --config .swiftformat + language: system + pass_filenames: false + types: [swift] diff --git a/.secrets.baseline b/.secrets.baseline index 4c0ca50a4..826d5b4de 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -159,23 +159,23 @@ { "type": "Base64 High Entropy String", "filename": "appcast.xml", - "hashed_secret": "1b1c2b73eca84e441a823c37a06c71c9fadcfe24", + "hashed_secret": "4e5f0a148d9ef42afeb73b1c77643e2ef2dee0b9", "is_verified": false, - "line_number": 19 + "line_number": 90 }, { "type": "Base64 High Entropy String", "filename": "appcast.xml", - "hashed_secret": "5c47736fee5151b26b3bb61bb38955da0e8937c6", + "hashed_secret": "f1ccdaf78c308ec2cf608818da13f5f1e4809ed1", "is_verified": false, - "line_number": 35 + "line_number": 138 }, { "type": "Base64 High Entropy String", "filename": "appcast.xml", - "hashed_secret": "bbbca47179268f154c63affa0ca441c6e49e650f", + "hashed_secret": "2691dc9c9ded92ba62a2d8ee589e2d78e2aa0479", "is_verified": false, - "line_number": 52 + "line_number": 212 } ], "apps/macos/Tests/ClawdbotIPCTests/AnthropicAuthResolverTests.swift": [ @@ -194,13 +194,22 @@ "line_number": 42 } ], - "apps/macos/Tests/ClawdbotIPCTests/ConnectionsSettingsSmokeTests.swift": [ + "apps/macos/Tests/ClawdbotIPCTests/GatewayEndpointStoreTests.swift": [ { "type": "Secret Keyword", - "filename": "apps/macos/Tests/ClawdbotIPCTests/ConnectionsSettingsSmokeTests.swift", - "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "filename": "apps/macos/Tests/ClawdbotIPCTests/GatewayEndpointStoreTests.swift", + "hashed_secret": "19dad5cecb110281417d1db56b60e1b006d55bb4", "is_verified": false, - "line_number": 83 + "line_number": 61 + } + ], + "apps/macos/Tests/ClawdbotIPCTests/GatewayLaunchAgentManagerTests.swift": [ + { + "type": "Secret Keyword", + "filename": "apps/macos/Tests/ClawdbotIPCTests/GatewayLaunchAgentManagerTests.swift", + "hashed_secret": "1a91d62f7ca67399625a4368a6ab5d4a3baa6073", + "is_verified": false, + "line_number": 13 } ], "apps/macos/Tests/ClawdbotIPCTests/TailscaleIntegrationSectionTests.swift": [ @@ -212,109 +221,919 @@ "line_number": 27 } ], - "docs/configuration.md": [ + "apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayChannel.swift": [ { "type": "Secret Keyword", - "filename": "docs/configuration.md", - "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "filename": "apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayChannel.swift", + "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_verified": false, - "line_number": 268 - }, + "line_number": 100 + } + ], + "docs/brave-search.md": [ { "type": "Secret Keyword", - "filename": "docs/configuration.md", - "hashed_secret": "1188d5a8ed7edcff5144a9472af960243eacf12e", + "filename": "docs/brave-search.md", + "hashed_secret": "491d458f895b9213facb2ee9375b1b044eaea3ac", "is_verified": false, - "line_number": 465 - }, + "line_number": 26 + } + ], + "docs/channels/bluebubbles.md": [ { "type": "Secret Keyword", - "filename": "docs/configuration.md", - "hashed_secret": "22af290a1a3d5e941193a41a3d3a9e4ca8da5e27", + "filename": "docs/channels/bluebubbles.md", + "hashed_secret": "555da20df20d4172e00f1b73d7c3943802055270", "is_verified": false, - "line_number": 718 - }, + "line_number": 32 + } + ], + "docs/channels/matrix.md": [ { "type": "Secret Keyword", - "filename": "docs/configuration.md", - "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209", - "is_verified": false, - "line_number": 760 - }, - { - "type": "Secret Keyword", - "filename": "docs/configuration.md", - "hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd", - "is_verified": false, - "line_number": 859 - }, - { - "type": "Secret Keyword", - "filename": "docs/configuration.md", + "filename": "docs/channels/matrix.md", "hashed_secret": "45d676e7c6ab44cf4b8fa366ef2d8fccd3e6d6e6", "is_verified": false, - "line_number": 982 + "line_number": 58 } ], - "docs/faq.md": [ + "docs/channels/nextcloud-talk.md": [ { "type": "Secret Keyword", - "filename": "docs/faq.md", - "hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281", + "filename": "docs/channels/nextcloud-talk.md", + "hashed_secret": "76ed0a056aa77060de25754586440cff390791d0", "is_verified": false, - "line_number": 593 + "line_number": 47 + } + ], + "docs/channels/nostr.md": [ + { + "type": "Secret Keyword", + "filename": "docs/channels/nostr.md", + "hashed_secret": "edeb23e25a619c434d22bb7f1c3ca4841166b4e8", + "is_verified": false, + "line_number": 65 + } + ], + "docs/channels/slack.md": [ + { + "type": "Secret Keyword", + "filename": "docs/channels/slack.md", + "hashed_secret": "3f4800fb7c1fb79a9a48bfd562d90bc6b2e2b718", + "is_verified": false, + "line_number": 141 + } + ], + "docs/concepts/memory.md": [ + { + "type": "Secret Keyword", + "filename": "docs/concepts/memory.md", + "hashed_secret": "39d711243bfcee9fec8299b204e1aa9c3430fa12", + "is_verified": false, + "line_number": 108 }, { "type": "Secret Keyword", - "filename": "docs/faq.md", + "filename": "docs/concepts/memory.md", + "hashed_secret": "1a8abbf465c52363ab4c9c6ad945b8e857cbea55", + "is_verified": false, + "line_number": 131 + }, + { + "type": "Secret Keyword", + "filename": "docs/concepts/memory.md", + "hashed_secret": "b9f640d6095b9f6b5a65983f7b76dbbb254e0044", + "is_verified": false, + "line_number": 373 + } + ], + "docs/concepts/model-providers.md": [ + { + "type": "Secret Keyword", + "filename": "docs/concepts/model-providers.md", "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", "is_verified": false, - "line_number": 650 - } - ], - "docs/skills-config.md": [ + "line_number": 168 + }, { "type": "Secret Keyword", - "filename": "docs/skills-config.md", + "filename": "docs/concepts/model-providers.md", + "hashed_secret": "ef83ad68b9b66e008727b7c417c6a8f618b5177e", + "is_verified": false, + "line_number": 255 + } + ], + "docs/environment.md": [ + { + "type": "Secret Keyword", + "filename": "docs/environment.md", + "hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281", + "is_verified": false, + "line_number": 29 + }, + { + "type": "Secret Keyword", + "filename": "docs/environment.md", + "hashed_secret": "b6f56e5e92078ed7c078c46fbfeedcbe5719bc25", + "is_verified": false, + "line_number": 31 + } + ], + "docs/gateway/configuration-examples.md": [ + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration-examples.md", + "hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281", + "is_verified": false, + "line_number": 53 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration-examples.md", + "hashed_secret": "b6f56e5e92078ed7c078c46fbfeedcbe5719bc25", + "is_verified": false, + "line_number": 55 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration-examples.md", + "hashed_secret": "22af290a1a3d5e941193a41a3d3a9e4ca8da5e27", + "is_verified": false, + "line_number": 319 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration-examples.md", "hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd", "is_verified": false, - "line_number": 28 - } - ], - "docs/skills.md": [ + "line_number": 414 + }, { "type": "Secret Keyword", - "filename": "docs/skills.md", + "filename": "docs/gateway/configuration-examples.md", + "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209", + "is_verified": false, + "line_number": 548 + } + ], + "docs/gateway/configuration.md": [ + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration.md", + "hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281", + "is_verified": false, + "line_number": 272 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration.md", + "hashed_secret": "b6f56e5e92078ed7c078c46fbfeedcbe5719bc25", + "is_verified": false, + "line_number": 274 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration.md", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 1029 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration.md", + "hashed_secret": "1188d5a8ed7edcff5144a9472af960243eacf12e", + "is_verified": false, + "line_number": 1470 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration.md", + "hashed_secret": "bde4db9b4c3be4049adc3b9a69851d7c35119770", + "is_verified": false, + "line_number": 1486 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration.md", + "hashed_secret": "22af290a1a3d5e941193a41a3d3a9e4ca8da5e27", + "is_verified": false, + "line_number": 2268 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 2344 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/configuration.md", "hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd", "is_verified": false, - "line_number": 97 - } - ], - "docs/tailscale.md": [ + "line_number": 2658 + }, { "type": "Secret Keyword", - "filename": "docs/tailscale.md", + "filename": "docs/gateway/configuration.md", + "hashed_secret": "45d676e7c6ab44cf4b8fa366ef2d8fccd3e6d6e6", + "is_verified": false, + "line_number": 2844 + } + ], + "docs/gateway/local-models.md": [ + { + "type": "Secret Keyword", + "filename": "docs/gateway/local-models.md", + "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209", + "is_verified": false, + "line_number": 32 + }, + { + "type": "Secret Keyword", + "filename": "docs/gateway/local-models.md", + "hashed_secret": "49fd535e63175a827aab3eff9ac58a9e82460ac9", + "is_verified": false, + "line_number": 121 + } + ], + "docs/gateway/tailscale.md": [ + { + "type": "Secret Keyword", + "filename": "docs/gateway/tailscale.md", "hashed_secret": "9cb0dc5383312aa15b9dc6745645bde18ff5ade9", "is_verified": false, - "line_number": 52 + "line_number": 75 } ], - "docs/talk.md": [ + "docs/help/faq.md": [ { "type": "Secret Keyword", - "filename": "docs/talk.md", + "filename": "docs/help/faq.md", + "hashed_secret": "491d458f895b9213facb2ee9375b1b044eaea3ac", + "is_verified": false, + "line_number": 925 + }, + { + "type": "Secret Keyword", + "filename": "docs/help/faq.md", + "hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281", + "is_verified": false, + "line_number": 1113 + }, + { + "type": "Secret Keyword", + "filename": "docs/help/faq.md", + "hashed_secret": "b6f56e5e92078ed7c078c46fbfeedcbe5719bc25", + "is_verified": false, + "line_number": 1114 + }, + { + "type": "Secret Keyword", + "filename": "docs/help/faq.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 1439 + }, + { + "type": "Secret Keyword", + "filename": "docs/help/faq.md", + "hashed_secret": "45d676e7c6ab44cf4b8fa366ef2d8fccd3e6d6e6", + "is_verified": false, + "line_number": 1715 + } + ], + "docs/nodes/talk.md": [ + { + "type": "Secret Keyword", + "filename": "docs/nodes/talk.md", "hashed_secret": "1188d5a8ed7edcff5144a9472af960243eacf12e", "is_verified": false, "line_number": 50 } ], - "docs/telegram.md": [ + "docs/perplexity.md": [ { "type": "Secret Keyword", - "filename": "docs/telegram.md", - "hashed_secret": "e9fe51f94eadabf54dbf2fbbd57188b9abee436e", + "filename": "docs/perplexity.md", + "hashed_secret": "6b26c117c66a0c030e239eef595c1e18865132a8", "is_verified": false, - "line_number": 57 + "line_number": 35 + } + ], + "docs/providers/anthropic.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/anthropic.md", + "hashed_secret": "c7a8c334eef5d1749fface7d42c66f9ae5e8cf36", + "is_verified": false, + "line_number": 32 + } + ], + "docs/providers/glm.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/glm.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 22 + } + ], + "docs/providers/minimax.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/minimax.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 49 + }, + { + "type": "Secret Keyword", + "filename": "docs/providers/minimax.md", + "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209", + "is_verified": false, + "line_number": 118 + } + ], + "docs/providers/moonshot.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/moonshot.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 39 + } + ], + "docs/providers/openai.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/openai.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 31 + } + ], + "docs/providers/opencode.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/opencode.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 25 + } + ], + "docs/providers/openrouter.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/openrouter.md", + "hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281", + "is_verified": false, + "line_number": 22 + } + ], + "docs/providers/synthetic.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/synthetic.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 31 + } + ], + "docs/providers/zai.md": [ + { + "type": "Secret Keyword", + "filename": "docs/providers/zai.md", + "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0", + "is_verified": false, + "line_number": 25 + } + ], + "docs/tools/browser.md": [ + { + "type": "Basic Auth Credentials", + "filename": "docs/tools/browser.md", + "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684", + "is_verified": false, + "line_number": 163 + } + ], + "docs/tools/firecrawl.md": [ + { + "type": "Secret Keyword", + "filename": "docs/tools/firecrawl.md", + "hashed_secret": "674397e2c0c2faaa85961c708d2a96a7cc7af217", + "is_verified": false, + "line_number": 28 + } + ], + "docs/tools/skills-config.md": [ + { + "type": "Secret Keyword", + "filename": "docs/tools/skills-config.md", + "hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd", + "is_verified": false, + "line_number": 30 + } + ], + "docs/tools/skills.md": [ + { + "type": "Secret Keyword", + "filename": "docs/tools/skills.md", + "hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd", + "is_verified": false, + "line_number": 160 + } + ], + "docs/tools/web.md": [ + { + "type": "Secret Keyword", + "filename": "docs/tools/web.md", + "hashed_secret": "6b26c117c66a0c030e239eef595c1e18865132a8", + "is_verified": false, + "line_number": 61 + }, + { + "type": "Secret Keyword", + "filename": "docs/tools/web.md", + "hashed_secret": "96c682c88ed551f22fe76d206c2dfb7df9221ad9", + "is_verified": false, + "line_number": 112 + }, + { + "type": "Secret Keyword", + "filename": "docs/tools/web.md", + "hashed_secret": "491d458f895b9213facb2ee9375b1b044eaea3ac", + "is_verified": false, + "line_number": 160 + }, + { + "type": "Secret Keyword", + "filename": "docs/tools/web.md", + "hashed_secret": "674397e2c0c2faaa85961c708d2a96a7cc7af217", + "is_verified": false, + "line_number": 223 + } + ], + "docs/tts.md": [ + { + "type": "Secret Keyword", + "filename": "docs/tts.md", + "hashed_secret": "bde4db9b4c3be4049adc3b9a69851d7c35119770", + "is_verified": false, + "line_number": 72 + }, + { + "type": "Secret Keyword", + "filename": "docs/tts.md", + "hashed_secret": "1188d5a8ed7edcff5144a9472af960243eacf12e", + "is_verified": false, + "line_number": 77 + } + ], + "extensions/bluebubbles/src/actions.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/actions.test.ts", + "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc", + "is_verified": false, + "line_number": 73 + } + ], + "extensions/bluebubbles/src/attachments.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/attachments.test.ts", + "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc", + "is_verified": false, + "line_number": 35 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/attachments.test.ts", + "hashed_secret": "db1530e1ea43af094d3d75b8dbaf19a4a182a318", + "is_verified": false, + "line_number": 99 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/attachments.test.ts", + "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", + "is_verified": false, + "line_number": 117 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/attachments.test.ts", + "hashed_secret": "052f076c732648ab32d2fcde9fe255319bfa0c7b", + "is_verified": false, + "line_number": 229 + } + ], + "extensions/bluebubbles/src/chat.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/chat.test.ts", + "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", + "is_verified": false, + "line_number": 33 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/chat.test.ts", + "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc", + "is_verified": false, + "line_number": 68 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/chat.test.ts", + "hashed_secret": "5c5a15a8b0b3e154d77746945e563ba40100681b", + "is_verified": false, + "line_number": 85 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/chat.test.ts", + "hashed_secret": "faacad0ce4ea1c19b46e128fd79679d37d3d331d", + "is_verified": false, + "line_number": 134 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/chat.test.ts", + "hashed_secret": "4dcc26a1d99532846fedf1265df4f40f4e0005b8", + "is_verified": false, + "line_number": 219 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/chat.test.ts", + "hashed_secret": "fd2a721f7be1ee3d691a011affcdb11d0ca365a8", + "is_verified": false, + "line_number": 282 + } + ], + "extensions/bluebubbles/src/monitor.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/monitor.test.ts", + "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc", + "is_verified": false, + "line_number": 187 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/monitor.test.ts", + "hashed_secret": "1ae0af3fe72b3ba394f9fa95a6cffc090d726c23", + "is_verified": false, + "line_number": 394 + } + ], + "extensions/bluebubbles/src/reactions.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/reactions.test.ts", + "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", + "is_verified": false, + "line_number": 38 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/reactions.test.ts", + "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc", + "is_verified": false, + "line_number": 179 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/reactions.test.ts", + "hashed_secret": "a4a05c9a6449eb9d6cdac81dd7edc49230e327e6", + "is_verified": false, + "line_number": 210 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/reactions.test.ts", + "hashed_secret": "a2833da9f0a16f09994754d0a31749cecf8c8c77", + "is_verified": false, + "line_number": 316 + } + ], + "extensions/bluebubbles/src/send.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/send.test.ts", + "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", + "is_verified": false, + "line_number": 38 + }, + { + "type": "Secret Keyword", + "filename": "extensions/bluebubbles/src/send.test.ts", + "hashed_secret": "faacad0ce4ea1c19b46e128fd79679d37d3d331d", + "is_verified": false, + "line_number": 675 + } + ], + "extensions/bluebubbles/src/targets.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "extensions/bluebubbles/src/targets.test.ts", + "hashed_secret": "a3af2fb0c1e2a30bb038049e1e4b401593af6225", + "is_verified": false, + "line_number": 62 + } + ], + "extensions/bluebubbles/src/targets.ts": [ + { + "type": "Hex High Entropy String", + "filename": "extensions/bluebubbles/src/targets.ts", + "hashed_secret": "a3af2fb0c1e2a30bb038049e1e4b401593af6225", + "is_verified": false, + "line_number": 214 + } + ], + "extensions/copilot-proxy/index.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/copilot-proxy/index.ts", + "hashed_secret": "50f013532a9770a2c2cfdc38b7581dd01df69b70", + "is_verified": false, + "line_number": 4 + } + ], + "extensions/google-antigravity-auth/index.ts": [ + { + "type": "Base64 High Entropy String", + "filename": "extensions/google-antigravity-auth/index.ts", + "hashed_secret": "709d0f232b6ac4f8d24dec3e4fabfdb14257174f", + "is_verified": false, + "line_number": 9 + } + ], + "extensions/matrix/src/matrix/accounts.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/matrix/src/matrix/accounts.test.ts", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 75 + } + ], + "extensions/matrix/src/matrix/client.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/matrix/src/matrix/client.test.ts", + "hashed_secret": "fe7fcdaea49ece14677acd32374d2f1225819d5c", + "is_verified": false, + "line_number": 14 + }, + { + "type": "Secret Keyword", + "filename": "extensions/matrix/src/matrix/client.test.ts", + "hashed_secret": "3dc927d80543dc0f643940b70d066bd4b4c4b78e", + "is_verified": false, + "line_number": 24 + } + ], + "extensions/matrix/src/matrix/client/storage.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/matrix/src/matrix/client/storage.ts", + "hashed_secret": "7505d64a54e061b7acd54ccd58b49dc43500b635", + "is_verified": false, + "line_number": 9 + } + ], + "extensions/memory-lancedb/config.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/memory-lancedb/config.ts", + "hashed_secret": "ecb252044b5ea0f679ee78ec1a12904739e2904d", + "is_verified": false, + "line_number": 70 + } + ], + "extensions/memory-lancedb/index.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/memory-lancedb/index.test.ts", + "hashed_secret": "ed65c049bb2f78ee4f703b2158ba9cc6ea31fb7e", + "is_verified": false, + "line_number": 70 + } + ], + "extensions/msteams/src/probe.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/msteams/src/probe.test.ts", + "hashed_secret": "1a91d62f7ca67399625a4368a6ab5d4a3baa6073", + "is_verified": false, + "line_number": 34 + } + ], + "extensions/nextcloud-talk/src/accounts.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/nextcloud-talk/src/accounts.ts", + "hashed_secret": "920f8f5815b381ea692e9e7c2f7119f2b1aa620a", + "is_verified": false, + "line_number": 26 + }, + { + "type": "Secret Keyword", + "filename": "extensions/nextcloud-talk/src/accounts.ts", + "hashed_secret": "71f8e7976e4cbc4561c9d62fb283e7f788202acb", + "is_verified": false, + "line_number": 139 + } + ], + "extensions/nextcloud-talk/src/channel.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/nextcloud-talk/src/channel.ts", + "hashed_secret": "71f8e7976e4cbc4561c9d62fb283e7f788202acb", + "is_verified": false, + "line_number": 390 + } + ], + "extensions/nostr/README.md": [ + { + "type": "Secret Keyword", + "filename": "extensions/nostr/README.md", + "hashed_secret": "edeb23e25a619c434d22bb7f1c3ca4841166b4e8", + "is_verified": false, + "line_number": 43 + } + ], + "extensions/nostr/src/channel.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/channel.test.ts", + "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13", + "is_verified": false, + "line_number": 48 + }, + { + "type": "Secret Keyword", + "filename": "extensions/nostr/src/channel.test.ts", + "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13", + "is_verified": false, + "line_number": 48 + } + ], + "extensions/nostr/src/nostr-bus.fuzz.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-bus.fuzz.test.ts", + "hashed_secret": "2b4489606a23fb31fcdc849fa7e577ba90f6d39a", + "is_verified": false, + "line_number": 202 + }, + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-bus.fuzz.test.ts", + "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13", + "is_verified": false, + "line_number": 203 + }, + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-bus.fuzz.test.ts", + "hashed_secret": "b84cb0c3925d34496e6c8b0e55b8c1664a438035", + "is_verified": false, + "line_number": 208 + } + ], + "extensions/nostr/src/nostr-bus.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-bus.test.ts", + "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13", + "is_verified": false, + "line_number": 11 + }, + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-bus.test.ts", + "hashed_secret": "7258e28563f03fb4c5994e8402e6f610d1f0f110", + "is_verified": false, + "line_number": 33 + }, + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-bus.test.ts", + "hashed_secret": "2b4489606a23fb31fcdc849fa7e577ba90f6d39a", + "is_verified": false, + "line_number": 101 + }, + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-bus.test.ts", + "hashed_secret": "ef717286343f6da3f4e6f68c6de02a5148a801c4", + "is_verified": false, + "line_number": 106 + }, + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-bus.test.ts", + "hashed_secret": "98b35fe4c45011220f509ebb5546d3889b55a891", + "is_verified": false, + "line_number": 111 + } + ], + "extensions/nostr/src/nostr-profile.fuzz.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-profile.fuzz.test.ts", + "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13", + "is_verified": false, + "line_number": 12 + } + ], + "extensions/nostr/src/nostr-profile.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/nostr-profile.test.ts", + "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13", + "is_verified": false, + "line_number": 14 + } + ], + "extensions/nostr/src/types.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "extensions/nostr/src/types.test.ts", + "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13", + "is_verified": false, + "line_number": 8 + }, + { + "type": "Secret Keyword", + "filename": "extensions/nostr/src/types.test.ts", + "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13", + "is_verified": false, + "line_number": 8 + }, + { + "type": "Secret Keyword", + "filename": "extensions/nostr/src/types.test.ts", + "hashed_secret": "3bee216ebc256d692260fc3adc765050508fef5e", + "is_verified": false, + "line_number": 127 + } + ], + "extensions/open-prose/skills/prose/SKILL.md": [ + { + "type": "Basic Auth Credentials", + "filename": "extensions/open-prose/skills/prose/SKILL.md", + "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684", + "is_verified": false, + "line_number": 200 + } + ], + "extensions/open-prose/skills/prose/state/postgres.md": [ + { + "type": "Secret Keyword", + "filename": "extensions/open-prose/skills/prose/state/postgres.md", + "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", + "is_verified": false, + "line_number": 75 + }, + { + "type": "Basic Auth Credentials", + "filename": "extensions/open-prose/skills/prose/state/postgres.md", + "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684", + "is_verified": false, + "line_number": 198 + } + ], + "extensions/zalo/README.md": [ + { + "type": "Secret Keyword", + "filename": "extensions/zalo/README.md", + "hashed_secret": "f51aaee16a4a756d287f126b99c081b73cba7f15", + "is_verified": false, + "line_number": 41 + } + ], + "extensions/zalo/src/monitor.webhook.test.ts": [ + { + "type": "Secret Keyword", + "filename": "extensions/zalo/src/monitor.webhook.test.ts", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 43 + } + ], + "skills/1password/references/cli-examples.md": [ + { + "type": "Secret Keyword", + "filename": "skills/1password/references/cli-examples.md", + "hashed_secret": "9dda0987cc3054773a2df97e352d4f64d233ef10", + "is_verified": false, + "line_number": 17 } ], "skills/local-places/SERVER_README.md": [ @@ -344,50 +1163,395 @@ "line_number": 18 } ], - "src/agents/models-config.test.ts": [ + "src/agents/memory-search.test.ts": [ { "type": "Secret Keyword", - "filename": "src/agents/models-config.test.ts", - "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d", + "filename": "src/agents/memory-search.test.ts", + "hashed_secret": "a1b49d68a91fdf9c9217773f3fac988d77fa0f50", "is_verified": false, - "line_number": 25 - }, - { - "type": "Secret Keyword", - "filename": "src/agents/models-config.test.ts", - "hashed_secret": "3a81eb091f80c845232225be5663d270e90dacb7", - "is_verified": false, - "line_number": 90 + "line_number": 164 } ], - "src/agents/skills.test.ts": [ + "src/agents/model-auth.test.ts": [ { "type": "Secret Keyword", - "filename": "src/agents/skills.test.ts", - "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "filename": "src/agents/model-auth.test.ts", + "hashed_secret": "07a6b9cec637c806195e8aa7e5c0851ab03dc35e", "is_verified": false, - "line_number": 158 + "line_number": 211 }, { "type": "Secret Keyword", - "filename": "src/agents/skills.test.ts", - "hashed_secret": "7a85f4764bbd6daf1c3545efbbf0f279a6dc0beb", + "filename": "src/agents/model-auth.test.ts", + "hashed_secret": "21f296583ccd80c5ab9b3330a8b0d47e4a409fb9", "is_verified": false, - "line_number": 265 + "line_number": 240 }, { "type": "Secret Keyword", - "filename": "src/agents/skills.test.ts", + "filename": "src/agents/model-auth.test.ts", + "hashed_secret": "77e991e9f56e6fa4ed1a908208048421f1214c07", + "is_verified": false, + "line_number": 264 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/model-auth.test.ts", + "hashed_secret": "dff6d4ff5dc357cf451d1855ab9cbda562645c9f", + "is_verified": false, + "line_number": 295 + } + ], + "src/agents/model-auth.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/model-auth.ts", + "hashed_secret": "8956265d216d474a080edaa97880d37fc1386f33", + "is_verified": false, + "line_number": 22 + } + ], + "src/agents/models-config.auto-injects-github-copilot-provider-token-is.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.auto-injects-github-copilot-provider-token-is.test.ts", + "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d", + "is_verified": false, + "line_number": 16 + } + ], + "src/agents/models-config.falls-back-default-baseurl-token-exchange-fails.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.falls-back-default-baseurl-token-exchange-fails.test.ts", + "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d", + "is_verified": false, + "line_number": 16 + } + ], + "src/agents/models-config.fills-missing-provider-apikey-from-env-var.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.fills-missing-provider-apikey-from-env-var.test.ts", + "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d", + "is_verified": false, + "line_number": 16 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.fills-missing-provider-apikey-from-env-var.test.ts", + "hashed_secret": "fcdd655b11f33ba4327695084a347b2ba192976c", + "is_verified": false, + "line_number": 50 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.fills-missing-provider-apikey-from-env-var.test.ts", + "hashed_secret": "3a81eb091f80c845232225be5663d270e90dacb7", + "is_verified": false, + "line_number": 108 + } + ], + "src/agents/models-config.normalizes-gemini-3-ids-preview-google-providers.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.normalizes-gemini-3-ids-preview-google-providers.test.ts", + "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d", + "is_verified": false, + "line_number": 16 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.normalizes-gemini-3-ids-preview-google-providers.test.ts", + "hashed_secret": "980d02eb9335ae7c9e9984f6c8ad432352a0d2ac", + "is_verified": false, + "line_number": 57 + } + ], + "src/agents/models-config.skips-writing-models-json-no-env-token.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.skips-writing-models-json-no-env-token.test.ts", + "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d", + "is_verified": false, + "line_number": 16 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.skips-writing-models-json-no-env-token.test.ts", + "hashed_secret": "fcdd655b11f33ba4327695084a347b2ba192976c", + "is_verified": false, + "line_number": 112 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.skips-writing-models-json-no-env-token.test.ts", + "hashed_secret": "94c4be5a1976115e8152960c21e04400a4fccdf6", + "is_verified": false, + "line_number": 146 + } + ], + "src/agents/models-config.uses-first-github-copilot-profile-env-tokens.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/models-config.uses-first-github-copilot-profile-env-tokens.test.ts", + "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d", + "is_verified": false, + "line_number": 16 + } + ], + "src/agents/openai-responses.reasoning-replay.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/openai-responses.reasoning-replay.test.ts", + "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", + "is_verified": false, + "line_number": 124 + } + ], + "src/agents/pi-embedded-runner.applygoogleturnorderingfix.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.applygoogleturnorderingfix.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 58 + } + ], + "src/agents/pi-embedded-runner.buildembeddedsandboxinfo.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.buildembeddedsandboxinfo.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 57 + } + ], + "src/agents/pi-embedded-runner.createsystempromptoverride.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.createsystempromptoverride.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 56 + } + ], + "src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.falls-back-provider-default-per-dm-not.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.falls-back-provider-default-per-dm-not.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 56 + } + ], + "src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.returns-undefined-sessionkey-is-undefined.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.returns-undefined-sessionkey-is-undefined.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 56 + } + ], + "src/agents/pi-embedded-runner.limithistoryturns.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.limithistoryturns.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 57 + } + ], + "src/agents/pi-embedded-runner.resolvesessionagentids.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.resolvesessionagentids.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 56 + } + ], + "src/agents/pi-embedded-runner.splitsdktools.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.splitsdktools.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 57 + } + ], + "src/agents/pi-embedded-runner.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 117 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/pi-embedded-runner.test.ts", + "hashed_secret": "fcdd655b11f33ba4327695084a347b2ba192976c", + "is_verified": false, + "line_number": 178 + } + ], + "src/agents/skills.applyskillenvoverrides.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/skills.applyskillenvoverrides.test.ts", "hashed_secret": "5df3a673d724e8a1eb673a8baf623e183940804d", "is_verified": false, - "line_number": 462 + "line_number": 54 }, { "type": "Secret Keyword", - "filename": "src/agents/skills.test.ts", + "filename": "src/agents/skills.applyskillenvoverrides.test.ts", "hashed_secret": "8921daaa546693e52bc1f9c40bdcf15e816e0448", "is_verified": false, - "line_number": 490 + "line_number": 80 + } + ], + "src/agents/skills.build-workspace-skills-prompt.prefers-workspace-skills-managed-skills.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/skills.build-workspace-skills-prompt.prefers-workspace-skills-managed-skills.test.ts", + "hashed_secret": "7a85f4764bbd6daf1c3545efbbf0f279a6dc0beb", + "is_verified": false, + "line_number": 124 + } + ], + "src/agents/skills.build-workspace-skills-prompt.syncs-merged-skills-into-target-workspace.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/skills.build-workspace-skills-prompt.syncs-merged-skills-into-target-workspace.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 102 + } + ], + "src/agents/tools/web-fetch.ssrf.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/tools/web-fetch.ssrf.test.ts", + "hashed_secret": "5ce8e9d54c77266fff990194d2219a708c59b76c", + "is_verified": false, + "line_number": 55 + } + ], + "src/agents/tools/web-search.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/tools/web-search.ts", + "hashed_secret": "dfba7aade0868074c2861c98e2a9a92f3178a51b", + "is_verified": false, + "line_number": 85 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/tools/web-search.ts", + "hashed_secret": "71f8e7976e4cbc4561c9d62fb283e7f788202acb", + "is_verified": false, + "line_number": 190 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/tools/web-search.ts", + "hashed_secret": "c4865ff9250aca23b0d98eb079dad70ebec1cced", + "is_verified": false, + "line_number": 198 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/tools/web-search.ts", + "hashed_secret": "527ee41f36386e85fa932ef09471ca017f3c95c8", + "is_verified": false, + "line_number": 199 + } + ], + "src/agents/tools/web-tools.enabled-defaults.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/tools/web-tools.enabled-defaults.test.ts", + "hashed_secret": "47b249a75ca78fdb578d0f28c33685e27ea82684", + "is_verified": false, + "line_number": 213 + }, + { + "type": "Secret Keyword", + "filename": "src/agents/tools/web-tools.enabled-defaults.test.ts", + "hashed_secret": "d0ffd81d6d7ad1bc3c365660fe8882480c9a986e", + "is_verified": false, + "line_number": 242 + } + ], + "src/agents/tools/web-tools.fetch.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/agents/tools/web-tools.fetch.test.ts", + "hashed_secret": "5ce8e9d54c77266fff990194d2219a708c59b76c", + "is_verified": false, + "line_number": 101 + } + ], + "src/auto-reply/reply.directive.directive-behavior.prefers-alias-matches-fuzzy-selection-is-ambiguous.e2e.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/auto-reply/reply.directive.directive-behavior.prefers-alias-matches-fuzzy-selection-is-ambiguous.e2e.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 90 + }, + { + "type": "Secret Keyword", + "filename": "src/auto-reply/reply.directive.directive-behavior.prefers-alias-matches-fuzzy-selection-is-ambiguous.e2e.test.ts", + "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209", + "is_verified": false, + "line_number": 96 + } + ], + "src/auto-reply/reply.directive.directive-behavior.supports-fuzzy-model-matches-model-directive.e2e.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/auto-reply/reply.directive.directive-behavior.supports-fuzzy-model-matches-model-directive.e2e.test.ts", + "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd", + "is_verified": false, + "line_number": 87 + }, + { + "type": "Secret Keyword", + "filename": "src/auto-reply/reply.directive.directive-behavior.supports-fuzzy-model-matches-model-directive.e2e.test.ts", + "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209", + "is_verified": false, + "line_number": 228 + } + ], + "src/auto-reply/status.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/auto-reply/status.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 20 + } + ], + "src/browser/cdp.helpers.test.ts": [ + { + "type": "Basic Auth Credentials", + "filename": "src/browser/cdp.helpers.test.ts", + "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684", + "is_verified": false, + "line_number": 22 + } + ], + "src/browser/cdp.test.ts": [ + { + "type": "Basic Auth Credentials", + "filename": "src/browser/cdp.test.ts", + "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684", + "is_verified": false, + "line_number": 172 } ], "src/browser/target-id.test.ts": [ @@ -396,57 +1560,386 @@ "filename": "src/browser/target-id.test.ts", "hashed_secret": "4e126c049580d66ca1549fa534d95a7263f27f46", "is_verified": false, - "line_number": 16 + "line_number": 13 } ], - "src/commands/antigravity-oauth.ts": [ + "src/cli/update-cli.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "src/cli/update-cli.test.ts", + "hashed_secret": "e4f91dd323bac5bfc4f60a6e433787671dc2421d", + "is_verified": false, + "line_number": 112 + } + ], + "src/commands/auth-choice.preferred-provider.ts": [ + { + "type": "Secret Keyword", + "filename": "src/commands/auth-choice.preferred-provider.ts", + "hashed_secret": "c03a8d10174dd7eb2b3288b570a5a74fdd9ae05d", + "is_verified": false, + "line_number": 8 + } + ], + "src/commands/auth-choice.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/commands/auth-choice.test.ts", + "hashed_secret": "2480500ff391183070fe22ba8665a8be19350833", + "is_verified": false, + "line_number": 289 + }, + { + "type": "Secret Keyword", + "filename": "src/commands/auth-choice.test.ts", + "hashed_secret": "77e991e9f56e6fa4ed1a908208048421f1214c07", + "is_verified": false, + "line_number": 350 + }, + { + "type": "Secret Keyword", + "filename": "src/commands/auth-choice.test.ts", + "hashed_secret": "1b4d8423b11d32dd0c466428ac81de84a4a9442b", + "is_verified": false, + "line_number": 528 + } + ], + "src/commands/configure.gateway-auth.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/commands/configure.gateway-auth.test.ts", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 8 + } + ], + "src/commands/models/list.status.test.ts": [ { "type": "Base64 High Entropy String", - "filename": "src/commands/antigravity-oauth.ts", - "hashed_secret": "709d0f232b6ac4f8d24dec3e4fabfdb14257174f", + "filename": "src/commands/models/list.status.test.ts", + "hashed_secret": "d6ae2508a78a232d5378ef24b85ce40cbb4d7ff0", "is_verified": false, - "line_number": 17 + "line_number": 11 }, { "type": "Base64 High Entropy String", - "filename": "src/commands/antigravity-oauth.ts", - "hashed_secret": "3848603b8e866f62d07c206ff622279b9dcb0238", + "filename": "src/commands/models/list.status.test.ts", + "hashed_secret": "2d8012102440ea97852b3152239218f00579bafa", "is_verified": false, - "line_number": 20 - } - ], - "src/commands/onboard-auth.ts": [ + "line_number": 18 + }, + { + "type": "Base64 High Entropy String", + "filename": "src/commands/models/list.status.test.ts", + "hashed_secret": "51848e2be4b461a549218d3167f19c01be6b98b8", + "is_verified": false, + "line_number": 46 + }, { "type": "Secret Keyword", - "filename": "src/commands/onboard-auth.ts", + "filename": "src/commands/models/list.status.test.ts", + "hashed_secret": "51848e2be4b461a549218d3167f19c01be6b98b8", + "is_verified": false, + "line_number": 46 + }, + { + "type": "Secret Keyword", + "filename": "src/commands/models/list.status.test.ts", + "hashed_secret": "1c1e381bfb72d3b7bfca9437053d9875356680f0", + "is_verified": false, + "line_number": 52 + } + ], + "src/commands/onboard-auth.config-minimax.ts": [ + { + "type": "Secret Keyword", + "filename": "src/commands/onboard-auth.config-minimax.ts", "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209", "is_verified": false, + "line_number": 30 + }, + { + "type": "Secret Keyword", + "filename": "src/commands/onboard-auth.config-minimax.ts", + "hashed_secret": "ddcb713196b974770575a9bea5a4e7d46361f8e9", + "is_verified": false, + "line_number": 85 + } + ], + "src/commands/onboard-auth.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/commands/onboard-auth.test.ts", + "hashed_secret": "666c100dab549a6f56da7da546bd848ed5086541", + "is_verified": false, + "line_number": 230 + }, + { + "type": "Secret Keyword", + "filename": "src/commands/onboard-auth.test.ts", + "hashed_secret": "e184b402822abc549b37689c84e8e0e33c39a1f1", + "is_verified": false, + "line_number": 262 + } + ], + "src/commands/onboard-non-interactive.ai-gateway.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/commands/onboard-non-interactive.ai-gateway.test.ts", + "hashed_secret": "77e991e9f56e6fa4ed1a908208048421f1214c07", + "is_verified": false, "line_number": 50 } ], - "src/config/config.test.ts": [ + "src/commands/onboard-non-interactive/api-keys.ts": [ { "type": "Secret Keyword", - "filename": "src/config/config.test.ts", - "hashed_secret": "bea2f7b64fab8d1d414d0449530b1e088d36d5b1", + "filename": "src/commands/onboard-non-interactive/api-keys.ts", + "hashed_secret": "112f3a99b283a4e1788dedd8e0e5d35375c33747", "is_verified": false, - "line_number": 520 + "line_number": 10 } ], - "src/gateway/server.auth.test.ts": [ + "src/config/config.env-vars.test.ts": [ { "type": "Secret Keyword", - "filename": "src/gateway/server.auth.test.ts", - "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "filename": "src/config/config.env-vars.test.ts", + "hashed_secret": "a24ef9c1a27cac44823571ceef2e8262718eee36", "is_verified": false, - "line_number": 89 + "line_number": 15 }, { "type": "Secret Keyword", - "filename": "src/gateway/server.auth.test.ts", + "filename": "src/config/config.env-vars.test.ts", + "hashed_secret": "29d5f92e9ee44d4854d6dfaeefc3dc27d779fdf3", + "is_verified": false, + "line_number": 47 + }, + { + "type": "Secret Keyword", + "filename": "src/config/config.env-vars.test.ts", + "hashed_secret": "1672b6a1e7956c6a70f45d699aa42a351b1f8b80", + "is_verified": false, + "line_number": 63 + } + ], + "src/config/config.talk-api-key-fallback.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/config/config.talk-api-key-fallback.test.ts", + "hashed_secret": "bea2f7b64fab8d1d414d0449530b1e088d36d5b1", + "is_verified": false, + "line_number": 42 + } + ], + "src/config/config.web-search-provider.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/config/config.web-search-provider.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 14 + } + ], + "src/config/env-substitution.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/config/env-substitution.test.ts", + "hashed_secret": "f2b14f68eb995facb3a1c35287b778d5bd785511", + "is_verified": false, + "line_number": 38 + }, + { + "type": "Secret Keyword", + "filename": "src/config/env-substitution.test.ts", + "hashed_secret": "ec417f567082612f8fd6afafe1abcab831fca840", + "is_verified": false, + "line_number": 69 + }, + { + "type": "Secret Keyword", + "filename": "src/config/env-substitution.test.ts", + "hashed_secret": "520bd69c3eb1646d9a78181ecb4c90c51fdf428d", + "is_verified": false, + "line_number": 70 + }, + { + "type": "Secret Keyword", + "filename": "src/config/env-substitution.test.ts", + "hashed_secret": "f136444bf9b3d01a9f9b772b80ac6bf7b6a43ef0", + "is_verified": false, + "line_number": 228 + } + ], + "src/config/schema.ts": [ + { + "type": "Secret Keyword", + "filename": "src/config/schema.ts", + "hashed_secret": "e73c9fcad85cd4eecc74181ec4bdb31064d68439", + "is_verified": false, + "line_number": 184 + }, + { + "type": "Secret Keyword", + "filename": "src/config/schema.ts", + "hashed_secret": "2eda7cd978f39eebec3bf03e4410a40e14167fff", + "is_verified": false, + "line_number": 220 + }, + { + "type": "Secret Keyword", + "filename": "src/config/schema.ts", + "hashed_secret": "9f4cda226d3868676ac7f86f59e4190eb94bd208", + "is_verified": false, + "line_number": 418 + }, + { + "type": "Secret Keyword", + "filename": "src/config/schema.ts", + "hashed_secret": "01822c8bbf6a8b136944b14182cb885100ec2eae", + "is_verified": false, + "line_number": 437 + }, + { + "type": "Secret Keyword", + "filename": "src/config/schema.ts", + "hashed_secret": "bb7dfd9746e660e4a4374951ec5938ef0e343255", + "is_verified": false, + "line_number": 487 + } + ], + "src/config/slack-http-config.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/config/slack-http-config.test.ts", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 11 + } + ], + "src/gateway/auth.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/gateway/auth.test.ts", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 43 + }, + { + "type": "Secret Keyword", + "filename": "src/gateway/auth.test.ts", "hashed_secret": "a4b48a81cdab1e1a5dd37907d6c85ca1c61ddc7c", "is_verified": false, - "line_number": 109 + "line_number": 51 + } + ], + "src/gateway/call.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/gateway/call.test.ts", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 285 + }, + { + "type": "Secret Keyword", + "filename": "src/gateway/call.test.ts", + "hashed_secret": "e493f561d90c6638c1f51c5a8a069c3b129b79ed", + "is_verified": false, + "line_number": 295 + }, + { + "type": "Secret Keyword", + "filename": "src/gateway/call.test.ts", + "hashed_secret": "2e07956ffc9bc4fd624064c40b7495c85d5f1467", + "is_verified": false, + "line_number": 300 + }, + { + "type": "Secret Keyword", + "filename": "src/gateway/call.test.ts", + "hashed_secret": "bddc29032de580fb53b3a9a0357dd409086db800", + "is_verified": false, + "line_number": 313 + } + ], + "src/gateway/client.test.ts": [ + { + "type": "Private Key", + "filename": "src/gateway/client.test.ts", + "hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9", + "is_verified": false, + "line_number": 83 + } + ], + "src/gateway/gateway-cli-backend.live.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "src/gateway/gateway-cli-backend.live.test.ts", + "hashed_secret": "3e2fd4a90d5afbd27974730c4d6a9592fe300825", + "is_verified": false, + "line_number": 38 + } + ], + "src/gateway/gateway-models.profiles.live.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "src/gateway/gateway-models.profiles.live.test.ts", + "hashed_secret": "3e2fd4a90d5afbd27974730c4d6a9592fe300825", + "is_verified": false, + "line_number": 219 + } + ], + "src/gateway/gateway.e2e.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/gateway/gateway.e2e.test.ts", + "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", + "is_verified": false, + "line_number": 73 + } + ], + "src/gateway/server.auth.e2e.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/gateway/server.auth.e2e.test.ts", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 179 + }, + { + "type": "Secret Keyword", + "filename": "src/gateway/server.auth.e2e.test.ts", + "hashed_secret": "a4b48a81cdab1e1a5dd37907d6c85ca1c61ddc7c", + "is_verified": false, + "line_number": 197 + } + ], + "src/gateway/session-utils.test.ts": [ + { + "type": "Base64 High Entropy String", + "filename": "src/gateway/session-utils.test.ts", + "hashed_secret": "bb9a5d9483409d2c60b28268a0efcb93324d4cda", + "is_verified": false, + "line_number": 156 + } + ], + "src/gateway/tools-invoke-http.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/gateway/tools-invoke-http.test.ts", + "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", + "is_verified": false, + "line_number": 56 + } + ], + "src/gateway/ws-log.test.ts": [ + { + "type": "Base64 High Entropy String", + "filename": "src/gateway/ws-log.test.ts", + "hashed_secret": "edd2e7ac4f61d0c606e80a0919d727540842a307", + "is_verified": false, + "line_number": 22 } ], "src/infra/env.test.ts": [ @@ -465,34 +1958,214 @@ "line_number": 25 } ], + "src/infra/outbound/message-action-runner.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "src/infra/outbound/message-action-runner.test.ts", + "hashed_secret": "804ec071803318791b835cffd6e509c8d32239db", + "is_verified": false, + "line_number": 88 + }, + { + "type": "Secret Keyword", + "filename": "src/infra/outbound/message-action-runner.test.ts", + "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc", + "is_verified": false, + "line_number": 385 + } + ], + "src/infra/outbound/outbound-policy.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "src/infra/outbound/outbound-policy.test.ts", + "hashed_secret": "804ec071803318791b835cffd6e509c8d32239db", + "is_verified": false, + "line_number": 33 + } + ], "src/infra/shell-env.test.ts": [ { "type": "Secret Keyword", "filename": "src/infra/shell-env.test.ts", "hashed_secret": "65c10dc3549fe07424148a8a4790a3341ecbc253", "is_verified": false, - "line_number": 35 - }, - { - "type": "Base64 High Entropy String", - "filename": "src/infra/shell-env.test.ts", - "hashed_secret": "64db6bf7f0e5a0491df4419f0eb1bbcc402989e8", - "is_verified": false, - "line_number": 56 + "line_number": 27 }, { "type": "Secret Keyword", "filename": "src/infra/shell-env.test.ts", "hashed_secret": "e013ffda590d2178607c16d11b1ea42f75ceb0e7", "is_verified": false, - "line_number": 73 + "line_number": 59 }, { "type": "Base64 High Entropy String", "filename": "src/infra/shell-env.test.ts", "hashed_secret": "be6ee9a6bf9f2dad84a5a67d6c0576a5bacc391e", "is_verified": false, - "line_number": 75 + "line_number": 61 + } + ], + "src/logging/redact.test.ts": [ + { + "type": "Base64 High Entropy String", + "filename": "src/logging/redact.test.ts", + "hashed_secret": "dd7754662b89333191ff45e8257a3e6d3fcd3990", + "is_verified": false, + "line_number": 9 + }, + { + "type": "Private Key", + "filename": "src/logging/redact.test.ts", + "hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9", + "is_verified": false, + "line_number": 64 + }, + { + "type": "Hex High Entropy String", + "filename": "src/logging/redact.test.ts", + "hashed_secret": "7992945213f7d76889fa83ff0f2be352409c837e", + "is_verified": false, + "line_number": 65 + }, + { + "type": "Base64 High Entropy String", + "filename": "src/logging/redact.test.ts", + "hashed_secret": "063995ecb4fa5afe2460397d322925cd867b7d74", + "is_verified": false, + "line_number": 79 + } + ], + "src/media-understanding/apply.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/media-understanding/apply.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 14 + } + ], + "src/media-understanding/providers/deepgram/audio.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/media-understanding/providers/deepgram/audio.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 31 + } + ], + "src/media-understanding/providers/google/video.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/media-understanding/providers/google/video.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 28 + } + ], + "src/media-understanding/providers/openai/audio.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/media-understanding/providers/openai/audio.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 26 + } + ], + "src/media-understanding/runner.auto-audio.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/media-understanding/runner.auto-audio.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 42 + } + ], + "src/media-understanding/runner.deepgram.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/media-understanding/runner.deepgram.test.ts", + "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", + "is_verified": false, + "line_number": 46 + } + ], + "src/memory/embeddings.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/memory/embeddings.test.ts", + "hashed_secret": "a47110e348a3063541fb1f1f640d635d457181a0", + "is_verified": false, + "line_number": 32 + }, + { + "type": "Secret Keyword", + "filename": "src/memory/embeddings.test.ts", + "hashed_secret": "c734e47630dda71619c696d88381f06f7511bd78", + "is_verified": false, + "line_number": 149 + }, + { + "type": "Secret Keyword", + "filename": "src/memory/embeddings.test.ts", + "hashed_secret": "56e1d57b8db262b08bc73c60ed08d8c92e59503f", + "is_verified": false, + "line_number": 179 + } + ], + "src/pairing/pairing-store.ts": [ + { + "type": "Base64 High Entropy String", + "filename": "src/pairing/pairing-store.ts", + "hashed_secret": "f8c6f1ff98c5ee78c27d34a3ca68f35ad79847af", + "is_verified": false, + "line_number": 12 + } + ], + "src/security/audit.test.ts": [ + { + "type": "Hex High Entropy String", + "filename": "src/security/audit.test.ts", + "hashed_secret": "b1775a785f09a6ebaf2dc33d6eaeb98974d9cdb8", + "is_verified": false, + "line_number": 180 + }, + { + "type": "Hex High Entropy String", + "filename": "src/security/audit.test.ts", + "hashed_secret": "fa8da98a5bdb77b4902cbb4338e6e94ea825300e", + "is_verified": false, + "line_number": 209 + }, + { + "type": "Secret Keyword", + "filename": "src/security/audit.test.ts", + "hashed_secret": "21f688ab56f76a99e5c6ed342291422f4e57e47f", + "is_verified": false, + "line_number": 1046 + }, + { + "type": "Secret Keyword", + "filename": "src/security/audit.test.ts", + "hashed_secret": "3dc927d80543dc0f643940b70d066bd4b4c4b78e", + "is_verified": false, + "line_number": 1077 + } + ], + "src/tts/tts.test.ts": [ + { + "type": "Secret Keyword", + "filename": "src/tts/tts.test.ts", + "hashed_secret": "2e7a7ee14caebf378fc32d6cf6f557f347c96773", + "is_verified": false, + "line_number": 33 + }, + { + "type": "Hex High Entropy String", + "filename": "src/tts/tts.test.ts", + "hashed_secret": "b214f706bb602c1cc2adc5c6165e73622305f4bb", + "is_verified": false, + "line_number": 68 } ], "src/web/qr-image.test.ts": [ @@ -504,15 +2177,15 @@ "line_number": 12 } ], - "vendor/a2ui/README.md": [ + "test/provider-timeout.e2e.test.ts": [ { "type": "Secret Keyword", - "filename": "vendor/a2ui/README.md", - "hashed_secret": "2619a5397a5d054dab3fe24e6a8da1fbd76ec3a6", + "filename": "test/provider-timeout.e2e.test.ts", + "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", "is_verified": false, - "line_number": 123 + "line_number": 182 } ] }, - "generated_at": "2026-01-05T13:01:00Z" + "generated_at": "2026-01-25T10:55:04Z" } diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 000000000..515f25a5f --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1,25 @@ +# ShellCheck configuration +# https://www.shellcheck.net/wiki/ + +# Disable common false positives and style suggestions + +# SC2034: Variable appears unused (often exported or used indirectly) +disable=SC2034 + +# SC2155: Declare and assign separately (common idiom, rarely causes issues) +disable=SC2155 + +# SC2295: Expansions inside ${..} need quoting (info-level, rarely causes issues) +disable=SC2295 + +# SC1012: \r is literal (tr -d '\r' works as intended on most systems) +disable=SC1012 + +# SC2026: Word outside quotes (info-level, often intentional) +disable=SC2026 + +# SC2016: Expressions don't expand in single quotes (often intentional in sed/awk) +disable=SC2016 + +# SC2129: Consider using { cmd1; cmd2; } >> file (style preference) +disable=SC2129 diff --git a/.swiftformat b/.swiftformat index e0d3bc8b6..fd8c0e631 100644 --- a/.swiftformat +++ b/.swiftformat @@ -23,7 +23,7 @@ # Whitespace --trimwhitespace always --emptybraces no-space ---nospaceoperators ...,..< +--nospaceoperators ...,..< --ranges no-space --someAny true --voidtype void @@ -48,4 +48,4 @@ --allman false # Exclusions ---exclude .build,.swiftpm,DerivedData,node_modules,dist,coverage,xcuserdata,Peekaboo,Swabble,apps/android,apps/ios,apps/shared,apps/macos/Sources/ClawdisProtocol,apps/macos/Sources/ClawdbotProtocol +--exclude .build,.swiftpm,DerivedData,node_modules,dist,coverage,xcuserdata,Peekaboo,Swabble,apps/android,apps/ios,apps/shared,apps/macos/Sources/MoltbotProtocol diff --git a/.swiftlint.yml b/.swiftlint.yml index 12500f4c7..b56228801 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -18,7 +18,7 @@ excluded: - coverage - "*.playground" # Generated (protocol-gen-swift.ts) - - apps/macos/Sources/ClawdbotProtocol/GatewayModels.swift + - apps/macos/Sources/MoltbotProtocol/GatewayModels.swift analyzer_rules: - unused_declaration diff --git a/AGENTS.md b/AGENTS.md index 3751208c8..44b0149fd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,5 @@ # Repository Guidelines -- Repo: https://github.com/clawdbot/clawdbot +- Repo: https://github.com/moltbot/moltbot - GitHub issues/comments/PR comments: use literal multiline strings or `-F - <<'EOF'` (or $'...') for real newlines; never embed "\\n". ## Project Structure & Module Organization @@ -7,39 +7,41 @@ - Tests: colocated `*.test.ts`. - Docs: `docs/` (images, queue, Pi config). Built output lives in `dist/`. - Plugins/extensions: live under `extensions/*` (workspace packages). Keep plugin-only deps in the extension `package.json`; do not add them to the root `package.json` unless core uses them. -- Plugins: install runs `npm install --omit=dev` in plugin dir; runtime deps must live in `dependencies`. Avoid `workspace:*` in `dependencies` (npm install breaks); put `clawdbot` in `devDependencies` or `peerDependencies` instead (runtime resolves `clawdbot/plugin-sdk` via jiti alias). -- Installers served from `https://clawd.bot/*`: live in the sibling repo `../clawd.bot` (`public/install.sh`, `public/install-cli.sh`, `public/install.ps1`). +- Plugins: install runs `npm install --omit=dev` in plugin dir; runtime deps must live in `dependencies`. Avoid `workspace:*` in `dependencies` (npm install breaks); put `moltbot` in `devDependencies` or `peerDependencies` instead (runtime resolves `clawdbot/plugin-sdk` via jiti alias). +- Installers served from `https://molt.bot/*`: live in the sibling repo `../molt.bot` (`public/install.sh`, `public/install-cli.sh`, `public/install.ps1`). - Messaging channels: always consider **all** built-in + extension channels when refactoring shared logic (routing, allowlists, pairing, command gating, onboarding, docs). - Core channel docs: `docs/channels/` - Core channel code: `src/telegram`, `src/discord`, `src/slack`, `src/signal`, `src/imessage`, `src/web` (WhatsApp web), `src/channels`, `src/routing` - Extensions (channel plugins): `extensions/*` (e.g. `extensions/msteams`, `extensions/matrix`, `extensions/zalo`, `extensions/zalouser`, `extensions/voice-call`) +- When adding channels/extensions/apps/docs, review `.github/labeler.yml` for label coverage. ## Docs Linking (Mintlify) -- Docs are hosted on Mintlify (docs.clawd.bot). +- Docs are hosted on Mintlify (docs.molt.bot). - Internal doc links in `docs/**/*.md`: root-relative, no `.md`/`.mdx` (example: `[Config](/configuration)`). - Section cross-references: use anchors on root-relative paths (example: `[Hooks](/configuration#hooks)`). - Doc headings and anchors: avoid em dashes and apostrophes in headings because they break Mintlify anchor links. -- When Peter asks for links, reply with full `https://docs.clawd.bot/...` URLs (not root-relative). -- When you touch docs, end the reply with the `https://docs.clawd.bot/...` URLs you referenced. -- README (GitHub): keep absolute docs URLs (`https://docs.clawd.bot/...`) so links work on GitHub. +- When Peter asks for links, reply with full `https://docs.molt.bot/...` URLs (not root-relative). +- When you touch docs, end the reply with the `https://docs.molt.bot/...` URLs you referenced. +- README (GitHub): keep absolute docs URLs (`https://docs.molt.bot/...`) so links work on GitHub. - Docs content must be generic: no personal device names/hostnames/paths; use placeholders like `user@gateway-host` and “gateway host”. ## exe.dev VM ops (general) - Access: stable path is `ssh exe.dev` then `ssh vm-name` (assume SSH key already set). - SSH flaky: use exe.dev web terminal or Shelley (web agent); keep a tmux session for long ops. -- Update: `sudo npm i -g clawdbot@latest` (global install needs root on `/usr/lib/node_modules`). -- Config: use `clawdbot config set ...`; ensure `gateway.mode=local` is set. +- Update: `sudo npm i -g moltbot@latest` (global install needs root on `/usr/lib/node_modules`). +- Config: use `moltbot config set ...`; ensure `gateway.mode=local` is set. - Discord: store raw token only (no `DISCORD_BOT_TOKEN=` prefix). - Restart: stop old gateway and run: - `pkill -9 -f clawdbot-gateway || true; nohup clawdbot gateway run --bind loopback --port 18789 --force > /tmp/clawdbot-gateway.log 2>&1 &` -- Verify: `clawdbot channels status --probe`, `ss -ltnp | rg 18789`, `tail -n 120 /tmp/clawdbot-gateway.log`. + `pkill -9 -f moltbot-gateway || true; nohup moltbot gateway run --bind loopback --port 18789 --force > /tmp/moltbot-gateway.log 2>&1 &` +- Verify: `moltbot channels status --probe`, `ss -ltnp | rg 18789`, `tail -n 120 /tmp/moltbot-gateway.log`. ## Build, Test, and Development Commands - Runtime baseline: Node **22+** (keep Node + Bun paths working). - Install deps: `pnpm install` +- Pre-commit hooks: `prek install` (runs same checks as CI) - Also supported: `bun install` (keep `pnpm-lock.yaml` + Bun patching in sync when touching deps/patches). - Prefer Bun for TypeScript execution (scripts, dev, tests): `bun ` / `bunx `. -- Run CLI in dev: `pnpm clawdbot ...` (bun) or `pnpm dev`. +- Run CLI in dev: `pnpm moltbot ...` (bun) or `pnpm dev`. - Node remains supported for running built output (`dist/*`) and production installs. - Mac packaging (dev): `scripts/package-mac-app.sh` defaults to current arch. Release checklist: `docs/platforms/mac/release.md`. - Type-check/build: `pnpm build` (tsc) @@ -52,7 +54,7 @@ - Add brief code comments for tricky or non-obvious logic. - Keep files concise; extract helpers instead of “V2” copies. Use existing patterns for CLI options and dependency injection via `createDefaultDeps`. - Aim to keep files under ~700 LOC; guideline only (not a hard guardrail). Split/refactor when it improves clarity or testability. -- Naming: use **Clawdbot** for product/app/docs headings; use `clawdbot` for CLI command, package/binary, paths, and config keys. +- Naming: use **Moltbot** for product/app/docs headings; use `moltbot` for CLI command, package/binary, paths, and config keys. ## Release Channels (Naming) - stable: tagged releases only (e.g. `vYYYY.M.D`), npm dist-tag `latest`. @@ -64,7 +66,7 @@ - Naming: match source names with `*.test.ts`; e2e in `*.e2e.test.ts`. - Run `pnpm test` (or `pnpm test:coverage`) before pushing when you touch logic. - Do not set test workers above 16; tried already. -- Live tests (real keys): `CLAWDBOT_LIVE_TEST=1 pnpm test:live` (Clawdbot-only) or `LIVE=1 pnpm test:live` (includes provider live tests). Docker: `pnpm test:docker:live-models`, `pnpm test:docker:live-gateway`. Onboarding Docker E2E: `pnpm test:docker:onboard`. +- Live tests (real keys): `CLAWDBOT_LIVE_TEST=1 pnpm test:live` (Moltbot-only) or `LIVE=1 pnpm test:live` (includes provider live tests). Docker: `pnpm test:docker:live-models`, `pnpm test:docker:live-gateway`. Onboarding Docker E2E: `pnpm test:docker:onboard`. - Full kit + what’s covered: `docs/testing.md`. - Pure test additions/fixes generally do **not** need a changelog entry unless they alter user-facing behavior or the user asks for one. - Mobile: before using a simulator, check for connected real devices (iOS + Android) and prefer them when available. @@ -95,19 +97,19 @@ - **Landing mode:** create an integration branch from `main`, bring in PR commits (**prefer rebase** for linear history; **merge allowed** when complexity/conflicts make it safer), apply fixes, add changelog (+ thanks + PR #), run full gate **locally before committing** (`pnpm lint && pnpm build && pnpm test`), commit, merge back to `main`, then `git switch main` (never stay on a topic branch after landing). Important: contributor needs to be in git graph after this! ## Security & Configuration Tips -- Web provider stores creds at `~/.clawdbot/credentials/`; rerun `clawdbot login` if logged out. +- Web provider stores creds at `~/.clawdbot/credentials/`; rerun `moltbot login` if logged out. - Pi sessions live under `~/.clawdbot/sessions/` by default; the base directory is not configurable. - Environment variables: see `~/.profile`. - Never commit or publish real phone numbers, videos, or live configuration values. Use obviously fake placeholders in docs, tests, and examples. - Release flow: always read `docs/reference/RELEASING.md` and `docs/platforms/mac/release.md` before any release work; do not ask routine questions once those docs answer them. ## Troubleshooting -- Rebrand/migration issues or legacy config/service warnings: run `clawdbot doctor` (see `docs/gateway/doctor.md`). +- Rebrand/migration issues or legacy config/service warnings: run `moltbot doctor` (see `docs/gateway/doctor.md`). ## Agent-Specific Notes - Vocabulary: "makeup" = "mac app". - Never edit `node_modules` (global/Homebrew/npm/git installs too). Updates overwrite. Skill notes go in `tools.md` or `AGENTS.md`. -- Signal: "update fly" => `fly ssh console -a flawd-bot -C "bash -lc 'cd /data/clawd/clawdbot && git pull --rebase origin main'"` then `fly machines restart e825232f34d058 -a flawd-bot`. +- Signal: "update fly" => `fly ssh console -a flawd-bot -C "bash -lc 'cd /data/clawd/moltbot && git pull --rebase origin main'"` then `fly machines restart e825232f34d058 -a flawd-bot`. - When working on a GitHub Issue or PR, print the full URL at the end of the task. - When answering questions, respond with high-confidence answers only: verify in code; do not guess. - Never update the Carbon dependency. @@ -115,12 +117,12 @@ - Patching dependencies (pnpm patches, overrides, or vendored changes) requires explicit approval; do not do this by default. - CLI progress: use `src/cli/progress.ts` (`osc-progress` + `@clack/prompts` spinner); don’t hand-roll spinners/bars. - Status output: keep tables + ANSI-safe wrapping (`src/terminal/table.ts`); `status --all` = read-only/pasteable, `status --deep` = probes. -- Gateway currently runs only as the menubar app; there is no separate LaunchAgent/helper label installed. Restart via the Clawdbot Mac app or `scripts/restart-mac.sh`; to verify/kill use `launchctl print gui/$UID | grep clawdbot` rather than assuming a fixed label. **When debugging on macOS, start/stop the gateway via the app, not ad-hoc tmux sessions; kill any temporary tunnels before handoff.** -- macOS logs: use `./scripts/clawlog.sh` to query unified logs for the Clawdbot subsystem; it supports follow/tail/category filters and expects passwordless sudo for `/usr/bin/log`. +- Gateway currently runs only as the menubar app; there is no separate LaunchAgent/helper label installed. Restart via the Moltbot Mac app or `scripts/restart-mac.sh`; to verify/kill use `launchctl print gui/$UID | grep moltbot` rather than assuming a fixed label. **When debugging on macOS, start/stop the gateway via the app, not ad-hoc tmux sessions; kill any temporary tunnels before handoff.** +- macOS logs: use `./scripts/clawlog.sh` to query unified logs for the Moltbot subsystem; it supports follow/tail/category filters and expects passwordless sudo for `/usr/bin/log`. - If shared guardrails are available locally, review them; otherwise follow this repo's guidance. - SwiftUI state management (iOS/macOS): prefer the `Observation` framework (`@Observable`, `@Bindable`) over `ObservableObject`/`@StateObject`; don’t introduce new `ObservableObject` unless required for compatibility, and migrate existing usages when touching related code. - Connection providers: when adding a new connection, update every UI surface and docs (macOS app, web UI, mobile if applicable, onboarding/overview docs) and add matching status + configuration forms so provider lists and settings stay in sync. -- Version locations: `package.json` (CLI), `apps/android/app/build.gradle.kts` (versionName/versionCode), `apps/ios/Sources/Info.plist` + `apps/ios/Tests/Info.plist` (CFBundleShortVersionString/CFBundleVersion), `apps/macos/Sources/Clawdbot/Resources/Info.plist` (CFBundleShortVersionString/CFBundleVersion), `docs/install/updating.md` (pinned npm version), `docs/platforms/mac/release.md` (APP_VERSION/APP_BUILD examples), Peekaboo Xcode projects/Info.plists (MARKETING_VERSION/CURRENT_PROJECT_VERSION). +- Version locations: `package.json` (CLI), `apps/android/app/build.gradle.kts` (versionName/versionCode), `apps/ios/Sources/Info.plist` + `apps/ios/Tests/Info.plist` (CFBundleShortVersionString/CFBundleVersion), `apps/macos/Sources/Moltbot/Resources/Info.plist` (CFBundleShortVersionString/CFBundleVersion), `docs/install/updating.md` (pinned npm version), `docs/platforms/mac/release.md` (APP_VERSION/APP_BUILD examples), Peekaboo Xcode projects/Info.plists (MARKETING_VERSION/CURRENT_PROJECT_VERSION). - **Restart apps:** “restart iOS/Android apps” means rebuild (recompile/install) and relaunch, not just kill/launch. - **Device checks:** before testing, verify connected real devices (iOS/Android) before reaching for simulators/emulators. - iOS Team ID lookup: `security find-identity -p codesigning -v` → use Apple Development (…) TEAMID. Fallback: `defaults read com.apple.dt.Xcode IDEProvisioningTeamIdentifiers`. @@ -147,9 +149,9 @@ - Do not rebuild the macOS app over SSH; rebuilds must be run directly on the Mac. - Never send streaming/partial replies to external messaging surfaces (WhatsApp, Telegram); only final replies should be delivered there. Streaming/tool events may still go to internal UIs/control channel. - Voice wake forwarding tips: - - Command template should stay `clawdbot-mac agent --message "${text}" --thinking low`; `VoiceWakeForwarder` already shell-escapes `${text}`. Don’t add extra quotes. - - launchd PATH is minimal; ensure the app’s launch agent PATH includes standard system paths plus your pnpm bin (typically `$HOME/Library/pnpm`) so `pnpm`/`clawdbot` binaries resolve when invoked via `clawdbot-mac`. -- For manual `clawdbot message send` messages that include `!`, use the heredoc pattern noted below to avoid the Bash tool’s escaping. + - Command template should stay `moltbot-mac agent --message "${text}" --thinking low`; `VoiceWakeForwarder` already shell-escapes `${text}`. Don’t add extra quotes. + - launchd PATH is minimal; ensure the app’s launch agent PATH includes standard system paths plus your pnpm bin (typically `$HOME/Library/pnpm`) so `pnpm`/`moltbot` binaries resolve when invoked via `moltbot-mac`. +- For manual `moltbot message send` messages that include `!`, use the heredoc pattern noted below to avoid the Bash tool’s escaping. - Release guardrails: do not change version numbers without operator’s explicit consent; always ask permission before running any npm publish/release step. ## NPM + 1Password (publish/verify) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59ce99755..9e503882d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,51 +1,193 @@ # Changelog -Docs: https://docs.clawd.bot +Docs: https://docs.molt.bot + +## 2026.1.27-beta.1 +Status: beta. + +### Changes +- Rebrand: rename the npm package/CLI to `moltbot`, add a `moltbot` compatibility shim, and move extensions to the `@moltbot/*` scope. +- Commands: group /help and /commands output with Telegram paging. (#2504) Thanks @hougangdev. +- macOS: limit project-local `node_modules/.bin` PATH preference to debug builds (reduce PATH hijacking risk). +- macOS: finish Moltbot app rename for macOS sources, bundle identifiers, and shared kit paths. (#2844) Thanks @fal3. +- Branding: update launchd labels, mobile bundle IDs, and logging subsystems to bot.molt (legacy com.clawdbot migrations). Thanks @thewilloftheshadow. +- Tools: add per-sender group tool policies and fix precedence. (#1757) Thanks @adam91holt. +- Agents: summarize dropped messages during compaction safeguard pruning. (#2509) Thanks @jogi47. +- Skills: add multi-image input support to Nano Banana Pro skill. (#1958) Thanks @tyler6204. +- Agents: honor tools.exec.safeBins in exec allowlist checks. (#2281) +- Matrix: switch plugin SDK to @vector-im/matrix-bot-sdk. +- Docs: tighten Fly private deployment steps. (#2289) Thanks @dguido. +- Docs: add migration guide for moving to a new machine. (#2381) +- Docs: add Northflank one-click deployment guide. (#2167) Thanks @AdeboyeDN. +- Gateway: warn on hook tokens via query params; document header auth preference. (#2200) Thanks @YuriNachos. +- Gateway: add dangerous Control UI device auth bypass flag + audit warnings. (#2248) +- Doctor: warn on gateway exposure without auth. (#2016) Thanks @Alex-Alaniz. +- Config: auto-migrate legacy state/config paths and keep config resolution consistent across legacy filenames. +- Discord: add configurable privileged gateway intents for presences/members. (#2266) Thanks @kentaro. +- Docs: add Vercel AI Gateway to providers sidebar. (#1901) Thanks @jerilynzheng. +- Agents: expand cron tool description with full schema docs. (#1988) Thanks @tomascupr. +- Skills: add missing dependency metadata for GitHub, Notion, Slack, Discord. (#1995) Thanks @jackheuberger. +- Docs: add Render deployment guide. (#1975) Thanks @anurag. +- Docs: add Claude Max API Proxy guide. (#1875) Thanks @atalovesyou. +- Docs: add DigitalOcean deployment guide. (#1870) Thanks @0xJonHoldsCrypto. +- Docs: add Oracle Cloud (OCI) platform guide + cross-links. (#2333) Thanks @hirefrank. +- Docs: add Raspberry Pi install guide. (#1871) Thanks @0xJonHoldsCrypto. +- Docs: add GCP Compute Engine deployment guide. (#1848) Thanks @hougangdev. +- Docs: add LINE channel guide. Thanks @thewilloftheshadow. +- Docs: credit both contributors for Control UI refresh. (#1852) Thanks @EnzeD. +- Onboarding: add Venice API key to non-interactive flow. (#1893) Thanks @jonisjongithub. +- Onboarding: strengthen security warning copy for beta + access control expectations. +- Tlon: format thread reply IDs as @ud. (#1837) Thanks @wca4a. +- Gateway: prefer newest session metadata when combining stores. (#1823) Thanks @emanuelst. +- Web UI: keep sub-agent announce replies visible in WebChat. (#1977) Thanks @andrescardonas7. +- CI: increase Node heap size for macOS checks. (#1890) Thanks @realZachi. +- macOS: avoid crash when rendering code blocks by bumping Textual to 0.3.1. (#2033) Thanks @garricn. +- Browser: fall back to URL matching for extension relay target resolution. (#1999) Thanks @jonit-dev. +- Browser: route browser control via gateway/node; remove standalone browser control command and control URL config. +- Browser: route `browser.request` via node proxies when available; honor proxy timeouts; derive browser ports from `gateway.port`. +- Update: ignore dist/control-ui for dirty checks and restore after ui builds. (#1976) Thanks @Glucksberg. +- Build: bundle A2UI assets during build and stop tracking generated bundles. (#2455) Thanks @0oAstro. +- Telegram: allow caption param for media sends. (#1888) Thanks @mguellsegarra. +- Telegram: support plugin sendPayload channelData (media/buttons) and validate plugin commands. (#1917) Thanks @JoshuaLelon. +- Telegram: avoid block replies when streaming is disabled. (#1885) Thanks @ivancasco. +- Docs: keep docs header sticky so navbar stays visible while scrolling. (#2445) Thanks @chenyuan99. +- Docs: update exe.dev install instructions. (#https://github.com/moltbot/moltbot/pull/3047) Thanks @zackerthescar. +- Security: use Windows ACLs for permission audits and fixes on Windows. (#1957) +- Auth: show copyable Google auth URL after ASCII prompt. (#1787) Thanks @robbyczgw-cla. +- Routing: precompile session key regexes. (#1697) Thanks @Ray0907. +- TUI: avoid width overflow when rendering selection lists. (#1686) Thanks @mossein. +- Telegram: keep topic IDs in restart sentinel notifications. (#1807) Thanks @hsrvc. +- Telegram: add optional silent send flag (disable notifications). (#2382) Thanks @Suksham-sharma. +- Telegram: support editing sent messages via message(action="edit"). (#2394) Thanks @marcelomar21. +- Telegram: support quote replies for message tool and inbound context. (#2900) Thanks @aduk059. +- Telegram: add sticker receive/send with vision caching. (#2629) Thanks @longjos. +- Telegram: send sticker pixels to vision models. (#2650) +- Config: apply config.env before ${VAR} substitution. (#1813) Thanks @spanishflu-est1918. +- Slack: clear ack reaction after streamed replies. (#2044) Thanks @fancyboi999. +- macOS: keep custom SSH usernames in remote target. (#2046) Thanks @algal. +- CLI: use Node's module compile cache for faster startup. (#2808) Thanks @pi0. + +### Breaking +- **BREAKING:** Gateway auth mode "none" is removed; gateway now requires token/password (Tailscale Serve identity still allowed). + +### Fixes +- Agents: prevent retries on oversized image errors and surface size limits. (#2871) Thanks @Suksham-sharma. +- Agents: inherit provider baseUrl/api for inline models. (#2740) Thanks @lploc94. +- Memory Search: keep auto provider model defaults and only include remote when configured. (#2576) Thanks @papago2355. +- macOS: auto-scroll to bottom when sending a new message while scrolled up. (#2471) Thanks @kennyklee. +- Web UI: auto-expand the chat compose textarea while typing (with sensible max height). (#2950) Thanks @shivamraut101. +- Gateway: prevent crashes on transient network errors (fetch failures, timeouts, DNS). Added fatal error detection to only exit on truly critical errors. Fixes #2895, #2879, #2873. (#2980) Thanks @elliotsecops. +- Agents: guard channel tool listActions to avoid plugin crashes. (#2859) Thanks @mbelinky. +- Providers: update MiniMax API endpoint and compatibility mode. (#3064) Thanks @hlbbbbbbb. +- Telegram: treat more network errors as recoverable in polling. (#3013) Thanks @ryancontent. +- Gateway: suppress AbortError and transient network errors in unhandled rejections. (#2451) Thanks @Glucksberg. +- TTS: keep /tts status replies on text-only commands and avoid duplicate block-stream audio. (#2451) Thanks @Glucksberg. +- Security: pin npm overrides to keep tar@7.5.4 for install toolchains. +- Security: properly test Windows ACL audit for config includes. (#2403) Thanks @dominicnunez. +- CLI: recognize versioned Node executables when parsing argv. (#2490) Thanks @David-Marsh-Photo. +- CLI: avoid prompting for gateway runtime under the spinner. (#2874) +- BlueBubbles: coalesce inbound URL link preview messages. (#1981) Thanks @tyler6204. +- Cron: allow payloads containing "heartbeat" in event filter. (#2219) Thanks @dwfinkelstein. +- CLI: avoid loading config for global help/version while registering plugin commands. (#2212) Thanks @dial481. +- Agents: include memory.md when bootstrapping memory context. (#2318) Thanks @czekaj. +- Agents: release session locks on process termination and cover more signals. (#2483) Thanks @janeexai. +- Agents: skip cooldowned providers during model failover. (#2143) Thanks @YiWang24. +- Telegram: harden polling + retry behavior for transient network errors and Node 22 transport issues. (#2420) Thanks @techboss. +- Telegram: wrap reasoning italics per line to avoid raw underscores. (#2181) Thanks @YuriNachos. +- Telegram: centralize API error logging for delivery and bot calls. (#2492) Thanks @altryne. +- Voice Call: enforce Twilio webhook signature verification for ngrok URLs; disable ngrok free tier bypass by default. +- Security: harden Tailscale Serve auth by validating identity via local tailscaled before trusting headers. +- Build: align memory-core peer dependency with lockfile. +- Security: add mDNS discovery mode with minimal default to reduce information disclosure. (#1882) Thanks @orlyjamie. +- Security: harden URL fetches with DNS pinning to reduce rebinding risk. Thanks Chris Zheng. +- Web UI: improve WebChat image paste previews and allow image-only sends. (#1925) Thanks @smartprogrammer93. +- Security: wrap external hook content by default with a per-hook opt-out. (#1827) Thanks @mertcicekci0. +- Gateway: default auth now fail-closed (token/password required; Tailscale Serve identity remains allowed). +- Gateway: treat loopback + non-local Host connections as remote unless trusted proxy headers are present. +- Onboarding: remove unsupported gateway auth "off" choice from onboarding/configure flows and CLI flags. + +## 2026.1.24-3 + +### Fixes +- Slack: fix image downloads failing due to missing Authorization header on cross-origin redirects. (#1936) Thanks @sanderhelgesen. +- Gateway: harden reverse proxy handling for local-client detection and unauthenticated proxied connects. (#1795) Thanks @orlyjamie. +- Security audit: flag loopback Control UI with auth disabled as critical. (#1795) Thanks @orlyjamie. +- CLI: resume claude-cli sessions and stream CLI replies to TUI clients. (#1921) Thanks @rmorse. + +## 2026.1.24-2 + +### Fixes +- Packaging: include dist/link-understanding output in npm tarball (fixes missing apply.js import on install). + +## 2026.1.24-1 + +### Fixes +- Packaging: include dist/shared output in npm tarball (fixes missing reasoning-tags import on install). ## 2026.1.24 ### Highlights -- Ollama: provider discovery + docs. (#1606) Thanks @abhaymundhara. https://docs.clawd.bot/providers/ollama -- Venius (Venice AI): highlight provider guide + cross-links + expanded guidance. https://docs.clawd.bot/providers/venice +- Providers: Ollama discovery + docs; Venice guide upgrades + cross-links. (#1606) Thanks @abhaymundhara. https://docs.molt.bot/providers/ollama https://docs.molt.bot/providers/venice +- Channels: LINE plugin (Messaging API) with rich replies + quick replies. (#1630) Thanks @plum-dawg. +- TTS: Edge fallback (keyless) + `/tts` auto modes. (#1668, #1667) Thanks @steipete, @sebslight. https://docs.molt.bot/tts +- Exec approvals: approve in-chat via `/approve` across all channels (including plugins). (#1621) Thanks @czekaj. https://docs.molt.bot/tools/exec-approvals https://docs.molt.bot/tools/slash-commands +- Telegram: DM topics as separate sessions + outbound link preview toggle. (#1597, #1700) Thanks @rohannagpal, @zerone0x. https://docs.molt.bot/channels/telegram ### Changes -- TTS: add Edge TTS provider fallback, defaulting to keyless Edge with MP3 retry on format failures. (#1668) Thanks @steipete. https://docs.clawd.bot/tts -- Web search: add Brave freshness filter parameter for time-scoped results. (#1688) Thanks @JonUleis. https://docs.clawd.bot/tools/web -- TTS: add auto mode enum (off/always/inbound/tagged) with per-session `/tts` override. (#1667) Thanks @sebslight. https://docs.clawd.bot/tts +- Channels: add LINE plugin (Messaging API) with rich replies, quick replies, and plugin HTTP registry. (#1630) Thanks @plum-dawg. +- TTS: add Edge TTS provider fallback, defaulting to keyless Edge with MP3 retry on format failures. (#1668) Thanks @steipete. https://docs.molt.bot/tts +- TTS: add auto mode enum (off/always/inbound/tagged) with per-session `/tts` override. (#1667) Thanks @sebslight. https://docs.molt.bot/tts +- Telegram: treat DM topics as separate sessions and keep DM history limits stable with thread suffixes. (#1597) Thanks @rohannagpal. +- Telegram: add `channels.telegram.linkPreview` to toggle outbound link previews. (#1700) Thanks @zerone0x. https://docs.molt.bot/channels/telegram +- Web search: add Brave freshness filter parameter for time-scoped results. (#1688) Thanks @JonUleis. https://docs.molt.bot/tools/web +- UI: refresh Control UI dashboard design system (colors, icons, typography). (#1745, #1786) Thanks @EnzeD, @mousberg. +- Exec approvals: forward approval prompts to chat with `/approve` for all channels (including plugins). (#1621) Thanks @czekaj. https://docs.molt.bot/tools/exec-approvals https://docs.molt.bot/tools/slash-commands +- Gateway: expose config.patch in the gateway tool with safe partial updates + restart sentinel. (#1653) Thanks @Glucksberg. +- Diagnostics: add diagnostic flags for targeted debug logs (config + env override). https://docs.molt.bot/diagnostics/flags - Docs: expand FAQ (migration, scheduling, concurrency, model recommendations, OpenAI subscription auth, Pi sizing, hackable install, docs SSL workaround). - Docs: add verbose installer troubleshooting guidance. - Docs: add macOS VM guide with local/hosted options + VPS/nodes guidance. (#1693) Thanks @f-trycua. +- Docs: add Bedrock EC2 instance role setup + IAM steps. (#1625) Thanks @sergical. https://docs.molt.bot/bedrock - Docs: update Fly.io guide notes. -- Docs: add Bedrock EC2 instance role setup + IAM steps. (#1625) Thanks @sergical. https://docs.clawd.bot/bedrock -- Exec approvals: forward approval prompts to chat with `/approve` for all channels (including plugins). (#1621) Thanks @czekaj. https://docs.clawd.bot/tools/exec-approvals https://docs.clawd.bot/tools/slash-commands -- Gateway: expose config.patch in the gateway tool with safe partial updates + restart sentinel. (#1653) Thanks @Glucksberg. -- Telegram: treat DM topics as separate sessions and keep DM history limits stable with thread suffixes. (#1597) Thanks @rohannagpal. -- Telegram: add verbose raw-update logging for inbound Telegram updates. (#1597) Thanks @rohannagpal. +- Dev: add prek pre-commit hooks + dependabot config for weekly updates. (#1720) Thanks @dguido. ### Fixes -- BlueBubbles: keep part-index GUIDs in reply tags when short IDs are missing. -- Web UI: hide internal `message_id` hints in chat bubbles. +- Web UI: fix config/debug layout overflow, scrolling, and code block sizing. (#1715) Thanks @saipreetham589. - Web UI: show Stop button during active runs, swap back to New session when idle. (#1664) Thanks @ndbroadbent. - Web UI: clear stale disconnect banners on reconnect; allow form saves with unsupported schema paths but block missing schema. (#1707) Thanks @Glucksberg. -- Heartbeat: normalize target identifiers for consistent routing. -- TUI: reload history after gateway reconnect to restore session state. (#1663) -- Telegram: use wrapped fetch for long-polling on Node to normalize AbortSignal handling. (#1639) -- Telegram: set fetch duplex="half" for uploads on Node 22 to avoid sendPhoto failures. (#1684) Thanks @commdata2338. +- Web UI: hide internal `message_id` hints in chat bubbles. +- Gateway: allow Control UI token-only auth to skip device pairing even when device identity is present (`gateway.controlUi.allowInsecureAuth`). (#1679) Thanks @steipete. +- Matrix: decrypt E2EE media attachments with preflight size guard. (#1744) Thanks @araa47. +- BlueBubbles: route phone-number targets to DMs, avoid leaking routing IDs, and auto-create missing DMs (Private API required). (#1751) Thanks @tyler6204. https://docs.molt.bot/channels/bluebubbles +- BlueBubbles: keep part-index GUIDs in reply tags when short IDs are missing. +- iMessage: normalize chat_id/chat_guid/chat_identifier prefixes case-insensitively and keep service-prefixed handles stable. (#1708) Thanks @aaronn. - Signal: repair reaction sends (group/UUID targets + CLI author flags). (#1651) Thanks @vilkasdev. -- Signal: add configurable signal-cli startup timeout + external daemon mode docs. (#1677) https://docs.clawd.bot/channels/signal -- Exec: keep approvals for elevated ask unless full mode. (#1616) Thanks @ivancasco. +- Signal: add configurable signal-cli startup timeout + external daemon mode docs. (#1677) https://docs.molt.bot/channels/signal +- Telegram: set fetch duplex="half" for uploads on Node 22 to avoid sendPhoto failures. (#1684) Thanks @commdata2338. +- Telegram: use wrapped fetch for long-polling on Node to normalize AbortSignal handling. (#1639) +- Telegram: honor per-account proxy for outbound API calls. (#1774) Thanks @radek-paclt. +- Telegram: fall back to text when voice notes are blocked by privacy settings. (#1725) Thanks @foeken. +- Voice Call: return stream TwiML for outbound conversation calls on initial Twilio webhook. (#1634) +- Voice Call: serialize Twilio TTS playback and cancel on barge-in to prevent overlap. (#1713) Thanks @dguido. +- Google Chat: tighten email allowlist matching, typing cleanup, media caps, and onboarding/docs/tests. (#1635) Thanks @iHildy. +- Google Chat: normalize space targets without double `spaces/` prefix. - Agents: auto-compact on context overflow prompt errors before failing. (#1627) Thanks @rodrigouroz. - Agents: use the active auth profile for auto-compaction recovery. +- Media understanding: skip image understanding when the primary model already supports vision. (#1747) Thanks @tyler6204. - Models: default missing custom provider fields so minimal configs are accepted. +- Messaging: keep newline chunking safe for fenced markdown blocks across channels. +- Messaging: treat newline chunking as paragraph-aware (blank-line splits) to keep lists and headings together. (#1726) Thanks @tyler6204. +- TUI: reload history after gateway reconnect to restore session state. (#1663) +- Heartbeat: normalize target identifiers for consistent routing. +- Exec: keep approvals for elevated ask unless full mode. (#1616) Thanks @ivancasco. +- Exec: treat Windows platform labels as Windows for node shell selection. (#1760) Thanks @ymat19. +- Gateway: include inline config env vars in service install environments. (#1735) Thanks @Seredeep. - Gateway: skip Tailscale DNS probing when tailscale.mode is off. (#1671) - Gateway: reduce log noise for late invokes + remote node probes; debounce skills refresh. (#1607) Thanks @petter-b. - Gateway: clarify Control UI/WebChat auth error hints for missing tokens. (#1690) - Gateway: listen on IPv6 loopback when bound to 127.0.0.1 so localhost webhooks work. +- Gateway: store lock files in the temp directory to avoid stale locks on persistent volumes. (#1676) - macOS: default direct-transport `ws://` URLs to port 18789; document `gateway.remote.transport`. (#1603) Thanks @ngutman. -- Voice Call: return stream TwiML for outbound conversation calls on initial Twilio webhook. (#1634) -- Google Chat: tighten email allowlist matching, typing cleanup, media caps, and onboarding/docs/tests. (#1635) Thanks @iHildy. -- Google Chat: normalize space targets without double `spaces/` prefix. -- Messaging: keep newline chunking safe for fenced markdown blocks across channels. - Tests: cap Vitest workers on CI macOS to reduce timeouts. (#1597) Thanks @rohannagpal. - Tests: avoid fake-timer dependency in embedded runner stream mock to reduce CI flakes. (#1597) Thanks @rohannagpal. - Tests: increase embedded runner ordering test timeout to reduce CI flakes. (#1597) Thanks @rohannagpal. @@ -58,25 +200,25 @@ Docs: https://docs.clawd.bot ## 2026.1.23 ### Highlights -- TTS: move Telegram TTS into core + enable model-driven TTS tags by default for expressive audio replies. (#1559) Thanks @Glucksberg. https://docs.clawd.bot/tts -- Gateway: add `/tools/invoke` HTTP endpoint for direct tool calls (auth + tool policy enforced). (#1575) Thanks @vignesh07. https://docs.clawd.bot/gateway/tools-invoke-http-api -- Heartbeat: per-channel visibility controls (OK/alerts/indicator). (#1452) Thanks @dlauer. https://docs.clawd.bot/gateway/heartbeat -- Deploy: add Fly.io deployment support + guide. (#1570) https://docs.clawd.bot/platforms/fly -- Channels: add Tlon/Urbit channel plugin (DMs, group mentions, thread replies). (#1544) Thanks @wca4a. https://docs.clawd.bot/channels/tlon +- TTS: move Telegram TTS into core + enable model-driven TTS tags by default for expressive audio replies. (#1559) Thanks @Glucksberg. https://docs.molt.bot/tts +- Gateway: add `/tools/invoke` HTTP endpoint for direct tool calls (auth + tool policy enforced). (#1575) Thanks @vignesh07. https://docs.molt.bot/gateway/tools-invoke-http-api +- Heartbeat: per-channel visibility controls (OK/alerts/indicator). (#1452) Thanks @dlauer. https://docs.molt.bot/gateway/heartbeat +- Deploy: add Fly.io deployment support + guide. (#1570) https://docs.molt.bot/platforms/fly +- Channels: add Tlon/Urbit channel plugin (DMs, group mentions, thread replies). (#1544) Thanks @wca4a. https://docs.molt.bot/channels/tlon ### Changes -- Channels: allow per-group tool allow/deny policies across built-in + plugin channels. (#1546) Thanks @adam91holt. https://docs.clawd.bot/multi-agent-sandbox-tools -- Agents: add Bedrock auto-discovery defaults + config overrides. (#1553) Thanks @fal3. https://docs.clawd.bot/bedrock -- CLI: add `clawdbot system` for system events + heartbeat controls; remove standalone `wake`. (commit 71203829d) https://docs.clawd.bot/cli/system -- CLI: add live auth probes to `clawdbot models status` for per-profile verification. (commit 40181afde) https://docs.clawd.bot/cli/models -- CLI: restart the gateway by default after `clawdbot update`; add `--no-restart` to skip it. (commit 2c85b1b40) +- Channels: allow per-group tool allow/deny policies across built-in + plugin channels. (#1546) Thanks @adam91holt. https://docs.molt.bot/multi-agent-sandbox-tools +- Agents: add Bedrock auto-discovery defaults + config overrides. (#1553) Thanks @fal3. https://docs.molt.bot/bedrock +- CLI: add `moltbot system` for system events + heartbeat controls; remove standalone `wake`. (commit 71203829d) https://docs.molt.bot/cli/system +- CLI: add live auth probes to `moltbot models status` for per-profile verification. (commit 40181afde) https://docs.molt.bot/cli/models +- CLI: restart the gateway by default after `moltbot update`; add `--no-restart` to skip it. (commit 2c85b1b40) - Browser: add node-host proxy auto-routing for remote gateways (configurable per gateway/node). (commit c3cb26f7c) -- Plugins: add optional `llm-task` JSON-only tool for workflows. (#1498) Thanks @vignesh07. https://docs.clawd.bot/tools/llm-task +- Plugins: add optional `llm-task` JSON-only tool for workflows. (#1498) Thanks @vignesh07. https://docs.molt.bot/tools/llm-task - Markdown: add per-channel table conversion (bullets for Signal/WhatsApp, code blocks elsewhere). (#1495) Thanks @odysseus0. - Agents: keep system prompt time zone-only and move current time to `session_status` for better cache hits. (commit 66eec295b) - Agents: remove redundant bash tool alias from tool registration/display. (#1571) Thanks @Takhoffman. -- Docs: add cron vs heartbeat decision guide (with Lobster workflow notes). (#1533) Thanks @JustYannicc. https://docs.clawd.bot/automation/cron-vs-heartbeat -- Docs: clarify HEARTBEAT.md empty file skips heartbeats, missing file still runs. (#1535) Thanks @JustYannicc. https://docs.clawd.bot/gateway/heartbeat +- Docs: add cron vs heartbeat decision guide (with Lobster workflow notes). (#1533) Thanks @JustYannicc. https://docs.molt.bot/automation/cron-vs-heartbeat +- Docs: clarify HEARTBEAT.md empty file skips heartbeats, missing file still runs. (#1535) Thanks @JustYannicc. https://docs.molt.bot/gateway/heartbeat ### Fixes - Sessions: accept non-UUID sessionIds for history/send/status while preserving agent scoping. (#1518) @@ -109,7 +251,7 @@ Docs: https://docs.clawd.bot - UI: keep the Control UI sidebar visible while scrolling long pages. (#1515) Thanks @pookNast. - UI: cache Control UI markdown rendering + memoize chat text extraction to reduce Safari typing jank. (commit d57cb2e1a) - TUI: forward unknown slash commands, include Gateway commands in autocomplete, and render slash replies as system output. (commit 1af227b61, commit 8195497ce, commit 6fba598ea) -- CLI: auth probe output polish (table output, inline errors, reduced noise, and wrap fixes in `clawdbot models status`). (commit da3f2b489, commit 00ae21bed, commit 31e59cd58, commit f7dc27f2d, commit 438e782f8, commit 886752217, commit aabe0bed3, commit 81535d512, commit c63144ab1) +- CLI: auth probe output polish (table output, inline errors, reduced noise, and wrap fixes in `moltbot models status`). (commit da3f2b489, commit 00ae21bed, commit 31e59cd58, commit f7dc27f2d, commit 438e782f8, commit 886752217, commit aabe0bed3, commit 81535d512, commit c63144ab1) - Media: only parse `MEDIA:` tags when they start the line to avoid stripping prose mentions. (#1206) - Media: preserve PNG alpha when possible; fall back to JPEG when still over size cap. (#1491) Thanks @robbyczgw-cla. - Skills: gate bird Homebrew install to macOS. (#1569) Thanks @bradleypriest. @@ -154,35 +296,35 @@ Docs: https://docs.clawd.bot ## 2026.1.21-2 ### Fixes -- Control UI: ignore bootstrap identity placeholder text for avatar values and fall back to the default avatar. https://docs.clawd.bot/cli/agents https://docs.clawd.bot/web/control-ui +- Control UI: ignore bootstrap identity placeholder text for avatar values and fall back to the default avatar. https://docs.molt.bot/cli/agents https://docs.molt.bot/web/control-ui - Slack: remove deprecated `filetype` field from `files.uploadV2` to eliminate API warnings. (#1447) ## 2026.1.21 ### Changes -- Highlight: Lobster optional plugin tool for typed workflows + approval gates. https://docs.clawd.bot/tools/lobster -- Lobster: allow workflow file args via `argsJson` in the plugin tool. https://docs.clawd.bot/tools/lobster +- Highlight: Lobster optional plugin tool for typed workflows + approval gates. https://docs.molt.bot/tools/lobster +- Lobster: allow workflow file args via `argsJson` in the plugin tool. https://docs.molt.bot/tools/lobster - Heartbeat: allow running heartbeats in an explicit session key. (#1256) Thanks @zknicker. - CLI: default exec approvals to the local host, add gateway/node targeting flags, and show target details in allowlist output. - CLI: exec approvals mutations render tables instead of raw JSON. - Exec approvals: support wildcard agent allowlists (`*`) across all agents. - Exec approvals: allowlist matches resolved binary paths only, add safe stdin-only bins, and tighten allowlist shell parsing. - Nodes: expose node PATH in status/describe and bootstrap PATH for node-host execution. -- CLI: flatten node service commands under `clawdbot node` and remove `service node` docs. -- CLI: move gateway service commands under `clawdbot gateway` and add `gateway probe` for reachability. +- CLI: flatten node service commands under `moltbot node` and remove `service node` docs. +- CLI: move gateway service commands under `moltbot gateway` and add `gateway probe` for reachability. - Sessions: add per-channel reset overrides via `session.resetByChannel`. (#1353) Thanks @cash-echo-bot. - Agents: add identity avatar config support and Control UI avatar rendering. (#1329, #1424) Thanks @dlauer. - UI: show per-session assistant identity in the Control UI. (#1420) Thanks @robbyczgw-cla. -- CLI: add `clawdbot update wizard` for interactive channel selection and restart prompts. https://docs.clawd.bot/cli/update +- CLI: add `moltbot update wizard` for interactive channel selection and restart prompts. https://docs.molt.bot/cli/update - Signal: add typing indicators and DM read receipts via signal-cli. - MSTeams: add file uploads, adaptive cards, and attachment handling improvements. (#1410) Thanks @Evizero. - Onboarding: remove the run setup-token auth option (paste setup-token or reuse CLI creds instead). -- Docs: add troubleshooting entry for gateway.mode blocking gateway start. https://docs.clawd.bot/gateway/troubleshooting +- Docs: add troubleshooting entry for gateway.mode blocking gateway start. https://docs.molt.bot/gateway/troubleshooting - Docs: add /model allowlist troubleshooting note. (#1405) - Docs: add per-message Gmail search example for gog. (#1220) Thanks @mbelinky. ### Breaking -- **BREAKING:** Control UI now rejects insecure HTTP without device identity by default. Use HTTPS (Tailscale Serve) or set `gateway.controlUi.allowInsecureAuth: true` to allow token-only auth. https://docs.clawd.bot/web/control-ui#insecure-http +- **BREAKING:** Control UI now rejects insecure HTTP without device identity by default. Use HTTPS (Tailscale Serve) or set `gateway.controlUi.allowInsecureAuth: true` to allow token-only auth. https://docs.molt.bot/web/control-ui#insecure-http - **BREAKING:** Envelope and system event timestamps now default to host-local time (was UTC) so agents don’t have to constantly convert. ### Fixes @@ -208,68 +350,68 @@ Docs: https://docs.clawd.bot ## 2026.1.20 ### Changes -- Control UI: add copy-as-markdown with error feedback. (#1345) https://docs.clawd.bot/web/control-ui -- Control UI: drop the legacy list view. (#1345) https://docs.clawd.bot/web/control-ui -- TUI: add syntax highlighting for code blocks. (#1200) https://docs.clawd.bot/tui -- TUI: session picker shows derived titles, fuzzy search, relative times, and last message preview. (#1271) https://docs.clawd.bot/tui -- TUI: add a searchable model picker for quicker model selection. (#1198) https://docs.clawd.bot/tui -- TUI: add input history (up/down) for submitted messages. (#1348) https://docs.clawd.bot/tui -- ACP: add `clawdbot acp` for IDE integrations. https://docs.clawd.bot/cli/acp -- ACP: add `clawdbot acp client` interactive harness for debugging. https://docs.clawd.bot/cli/acp -- Skills: add download installs with OS-filtered options. https://docs.clawd.bot/tools/skills -- Skills: add the local sherpa-onnx-tts skill. https://docs.clawd.bot/tools/skills -- Memory: add hybrid BM25 + vector search (FTS5) with weighted merging and fallback. https://docs.clawd.bot/concepts/memory -- Memory: add SQLite embedding cache to speed up reindexing and frequent updates. https://docs.clawd.bot/concepts/memory -- Memory: add OpenAI batch indexing for embeddings when configured. https://docs.clawd.bot/concepts/memory -- Memory: enable OpenAI batch indexing by default for OpenAI embeddings. https://docs.clawd.bot/concepts/memory -- Memory: allow parallel OpenAI batch indexing jobs (default concurrency: 2). https://docs.clawd.bot/concepts/memory -- Memory: render progress immediately, color batch statuses in verbose logs, and poll OpenAI batch status every 2s by default. https://docs.clawd.bot/concepts/memory -- Memory: add `--verbose` logging for memory status + batch indexing details. https://docs.clawd.bot/concepts/memory -- Memory: add native Gemini embeddings provider for memory search. (#1151) https://docs.clawd.bot/concepts/memory -- Browser: allow config defaults for efficient snapshots in the tool/CLI. (#1336) https://docs.clawd.bot/tools/browser -- Nostr: add the Nostr channel plugin with profile management + onboarding defaults. (#1323) https://docs.clawd.bot/channels/nostr -- Matrix: migrate to matrix-bot-sdk with E2EE support, location handling, and group allowlist upgrades. (#1298) https://docs.clawd.bot/channels/matrix -- Slack: add HTTP webhook mode via Bolt HTTP receiver. (#1143) https://docs.clawd.bot/channels/slack -- Telegram: enrich forwarded-message context with normalized origin details + legacy fallback. (#1090) https://docs.clawd.bot/channels/telegram +- Control UI: add copy-as-markdown with error feedback. (#1345) https://docs.molt.bot/web/control-ui +- Control UI: drop the legacy list view. (#1345) https://docs.molt.bot/web/control-ui +- TUI: add syntax highlighting for code blocks. (#1200) https://docs.molt.bot/tui +- TUI: session picker shows derived titles, fuzzy search, relative times, and last message preview. (#1271) https://docs.molt.bot/tui +- TUI: add a searchable model picker for quicker model selection. (#1198) https://docs.molt.bot/tui +- TUI: add input history (up/down) for submitted messages. (#1348) https://docs.molt.bot/tui +- ACP: add `moltbot acp` for IDE integrations. https://docs.molt.bot/cli/acp +- ACP: add `moltbot acp client` interactive harness for debugging. https://docs.molt.bot/cli/acp +- Skills: add download installs with OS-filtered options. https://docs.molt.bot/tools/skills +- Skills: add the local sherpa-onnx-tts skill. https://docs.molt.bot/tools/skills +- Memory: add hybrid BM25 + vector search (FTS5) with weighted merging and fallback. https://docs.molt.bot/concepts/memory +- Memory: add SQLite embedding cache to speed up reindexing and frequent updates. https://docs.molt.bot/concepts/memory +- Memory: add OpenAI batch indexing for embeddings when configured. https://docs.molt.bot/concepts/memory +- Memory: enable OpenAI batch indexing by default for OpenAI embeddings. https://docs.molt.bot/concepts/memory +- Memory: allow parallel OpenAI batch indexing jobs (default concurrency: 2). https://docs.molt.bot/concepts/memory +- Memory: render progress immediately, color batch statuses in verbose logs, and poll OpenAI batch status every 2s by default. https://docs.molt.bot/concepts/memory +- Memory: add `--verbose` logging for memory status + batch indexing details. https://docs.molt.bot/concepts/memory +- Memory: add native Gemini embeddings provider for memory search. (#1151) https://docs.molt.bot/concepts/memory +- Browser: allow config defaults for efficient snapshots in the tool/CLI. (#1336) https://docs.molt.bot/tools/browser +- Nostr: add the Nostr channel plugin with profile management + onboarding defaults. (#1323) https://docs.molt.bot/channels/nostr +- Matrix: migrate to matrix-bot-sdk with E2EE support, location handling, and group allowlist upgrades. (#1298) https://docs.molt.bot/channels/matrix +- Slack: add HTTP webhook mode via Bolt HTTP receiver. (#1143) https://docs.molt.bot/channels/slack +- Telegram: enrich forwarded-message context with normalized origin details + legacy fallback. (#1090) https://docs.molt.bot/channels/telegram - Discord: fall back to `/skill` when native command limits are exceeded. (#1287) - Discord: expose `/skill` globally. (#1287) -- Zalouser: add channel dock metadata, config schema, setup wiring, probe, and status issues. (#1219) https://docs.clawd.bot/plugins/zalouser -- Plugins: require manifest-embedded config schemas with preflight validation warnings. (#1272) https://docs.clawd.bot/plugins/manifest -- Plugins: move channel catalog metadata into plugin manifests. (#1290) https://docs.clawd.bot/plugins/manifest -- Plugins: align Nextcloud Talk policy helpers with core patterns. (#1290) https://docs.clawd.bot/plugins/manifest -- Plugins/UI: let channel plugin metadata drive UI labels/icons and cron channel options. (#1306) https://docs.clawd.bot/web/control-ui -- Agents/UI: add agent avatar support in identity config, IDENTITY.md, and the Control UI. (#1329) https://docs.clawd.bot/gateway/configuration -- Plugins: add plugin slots with a dedicated memory slot selector. https://docs.clawd.bot/plugins/agent-tools -- Plugins: ship the bundled BlueBubbles channel plugin (disabled by default). https://docs.clawd.bot/channels/bluebubbles +- Zalouser: add channel dock metadata, config schema, setup wiring, probe, and status issues. (#1219) https://docs.molt.bot/plugins/zalouser +- Plugins: require manifest-embedded config schemas with preflight validation warnings. (#1272) https://docs.molt.bot/plugins/manifest +- Plugins: move channel catalog metadata into plugin manifests. (#1290) https://docs.molt.bot/plugins/manifest +- Plugins: align Nextcloud Talk policy helpers with core patterns. (#1290) https://docs.molt.bot/plugins/manifest +- Plugins/UI: let channel plugin metadata drive UI labels/icons and cron channel options. (#1306) https://docs.molt.bot/web/control-ui +- Agents/UI: add agent avatar support in identity config, IDENTITY.md, and the Control UI. (#1329) https://docs.molt.bot/gateway/configuration +- Plugins: add plugin slots with a dedicated memory slot selector. https://docs.molt.bot/plugins/agent-tools +- Plugins: ship the bundled BlueBubbles channel plugin (disabled by default). https://docs.molt.bot/channels/bluebubbles - Plugins: migrate bundled messaging extensions to the plugin SDK and resolve plugin-sdk imports in the loader. -- Plugins: migrate the Zalo plugin to the shared plugin SDK runtime. https://docs.clawd.bot/channels/zalo -- Plugins: migrate the Zalo Personal plugin to the shared plugin SDK runtime. https://docs.clawd.bot/plugins/zalouser -- Plugins: allow optional agent tools with explicit allowlists and add the plugin tool authoring guide. https://docs.clawd.bot/plugins/agent-tools +- Plugins: migrate the Zalo plugin to the shared plugin SDK runtime. https://docs.molt.bot/channels/zalo +- Plugins: migrate the Zalo Personal plugin to the shared plugin SDK runtime. https://docs.molt.bot/plugins/zalouser +- Plugins: allow optional agent tools with explicit allowlists and add the plugin tool authoring guide. https://docs.molt.bot/plugins/agent-tools - Plugins: auto-enable bundled channel/provider plugins when configuration is present. -- Plugins: sync plugin sources on channel switches and update npm-installed plugins during `clawdbot update`. -- Plugins: share npm plugin update logic between `clawdbot update` and `clawdbot plugins update`. +- Plugins: sync plugin sources on channel switches and update npm-installed plugins during `moltbot update`. +- Plugins: share npm plugin update logic between `moltbot update` and `moltbot plugins update`. - Gateway/API: add `/v1/responses` (OpenResponses) with item-based input + semantic streaming events. (#1229) - Gateway/API: expand `/v1/responses` to support file/image inputs, tool_choice, usage, and output limits. (#1229) -- Usage: add `/usage cost` summaries and macOS menu cost charts. https://docs.clawd.bot/reference/api-usage-costs -- Security: warn when <=300B models run without sandboxing while web tools are enabled. https://docs.clawd.bot/cli/security -- Exec: add host/security/ask routing for gateway + node exec. https://docs.clawd.bot/tools/exec -- Exec: add `/exec` directive for per-session exec defaults (host/security/ask/node). https://docs.clawd.bot/tools/exec -- Exec approvals: migrate approvals to `~/.clawdbot/exec-approvals.json` with per-agent allowlists + skill auto-allow toggle, and add approvals UI + node exec lifecycle events. https://docs.clawd.bot/tools/exec-approvals -- Nodes: add headless node host (`clawdbot node start`) for `system.run`/`system.which`. https://docs.clawd.bot/cli/node -- Nodes: add node daemon service install/status/start/stop/restart. https://docs.clawd.bot/cli/node +- Usage: add `/usage cost` summaries and macOS menu cost charts. https://docs.molt.bot/reference/api-usage-costs +- Security: warn when <=300B models run without sandboxing while web tools are enabled. https://docs.molt.bot/cli/security +- Exec: add host/security/ask routing for gateway + node exec. https://docs.molt.bot/tools/exec +- Exec: add `/exec` directive for per-session exec defaults (host/security/ask/node). https://docs.molt.bot/tools/exec +- Exec approvals: migrate approvals to `~/.clawdbot/exec-approvals.json` with per-agent allowlists + skill auto-allow toggle, and add approvals UI + node exec lifecycle events. https://docs.molt.bot/tools/exec-approvals +- Nodes: add headless node host (`moltbot node start`) for `system.run`/`system.which`. https://docs.molt.bot/cli/node +- Nodes: add node daemon service install/status/start/stop/restart. https://docs.molt.bot/cli/node - Bridge: add `skills.bins` RPC to support node host auto-allow skill bins. -- Sessions: add daily reset policy with per-type overrides and idle windows (default 4am local), preserving legacy idle-only configs. (#1146) https://docs.clawd.bot/concepts/session -- Sessions: allow `sessions_spawn` to override thinking level for sub-agent runs. https://docs.clawd.bot/tools/subagents -- Channels: unify thread/topic allowlist matching + command/mention gating helpers across core providers. https://docs.clawd.bot/concepts/groups -- Models: add Qwen Portal OAuth provider support. (#1120) https://docs.clawd.bot/providers/qwen -- Onboarding: add allowlist prompts and username-to-id resolution across core and extension channels. https://docs.clawd.bot/start/onboarding -- Docs: clarify allowlist input types and onboarding behavior for messaging channels. https://docs.clawd.bot/start/onboarding -- Docs: refresh Android node discovery docs for the Gateway WS service type. https://docs.clawd.bot/platforms/android -- Docs: surface Amazon Bedrock in provider lists and clarify Bedrock auth env vars. (#1289) https://docs.clawd.bot/bedrock -- Docs: clarify WhatsApp voice notes. https://docs.clawd.bot/channels/whatsapp -- Docs: clarify Windows WSL portproxy LAN access notes. https://docs.clawd.bot/platforms/windows -- Docs: refresh bird skill install metadata and usage notes. (#1302) https://docs.clawd.bot/tools/browser-login +- Sessions: add daily reset policy with per-type overrides and idle windows (default 4am local), preserving legacy idle-only configs. (#1146) https://docs.molt.bot/concepts/session +- Sessions: allow `sessions_spawn` to override thinking level for sub-agent runs. https://docs.molt.bot/tools/subagents +- Channels: unify thread/topic allowlist matching + command/mention gating helpers across core providers. https://docs.molt.bot/concepts/groups +- Models: add Qwen Portal OAuth provider support. (#1120) https://docs.molt.bot/providers/qwen +- Onboarding: add allowlist prompts and username-to-id resolution across core and extension channels. https://docs.molt.bot/start/onboarding +- Docs: clarify allowlist input types and onboarding behavior for messaging channels. https://docs.molt.bot/start/onboarding +- Docs: refresh Android node discovery docs for the Gateway WS service type. https://docs.molt.bot/platforms/android +- Docs: surface Amazon Bedrock in provider lists and clarify Bedrock auth env vars. (#1289) https://docs.molt.bot/bedrock +- Docs: clarify WhatsApp voice notes. https://docs.molt.bot/channels/whatsapp +- Docs: clarify Windows WSL portproxy LAN access notes. https://docs.molt.bot/platforms/windows +- Docs: refresh bird skill install metadata and usage notes. (#1302) https://docs.molt.bot/tools/browser-login - Agents: add local docs path resolution and include docs/mirror/source/community pointers in the system prompt. - Agents: clarify node_modules read-only guidance in agent instructions. - Config: stamp last-touched metadata on write and warn if the config is newer than the running build. @@ -286,10 +428,10 @@ Docs: https://docs.clawd.bot - Swabble: use the tagged Commander Swift package release. ### Breaking -- **BREAKING:** Reject invalid/unknown config entries and refuse to start the gateway for safety. Run `clawdbot doctor --fix` to repair, then update plugins (`clawdbot plugins update`) if you use any. +- **BREAKING:** Reject invalid/unknown config entries and refuse to start the gateway for safety. Run `moltbot doctor --fix` to repair, then update plugins (`moltbot plugins update`) if you use any. ### Fixes -- Discovery: shorten Bonjour DNS-SD service type to `_clawdbot-gw._tcp` and update discovery clients/docs. +- Discovery: shorten Bonjour DNS-SD service type to `_moltbot-gw._tcp` and update discovery clients/docs. - Diagnostics: export OTLP logs, correct queue depth tracking, and document message-flow telemetry. - Diagnostics: emit message-flow diagnostics across channels via shared dispatch. (#1244) - Diagnostics: gate heartbeat/webhook logging. (#1244) @@ -316,7 +458,7 @@ Docs: https://docs.clawd.bot - Plugins: add Nextcloud Talk manifest for plugin config validation. (#1297) - Plugins: surface plugin load/register/config errors in gateway logs with plugin/source context. - CLI: preserve cron delivery settings when editing message payloads. (#1322) -- CLI: keep `clawdbot logs` output resilient to broken pipes while preserving progress output. +- CLI: keep `moltbot logs` output resilient to broken pipes while preserving progress output. - CLI: avoid duplicating --profile/--dev flags when formatting commands. - CLI: centralize CLI command registration to keep fast-path routing and program wiring in sync. (#1207) - CLI: keep banners on routed commands, restore config guarding outside fast-path routing, and tighten fast-path flag parsing while skipping console capture for extra speed. (#1195) @@ -334,7 +476,7 @@ Docs: https://docs.clawd.bot - TUI: show generic empty-state text for searchable pickers. (#1201) - TUI: highlight model search matches and stabilize search ordering. - Configure: hide OpenRouter auto routing model from the model picker. (#1182) -- Memory: show total file counts + scan issues in `clawdbot memory status`. +- Memory: show total file counts + scan issues in `moltbot memory status`. - Memory: fall back to non-batch embeddings after repeated batch failures. - Memory: apply OpenAI batch defaults even without explicit remote config. - Memory: index atomically so failed reindex preserves the previous memory database. (#1151) @@ -344,7 +486,7 @@ Docs: https://docs.clawd.bot - Memory: split overly long lines to keep embeddings under token limits. - Memory: skip empty chunks to avoid invalid embedding inputs. - Memory: split embedding batches to avoid OpenAI token limits during indexing. -- Memory: probe sqlite-vec availability in `clawdbot memory status`. +- Memory: probe sqlite-vec availability in `moltbot memory status`. - Exec approvals: enforce allowlist when ask is off. - Exec approvals: prefer raw command for node approvals/events. - Tools: show exec elevated flag before the command and keep it outside markdown in tool summaries. @@ -394,20 +536,20 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic ## 2026.1.16-1 ### Highlights -- Hooks: add hooks system with bundled hooks, CLI tooling, and docs. (#1028) — thanks @ThomsenDrake. https://docs.clawd.bot/hooks -- Media: add inbound media understanding (image/audio/video) with provider + CLI fallbacks. https://docs.clawd.bot/nodes/media-understanding -- Plugins: add Zalo Personal plugin (`@clawdbot/zalouser`) and unify channel directory for plugins. (#1032) — thanks @suminhthanh. https://docs.clawd.bot/plugins/zalouser -- Models: add Vercel AI Gateway auth choice + onboarding updates. (#1016) — thanks @timolins. https://docs.clawd.bot/providers/vercel-ai-gateway -- Sessions: add `session.identityLinks` for cross-platform DM session li nking. (#1033) — thanks @thewilloftheshadow. https://docs.clawd.bot/concepts/session -- Web search: add `country`/`language` parameters (schema + Brave API) and docs. (#1046) — thanks @YuriNachos. https://docs.clawd.bot/tools/web +- Hooks: add hooks system with bundled hooks, CLI tooling, and docs. (#1028) — thanks @ThomsenDrake. https://docs.molt.bot/hooks +- Media: add inbound media understanding (image/audio/video) with provider + CLI fallbacks. https://docs.molt.bot/nodes/media-understanding +- Plugins: add Zalo Personal plugin (`@moltbot/zalouser`) and unify channel directory for plugins. (#1032) — thanks @suminhthanh. https://docs.molt.bot/plugins/zalouser +- Models: add Vercel AI Gateway auth choice + onboarding updates. (#1016) — thanks @timolins. https://docs.molt.bot/providers/vercel-ai-gateway +- Sessions: add `session.identityLinks` for cross-platform DM session li nking. (#1033) — thanks @thewilloftheshadow. https://docs.molt.bot/concepts/session +- Web search: add `country`/`language` parameters (schema + Brave API) and docs. (#1046) — thanks @YuriNachos. https://docs.molt.bot/tools/web ### Breaking -- **BREAKING:** `clawdbot message` and message tool now require `target` (dropping `to`/`channelId` for destinations). (#1034) — thanks @tobalsan. +- **BREAKING:** `moltbot message` and message tool now require `target` (dropping `to`/`channelId` for destinations). (#1034) — thanks @tobalsan. - **BREAKING:** Channel auth now prefers config over env for Discord/Telegram/Matrix (env is fallback only). (#1040) — thanks @thewilloftheshadow. - **BREAKING:** Drop legacy `chatType: "room"` support; use `chatType: "channel"`. - **BREAKING:** remove legacy provider-specific target resolution fallbacks; target resolution is centralized with plugin hints + directory lookups. -- **BREAKING:** `clawdbot hooks` is now `clawdbot webhooks`; hooks live under `clawdbot hooks`. https://docs.clawd.bot/cli/webhooks -- **BREAKING:** `clawdbot plugins install ` now copies into `~/.clawdbot/extensions` (use `--link` to keep path-based loading). +- **BREAKING:** `moltbot hooks` is now `moltbot webhooks`; hooks live under `moltbot hooks`. https://docs.molt.bot/cli/webhooks +- **BREAKING:** `moltbot plugins install ` now copies into `~/.clawdbot/extensions` (use `--link` to keep path-based loading). ### Changes - Plugins: ship bundled plugins disabled by default and allow overrides by installed versions. (#1066) — thanks @ItzR3NO. @@ -417,7 +559,7 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Tools: send Chrome-like headers by default for `web_fetch` to improve extraction on bot-sensitive sites. - Tools: Firecrawl fallback now uses bot-circumvention + cache by default; remove basic HTML fallback when extraction fails. - Tools: default `exec` exit notifications and auto-migrate legacy `tools.bash` to `tools.exec`. -- Tools: add `exec` PTY support for interactive sessions. https://docs.clawd.bot/tools/exec +- Tools: add `exec` PTY support for interactive sessions. https://docs.molt.bot/tools/exec - Tools: add tmux-style `process send-keys` and bracketed paste helpers for PTY sessions. - Tools: add `process submit` helper to send CR for PTY sessions. - Tools: respond to PTY cursor position queries to unblock interactive TUIs. @@ -425,7 +567,7 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Skills: update coding-agent guidance to prefer PTY-enabled exec runs and simplify tmux usage. - TUI: refresh session token counts after runs complete or fail. (#1079) — thanks @d-ploutarchos. - Status: trim `/status` to current-provider usage only and drop the OAuth/token block. -- Directory: unify `clawdbot directory` across channels and plugin channels. +- Directory: unify `moltbot directory` across channels and plugin channels. - UI: allow deleting sessions from the Control UI. - Memory: add sqlite-vec vector acceleration with CLI status details. - Memory: add experimental session transcript indexing for memory_search (opt-in via memorySearch.experimental.sessionMemory + sources). @@ -441,7 +583,7 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Docs: add `/help` hub, Node/npm PATH guide, and expand directory CLI docs. - Config: support env var substitution in config values. (#1044) — thanks @sebslight. - Health: add per-agent session summaries and account-level health details, and allow selective probes. (#1047) — thanks @gumadeiras. -- Hooks: add hook pack installs (npm/path/zip/tar) with `clawdbot.hooks` manifests and `clawdbot hooks install/update`. +- Hooks: add hook pack installs (npm/path/zip/tar) with `moltbot.hooks` manifests and `moltbot hooks install/update`. - Plugins: add zip installs and `--link` to avoid copying local paths. ### Fixes @@ -472,10 +614,10 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Sessions: hard-stop `sessions.delete` cleanup. - Channels: treat replies to the bot as implicit mentions across supported channels. - Channels: normalize object-format capabilities in channel capability parsing. -- Security: default-deny slash/control commands unless a channel computed `CommandAuthorized` (fixes accidental “open” behavior), and ensure WhatsApp + Zalo plugin channels gate inline `/…` tokens correctly. https://docs.clawd.bot/gateway/security +- Security: default-deny slash/control commands unless a channel computed `CommandAuthorized` (fixes accidental “open” behavior), and ensure WhatsApp + Zalo plugin channels gate inline `/…` tokens correctly. https://docs.molt.bot/gateway/security - Security: redact sensitive text in gateway WS logs. - Tools: cap pending `exec` process output to avoid unbounded buffers. -- CLI: speed up `clawdbot sandbox-explain` by avoiding heavy plugin imports when normalizing channel ids. +- CLI: speed up `moltbot sandbox-explain` by avoiding heavy plugin imports when normalizing channel ids. - Browser: remote profile tab operations prefer persistent Playwright and avoid silent HTTP fallbacks. (#1057) — thanks @mukhtharcm. - Browser: remote profile tab ops follow-up: shared Playwright loader, Playwright-based focus, and more coverage (incl. opt-in live Browserless test). (follow-up to #1057) — thanks @mukhtharcm. - Browser: refresh extension relay tab metadata after navigation so `/json/list` stays current. (#1073) — thanks @roshanasingh4. @@ -501,19 +643,19 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic ## 2026.1.15 ### Highlights -- Plugins: add provider auth registry + `clawdbot models auth login` for plugin-driven OAuth/API key flows. +- Plugins: add provider auth registry + `moltbot models auth login` for plugin-driven OAuth/API key flows. - Browser: improve remote CDP/Browserless support (auth passthrough, `wss` upgrade, timeouts, clearer errors). - Heartbeat: per-agent configuration + 24h duplicate suppression. (#980) — thanks @voidserf. - Security: audit warns on weak model tiers; app nodes store auth tokens encrypted (Keychain/SecurePrefs). ### Breaking - **BREAKING:** iOS minimum version is now 18.0 to support Textual markdown rendering in native chat. (#702) -- **BREAKING:** Microsoft Teams is now a plugin; install `@clawdbot/msteams` via `clawdbot plugins install @clawdbot/msteams`. +- **BREAKING:** Microsoft Teams is now a plugin; install `@moltbot/msteams` via `moltbot plugins install @moltbot/msteams`. - **BREAKING:** Channel auth now prefers config over env for Discord/Telegram/Matrix (env is fallback only). (#1040) — thanks @thewilloftheshadow. ### Changes - UI/Apps: move channel/config settings to schema-driven forms and rename Connections → Channels. (#1040) — thanks @thewilloftheshadow. -- CLI: set process titles to `clawdbot-` for clearer process listings. +- CLI: set process titles to `moltbot-` for clearer process listings. - CLI/macOS: sync remote SSH target/identity to config and let `gateway status` auto-infer SSH targets (ssh-config aware). - Telegram: scope inline buttons with allowlist default + callback gating in DMs/groups. - Telegram: default reaction notifications to own. @@ -521,13 +663,13 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Heartbeat: tighten prompt guidance + suppress duplicate alerts for 24h. (#980) — thanks @voidserf. - Repo: ignore local identity files to avoid accidental commits. (#1001) — thanks @gerardward2007. - Sessions/Security: add `session.dmScope` for multi-user DM isolation and audit warnings. (#948) — thanks @Alphonse-arianee. -- Plugins: add provider auth registry + `clawdbot models auth login` for plugin-driven OAuth/API key flows. +- Plugins: add provider auth registry + `moltbot models auth login` for plugin-driven OAuth/API key flows. - Onboarding: switch channels setup to a single-select loop with per-channel actions and disabled hints in the picker. - TUI: show provider/model labels for the active session and default model. - Heartbeat: add per-agent heartbeat configuration and multi-agent docs example. - UI: show gateway auth guidance + doc link on unauthorized Control UI connections. - UI: add session deletion action in Control UI sessions list. (#1017) — thanks @Szpadel. -- Security: warn on weak model tiers (Haiku, below GPT-5, below Claude 4.5) in `clawdbot security audit`. +- Security: warn on weak model tiers (Haiku, below GPT-5, below Claude 4.5) in `moltbot security audit`. - Apps: store node auth tokens encrypted (Keychain/SecurePrefs). - Daemon: share profile/state-dir resolution across service helpers and honor `CLAWDBOT_STATE_DIR` for Windows task scripts. - Docs: clarify multi-gateway rescue bot guidance. (#969) — thanks @bjesuiter. @@ -537,8 +679,8 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Docs: add Date & Time guide and update prompt/timezone configuration docs. - Messages: debounce rapid inbound messages across channels with per-connector overrides. (#971) — thanks @juanpablodlc. - Messages: allow media-only sends (CLI/tool) and show Telegram voice recording status for voice notes. (#957) — thanks @rdev. -- Auth/Status: keep auth profiles sticky per session (rotate on compaction/new), surface provider usage headers in `/status` and `clawdbot models status`, and update docs. -- CLI: add `--json` output for `clawdbot daemon` lifecycle/install commands. +- Auth/Status: keep auth profiles sticky per session (rotate on compaction/new), surface provider usage headers in `/status` and `moltbot models status`, and update docs. +- CLI: add `--json` output for `moltbot daemon` lifecycle/install commands. - Memory: make `node-llama-cpp` an optional dependency (avoid Node 25 install failures) and improve local-embeddings fallback/errors. - Browser: add `snapshot refs=aria` (Playwright aria-ref ids) for self-resolving refs across `snapshot` → `act`. - Browser: `profile="chrome"` now defaults to host control and returns clearer “attach a tab” errors. @@ -561,10 +703,10 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - iMessage: treat missing `imsg rpc` support as fatal to avoid restart loops. - Auth: merge main auth profiles into per-agent stores for sub-agents and document inheritance. (#1013) — thanks @marcmarg. - Agents: avoid JSON Schema `format` collisions in tool params by renaming snapshot format fields. (#1013) — thanks @marcmarg. -- Fix: make `clawdbot update` auto-update global installs when installed via a package manager. +- Fix: make `moltbot update` auto-update global installs when installed via a package manager. - Fix: list model picker entries as provider/model pairs for explicit selection. (#970) — thanks @mcinteerj. - Fix: align OpenAI image-gen defaults with DALL-E 3 standard quality and document output formats. (#880) — thanks @mkbehr. -- Fix: persist `gateway.mode=local` after selecting Local run mode in `clawdbot configure`, even if no other sections are chosen. +- Fix: persist `gateway.mode=local` after selecting Local run mode in `moltbot configure`, even if no other sections are chosen. - Daemon: fix profile-aware service label resolution (env-driven) and add coverage for launchd/systemd/schtasks. (#969) — thanks @bjesuiter. - Agents: avoid false positives when logging unsupported Google tool schema keywords. - Agents: skip Gemini history downgrades for google-antigravity to preserve tool calls. (#894) — thanks @mukhtharcm. @@ -588,15 +730,15 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic ### Highlights - Web search: `web_search`/`web_fetch` tools (Brave API) + first-time setup in onboarding/configure. -- Browser control: Chrome extension relay takeover mode + remote browser control via `clawdbot browser serve`. +- Browser control: Chrome extension relay takeover mode + remote browser control support. - Plugins: channel plugins (gateway HTTP hooks) + Zalo plugin + onboarding install flow. (#854) — thanks @longmaba. -- Security: expanded `clawdbot security audit` (+ `--fix`), detect-secrets CI scan, and a `SECURITY.md` reporting policy. +- Security: expanded `moltbot security audit` (+ `--fix`), detect-secrets CI scan, and a `SECURITY.md` reporting policy. ### Changes - Docs: clarify per-agent auth stores, sandboxed skill binaries, and elevated semantics. - Docs: add FAQ entries for missing provider auth after adding agents and Gemini thinking signature errors. - Agents: add optional auth-profile copy prompt on `agents add` and improve auth error messaging. -- Security: expand `clawdbot security audit` checks (model hygiene, config includes, plugin allowlists, exposure matrix) and extend `--fix` to tighten more sensitive state paths. +- Security: expand `moltbot security audit` checks (model hygiene, config includes, plugin allowlists, exposure matrix) and extend `--fix` to tighten more sensitive state paths. - Security: add `SECURITY.md` reporting policy. - Channels: add Matrix plugin (external) with docs + onboarding hooks. - Plugins: add Zalo channel plugin with gateway HTTP hooks and onboarding install prompt. (#854) — thanks @longmaba. @@ -606,7 +748,7 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Security: add detect-secrets CI scan and baseline guidance. (#227) — thanks @Hyaxia. - Tools: add `web_search`/`web_fetch` (Brave API), auto-enable `web_fetch` for sandboxed sessions, and remove the `brave-search` skill. - CLI/Docs: add a web tools configure section for storing Brave API keys and update onboarding tips. -- Browser: add Chrome extension relay takeover mode (toolbar button), plus `clawdbot browser extension install/path` and remote browser control via `clawdbot browser serve` + `browser.controlToken`. +- Browser: add Chrome extension relay takeover mode (toolbar button), plus `moltbot browser extension install/path` and remote browser control (standalone server + token auth). ### Fixes - Sessions: refactor session store updates to lock + mutate per-entry, add chat.inject, and harden subagent cleanup flow. (#944) — thanks @tyler6204. @@ -703,19 +845,19 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic ### New & Improved - Memory: add custom OpenAI-compatible embedding endpoints; support OpenAI/local `node-llama-cpp` embeddings with per-agent overrides and provider metadata in tools/CLI. (#819) — thanks @mukhtharcm. -- Memory: new `clawdbot memory` CLI plus `memory_search`/`memory_get` tools with snippets + line ranges; index stored under `~/.clawdbot/memory/{agentId}.sqlite` with watch-on-by-default. +- Memory: new `moltbot memory` CLI plus `memory_search`/`memory_get` tools with snippets + line ranges; index stored under `~/.clawdbot/memory/{agentId}.sqlite` with watch-on-by-default. - Agents: strengthen memory recall guidance; make workspace bootstrap truncation configurable (default 20k) with warnings; add default sub-agent model config. - Tools/Sandbox: add tool profiles + group shorthands; support tool-policy groups in `tools.sandbox.tools`; drop legacy `memory` shorthand; allow Docker bind mounts via `docker.binds`. (#790) — thanks @akonyer. - Tools: add provider/model-specific tool policy overrides (`tools.byProvider`) to trim tool exposure per provider. - Tools: add browser `scrollintoview` action; allow Claude/Gemini tool param aliases; allow thinking `xhigh` for GPT-5.2/Codex with safe downgrades. (#793) — thanks @hsrvc; (#444) — thanks @grp06. -- Gateway/CLI: add Tailscale binary discovery, custom bind mode, and probe auth retry; add `clawdbot dashboard` auto-open flow; default native slash commands to `"auto"` with per-provider overrides. (#740) — thanks @jeffersonwarrior. +- Gateway/CLI: add Tailscale binary discovery, custom bind mode, and probe auth retry; add `moltbot dashboard` auto-open flow; default native slash commands to `"auto"` with per-provider overrides. (#740) — thanks @jeffersonwarrior. - Auth/Onboarding: add Chutes OAuth (PKCE + refresh + onboarding choice); normalize API key inputs; default TUI onboarding to `deliver: false`. (#726) — thanks @FrieSei; (#791) — thanks @roshanasingh4. - Providers: add `discord.allowBots`; trim legacy MiniMax M2 from default catalogs; route MiniMax vision to the Coding Plan VLM endpoint (also accepts `@/path/to/file.png` inputs). (#802) — thanks @zknicker. - Gateway: allow Tailscale Serve identity headers to satisfy token auth; rebuild Control UI assets when protocol schema is newer. (#823) — thanks @roshanasingh4; (#786) — thanks @meaningfool. - Heartbeat: default `ackMaxChars` to 300 so short `HEARTBEAT_OK` replies stay internal. ### Installer -- Install: run `clawdbot doctor --non-interactive` after git installs/updates and nudge daemon restarts when detected. +- Install: run `moltbot doctor --non-interactive` after git installs/updates and nudge daemon restarts when detected. ### Fixes - Doctor: warn on pnpm workspace mismatches, missing Control UI assets, and missing tsx binaries; offer UI rebuilds. @@ -736,7 +878,7 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Telegram: preserve forum topic thread ids, persist polling offsets, respect account bindings in webhook mode, and show typing indicator in General topics. (#727, #739) — thanks @thewilloftheshadow; (#821) — thanks @gumadeiras; (#779) — thanks @azade-c. - Slack: accept slash commands with or without leading `/` for custom command configs. (#798) — thanks @thewilloftheshadow. - Cron: persist disabled jobs correctly; accept `jobId` aliases for update/run/remove params. (#205, #252) — thanks @thewilloftheshadow. -- Gateway/CLI: honor `CLAWDBOT_LAUNCHD_LABEL` / `CLAWDBOT_SYSTEMD_UNIT` overrides; `agents.list` respects explicit config; reduce noisy loopback WS logs during tests; run `clawdbot doctor --non-interactive` during updates. (#781) — thanks @ronyrus. +- Gateway/CLI: honor `CLAWDBOT_LAUNCHD_LABEL` / `CLAWDBOT_SYSTEMD_UNIT` overrides; `agents.list` respects explicit config; reduce noisy loopback WS logs during tests; run `moltbot doctor --non-interactive` during updates. (#781) — thanks @ronyrus. - Onboarding/Control UI: refuse invalid configs (run doctor first); quote Windows browser URLs for OAuth; keep chat scroll position unless the user is near the bottom. (#764) — thanks @mukhtharcm; (#794) — thanks @roshanasingh4; (#217) — thanks @thewilloftheshadow. - Tools/UI: harden tool input schemas for strict providers; drop null-only union variants for Gemini schema cleanup; treat `maxChars: 0` as unlimited; keep TUI last streamed response instead of "(no output)". (#782) — thanks @AbhisekBasu1; (#796) — thanks @gabriel-trigo; (#747) — thanks @thewilloftheshadow. - Connections UI: polish multi-account account cards. (#816) — thanks @steipete. @@ -765,7 +907,7 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Auto-reply: add compact `/model` picker (models + available providers) and show provider endpoints in `/model status`. - Control UI: add Config tab model presets (MiniMax M2.1, GLM 4.7, Kimi) for one-click setup. - Plugins: add extension loader (tools/RPC/CLI/services), discovery paths, and config schema + Control UI labels (uiHints). -- Plugins: add `clawdbot plugins install` (path/tgz/npm), plus `list|info|enable|disable|doctor` UX. +- Plugins: add `moltbot plugins install` (path/tgz/npm), plus `list|info|enable|disable|doctor` UX. - Plugins: voice-call plugin now real (Twilio/log), adds start/status RPC/CLI/tool + tests. - Docs: add plugins doc + cross-links from tools/skills/gateway config. - Docs: add beginner-friendly plugin quick start + expand Voice Call plugin docs. @@ -778,7 +920,7 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Agents: add pre-compaction memory flush config (`agents.defaults.compaction.*`) with a soft threshold + system prompt. - Config: add `$include` directive for modular config files. (#731) — thanks @pasogott. - Build: set pnpm minimum release age to 2880 minutes (2 days). (#718) — thanks @dan-dr. -- macOS: prompt to install the global `clawdbot` CLI when missing in local mode; install via `clawd.bot/install-cli.sh` (no onboarding) and use external launchd/CLI instead of the embedded gateway runtime. +- macOS: prompt to install the global `moltbot` CLI when missing in local mode; install via `molt.bot/install-cli.sh` (no onboarding) and use external launchd/CLI instead of the embedded gateway runtime. - Docs: add gog calendar event color IDs from `gog calendar colors`. (#715) — thanks @mjrussell. - Cron/CLI: add `--model` flag to cron add/edit commands. (#711) — thanks @mjrussell. - Cron/CLI: trim model overrides on cron edits and document main-session guidance. (#711) — thanks @mjrussell. @@ -792,7 +934,7 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic ### Installer - Postinstall: replace `git apply` with builtin JS patcher (works npm/pnpm/bun; no git dependency) plus regression tests. - Postinstall: skip pnpm patch fallback when the new patcher is active. -- Installer tests: add root+non-root docker smokes, CI workflow to fetch clawd.bot scripts and run install sh/cli with onboarding skipped. +- Installer tests: add root+non-root docker smokes, CI workflow to fetch molt.bot scripts and run install sh/cli with onboarding skipped. - Installer UX: support `CLAWDBOT_NO_ONBOARD=1` for non-interactive installs; fix npm prefix on Linux and auto-install git. - Installer UX: add `install.sh --help` with flags/env and git install hint. - Installer UX: add `--install-method git|npm` and auto-detect source checkouts (prompt to update git checkout vs migrate to npm). @@ -808,7 +950,7 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Auth: read Codex keychain credentials and make the lookup platform-aware. - macOS/Release: avoid bundling dist artifacts in relay builds and generate appcasts from zip-only sources. - Doctor: surface plugin diagnostics in the report. -- Plugins: treat `plugins.load.paths` directory entries as package roots when they contain `package.json` + `clawdbot.extensions`; load plugin packages from config dirs; extract archives without system tar. +- Plugins: treat `plugins.load.paths` directory entries as package roots when they contain `package.json` + `moltbot.extensions`; load plugin packages from config dirs; extract archives without system tar. - Config: expand `~` in `CLAWDBOT_CONFIG_PATH` and common path-like config fields (including `plugins.load.paths`); guard invalid `$include` paths. (#731) — thanks @pasogott. - Agents: stop pre-creating session transcripts so first user messages persist in JSONL history. - Agents: skip pre-compaction memory flush when the session workspace is read-only. @@ -837,9 +979,9 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic ## 2026.1.10 ### Highlights -- CLI: `clawdbot status` now table-based + shows OS/update/gateway/daemon/agents/sessions; `status --all` adds a full read-only debug report (tables, log tails, Tailscale summary, and scan progress via OSC-9 + spinner). +- CLI: `moltbot status` now table-based + shows OS/update/gateway/daemon/agents/sessions; `status --all` adds a full read-only debug report (tables, log tails, Tailscale summary, and scan progress via OSC-9 + spinner). - CLI Backends: add Codex CLI fallback with resume support (text output) and JSONL parsing for new runs, plus a live CLI resume probe. -- CLI: add `clawdbot update` (safe-ish git checkout update) + `--update` shorthand. (#673) — thanks @fm1randa. +- CLI: add `moltbot update` (safe-ish git checkout update) + `--update` shorthand. (#673) — thanks @fm1randa. - Gateway: add OpenAI-compatible `/v1/chat/completions` HTTP endpoint (auth, SSE streaming, per-agent routing). (#680). ### Changes @@ -849,7 +991,7 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Agents/Browser: add `browser.target` (sandbox/host/custom) with sandbox host-control gating via `agents.defaults.sandbox.browser.allowHostControl`, allowlists for custom control URLs/hosts/ports, and expand browser tool docs (remote control, profiles, internals). - Onboarding/Models: add catalog-backed default model picker to onboarding + configure. (#611) — thanks @jonasjancarik. - Agents/OpenCode Zen: update fallback models + defaults, keep legacy alias mappings. (#669) — thanks @magimetal. -- CLI: add `clawdbot reset` and `clawdbot uninstall` flows (interactive + non-interactive) plus docker cleanup smoke test. +- CLI: add `moltbot reset` and `moltbot uninstall` flows (interactive + non-interactive) plus docker cleanup smoke test. - Providers: move provider wiring to a plugin architecture. (#661). - Providers: unify group history context wrappers across providers with per-provider/per-account `historyLimit` overrides (fallback to `messages.groupChat.historyLimit`). Set `0` to disable. (#672). - Gateway/Heartbeat: optionally deliver heartbeat `Reasoning:` output (`agents.defaults.heartbeat.includeReasoning`). (#690) @@ -858,7 +1000,7 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic ### Fixes - Auto-reply: suppress draft/typing streaming for `NO_REPLY` (silent system ops) so it doesn’t leak partial output. - CLI/Status: expand tables to full terminal width; clarify provider setup vs runtime warnings; richer per-provider detail; token previews in `status` while keeping `status --all` redacted; add troubleshooting link footer; keep log tails pasteable; show gateway auth used when reachable; surface provider runtime errors (Signal/iMessage/Slack); harden `tailscale status --json` parsing; make `status --all` scan progress determinate; and replace the footer with a 3-line “Next steps” recommendation (share/debug/probe). -- CLI/Gateway: clarify that `clawdbot gateway status` reports RPC health (connect + RPC) and shows RPC failures separately from connect failures. +- CLI/Gateway: clarify that `moltbot gateway status` reports RPC health (connect + RPC) and shows RPC failures separately from connect failures. - CLI/Update: gate progress spinner on stdout TTY and align clean-check step label. (#701) — thanks @bjesuiter. - Telegram: add `/whoami` + `/id` commands to reveal sender id for allowlists; allow `@username` and prefixed ids in `allowFrom` prompts (with stability warning). - Heartbeat: strip markup-wrapped `HEARTBEAT_OK` so acks don’t leak to external providers (e.g., Telegram). @@ -871,7 +1013,7 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Agents/Pi: inject config `temperature`/`maxTokens` into streaming without replacing the session streamFn; cover with live maxTokens probe. (#732) — thanks @peschee. - macOS: clear unsigned launchd overrides on signed restarts and warn via doctor when attach-only/disable markers are set. (#695) — thanks @jeffersonwarrior. - Agents: enforce single-writer session locks and drop orphan tool results to prevent tool-call ID failures (MiniMax/Anthropic-compatible APIs). -- Docs: make `clawdbot status` the first diagnostic step, clarify `status --deep` behavior, and document `/whoami` + `/id`. +- Docs: make `moltbot status` the first diagnostic step, clarify `status --deep` behavior, and document `/whoami` + `/id`. - Docs/Testing: clarify live tool+image probes and how to list your testable `provider/model` ids. - Tests/Live: make gateway bash+read probes resilient to provider formatting while still validating real tool calls. - WhatsApp: detect @lid mentions in groups using authDir reverse mapping + resolve self JID E.164 for mention gating. (#692) — thanks @peschee. @@ -891,7 +1033,7 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - WhatsApp: expose group participant IDs to the model so reactions can target the right sender. - Cron: `wakeMode: "now"` waits for heartbeat completion (and retries when the main lane is busy). (#666) — thanks @roshanasingh4. - Agents/OpenAI: fix Responses tool-only → follow-up turn handling (avoid standalone `reasoning` items that trigger 400 “required following item”) and replay reasoning items in Responses/Codex Responses history for tool-call-only turns. -- Sandbox: add `clawdbot sandbox explain` (effective policy inspector + fix-it keys); improve “sandbox jail” tool-policy/elevated errors with actionable config key paths; link to docs. +- Sandbox: add `moltbot sandbox explain` (effective policy inspector + fix-it keys); improve “sandbox jail” tool-policy/elevated errors with actionable config key paths; link to docs. - Hooks/Gmail: keep Tailscale serve path at `/` while preserving the public path. (#668) — thanks @antons. - Hooks/Gmail: allow Tailscale target URLs to preserve internal serve paths. - Auth: update Claude Code keychain credentials in-place during refresh sync; share JSON file helpers; add CLI fallback coverage. @@ -903,12 +1045,12 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Gateway/Control UI: sniff image attachments for chat.send, drop non-images, and log mismatches. (#670) — thanks @cristip73. - macOS: force `restart-mac.sh --sign` to require identities and keep bundled Node signed for relay verification. (#580) — thanks @jeffersonwarrior. - Gateway/Agent: accept image attachments on `agent` (multimodal message) and add live gateway image probe (`CLAWDBOT_LIVE_GATEWAY_IMAGE_PROBE=1`). -- CLI: `clawdbot sessions` now includes `elev:*` + `usage:*` flags in the table output. +- CLI: `moltbot sessions` now includes `elev:*` + `usage:*` flags in the table output. - CLI/Pairing: accept positional provider for `pairing list|approve` (npm-run compatible); update docs/bot hints. -- Branding: normalize user-facing “ClawdBot”/“CLAWDBOT” → “Clawdbot” (CLI, status, docs). +- Branding: normalize legacy casing/branding to “Moltbot” (CLI, status, docs). - Auto-reply: fix native `/model` not updating the actual chat session (Telegram/Slack/Discord). (#646) -- Doctor: offer to run `clawdbot update` first on git installs (keeps doctor output aligned with latest). -- Doctor: avoid false legacy workspace warning when install dir is `~/clawdbot`. (#660) +- Doctor: offer to run `moltbot update` first on git installs (keeps doctor output aligned with latest). +- Doctor: avoid false legacy workspace warning when install dir is `~/moltbot`. (#660) - iMessage: fix reasoning persistence across DMs; avoid partial/duplicate replies when reasoning is enabled. (#655) — thanks @antons. - Models/Auth: allow MiniMax API configs without `models.providers.minimax.apiKey` (auth profiles / `MINIMAX_API_KEY`). (#656) — thanks @mneves75. - Agents: avoid duplicate replies when the message tool sends. (#659) — thanks @mickahouan. @@ -939,12 +1081,12 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Control UI/TUI: queued messages, session links, reasoning view, mobile polish, logs UX. ### Breaking -- CLI: `clawdbot message` now subcommands (`message send|poll|...`) and requires `--provider` unless only one provider configured. +- CLI: `moltbot message` now subcommands (`message send|poll|...`) and requires `--provider` unless only one provider configured. - Commands/Tools: `/restart` and gateway restart tool disabled by default; enable with `commands.restart=true`. ### New Features and Changes - Models/Auth: OpenCode Zen onboarding (#623) — thanks @magimetal; MiniMax Anthropic-compatible API + hosted onboarding (#590, #495) — thanks @mneves75, @tobiasbischoff. -- Models/Auth: setup-token + token auth profiles; `clawdbot models auth order {get,set,clear}`; per-agent auth candidates in `/model status`; OAuth expiry checks in doctor/status. +- Models/Auth: setup-token + token auth profiles; `moltbot models auth order {get,set,clear}`; per-agent auth candidates in `/model status`; OAuth expiry checks in doctor/status. - Agent/System: claude-cli runner; `session_status` tool (and sandbox allow); adaptive context pruning default; system prompt messaging guidance + no auto self-update; eligible skills list injection; sub-agent context trimmed. - Commands: `/commands` list; `/models` alias; `/usage` alias; `/debug` runtime overrides + effective config view; `/config` chat updates + `/config get`; `config --section`. - CLI/Gateway: unified message tool + message subcommands; gateway discover (local + wide-area DNS-SD) with JSON/timeout; gateway status human-readable + JSON + SSH loopback; wide-area records include gatewayPort/sshPort/cliPath + tailnet DNS fallback. @@ -976,7 +1118,7 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Signal: reaction handling safety; own-reaction matching (uuid+phone); UUID-only senders accepted; ignore reaction-only messages. - MS Teams: download image attachments reliably; fix top-level replies; stop on shutdown + honor chunk limits; normalize poll providers/deps; pairing label fixes. - iMessage: isolate group-ish threads by chat_id. -- Gateway/Daemon/Doctor: atomic config writes; repair gateway service entrypoint + install switches; non-interactive legacy migrations; systemd unit alignment + KillMode=process; node bridge keepalive/pings; Launch at Login persistence; bundle ClawdbotKit resources + Swift 6.2 compat dylib; relay version check + remove smoke test; regen Swift GatewayModels + keep agent provider string; cron jobId alias + channel alias migration + main session key normalization; heartbeat Telegram accountId resolution; avoid WhatsApp fallback for internal runs; gateway listener error wording; serveBaseUrl param; honor gateway --dev; fix wide-area discovery updates; align agents.defaults schema; provider account metadata in daemon status; refresh Carbon patch for gateway fixes; restore doctor prompter initialValue handling. +- Gateway/Daemon/Doctor: atomic config writes; repair gateway service entrypoint + install switches; non-interactive legacy migrations; systemd unit alignment + KillMode=process; node bridge keepalive/pings; Launch at Login persistence; bundle MoltbotKit resources + Swift 6.2 compat dylib; relay version check + remove smoke test; regen Swift GatewayModels + keep agent provider string; cron jobId alias + channel alias migration + main session key normalization; heartbeat Telegram accountId resolution; avoid WhatsApp fallback for internal runs; gateway listener error wording; serveBaseUrl param; honor gateway --dev; fix wide-area discovery updates; align agents.defaults schema; provider account metadata in daemon status; refresh Carbon patch for gateway fixes; restore doctor prompter initialValue handling. - Control UI/TUI: persist per-session verbose off + hide tool cards; logs tab opens at bottom; relative asset paths + landing cleanup; session labels lookup/persistence; stop pinning main session in recents; start logs at bottom; TUI status bar refresh + timeout handling + hide reasoning label when off. - Onboarding/Configure: QuickStart single-select provider picker; avoid Codex CLI false-expiry warnings; clarify WhatsApp owner prompt; fix Minimax hosted onboarding (agents.defaults + msteams heartbeat target); remove configure Control UI prompt; honor gateway --dev flag. @@ -1002,7 +1144,7 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Previously, if you didn’t configure an allowlist, your bot could be **open to anyone** (especially discoverable Telegram bots). - New default: DM pairing (`dmPolicy="pairing"` / `discord.dm.policy="pairing"` / `slack.dm.policy="pairing"`). - To keep old “open to everyone” behavior: set `dmPolicy="open"` and include `"*"` in the relevant `allowFrom` (Discord/Slack: `discord.dm.allowFrom` / `slack.dm.allowFrom`). - - Approve requests via `clawdbot pairing list ` + `clawdbot pairing approve `. + - Approve requests via `moltbot pairing list ` + `moltbot pairing approve `. - Sandbox: default `agent.sandbox.scope` to `"agent"` (one container/workspace per agent). Use `"session"` for per-session isolation; `"shared"` disables cross-session isolation. - Timestamps in agent envelopes are now UTC (compact `YYYY-MM-DDTHH:mmZ`); removed `messages.timestampPrefix`. Add `agent.userTimezone` to tell the model the user’s local time (system prompt only). - Model config schema changes (auth profiles + model lists); doctor auto-migrates and the gateway rewrites legacy configs on startup. @@ -1016,7 +1158,7 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - **Agent loop + compaction:** compaction/pruning tuning, overflow handling, safer bootstrap context, and per-provider threading/confirmations; opt-in tool-result pruning + compact tracking. - **Sandbox + tools:** per-agent sandbox overrides, workspaceAccess controls, session tool visibility, tool policy overrides, process isolation, and tool schema/timeout/reaction unification. - **Providers (Telegram/WhatsApp/Discord/Slack/Signal/iMessage):** retry/backoff, threading, reactions, media groups/attachments, mention gating, typing behavior, and error/log stability; long polling + forum topic isolation for Telegram. -- **Gateway/CLI UX:** `clawdbot logs`, cron list colors/aliases, docs search, agents list/add/delete flows, status usage snapshots, runtime/auth source display, and `/status`/commands auth unification. +- **Gateway/CLI UX:** `moltbot logs`, cron list colors/aliases, docs search, agents list/add/delete flows, status usage snapshots, runtime/auth source display, and `/status`/commands auth unification. - **Control UI/Web:** logs tab, focus mode polish, config form resilience, streaming stability, tool output caps, windowed chat history, and reconnect/password URL auth. - **macOS/Android/TUI/Build:** macOS gateway races, QR bundling, JSON5 config safety, Voice Wake hardening; Android EXIF rotation + APK naming/versioning; TUI key handling; tooling/bundling fixes. - **Packaging/compat:** npm dist folder coverage, Node 25 qrcode-terminal import fixes, Bun/Playwright/WebSocket patches, and Docker Bun install. @@ -1058,4 +1200,4 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic - Agent tools: honor `agent.tools` allow/deny policy even when sandbox is off. - Discord: avoid duplicate replies when OpenAI emits repeated `message_end` events. - Commands: unify /status (inline) and command auth across providers; group bypass for authorized control commands; remove Discord /clawd slash handler. -- CLI: run `clawdbot agent` via the Gateway by default; use `--local` to force embedded mode. +- CLI: run `moltbot agent` via the Gateway by default; use `--local` to force embedded mode. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7b37ea389..509d5b11a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,11 +1,11 @@ -# Contributing to Clawdbot +# Contributing to Moltbot Welcome to the lobster tank! 🦞 ## Quick Links -- **GitHub:** https://github.com/clawdbot/clawdbot +- **GitHub:** https://github.com/moltbot/moltbot - **Discord:** https://discord.gg/qkhbAGHRBT -- **X/Twitter:** [@steipete](https://x.com/steipete) / [@clawdbot](https://x.com/clawdbot) +- **X/Twitter:** [@steipete](https://x.com/steipete) / [@moltbot](https://x.com/moltbot) ## Maintainers @@ -20,11 +20,11 @@ Welcome to the lobster tank! 🦞 ## How to Contribute 1. **Bugs & small fixes** → Open a PR! -2. **New features / architecture** → Start a [GitHub Discussion](https://github.com/clawdbot/clawdbot/discussions) or ask in Discord first +2. **New features / architecture** → Start a [GitHub Discussion](https://github.com/moltbot/moltbot/discussions) or ask in Discord first 3. **Questions** → Discord #setup-help ## Before You PR -- Test locally with your Clawdbot instance +- Test locally with your Moltbot instance - Run linter: `npm run lint` - Keep PRs focused (one thing per PR) - Describe what & why @@ -40,3 +40,13 @@ Please include in your PR: - [ ] Confirm you understand what the code does AI PRs are first-class citizens here. We just want transparency so reviewers know what to look for. + +## Current Focus & Roadmap 🗺 + +We are currently prioritizing: +- **Stability**: Fixing edge cases in channel connections (WhatsApp/Telegram). +- **UX**: Improving the onboarding wizard and error messages. +- **Skills**: Expanding the library of bundled skills and improving the Skill Creation developer experience. +- **Performance**: Optimizing token usage and compaction logic. + +Check the [GitHub Issues](https://github.com/moltbot/moltbot/issues) for "good first issue" labels! diff --git a/Dockerfile b/Dockerfile index a33f0077d..9c6aa7036 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ COPY scripts ./scripts RUN pnpm install --frozen-lockfile COPY . . -RUN pnpm build +RUN CLAWDBOT_A2UI_SKIP_MISSING=1 pnpm build # Force pnpm for UI build (Bun may fail on ARM/Synology architectures) ENV CLAWDBOT_PREFER_PNPM=1 RUN pnpm ui:install @@ -32,4 +32,9 @@ RUN pnpm ui:build ENV NODE_ENV=production +# Security hardening: Run as non-root user +# The node:22-bookworm image includes a 'node' user (uid 1000) +# This reduces the attack surface by preventing container escape via root privileges +USER node + CMD ["node", "dist/index.js"] diff --git a/Dockerfile.sandbox-browser b/Dockerfile.sandbox-browser index 3b18917b1..8d8a08701 100644 --- a/Dockerfile.sandbox-browser +++ b/Dockerfile.sandbox-browser @@ -20,9 +20,9 @@ RUN apt-get update \ xvfb \ && rm -rf /var/lib/apt/lists/* -COPY scripts/sandbox-browser-entrypoint.sh /usr/local/bin/clawdbot-sandbox-browser -RUN chmod +x /usr/local/bin/clawdbot-sandbox-browser +COPY scripts/sandbox-browser-entrypoint.sh /usr/local/bin/moltbot-sandbox-browser +RUN chmod +x /usr/local/bin/moltbot-sandbox-browser EXPOSE 9222 5900 6080 -CMD ["clawdbot-sandbox-browser"] +CMD ["moltbot-sandbox-browser"] diff --git a/README.md b/README.md index 2c2093b95..7e884be33 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# 🦞 Clawdbot — Personal AI Assistant +# 🦞 Moltbot — Personal AI Assistant

- Clawdbot + Clawdbot

@@ -9,67 +9,68 @@

- CI status - GitHub release - DeepWiki + CI status + GitHub release + DeepWiki Discord MIT License

-**Clawdbot** is a *personal AI assistant* you run on your own devices. +**Moltbot** is a *personal AI assistant* you run on your own devices. It answers you on the channels you already use (WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, Microsoft Teams, WebChat), plus extension channels like BlueBubbles, Matrix, Zalo, and Zalo Personal. It can speak and listen on macOS/iOS/Android, and can render a live Canvas you control. The Gateway is just the control plane — the product is the assistant. If you want a personal, single-user assistant that feels local, fast, and always-on, this is it. -[Website](https://clawdbot.com) · [Docs](https://docs.clawd.bot) · [Getting Started](https://docs.clawd.bot/start/getting-started) · [Updating](https://docs.clawd.bot/install/updating) · [Showcase](https://docs.clawd.bot/start/showcase) · [FAQ](https://docs.clawd.bot/start/faq) · [Wizard](https://docs.clawd.bot/start/wizard) · [Nix](https://github.com/clawdbot/nix-clawdbot) · [Docker](https://docs.clawd.bot/install/docker) · [Discord](https://discord.gg/clawd) +[Website](https://molt.bot) · [Docs](https://docs.molt.bot) · [Getting Started](https://docs.molt.bot/start/getting-started) · [Updating](https://docs.molt.bot/install/updating) · [Showcase](https://docs.molt.bot/start/showcase) · [FAQ](https://docs.molt.bot/start/faq) · [Wizard](https://docs.molt.bot/start/wizard) · [Nix](https://github.com/moltbot/nix-clawdbot) · [Docker](https://docs.molt.bot/install/docker) · [Discord](https://discord.gg/clawd) -Preferred setup: run the onboarding wizard (`clawdbot onboard`). It walks through gateway, workspace, channels, and skills. The CLI wizard is the recommended path and works on **macOS, Linux, and Windows (via WSL2; strongly recommended)**. +Preferred setup: run the onboarding wizard (`moltbot onboard`). It walks through gateway, workspace, channels, and skills. The CLI wizard is the recommended path and works on **macOS, Linux, and Windows (via WSL2; strongly recommended)**. Works with npm, pnpm, or bun. -New install? Start here: [Getting started](https://docs.clawd.bot/start/getting-started) +New install? Start here: [Getting started](https://docs.molt.bot/start/getting-started) **Subscriptions (OAuth):** - **[Anthropic](https://www.anthropic.com/)** (Claude Pro/Max) - **[OpenAI](https://openai.com/)** (ChatGPT/Codex) -Model note: while any model is supported, I strongly recommend **Anthropic Pro/Max (100/200) + Opus 4.5** for long‑context strength and better prompt‑injection resistance. See [Onboarding](https://docs.clawd.bot/start/onboarding). +Model note: while any model is supported, I strongly recommend **Anthropic Pro/Max (100/200) + Opus 4.5** for long‑context strength and better prompt‑injection resistance. See [Onboarding](https://docs.molt.bot/start/onboarding). ## Models (selection + auth) -- Models config + CLI: [Models](https://docs.clawd.bot/concepts/models) -- Auth profile rotation (OAuth vs API keys) + fallbacks: [Model failover](https://docs.clawd.bot/concepts/model-failover) +- Models config + CLI: [Models](https://docs.molt.bot/concepts/models) +- Auth profile rotation (OAuth vs API keys) + fallbacks: [Model failover](https://docs.molt.bot/concepts/model-failover) ## Install (recommended) Runtime: **Node ≥22**. ```bash -npm install -g clawdbot@latest -# or: pnpm add -g clawdbot@latest +npm install -g moltbot@latest +# or: pnpm add -g moltbot@latest -clawdbot onboard --install-daemon +moltbot onboard --install-daemon ``` The wizard installs the Gateway daemon (launchd/systemd user service) so it stays running. +Legacy note: `clawdbot` remains available as a compatibility shim. ## Quick start (TL;DR) Runtime: **Node ≥22**. -Full beginner guide (auth, pairing, channels): [Getting started](https://docs.clawd.bot/start/getting-started) +Full beginner guide (auth, pairing, channels): [Getting started](https://docs.molt.bot/start/getting-started) ```bash -clawdbot onboard --install-daemon +moltbot onboard --install-daemon -clawdbot gateway --port 18789 --verbose +moltbot gateway --port 18789 --verbose # Send a message -clawdbot message send --to +1234567890 --message "Hello from Clawdbot" +moltbot message send --to +1234567890 --message "Hello from Moltbot" # Talk to the assistant (optionally deliver back to any connected channel: WhatsApp/Telegram/Slack/Discord/Google Chat/Signal/iMessage/BlueBubbles/Microsoft Teams/Matrix/Zalo/Zalo Personal/WebChat) -clawdbot agent --message "Ship checklist" --thinking high +moltbot agent --message "Ship checklist" --thinking high ``` -Upgrading? [Updating guide](https://docs.clawd.bot/install/updating) (and run `clawdbot doctor`). +Upgrading? [Updating guide](https://docs.molt.bot/install/updating) (and run `moltbot doctor`). ## Development channels @@ -77,94 +78,94 @@ Upgrading? [Updating guide](https://docs.clawd.bot/install/updating) (and run `c - **beta**: prerelease tags (`vYYYY.M.D-beta.N`), npm dist-tag `beta` (macOS app may be missing). - **dev**: moving head of `main`, npm dist-tag `dev` (when published). -Switch channels (git + npm): `clawdbot update --channel stable|beta|dev`. -Details: [Development channels](https://docs.clawd.bot/install/development-channels). +Switch channels (git + npm): `moltbot update --channel stable|beta|dev`. +Details: [Development channels](https://docs.molt.bot/install/development-channels). ## From source (development) Prefer `pnpm` for builds from source. Bun is optional for running TypeScript directly. ```bash -git clone https://github.com/clawdbot/clawdbot.git -cd clawdbot +git clone https://github.com/moltbot/moltbot.git +cd moltbot pnpm install pnpm ui:build # auto-installs UI deps on first run pnpm build -pnpm clawdbot onboard --install-daemon +pnpm moltbot onboard --install-daemon # Dev loop (auto-reload on TS changes) pnpm gateway:watch ``` -Note: `pnpm clawdbot ...` runs TypeScript directly (via `tsx`). `pnpm build` produces `dist/` for running via Node / the packaged `clawdbot` binary. +Note: `pnpm moltbot ...` runs TypeScript directly (via `tsx`). `pnpm build` produces `dist/` for running via Node / the packaged `moltbot` binary. ## Security defaults (DM access) -Clawdbot connects to real messaging surfaces. Treat inbound DMs as **untrusted input**. +Moltbot connects to real messaging surfaces. Treat inbound DMs as **untrusted input**. -Full security guide: [Security](https://docs.clawd.bot/gateway/security) +Full security guide: [Security](https://docs.molt.bot/gateway/security) Default behavior on Telegram/WhatsApp/Signal/iMessage/Microsoft Teams/Discord/Google Chat/Slack: - **DM pairing** (`dmPolicy="pairing"` / `channels.discord.dm.policy="pairing"` / `channels.slack.dm.policy="pairing"`): unknown senders receive a short pairing code and the bot does not process their message. -- Approve with: `clawdbot pairing approve ` (then the sender is added to a local allowlist store). +- Approve with: `moltbot pairing approve ` (then the sender is added to a local allowlist store). - Public inbound DMs require an explicit opt-in: set `dmPolicy="open"` and include `"*"` in the channel allowlist (`allowFrom` / `channels.discord.dm.allowFrom` / `channels.slack.dm.allowFrom`). -Run `clawdbot doctor` to surface risky/misconfigured DM policies. +Run `moltbot doctor` to surface risky/misconfigured DM policies. ## Highlights -- **[Local-first Gateway](https://docs.clawd.bot/gateway)** — single control plane for sessions, channels, tools, and events. -- **[Multi-channel inbox](https://docs.clawd.bot/channels)** — WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, BlueBubbles, Microsoft Teams, Matrix, Zalo, Zalo Personal, WebChat, macOS, iOS/Android. -- **[Multi-agent routing](https://docs.clawd.bot/gateway/configuration)** — route inbound channels/accounts/peers to isolated agents (workspaces + per-agent sessions). -- **[Voice Wake](https://docs.clawd.bot/nodes/voicewake) + [Talk Mode](https://docs.clawd.bot/nodes/talk)** — always-on speech for macOS/iOS/Android with ElevenLabs. -- **[Live Canvas](https://docs.clawd.bot/platforms/mac/canvas)** — agent-driven visual workspace with [A2UI](https://docs.clawd.bot/platforms/mac/canvas#canvas-a2ui). -- **[First-class tools](https://docs.clawd.bot/tools)** — browser, canvas, nodes, cron, sessions, and Discord/Slack actions. -- **[Companion apps](https://docs.clawd.bot/platforms/macos)** — macOS menu bar app + iOS/Android [nodes](https://docs.clawd.bot/nodes). -- **[Onboarding](https://docs.clawd.bot/start/wizard) + [skills](https://docs.clawd.bot/tools/skills)** — wizard-driven setup with bundled/managed/workspace skills. +- **[Local-first Gateway](https://docs.molt.bot/gateway)** — single control plane for sessions, channels, tools, and events. +- **[Multi-channel inbox](https://docs.molt.bot/channels)** — WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, BlueBubbles, Microsoft Teams, Matrix, Zalo, Zalo Personal, WebChat, macOS, iOS/Android. +- **[Multi-agent routing](https://docs.molt.bot/gateway/configuration)** — route inbound channels/accounts/peers to isolated agents (workspaces + per-agent sessions). +- **[Voice Wake](https://docs.molt.bot/nodes/voicewake) + [Talk Mode](https://docs.molt.bot/nodes/talk)** — always-on speech for macOS/iOS/Android with ElevenLabs. +- **[Live Canvas](https://docs.molt.bot/platforms/mac/canvas)** — agent-driven visual workspace with [A2UI](https://docs.molt.bot/platforms/mac/canvas#canvas-a2ui). +- **[First-class tools](https://docs.molt.bot/tools)** — browser, canvas, nodes, cron, sessions, and Discord/Slack actions. +- **[Companion apps](https://docs.molt.bot/platforms/macos)** — macOS menu bar app + iOS/Android [nodes](https://docs.molt.bot/nodes). +- **[Onboarding](https://docs.molt.bot/start/wizard) + [skills](https://docs.molt.bot/tools/skills)** — wizard-driven setup with bundled/managed/workspace skills. ## Star History -[![Star History Chart](https://api.star-history.com/svg?repos=clawdbot/clawdbot&type=date&legend=top-left)](https://www.star-history.com/#clawdbot/clawdbot&type=date&legend=top-left) +[![Star History Chart](https://api.star-history.com/svg?repos=moltbot/moltbot&type=date&legend=top-left)](https://www.star-history.com/#moltbot/moltbot&type=date&legend=top-left) ## Everything we built so far ### Core platform -- [Gateway WS control plane](https://docs.clawd.bot/gateway) with sessions, presence, config, cron, webhooks, [Control UI](https://docs.clawd.bot/web), and [Canvas host](https://docs.clawd.bot/platforms/mac/canvas#canvas-a2ui). -- [CLI surface](https://docs.clawd.bot/tools/agent-send): gateway, agent, send, [wizard](https://docs.clawd.bot/start/wizard), and [doctor](https://docs.clawd.bot/gateway/doctor). -- [Pi agent runtime](https://docs.clawd.bot/concepts/agent) in RPC mode with tool streaming and block streaming. -- [Session model](https://docs.clawd.bot/concepts/session): `main` for direct chats, group isolation, activation modes, queue modes, reply-back. Group rules: [Groups](https://docs.clawd.bot/concepts/groups). -- [Media pipeline](https://docs.clawd.bot/nodes/images): images/audio/video, transcription hooks, size caps, temp file lifecycle. Audio details: [Audio](https://docs.clawd.bot/nodes/audio). +- [Gateway WS control plane](https://docs.molt.bot/gateway) with sessions, presence, config, cron, webhooks, [Control UI](https://docs.molt.bot/web), and [Canvas host](https://docs.molt.bot/platforms/mac/canvas#canvas-a2ui). +- [CLI surface](https://docs.molt.bot/tools/agent-send): gateway, agent, send, [wizard](https://docs.molt.bot/start/wizard), and [doctor](https://docs.molt.bot/gateway/doctor). +- [Pi agent runtime](https://docs.molt.bot/concepts/agent) in RPC mode with tool streaming and block streaming. +- [Session model](https://docs.molt.bot/concepts/session): `main` for direct chats, group isolation, activation modes, queue modes, reply-back. Group rules: [Groups](https://docs.molt.bot/concepts/groups). +- [Media pipeline](https://docs.molt.bot/nodes/images): images/audio/video, transcription hooks, size caps, temp file lifecycle. Audio details: [Audio](https://docs.molt.bot/nodes/audio). ### Channels -- [Channels](https://docs.clawd.bot/channels): [WhatsApp](https://docs.clawd.bot/channels/whatsapp) (Baileys), [Telegram](https://docs.clawd.bot/channels/telegram) (grammY), [Slack](https://docs.clawd.bot/channels/slack) (Bolt), [Discord](https://docs.clawd.bot/channels/discord) (discord.js), [Google Chat](https://docs.clawd.bot/channels/googlechat) (Chat API), [Signal](https://docs.clawd.bot/channels/signal) (signal-cli), [iMessage](https://docs.clawd.bot/channels/imessage) (imsg), [BlueBubbles](https://docs.clawd.bot/channels/bluebubbles) (extension), [Microsoft Teams](https://docs.clawd.bot/channels/msteams) (extension), [Matrix](https://docs.clawd.bot/channels/matrix) (extension), [Zalo](https://docs.clawd.bot/channels/zalo) (extension), [Zalo Personal](https://docs.clawd.bot/channels/zalouser) (extension), [WebChat](https://docs.clawd.bot/web/webchat). -- [Group routing](https://docs.clawd.bot/concepts/group-messages): mention gating, reply tags, per-channel chunking and routing. Channel rules: [Channels](https://docs.clawd.bot/channels). +- [Channels](https://docs.molt.bot/channels): [WhatsApp](https://docs.molt.bot/channels/whatsapp) (Baileys), [Telegram](https://docs.molt.bot/channels/telegram) (grammY), [Slack](https://docs.molt.bot/channels/slack) (Bolt), [Discord](https://docs.molt.bot/channels/discord) (discord.js), [Google Chat](https://docs.molt.bot/channels/googlechat) (Chat API), [Signal](https://docs.molt.bot/channels/signal) (signal-cli), [iMessage](https://docs.molt.bot/channels/imessage) (imsg), [BlueBubbles](https://docs.molt.bot/channels/bluebubbles) (extension), [Microsoft Teams](https://docs.molt.bot/channels/msteams) (extension), [Matrix](https://docs.molt.bot/channels/matrix) (extension), [Zalo](https://docs.molt.bot/channels/zalo) (extension), [Zalo Personal](https://docs.molt.bot/channels/zalouser) (extension), [WebChat](https://docs.molt.bot/web/webchat). +- [Group routing](https://docs.molt.bot/concepts/group-messages): mention gating, reply tags, per-channel chunking and routing. Channel rules: [Channels](https://docs.molt.bot/channels). ### Apps + nodes -- [macOS app](https://docs.clawd.bot/platforms/macos): menu bar control plane, [Voice Wake](https://docs.clawd.bot/nodes/voicewake)/PTT, [Talk Mode](https://docs.clawd.bot/nodes/talk) overlay, [WebChat](https://docs.clawd.bot/web/webchat), debug tools, [remote gateway](https://docs.clawd.bot/gateway/remote) control. -- [iOS node](https://docs.clawd.bot/platforms/ios): [Canvas](https://docs.clawd.bot/platforms/mac/canvas), [Voice Wake](https://docs.clawd.bot/nodes/voicewake), [Talk Mode](https://docs.clawd.bot/nodes/talk), camera, screen recording, Bonjour pairing. -- [Android node](https://docs.clawd.bot/platforms/android): [Canvas](https://docs.clawd.bot/platforms/mac/canvas), [Talk Mode](https://docs.clawd.bot/nodes/talk), camera, screen recording, optional SMS. -- [macOS node mode](https://docs.clawd.bot/nodes): system.run/notify + canvas/camera exposure. +- [macOS app](https://docs.molt.bot/platforms/macos): menu bar control plane, [Voice Wake](https://docs.molt.bot/nodes/voicewake)/PTT, [Talk Mode](https://docs.molt.bot/nodes/talk) overlay, [WebChat](https://docs.molt.bot/web/webchat), debug tools, [remote gateway](https://docs.molt.bot/gateway/remote) control. +- [iOS node](https://docs.molt.bot/platforms/ios): [Canvas](https://docs.molt.bot/platforms/mac/canvas), [Voice Wake](https://docs.molt.bot/nodes/voicewake), [Talk Mode](https://docs.molt.bot/nodes/talk), camera, screen recording, Bonjour pairing. +- [Android node](https://docs.molt.bot/platforms/android): [Canvas](https://docs.molt.bot/platforms/mac/canvas), [Talk Mode](https://docs.molt.bot/nodes/talk), camera, screen recording, optional SMS. +- [macOS node mode](https://docs.molt.bot/nodes): system.run/notify + canvas/camera exposure. ### Tools + automation -- [Browser control](https://docs.clawd.bot/tools/browser): dedicated clawd Chrome/Chromium, snapshots, actions, uploads, profiles. -- [Canvas](https://docs.clawd.bot/platforms/mac/canvas): [A2UI](https://docs.clawd.bot/platforms/mac/canvas#canvas-a2ui) push/reset, eval, snapshot. -- [Nodes](https://docs.clawd.bot/nodes): camera snap/clip, screen record, [location.get](https://docs.clawd.bot/nodes/location-command), notifications. -- [Cron + wakeups](https://docs.clawd.bot/automation/cron-jobs); [webhooks](https://docs.clawd.bot/automation/webhook); [Gmail Pub/Sub](https://docs.clawd.bot/automation/gmail-pubsub). -- [Skills platform](https://docs.clawd.bot/tools/skills): bundled, managed, and workspace skills with install gating + UI. +- [Browser control](https://docs.molt.bot/tools/browser): dedicated moltbot Chrome/Chromium, snapshots, actions, uploads, profiles. +- [Canvas](https://docs.molt.bot/platforms/mac/canvas): [A2UI](https://docs.molt.bot/platforms/mac/canvas#canvas-a2ui) push/reset, eval, snapshot. +- [Nodes](https://docs.molt.bot/nodes): camera snap/clip, screen record, [location.get](https://docs.molt.bot/nodes/location-command), notifications. +- [Cron + wakeups](https://docs.molt.bot/automation/cron-jobs); [webhooks](https://docs.molt.bot/automation/webhook); [Gmail Pub/Sub](https://docs.molt.bot/automation/gmail-pubsub). +- [Skills platform](https://docs.molt.bot/tools/skills): bundled, managed, and workspace skills with install gating + UI. ### Runtime + safety -- [Channel routing](https://docs.clawd.bot/concepts/channel-routing), [retry policy](https://docs.clawd.bot/concepts/retry), and [streaming/chunking](https://docs.clawd.bot/concepts/streaming). -- [Presence](https://docs.clawd.bot/concepts/presence), [typing indicators](https://docs.clawd.bot/concepts/typing-indicators), and [usage tracking](https://docs.clawd.bot/concepts/usage-tracking). -- [Models](https://docs.clawd.bot/concepts/models), [model failover](https://docs.clawd.bot/concepts/model-failover), and [session pruning](https://docs.clawd.bot/concepts/session-pruning). -- [Security](https://docs.clawd.bot/gateway/security) and [troubleshooting](https://docs.clawd.bot/channels/troubleshooting). +- [Channel routing](https://docs.molt.bot/concepts/channel-routing), [retry policy](https://docs.molt.bot/concepts/retry), and [streaming/chunking](https://docs.molt.bot/concepts/streaming). +- [Presence](https://docs.molt.bot/concepts/presence), [typing indicators](https://docs.molt.bot/concepts/typing-indicators), and [usage tracking](https://docs.molt.bot/concepts/usage-tracking). +- [Models](https://docs.molt.bot/concepts/models), [model failover](https://docs.molt.bot/concepts/model-failover), and [session pruning](https://docs.molt.bot/concepts/session-pruning). +- [Security](https://docs.molt.bot/gateway/security) and [troubleshooting](https://docs.molt.bot/channels/troubleshooting). ### Ops + packaging -- [Control UI](https://docs.clawd.bot/web) + [WebChat](https://docs.clawd.bot/web/webchat) served directly from the Gateway. -- [Tailscale Serve/Funnel](https://docs.clawd.bot/gateway/tailscale) or [SSH tunnels](https://docs.clawd.bot/gateway/remote) with token/password auth. -- [Nix mode](https://docs.clawd.bot/install/nix) for declarative config; [Docker](https://docs.clawd.bot/install/docker)-based installs. -- [Doctor](https://docs.clawd.bot/gateway/doctor) migrations, [logging](https://docs.clawd.bot/logging). +- [Control UI](https://docs.molt.bot/web) + [WebChat](https://docs.molt.bot/web/webchat) served directly from the Gateway. +- [Tailscale Serve/Funnel](https://docs.molt.bot/gateway/tailscale) or [SSH tunnels](https://docs.molt.bot/gateway/remote) with token/password auth. +- [Nix mode](https://docs.molt.bot/install/nix) for declarative config; [Docker](https://docs.molt.bot/install/docker)-based installs. +- [Doctor](https://docs.molt.bot/gateway/doctor) migrations, [logging](https://docs.molt.bot/logging). ## How it works (short) @@ -179,7 +180,7 @@ WhatsApp / Telegram / Slack / Discord / Google Chat / Signal / iMessage / BlueBu └──────────────┬────────────────┘ │ ├─ Pi agent (RPC) - ├─ CLI (clawdbot …) + ├─ CLI (moltbot …) ├─ WebChat UI ├─ macOS app └─ iOS / Android nodes @@ -187,28 +188,28 @@ WhatsApp / Telegram / Slack / Discord / Google Chat / Signal / iMessage / BlueBu ## Key subsystems -- **[Gateway WebSocket network](https://docs.clawd.bot/concepts/architecture)** — single WS control plane for clients, tools, and events (plus ops: [Gateway runbook](https://docs.clawd.bot/gateway)). -- **[Tailscale exposure](https://docs.clawd.bot/gateway/tailscale)** — Serve/Funnel for the Gateway dashboard + WS (remote access: [Remote](https://docs.clawd.bot/gateway/remote)). -- **[Browser control](https://docs.clawd.bot/tools/browser)** — clawd‑managed Chrome/Chromium with CDP control. -- **[Canvas + A2UI](https://docs.clawd.bot/platforms/mac/canvas)** — agent‑driven visual workspace (A2UI host: [Canvas/A2UI](https://docs.clawd.bot/platforms/mac/canvas#canvas-a2ui)). -- **[Voice Wake](https://docs.clawd.bot/nodes/voicewake) + [Talk Mode](https://docs.clawd.bot/nodes/talk)** — always‑on speech and continuous conversation. -- **[Nodes](https://docs.clawd.bot/nodes)** — Canvas, camera snap/clip, screen record, `location.get`, notifications, plus macOS‑only `system.run`/`system.notify`. +- **[Gateway WebSocket network](https://docs.molt.bot/concepts/architecture)** — single WS control plane for clients, tools, and events (plus ops: [Gateway runbook](https://docs.molt.bot/gateway)). +- **[Tailscale exposure](https://docs.molt.bot/gateway/tailscale)** — Serve/Funnel for the Gateway dashboard + WS (remote access: [Remote](https://docs.molt.bot/gateway/remote)). +- **[Browser control](https://docs.molt.bot/tools/browser)** — moltbot‑managed Chrome/Chromium with CDP control. +- **[Canvas + A2UI](https://docs.molt.bot/platforms/mac/canvas)** — agent‑driven visual workspace (A2UI host: [Canvas/A2UI](https://docs.molt.bot/platforms/mac/canvas#canvas-a2ui)). +- **[Voice Wake](https://docs.molt.bot/nodes/voicewake) + [Talk Mode](https://docs.molt.bot/nodes/talk)** — always‑on speech and continuous conversation. +- **[Nodes](https://docs.molt.bot/nodes)** — Canvas, camera snap/clip, screen record, `location.get`, notifications, plus macOS‑only `system.run`/`system.notify`. ## Tailscale access (Gateway dashboard) -Clawdbot can auto-configure Tailscale **Serve** (tailnet-only) or **Funnel** (public) while the Gateway stays bound to loopback. Configure `gateway.tailscale.mode`: +Moltbot can auto-configure Tailscale **Serve** (tailnet-only) or **Funnel** (public) while the Gateway stays bound to loopback. Configure `gateway.tailscale.mode`: - `off`: no Tailscale automation (default). - `serve`: tailnet-only HTTPS via `tailscale serve` (uses Tailscale identity headers by default). - `funnel`: public HTTPS via `tailscale funnel` (requires shared password auth). Notes: -- `gateway.bind` must stay `loopback` when Serve/Funnel is enabled (Clawdbot enforces this). +- `gateway.bind` must stay `loopback` when Serve/Funnel is enabled (Moltbot enforces this). - Serve can be forced to require a password by setting `gateway.auth.mode: "password"` or `gateway.auth.allowTailscale: false`. - Funnel refuses to start unless `gateway.auth.mode: "password"` is set. - Optional: `gateway.tailscale.resetOnExit` to undo Serve/Funnel on shutdown. -Details: [Tailscale guide](https://docs.clawd.bot/gateway/tailscale) · [Web surfaces](https://docs.clawd.bot/web) +Details: [Tailscale guide](https://docs.molt.bot/gateway/tailscale) · [Web surfaces](https://docs.molt.bot/web) ## Remote Gateway (Linux is great) @@ -218,7 +219,7 @@ It’s perfectly fine to run the Gateway on a small Linux instance. Clients (mac - **Device nodes** run device‑local actions (`system.run`, camera, screen recording, notifications) via `node.invoke`. In short: exec runs where the Gateway lives; device actions run where the device lives. -Details: [Remote access](https://docs.clawd.bot/gateway/remote) · [Nodes](https://docs.clawd.bot/nodes) · [Security](https://docs.clawd.bot/gateway/security) +Details: [Remote access](https://docs.molt.bot/gateway/remote) · [Nodes](https://docs.molt.bot/nodes) · [Security](https://docs.molt.bot/gateway/security) ## macOS permissions via the Gateway protocol @@ -233,7 +234,7 @@ Elevated bash (host permissions) is separate from macOS TCC: - Use `/elevated on|off` to toggle per‑session elevated access when enabled + allowlisted. - Gateway persists the per‑session toggle via `sessions.patch` (WS method) alongside `thinkingLevel`, `verboseLevel`, `model`, `sendPolicy`, and `groupActivation`. -Details: [Nodes](https://docs.clawd.bot/nodes) · [macOS app](https://docs.clawd.bot/platforms/macos) · [Gateway protocol](https://docs.clawd.bot/concepts/architecture) +Details: [Nodes](https://docs.molt.bot/nodes) · [macOS app](https://docs.molt.bot/platforms/macos) · [Gateway protocol](https://docs.molt.bot/concepts/architecture) ## Agent to Agent (sessions_* tools) @@ -242,7 +243,7 @@ Details: [Nodes](https://docs.clawd.bot/nodes) · [macOS app](https://docs.clawd - `sessions_history` — fetch transcript logs for a session. - `sessions_send` — message another session; optional reply‑back ping‑pong + announce step (`REPLY_SKIP`, `ANNOUNCE_SKIP`). -Details: [Session tools](https://docs.clawd.bot/concepts/session-tool) +Details: [Session tools](https://docs.molt.bot/concepts/session-tool) ## Skills registry (ClawdHub) @@ -269,7 +270,7 @@ The Gateway alone delivers a great experience. All apps are optional and add ext If you plan to build/run companion apps, follow the platform runbooks below. -### macOS (Clawdbot.app) (optional) +### macOS (Moltbot.app) (optional) - Menu bar control for the Gateway and health. - Voice Wake + push-to-talk overlay. @@ -282,15 +283,15 @@ Note: signed builds required for macOS permissions to stick across rebuilds (see - Pairs as a node via the Bridge. - Voice trigger forwarding + Canvas surface. -- Controlled via `clawdbot nodes …`. +- Controlled via `moltbot nodes …`. -Runbook: [iOS connect](https://docs.clawd.bot/platforms/ios). +Runbook: [iOS connect](https://docs.molt.bot/platforms/ios). ### Android node (optional) - Pairs via the same Bridge + pairing flow as iOS. - Exposes Canvas, Camera, and Screen capture commands. -- Runbook: [Android connect](https://docs.clawd.bot/platforms/android). +- Runbook: [Android connect](https://docs.molt.bot/platforms/android). ## Agent workspace + skills @@ -300,7 +301,7 @@ Runbook: [iOS connect](https://docs.clawd.bot/platforms/ios). ## Configuration -Minimal `~/.clawdbot/clawdbot.json` (model + defaults): +Minimal `~/.clawdbot/moltbot.json` (model + defaults): ```json5 { @@ -310,7 +311,7 @@ Minimal `~/.clawdbot/clawdbot.json` (model + defaults): } ``` -[Full configuration reference (all keys + examples).](https://docs.clawd.bot/gateway/configuration) +[Full configuration reference (all keys + examples).](https://docs.molt.bot/gateway/configuration) ## Security model (important) @@ -318,15 +319,15 @@ Minimal `~/.clawdbot/clawdbot.json` (model + defaults): - **Group/channel safety:** set `agents.defaults.sandbox.mode: "non-main"` to run **non‑main sessions** (groups/channels) inside per‑session Docker sandboxes; bash then runs in Docker for those sessions. - **Sandbox defaults:** allowlist `bash`, `process`, `read`, `write`, `edit`, `sessions_list`, `sessions_history`, `sessions_send`, `sessions_spawn`; denylist `browser`, `canvas`, `nodes`, `cron`, `discord`, `gateway`. -Details: [Security guide](https://docs.clawd.bot/gateway/security) · [Docker + sandboxing](https://docs.clawd.bot/install/docker) · [Sandbox config](https://docs.clawd.bot/gateway/configuration) +Details: [Security guide](https://docs.molt.bot/gateway/security) · [Docker + sandboxing](https://docs.molt.bot/install/docker) · [Sandbox config](https://docs.molt.bot/gateway/configuration) -### [WhatsApp](https://docs.clawd.bot/channels/whatsapp) +### [WhatsApp](https://docs.molt.bot/channels/whatsapp) -- Link the device: `pnpm clawdbot channels login` (stores creds in `~/.clawdbot/credentials`). +- Link the device: `pnpm moltbot channels login` (stores creds in `~/.clawdbot/credentials`). - Allowlist who can talk to the assistant via `channels.whatsapp.allowFrom`. - If `channels.whatsapp.groups` is set, it becomes a group allowlist; include `"*"` to allow all. -### [Telegram](https://docs.clawd.bot/channels/telegram) +### [Telegram](https://docs.molt.bot/channels/telegram) - Set `TELEGRAM_BOT_TOKEN` or `channels.telegram.botToken` (env wins). - Optional: set `channels.telegram.groups` (with `channels.telegram.groups."*".requireMention`); when set, it is a group allowlist (include `"*"` to allow all). Also `channels.telegram.allowFrom` or `channels.telegram.webhookUrl` as needed. @@ -341,11 +342,11 @@ Details: [Security guide](https://docs.clawd.bot/gateway/security) · [Docker + } ``` -### [Slack](https://docs.clawd.bot/channels/slack) +### [Slack](https://docs.molt.bot/channels/slack) - Set `SLACK_BOT_TOKEN` + `SLACK_APP_TOKEN` (or `channels.slack.botToken` + `channels.slack.appToken`). -### [Discord](https://docs.clawd.bot/channels/discord) +### [Discord](https://docs.molt.bot/channels/discord) - Set `DISCORD_BOT_TOKEN` or `channels.discord.token` (env wins). - Optional: set `commands.native`, `commands.text`, or `commands.useAccessGroups`, plus `channels.discord.dm.allowFrom`, `channels.discord.guilds`, or `channels.discord.mediaMaxMb` as needed. @@ -360,21 +361,21 @@ Details: [Security guide](https://docs.clawd.bot/gateway/security) · [Docker + } ``` -### [Signal](https://docs.clawd.bot/channels/signal) +### [Signal](https://docs.molt.bot/channels/signal) - Requires `signal-cli` and a `channels.signal` config section. -### [iMessage](https://docs.clawd.bot/channels/imessage) +### [iMessage](https://docs.molt.bot/channels/imessage) - macOS only; Messages must be signed in. - If `channels.imessage.groups` is set, it becomes a group allowlist; include `"*"` to allow all. -### [Microsoft Teams](https://docs.clawd.bot/channels/msteams) +### [Microsoft Teams](https://docs.molt.bot/channels/msteams) - Configure a Teams app + Bot Framework, then add a `msteams` config section. - Allowlist who can talk via `msteams.allowFrom`; group access via `msteams.groupAllowFrom` or `msteams.groupPolicy: "open"`. -### [WebChat](https://docs.clawd.bot/web/webchat) +### [WebChat](https://docs.molt.bot/web/webchat) - Uses the Gateway WebSocket; no separate WebChat port/config. @@ -384,7 +385,6 @@ Browser control (optional): { browser: { enabled: true, - controlUrl: "http://127.0.0.1:18791", color: "#FF4500" } } @@ -393,82 +393,83 @@ Browser control (optional): ## Docs Use these when you’re past the onboarding flow and want the deeper reference. -- [Start with the docs index for navigation and “what’s where.”](https://docs.clawd.bot) -- [Read the architecture overview for the gateway + protocol model.](https://docs.clawd.bot/concepts/architecture) -- [Use the full configuration reference when you need every key and example.](https://docs.clawd.bot/gateway/configuration) -- [Run the Gateway by the book with the operational runbook.](https://docs.clawd.bot/gateway) -- [Learn how the Control UI/Web surfaces work and how to expose them safely.](https://docs.clawd.bot/web) -- [Understand remote access over SSH tunnels or tailnets.](https://docs.clawd.bot/gateway/remote) -- [Follow the onboarding wizard flow for a guided setup.](https://docs.clawd.bot/start/wizard) -- [Wire external triggers via the webhook surface.](https://docs.clawd.bot/automation/webhook) -- [Set up Gmail Pub/Sub triggers.](https://docs.clawd.bot/automation/gmail-pubsub) -- [Learn the macOS menu bar companion details.](https://docs.clawd.bot/platforms/mac/menu-bar) -- [Platform guides: Windows (WSL2)](https://docs.clawd.bot/platforms/windows), [Linux](https://docs.clawd.bot/platforms/linux), [macOS](https://docs.clawd.bot/platforms/macos), [iOS](https://docs.clawd.bot/platforms/ios), [Android](https://docs.clawd.bot/platforms/android) -- [Debug common failures with the troubleshooting guide.](https://docs.clawd.bot/channels/troubleshooting) -- [Review security guidance before exposing anything.](https://docs.clawd.bot/gateway/security) +- [Start with the docs index for navigation and “what’s where.”](https://docs.molt.bot) +- [Read the architecture overview for the gateway + protocol model.](https://docs.molt.bot/concepts/architecture) +- [Use the full configuration reference when you need every key and example.](https://docs.molt.bot/gateway/configuration) +- [Run the Gateway by the book with the operational runbook.](https://docs.molt.bot/gateway) +- [Learn how the Control UI/Web surfaces work and how to expose them safely.](https://docs.molt.bot/web) +- [Understand remote access over SSH tunnels or tailnets.](https://docs.molt.bot/gateway/remote) +- [Follow the onboarding wizard flow for a guided setup.](https://docs.molt.bot/start/wizard) +- [Wire external triggers via the webhook surface.](https://docs.molt.bot/automation/webhook) +- [Set up Gmail Pub/Sub triggers.](https://docs.molt.bot/automation/gmail-pubsub) +- [Learn the macOS menu bar companion details.](https://docs.molt.bot/platforms/mac/menu-bar) +- [Platform guides: Windows (WSL2)](https://docs.molt.bot/platforms/windows), [Linux](https://docs.molt.bot/platforms/linux), [macOS](https://docs.molt.bot/platforms/macos), [iOS](https://docs.molt.bot/platforms/ios), [Android](https://docs.molt.bot/platforms/android) +- [Debug common failures with the troubleshooting guide.](https://docs.molt.bot/channels/troubleshooting) +- [Review security guidance before exposing anything.](https://docs.molt.bot/gateway/security) ## Advanced docs (discovery + control) -- [Discovery + transports](https://docs.clawd.bot/gateway/discovery) -- [Bonjour/mDNS](https://docs.clawd.bot/gateway/bonjour) -- [Gateway pairing](https://docs.clawd.bot/gateway/pairing) -- [Remote gateway README](https://docs.clawd.bot/gateway/remote-gateway-readme) -- [Control UI](https://docs.clawd.bot/web/control-ui) -- [Dashboard](https://docs.clawd.bot/web/dashboard) +- [Discovery + transports](https://docs.molt.bot/gateway/discovery) +- [Bonjour/mDNS](https://docs.molt.bot/gateway/bonjour) +- [Gateway pairing](https://docs.molt.bot/gateway/pairing) +- [Remote gateway README](https://docs.molt.bot/gateway/remote-gateway-readme) +- [Control UI](https://docs.molt.bot/web/control-ui) +- [Dashboard](https://docs.molt.bot/web/dashboard) ## Operations & troubleshooting -- [Health checks](https://docs.clawd.bot/gateway/health) -- [Gateway lock](https://docs.clawd.bot/gateway/gateway-lock) -- [Background process](https://docs.clawd.bot/gateway/background-process) -- [Browser troubleshooting (Linux)](https://docs.clawd.bot/tools/browser-linux-troubleshooting) -- [Logging](https://docs.clawd.bot/logging) +- [Health checks](https://docs.molt.bot/gateway/health) +- [Gateway lock](https://docs.molt.bot/gateway/gateway-lock) +- [Background process](https://docs.molt.bot/gateway/background-process) +- [Browser troubleshooting (Linux)](https://docs.molt.bot/tools/browser-linux-troubleshooting) +- [Logging](https://docs.molt.bot/logging) ## Deep dives -- [Agent loop](https://docs.clawd.bot/concepts/agent-loop) -- [Presence](https://docs.clawd.bot/concepts/presence) -- [TypeBox schemas](https://docs.clawd.bot/concepts/typebox) -- [RPC adapters](https://docs.clawd.bot/reference/rpc) -- [Queue](https://docs.clawd.bot/concepts/queue) +- [Agent loop](https://docs.molt.bot/concepts/agent-loop) +- [Presence](https://docs.molt.bot/concepts/presence) +- [TypeBox schemas](https://docs.molt.bot/concepts/typebox) +- [RPC adapters](https://docs.molt.bot/reference/rpc) +- [Queue](https://docs.molt.bot/concepts/queue) ## Workspace & skills -- [Skills config](https://docs.clawd.bot/tools/skills-config) -- [Default AGENTS](https://docs.clawd.bot/reference/AGENTS.default) -- [Templates: AGENTS](https://docs.clawd.bot/reference/templates/AGENTS) -- [Templates: BOOTSTRAP](https://docs.clawd.bot/reference/templates/BOOTSTRAP) -- [Templates: IDENTITY](https://docs.clawd.bot/reference/templates/IDENTITY) -- [Templates: SOUL](https://docs.clawd.bot/reference/templates/SOUL) -- [Templates: TOOLS](https://docs.clawd.bot/reference/templates/TOOLS) -- [Templates: USER](https://docs.clawd.bot/reference/templates/USER) +- [Skills config](https://docs.molt.bot/tools/skills-config) +- [Default AGENTS](https://docs.molt.bot/reference/AGENTS.default) +- [Templates: AGENTS](https://docs.molt.bot/reference/templates/AGENTS) +- [Templates: BOOTSTRAP](https://docs.molt.bot/reference/templates/BOOTSTRAP) +- [Templates: IDENTITY](https://docs.molt.bot/reference/templates/IDENTITY) +- [Templates: SOUL](https://docs.molt.bot/reference/templates/SOUL) +- [Templates: TOOLS](https://docs.molt.bot/reference/templates/TOOLS) +- [Templates: USER](https://docs.molt.bot/reference/templates/USER) ## Platform internals -- [macOS dev setup](https://docs.clawd.bot/platforms/mac/dev-setup) -- [macOS menu bar](https://docs.clawd.bot/platforms/mac/menu-bar) -- [macOS voice wake](https://docs.clawd.bot/platforms/mac/voicewake) -- [iOS node](https://docs.clawd.bot/platforms/ios) -- [Android node](https://docs.clawd.bot/platforms/android) -- [Windows (WSL2)](https://docs.clawd.bot/platforms/windows) -- [Linux app](https://docs.clawd.bot/platforms/linux) +- [macOS dev setup](https://docs.molt.bot/platforms/mac/dev-setup) +- [macOS menu bar](https://docs.molt.bot/platforms/mac/menu-bar) +- [macOS voice wake](https://docs.molt.bot/platforms/mac/voicewake) +- [iOS node](https://docs.molt.bot/platforms/ios) +- [Android node](https://docs.molt.bot/platforms/android) +- [Windows (WSL2)](https://docs.molt.bot/platforms/windows) +- [Linux app](https://docs.molt.bot/platforms/linux) ## Email hooks (Gmail) -- [docs.clawd.bot/gmail-pubsub](https://docs.clawd.bot/automation/gmail-pubsub) +- [docs.molt.bot/gmail-pubsub](https://docs.molt.bot/automation/gmail-pubsub) -## Clawd +## Molty -Clawdbot was built for **Clawd**, a space lobster AI assistant. 🦞 +Moltbot was built for **Molty**, a space lobster AI assistant. 🦞 by Peter Steinberger and the community. - [clawd.me](https://clawd.me) - [soul.md](https://soul.md) - [steipete.me](https://steipete.me) +- [@moltbot](https://x.com/moltbot) ## Community -See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines, maintainers, and how to submit PRs. +See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines, maintainers, and how to submit PRs. AI/vibe-coded PRs welcome! 🤖 Special thanks to [Mario Zechner](https://mariozechner.at/) for his support and for @@ -477,32 +478,37 @@ Special thanks to [Mario Zechner](https://mariozechner.at/) for his support and Thanks to all clawtributors:

- steipete bohdanpodvirnyi iHildy joaohlisboa mneves75 MatthieuBizien MaudeBot Glucksberg rahthakor vrknetha - radek-paclt Tobias Bischoff joshp123 czekaj mukhtharcm maxsumrall xadenryan rodrigouroz juanpablodlc hsrvc - magimetal meaningfool patelhiren NicholasSpisak sebslight jonisjongithub abhisekbasu1 zerone0x jamesgroat claude - JustYannicc tyler6204 SocialNerd42069 Hyaxia dantelex daveonkels google-labs-jules[bot] lc0rp vignesh07 mteam88 - Eng. Juan Combetto Mariano Belinky dbhurley TSavo julianengel bradleypriest benithors timolins nachx639 pvoo - sreekaransrinath gupsammy cristip73 stefangalescu nachoiacovino Vasanth Rao Naik Sabavat petter-b cpojer scald gumadeiras - andranik-sahakyan davidguttman sleontenko denysvitali sircrumpet peschee rafaelreis-r thewilloftheshadow ratulsarna lutr0 - danielz1z emanuelst KristijanJovanovski CashWilliams rdev osolmaz joshrad-dev kiranjd adityashaw2 sheeek - artuskg Takhoffman onutc pauloportella neooriginal manuelhettich minghinmatthewlam myfunc travisirby buddyh - connorshea kyleok mcinteerj dependabot[bot] John-Rood timkrase gerardward2007 obviyus roshanasingh4 tosh-hamburg - azade-c bjesuiter cheeeee Josh Phillips dlauer pookNast Whoaa512 YuriNachos chriseidhof robbyczgw-cla - ysqander aj47 superman32432432 Yurii Chukhlib grp06 ngutman antons austinm911 blacksmith-sh[bot] damoahdominic + steipete plum-dawg bohdanpodvirnyi iHildy jaydenfyi joaohlisboa mneves75 MatthieuBizien MaudeBot Glucksberg + rahthakor vrknetha radek-paclt Tobias Bischoff joshp123 vignesh07 czekaj mukhtharcm sebslight maxsumrall + xadenryan rodrigouroz juanpablodlc hsrvc magimetal zerone0x meaningfool tyler6204 patelhiren NicholasSpisak + jonisjongithub abhisekbasu1 jamesgroat claude JustYannicc Hyaxia dantelex SocialNerd42069 daveonkels google-labs-jules[bot] + lc0rp mousberg adam91holt hougangdev gumadeiras mteam88 hirefrank joeynyc orlyjamie dbhurley + Mariano Belinky Eng. Juan Combetto TSavo julianengel bradleypriest benithors rohannagpal timolins f-trycua benostein + nachx639 shakkernerd pvoo sreekaransrinath gupsammy cristip73 stefangalescu nachoiacovino Vasanth Rao Naik Sabavat petter-b + cpojer scald thewilloftheshadow andranik-sahakyan davidguttman sleontenko denysvitali sircrumpet peschee rafaelreis-r + dominicnunez ratulsarna lutr0 danielz1z AdeboyeDN Alg0rix papago2355 emanuelst KristijanJovanovski rdev + rhuanssauro joshrad-dev kiranjd osolmaz adityashaw2 CashWilliams sheeek artuskg Takhoffman onutc + pauloportella neooriginal manuelhettich minghinmatthewlam myfunc travisirby buddyh connorshea kyleok obviyus + mcinteerj dependabot[bot] John-Rood timkrase uos-status gerardward2007 roshanasingh4 tosh-hamburg azade-c dlauer + JonUleis bjesuiter cheeeee robbyczgw-cla Josh Phillips YuriNachos pookNast Whoaa512 chriseidhof ngutman + ysqander aj47 kennyklee superman32432432 Yurii Chukhlib grp06 antons austinm911 blacksmith-sh[bot] damoahdominic dan-dr HeimdallStrategy imfing jalehman jarvis-medmatic kkarimi mahmoudashraf93 pkrmf RandyVentures Ryan Lisse - dougvk erikpr1994 Ghost jonasjancarik Keith the Silly Goose L36 Server Marc mitschabaude-bot mkbehr neist - sibbl chrisrodz Friederike Seiler gabriel-trigo iamadig Jonathan D. Rhyne (DJ-D) Kit koala73 manmal ogulcancelik - pasogott petradonka rubyrunsstuff siddhantjain suminhthanh svkozak VACInc wes-davis zats 24601 - adam91holt ameno- Chris Taylor Django Navarro evalexpr henrino3 humanwritten larlyssa odysseus0 oswalpalash - pcty-nextgen-service-account Syhids Aaron Konyer aaronveklabs andreabadesso Andrii cash-echo-bot Clawd ClawdFx erik-agens - Evizero fcatuhe itsjaydesu ivancasco ivanrvpereira jayhickey jeffersonwarrior jeffersonwarrior jverdi longmaba - mickahouan mjrussell odnxe p6l-richard philipp-spiess robaxelsen Sash Catanzarite T5-AndyML travisp VAC - william arzt zknicker abhaymundhara alejandro maza andrewting19 anpoirier arthyn Asleep123 bolismauro conhecendoia - dasilva333 Dimitrios Ploutarchos Drake Thomsen EnzeD fal3 Felix Krause ganghyun kim grrowl gtsifrikas HazAT - hrdwdmrbl hugobarauna Jamie Openshaw Jarvis Jefferson Nunn Kevin Lin kitze levifig Lloyd loukotal - martinpucik Matt mini Miles mrdbstn MSch Mustafa Tag Eldeen ndraiman nexty5870 prathamdby ptn1411 - reeltimeapps RLTCmpe Rolf Fredheim Rony Kelner Samrat Jha sergical shiv19 siraht snopoke testingabc321 - The Admiral thesash Ubuntu voidserf Vultr-Clawd Admin Wimmie wstock yazinsai Zach Knickerbocker Alphonse-arianee - Azade carlulsoe ddyo Erik latitudeki5223 Manuel Maly Mourad Boustani odrobnik pcty-nextgen-ios-builder Quentin - Randy Torres rhjoh ronak-guliani William Stock + dougvk erikpr1994 fal3 Ghost jonasjancarik Keith the Silly Goose L36 Server Marc mitschabaude-bot mkbehr + neist sibbl chrisrodz Friederike Seiler gabriel-trigo iamadig Jonathan D. Rhyne (DJ-D) Joshua Mitchell Kit koala73 + manmal ogulcancelik pasogott petradonka rubyrunsstuff siddhantjain suminhthanh svkozak VACInc wes-davis + zats 24601 ameno- Chris Taylor dguido Django Navarro evalexpr henrino3 humanwritten larlyssa + odysseus0 oswalpalash pcty-nextgen-service-account rmorse Syhids Aaron Konyer aaronveklabs andreabadesso Andrii cash-echo-bot + Clawd ClawdFx EnzeD erik-agens Evizero fcatuhe itsjaydesu ivancasco ivanrvpereira jayhickey + jeffersonwarrior jeffersonwarrior jverdi longmaba mickahouan mjrussell odnxe p6l-richard philipp-spiess Pocket Clawd + robaxelsen Sash Catanzarite T5-AndyML travisp VAC william arzt zknicker 0oAstro abhaymundhara alejandro maza + Alex-Alaniz alexstyl andrewting19 anpoirier arthyn Asleep123 bolismauro chenyuan99 Clawdbot Maintainers conhecendoia + dasilva333 David-Marsh-Photo Developer Dimitrios Ploutarchos Drake Thomsen Felix Krause foeken ganghyun kim grrowl gtsifrikas + HazAT hrdwdmrbl hugobarauna Jamie Openshaw Jane Jarvis Jefferson Nunn jogi47 kentaro Kevin Lin + kitze Kiwitwitter levifig Lloyd longjos loukotal louzhixian martinpucik Matt mini mertcicekci0 + Miles mrdbstn MSch Mustafa Tag Eldeen ndraiman nexty5870 Noctivoro ppamment prathamdby ptn1411 + reeltimeapps RLTCmpe Rolf Fredheim Rony Kelner Samrat Jha senoldogann Seredeep sergical shiv19 shiyuanhai + siraht snopoke Suksham-sharma techboss testingabc321 The Admiral thesash Ubuntu voidserf Vultr-Clawd Admin + Wimmie wolfred wstock yazinsai YiWang24 ymat19 Zach Knickerbocker 0xJonHoldsCrypto aaronn Alphonse-arianee + atalovesyou Azade carlulsoe ddyo Erik latitudeki5223 Manuel Maly Mourad Boustani odrobnik pcty-nextgen-ios-builder + Quentin Randy Torres rhjoh ronak-guliani William Stock

diff --git a/SECURITY.md b/SECURITY.md index d2af462ba..414def17f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,6 +1,6 @@ # Security Policy -If you believe you’ve found a security issue in Clawdbot, please report it privately. +If you believe you've found a security issue in Moltbot, please report it privately. ## Reporting @@ -9,7 +9,53 @@ If you believe you’ve found a security issue in Clawdbot, please report it pri ## Operational Guidance -For threat model + hardening guidance (including `clawdbot security audit --deep` and `--fix`), see: +For threat model + hardening guidance (including `moltbot security audit --deep` and `--fix`), see: -- `https://docs.clawd.bot/gateway/security` +- `https://docs.molt.bot/gateway/security` +### Web Interface Safety + +Moltbot's web interface is intended for local use only. Do **not** bind it to the public internet; it is not hardened for public exposure. + +## Runtime Requirements + +### Node.js Version + +Moltbot requires **Node.js 22.12.0 or later** (LTS). This version includes important security patches: + +- CVE-2025-59466: async_hooks DoS vulnerability +- CVE-2026-21636: Permission model bypass vulnerability + +Verify your Node.js version: + +```bash +node --version # Should be v22.12.0 or later +``` + +### Docker Security + +When running Moltbot in Docker: + +1. The official image runs as a non-root user (`node`) for reduced attack surface +2. Use `--read-only` flag when possible for additional filesystem protection +3. Limit container capabilities with `--cap-drop=ALL` + +Example secure Docker run: + +```bash +docker run --read-only --cap-drop=ALL \ + -v moltbot-data:/app/data \ + moltbot/moltbot:latest +``` + +## Security Scanning + +This project uses `detect-secrets` for automated secret detection in CI/CD. +See `.detect-secrets.cfg` for configuration and `.secrets.baseline` for the baseline. + +Run locally: + +```bash +pip install detect-secrets==1.5.0 +detect-secrets scan --baseline .secrets.baseline +``` diff --git a/appcast.xml b/appcast.xml index bed929dfb..568632fd7 100644 --- a/appcast.xml +++ b/appcast.xml @@ -1,15 +1,110 @@ - Clawdbot + Moltbot + + 2026.1.24-1 + Sun, 25 Jan 2026 14:05:25 +0000 + https://raw.githubusercontent.com/moltbot/moltbot/main/appcast.xml + 7952 + 2026.1.24-1 + 15.0 + Moltbot 2026.1.24-1 +

Fixes

+
    +
  • Packaging: include dist/shared output in npm tarball (fixes missing reasoning-tags import on install).
  • +
+

View full changelog

+]]>
+ +
+ + 2026.1.24 + Sun, 25 Jan 2026 13:31:05 +0000 + https://raw.githubusercontent.com/moltbot/moltbot/main/appcast.xml + 7944 + 2026.1.24 + 15.0 + Moltbot 2026.1.24 +

Highlights

+
    +
  • Providers: Ollama discovery + docs; Venice guide upgrades + cross-links. (#1606) Thanks @abhaymundhara. https://docs.molt.bot/providers/ollama https://docs.molt.bot/providers/venice
  • +
  • Channels: LINE plugin (Messaging API) with rich replies + quick replies. (#1630) Thanks @plum-dawg.
  • +
  • TTS: Edge fallback (keyless) + /tts auto modes. (#1668, #1667) Thanks @steipete, @sebslight. https://docs.molt.bot/tts
  • +
  • Exec approvals: approve in-chat via /approve across all channels (including plugins). (#1621) Thanks @czekaj. https://docs.molt.bot/tools/exec-approvals https://docs.molt.bot/tools/slash-commands
  • +
  • Telegram: DM topics as separate sessions + outbound link preview toggle. (#1597, #1700) Thanks @rohannagpal, @zerone0x. https://docs.molt.bot/channels/telegram
  • +
+

Changes

+
    +
  • Channels: add LINE plugin (Messaging API) with rich replies, quick replies, and plugin HTTP registry. (#1630) Thanks @plum-dawg.
  • +
  • TTS: add Edge TTS provider fallback, defaulting to keyless Edge with MP3 retry on format failures. (#1668) Thanks @steipete. https://docs.molt.bot/tts
  • +
  • TTS: add auto mode enum (off/always/inbound/tagged) with per-session /tts override. (#1667) Thanks @sebslight. https://docs.molt.bot/tts
  • +
  • Telegram: treat DM topics as separate sessions and keep DM history limits stable with thread suffixes. (#1597) Thanks @rohannagpal.
  • +
  • Telegram: add channels.telegram.linkPreview to toggle outbound link previews. (#1700) Thanks @zerone0x. https://docs.molt.bot/channels/telegram
  • +
  • Web search: add Brave freshness filter parameter for time-scoped results. (#1688) Thanks @JonUleis. https://docs.molt.bot/tools/web
  • +
  • UI: refresh Control UI dashboard design system (typography, colors, spacing). (#1786) Thanks @mousberg.
  • +
  • Exec approvals: forward approval prompts to chat with /approve for all channels (including plugins). (#1621) Thanks @czekaj. https://docs.molt.bot/tools/exec-approvals https://docs.molt.bot/tools/slash-commands
  • +
  • Gateway: expose config.patch in the gateway tool with safe partial updates + restart sentinel. (#1653) Thanks @Glucksberg.
  • +
  • Diagnostics: add diagnostic flags for targeted debug logs (config + env override). https://docs.molt.bot/diagnostics/flags
  • +
  • Docs: expand FAQ (migration, scheduling, concurrency, model recommendations, OpenAI subscription auth, Pi sizing, hackable install, docs SSL workaround).
  • +
  • Docs: add verbose installer troubleshooting guidance.
  • +
  • Docs: add macOS VM guide with local/hosted options + VPS/nodes guidance. (#1693) Thanks @f-trycua.
  • +
  • Docs: add Bedrock EC2 instance role setup + IAM steps. (#1625) Thanks @sergical. https://docs.molt.bot/bedrock
  • +
  • Docs: update Fly.io guide notes.
  • +
  • Dev: add prek pre-commit hooks + dependabot config for weekly updates. (#1720) Thanks @dguido.
  • +
+

Fixes

+
    +
  • Web UI: fix config/debug layout overflow, scrolling, and code block sizing. (#1715) Thanks @saipreetham589.
  • +
  • Web UI: show Stop button during active runs, swap back to New session when idle. (#1664) Thanks @ndbroadbent.
  • +
  • Web UI: clear stale disconnect banners on reconnect; allow form saves with unsupported schema paths but block missing schema. (#1707) Thanks @Glucksberg.
  • +
  • Web UI: hide internal message_id hints in chat bubbles.
  • +
  • Gateway: allow Control UI token-only auth to skip device pairing even when device identity is present (gateway.controlUi.allowInsecureAuth). (#1679) Thanks @steipete.
  • +
  • Matrix: decrypt E2EE media attachments with preflight size guard. (#1744) Thanks @araa47.
  • +
  • BlueBubbles: route phone-number targets to DMs, avoid leaking routing IDs, and auto-create missing DMs (Private API required). (#1751) Thanks @tyler6204. https://docs.molt.bot/channels/bluebubbles
  • +
  • BlueBubbles: keep part-index GUIDs in reply tags when short IDs are missing.
  • +
  • Signal: repair reaction sends (group/UUID targets + CLI author flags). (#1651) Thanks @vilkasdev.
  • +
  • Signal: add configurable signal-cli startup timeout + external daemon mode docs. (#1677) https://docs.molt.bot/channels/signal
  • +
  • Telegram: set fetch duplex="half" for uploads on Node 22 to avoid sendPhoto failures. (#1684) Thanks @commdata2338.
  • +
  • Telegram: use wrapped fetch for long-polling on Node to normalize AbortSignal handling. (#1639)
  • +
  • Telegram: honor per-account proxy for outbound API calls. (#1774) Thanks @radek-paclt.
  • +
  • Telegram: fall back to text when voice notes are blocked by privacy settings. (#1725) Thanks @foeken.
  • +
  • Voice Call: return stream TwiML for outbound conversation calls on initial Twilio webhook. (#1634)
  • +
  • Voice Call: serialize Twilio TTS playback and cancel on barge-in to prevent overlap. (#1713) Thanks @dguido.
  • +
  • Google Chat: tighten email allowlist matching, typing cleanup, media caps, and onboarding/docs/tests. (#1635) Thanks @iHildy.
  • +
  • Google Chat: normalize space targets without double spaces/ prefix.
  • +
  • Agents: auto-compact on context overflow prompt errors before failing. (#1627) Thanks @rodrigouroz.
  • +
  • Agents: use the active auth profile for auto-compaction recovery.
  • +
  • Media understanding: skip image understanding when the primary model already supports vision. (#1747) Thanks @tyler6204.
  • +
  • Models: default missing custom provider fields so minimal configs are accepted.
  • +
  • Messaging: keep newline chunking safe for fenced markdown blocks across channels.
  • +
  • TUI: reload history after gateway reconnect to restore session state. (#1663)
  • +
  • Heartbeat: normalize target identifiers for consistent routing.
  • +
  • Exec: keep approvals for elevated ask unless full mode. (#1616) Thanks @ivancasco.
  • +
  • Exec: treat Windows platform labels as Windows for node shell selection. (#1760) Thanks @ymat19.
  • +
  • Gateway: include inline config env vars in service install environments. (#1735) Thanks @Seredeep.
  • +
  • Gateway: skip Tailscale DNS probing when tailscale.mode is off. (#1671)
  • +
  • Gateway: reduce log noise for late invokes + remote node probes; debounce skills refresh. (#1607) Thanks @petter-b.
  • +
  • Gateway: clarify Control UI/WebChat auth error hints for missing tokens. (#1690)
  • +
  • Gateway: listen on IPv6 loopback when bound to 127.0.0.1 so localhost webhooks work.
  • +
  • Gateway: store lock files in the temp directory to avoid stale locks on persistent volumes. (#1676)
  • +
  • macOS: default direct-transport ws:// URLs to port 18789; document gateway.remote.transport. (#1603) Thanks @ngutman.
  • +
  • Tests: cap Vitest workers on CI macOS to reduce timeouts. (#1597) Thanks @rohannagpal.
  • +
  • Tests: avoid fake-timer dependency in embedded runner stream mock to reduce CI flakes. (#1597) Thanks @rohannagpal.
  • +
  • Tests: increase embedded runner ordering test timeout to reduce CI flakes. (#1597) Thanks @rohannagpal.
  • +
+

View full changelog

+]]>
+ +
2026.1.23 Sat, 24 Jan 2026 13:02:18 +0000 - https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml + https://raw.githubusercontent.com/moltbot/moltbot/main/appcast.xml 7750 2026.1.23 15.0 - Clawdbot 2026.1.23 + Moltbot 2026.1.23

Highlights

  • TTS: allow model-driven TTS tags by default for expressive audio replies (laughter, singing cues, etc.).
  • @@ -22,9 +117,9 @@
  • Browser: add node-host proxy auto-routing for remote gateways (configurable per gateway/node).
  • Heartbeat: add per-channel visibility controls (OK/alerts/indicator). (#1452) Thanks @dlauer.
  • Plugins: add optional llm-task JSON-only tool for workflows. (#1498) Thanks @vignesh07.
  • -
  • CLI: restart the gateway by default after clawdbot update; add --no-restart to skip it.
  • -
  • CLI: add live auth probes to clawdbot models status for per-profile verification.
  • -
  • CLI: add clawdbot system for system events + heartbeat controls; remove standalone wake.
  • +
  • CLI: restart the gateway by default after moltbot update; add --no-restart to skip it.
  • +
  • CLI: add live auth probes to moltbot models status for per-profile verification.
  • +
  • CLI: add moltbot system for system events + heartbeat controls; remove standalone wake.
  • Agents: add Bedrock auto-discovery defaults + config overrides. (#1553) Thanks @fal3.
  • Docs: add cron vs heartbeat decision guide (with Lobster workflow notes). (#1533) Thanks @JustYannicc.
  • Docs: clarify HEARTBEAT.md empty file skips heartbeats, missing file still runs. (#1535) Thanks @JustYannicc.
  • @@ -59,10 +154,10 @@
  • Exec: honor tools.exec ask/security defaults for elevated approvals (avoid unwanted prompts).
  • TUI: forward unknown slash commands (for example, /context) to the Gateway.
  • TUI: include Gateway slash commands in autocomplete and /help.
  • -
  • CLI: skip usage lines in clawdbot models status when provider usage is unavailable.
  • +
  • CLI: skip usage lines in moltbot models status when provider usage is unavailable.
  • CLI: suppress diagnostic session/run noise during auth probes.
  • CLI: hide auth probe timeout warnings from embedded runs.
  • -
  • CLI: render auth probe results as a table in clawdbot models status.
  • +
  • CLI: render auth probe results as a table in moltbot models status.
  • CLI: suppress probe-only embedded logs unless --verbose is set.
  • CLI: move auth probe errors below the table to reduce wrapping.
  • CLI: prevent ANSI color bleed when table cells wrap.
  • @@ -85,131 +180,9 @@
  • Browser: keep extension relay tabs controllable when the extension reuses a session id after switching tabs. (#1160)
  • Agents: warn and ignore tool allowlists that only reference unknown or unloaded plugin tools. (#1566)
-

View full changelog

+

View full changelog

]]>
- -
- - 2026.1.22 - Fri, 23 Jan 2026 08:58:14 +0000 - https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml - 7530 - 2026.1.22 - 15.0 - Clawdbot 2026.1.22 -

Changes

-
    -
  • Highlight: Compaction safeguard now uses adaptive chunking, progressive fallback, and UI status + retries. (#1466) Thanks @dlauer.
  • -
  • Providers: add Antigravity usage tracking to status output. (#1490) Thanks @patelhiren.
  • -
  • Slack: add chat-type reply threading overrides via replyToModeByChatType. (#1442) Thanks @stefangalescu.
  • -
  • BlueBubbles: add asVoice support for MP3/CAF voice memos in sendAttachment. (#1477, #1482) Thanks @Nicell.
  • -
  • Onboarding: add hatch choice (TUI/Web/Later), token explainer, background dashboard seed on macOS, and showcase link.
  • -
-

Fixes

-
    -
  • BlueBubbles: stop typing indicator on idle/no-reply. (#1439) Thanks @Nicell.
  • -
  • Message tool: keep path/filePath as-is for send; hydrate buffers only for sendAttachment. (#1444) Thanks @hopyky.
  • -
  • Auto-reply: only report a model switch when session state is available. (#1465) Thanks @robbyczgw-cla.
  • -
  • Control UI: resolve local avatar URLs with basePath across injection + identity RPC. (#1457) Thanks @dlauer.
  • -
  • Agents: sanitize assistant history text to strip tool-call markers. (#1456) Thanks @zerone0x.
  • -
  • Discord: clarify Message Content Intent onboarding hint. (#1487) Thanks @kyleok.
  • -
  • Gateway: stop the service before uninstalling and fail if it remains loaded.
  • -
  • Agents: surface concrete API error details instead of generic AI service errors.
  • -
  • Exec: fall back to non-PTY when PTY spawn fails (EBADF). (#1484)
  • -
  • Exec approvals: allow per-segment allowlists for chained shell commands on gateway + node hosts. (#1458) Thanks @czekaj.
  • -
  • Agents: make OpenAI sessions image-sanitize-only; gate tool-id/repair sanitization by provider.
  • -
  • Doctor: honor CLAWDBOT_GATEWAY_TOKEN for auth checks and security audit token reuse. (#1448) Thanks @azade-c.
  • -
  • Agents: make tool summaries more readable and only show optional params when set.
  • -
  • Agents: honor SOUL.md guidance even when the file is nested or path-qualified. (#1434) Thanks @neooriginal.
  • -
  • Matrix (plugin): persist m.direct for resolved DMs and harden room fallback. (#1436, #1486) Thanks @sibbl.
  • -
  • CLI: prefer ~ for home paths in output.
  • -
  • Mattermost (plugin): enforce pairing/allowlist gating, keep @username targets, and clarify plugin-only docs. (#1428) Thanks @damoahdominic.
  • -
  • Agents: centralize transcript sanitization in the runner; keep tags and error turns intact.
  • -
  • Auth: skip auth profiles in cooldown during initial selection and rotation. (#1316) Thanks @odrobnik.
  • -
  • Agents/TUI: honor user-pinned auth profiles during cooldown and preserve search picker ranking. (#1432) Thanks @tobiasbischoff.
  • -
  • Docs: fix gog auth services example to include docs scope. (#1454) Thanks @zerone0x.
  • -
  • Slack: reduce WebClient retries to avoid duplicate sends. (#1481)
  • -
  • Slack: read thread replies for message reads when threadId is provided (replies-only). (#1450) Thanks @rodrigouroz.
  • -
  • macOS: prefer linked channels in gateway summary to avoid false “not linked” status.
  • -
  • macOS/tests: fix gateway summary lookup after guard unwrap; prevent browser opens during tests. (ECID-1483)
  • -
-

View full changelog

-]]>
- -
- - 2026.1.21 - Thu, 22 Jan 2026 12:22:35 +0000 - https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml - 7374 - 2026.1.21 - 15.0 - Clawdbot 2026.1.21 -

Highlights

-
    -
  • Lobster optional plugin tool for typed workflows + approval gates. https://docs.clawd.bot/tools/lobster
  • -
  • Custom assistant identity + avatars in the Control UI. https://docs.clawd.bot/cli/agents https://docs.clawd.bot/web/control-ui
  • -
  • Cache optimizations: cache-ttl pruning + defaults reduce token spend on cold requests. https://docs.clawd.bot/concepts/session-pruning
  • -
  • Exec approvals + elevated ask/full modes. https://docs.clawd.bot/tools/exec-approvals https://docs.clawd.bot/tools/elevated
  • -
  • Signal typing/read receipts + MSTeams attachments. https://docs.clawd.bot/channels/signal https://docs.clawd.bot/channels/msteams
  • -
  • /models UX refresh + clawdbot update wizard. https://docs.clawd.bot/cli/models https://docs.clawd.bot/cli/update
  • -
-

Changes

-
    -
  • Highlight: Lobster optional plugin tool for typed workflows + approval gates. https://docs.clawd.bot/tools/lobster (#1152) Thanks @vignesh07.
  • -
  • Agents/UI: add identity avatar config support and Control UI avatar rendering. (#1329, #1424) Thanks @dlauer. https://docs.clawd.bot/gateway/configuration https://docs.clawd.bot/cli/agents
  • -
  • Control UI: add custom assistant identity support and per-session identity display. (#1420) Thanks @robbyczgw-cla. https://docs.clawd.bot/web/control-ui
  • -
  • CLI: add clawdbot update wizard with interactive channel selection + restart prompts, plus preflight checks before rebasing. https://docs.clawd.bot/cli/update
  • -
  • Models/Commands: add /models, improve /model listing UX, and expand clawdbot models paging. (#1398) Thanks @vignesh07. https://docs.clawd.bot/cli/models
  • -
  • CLI: move gateway service commands under clawdbot gateway, flatten node service commands under clawdbot node, and add gateway probe for reachability. https://docs.clawd.bot/cli/gateway https://docs.clawd.bot/cli/node
  • -
  • Exec: add elevated ask/full modes, tighten allowlist gating, and render approvals tables on write. https://docs.clawd.bot/tools/elevated https://docs.clawd.bot/tools/exec-approvals
  • -
  • Exec approvals: default to local host, add gateway/node targeting + target details, support wildcard agent allowlists, and tighten allowlist parsing/safe bins. https://docs.clawd.bot/cli/approvals https://docs.clawd.bot/tools/exec-approvals
  • -
  • Heartbeat: allow explicit session keys and active hours. (#1256) Thanks @zknicker. https://docs.clawd.bot/gateway/heartbeat
  • -
  • Sessions: add per-channel idle durations via sessions.channelIdleMinutes. (#1353) Thanks @cash-echo-bot.
  • -
  • Nodes: run exec-style, expose PATH in status/describe, and bootstrap PATH for node-host execution. https://docs.clawd.bot/cli/node
  • -
  • Cache: add cache.ttlPrune mode and auth-aware defaults for cache TTL behavior.
  • -
  • Queue: add per-channel debounce overrides for auto-reply. https://docs.clawd.bot/concepts/queue
  • -
  • Discord: add wildcard channel config support. (#1334) Thanks @pvoo. https://docs.clawd.bot/channels/discord
  • -
  • Signal: add typing indicators and DM read receipts via signal-cli. https://docs.clawd.bot/channels/signal
  • -
  • MSTeams: add file uploads, adaptive cards, and attachment handling improvements. (#1410) Thanks @Evizero. https://docs.clawd.bot/channels/msteams
  • -
  • Onboarding: remove the run setup-token auth option (paste setup-token or reuse CLI creds instead).
  • -
  • macOS: refresh Settings (location access in Permissions, connection mode in menu, remove CLI install UI).
  • -
  • Diagnostics: add cache trace config for debugging. (#1370) Thanks @parubets.
  • -
  • Docs: Lobster guides + org URL updates, /model allowlist troubleshooting, Gmail message search examples, gateway.mode troubleshooting, prompt injection guidance, npm prefix/node CLI notes, control UI dev gatewayUrl note, tool_use FAQ, showcase video, and sharp/node-gyp workaround. (#1427, #1220, #1405) Thanks @vignesh07, @mbelinky.
  • -
-

Breaking

-
    -
  • BREAKING: Control UI now rejects insecure HTTP without device identity by default. Use HTTPS (Tailscale Serve) or set gateway.controlUi.allowInsecureAuth: true to allow token-only auth. https://docs.clawd.bot/web/control-ui#insecure-http
  • -
  • BREAKING: Envelope and system event timestamps now default to host-local time (was UTC) so agents don’t have to constantly convert.
  • -
-

Fixes

-
    -
  • Streaming/Typing/Media: keep reply tags across streamed chunks, start typing indicators at run start, and accept MEDIA paths with spaces/tilde while preferring the message tool hint for image replies.
  • -
  • Agents/Providers: drop unsigned thinking blocks for Claude models (Google Antigravity) and enforce alphanumeric tool call ids for strict providers (Mistral/OpenRouter). (#1372) Thanks @zerone0x.
  • -
  • Exec approvals: treat main as the default agent, align node/gateway allowlist prechecks, validate resolved paths, avoid allowlist resolve races, and avoid null optional params. (#1417, #1414, #1425) Thanks @czekaj.
  • -
  • Exec/Windows: resolve Windows exec paths with extensions and handle safe-bin exe names.
  • -
  • Nodes/macOS: prompt on allowlist miss for node exec approvals, persist allowlist decisions, and flatten node invoke errors. (#1394) Thanks @ngutman.
  • -
  • Gateway: prevent multiple gateways from sharing the same config/state (singleton lock), keep auto bind loopback-first with explicit tailnet binding, and improve SSH auth handling. (#1380)
  • -
  • Control UI: remove the chat stop button, keep the composer aligned to the bottom edge, stabilize session previews, and refresh the debug panel on route-driven tab changes. (#1373) Thanks @yazinsai.
  • -
  • UI/config: export SECTION_META for config form modules. (#1418) Thanks @MaudeBot.
  • -
  • macOS: keep chat pinned during streaming replies, include Textual resources, respect wildcard exec approvals, allow SSH agent auth, and default distribution builds to universal binaries. (#1279, #1362, #1384, #1396) Thanks @ameno-, @JustYannicc.
  • -
  • BlueBubbles: resolve short message IDs safely, expose full IDs in templates, and harden short-id fetch wrappers. (#1369, #1387) Thanks @tyler6204.
  • -
  • Models/Configure: inherit session model overrides in threads/topics, map OpenCode Zen models to the correct APIs, narrow Anthropic OAuth allowlist handling, seed allowlist fallbacks, list the full catalog when no allowlist is set, and limit /model list output. (#1376, #1416)
  • -
  • Memory: prevent CLI hangs by deferring vector probes, add sqlite-vec/embedding timeouts, and make session memory indexing async.
  • -
  • Cron: cap reminder context history to 10 messages and honor contextMessages. (#1103) Thanks @mkbehr.
  • -
  • Cache: restore the 1h cache TTL option and reset the pruning window.
  • -
  • Zalo Personal: tolerate ANSI/log-prefixed JSON output from zca. (#1379) Thanks @ptn1411.
  • -
  • Browser: suppress Chrome restore prompts for managed profiles. (#1419) Thanks @jamesgroat.
  • -
  • Infra: preserve fetch helper methods/preconnect when wrapping abort signals and normalize Telegram fetch aborts.
  • -
  • Config/Doctor: avoid stack traces for invalid configs, log the config path, avoid WhatsApp config resurrection, and warn when gateway.mode is unset. (#900)
  • -
  • CLI: read Codex CLI account_id for workspace billing. (#1422) Thanks @aj47.
  • -
  • Logs/Status: align rolling log filenames with local time and report sandboxed runtime in clawdbot status. (#1343)
  • -
  • Embedded runner: persist injected history images so attachments aren’t reloaded each turn. (#1374) Thanks @Nicell.
  • -
  • Nodes/Subagents: include agent/node/gateway context in tool failure logs and ensure subagent list uses the command session.
  • -
-

View full changelog

-]]>
- +
\ No newline at end of file diff --git a/apps/android/app/build.gradle.kts b/apps/android/app/build.gradle.kts index d8d77ebe1..3ddcb3b81 100644 --- a/apps/android/app/build.gradle.kts +++ b/apps/android/app/build.gradle.kts @@ -8,21 +8,21 @@ plugins { } android { - namespace = "com.clawdbot.android" + namespace = "bot.molt.android" compileSdk = 36 sourceSets { getByName("main") { - assets.srcDir(file("../../shared/ClawdbotKit/Sources/ClawdbotKit/Resources")) + assets.srcDir(file("../../shared/MoltbotKit/Sources/MoltbotKit/Resources")) } } defaultConfig { - applicationId = "com.clawdbot.android" + applicationId = "bot.molt.android" minSdk = 31 targetSdk = 36 - versionCode = 202601240 - versionName = "2026.1.24" + versionCode = 202601260 + versionName = "2026.1.27-beta.1" } buildTypes { @@ -65,7 +65,7 @@ androidComponents { val versionName = output.versionName.orNull ?: "0" val buildType = variant.buildType - val outputFileName = "clawdbot-${versionName}-${buildType}.apk" + val outputFileName = "moltbot-${versionName}-${buildType}.apk" output.outputFileName = outputFileName } } diff --git a/apps/android/app/src/main/AndroidManifest.xml b/apps/android/app/src/main/AndroidManifest.xml index df2aa94b3..e0aab841e 100644 --- a/apps/android/app/src/main/AndroidManifest.xml +++ b/apps/android/app/src/main/AndroidManifest.xml @@ -32,7 +32,7 @@ android:label="@string/app_name" android:supportsRtl="true" android:networkSecurityConfig="@xml/network_security_config" - android:theme="@style/Theme.ClawdbotNode"> + android:theme="@style/Theme.MoltbotNode"> Quint(status, server, connected, voiceMode, voiceListening) }.collect { (status, server, connected, voiceMode, voiceListening) -> - val title = if (connected) "Clawdbot Node · Connected" else "Clawdbot Node" + val title = if (connected) "Moltbot Node · Connected" else "Moltbot Node" val voiceSuffix = if (voiceMode == VoiceWakeMode.Always) { if (voiceListening) " · Voice Wake: Listening" else " · Voice Wake: Paused" @@ -91,7 +91,7 @@ class NodeForegroundService : Service() { "Connection", NotificationManager.IMPORTANCE_LOW, ).apply { - description = "Clawdbot node connection status" + description = "Moltbot node connection status" setShowBadge(false) } mgr.createNotificationChannel(channel) @@ -163,7 +163,7 @@ class NodeForegroundService : Service() { private const val CHANNEL_ID = "connection" private const val NOTIFICATION_ID = 1 - private const val ACTION_STOP = "com.clawdbot.android.action.STOP" + private const val ACTION_STOP = "bot.molt.android.action.STOP" fun start(context: Context) { val intent = Intent(context, NodeForegroundService::class.java) diff --git a/apps/android/app/src/main/java/com/clawdbot/android/NodeRuntime.kt b/apps/android/app/src/main/java/bot/molt/android/NodeRuntime.kt similarity index 90% rename from apps/android/app/src/main/java/com/clawdbot/android/NodeRuntime.kt rename to apps/android/app/src/main/java/bot/molt/android/NodeRuntime.kt index 603e4b82b..5fd429e9e 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/NodeRuntime.kt +++ b/apps/android/app/src/main/java/bot/molt/android/NodeRuntime.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android +package bot.molt.android import android.Manifest import android.content.Context @@ -7,35 +7,35 @@ import android.location.LocationManager import android.os.Build import android.os.SystemClock import androidx.core.content.ContextCompat -import com.clawdbot.android.chat.ChatController -import com.clawdbot.android.chat.ChatMessage -import com.clawdbot.android.chat.ChatPendingToolCall -import com.clawdbot.android.chat.ChatSessionEntry -import com.clawdbot.android.chat.OutgoingAttachment -import com.clawdbot.android.gateway.DeviceAuthStore -import com.clawdbot.android.gateway.DeviceIdentityStore -import com.clawdbot.android.gateway.GatewayClientInfo -import com.clawdbot.android.gateway.GatewayConnectOptions -import com.clawdbot.android.gateway.GatewayDiscovery -import com.clawdbot.android.gateway.GatewayEndpoint -import com.clawdbot.android.gateway.GatewaySession -import com.clawdbot.android.gateway.GatewayTlsParams -import com.clawdbot.android.node.CameraCaptureManager -import com.clawdbot.android.node.LocationCaptureManager -import com.clawdbot.android.BuildConfig -import com.clawdbot.android.node.CanvasController -import com.clawdbot.android.node.ScreenRecordManager -import com.clawdbot.android.node.SmsManager -import com.clawdbot.android.protocol.ClawdbotCapability -import com.clawdbot.android.protocol.ClawdbotCameraCommand -import com.clawdbot.android.protocol.ClawdbotCanvasA2UIAction -import com.clawdbot.android.protocol.ClawdbotCanvasA2UICommand -import com.clawdbot.android.protocol.ClawdbotCanvasCommand -import com.clawdbot.android.protocol.ClawdbotScreenCommand -import com.clawdbot.android.protocol.ClawdbotLocationCommand -import com.clawdbot.android.protocol.ClawdbotSmsCommand -import com.clawdbot.android.voice.TalkModeManager -import com.clawdbot.android.voice.VoiceWakeManager +import bot.molt.android.chat.ChatController +import bot.molt.android.chat.ChatMessage +import bot.molt.android.chat.ChatPendingToolCall +import bot.molt.android.chat.ChatSessionEntry +import bot.molt.android.chat.OutgoingAttachment +import bot.molt.android.gateway.DeviceAuthStore +import bot.molt.android.gateway.DeviceIdentityStore +import bot.molt.android.gateway.GatewayClientInfo +import bot.molt.android.gateway.GatewayConnectOptions +import bot.molt.android.gateway.GatewayDiscovery +import bot.molt.android.gateway.GatewayEndpoint +import bot.molt.android.gateway.GatewaySession +import bot.molt.android.gateway.GatewayTlsParams +import bot.molt.android.node.CameraCaptureManager +import bot.molt.android.node.LocationCaptureManager +import bot.molt.android.BuildConfig +import bot.molt.android.node.CanvasController +import bot.molt.android.node.ScreenRecordManager +import bot.molt.android.node.SmsManager +import bot.molt.android.protocol.MoltbotCapability +import bot.molt.android.protocol.MoltbotCameraCommand +import bot.molt.android.protocol.MoltbotCanvasA2UIAction +import bot.molt.android.protocol.MoltbotCanvasA2UICommand +import bot.molt.android.protocol.MoltbotCanvasCommand +import bot.molt.android.protocol.MoltbotScreenCommand +import bot.molt.android.protocol.MoltbotLocationCommand +import bot.molt.android.protocol.MoltbotSmsCommand +import bot.molt.android.voice.TalkModeManager +import bot.molt.android.voice.VoiceWakeManager import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -451,38 +451,38 @@ class NodeRuntime(context: Context) { private fun buildInvokeCommands(): List = buildList { - add(ClawdbotCanvasCommand.Present.rawValue) - add(ClawdbotCanvasCommand.Hide.rawValue) - add(ClawdbotCanvasCommand.Navigate.rawValue) - add(ClawdbotCanvasCommand.Eval.rawValue) - add(ClawdbotCanvasCommand.Snapshot.rawValue) - add(ClawdbotCanvasA2UICommand.Push.rawValue) - add(ClawdbotCanvasA2UICommand.PushJSONL.rawValue) - add(ClawdbotCanvasA2UICommand.Reset.rawValue) - add(ClawdbotScreenCommand.Record.rawValue) + add(MoltbotCanvasCommand.Present.rawValue) + add(MoltbotCanvasCommand.Hide.rawValue) + add(MoltbotCanvasCommand.Navigate.rawValue) + add(MoltbotCanvasCommand.Eval.rawValue) + add(MoltbotCanvasCommand.Snapshot.rawValue) + add(MoltbotCanvasA2UICommand.Push.rawValue) + add(MoltbotCanvasA2UICommand.PushJSONL.rawValue) + add(MoltbotCanvasA2UICommand.Reset.rawValue) + add(MoltbotScreenCommand.Record.rawValue) if (cameraEnabled.value) { - add(ClawdbotCameraCommand.Snap.rawValue) - add(ClawdbotCameraCommand.Clip.rawValue) + add(MoltbotCameraCommand.Snap.rawValue) + add(MoltbotCameraCommand.Clip.rawValue) } if (locationMode.value != LocationMode.Off) { - add(ClawdbotLocationCommand.Get.rawValue) + add(MoltbotLocationCommand.Get.rawValue) } if (sms.canSendSms()) { - add(ClawdbotSmsCommand.Send.rawValue) + add(MoltbotSmsCommand.Send.rawValue) } } private fun buildCapabilities(): List = buildList { - add(ClawdbotCapability.Canvas.rawValue) - add(ClawdbotCapability.Screen.rawValue) - if (cameraEnabled.value) add(ClawdbotCapability.Camera.rawValue) - if (sms.canSendSms()) add(ClawdbotCapability.Sms.rawValue) + add(MoltbotCapability.Canvas.rawValue) + add(MoltbotCapability.Screen.rawValue) + if (cameraEnabled.value) add(MoltbotCapability.Camera.rawValue) + if (sms.canSendSms()) add(MoltbotCapability.Sms.rawValue) if (voiceWakeMode.value != VoiceWakeMode.Off && hasRecordAudioPermission()) { - add(ClawdbotCapability.VoiceWake.rawValue) + add(MoltbotCapability.VoiceWake.rawValue) } if (locationMode.value != LocationMode.Off) { - add(ClawdbotCapability.Location.rawValue) + add(MoltbotCapability.Location.rawValue) } } @@ -506,7 +506,7 @@ class NodeRuntime(context: Context) { val version = resolvedVersionName() val release = Build.VERSION.RELEASE?.trim().orEmpty() val releaseLabel = if (release.isEmpty()) "unknown" else release - return "ClawdbotAndroid/$version (Android $releaseLabel; SDK ${Build.VERSION.SDK_INT})" + return "MoltbotAndroid/$version (Android $releaseLabel; SDK ${Build.VERSION.SDK_INT})" } private fun buildClientInfo(clientId: String, clientMode: String): GatewayClientInfo { @@ -529,7 +529,7 @@ class NodeRuntime(context: Context) { caps = buildCapabilities(), commands = buildInvokeCommands(), permissions = emptyMap(), - client = buildClientInfo(clientId = "clawdbot-android", clientMode = "node"), + client = buildClientInfo(clientId = "moltbot-android", clientMode = "node"), userAgent = buildUserAgent(), ) } @@ -541,7 +541,7 @@ class NodeRuntime(context: Context) { caps = emptyList(), commands = emptyList(), permissions = emptyMap(), - client = buildClientInfo(clientId = "clawdbot-control-ui", clientMode = "ui"), + client = buildClientInfo(clientId = "moltbot-control-ui", clientMode = "ui"), userAgent = buildUserAgent(), ) } @@ -665,7 +665,7 @@ class NodeRuntime(context: Context) { val actionId = (userActionObj["id"] as? JsonPrimitive)?.content?.trim().orEmpty().ifEmpty { java.util.UUID.randomUUID().toString() } - val name = ClawdbotCanvasA2UIAction.extractActionName(userActionObj) ?: return@launch + val name = MoltbotCanvasA2UIAction.extractActionName(userActionObj) ?: return@launch val surfaceId = (userActionObj["surfaceId"] as? JsonPrimitive)?.content?.trim().orEmpty().ifEmpty { "main" } @@ -675,7 +675,7 @@ class NodeRuntime(context: Context) { val sessionKey = resolveMainSessionKey() val message = - ClawdbotCanvasA2UIAction.formatAgentMessage( + MoltbotCanvasA2UIAction.formatAgentMessage( actionName = name, sessionKey = sessionKey, surfaceId = surfaceId, @@ -709,7 +709,7 @@ class NodeRuntime(context: Context) { try { canvas.eval( - ClawdbotCanvasA2UIAction.jsDispatchA2UIActionStatus( + MoltbotCanvasA2UIAction.jsDispatchA2UIActionStatus( actionId = actionId, ok = connected && error == null, error = error, @@ -827,10 +827,10 @@ class NodeRuntime(context: Context) { private suspend fun handleInvoke(command: String, paramsJson: String?): GatewaySession.InvokeResult { if ( - command.startsWith(ClawdbotCanvasCommand.NamespacePrefix) || - command.startsWith(ClawdbotCanvasA2UICommand.NamespacePrefix) || - command.startsWith(ClawdbotCameraCommand.NamespacePrefix) || - command.startsWith(ClawdbotScreenCommand.NamespacePrefix) + command.startsWith(MoltbotCanvasCommand.NamespacePrefix) || + command.startsWith(MoltbotCanvasA2UICommand.NamespacePrefix) || + command.startsWith(MoltbotCameraCommand.NamespacePrefix) || + command.startsWith(MoltbotScreenCommand.NamespacePrefix) ) { if (!isForeground.value) { return GatewaySession.InvokeResult.error( @@ -839,13 +839,13 @@ class NodeRuntime(context: Context) { ) } } - if (command.startsWith(ClawdbotCameraCommand.NamespacePrefix) && !cameraEnabled.value) { + if (command.startsWith(MoltbotCameraCommand.NamespacePrefix) && !cameraEnabled.value) { return GatewaySession.InvokeResult.error( code = "CAMERA_DISABLED", message = "CAMERA_DISABLED: enable Camera in Settings", ) } - if (command.startsWith(ClawdbotLocationCommand.NamespacePrefix) && + if (command.startsWith(MoltbotLocationCommand.NamespacePrefix) && locationMode.value == LocationMode.Off ) { return GatewaySession.InvokeResult.error( @@ -855,18 +855,18 @@ class NodeRuntime(context: Context) { } return when (command) { - ClawdbotCanvasCommand.Present.rawValue -> { + MoltbotCanvasCommand.Present.rawValue -> { val url = CanvasController.parseNavigateUrl(paramsJson) canvas.navigate(url) GatewaySession.InvokeResult.ok(null) } - ClawdbotCanvasCommand.Hide.rawValue -> GatewaySession.InvokeResult.ok(null) - ClawdbotCanvasCommand.Navigate.rawValue -> { + MoltbotCanvasCommand.Hide.rawValue -> GatewaySession.InvokeResult.ok(null) + MoltbotCanvasCommand.Navigate.rawValue -> { val url = CanvasController.parseNavigateUrl(paramsJson) canvas.navigate(url) GatewaySession.InvokeResult.ok(null) } - ClawdbotCanvasCommand.Eval.rawValue -> { + MoltbotCanvasCommand.Eval.rawValue -> { val js = CanvasController.parseEvalJs(paramsJson) ?: return GatewaySession.InvokeResult.error( @@ -884,7 +884,7 @@ class NodeRuntime(context: Context) { } GatewaySession.InvokeResult.ok("""{"result":${result.toJsonString()}}""") } - ClawdbotCanvasCommand.Snapshot.rawValue -> { + MoltbotCanvasCommand.Snapshot.rawValue -> { val snapshotParams = CanvasController.parseSnapshotParams(paramsJson) val base64 = try { @@ -901,7 +901,7 @@ class NodeRuntime(context: Context) { } GatewaySession.InvokeResult.ok("""{"format":"${snapshotParams.format.rawValue}","base64":"$base64"}""") } - ClawdbotCanvasA2UICommand.Reset.rawValue -> { + MoltbotCanvasA2UICommand.Reset.rawValue -> { val a2uiUrl = resolveA2uiHostUrl() ?: return GatewaySession.InvokeResult.error( code = "A2UI_HOST_NOT_CONFIGURED", @@ -917,7 +917,7 @@ class NodeRuntime(context: Context) { val res = canvas.eval(a2uiResetJS) GatewaySession.InvokeResult.ok(res) } - ClawdbotCanvasA2UICommand.Push.rawValue, ClawdbotCanvasA2UICommand.PushJSONL.rawValue -> { + MoltbotCanvasA2UICommand.Push.rawValue, MoltbotCanvasA2UICommand.PushJSONL.rawValue -> { val messages = try { decodeA2uiMessages(command, paramsJson) @@ -940,7 +940,7 @@ class NodeRuntime(context: Context) { val res = canvas.eval(js) GatewaySession.InvokeResult.ok(res) } - ClawdbotCameraCommand.Snap.rawValue -> { + MoltbotCameraCommand.Snap.rawValue -> { showCameraHud(message = "Taking photo…", kind = CameraHudKind.Photo) triggerCameraFlash() val res = @@ -954,7 +954,7 @@ class NodeRuntime(context: Context) { showCameraHud(message = "Photo captured", kind = CameraHudKind.Success, autoHideMs = 1600) GatewaySession.InvokeResult.ok(res.payloadJson) } - ClawdbotCameraCommand.Clip.rawValue -> { + MoltbotCameraCommand.Clip.rawValue -> { val includeAudio = paramsJson?.contains("\"includeAudio\":true") != false if (includeAudio) externalAudioCaptureActive.value = true try { @@ -973,7 +973,7 @@ class NodeRuntime(context: Context) { if (includeAudio) externalAudioCaptureActive.value = false } } - ClawdbotLocationCommand.Get.rawValue -> { + MoltbotLocationCommand.Get.rawValue -> { val mode = locationMode.value if (!isForeground.value && mode != LocationMode.Always) { return GatewaySession.InvokeResult.error( @@ -1026,7 +1026,7 @@ class NodeRuntime(context: Context) { GatewaySession.InvokeResult.error(code = "LOCATION_UNAVAILABLE", message = message) } } - ClawdbotScreenCommand.Record.rawValue -> { + MoltbotScreenCommand.Record.rawValue -> { // Status pill mirrors screen recording state so it stays visible without overlay stacking. _screenRecordActive.value = true try { @@ -1042,7 +1042,7 @@ class NodeRuntime(context: Context) { _screenRecordActive.value = false } } - ClawdbotSmsCommand.Send.rawValue -> { + MoltbotSmsCommand.Send.rawValue -> { val res = sms.send(paramsJson) if (res.ok) { GatewaySession.InvokeResult.ok(res.payloadJson) @@ -1115,7 +1115,7 @@ class NodeRuntime(context: Context) { val raw = if (nodeRaw.isNotBlank()) nodeRaw else operatorRaw if (raw.isBlank()) return null val base = raw.trimEnd('/') - return "${base}/__clawdbot__/a2ui/?platform=android" + return "${base}/__moltbot__/a2ui/?platform=android" } private suspend fun ensureA2uiReady(a2uiUrl: String): Boolean { @@ -1150,7 +1150,7 @@ class NodeRuntime(context: Context) { val jsonlField = (obj["jsonl"] as? JsonPrimitive)?.content?.trim().orEmpty() val hasMessagesArray = obj["messages"] is JsonArray - if (command == ClawdbotCanvasA2UICommand.PushJSONL.rawValue || (!hasMessagesArray && jsonlField.isNotBlank())) { + if (command == MoltbotCanvasA2UICommand.PushJSONL.rawValue || (!hasMessagesArray && jsonlField.isNotBlank())) { val jsonl = jsonlField if (jsonl.isBlank()) throw IllegalArgumentException("INVALID_REQUEST: jsonl required") val messages = @@ -1218,7 +1218,7 @@ private const val a2uiResetJS: String = """ (() => { try { - if (!globalThis.clawdbotA2UI) return { ok: false, error: "missing clawdbotA2UI" }; + if (!globalThis.clawdbotA2UI) return { ok: false, error: "missing moltbotA2UI" }; return globalThis.clawdbotA2UI.reset(); } catch (e) { return { ok: false, error: String(e?.message ?? e) }; @@ -1230,7 +1230,7 @@ private fun a2uiApplyMessagesJS(messagesJson: String): String { return """ (() => { try { - if (!globalThis.clawdbotA2UI) return { ok: false, error: "missing clawdbotA2UI" }; + if (!globalThis.clawdbotA2UI) return { ok: false, error: "missing moltbotA2UI" }; const messages = $messagesJson; return globalThis.clawdbotA2UI.applyMessages(messages); } catch (e) { diff --git a/apps/android/app/src/main/java/com/clawdbot/android/PermissionRequester.kt b/apps/android/app/src/main/java/bot/molt/android/PermissionRequester.kt similarity index 97% rename from apps/android/app/src/main/java/com/clawdbot/android/PermissionRequester.kt rename to apps/android/app/src/main/java/bot/molt/android/PermissionRequester.kt index fa294aa24..78ae0b62b 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/PermissionRequester.kt +++ b/apps/android/app/src/main/java/bot/molt/android/PermissionRequester.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android +package bot.molt.android import android.content.pm.PackageManager import android.content.Intent @@ -115,7 +115,7 @@ class PermissionRequester(private val activity: ComponentActivity) { private fun buildRationaleMessage(permissions: List): String { val labels = permissions.map { permissionLabel(it) } - return "Clawdbot needs ${labels.joinToString(", ")} permissions to continue." + return "Moltbot needs ${labels.joinToString(", ")} permissions to continue." } private fun buildSettingsMessage(permissions: List): String { diff --git a/apps/android/app/src/main/java/com/clawdbot/android/ScreenCaptureRequester.kt b/apps/android/app/src/main/java/bot/molt/android/ScreenCaptureRequester.kt similarity index 95% rename from apps/android/app/src/main/java/com/clawdbot/android/ScreenCaptureRequester.kt rename to apps/android/app/src/main/java/bot/molt/android/ScreenCaptureRequester.kt index d270d5273..29d662044 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/ScreenCaptureRequester.kt +++ b/apps/android/app/src/main/java/bot/molt/android/ScreenCaptureRequester.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android +package bot.molt.android import android.app.Activity import android.content.Context @@ -55,7 +55,7 @@ class ScreenCaptureRequester(private val activity: ComponentActivity) { suspendCancellableCoroutine { cont -> AlertDialog.Builder(activity) .setTitle("Screen recording required") - .setMessage("Clawdbot needs to record the screen for this command.") + .setMessage("Moltbot needs to record the screen for this command.") .setPositiveButton("Continue") { _, _ -> cont.resume(true) } .setNegativeButton("Not now") { _, _ -> cont.resume(false) } .setOnCancelListener { cont.resume(false) } diff --git a/apps/android/app/src/main/java/com/clawdbot/android/SecurePrefs.kt b/apps/android/app/src/main/java/bot/molt/android/SecurePrefs.kt similarity index 99% rename from apps/android/app/src/main/java/com/clawdbot/android/SecurePrefs.kt rename to apps/android/app/src/main/java/bot/molt/android/SecurePrefs.kt index cd6270dd5..7ee3294dc 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/SecurePrefs.kt +++ b/apps/android/app/src/main/java/bot/molt/android/SecurePrefs.kt @@ -1,6 +1,6 @@ @file:Suppress("DEPRECATION") -package com.clawdbot.android +package bot.molt.android import android.content.Context import androidx.core.content.edit @@ -31,7 +31,7 @@ class SecurePrefs(context: Context) { private val prefs = EncryptedSharedPreferences.create( context, - "clawdbot.node.secure", + "moltbot.node.secure", masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM, diff --git a/apps/android/app/src/main/java/com/clawdbot/android/SessionKey.kt b/apps/android/app/src/main/java/bot/molt/android/SessionKey.kt similarity index 92% rename from apps/android/app/src/main/java/com/clawdbot/android/SessionKey.kt rename to apps/android/app/src/main/java/bot/molt/android/SessionKey.kt index e1aae9ec0..e64051649 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/SessionKey.kt +++ b/apps/android/app/src/main/java/bot/molt/android/SessionKey.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android +package bot.molt.android internal fun normalizeMainKey(raw: String?): String { val trimmed = raw?.trim() diff --git a/apps/android/app/src/main/java/com/clawdbot/android/VoiceWakeMode.kt b/apps/android/app/src/main/java/bot/molt/android/VoiceWakeMode.kt similarity index 90% rename from apps/android/app/src/main/java/com/clawdbot/android/VoiceWakeMode.kt rename to apps/android/app/src/main/java/bot/molt/android/VoiceWakeMode.kt index e8a6eff15..e0862cc25 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/VoiceWakeMode.kt +++ b/apps/android/app/src/main/java/bot/molt/android/VoiceWakeMode.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android +package bot.molt.android enum class VoiceWakeMode(val rawValue: String) { Off("off"), @@ -12,4 +12,3 @@ enum class VoiceWakeMode(val rawValue: String) { } } } - diff --git a/apps/android/app/src/main/java/com/clawdbot/android/WakeWords.kt b/apps/android/app/src/main/java/bot/molt/android/WakeWords.kt similarity index 95% rename from apps/android/app/src/main/java/com/clawdbot/android/WakeWords.kt rename to apps/android/app/src/main/java/bot/molt/android/WakeWords.kt index d54ed1e08..56b85a5df 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/WakeWords.kt +++ b/apps/android/app/src/main/java/bot/molt/android/WakeWords.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android +package bot.molt.android object WakeWords { const val maxWords: Int = 32 diff --git a/apps/android/app/src/main/java/com/clawdbot/android/chat/ChatController.kt b/apps/android/app/src/main/java/bot/molt/android/chat/ChatController.kt similarity index 99% rename from apps/android/app/src/main/java/com/clawdbot/android/chat/ChatController.kt rename to apps/android/app/src/main/java/bot/molt/android/chat/ChatController.kt index a8e64048c..eef66fece 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/chat/ChatController.kt +++ b/apps/android/app/src/main/java/bot/molt/android/chat/ChatController.kt @@ -1,6 +1,6 @@ -package com.clawdbot.android.chat +package bot.molt.android.chat -import com.clawdbot.android.gateway.GatewaySession +import bot.molt.android.gateway.GatewaySession import java.util.UUID import java.util.concurrent.ConcurrentHashMap import kotlinx.coroutines.CoroutineScope diff --git a/apps/android/app/src/main/java/com/clawdbot/android/chat/ChatModels.kt b/apps/android/app/src/main/java/bot/molt/android/chat/ChatModels.kt similarity index 96% rename from apps/android/app/src/main/java/com/clawdbot/android/chat/ChatModels.kt rename to apps/android/app/src/main/java/bot/molt/android/chat/ChatModels.kt index ad84e8c69..340624452 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/chat/ChatModels.kt +++ b/apps/android/app/src/main/java/bot/molt/android/chat/ChatModels.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.chat +package bot.molt.android.chat data class ChatMessage( val id: String, diff --git a/apps/android/app/src/main/java/com/clawdbot/android/gateway/BonjourEscapes.kt b/apps/android/app/src/main/java/bot/molt/android/gateway/BonjourEscapes.kt similarity index 96% rename from apps/android/app/src/main/java/com/clawdbot/android/gateway/BonjourEscapes.kt rename to apps/android/app/src/main/java/bot/molt/android/gateway/BonjourEscapes.kt index c05d41b4b..2c0c34d68 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/gateway/BonjourEscapes.kt +++ b/apps/android/app/src/main/java/bot/molt/android/gateway/BonjourEscapes.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.gateway +package bot.molt.android.gateway object BonjourEscapes { fun decode(input: String): String { diff --git a/apps/android/app/src/main/java/com/clawdbot/android/gateway/DeviceAuthStore.kt b/apps/android/app/src/main/java/bot/molt/android/gateway/DeviceAuthStore.kt similarity index 90% rename from apps/android/app/src/main/java/com/clawdbot/android/gateway/DeviceAuthStore.kt rename to apps/android/app/src/main/java/bot/molt/android/gateway/DeviceAuthStore.kt index 88643d8d7..6b90b4672 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/gateway/DeviceAuthStore.kt +++ b/apps/android/app/src/main/java/bot/molt/android/gateway/DeviceAuthStore.kt @@ -1,6 +1,6 @@ -package com.clawdbot.android.gateway +package bot.molt.android.gateway -import com.clawdbot.android.SecurePrefs +import bot.molt.android.SecurePrefs class DeviceAuthStore(private val prefs: SecurePrefs) { fun loadToken(deviceId: String, role: String): String? { diff --git a/apps/android/app/src/main/java/com/clawdbot/android/gateway/DeviceIdentityStore.kt b/apps/android/app/src/main/java/bot/molt/android/gateway/DeviceIdentityStore.kt similarity index 97% rename from apps/android/app/src/main/java/com/clawdbot/android/gateway/DeviceIdentityStore.kt rename to apps/android/app/src/main/java/bot/molt/android/gateway/DeviceIdentityStore.kt index 72500b750..58a0aceff 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/gateway/DeviceIdentityStore.kt +++ b/apps/android/app/src/main/java/bot/molt/android/gateway/DeviceIdentityStore.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.gateway +package bot.molt.android.gateway import android.content.Context import android.util.Base64 @@ -21,7 +21,7 @@ data class DeviceIdentity( class DeviceIdentityStore(context: Context) { private val json = Json { ignoreUnknownKeys = true } - private val identityFile = File(context.filesDir, "clawdbot/identity/device.json") + private val identityFile = File(context.filesDir, "moltbot/identity/device.json") @Synchronized fun loadOrCreate(): DeviceIdentity { diff --git a/apps/android/app/src/main/java/com/clawdbot/android/gateway/GatewayDiscovery.kt b/apps/android/app/src/main/java/bot/molt/android/gateway/GatewayDiscovery.kt similarity index 98% rename from apps/android/app/src/main/java/com/clawdbot/android/gateway/GatewayDiscovery.kt rename to apps/android/app/src/main/java/bot/molt/android/gateway/GatewayDiscovery.kt index 121a95485..53bdb5588 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/gateway/GatewayDiscovery.kt +++ b/apps/android/app/src/main/java/bot/molt/android/gateway/GatewayDiscovery.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.gateway +package bot.molt.android.gateway import android.content.Context import android.net.ConnectivityManager @@ -51,9 +51,9 @@ class GatewayDiscovery( private val nsd = context.getSystemService(NsdManager::class.java) private val connectivity = context.getSystemService(ConnectivityManager::class.java) private val dns = DnsResolver.getInstance() - private val serviceType = "_clawdbot-gw._tcp." - private val wideAreaDomain = "clawdbot.internal." - private val logTag = "Clawdbot/GatewayDiscovery" + private val serviceType = "_moltbot-gw._tcp." + private val wideAreaDomain = "moltbot.internal." + private val logTag = "Moltbot/GatewayDiscovery" private val localById = ConcurrentHashMap() private val unicastById = ConcurrentHashMap() diff --git a/apps/android/app/src/main/java/com/clawdbot/android/gateway/GatewayEndpoint.kt b/apps/android/app/src/main/java/bot/molt/android/gateway/GatewayEndpoint.kt similarity index 94% rename from apps/android/app/src/main/java/com/clawdbot/android/gateway/GatewayEndpoint.kt rename to apps/android/app/src/main/java/bot/molt/android/gateway/GatewayEndpoint.kt index ab8aeacc9..2c524cc67 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/gateway/GatewayEndpoint.kt +++ b/apps/android/app/src/main/java/bot/molt/android/gateway/GatewayEndpoint.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.gateway +package bot.molt.android.gateway data class GatewayEndpoint( val stableId: String, diff --git a/apps/android/app/src/main/java/com/clawdbot/android/gateway/GatewayProtocol.kt b/apps/android/app/src/main/java/bot/molt/android/gateway/GatewayProtocol.kt similarity index 51% rename from apps/android/app/src/main/java/com/clawdbot/android/gateway/GatewayProtocol.kt rename to apps/android/app/src/main/java/bot/molt/android/gateway/GatewayProtocol.kt index 4873de122..6836331be 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/gateway/GatewayProtocol.kt +++ b/apps/android/app/src/main/java/bot/molt/android/gateway/GatewayProtocol.kt @@ -1,3 +1,3 @@ -package com.clawdbot.android.gateway +package bot.molt.android.gateway const val GATEWAY_PROTOCOL_VERSION = 3 diff --git a/apps/android/app/src/main/java/com/clawdbot/android/gateway/GatewaySession.kt b/apps/android/app/src/main/java/bot/molt/android/gateway/GatewaySession.kt similarity index 99% rename from apps/android/app/src/main/java/com/clawdbot/android/gateway/GatewaySession.kt rename to apps/android/app/src/main/java/bot/molt/android/gateway/GatewaySession.kt index ddd249a8e..13074b918 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/gateway/GatewaySession.kt +++ b/apps/android/app/src/main/java/bot/molt/android/gateway/GatewaySession.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.gateway +package bot.molt.android.gateway import android.util.Log import java.util.Locale @@ -148,7 +148,7 @@ class GatewaySession( try { conn.request("node.event", params, timeoutMs = 8_000) } catch (err: Throwable) { - Log.w("ClawdbotGateway", "node.event failed: ${err.message ?: err::class.java.simpleName}") + Log.w("MoltbotGateway", "node.event failed: ${err.message ?: err::class.java.simpleName}") } } @@ -181,7 +181,7 @@ class GatewaySession( private val connectNonceDeferred = CompletableDeferred() private val client: OkHttpClient = buildClient() private var socket: WebSocket? = null - private val loggerTag = "ClawdbotGateway" + private val loggerTag = "MoltbotGateway" val remoteAddress: String = if (endpoint.host.contains(":")) { diff --git a/apps/android/app/src/main/java/com/clawdbot/android/gateway/GatewayTls.kt b/apps/android/app/src/main/java/bot/molt/android/gateway/GatewayTls.kt similarity index 98% rename from apps/android/app/src/main/java/com/clawdbot/android/gateway/GatewayTls.kt rename to apps/android/app/src/main/java/bot/molt/android/gateway/GatewayTls.kt index bcca51583..673d60c8f 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/gateway/GatewayTls.kt +++ b/apps/android/app/src/main/java/bot/molt/android/gateway/GatewayTls.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.gateway +package bot.molt.android.gateway import android.annotation.SuppressLint import java.security.MessageDigest diff --git a/apps/android/app/src/main/java/com/clawdbot/android/node/CameraCaptureManager.kt b/apps/android/app/src/main/java/bot/molt/android/node/CameraCaptureManager.kt similarity index 98% rename from apps/android/app/src/main/java/com/clawdbot/android/node/CameraCaptureManager.kt rename to apps/android/app/src/main/java/bot/molt/android/node/CameraCaptureManager.kt index 0361c2e55..cb15a3915 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/node/CameraCaptureManager.kt +++ b/apps/android/app/src/main/java/bot/molt/android/node/CameraCaptureManager.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.node +package bot.molt.android.node import android.Manifest import android.content.Context @@ -22,7 +22,7 @@ import androidx.camera.video.VideoRecordEvent import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat.checkSelfPermission import androidx.core.graphics.scale -import com.clawdbot.android.PermissionRequester +import bot.molt.android.PermissionRequester import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withTimeout @@ -155,7 +155,7 @@ class CameraCaptureManager(private val context: Context) { provider.unbindAll() provider.bindToLifecycle(owner, selector, videoCapture) - val file = File.createTempFile("clawdbot-clip-", ".mp4") + val file = File.createTempFile("moltbot-clip-", ".mp4") val outputOptions = FileOutputOptions.Builder(file).build() val finalized = kotlinx.coroutines.CompletableDeferred() @@ -285,7 +285,7 @@ private suspend fun Context.cameraProvider(): ProcessCameraProvider = /** Returns (jpegBytes, exifOrientation) so caller can rotate the decoded bitmap. */ private suspend fun ImageCapture.takeJpegWithExif(executor: Executor): Pair = suspendCancellableCoroutine { cont -> - val file = File.createTempFile("clawdbot-snap-", ".jpg") + val file = File.createTempFile("moltbot-snap-", ".jpg") val options = ImageCapture.OutputFileOptions.Builder(file).build() takePicture( options, diff --git a/apps/android/app/src/main/java/com/clawdbot/android/node/CanvasController.kt b/apps/android/app/src/main/java/bot/molt/android/node/CanvasController.kt similarity index 97% rename from apps/android/app/src/main/java/com/clawdbot/android/node/CanvasController.kt rename to apps/android/app/src/main/java/bot/molt/android/node/CanvasController.kt index c7c7a76e1..4d33ed0a6 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/node/CanvasController.kt +++ b/apps/android/app/src/main/java/bot/molt/android/node/CanvasController.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.node +package bot.molt.android.node import android.graphics.Bitmap import android.graphics.Canvas @@ -17,7 +17,7 @@ import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive -import com.clawdbot.android.BuildConfig +import bot.molt.android.BuildConfig import kotlin.coroutines.resume class CanvasController { @@ -84,12 +84,12 @@ class CanvasController { withWebViewOnMain { wv -> if (currentUrl == null) { if (BuildConfig.DEBUG) { - Log.d("ClawdbotCanvas", "load scaffold: $scaffoldAssetUrl") + Log.d("MoltbotCanvas", "load scaffold: $scaffoldAssetUrl") } wv.loadUrl(scaffoldAssetUrl) } else { if (BuildConfig.DEBUG) { - Log.d("ClawdbotCanvas", "load url: $currentUrl") + Log.d("MoltbotCanvas", "load url: $currentUrl") } wv.loadUrl(currentUrl) } @@ -106,7 +106,7 @@ class CanvasController { val js = """ (() => { try { - const api = globalThis.__clawdbot; + const api = globalThis.__moltbot; if (!api) return; if (typeof api.setDebugStatusEnabled === 'function') { api.setDebugStatusEnabled(${if (enabled) "true" else "false"}); diff --git a/apps/android/app/src/main/java/com/clawdbot/android/node/JpegSizeLimiter.kt b/apps/android/app/src/main/java/bot/molt/android/node/JpegSizeLimiter.kt similarity index 98% rename from apps/android/app/src/main/java/com/clawdbot/android/node/JpegSizeLimiter.kt rename to apps/android/app/src/main/java/bot/molt/android/node/JpegSizeLimiter.kt index ec71e9a4b..8fb6c35d4 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/node/JpegSizeLimiter.kt +++ b/apps/android/app/src/main/java/bot/molt/android/node/JpegSizeLimiter.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.node +package bot.molt.android.node import kotlin.math.max import kotlin.math.min diff --git a/apps/android/app/src/main/java/com/clawdbot/android/node/LocationCaptureManager.kt b/apps/android/app/src/main/java/bot/molt/android/node/LocationCaptureManager.kt similarity index 99% rename from apps/android/app/src/main/java/com/clawdbot/android/node/LocationCaptureManager.kt rename to apps/android/app/src/main/java/bot/molt/android/node/LocationCaptureManager.kt index c701be70d..c56eee03a 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/node/LocationCaptureManager.kt +++ b/apps/android/app/src/main/java/bot/molt/android/node/LocationCaptureManager.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.node +package bot.molt.android.node import android.Manifest import android.content.Context diff --git a/apps/android/app/src/main/java/com/clawdbot/android/node/ScreenRecordManager.kt b/apps/android/app/src/main/java/bot/molt/android/node/ScreenRecordManager.kt similarity index 95% rename from apps/android/app/src/main/java/com/clawdbot/android/node/ScreenRecordManager.kt rename to apps/android/app/src/main/java/bot/molt/android/node/ScreenRecordManager.kt index 82e805530..0e785c245 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/node/ScreenRecordManager.kt +++ b/apps/android/app/src/main/java/bot/molt/android/node/ScreenRecordManager.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.node +package bot.molt.android.node import android.content.Context import android.hardware.display.DisplayManager @@ -6,7 +6,7 @@ import android.media.MediaRecorder import android.media.projection.MediaProjectionManager import android.os.Build import android.util.Base64 -import com.clawdbot.android.ScreenCaptureRequester +import bot.molt.android.ScreenCaptureRequester import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.withContext @@ -17,13 +17,13 @@ class ScreenRecordManager(private val context: Context) { data class Payload(val payloadJson: String) @Volatile private var screenCaptureRequester: ScreenCaptureRequester? = null - @Volatile private var permissionRequester: com.clawdbot.android.PermissionRequester? = null + @Volatile private var permissionRequester: bot.molt.android.PermissionRequester? = null fun attachScreenCaptureRequester(requester: ScreenCaptureRequester) { screenCaptureRequester = requester } - fun attachPermissionRequester(requester: com.clawdbot.android.PermissionRequester) { + fun attachPermissionRequester(requester: bot.molt.android.PermissionRequester) { permissionRequester = requester } @@ -63,7 +63,7 @@ class ScreenRecordManager(private val context: Context) { val height = metrics.heightPixels val densityDpi = metrics.densityDpi - val file = File.createTempFile("clawdbot-screen-", ".mp4") + val file = File.createTempFile("moltbot-screen-", ".mp4") if (includeAudio) ensureMicPermission() val recorder = createMediaRecorder() @@ -90,7 +90,7 @@ class ScreenRecordManager(private val context: Context) { val surface = recorder.surface virtualDisplay = projection.createVirtualDisplay( - "clawdbot-screen", + "moltbot-screen", width, height, densityDpi, diff --git a/apps/android/app/src/main/java/com/clawdbot/android/node/SmsManager.kt b/apps/android/app/src/main/java/bot/molt/android/node/SmsManager.kt similarity index 98% rename from apps/android/app/src/main/java/com/clawdbot/android/node/SmsManager.kt rename to apps/android/app/src/main/java/bot/molt/android/node/SmsManager.kt index e449993d2..0314ee1a7 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/node/SmsManager.kt +++ b/apps/android/app/src/main/java/bot/molt/android/node/SmsManager.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.node +package bot.molt.android.node import android.Manifest import android.content.Context @@ -11,7 +11,7 @@ import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.jsonObject import kotlinx.serialization.encodeToString -import com.clawdbot.android.PermissionRequester +import bot.molt.android.PermissionRequester /** * Sends SMS messages via the Android SMS API. @@ -135,7 +135,7 @@ class SmsManager(private val context: Context) { /** * Send an SMS message. - * + * * @param paramsJson JSON with "to" (phone number) and "message" (text) fields * @return SendResult indicating success or failure */ diff --git a/apps/android/app/src/main/java/com/clawdbot/android/protocol/ClawdbotCanvasA2UIAction.kt b/apps/android/app/src/main/java/bot/molt/android/protocol/ClawdbotCanvasA2UIAction.kt similarity index 89% rename from apps/android/app/src/main/java/com/clawdbot/android/protocol/ClawdbotCanvasA2UIAction.kt rename to apps/android/app/src/main/java/bot/molt/android/protocol/ClawdbotCanvasA2UIAction.kt index a6d6ee136..f73879bb2 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/protocol/ClawdbotCanvasA2UIAction.kt +++ b/apps/android/app/src/main/java/bot/molt/android/protocol/ClawdbotCanvasA2UIAction.kt @@ -1,9 +1,9 @@ -package com.clawdbot.android.protocol +package bot.molt.android.protocol import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive -object ClawdbotCanvasA2UIAction { +object MoltbotCanvasA2UIAction { fun extractActionName(userAction: JsonObject): String? { val name = (userAction["name"] as? JsonPrimitive) @@ -61,6 +61,6 @@ object ClawdbotCanvasA2UIAction { val err = (error ?: "").replace("\\", "\\\\").replace("\"", "\\\"") val okLiteral = if (ok) "true" else "false" val idEscaped = actionId.replace("\\", "\\\\").replace("\"", "\\\"") - return "window.dispatchEvent(new CustomEvent('clawdbot:a2ui-action-status', { detail: { id: \"${idEscaped}\", ok: ${okLiteral}, error: \"${err}\" } }));" + return "window.dispatchEvent(new CustomEvent('moltbot:a2ui-action-status', { detail: { id: \"${idEscaped}\", ok: ${okLiteral}, error: \"${err}\" } }));" } } diff --git a/apps/android/app/src/main/java/com/clawdbot/android/protocol/ClawdbotProtocolConstants.kt b/apps/android/app/src/main/java/bot/molt/android/protocol/ClawdbotProtocolConstants.kt similarity index 69% rename from apps/android/app/src/main/java/com/clawdbot/android/protocol/ClawdbotProtocolConstants.kt rename to apps/android/app/src/main/java/bot/molt/android/protocol/ClawdbotProtocolConstants.kt index bfdb5f782..27d46c3f1 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/protocol/ClawdbotProtocolConstants.kt +++ b/apps/android/app/src/main/java/bot/molt/android/protocol/ClawdbotProtocolConstants.kt @@ -1,6 +1,6 @@ -package com.clawdbot.android.protocol +package bot.molt.android.protocol -enum class ClawdbotCapability(val rawValue: String) { +enum class MoltbotCapability(val rawValue: String) { Canvas("canvas"), Camera("camera"), Screen("screen"), @@ -9,7 +9,7 @@ enum class ClawdbotCapability(val rawValue: String) { Location("location"), } -enum class ClawdbotCanvasCommand(val rawValue: String) { +enum class MoltbotCanvasCommand(val rawValue: String) { Present("canvas.present"), Hide("canvas.hide"), Navigate("canvas.navigate"), @@ -22,7 +22,7 @@ enum class ClawdbotCanvasCommand(val rawValue: String) { } } -enum class ClawdbotCanvasA2UICommand(val rawValue: String) { +enum class MoltbotCanvasA2UICommand(val rawValue: String) { Push("canvas.a2ui.push"), PushJSONL("canvas.a2ui.pushJSONL"), Reset("canvas.a2ui.reset"), @@ -33,7 +33,7 @@ enum class ClawdbotCanvasA2UICommand(val rawValue: String) { } } -enum class ClawdbotCameraCommand(val rawValue: String) { +enum class MoltbotCameraCommand(val rawValue: String) { Snap("camera.snap"), Clip("camera.clip"), ; @@ -43,7 +43,7 @@ enum class ClawdbotCameraCommand(val rawValue: String) { } } -enum class ClawdbotScreenCommand(val rawValue: String) { +enum class MoltbotScreenCommand(val rawValue: String) { Record("screen.record"), ; @@ -52,7 +52,7 @@ enum class ClawdbotScreenCommand(val rawValue: String) { } } -enum class ClawdbotSmsCommand(val rawValue: String) { +enum class MoltbotSmsCommand(val rawValue: String) { Send("sms.send"), ; @@ -61,7 +61,7 @@ enum class ClawdbotSmsCommand(val rawValue: String) { } } -enum class ClawdbotLocationCommand(val rawValue: String) { +enum class MoltbotLocationCommand(val rawValue: String) { Get("location.get"), ; diff --git a/apps/android/app/src/main/java/com/clawdbot/android/tools/ToolDisplay.kt b/apps/android/app/src/main/java/bot/molt/android/tools/ToolDisplay.kt similarity index 99% rename from apps/android/app/src/main/java/com/clawdbot/android/tools/ToolDisplay.kt rename to apps/android/app/src/main/java/bot/molt/android/tools/ToolDisplay.kt index aed5d0b4b..6f4862887 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/tools/ToolDisplay.kt +++ b/apps/android/app/src/main/java/bot/molt/android/tools/ToolDisplay.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.tools +package bot.molt.android.tools import android.content.Context import kotlinx.serialization.Serializable diff --git a/apps/android/app/src/main/java/com/clawdbot/android/ui/CameraHudOverlay.kt b/apps/android/app/src/main/java/bot/molt/android/ui/CameraHudOverlay.kt similarity index 97% rename from apps/android/app/src/main/java/com/clawdbot/android/ui/CameraHudOverlay.kt rename to apps/android/app/src/main/java/bot/molt/android/ui/CameraHudOverlay.kt index 2143ba7f8..7b45efae9 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/ui/CameraHudOverlay.kt +++ b/apps/android/app/src/main/java/bot/molt/android/ui/CameraHudOverlay.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.ui +package bot.molt.android.ui import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box diff --git a/apps/android/app/src/main/java/com/clawdbot/android/ui/ChatSheet.kt b/apps/android/app/src/main/java/bot/molt/android/ui/ChatSheet.kt similarity index 52% rename from apps/android/app/src/main/java/com/clawdbot/android/ui/ChatSheet.kt rename to apps/android/app/src/main/java/bot/molt/android/ui/ChatSheet.kt index 6f15e5922..21af1a4c6 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/ui/ChatSheet.kt +++ b/apps/android/app/src/main/java/bot/molt/android/ui/ChatSheet.kt @@ -1,8 +1,8 @@ -package com.clawdbot.android.ui +package bot.molt.android.ui import androidx.compose.runtime.Composable -import com.clawdbot.android.MainViewModel -import com.clawdbot.android.ui.chat.ChatSheetContent +import bot.molt.android.MainViewModel +import bot.molt.android.ui.chat.ChatSheetContent @Composable fun ChatSheet(viewModel: MainViewModel) { diff --git a/apps/android/app/src/main/java/com/clawdbot/android/ui/ClawdbotTheme.kt b/apps/android/app/src/main/java/bot/molt/android/ui/ClawdbotTheme.kt similarity index 92% rename from apps/android/app/src/main/java/com/clawdbot/android/ui/ClawdbotTheme.kt rename to apps/android/app/src/main/java/bot/molt/android/ui/ClawdbotTheme.kt index 0eea8da39..c292aa25d 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/ui/ClawdbotTheme.kt +++ b/apps/android/app/src/main/java/bot/molt/android/ui/ClawdbotTheme.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.ui +package bot.molt.android.ui import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme @@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext @Composable -fun ClawdbotTheme(content: @Composable () -> Unit) { +fun MoltbotTheme(content: @Composable () -> Unit) { val context = LocalContext.current val isDark = isSystemInDarkTheme() val colorScheme = if (isDark) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) diff --git a/apps/android/app/src/main/java/com/clawdbot/android/ui/RootScreen.kt b/apps/android/app/src/main/java/bot/molt/android/ui/RootScreen.kt similarity index 96% rename from apps/android/app/src/main/java/com/clawdbot/android/ui/RootScreen.kt rename to apps/android/app/src/main/java/bot/molt/android/ui/RootScreen.kt index 96d2543a7..67d76b82f 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/ui/RootScreen.kt +++ b/apps/android/app/src/main/java/bot/molt/android/ui/RootScreen.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.ui +package bot.molt.android.ui import android.annotation.SuppressLint import android.Manifest @@ -65,8 +65,8 @@ import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.window.Popup import androidx.compose.ui.window.PopupProperties import androidx.core.content.ContextCompat -import com.clawdbot.android.CameraHudKind -import com.clawdbot.android.MainViewModel +import bot.molt.android.CameraHudKind +import bot.molt.android.MainViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -333,7 +333,7 @@ private fun CanvasView(viewModel: MainViewModel, modifier: Modifier = Modifier) disableForceDarkIfSupported(settings) } if (isDebuggable) { - Log.d("ClawdbotWebView", "userAgent: ${settings.userAgentString}") + Log.d("MoltbotWebView", "userAgent: ${settings.userAgentString}") } isScrollContainer = true overScrollMode = View.OVER_SCROLL_IF_CONTENT_SCROLLS @@ -348,7 +348,7 @@ private fun CanvasView(viewModel: MainViewModel, modifier: Modifier = Modifier) ) { if (!isDebuggable) return if (!request.isForMainFrame) return - Log.e("ClawdbotWebView", "onReceivedError: ${error.errorCode} ${error.description} ${request.url}") + Log.e("MoltbotWebView", "onReceivedError: ${error.errorCode} ${error.description} ${request.url}") } override fun onReceivedHttpError( @@ -359,14 +359,14 @@ private fun CanvasView(viewModel: MainViewModel, modifier: Modifier = Modifier) if (!isDebuggable) return if (!request.isForMainFrame) return Log.e( - "ClawdbotWebView", + "MoltbotWebView", "onReceivedHttpError: ${errorResponse.statusCode} ${errorResponse.reasonPhrase} ${request.url}", ) } override fun onPageFinished(view: WebView, url: String?) { if (isDebuggable) { - Log.d("ClawdbotWebView", "onPageFinished: $url") + Log.d("MoltbotWebView", "onPageFinished: $url") } viewModel.canvas.onPageFinished() } @@ -377,7 +377,7 @@ private fun CanvasView(viewModel: MainViewModel, modifier: Modifier = Modifier) ): Boolean { if (isDebuggable) { Log.e( - "ClawdbotWebView", + "MoltbotWebView", "onRenderProcessGone didCrash=${detail.didCrash()} priorityAtExit=${detail.rendererPriorityAtExit()}", ) } @@ -390,7 +390,7 @@ private fun CanvasView(viewModel: MainViewModel, modifier: Modifier = Modifier) if (!isDebuggable) return false val msg = consoleMessage ?: return false Log.d( - "ClawdbotWebView", + "MoltbotWebView", "console ${msg.messageLevel()} @ ${msg.sourceId()}:${msg.lineNumber()} ${msg.message()}", ) return false @@ -428,7 +428,7 @@ private class CanvasA2UIActionBridge(private val onMessage: (String) -> Unit) { } companion object { - const val interfaceName: String = "clawdbotCanvasA2UIAction" + const val interfaceName: String = "moltbotCanvasA2UIAction" } } diff --git a/apps/android/app/src/main/java/com/clawdbot/android/ui/SettingsSheet.kt b/apps/android/app/src/main/java/bot/molt/android/ui/SettingsSheet.kt similarity index 98% rename from apps/android/app/src/main/java/com/clawdbot/android/ui/SettingsSheet.kt rename to apps/android/app/src/main/java/bot/molt/android/ui/SettingsSheet.kt index e3a9b3ecb..f96731acf 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/ui/SettingsSheet.kt +++ b/apps/android/app/src/main/java/bot/molt/android/ui/SettingsSheet.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.ui +package bot.molt.android.ui import android.Manifest import android.content.Context @@ -58,12 +58,12 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat -import com.clawdbot.android.BuildConfig -import com.clawdbot.android.LocationMode -import com.clawdbot.android.MainViewModel -import com.clawdbot.android.NodeForegroundService -import com.clawdbot.android.VoiceWakeMode -import com.clawdbot.android.WakeWords +import bot.molt.android.BuildConfig +import bot.molt.android.LocationMode +import bot.molt.android.MainViewModel +import bot.molt.android.NodeForegroundService +import bot.molt.android.VoiceWakeMode +import bot.molt.android.WakeWords @Composable fun SettingsSheet(viewModel: MainViewModel) { @@ -457,7 +457,7 @@ fun SettingsSheet(viewModel: MainViewModel) { Column(verticalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.fillMaxWidth()) { ListItem( headlineContent = { Text("Foreground Only") }, - supportingContent = { Text("Listens only while Clawdbot is open.") }, + supportingContent = { Text("Listens only while Moltbot is open.") }, trailingContent = { RadioButton( selected = voiceWakeMode == VoiceWakeMode.Foreground, @@ -603,7 +603,7 @@ fun SettingsSheet(viewModel: MainViewModel) { ) ListItem( headlineContent = { Text("While Using") }, - supportingContent = { Text("Only while Clawdbot is open.") }, + supportingContent = { Text("Only while Moltbot is open.") }, trailingContent = { RadioButton( selected = locationMode == LocationMode.WhileUsing, @@ -650,7 +650,7 @@ fun SettingsSheet(viewModel: MainViewModel) { item { ListItem( headlineContent = { Text("Prevent Sleep") }, - supportingContent = { Text("Keeps the screen awake while Clawdbot is open.") }, + supportingContent = { Text("Keeps the screen awake while Moltbot is open.") }, trailingContent = { Switch(checked = preventSleep, onCheckedChange = viewModel::setPreventSleep) }, ) } diff --git a/apps/android/app/src/main/java/com/clawdbot/android/ui/StatusPill.kt b/apps/android/app/src/main/java/bot/molt/android/ui/StatusPill.kt similarity index 99% rename from apps/android/app/src/main/java/com/clawdbot/android/ui/StatusPill.kt rename to apps/android/app/src/main/java/bot/molt/android/ui/StatusPill.kt index 564d96b52..199bcbf82 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/ui/StatusPill.kt +++ b/apps/android/app/src/main/java/bot/molt/android/ui/StatusPill.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.ui +package bot.molt.android.ui import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row diff --git a/apps/android/app/src/main/java/com/clawdbot/android/ui/TalkOrbOverlay.kt b/apps/android/app/src/main/java/bot/molt/android/ui/TalkOrbOverlay.kt similarity index 99% rename from apps/android/app/src/main/java/com/clawdbot/android/ui/TalkOrbOverlay.kt rename to apps/android/app/src/main/java/bot/molt/android/ui/TalkOrbOverlay.kt index 32225b486..9098c06ff 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/ui/TalkOrbOverlay.kt +++ b/apps/android/app/src/main/java/bot/molt/android/ui/TalkOrbOverlay.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.ui +package bot.molt.android.ui import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.RepeatMode diff --git a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatComposer.kt b/apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatComposer.kt similarity index 99% rename from apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatComposer.kt rename to apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatComposer.kt index 1f30938e0..bc0d9917f 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatComposer.kt +++ b/apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatComposer.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.ui.chat +package bot.molt.android.ui.chat import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -38,7 +38,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import com.clawdbot.android.chat.ChatSessionEntry +import bot.molt.android.chat.ChatSessionEntry @Composable fun ChatComposer( diff --git a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatMarkdown.kt b/apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatMarkdown.kt similarity index 99% rename from apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatMarkdown.kt rename to apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatMarkdown.kt index f15673fb3..10cf25b81 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatMarkdown.kt +++ b/apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatMarkdown.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.ui.chat +package bot.molt.android.ui.chat import android.graphics.BitmapFactory import android.util.Base64 diff --git a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatMessageListCard.kt b/apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatMessageListCard.kt similarity index 96% rename from apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatMessageListCard.kt rename to apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatMessageListCard.kt index a3229d4a2..1091de6c8 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatMessageListCard.kt +++ b/apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatMessageListCard.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.ui.chat +package bot.molt.android.ui.chat import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -20,8 +20,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.unit.dp -import com.clawdbot.android.chat.ChatMessage -import com.clawdbot.android.chat.ChatPendingToolCall +import bot.molt.android.chat.ChatMessage +import bot.molt.android.chat.ChatPendingToolCall @Composable fun ChatMessageListCard( diff --git a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatMessageViews.kt b/apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatMessageViews.kt similarity index 97% rename from apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatMessageViews.kt rename to apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatMessageViews.kt index 59479744e..59445be37 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatMessageViews.kt +++ b/apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatMessageViews.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.ui.chat +package bot.molt.android.ui.chat import android.graphics.BitmapFactory import android.util.Base64 @@ -31,10 +31,10 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import androidx.compose.foundation.Image -import com.clawdbot.android.chat.ChatMessage -import com.clawdbot.android.chat.ChatMessageContent -import com.clawdbot.android.chat.ChatPendingToolCall -import com.clawdbot.android.tools.ToolDisplayRegistry +import bot.molt.android.chat.ChatMessage +import bot.molt.android.chat.ChatMessageContent +import bot.molt.android.chat.ChatPendingToolCall +import bot.molt.android.tools.ToolDisplayRegistry import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import androidx.compose.ui.platform.LocalContext diff --git a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatSessionsDialog.kt b/apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatSessionsDialog.kt similarity index 97% rename from apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatSessionsDialog.kt rename to apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatSessionsDialog.kt index 9474b2362..377a13daa 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatSessionsDialog.kt +++ b/apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatSessionsDialog.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.ui.chat +package bot.molt.android.ui.chat import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -20,7 +20,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.clawdbot.android.chat.ChatSessionEntry +import bot.molt.android.chat.ChatSessionEntry @Composable fun ChatSessionsDialog( diff --git a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatSheetContent.kt b/apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatSheetContent.kt similarity index 97% rename from apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatSheetContent.kt rename to apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatSheetContent.kt index 2b58c626b..5632be70f 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatSheetContent.kt +++ b/apps/android/app/src/main/java/bot/molt/android/ui/chat/ChatSheetContent.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.ui.chat +package bot.molt.android.ui.chat import android.content.ContentResolver import android.net.Uri @@ -19,8 +19,8 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp -import com.clawdbot.android.MainViewModel -import com.clawdbot.android.chat.OutgoingAttachment +import bot.molt.android.MainViewModel +import bot.molt.android.chat.OutgoingAttachment import java.io.ByteArrayOutputStream import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch diff --git a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/SessionFilters.kt b/apps/android/app/src/main/java/bot/molt/android/ui/chat/SessionFilters.kt similarity index 94% rename from apps/android/app/src/main/java/com/clawdbot/android/ui/chat/SessionFilters.kt rename to apps/android/app/src/main/java/bot/molt/android/ui/chat/SessionFilters.kt index da08dbd1e..227fb0a02 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/SessionFilters.kt +++ b/apps/android/app/src/main/java/bot/molt/android/ui/chat/SessionFilters.kt @@ -1,6 +1,6 @@ -package com.clawdbot.android.ui.chat +package bot.molt.android.ui.chat -import com.clawdbot.android.chat.ChatSessionEntry +import bot.molt.android.chat.ChatSessionEntry private const val RECENT_WINDOW_MS = 24 * 60 * 60 * 1000L diff --git a/apps/android/app/src/main/java/com/clawdbot/android/voice/StreamingMediaDataSource.kt b/apps/android/app/src/main/java/bot/molt/android/voice/StreamingMediaDataSource.kt similarity index 98% rename from apps/android/app/src/main/java/com/clawdbot/android/voice/StreamingMediaDataSource.kt rename to apps/android/app/src/main/java/bot/molt/android/voice/StreamingMediaDataSource.kt index 6b1536ad5..7a7f61165 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/voice/StreamingMediaDataSource.kt +++ b/apps/android/app/src/main/java/bot/molt/android/voice/StreamingMediaDataSource.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.voice +package bot.molt.android.voice import android.media.MediaDataSource import kotlin.math.min diff --git a/apps/android/app/src/main/java/com/clawdbot/android/voice/TalkDirectiveParser.kt b/apps/android/app/src/main/java/bot/molt/android/voice/TalkDirectiveParser.kt similarity index 99% rename from apps/android/app/src/main/java/com/clawdbot/android/voice/TalkDirectiveParser.kt rename to apps/android/app/src/main/java/bot/molt/android/voice/TalkDirectiveParser.kt index 02d2c3967..0d969e4d1 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/voice/TalkDirectiveParser.kt +++ b/apps/android/app/src/main/java/bot/molt/android/voice/TalkDirectiveParser.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.voice +package bot.molt.android.voice import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement diff --git a/apps/android/app/src/main/java/com/clawdbot/android/voice/TalkModeManager.kt b/apps/android/app/src/main/java/bot/molt/android/voice/TalkModeManager.kt similarity index 99% rename from apps/android/app/src/main/java/com/clawdbot/android/voice/TalkModeManager.kt rename to apps/android/app/src/main/java/bot/molt/android/voice/TalkModeManager.kt index 41f98140d..f050f8bd2 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/voice/TalkModeManager.kt +++ b/apps/android/app/src/main/java/bot/molt/android/voice/TalkModeManager.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.voice +package bot.molt.android.voice import android.Manifest import android.content.Context @@ -20,9 +20,9 @@ import android.speech.tts.TextToSpeech import android.speech.tts.UtteranceProgressListener import android.util.Log import androidx.core.content.ContextCompat -import com.clawdbot.android.gateway.GatewaySession -import com.clawdbot.android.isCanonicalMainSessionKey -import com.clawdbot.android.normalizeMainKey +import bot.molt.android.gateway.GatewaySession +import bot.molt.android.isCanonicalMainSessionKey +import bot.molt.android.normalizeMainKey import java.net.HttpURLConnection import java.net.URL import java.util.UUID diff --git a/apps/android/app/src/main/java/com/clawdbot/android/voice/VoiceWakeCommandExtractor.kt b/apps/android/app/src/main/java/bot/molt/android/voice/VoiceWakeCommandExtractor.kt similarity index 97% rename from apps/android/app/src/main/java/com/clawdbot/android/voice/VoiceWakeCommandExtractor.kt rename to apps/android/app/src/main/java/bot/molt/android/voice/VoiceWakeCommandExtractor.kt index 1f527b8c8..8da4e3289 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/voice/VoiceWakeCommandExtractor.kt +++ b/apps/android/app/src/main/java/bot/molt/android/voice/VoiceWakeCommandExtractor.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.voice +package bot.molt.android.voice object VoiceWakeCommandExtractor { fun extractCommand(text: String, triggerWords: List): String? { diff --git a/apps/android/app/src/main/java/com/clawdbot/android/voice/VoiceWakeManager.kt b/apps/android/app/src/main/java/bot/molt/android/voice/VoiceWakeManager.kt similarity index 99% rename from apps/android/app/src/main/java/com/clawdbot/android/voice/VoiceWakeManager.kt rename to apps/android/app/src/main/java/bot/molt/android/voice/VoiceWakeManager.kt index 69863b4cc..b27d0e3c7 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/voice/VoiceWakeManager.kt +++ b/apps/android/app/src/main/java/bot/molt/android/voice/VoiceWakeManager.kt @@ -1,4 +1,4 @@ -package com.clawdbot.android.voice +package bot.molt.android.voice import android.content.Context import android.content.Intent diff --git a/apps/android/app/src/main/res/values/colors.xml b/apps/android/app/src/main/res/values/colors.xml index 6e79939c6..dfadc94cf 100644 --- a/apps/android/app/src/main/res/values/colors.xml +++ b/apps/android/app/src/main/res/values/colors.xml @@ -1,4 +1,3 @@ #0A0A0A - diff --git a/apps/android/app/src/main/res/values/strings.xml b/apps/android/app/src/main/res/values/strings.xml index 5e4d8a77d..0aae9e739 100644 --- a/apps/android/app/src/main/res/values/strings.xml +++ b/apps/android/app/src/main/res/values/strings.xml @@ -1,4 +1,3 @@ - Clawdbot Node + Moltbot Node - diff --git a/apps/android/app/src/main/res/values/themes.xml b/apps/android/app/src/main/res/values/themes.xml index fe7c9b17f..f90f40dc9 100644 --- a/apps/android/app/src/main/res/values/themes.xml +++ b/apps/android/app/src/main/res/values/themes.xml @@ -1,5 +1,5 @@ - - -
+ +
-
Ready
-
Waiting for agent
+
Ready
+
Waiting for agent
- - - - - - diff --git a/docker-compose.yml b/docker-compose.yml index 8b9859f05..8ce610d6a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: - clawdbot-gateway: - image: ${CLAWDBOT_IMAGE:-clawdbot:local} + moltbot-gateway: + image: ${CLAWDBOT_IMAGE:-moltbot:local} environment: HOME: /home/node TERM: xterm-256color @@ -27,8 +27,8 @@ services: "${CLAWDBOT_GATEWAY_PORT:-18789}" ] - clawdbot-cli: - image: ${CLAWDBOT_IMAGE:-clawdbot:local} + moltbot-cli: + image: ${CLAWDBOT_IMAGE:-moltbot:local} environment: HOME: /home/node TERM: xterm-256color diff --git a/docker-setup.sh b/docker-setup.sh index 776541827..0f7571e96 100755 --- a/docker-setup.sh +++ b/docker-setup.sh @@ -4,7 +4,7 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" COMPOSE_FILE="$ROOT_DIR/docker-compose.yml" EXTRA_COMPOSE_FILE="$ROOT_DIR/docker-compose.extra.yml" -IMAGE_NAME="${CLAWDBOT_IMAGE:-clawdbot:local}" +IMAGE_NAME="${CLAWDBOT_IMAGE:-moltbot:local}" EXTRA_MOUNTS="${CLAWDBOT_EXTRA_MOUNTS:-}" HOME_VOLUME_NAME="${CLAWDBOT_HOME_VOLUME:-}" @@ -56,7 +56,7 @@ write_extra_compose() { cat >"$EXTRA_COMPOSE_FILE" <<'YAML' services: - clawdbot-gateway: + moltbot-gateway: volumes: YAML @@ -71,7 +71,7 @@ YAML done cat >>"$EXTRA_COMPOSE_FILE" <<'YAML' - clawdbot-cli: + moltbot-cli: volumes: YAML @@ -181,21 +181,21 @@ echo " - Gateway token: $CLAWDBOT_GATEWAY_TOKEN" echo " - Tailscale exposure: Off" echo " - Install Gateway daemon: No" echo "" -docker compose "${COMPOSE_ARGS[@]}" run --rm clawdbot-cli onboard --no-install-daemon +docker compose "${COMPOSE_ARGS[@]}" run --rm moltbot-cli onboard --no-install-daemon echo "" echo "==> Provider setup (optional)" echo "WhatsApp (QR):" -echo " ${COMPOSE_HINT} run --rm clawdbot-cli providers login" +echo " ${COMPOSE_HINT} run --rm moltbot-cli providers login" echo "Telegram (bot token):" -echo " ${COMPOSE_HINT} run --rm clawdbot-cli providers add --provider telegram --token " +echo " ${COMPOSE_HINT} run --rm moltbot-cli providers add --provider telegram --token " echo "Discord (bot token):" -echo " ${COMPOSE_HINT} run --rm clawdbot-cli providers add --provider discord --token " -echo "Docs: https://docs.clawd.bot/providers" +echo " ${COMPOSE_HINT} run --rm moltbot-cli providers add --provider discord --token " +echo "Docs: https://docs.molt.bot/providers" echo "" echo "==> Starting gateway" -docker compose "${COMPOSE_ARGS[@]}" up -d clawdbot-gateway +docker compose "${COMPOSE_ARGS[@]}" up -d moltbot-gateway echo "" echo "Gateway running with host port mapping." @@ -205,5 +205,5 @@ echo "Workspace: $CLAWDBOT_WORKSPACE_DIR" echo "Token: $CLAWDBOT_GATEWAY_TOKEN" echo "" echo "Commands:" -echo " ${COMPOSE_HINT} logs -f clawdbot-gateway" -echo " ${COMPOSE_HINT} exec clawdbot-gateway node dist/index.js health --token \"$CLAWDBOT_GATEWAY_TOKEN\"" +echo " ${COMPOSE_HINT} logs -f moltbot-gateway" +echo " ${COMPOSE_HINT} exec moltbot-gateway node dist/index.js health --token \"$CLAWDBOT_GATEWAY_TOKEN\"" diff --git a/docs.acp.md b/docs.acp.md index ca664134e..4cc977889 100644 --- a/docs.acp.md +++ b/docs.acp.md @@ -1,12 +1,12 @@ -# Clawdbot ACP Bridge +# Moltbot ACP Bridge -This document describes how the Clawdbot ACP (Agent Client Protocol) bridge works, +This document describes how the Moltbot ACP (Agent Client Protocol) bridge works, how it maps ACP sessions to Gateway sessions, and how IDEs should invoke it. ## Overview -`clawdbot acp` exposes an ACP agent over stdio and forwards prompts to a running -Clawdbot Gateway over WebSocket. It keeps ACP session ids mapped to Gateway +`moltbot acp` exposes an ACP agent over stdio and forwards prompts to a running +Moltbot Gateway over WebSocket. It keeps ACP session ids mapped to Gateway session keys so IDEs can reconnect to the same agent transcript or reset it on request. @@ -20,25 +20,25 @@ Key goals: ## How can I use this Use ACP when an IDE or tooling speaks Agent Client Protocol and you want it to -drive a Clawdbot Gateway session. +drive a Moltbot Gateway session. Quick steps: 1. Run a Gateway (local or remote). 2. Configure the Gateway target (`gateway.remote.url` + auth) or pass flags. -3. Point the IDE to run `clawdbot acp` over stdio. +3. Point the IDE to run `moltbot acp` over stdio. Example config: ```bash -clawdbot config set gateway.remote.url wss://gateway-host:18789 -clawdbot config set gateway.remote.token +moltbot config set gateway.remote.url wss://gateway-host:18789 +moltbot config set gateway.remote.token ``` Example run: ```bash -clawdbot acp --url wss://gateway-host:18789 --token +moltbot acp --url wss://gateway-host:18789 --token ``` ## Selecting agents @@ -48,9 +48,9 @@ ACP does not pick agents directly. It routes by the Gateway session key. Use agent-scoped session keys to target a specific agent: ```bash -clawdbot acp --session agent:main:main -clawdbot acp --session agent:design:main -clawdbot acp --session agent:qa:bug-123 +moltbot acp --session agent:main:main +moltbot acp --session agent:design:main +moltbot acp --session agent:qa:bug-123 ``` Each ACP session maps to a single Gateway session key. One agent can have many @@ -64,9 +64,9 @@ Add a custom ACP agent in `~/.config/zed/settings.json`: ```json { "agent_servers": { - "Clawdbot ACP": { + "Moltbot ACP": { "type": "custom", - "command": "clawdbot", + "command": "moltbot", "args": ["acp"], "env": {} } @@ -79,9 +79,9 @@ To target a specific Gateway or agent: ```json { "agent_servers": { - "Clawdbot ACP": { + "Moltbot ACP": { "type": "custom", - "command": "clawdbot", + "command": "moltbot", "args": [ "acp", "--url", "wss://gateway-host:18789", @@ -94,11 +94,11 @@ To target a specific Gateway or agent: } ``` -In Zed, open the Agent panel and select “Clawdbot ACP” to start a thread. +In Zed, open the Agent panel and select “Moltbot ACP” to start a thread. ## Execution Model -- ACP client spawns `clawdbot acp` and speaks ACP messages over stdio. +- ACP client spawns `moltbot acp` and speaks ACP messages over stdio. - The bridge connects to the Gateway using existing auth config (or CLI flags). - ACP `prompt` translates to Gateway `chat.send`. - Gateway streaming events are translated back into ACP streaming events. @@ -115,9 +115,9 @@ You can override or reuse sessions in two ways: 1) CLI defaults ```bash -clawdbot acp --session agent:main:main -clawdbot acp --session-label "support inbox" -clawdbot acp --reset-session +moltbot acp --session agent:main:main +moltbot acp --session-label "support inbox" +moltbot acp --reset-session ``` 2) ACP metadata per session @@ -164,7 +164,7 @@ updates. Terminal Gateway states map to ACP `done` with stop reasons: ## Auth + Gateway Discovery -`clawdbot acp` resolves the Gateway URL and auth from CLI flags or config: +`moltbot acp` resolves the Gateway URL and auth from CLI flags or config: - `--url` / `--token` / `--password` take precedence. - Otherwise use configured `gateway.remote.*` settings. diff --git a/docs/CNAME b/docs/CNAME index afc13c7fe..43ca6ac7f 100644 --- a/docs/CNAME +++ b/docs/CNAME @@ -1 +1 @@ -clawdbot.com +docs.molt.bot diff --git a/docs/_config.yml b/docs/_config.yml index 13c41bb84..7233d461a 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "Clawdbot Docs" +title: "Moltbot Docs" description: "A TypeScript/Node gateway + macOS/iOS/Android companions for WhatsApp (web) and Telegram (bot)." markdown: kramdown highlighter: rouge @@ -22,7 +22,7 @@ defaults: nav: - title: "Home" url: "/" - - title: "Clawdbot Assistant" + - title: "Moltbot Assistant" url: "/start/clawd/" - title: "Gateway" url: "/gateway/" diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html index f61a98db6..a81321bf2 100644 --- a/docs/_layouts/default.html +++ b/docs/_layouts/default.html @@ -17,7 +17,7 @@ diff --git a/ui/package.json b/ui/package.json index 35f5666e8..3376e1029 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,5 +1,5 @@ { - "name": "clawdbot-control-ui", + "name": "moltbot-control-ui", "private": true, "type": "module", "scripts": { diff --git a/ui/src/main.ts b/ui/src/main.ts index 31d493921..9374bb20e 100644 --- a/ui/src/main.ts +++ b/ui/src/main.ts @@ -1,3 +1,2 @@ import "./styles.css"; import "./ui/app.ts"; - diff --git a/ui/src/styles/base.css b/ui/src/styles/base.css index 0b584d88f..f77cff9ed 100644 --- a/ui/src/styles/base.css +++ b/ui/src/styles/base.css @@ -1,60 +1,189 @@ -@import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=Unbounded:wght@400;500;600&family=Work+Sans:wght@400;500;600;700&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"); :root { - --bg: #0a0f14; - --bg-accent: #111826; - --bg-grad-1: #162031; - --bg-grad-2: #1f2a22; - --bg-overlay: rgba(255, 255, 255, 0.05); - --bg-glow: rgba(245, 159, 74, 0.12); - --panel: rgba(14, 20, 30, 0.88); - --panel-strong: rgba(18, 26, 38, 0.96); - --chrome: rgba(9, 14, 20, 0.72); - --chrome-strong: rgba(9, 14, 20, 0.86); - --text: rgba(244, 246, 251, 0.96); - --chat-text: rgba(231, 237, 244, 0.92); - --muted: rgba(156, 169, 189, 0.72); - --border: rgba(255, 255, 255, 0.09); - --border-strong: rgba(255, 255, 255, 0.16); - --accent: #f59f4a; - --accent-2: #34c7b7; - --ok: #2bd97f; - --warn: #f2c94c; - --danger: #ff6b6b; - --focus: rgba(245, 159, 74, 0.35); + /* Background - Warmer dark with depth */ + --bg: #12141a; + --bg-accent: #14161d; + --bg-elevated: #1a1d25; + --bg-hover: #262a35; + --bg-muted: #262a35; + + /* Card / Surface - More contrast between levels */ + --card: #181b22; + --card-foreground: #f4f4f5; + --card-highlight: rgba(255, 255, 255, 0.05); + --popover: #181b22; + --popover-foreground: #f4f4f5; + + /* Panel */ + --panel: #12141a; + --panel-strong: #1a1d25; + --panel-hover: #262a35; + --chrome: rgba(18, 20, 26, 0.95); + --chrome-strong: rgba(18, 20, 26, 0.98); + + /* Text - Slightly warmer */ + --text: #e4e4e7; + --text-strong: #fafafa; + --chat-text: #e4e4e7; + --muted: #71717a; + --muted-strong: #52525b; + --muted-foreground: #71717a; + + /* Border - Subtle but defined */ + --border: #27272a; + --border-strong: #3f3f46; + --border-hover: #52525b; + --input: #27272a; + --ring: #ff5c5c; + + /* Accent - Punchy signature red */ + --accent: #ff5c5c; + --accent-hover: #ff7070; + --accent-muted: #ff5c5c; + --accent-subtle: rgba(255, 92, 92, 0.15); + --accent-foreground: #fafafa; + --accent-glow: rgba(255, 92, 92, 0.25); + --primary: #ff5c5c; + --primary-foreground: #ffffff; + + /* Secondary - Teal accent for variety */ + --secondary: #1e2028; + --secondary-foreground: #f4f4f5; + --accent-2: #14b8a6; + --accent-2-muted: rgba(20, 184, 166, 0.7); + --accent-2-subtle: rgba(20, 184, 166, 0.15); + + /* Semantic - More saturated */ + --ok: #22c55e; + --ok-muted: rgba(34, 197, 94, 0.75); + --ok-subtle: rgba(34, 197, 94, 0.12); + --destructive: #ef4444; + --destructive-foreground: #fafafa; + --warn: #f59e0b; + --warn-muted: rgba(245, 158, 11, 0.75); + --warn-subtle: rgba(245, 158, 11, 0.12); + --danger: #ef4444; + --danger-muted: rgba(239, 68, 68, 0.75); + --danger-subtle: rgba(239, 68, 68, 0.12); + --info: #3b82f6; + + /* Focus - With glow */ + --focus: rgba(255, 92, 92, 0.25); + --focus-ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--ring); + --focus-glow: 0 0 0 2px var(--bg), 0 0 0 4px var(--ring), 0 0 20px var(--accent-glow); + + /* Grid */ --grid-line: rgba(255, 255, 255, 0.04); + + /* Theme transition */ --theme-switch-x: 50%; --theme-switch-y: 50%; - --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, - "Liberation Mono", "Courier New", monospace; - --font-body: "Work Sans", system-ui, sans-serif; - --font-display: "Unbounded", "Times New Roman", serif; + + /* Typography - Space Grotesk for personality */ + --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, monospace; + --font-body: "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + --font-display: "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + + /* Shadows - Richer with subtle color */ + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.03); + --shadow-lg: 0 12px 28px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(255, 255, 255, 0.03); + --shadow-xl: 0 24px 48px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.03); + --shadow-glow: 0 0 30px var(--accent-glow); + + /* Radii - Slightly larger for friendlier feel */ + --radius-sm: 6px; + --radius-md: 8px; + --radius-lg: 12px; + --radius-xl: 16px; + --radius-full: 9999px; + --radius: 8px; + + /* Transitions - Snappy but smooth */ + --ease-out: cubic-bezier(0.16, 1, 0.3, 1); + --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); + --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); + --duration-fast: 120ms; + --duration-normal: 200ms; + --duration-slow: 350ms; + color-scheme: dark; } +/* Light theme - Clean with subtle warmth */ :root[data-theme="light"] { - --bg: #f5f1ea; - --bg-accent: #ffffff; - --bg-grad-1: #f1e6d6; - --bg-grad-2: #e5eef4; - --bg-overlay: rgba(28, 32, 46, 0.05); - --bg-glow: rgba(52, 199, 183, 0.14); - --panel: rgba(255, 255, 255, 0.9); - --panel-strong: rgba(255, 255, 255, 0.97); - --chrome: rgba(255, 255, 255, 0.75); - --chrome-strong: rgba(255, 255, 255, 0.88); - --text: rgba(27, 36, 50, 0.98); - --chat-text: rgba(36, 48, 66, 0.9); - --muted: rgba(80, 94, 114, 0.7); - --border: rgba(18, 24, 40, 0.12); - --border-strong: rgba(18, 24, 40, 0.2); - --accent: #e28a3f; - --accent-2: #1ba99d; - --ok: #1aa86c; - --warn: #b3771c; - --danger: #d44848; - --focus: rgba(226, 138, 63, 0.35); - --grid-line: rgba(18, 24, 40, 0.06); + --bg: #fafafa; + --bg-accent: #f5f5f5; + --bg-elevated: #ffffff; + --bg-hover: #f0f0f0; + --bg-muted: #f0f0f0; + --bg-content: #f5f5f5; + + --card: #ffffff; + --card-foreground: #18181b; + --card-highlight: rgba(0, 0, 0, 0.03); + --popover: #ffffff; + --popover-foreground: #18181b; + + --panel: #fafafa; + --panel-strong: #f5f5f5; + --panel-hover: #ebebeb; + --chrome: rgba(250, 250, 250, 0.95); + --chrome-strong: rgba(250, 250, 250, 0.98); + + --text: #3f3f46; + --text-strong: #18181b; + --chat-text: #3f3f46; + --muted: #71717a; + --muted-strong: #52525b; + --muted-foreground: #71717a; + + --border: #e4e4e7; + --border-strong: #d4d4d8; + --border-hover: #a1a1aa; + --input: #e4e4e7; + + --accent: #dc2626; + --accent-hover: #ef4444; + --accent-muted: #dc2626; + --accent-subtle: rgba(220, 38, 38, 0.12); + --accent-foreground: #ffffff; + --accent-glow: rgba(220, 38, 38, 0.15); + --primary: #dc2626; + --primary-foreground: #ffffff; + + --secondary: #f4f4f5; + --secondary-foreground: #3f3f46; + --accent-2: #0d9488; + --accent-2-muted: rgba(13, 148, 136, 0.75); + --accent-2-subtle: rgba(13, 148, 136, 0.12); + + --ok: #16a34a; + --ok-muted: rgba(22, 163, 74, 0.75); + --ok-subtle: rgba(22, 163, 74, 0.1); + --destructive: #dc2626; + --destructive-foreground: #fafafa; + --warn: #d97706; + --warn-muted: rgba(217, 119, 6, 0.75); + --warn-subtle: rgba(217, 119, 6, 0.1); + --danger: #dc2626; + --danger-muted: rgba(220, 38, 38, 0.75); + --danger-subtle: rgba(220, 38, 38, 0.1); + --info: #2563eb; + + --focus: rgba(220, 38, 38, 0.2); + --focus-glow: 0 0 0 2px var(--bg), 0 0 0 4px var(--ring), 0 0 16px var(--accent-glow); + + --grid-line: rgba(0, 0, 0, 0.05); + + /* Light shadows */ + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08), 0 0 0 1px rgba(0, 0, 0, 0.04); + --shadow-lg: 0 12px 28px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(0, 0, 0, 0.04); + --shadow-xl: 0 24px 48px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(0, 0, 0, 0.04); + --shadow-glow: 0 0 24px var(--accent-glow); + color-scheme: light; } @@ -69,44 +198,21 @@ body { body { margin: 0; - font: 15px/1.5 var(--font-body); - background: - radial-gradient(1200px 900px at 15% -10%, var(--bg-grad-1) 0%, transparent 55%) - fixed, - radial-gradient(900px 700px at 80% 10%, var(--bg-grad-2) 0%, transparent 60%) - fixed, - linear-gradient(160deg, var(--bg) 0%, var(--bg-accent) 100%) fixed; + font: 400 14px/1.55 var(--font-body); + letter-spacing: -0.02em; + background: var(--bg); color: var(--text); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } -body::before { - content: ""; - position: fixed; - inset: 0; - background: - linear-gradient( - 140deg, - var(--bg-overlay) 0%, - rgba(255, 255, 255, 0) 40% - ), - radial-gradient(620px 420px at 75% 75%, var(--bg-glow), transparent 60%); - pointer-events: none; - z-index: 0; -} - -/* Grid overlay removed for cleaner look */ - +/* Theme transition */ @keyframes theme-circle-transition { 0% { - clip-path: circle( - 0% at var(--theme-switch-x, 50%) var(--theme-switch-y, 50%) - ); + clip-path: circle(0% at var(--theme-switch-x, 50%) var(--theme-switch-y, 50%)); } - 100% { - clip-path: circle( - 150% at var(--theme-switch-x, 50%) var(--theme-switch-y, 50%) - ); + clip-path: circle(150% at var(--theme-switch-x, 50%) var(--theme-switch-y, 50%)); } } @@ -123,7 +229,7 @@ html.theme-transition::view-transition-old(theme) { html.theme-transition::view-transition-new(theme) { mix-blend-mode: normal; z-index: 2; - animation: theme-circle-transition 0.45s ease-out forwards; + animation: theme-circle-transition 0.4s var(--ease-out) forwards; } @media (prefers-reduced-motion: reduce) { @@ -133,7 +239,7 @@ html.theme-transition::view-transition-new(theme) { } } -clawdbot-app { +moltbot-app { display: block; position: relative; z-index: 1; @@ -141,7 +247,12 @@ clawdbot-app { } a { - color: inherit; + color: var(--accent); + text-decoration: none; +} + +a:hover { + text-decoration: underline; } button, @@ -152,10 +263,35 @@ select { color: inherit; } +::selection { + background: var(--accent-subtle); + color: var(--text-strong); +} + +/* Scrollbar styling */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: var(--radius-full); +} + +::-webkit-scrollbar-thumb:hover { + background: var(--border-strong); +} + +/* Animations - Polished with spring feel */ @keyframes rise { from { opacity: 0; - transform: translateY(6px); + transform: translateY(8px); } to { opacity: 1; @@ -163,6 +299,26 @@ select { } } +@keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes scale-in { + from { + opacity: 0; + transform: scale(0.95); + } + to { + opacity: 1; + transform: scale(1); + } +} + @keyframes dashboard-enter { from { opacity: 0; @@ -173,3 +329,44 @@ select { transform: translateY(0); } } + +@keyframes shimmer { + 0% { + background-position: -200% 0; + } + 100% { + background-position: 200% 0; + } +} + +@keyframes pulse-subtle { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.7; + } +} + +@keyframes glow-pulse { + 0%, 100% { + box-shadow: 0 0 0 rgba(255, 92, 92, 0); + } + 50% { + box-shadow: 0 0 20px var(--accent-glow); + } +} + +/* Stagger animation delays for grouped elements */ +.stagger-1 { animation-delay: 0ms; } +.stagger-2 { animation-delay: 50ms; } +.stagger-3 { animation-delay: 100ms; } +.stagger-4 { animation-delay: 150ms; } +.stagger-5 { animation-delay: 200ms; } +.stagger-6 { animation-delay: 250ms; } + +/* Focus visible styles */ +:focus-visible { + outline: none; + box-shadow: var(--focus-ring); +} diff --git a/ui/src/styles/chat/grouped.css b/ui/src/styles/chat/grouped.css index 371cf03c6..39b641826 100644 --- a/ui/src/styles/chat/grouped.css +++ b/ui/src/styles/chat/grouped.css @@ -8,7 +8,7 @@ gap: 12px; align-items: flex-start; margin-bottom: 16px; - margin-left: 16px; + margin-left: 4px; margin-right: 16px; } @@ -70,23 +70,23 @@ } .chat-avatar.user { - background: rgba(245, 159, 74, 0.2); - color: rgba(245, 159, 74, 1); + background: var(--accent-subtle); + color: var(--accent); } .chat-avatar.assistant { - background: rgba(52, 199, 183, 0.2); - color: rgba(52, 199, 183, 1); + background: var(--secondary); + color: var(--muted); } .chat-avatar.other { - background: rgba(150, 150, 150, 0.2); - color: rgba(150, 150, 150, 1); + background: var(--secondary); + color: var(--muted); } .chat-avatar.tool { - background: rgba(134, 142, 150, 0.2); - color: rgba(134, 142, 150, 1); + background: var(--secondary); + color: var(--muted); } /* Image avatar support */ @@ -100,9 +100,9 @@ img.chat-avatar { .chat-bubble { position: relative; display: inline-block; - border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.12); - border-radius: 12px; + border: 1px solid transparent; + background: var(--card); + border-radius: var(--radius-lg); padding: 10px 14px; box-shadow: none; transition: background 150ms ease-out, border-color 150ms ease-out; @@ -119,9 +119,9 @@ img.chat-avatar { top: 6px; right: 8px; border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.22); + background: var(--bg); color: var(--muted); - border-radius: 8px; + border-radius: var(--radius-md); padding: 4px 6px; font-size: 14px; line-height: 1; @@ -132,9 +132,40 @@ img.chat-avatar { } .chat-copy-btn__icon { - display: inline-block; - width: 1em; - text-align: center; + display: inline-flex; + width: 14px; + height: 14px; + position: relative; +} + +.chat-copy-btn__icon svg { + width: 14px; + height: 14px; + stroke: currentColor; + fill: none; + stroke-width: 1.5px; + stroke-linecap: round; + stroke-linejoin: round; +} + +.chat-copy-btn__icon-copy, +.chat-copy-btn__icon-check { + position: absolute; + top: 0; + left: 0; + transition: opacity 150ms ease; +} + +.chat-copy-btn__icon-check { + opacity: 0; +} + +.chat-copy-btn[data-copied="1"] .chat-copy-btn__icon-copy { + opacity: 0; +} + +.chat-copy-btn[data-copied="1"] .chat-copy-btn__icon-check { + opacity: 1; } .chat-bubble:hover .chat-copy-btn { @@ -143,7 +174,7 @@ img.chat-avatar { } .chat-copy-btn:hover { - background: rgba(0, 0, 0, 0.3); + background: var(--bg-hover); } .chat-copy-btn[data-copying="1"] { @@ -154,17 +185,17 @@ img.chat-avatar { .chat-copy-btn[data-error="1"] { opacity: 1; pointer-events: auto; - border-color: rgba(255, 69, 58, 0.8); - background: rgba(255, 69, 58, 0.18); - color: rgba(255, 69, 58, 1); + border-color: var(--danger-subtle); + background: var(--danger-subtle); + color: var(--danger); } .chat-copy-btn[data-copied="1"] { opacity: 1; pointer-events: auto; - border-color: rgba(52, 199, 183, 0.8); - background: rgba(52, 199, 183, 0.18); - color: rgba(52, 199, 183, 1); + border-color: var(--ok-subtle); + background: var(--ok-subtle); + color: var(--ok); } .chat-copy-btn:focus-visible { @@ -181,18 +212,29 @@ img.chat-avatar { } } +/* Light mode: restore borders */ +:root[data-theme="light"] .chat-bubble { + border-color: var(--border); + box-shadow: inset 0 1px 0 var(--card-highlight); +} + .chat-bubble:hover { - background: rgba(0, 0, 0, 0.18); + background: var(--bg-hover); } /* User bubbles have different styling */ .chat-group.user .chat-bubble { - background: rgba(245, 159, 74, 0.15); - border-color: rgba(245, 159, 74, 0.3); + background: var(--accent-subtle); + border-color: transparent; +} + +:root[data-theme="light"] .chat-group.user .chat-bubble { + border-color: rgba(234, 88, 12, 0.2); + background: rgba(251, 146, 60, 0.12); } .chat-group.user .chat-bubble:hover { - background: rgba(245, 159, 74, 0.22); + background: rgba(255, 77, 77, 0.15); } /* Streaming animation */ diff --git a/ui/src/styles/chat/layout.css b/ui/src/styles/chat/layout.css index a416d92cd..589b0b62d 100644 --- a/ui/src/styles/chat/layout.css +++ b/ui/src/styles/chat/layout.css @@ -52,8 +52,8 @@ flex: 1 1 0; /* Grow, shrink, and use 0 base for proper scrolling */ overflow-y: auto; overflow-x: hidden; - padding: 12px; - margin: 0 -12px; + padding: 12px 4px; + margin: 0 -4px; min-height: 0; /* Allow shrinking for flex scroll behavior */ border-radius: 12px; background: transparent; @@ -87,23 +87,154 @@ border-color: var(--accent); } +.chat-focus-exit svg { + width: 16px; + height: 16px; + stroke: currentColor; + fill: none; + stroke-width: 2px; + stroke-linecap: round; + stroke-linejoin: round; +} + /* Chat compose - sticky at bottom */ .chat-compose { position: sticky; bottom: 0; flex-shrink: 0; display: flex; - align-items: flex-end; + flex-direction: column; gap: 12px; margin-top: auto; /* Push to bottom of flex container */ - padding: 16px 0 4px; + padding: 12px 4px 4px; background: linear-gradient(to bottom, transparent, var(--bg) 20%); z-index: 10; } +/* Image attachments preview */ +.chat-attachments { + display: inline-flex; + flex-wrap: wrap; + gap: 8px; + padding: 8px; + background: var(--panel); + border-radius: 8px; + border: 1px solid var(--border); + width: fit-content; + max-width: 100%; + align-self: flex-start; /* Don't stretch in flex column parent */ +} + +.chat-attachment { + position: relative; + width: 80px; + height: 80px; + border-radius: 6px; + overflow: hidden; + border: 1px solid var(--border); + background: var(--bg); +} + +.chat-attachment__img { + width: 100%; + height: 100%; + object-fit: contain; +} + +.chat-attachment__remove { + position: absolute; + top: 4px; + right: 4px; + width: 20px; + height: 20px; + border-radius: 50%; + border: none; + background: rgba(0, 0, 0, 0.7); + color: #fff; + font-size: 12px; + line-height: 1; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + transition: opacity 150ms ease-out; +} + +.chat-attachment:hover .chat-attachment__remove { + opacity: 1; +} + +.chat-attachment__remove:hover { + background: rgba(220, 38, 38, 0.9); +} + +.chat-attachment__remove svg { + width: 12px; + height: 12px; + stroke: currentColor; + fill: none; + stroke-width: 2px; +} + +/* Light theme attachment overrides */ +:root[data-theme="light"] .chat-attachments { + background: #f8fafc; + border-color: rgba(16, 24, 40, 0.1); +} + +:root[data-theme="light"] .chat-attachment { + border-color: rgba(16, 24, 40, 0.15); + background: #fff; +} + +:root[data-theme="light"] .chat-attachment__remove { + background: rgba(0, 0, 0, 0.6); +} + +/* Message images (sent images displayed in chat) */ +.chat-message-images { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 8px; +} + +.chat-message-image { + max-width: 300px; + max-height: 200px; + border-radius: 8px; + object-fit: contain; + cursor: pointer; + transition: transform 150ms ease-out; +} + +.chat-message-image:hover { + transform: scale(1.02); +} + +/* User message images align right */ +.chat-group.user .chat-message-images { + justify-content: flex-end; +} + +/* Compose input row - horizontal layout */ +.chat-compose__row { + display: flex; + align-items: stretch; + gap: 12px; + flex: 1; +} + +:root[data-theme="light"] .chat-compose { + background: linear-gradient(to bottom, transparent, var(--bg-content) 20%); +} + .chat-compose__field { flex: 1 1 auto; min-width: 0; + display: flex; + align-items: stretch; } /* Hide the "Message" label - keep textarea only */ @@ -114,11 +245,13 @@ /* Override .field textarea min-height (180px) from components.css */ .chat-compose .chat-compose__field textarea { width: 100%; - min-height: 36px; + height: 40px; + min-height: 40px; max-height: 150px; - padding: 8px 12px; - border-radius: 10px; - resize: vertical; + padding: 9px 12px; + border-radius: 8px; + overflow-y: auto; + resize: none; white-space: pre-wrap; font-family: var(--font-body); font-size: 14px; @@ -134,13 +267,18 @@ flex-shrink: 0; display: flex; align-items: stretch; + gap: 8px; } .chat-compose .chat-compose__actions .btn { - padding: 8px 16px; + padding: 0 16px; font-size: 13px; - min-height: 36px; + height: 40px; + min-height: 40px; + max-height: 40px; + line-height: 1; white-space: nowrap; + box-sizing: border-box; } /* Chat controls - moved to content-header area, left aligned */ @@ -194,20 +332,27 @@ /* Light theme icon button overrides */ :root[data-theme="light"] .btn--icon { - background: rgba(255, 255, 255, 0.9); - border-color: rgba(16, 24, 40, 0.2); + background: #ffffff; + border-color: var(--border); box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05); - color: rgba(16, 24, 40, 0.7); + color: var(--muted); } :root[data-theme="light"] .btn--icon:hover { - background: rgba(255, 255, 255, 1); - border-color: rgba(16, 24, 40, 0.3); - color: rgba(16, 24, 40, 0.9); + background: #ffffff; + border-color: var(--border-strong); + color: var(--text); } .btn--icon svg { display: block; + width: 18px; + height: 18px; + stroke: currentColor; + fill: none; + stroke-width: 1.5px; + stroke-linecap: round; + stroke-linejoin: round; } .chat-controls__session select { @@ -250,4 +395,3 @@ min-width: 120px; } } - diff --git a/ui/src/styles/chat/text.css b/ui/src/styles/chat/text.css index 3d01dd493..13e245de2 100644 --- a/ui/src/styles/chat/text.css +++ b/ui/src/styles/chat/text.css @@ -14,8 +14,8 @@ } :root[data-theme="light"] .chat-thinking { - border-color: rgba(16, 24, 40, 0.18); - background: rgba(16, 24, 40, 0.03); + border-color: rgba(16, 24, 40, 0.25); + background: rgba(16, 24, 40, 0.04); } .chat-text { @@ -75,9 +75,46 @@ } .chat-text :where(blockquote) { - border-left: 3px solid var(--border); + border-left: 3px solid var(--border-strong); padding-left: 12px; + margin-left: 0; color: var(--muted); + background: rgba(255, 255, 255, 0.02); + padding: 8px 12px; + border-radius: 0 var(--radius-sm) var(--radius-sm) 0; +} + +.chat-text :where(blockquote blockquote) { + margin-top: 8px; + border-left-color: var(--border-hover); + background: rgba(255, 255, 255, 0.03); +} + +.chat-text :where(blockquote blockquote blockquote) { + border-left-color: var(--muted-strong); + background: rgba(255, 255, 255, 0.04); +} + +:root[data-theme="light"] .chat-text :where(blockquote) { + background: rgba(0, 0, 0, 0.03); +} + +:root[data-theme="light"] .chat-text :where(blockquote blockquote) { + background: rgba(0, 0, 0, 0.05); +} + +:root[data-theme="light"] .chat-text :where(blockquote blockquote blockquote) { + background: rgba(0, 0, 0, 0.04); +} + +:root[data-theme="light"] .chat-text :where(:not(pre) > code) { + background: rgba(0, 0, 0, 0.08); + border: 1px solid rgba(0, 0, 0, 0.1); +} + +:root[data-theme="light"] .chat-text :where(pre) { + background: rgba(0, 0, 0, 0.05); + border: 1px solid rgba(0, 0, 0, 0.1); } .chat-text :where(hr) { @@ -85,4 +122,3 @@ border-top: 1px solid var(--border); margin: 1em 0; } - diff --git a/ui/src/styles/chat/tool-cards.css b/ui/src/styles/chat/tool-cards.css index d6998a35c..052e63dbb 100644 --- a/ui/src/styles/chat/tool-cards.css +++ b/ui/src/styles/chat/tool-cards.css @@ -4,6 +4,8 @@ border-radius: 8px; padding: 12px; margin-top: 8px; + background: var(--card); + box-shadow: inset 0 1px 0 var(--card-highlight); transition: border-color 150ms ease-out, background 150ms ease-out; /* Fixed max-height to ensure cards don't expand too much */ max-height: 120px; @@ -11,8 +13,8 @@ } .chat-tool-card:hover { - border-color: var(--accent); - background: rgba(0, 0, 0, 0.06); + border-color: var(--border-strong); + background: var(--bg-hover); } /* First tool card in a group - no top margin */ @@ -50,33 +52,63 @@ display: inline-flex; align-items: center; justify-content: center; - width: 18px; - height: 18px; - font-size: 14px; - line-height: 1; - font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif; - vertical-align: middle; + width: 16px; + height: 16px; flex-shrink: 0; } +.chat-tool-card__icon svg { + width: 14px; + height: 14px; + stroke: currentColor; + fill: none; + stroke-width: 1.5px; + stroke-linecap: round; + stroke-linejoin: round; +} + /* "View >" action link */ .chat-tool-card__action { + display: inline-flex; + align-items: center; + gap: 4px; font-size: 12px; color: var(--accent); opacity: 0.8; transition: opacity 150ms ease-out; } +.chat-tool-card__action svg { + width: 12px; + height: 12px; + stroke: currentColor; + fill: none; + stroke-width: 1.5px; + stroke-linecap: round; + stroke-linejoin: round; +} + .chat-tool-card--clickable:hover .chat-tool-card__action { opacity: 1; } /* Status indicator for completed/empty results */ .chat-tool-card__status { - font-size: 14px; + display: inline-flex; + align-items: center; color: var(--ok); } +.chat-tool-card__status svg { + width: 14px; + height: 14px; + stroke: currentColor; + fill: none; + stroke-width: 2px; + stroke-linecap: round; + stroke-linejoin: round; +} + .chat-tool-card__status-text { font-size: 11px; margin-top: 4px; @@ -94,18 +126,18 @@ color: var(--muted); margin-top: 8px; padding: 8px 10px; - background: rgba(0, 0, 0, 0.08); - border-radius: 6px; + background: var(--secondary); + border-radius: var(--radius-md); white-space: pre-wrap; overflow: hidden; max-height: 44px; line-height: 1.4; - border: 1px solid rgba(255, 255, 255, 0.04); + border: 1px solid var(--border); } .chat-tool-card--clickable:hover .chat-tool-card__preview { - background: rgba(0, 0, 0, 0.12); - border-color: rgba(255, 255, 255, 0.08); + background: var(--bg-hover); + border-color: var(--border-strong); } /* Short inline output */ @@ -114,8 +146,8 @@ color: var(--text); margin-top: 6px; padding: 6px 8px; - background: rgba(0, 0, 0, 0.06); - border-radius: 4px; + background: var(--secondary); + border-radius: var(--radius-sm); white-space: pre-wrap; word-break: break-word; } @@ -164,4 +196,3 @@ transform: scale(1); } } - diff --git a/ui/src/styles/components.css b/ui/src/styles/components.css index b46f55b6f..27dfe62d1 100644 --- a/ui/src/styles/components.css +++ b/ui/src/styles/components.css @@ -1,45 +1,75 @@ @import './chat.css'; +/* =========================================== + Cards - Refined with depth + =========================================== */ + .card { border: 1px solid var(--border); - background: linear-gradient(160deg, rgba(255, 255, 255, 0.04), transparent 65%), - var(--panel); - border-radius: 16px; - padding: 16px; - box-shadow: 0 18px 36px rgba(0, 0, 0, 0.28); - animation: rise 0.4s ease; + background: var(--card); + border-radius: var(--radius-lg); + padding: 20px; + animation: rise 0.35s var(--ease-out) backwards; + transition: + border-color var(--duration-normal) var(--ease-out), + box-shadow var(--duration-normal) var(--ease-out), + transform var(--duration-normal) var(--ease-out); + box-shadow: var(--shadow-sm), inset 0 1px 0 var(--card-highlight); +} + +.card:hover { + border-color: var(--border-strong); + box-shadow: var(--shadow-md), inset 0 1px 0 var(--card-highlight); } .card-title { - font-family: var(--font-display); - font-size: 16px; - letter-spacing: 0.6px; - text-transform: uppercase; + font-size: 15px; + font-weight: 600; + letter-spacing: -0.02em; + color: var(--text-strong); } .card-sub { color: var(--muted); - font-size: 12px; + font-size: 13px; + margin-top: 6px; + line-height: 1.5; } +/* =========================================== + Stats - Bold values, subtle labels + =========================================== */ + .stat { - background: linear-gradient(140deg, rgba(255, 255, 255, 0.04), transparent 70%), - var(--panel-strong); - border-radius: 14px; - padding: 12px; - border: 1px solid var(--border-strong); + background: var(--card); + border-radius: var(--radius-md); + padding: 14px 16px; + border: 1px solid var(--border); + transition: + border-color var(--duration-normal) var(--ease-out), + box-shadow var(--duration-normal) var(--ease-out); + box-shadow: inset 0 1px 0 var(--card-highlight); +} + +.stat:hover { + border-color: var(--border-strong); + box-shadow: var(--shadow-sm), inset 0 1px 0 var(--card-highlight); } .stat-label { color: var(--muted); font-size: 11px; + font-weight: 500; text-transform: uppercase; - letter-spacing: 1px; + letter-spacing: 0.04em; } .stat-value { - font-size: 18px; + font-size: 24px; + font-weight: 700; margin-top: 6px; + letter-spacing: -0.03em; + line-height: 1.1; } .stat-value.ok { @@ -57,9 +87,13 @@ .note-title { font-weight: 600; - letter-spacing: 0.2px; + letter-spacing: -0.01em; } +/* =========================================== + Status List + =========================================== */ + .status-list { display: grid; gap: 8px; @@ -69,8 +103,8 @@ display: flex; justify-content: space-between; gap: 12px; - padding: 6px 0; - border-bottom: 1px dashed rgba(255, 255, 255, 0.06); + padding: 8px 0; + border-bottom: 1px solid var(--border); } .status-list div:last-child { @@ -78,25 +112,28 @@ } .account-count { - margin-top: 8px; + margin-top: 10px; font-size: 12px; - font-weight: 600; - letter-spacing: 0.4px; + font-weight: 500; color: var(--muted); } .account-card-list { margin-top: 16px; display: grid; - gap: 10px; + gap: 12px; } .account-card { border: 1px solid var(--border); - border-radius: 10px; + border-radius: var(--radius-md); padding: 12px; - background: linear-gradient(160deg, rgba(255, 255, 255, 0.06), transparent), - rgba(255, 255, 255, 0.03); + background: var(--bg-elevated); + transition: border-color var(--duration-fast) ease; +} + +.account-card:hover { + border-color: var(--border-strong); } .account-card-header { @@ -107,7 +144,7 @@ } .account-card-title { - font-weight: 600; + font-weight: 500; } .account-card-id { @@ -117,7 +154,7 @@ } .account-card-status { - margin-top: 8px; + margin-top: 10px; font-size: 13px; } @@ -126,33 +163,52 @@ } .account-card-error { - margin-top: 6px; + margin-top: 8px; color: var(--danger); font-size: 12px; } +/* =========================================== + Labels & Pills + =========================================== */ + .label { color: var(--muted); - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.9px; + font-size: 12px; + font-weight: 500; } .pill { display: inline-flex; align-items: center; - gap: 8px; - border: 1px solid var(--border-strong); + gap: 6px; + border: 1px solid var(--border); padding: 6px 12px; - border-radius: 999px; - background: linear-gradient(160deg, rgba(255, 255, 255, 0.06), transparent), - var(--panel); + border-radius: var(--radius-full); + background: var(--secondary); + font-size: 13px; + font-weight: 500; + transition: border-color var(--duration-fast) ease; } +.pill:hover { + border-color: var(--border-strong); +} + +.pill.danger { + border-color: var(--danger-subtle); + background: var(--danger-subtle); + color: var(--danger); +} + +/* =========================================== + Theme Toggle + =========================================== */ + .theme-toggle { --theme-item: 28px; - --theme-gap: 6px; - --theme-pad: 6px; + --theme-gap: 2px; + --theme-pad: 4px; position: relative; } @@ -162,9 +218,9 @@ grid-template-columns: repeat(3, var(--theme-item)); gap: var(--theme-gap); padding: var(--theme-pad); - border-radius: 999px; - border: 1px solid var(--border-strong); - background: rgba(255, 255, 255, 0.04); + border-radius: var(--radius-full); + border: 1px solid var(--border); + background: var(--secondary); } .theme-toggle__indicator { @@ -173,15 +229,11 @@ left: var(--theme-pad); width: var(--theme-item); height: var(--theme-item); - border-radius: 999px; + border-radius: var(--radius-full); transform: translateY(-50%) translateX(calc(var(--theme-index, 0) * (var(--theme-item) + var(--theme-gap)))); - background: linear-gradient(160deg, rgba(255, 255, 255, 0.12), transparent), - var(--panel-strong); - border: 1px solid var(--border-strong); - box-shadow: 0 8px 16px rgba(0, 0, 0, 0.25); - transition: transform 180ms ease-out, background 180ms ease-out, - box-shadow 180ms ease-out; + background: var(--accent); + transition: transform var(--duration-normal) var(--ease-out); z-index: 0; } @@ -191,92 +243,176 @@ display: grid; place-items: center; border: 0; - border-radius: 999px; + border-radius: var(--radius-full); background: transparent; color: var(--muted); cursor: pointer; position: relative; z-index: 1; - transition: color 150ms ease-out, background 150ms ease-out; + transition: color var(--duration-fast) ease; } .theme-toggle__button:hover { color: var(--text); - background: rgba(255, 255, 255, 0.08); } .theme-toggle__button.active { - color: var(--text); + color: var(--accent-foreground); +} + +.theme-toggle__button.active .theme-icon { + stroke: var(--accent-foreground); } .theme-icon { - width: 16px; - height: 16px; + width: 14px; + height: 14px; stroke: currentColor; fill: none; - stroke-width: 1.75px; + stroke-width: 1.5px; stroke-linecap: round; stroke-linejoin: round; } -.pill.danger { - border-color: rgba(255, 92, 92, 0.5); - color: var(--danger); -} +/* =========================================== + Status Dot - With glow for emphasis + =========================================== */ .statusDot { width: 8px; height: 8px; - border-radius: 999px; + border-radius: var(--radius-full); background: var(--danger); - box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.25); + box-shadow: 0 0 8px rgba(239, 68, 68, 0.5); + animation: pulse-subtle 2s ease-in-out infinite; } .statusDot.ok { background: var(--ok); - box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.25), 0 0 10px rgba(43, 217, 127, 0.4); + box-shadow: 0 0 8px rgba(34, 197, 94, 0.5); + animation: none; } +/* =========================================== + Buttons - Tactile with personality + =========================================== */ + .btn { - border: 1px solid var(--border-strong); - background: rgba(255, 255, 255, 0.04); - padding: 8px 14px; - border-radius: 999px; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + border: 1px solid var(--border); + background: var(--bg-elevated); + padding: 9px 16px; + border-radius: var(--radius-md); + font-size: 13px; + font-weight: 500; + letter-spacing: -0.01em; cursor: pointer; - transition: transform 150ms ease, border-color 150ms ease, background 150ms ease; + transition: + border-color var(--duration-fast) var(--ease-out), + background var(--duration-fast) var(--ease-out), + box-shadow var(--duration-fast) var(--ease-out), + transform var(--duration-fast) var(--ease-out); } .btn:hover { - background: rgba(255, 255, 255, 0.1); + background: var(--bg-hover); + border-color: var(--border-strong); transform: translateY(-1px); + box-shadow: var(--shadow-sm); +} + +.btn:active { + background: var(--secondary); + transform: translateY(0); + box-shadow: none; +} + +.btn svg { + width: 16px; + height: 16px; + stroke: currentColor; + fill: none; + stroke-width: 1.5px; + stroke-linecap: round; + stroke-linejoin: round; + flex-shrink: 0; } .btn.primary { - border-color: rgba(245, 159, 74, 0.45); - background: rgba(245, 159, 74, 0.2); + border-color: var(--accent); + background: var(--accent); + color: var(--primary-foreground); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); +} + +.btn.primary:hover { + background: var(--accent-hover); + border-color: var(--accent-hover); + box-shadow: var(--shadow-md), 0 0 20px var(--accent-glow); +} + +/* Keyboard shortcut badge (shadcn style) */ +.btn-kbd { + display: inline-flex; + align-items: center; + justify-content: center; + margin-left: 6px; + padding: 2px 5px; + font-family: var(--mono); + font-size: 11px; + font-weight: 500; + line-height: 1; + border-radius: 4px; + background: rgba(255, 255, 255, 0.15); + color: inherit; + opacity: 0.8; +} + +.btn.primary .btn-kbd { + background: rgba(255, 255, 255, 0.2); +} + +:root[data-theme="light"] .btn-kbd { + background: rgba(0, 0, 0, 0.08); +} + +:root[data-theme="light"] .btn.primary .btn-kbd { + background: rgba(255, 255, 255, 0.25); } .btn.active { - border-color: rgba(245, 159, 74, 0.55); - background: rgba(245, 159, 74, 0.16); + border-color: var(--accent); + background: var(--accent-subtle); + color: var(--accent); } .btn.danger { - border-color: rgba(255, 107, 107, 0.45); - background: rgba(255, 107, 107, 0.18); + border-color: transparent; + background: var(--danger-subtle); + color: var(--danger); +} + +.btn.danger:hover { + background: rgba(239, 68, 68, 0.15); } .btn--sm { - padding: 5px 10px; + padding: 6px 10px; font-size: 12px; } .btn:disabled { opacity: 0.5; cursor: not-allowed; - transform: none; } +/* =========================================== + Form Fields + =========================================== */ + .field { display: grid; gap: 6px; @@ -288,56 +424,46 @@ .field span { color: var(--muted); - font-size: 11px; - letter-spacing: 0.4px; + font-size: 13px; + font-weight: 500; } .field input, .field textarea, .field select { - border: 1px solid var(--border-strong); - background: rgba(0, 0, 0, 0.22); - border-radius: 12px; - padding: 9px 11px; + border: 1px solid var(--input); + background: var(--card); + border-radius: var(--radius-md); + padding: 8px 12px; outline: none; - transition: border-color 150ms ease, box-shadow 150ms ease, - background 150ms ease; + box-shadow: inset 0 1px 0 var(--card-highlight); + transition: + border-color var(--duration-fast) ease, + box-shadow var(--duration-fast) ease; } .field input:focus, .field textarea:focus, .field select:focus { - border-color: var(--accent); - box-shadow: 0 0 0 3px var(--focus); - background: rgba(0, 0, 0, 0.28); + border-color: var(--ring); + box-shadow: var(--focus-ring); } .field select { appearance: none; - padding-right: 38px; - background-color: var(--panel-strong); - background-image: - linear-gradient(45deg, transparent 50%, var(--muted) 50%), - linear-gradient(135deg, var(--muted) 50%, transparent 50%), - linear-gradient(to right, transparent, transparent); - background-position: - calc(100% - 18px) 50%, - calc(100% - 12px) 50%, - calc(100% - 38px) 50%; - background-size: 6px 6px, 6px 6px, 1px 60%; + padding-right: 36px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23a1a1aa' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); background-repeat: no-repeat; - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04); + background-position: right 10px center; + cursor: pointer; } .field textarea { font-family: var(--mono); - min-height: 180px; + min-height: 160px; resize: vertical; white-space: pre; -} - -.field textarea:focus { - background: rgba(0, 0, 0, 0.32); + line-height: 1.5; } .field.checkbox { @@ -352,6 +478,9 @@ .config-form .field.checkbox input[type="checkbox"] { margin: 0; + width: 16px; + height: 16px; + accent-color: var(--accent); } .form-grid { @@ -363,36 +492,27 @@ :root[data-theme="light"] .field input, :root[data-theme="light"] .field textarea, :root[data-theme="light"] .field select { - background: rgba(255, 255, 255, 1); - border-color: rgba(16, 24, 40, 0.25); - box-shadow: 0 1px 2px rgba(16, 24, 40, 0.06); + background: var(--card); + border-color: var(--input); } -:root[data-theme="light"] .field input:focus, -:root[data-theme="light"] .field textarea:focus, -:root[data-theme="light"] .field select:focus { - background: #ffffff; -} - -/* Light theme button overrides */ :root[data-theme="light"] .btn { - background: rgba(255, 255, 255, 0.9); - border-color: rgba(16, 24, 40, 0.2); - box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05); + background: var(--bg); + border-color: var(--input); } :root[data-theme="light"] .btn:hover { - background: rgba(255, 255, 255, 1); - border-color: rgba(16, 24, 40, 0.3); + background: var(--bg-hover); } :root[data-theme="light"] .btn.primary { - background: rgba(245, 159, 74, 0.15); + background: var(--accent); + border-color: var(--accent); } -:root[data-theme="light"] .btn.active { - background: rgba(245, 159, 74, 0.12); -} +/* =========================================== + Utilities + =========================================== */ .muted { color: var(--muted); @@ -402,34 +522,44 @@ font-family: var(--mono); } +/* =========================================== + Callouts - Informative with subtle depth + =========================================== */ + .callout { - padding: 10px 12px; - border-radius: 14px; - background: linear-gradient(160deg, rgba(255, 255, 255, 0.06), transparent), - rgba(255, 255, 255, 0.03); + padding: 14px 16px; + border-radius: var(--radius-md); + background: var(--secondary); border: 1px solid var(--border); + font-size: 13px; + line-height: 1.5; + position: relative; } .callout.danger { - border-color: rgba(255, 92, 92, 0.4); + border-color: rgba(239, 68, 68, 0.25); + background: linear-gradient(135deg, rgba(239, 68, 68, 0.08) 0%, rgba(239, 68, 68, 0.04) 100%); color: var(--danger); } .callout.info { - border-color: rgba(92, 156, 255, 0.4); - color: var(--accent); + border-color: rgba(59, 130, 246, 0.25); + background: linear-gradient(135deg, rgba(59, 130, 246, 0.08) 0%, rgba(59, 130, 246, 0.04) 100%); + color: var(--info); } .callout.success { - border-color: rgba(92, 255, 128, 0.4); - color: var(--positive, #5cff80); + border-color: rgba(34, 197, 94, 0.25); + background: linear-gradient(135deg, rgba(34, 197, 94, 0.08) 0%, rgba(34, 197, 94, 0.04) 100%); + color: var(--ok); } +/* Compaction indicator */ .compaction-indicator { font-size: 13px; - padding: 8px 12px; + padding: 10px 12px; margin-bottom: 8px; - animation: compaction-fade-in 0.2s ease-out; + animation: fade-in 0.2s var(--ease-out); } .compaction-indicator--active { @@ -437,18 +567,7 @@ } .compaction-indicator--complete { - animation: compaction-fade-in 0.2s ease-out; -} - -@keyframes compaction-fade-in { - from { - opacity: 0; - transform: translateY(-4px); - } - to { - opacity: 1; - transform: translateY(0); - } + animation: fade-in 0.2s var(--ease-out); } @keyframes compaction-pulse { @@ -460,44 +579,54 @@ } } +/* =========================================== + Code Blocks + =========================================== */ + .code-block { font-family: var(--mono); - font-size: 12px; - background: rgba(0, 0, 0, 0.35); - padding: 10px; - border-radius: 12px; + font-size: 13px; + line-height: 1.5; + background: var(--secondary); + padding: 12px; + border-radius: var(--radius-md); border: 1px solid var(--border); max-height: 360px; overflow: auto; + max-width: 100%; } :root[data-theme="light"] .code-block, :root[data-theme="light"] .list-item, :root[data-theme="light"] .table-row, :root[data-theme="light"] .chip { - background: rgba(255, 255, 255, 0.85); + background: var(--bg); } +/* =========================================== + Lists + =========================================== */ + .list { display: grid; - gap: 12px; + gap: 8px; container-type: inline-size; } .list-item { display: grid; - grid-template-columns: minmax(0, 1fr) minmax(220px, 260px); - gap: 14px; + grid-template-columns: minmax(0, 1fr) minmax(200px, 260px); + gap: 16px; align-items: start; border: 1px solid var(--border); - border-radius: 14px; + border-radius: var(--radius-md); padding: 12px; - background: rgba(0, 0, 0, 0.2); + background: var(--card); + transition: border-color var(--duration-fast) ease; } .list-item-clickable { cursor: pointer; - transition: border-color 0.15s ease, box-shadow 0.15s ease; } .list-item-clickable:hover { @@ -506,17 +635,17 @@ .list-item-selected { border-color: var(--accent); - box-shadow: 0 0 0 1px var(--focus); + box-shadow: var(--focus-ring); } .list-main { display: grid; - gap: 6px; + gap: 4px; min-width: 0; } .list-title { - font-weight: 600; + font-weight: 500; } .list-sub { @@ -527,10 +656,10 @@ .list-meta { text-align: right; color: var(--muted); - font-size: 11px; + font-size: 12px; display: grid; gap: 4px; - min-width: 220px; + min-width: 200px; } .list-meta .btn { @@ -554,19 +683,33 @@ } } +/* =========================================== + Chips - Compact and punchy + =========================================== */ + .chip-row { display: flex; flex-wrap: wrap; - gap: 6px; + gap: 8px; } .chip { - font-size: 11px; + font-size: 12px; + font-weight: 500; border: 1px solid var(--border); - border-radius: 999px; - padding: 4px 8px; + border-radius: var(--radius-full); + padding: 5px 12px; color: var(--muted); - background: rgba(0, 0, 0, 0.2); + background: var(--secondary); + transition: + border-color var(--duration-fast) var(--ease-out), + background var(--duration-fast) var(--ease-out), + transform var(--duration-fast) var(--ease-out); +} + +.chip:hover { + border-color: var(--border-strong); + transform: translateY(-1px); } .chip input { @@ -575,17 +718,23 @@ .chip-ok { color: var(--ok); - border-color: rgba(27, 217, 138, 0.4); + border-color: rgba(34, 197, 94, 0.3); + background: var(--ok-subtle); } .chip-warn { color: var(--warn); - border-color: rgba(242, 201, 76, 0.4); + border-color: rgba(245, 158, 11, 0.3); + background: var(--warn-subtle); } +/* =========================================== + Tables + =========================================== */ + .table { display: grid; - gap: 8px; + gap: 6px; } .table-head, @@ -597,33 +746,43 @@ } .table-head { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.8px; + font-size: 12px; + font-weight: 500; color: var(--muted); + padding: 0 12px; } .table-row { border: 1px solid var(--border); - padding: 10px; - border-radius: 12px; - background: rgba(0, 0, 0, 0.2); + padding: 10px 12px; + border-radius: var(--radius-md); + background: var(--card); + transition: border-color var(--duration-fast) ease; +} + +.table-row:hover { + border-color: var(--border-strong); } .session-link { text-decoration: none; color: var(--accent); + font-weight: 500; } .session-link:hover { text-decoration: underline; } +/* =========================================== + Log Stream + =========================================== */ + .log-stream { border: 1px solid var(--border); - border-radius: 14px; - background: rgba(0, 0, 0, 0.2); - max-height: 520px; + border-radius: var(--radius-md); + background: var(--card); + max-height: 500px; overflow: auto; container-type: inline-size; } @@ -633,9 +792,14 @@ grid-template-columns: 90px 70px minmax(140px, 200px) minmax(0, 1fr); gap: 12px; align-items: start; - padding: 6px 10px; + padding: 8px 12px; border-bottom: 1px solid var(--border); font-size: 12px; + transition: background var(--duration-fast) ease; +} + +.log-row:hover { + background: var(--bg-hover); } .log-row:last-child { @@ -644,14 +808,14 @@ .log-time { color: var(--muted); + font-family: var(--mono); } .log-level { - text-transform: uppercase; - font-size: 10px; - font-weight: 600; + font-size: 11px; + font-weight: 500; border: 1px solid var(--border); - border-radius: 999px; + border-radius: var(--radius-sm); padding: 2px 6px; width: fit-content; } @@ -663,18 +827,18 @@ .log-level.info { color: var(--info); - border-color: rgba(76, 150, 242, 0.4); + border-color: rgba(59, 130, 246, 0.3); } .log-level.warn { color: var(--warn); - border-color: rgba(242, 201, 76, 0.4); + border-color: var(--warn-subtle); } .log-level.error, .log-level.fatal { color: var(--danger); - border-color: rgba(255, 92, 92, 0.4); + border-color: var(--danger-subtle); } .log-chip.trace, @@ -684,27 +848,29 @@ .log-chip.info { color: var(--info); - border-color: rgba(76, 150, 242, 0.4); + border-color: rgba(59, 130, 246, 0.3); } .log-chip.warn { color: var(--warn); - border-color: rgba(242, 201, 76, 0.4); + border-color: var(--warn-subtle); } .log-chip.error, .log-chip.fatal { color: var(--danger); - border-color: rgba(255, 92, 92, 0.4); + border-color: var(--danger-subtle); } .log-subsystem { color: var(--muted); + font-family: var(--mono); } .log-message { white-space: pre-wrap; word-break: break-word; + font-family: var(--mono); } @container (max-width: 620px) { @@ -717,6 +883,10 @@ } } +/* =========================================== + Chat + =========================================== */ + .chat { display: flex; flex-direction: column; @@ -731,7 +901,7 @@ display: flex; justify-content: space-between; align-items: flex-end; - gap: 12px; + gap: 16px; flex-wrap: wrap; } @@ -746,7 +916,7 @@ .chat-header__right { display: flex; align-items: center; - gap: 10px; + gap: 8px; } .chat-session { @@ -754,42 +924,36 @@ } .chat-thread { - margin-top: 12px; + margin-top: 16px; display: flex; flex-direction: column; gap: 12px; flex: 1; - min-height: 0; /* Allow flex shrinking for scroll behavior */ - overflow-y: auto; /* Enable scrolling */ + min-height: 0; + overflow-y: auto; overflow-x: hidden; - padding: 14px 12px; + padding: 16px 12px; min-width: 0; border-radius: 0; border: none; background: transparent; } -:root[data-theme="light"] .chat-thread { - background: transparent; -} - +/* Chat queue */ .chat-queue { margin-top: 12px; - padding: 10px 12px; - border-radius: 16px; + padding: 12px; + border-radius: var(--radius-lg); border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.18); + background: var(--card); display: grid; gap: 8px; } -:root[data-theme="light"] .chat-queue { - background: rgba(16, 24, 40, 0.04); -} - .chat-queue__title { - font-family: var(--font-mono); + font-family: var(--mono); font-size: 12px; + font-weight: 500; color: var(--muted); } @@ -802,21 +966,17 @@ display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; - gap: 10px; - padding: 8px 10px; - border-radius: 12px; - border: 1px dashed var(--border); - background: rgba(0, 0, 0, 0.2); -} - -:root[data-theme="light"] .chat-queue__item { - background: rgba(16, 24, 40, 0.05); + gap: 12px; + padding: 10px 12px; + border-radius: var(--radius-md); + border: 1px dashed var(--border-strong); + background: var(--secondary); } .chat-queue__text { color: var(--chat-text); font-size: 13px; - line-height: 1.4; + line-height: 1.45; white-space: pre-wrap; overflow: hidden; display: -webkit-box; @@ -831,6 +991,7 @@ line-height: 1; } +/* Chat lines */ .chat-line { display: flex; } @@ -847,128 +1008,119 @@ .chat-msg { display: grid; gap: 6px; - max-width: min(720px, 82%); + max-width: min(700px, 82%); } .chat-line.user .chat-msg { justify-items: end; } +/* Chat bubbles */ .chat-bubble { - border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.24); - border-radius: 16px; - padding: 10px 12px; + border: 1px solid transparent; + background: var(--card); + border-radius: var(--radius-lg); + padding: 10px 14px; min-width: 0; - box-shadow: 0 12px 22px rgba(0, 0, 0, 0.24); } :root[data-theme="light"] .chat-bubble { - background: rgba(255, 255, 255, 0.85); - box-shadow: 0 12px 26px rgba(16, 24, 40, 0.08); + border-color: var(--border); + background: var(--bg); } .chat-line.user .chat-bubble { - border-color: rgba(245, 159, 74, 0.45); - background: linear-gradient( - 135deg, - rgba(245, 159, 74, 0.26) 0%, - rgba(245, 159, 74, 0.12) 100% - ); + border-color: transparent; + background: var(--accent-subtle); +} + +:root[data-theme="light"] .chat-line.user .chat-bubble { + border-color: rgba(234, 88, 12, 0.2); + background: rgba(251, 146, 60, 0.12); } .chat-line.assistant .chat-bubble { - border-color: rgba(52, 199, 183, 0.2); - background: linear-gradient( - 135deg, - rgba(52, 199, 183, 0.12) 0%, - rgba(0, 0, 0, 0.24) 100% - ); + border-color: transparent; + background: var(--secondary); } :root[data-theme="light"] .chat-line.assistant .chat-bubble { - background: linear-gradient( - 135deg, - rgba(27, 185, 177, 0.12) 0%, - rgba(255, 255, 255, 0.85) 100% - ); + border-color: var(--border); + background: var(--bg-muted); } @keyframes chatStreamPulse { - 0% { - box-shadow: 0 12px 22px rgba(0, 0, 0, 0.24), 0 0 0 0 rgba(52, 199, 183, 0); + 0%, 100% { + border-color: var(--border); } - 60% { - box-shadow: 0 12px 22px rgba(0, 0, 0, 0.24), 0 0 0 6px rgba(52, 199, 183, 0.08); - } - 100% { - box-shadow: 0 12px 22px rgba(0, 0, 0, 0.24), 0 0 0 0 rgba(52, 199, 183, 0); + 50% { + border-color: var(--accent); } } .chat-bubble.streaming { - border-color: rgba(52, 199, 183, 0.4); - animation: chatStreamPulse 1.6s ease-in-out infinite; + animation: chatStreamPulse 1.5s ease-in-out infinite; } @media (prefers-reduced-motion: reduce) { .chat-bubble.streaming { animation: none; + border-color: var(--accent); } } +/* Reading indicator */ .chat-bubble.chat-reading-indicator { width: fit-content; - padding: 10px 14px; + padding: 10px 16px; } .chat-reading-indicator__dots { display: inline-flex; align-items: center; - gap: 6px; - height: 10px; + gap: 4px; + height: 12px; } .chat-reading-indicator__dots > span { display: inline-block; width: 6px; height: 6px; - border-radius: 999px; - background: var(--chat-text); - opacity: 0.55; + border-radius: var(--radius-full); + background: var(--muted); + opacity: 0.6; transform: translateY(0); - animation: chatReadingDot 1.1s ease-in-out infinite; + animation: chatReadingDot 1.2s ease-in-out infinite; will-change: transform, opacity; } .chat-reading-indicator__dots > span:nth-child(2) { - animation-delay: 0.12s; + animation-delay: 0.15s; } .chat-reading-indicator__dots > span:nth-child(3) { - animation-delay: 0.24s; + animation-delay: 0.3s; } @keyframes chatReadingDot { - 0%, - 80%, - 100% { - opacity: 0.38; - transform: translateY(0) scale(0.92); + 0%, 80%, 100% { + opacity: 0.4; + transform: translateY(0); } 40% { opacity: 1; - transform: translateY(-3px) scale(1.18); + transform: translateY(-3px); } } @media (prefers-reduced-motion: reduce) { .chat-reading-indicator__dots > span { animation: none; - opacity: 0.75; + opacity: 0.6; } } +/* Chat text */ .chat-text { overflow-wrap: anywhere; word-break: break-word; @@ -985,7 +1137,7 @@ } .chat-text :where(ul, ol) { - padding-left: 1.1em; + padding-left: 1.2em; } .chat-text :where(li + li) { @@ -994,58 +1146,51 @@ .chat-text :where(a) { color: var(--accent); - text-decoration-thickness: 2px; - text-underline-offset: 2px; } .chat-text :where(a:hover) { - text-decoration-thickness: 3px; + text-decoration: underline; } .chat-text :where(blockquote) { - border-left: 2px solid rgba(255, 255, 255, 0.14); + border-left: 2px solid var(--border-strong); padding-left: 12px; color: var(--muted); } -:root[data-theme="light"] .chat-text :where(blockquote) { - border-left-color: rgba(16, 24, 40, 0.16); -} - .chat-text :where(hr) { border: 0; border-top: 1px solid var(--border); - opacity: 0.6; - margin: 0.9em 0; + margin: 1em 0; } .chat-text :where(code) { - font-family: var(--font-mono); - font-size: 0.92em; + font-family: var(--mono); + font-size: 0.9em; } .chat-text :where(:not(pre) > code) { padding: 0.15em 0.35em; - border-radius: 8px; + border-radius: var(--radius-sm); border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.2); + background: var(--secondary); } :root[data-theme="light"] .chat-text :where(:not(pre) > code) { - background: rgba(16, 24, 40, 0.05); + background: var(--bg-muted); } .chat-text :where(pre) { margin-top: 0.75em; padding: 10px 12px; - border-radius: 14px; + border-radius: var(--radius-md); border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.22); + background: var(--secondary); overflow: auto; } :root[data-theme="light"] .chat-text :where(pre) { - background: rgba(16, 24, 40, 0.04); + background: var(--bg-muted); } .chat-text :where(pre code) { @@ -1057,43 +1202,46 @@ margin-top: 0.75em; border-collapse: collapse; width: 100%; - font-size: 12px; + font-size: 13px; } .chat-text :where(th, td) { border: 1px solid var(--border); - padding: 6px 8px; + padding: 6px 10px; vertical-align: top; } .chat-text :where(th) { - font-family: var(--font-mono); - font-weight: 600; + font-family: var(--mono); + font-weight: 500; color: var(--muted); + background: var(--secondary); } +/* Tool cards */ .chat-tool-card { margin-top: 8px; - padding: 8px 10px; - border-radius: 12px; + padding: 10px 12px; + border-radius: var(--radius-md); border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.22); + background: var(--secondary); display: grid; gap: 4px; } :root[data-theme="light"] .chat-tool-card { - background: rgba(255, 255, 255, 0.7); + background: var(--bg-muted); } .chat-tool-card__title { - font-family: var(--font-mono); + font-family: var(--mono); font-size: 12px; - color: var(--chat-text); + font-weight: 500; + color: var(--text); } .chat-tool-card__detail { - font-family: var(--font-mono); + font-family: var(--mono); font-size: 11px; color: var(--muted); } @@ -1103,7 +1251,7 @@ } .chat-tool-card__summary { - font-family: var(--font-mono); + font-family: var(--mono); font-size: 11px; color: var(--muted); cursor: pointer; @@ -1119,28 +1267,28 @@ .chat-tool-card__summary-meta { color: var(--muted); - opacity: 0.8; + opacity: 0.7; } .chat-tool-card__details[open] .chat-tool-card__summary { - color: var(--chat-text); + color: var(--text); } .chat-tool-card__output { - margin-top: 6px; - font-family: var(--font-mono); + margin-top: 8px; + font-family: var(--mono); font-size: 11px; - line-height: 1.45; + line-height: 1.5; white-space: pre-wrap; color: var(--chat-text); - padding: 8px; - border-radius: 10px; + padding: 8px 10px; + border-radius: var(--radius-md); border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.2); + background: var(--card); } :root[data-theme="light"] .chat-tool-card__output { - background: rgba(16, 24, 40, 0.05); + background: var(--bg); } .chat-stamp { @@ -1152,11 +1300,11 @@ text-align: right; } +/* Chat compose */ .chat-compose { margin-top: 12px; - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - align-items: end; + display: flex; + flex-direction: column; gap: 10px; } @@ -1166,14 +1314,14 @@ z-index: 5; margin-top: 0; padding-top: 12px; - background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, var(--panel) 35%); + background: linear-gradient(180deg, transparent 0%, var(--bg) 40%); } .shell--chat-focus .chat-compose { bottom: calc(var(--shell-pad) + 8px); - padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px)); - border-bottom-left-radius: 18px; - border-bottom-right-radius: 18px; + padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px)); + border-bottom-left-radius: var(--radius-lg); + border-bottom-right-radius: var(--radius-lg); } .chat-compose__field { @@ -1182,16 +1330,27 @@ .chat-compose__field textarea { min-height: 72px; - padding: 10px 12px; - border-radius: 16px; + padding: 10px 14px; + border-radius: var(--radius-lg); resize: vertical; white-space: pre-wrap; font-family: var(--font-body); - line-height: 1.45; + line-height: 1.5; + border: 1px solid var(--input); + background: var(--card); + box-shadow: inset 0 1px 0 var(--card-highlight); + transition: + border-color var(--duration-fast) ease, + box-shadow var(--duration-fast) ease; +} + +.chat-compose__field textarea:focus { + border-color: var(--ring); + box-shadow: var(--focus-ring); } .chat-compose__field textarea:disabled { - opacity: 0.7; + opacity: 0.5; cursor: not-allowed; } @@ -1202,7 +1361,7 @@ @media (max-width: 900px) { .chat-session { - min-width: 200px; + min-width: 180px; } .chat-compose { @@ -1210,27 +1369,35 @@ } } +/* =========================================== + QR Code + =========================================== */ + .qr-wrap { - margin-top: 12px; - border-radius: 14px; - background: rgba(0, 0, 0, 0.2); - border: 1px dashed rgba(255, 255, 255, 0.18); - padding: 12px; + margin-top: 16px; + border-radius: var(--radius-md); + background: var(--card); + border: 1px dashed var(--border-strong); + padding: 16px; display: inline-flex; } .qr-wrap img { - width: 180px; - height: 180px; - border-radius: 10px; + width: 160px; + height: 160px; + border-radius: var(--radius-sm); image-rendering: pixelated; } +/* =========================================== + Exec Approval Modal + =========================================== */ + .exec-approval-overlay { position: fixed; inset: 0; - background: rgba(8, 12, 18, 0.7); - backdrop-filter: blur(6px); + background: rgba(0, 0, 0, 0.8); + backdrop-filter: blur(4px); display: flex; align-items: center; justify-content: center; @@ -1239,59 +1406,58 @@ } .exec-approval-card { - width: min(560px, 100%); - background: var(--panel-strong); - border: 1px solid var(--border-strong); - border-radius: 18px; + width: min(540px, 100%); + background: var(--card); + border: 1px solid var(--border); + border-radius: var(--radius-lg); padding: 20px; - box-shadow: 0 28px 60px rgba(0, 0, 0, 0.35); - animation: rise 0.25s ease; + animation: scale-in 0.2s var(--ease-out); } .exec-approval-header { display: flex; align-items: center; justify-content: space-between; - gap: 12px; + gap: 16px; } .exec-approval-title { - font-family: var(--font-display); font-size: 14px; - letter-spacing: 0.8px; - text-transform: uppercase; + font-weight: 600; } .exec-approval-sub { color: var(--muted); - font-size: 12px; + font-size: 13px; + margin-top: 4px; } .exec-approval-queue { font-size: 11px; - text-transform: uppercase; - letter-spacing: 1px; + font-weight: 500; color: var(--muted); border: 1px solid var(--border); - border-radius: 999px; + border-radius: var(--radius-full); padding: 4px 10px; } .exec-approval-command { margin-top: 12px; padding: 10px 12px; - background: rgba(0, 0, 0, 0.25); + background: var(--secondary); border: 1px solid var(--border); - border-radius: 12px; + border-radius: var(--radius-md); word-break: break-word; white-space: pre-wrap; + font-family: var(--mono); + font-size: 13px; } .exec-approval-meta { margin-top: 12px; display: grid; gap: 6px; - font-size: 12px; + font-size: 13px; color: var(--muted); } @@ -1308,7 +1474,7 @@ .exec-approval-error { margin-top: 10px; - font-size: 12px; + font-size: 13px; color: var(--danger); } @@ -1316,5 +1482,5 @@ margin-top: 16px; display: flex; flex-wrap: wrap; - gap: 10px; + gap: 8px; } diff --git a/ui/src/styles/config.css b/ui/src/styles/config.css index aa41505ae..7d96ac13f 100644 --- a/ui/src/styles/config.css +++ b/ui/src/styles/config.css @@ -1,15 +1,15 @@ /* =========================================== - Config Page - Modern Layout + Config Page - Carbon Design System =========================================== */ /* Layout Container */ .config-layout { display: grid; - grid-template-columns: 240px minmax(0, 1fr); + grid-template-columns: 260px minmax(0, 1fr); gap: 0; - min-height: calc(100vh - 140px); + height: calc(100vh - 160px); margin: -16px; - border-radius: 16px; + border-radius: var(--radius-xl); overflow: hidden; border: 1px solid var(--border); background: var(--panel); @@ -18,47 +18,50 @@ /* =========================================== Sidebar =========================================== */ + .config-sidebar { display: flex; flex-direction: column; - background: rgba(0, 0, 0, 0.2); + background: var(--bg-accent); border-right: 1px solid var(--border); + min-height: 0; + overflow: hidden; } :root[data-theme="light"] .config-sidebar { - background: rgba(0, 0, 0, 0.03); + background: var(--bg-hover); } .config-sidebar__header { display: flex; align-items: center; justify-content: space-between; - padding: 16px; + padding: 18px 18px; border-bottom: 1px solid var(--border); } .config-sidebar__title { font-weight: 600; font-size: 14px; - letter-spacing: 0.3px; + letter-spacing: -0.01em; } .config-sidebar__footer { margin-top: auto; - padding: 12px; + padding: 14px; border-top: 1px solid var(--border); } /* Search */ .config-search { position: relative; - padding: 12px; + padding: 14px; border-bottom: 1px solid var(--border); } .config-search__icon { position: absolute; - left: 24px; + left: 28px; top: 50%; transform: translateY(-50%); width: 16px; @@ -69,13 +72,16 @@ .config-search__input { width: 100%; - padding: 10px 32px 10px 40px; + padding: 11px 36px 11px 42px; border: 1px solid var(--border); - border-radius: 8px; - background: rgba(0, 0, 0, 0.15); + border-radius: var(--radius-md); + background: var(--bg-elevated); font-size: 13px; outline: none; - transition: border-color 150ms ease, box-shadow 150ms ease, background 150ms ease; + transition: + border-color var(--duration-fast) ease, + box-shadow var(--duration-fast) ease, + background var(--duration-fast) ease; } .config-search__input::placeholder { @@ -84,40 +90,42 @@ .config-search__input:focus { border-color: var(--accent); - box-shadow: 0 0 0 3px var(--focus); - background: rgba(0, 0, 0, 0.2); + box-shadow: var(--focus-ring); + background: var(--bg-hover); } :root[data-theme="light"] .config-search__input { - background: rgba(255, 255, 255, 0.8); + background: white; } :root[data-theme="light"] .config-search__input:focus { - background: #fff; + background: white; } .config-search__clear { position: absolute; - right: 20px; + right: 22px; top: 50%; transform: translateY(-50%); - width: 20px; - height: 20px; + width: 22px; + height: 22px; border: none; - border-radius: 50%; - background: rgba(255, 255, 255, 0.1); + border-radius: var(--radius-full); + background: var(--bg-hover); color: var(--muted); - font-size: 16px; + font-size: 14px; line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center; - transition: background 150ms ease, color 150ms ease; + transition: + background var(--duration-fast) ease, + color var(--duration-fast) ease; } .config-search__clear:hover { - background: rgba(255, 255, 255, 0.2); + background: var(--border-strong); color: var(--text); } @@ -125,7 +133,7 @@ .config-nav { flex: 1; overflow-y: auto; - padding: 8px; + padding: 10px; } .config-nav__item { @@ -133,29 +141,31 @@ align-items: center; gap: 12px; width: 100%; - padding: 10px 12px; + padding: 11px 14px; border: none; - border-radius: 8px; + border-radius: var(--radius-md); background: transparent; color: var(--muted); font-size: 13px; font-weight: 500; text-align: left; cursor: pointer; - transition: background 150ms ease, color 150ms ease; + transition: + background var(--duration-fast) ease, + color var(--duration-fast) ease; } .config-nav__item:hover { - background: rgba(255, 255, 255, 0.05); + background: var(--bg-hover); color: var(--text); } :root[data-theme="light"] .config-nav__item:hover { - background: rgba(0, 0, 0, 0.05); + background: rgba(0, 0, 0, 0.04); } .config-nav__item.active { - background: rgba(245, 159, 74, 0.12); + background: var(--accent-subtle); color: var(--accent); } @@ -165,7 +175,13 @@ display: flex; align-items: center; justify-content: center; - font-size: 14px; + font-size: 15px; + opacity: 0.7; +} + +.config-nav__item:hover .config-nav__icon, +.config-nav__item.active .config-nav__icon { + opacity: 1; } .config-nav__icon svg { @@ -185,27 +201,30 @@ /* Mode Toggle */ .config-mode-toggle { display: flex; - padding: 3px; - background: rgba(0, 0, 0, 0.2); - border-radius: 8px; + padding: 4px; + background: var(--bg-elevated); + border-radius: var(--radius-md); border: 1px solid var(--border); } :root[data-theme="light"] .config-mode-toggle { - background: rgba(0, 0, 0, 0.06); + background: white; } .config-mode-toggle__btn { flex: 1; - padding: 8px 12px; + padding: 9px 14px; border: none; - border-radius: 6px; + border-radius: var(--radius-sm); background: transparent; color: var(--muted); font-size: 12px; font-weight: 600; cursor: pointer; - transition: background 150ms ease, color 150ms ease, box-shadow 150ms ease; + transition: + background var(--duration-fast) ease, + color var(--duration-fast) ease, + box-shadow var(--duration-fast) ease; } .config-mode-toggle__btn:hover { @@ -213,24 +232,22 @@ } .config-mode-toggle__btn.active { - background: rgba(255, 255, 255, 0.1); - color: var(--text); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); -} - -:root[data-theme="light"] .config-mode-toggle__btn.active { - background: #fff; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + background: var(--accent); + color: white; + box-shadow: var(--shadow-sm); } /* =========================================== Main Content =========================================== */ + .config-main { display: flex; flex-direction: column; + min-height: 0; min-width: 0; background: var(--panel); + overflow: hidden; } /* Actions Bar */ @@ -238,28 +255,28 @@ display: flex; align-items: center; justify-content: space-between; - gap: 12px; - padding: 12px 20px; - background: rgba(0, 0, 0, 0.08); + gap: 14px; + padding: 14px 22px; + background: var(--bg-accent); border-bottom: 1px solid var(--border); } :root[data-theme="light"] .config-actions { - background: rgba(0, 0, 0, 0.02); + background: var(--bg-hover); } .config-actions__left, .config-actions__right { display: flex; align-items: center; - gap: 8px; + gap: 10px; } .config-changes-badge { - padding: 5px 12px; - border-radius: 999px; - background: rgba(245, 159, 74, 0.15); - border: 1px solid rgba(245, 159, 74, 0.3); + padding: 6px 14px; + border-radius: var(--radius-full); + background: var(--accent-subtle); + border: 1px solid rgba(255, 77, 77, 0.3); color: var(--accent); font-size: 12px; font-weight: 600; @@ -272,10 +289,10 @@ /* Diff Panel */ .config-diff { - margin: 16px 20px 0; - border: 1px solid rgba(245, 159, 74, 0.3); - border-radius: 10px; - background: rgba(245, 159, 74, 0.05); + margin: 18px 22px 0; + border: 1px solid rgba(255, 77, 77, 0.25); + border-radius: var(--radius-lg); + background: var(--accent-subtle); overflow: hidden; } @@ -283,7 +300,7 @@ display: flex; align-items: center; justify-content: space-between; - padding: 12px 16px; + padding: 14px 18px; cursor: pointer; font-size: 13px; font-weight: 600; @@ -298,7 +315,7 @@ .config-diff__chevron { width: 16px; height: 16px; - transition: transform 200ms ease; + transition: transform var(--duration-normal) var(--ease-out); } .config-diff__chevron svg { @@ -311,24 +328,24 @@ } .config-diff__content { - padding: 0 16px 16px; + padding: 0 18px 18px; display: grid; - gap: 8px; + gap: 10px; } .config-diff__item { display: flex; align-items: baseline; - gap: 12px; - padding: 8px 12px; - border-radius: 6px; - background: rgba(0, 0, 0, 0.1); + gap: 14px; + padding: 10px 14px; + border-radius: var(--radius-md); + background: var(--bg-elevated); font-size: 12px; font-family: var(--mono); } :root[data-theme="light"] .config-diff__item { - background: rgba(255, 255, 255, 0.6); + background: white; } .config-diff__path { @@ -340,14 +357,14 @@ .config-diff__values { display: flex; align-items: baseline; - gap: 8px; + gap: 10px; min-width: 0; flex-wrap: wrap; } .config-diff__from { color: var(--danger); - opacity: 0.8; + opacity: 0.85; } .config-diff__arrow { @@ -362,19 +379,19 @@ .config-section-hero { display: flex; align-items: center; - gap: 14px; - padding: 14px 20px; + gap: 16px; + padding: 16px 22px; border-bottom: 1px solid var(--border); - background: rgba(0, 0, 0, 0.04); + background: var(--bg-accent); } :root[data-theme="light"] .config-section-hero { - background: rgba(0, 0, 0, 0.015); + background: var(--bg-hover); } .config-section-hero__icon { - width: 28px; - height: 28px; + width: 30px; + height: 30px; color: var(--accent); display: flex; align-items: center; @@ -390,17 +407,21 @@ .config-section-hero__text { display: grid; - gap: 2px; + gap: 3px; min-width: 0; } .config-section-hero__title { - font-size: 15px; + font-size: 16px; font-weight: 600; + letter-spacing: -0.01em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .config-section-hero__desc { - font-size: 12px; + font-size: 13px; color: var(--muted); } @@ -408,60 +429,59 @@ .config-subnav { display: flex; gap: 8px; - padding: 10px 20px 12px; + padding: 12px 22px 14px; border-bottom: 1px solid var(--border); - background: rgba(0, 0, 0, 0.03); + background: var(--bg-accent); overflow-x: auto; } :root[data-theme="light"] .config-subnav { - background: rgba(0, 0, 0, 0.02); + background: var(--bg-hover); } .config-subnav__item { border: 1px solid transparent; - border-radius: 999px; - padding: 6px 12px; + border-radius: var(--radius-full); + padding: 7px 14px; font-size: 12px; font-weight: 600; color: var(--muted); - background: rgba(0, 0, 0, 0.12); + background: var(--bg-elevated); cursor: pointer; - transition: background 150ms ease, color 150ms ease, border-color 150ms ease; + transition: + background var(--duration-fast) ease, + color var(--duration-fast) ease, + border-color var(--duration-fast) ease; white-space: nowrap; } :root[data-theme="light"] .config-subnav__item { - background: rgba(0, 0, 0, 0.06); + background: white; } .config-subnav__item:hover { color: var(--text); - background: rgba(255, 255, 255, 0.08); -} - -:root[data-theme="light"] .config-subnav__item:hover { - background: rgba(0, 0, 0, 0.08); + border-color: var(--border); } .config-subnav__item.active { color: var(--accent); - border-color: rgba(245, 159, 74, 0.4); - background: rgba(245, 159, 74, 0.12); + border-color: rgba(255, 77, 77, 0.4); + background: var(--accent-subtle); } /* Content Area */ .config-content { flex: 1; overflow-y: auto; - padding: 20px; + padding: 22px; } .config-raw-field textarea { min-height: 500px; font-family: var(--mono); font-size: 13px; - line-height: 1.5; + line-height: 1.55; } /* Loading State */ @@ -470,22 +490,24 @@ flex-direction: column; align-items: center; justify-content: center; - gap: 16px; - padding: 80px 20px; + gap: 18px; + padding: 80px 24px; color: var(--muted); } .config-loading__spinner { - width: 36px; - height: 36px; + width: 40px; + height: 40px; border: 3px solid var(--border); border-top-color: var(--accent); - border-radius: 50%; - animation: spin 0.8s linear infinite; + border-radius: var(--radius-full); + animation: spin 0.75s linear infinite; } @keyframes spin { - to { transform: rotate(360deg); } + to { + transform: rotate(360deg); + } } /* Empty State */ @@ -494,14 +516,14 @@ flex-direction: column; align-items: center; justify-content: center; - gap: 16px; - padding: 80px 20px; + gap: 18px; + padding: 80px 24px; text-align: center; } .config-empty__icon { font-size: 56px; - opacity: 0.4; + opacity: 0.35; } .config-empty__text { @@ -512,38 +534,44 @@ /* =========================================== Section Cards =========================================== */ + .config-form--modern { display: grid; - gap: 24px; + gap: 26px; } .config-section-card { border: 1px solid var(--border); - border-radius: 12px; - background: rgba(255, 255, 255, 0.02); + border-radius: var(--radius-lg); + background: var(--bg-elevated); overflow: hidden; + transition: border-color var(--duration-fast) ease; +} + +.config-section-card:hover { + border-color: var(--border-strong); } :root[data-theme="light"] .config-section-card { - background: rgba(255, 255, 255, 0.5); + background: white; } .config-section-card__header { display: flex; align-items: flex-start; - gap: 14px; - padding: 18px 20px; - background: rgba(0, 0, 0, 0.06); + gap: 16px; + padding: 20px 22px; + background: var(--bg-accent); border-bottom: 1px solid var(--border); } :root[data-theme="light"] .config-section-card__header { - background: rgba(0, 0, 0, 0.02); + background: var(--bg-hover); } .config-section-card__icon { - width: 32px; - height: 32px; + width: 34px; + height: 34px; color: var(--accent); flex-shrink: 0; } @@ -562,37 +590,42 @@ margin: 0; font-size: 17px; font-weight: 600; + letter-spacing: -0.01em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .config-section-card__desc { - margin: 4px 0 0; + margin: 5px 0 0; font-size: 13px; color: var(--muted); - line-height: 1.4; + line-height: 1.45; } .config-section-card__content { - padding: 20px; + padding: 22px; } /* =========================================== Form Fields =========================================== */ + .cfg-fields { display: grid; - gap: 20px; + gap: 22px; } .cfg-field { display: grid; - gap: 6px; + gap: 8px; } .cfg-field--error { - padding: 12px; - border-radius: 8px; - background: rgba(255, 92, 92, 0.1); - border: 1px solid rgba(255, 92, 92, 0.3); + padding: 14px; + border-radius: var(--radius-md); + background: var(--danger-subtle); + border: 1px solid rgba(239, 68, 68, 0.3); } .cfg-field__label { @@ -604,7 +637,7 @@ .cfg-field__help { font-size: 12px; color: var(--muted); - line-height: 1.4; + line-height: 1.45; } .cfg-field__error { @@ -615,18 +648,21 @@ /* Text Input */ .cfg-input-wrap { display: flex; - gap: 8px; + gap: 10px; } .cfg-input { flex: 1; - padding: 10px 12px; - border: 1px solid var(--border); - border-radius: 8px; - background: rgba(0, 0, 0, 0.12); + padding: 11px 14px; + border: 1px solid var(--border-strong); + border-radius: var(--radius-md); + background: var(--bg-accent); font-size: 14px; outline: none; - transition: border-color 150ms ease, box-shadow 150ms ease, background 150ms ease; + transition: + border-color var(--duration-fast) ease, + box-shadow var(--duration-fast) ease, + background var(--duration-fast) ease; } .cfg-input::placeholder { @@ -636,36 +672,38 @@ .cfg-input:focus { border-color: var(--accent); - box-shadow: 0 0 0 3px var(--focus); - background: rgba(0, 0, 0, 0.18); + box-shadow: var(--focus-ring); + background: var(--bg-hover); } :root[data-theme="light"] .cfg-input { - background: #fff; + background: white; } :root[data-theme="light"] .cfg-input:focus { - background: #fff; + background: white; } .cfg-input--sm { - padding: 8px 10px; + padding: 9px 12px; font-size: 13px; } .cfg-input__reset { - padding: 8px 12px; + padding: 10px 14px; border: 1px solid var(--border); - border-radius: 8px; - background: rgba(255, 255, 255, 0.05); + border-radius: var(--radius-md); + background: var(--bg-elevated); color: var(--muted); font-size: 14px; cursor: pointer; - transition: background 150ms ease, color 150ms ease; + transition: + background var(--duration-fast) ease, + color var(--duration-fast) ease; } .cfg-input__reset:hover:not(:disabled) { - background: rgba(255, 255, 255, 0.1); + background: var(--bg-hover); color: var(--text); } @@ -677,58 +715,60 @@ /* Textarea */ .cfg-textarea { width: 100%; - padding: 10px 12px; - border: 1px solid var(--border); - border-radius: 8px; - background: rgba(0, 0, 0, 0.12); + padding: 12px 14px; + border: 1px solid var(--border-strong); + border-radius: var(--radius-md); + background: var(--bg-accent); font-family: var(--mono); font-size: 13px; - line-height: 1.5; + line-height: 1.55; resize: vertical; outline: none; - transition: border-color 150ms ease, box-shadow 150ms ease; + transition: + border-color var(--duration-fast) ease, + box-shadow var(--duration-fast) ease; } .cfg-textarea:focus { border-color: var(--accent); - box-shadow: 0 0 0 3px var(--focus); + box-shadow: var(--focus-ring); } :root[data-theme="light"] .cfg-textarea { - background: #fff; + background: white; } .cfg-textarea--sm { - padding: 8px 10px; + padding: 10px 12px; font-size: 12px; } /* Number Input */ .cfg-number { display: inline-flex; - border: 1px solid var(--border); - border-radius: 8px; + border: 1px solid var(--border-strong); + border-radius: var(--radius-md); overflow: hidden; - background: rgba(0, 0, 0, 0.12); + background: var(--bg-accent); } :root[data-theme="light"] .cfg-number { - background: #fff; + background: white; } .cfg-number__btn { - width: 40px; + width: 44px; border: none; - background: rgba(255, 255, 255, 0.05); + background: var(--bg-elevated); color: var(--text); font-size: 18px; font-weight: 300; cursor: pointer; - transition: background 150ms ease; + transition: background var(--duration-fast) ease; } .cfg-number__btn:hover:not(:disabled) { - background: rgba(255, 255, 255, 0.1); + background: var(--bg-hover); } .cfg-number__btn:disabled { @@ -737,16 +777,16 @@ } :root[data-theme="light"] .cfg-number__btn { - background: rgba(0, 0, 0, 0.03); + background: var(--bg-hover); } :root[data-theme="light"] .cfg-number__btn:hover:not(:disabled) { - background: rgba(0, 0, 0, 0.06); + background: var(--border); } .cfg-number__input { - width: 80px; - padding: 10px; + width: 85px; + padding: 11px; border: none; border-left: 1px solid var(--border); border-right: 1px solid var(--border); @@ -765,52 +805,57 @@ /* Select */ .cfg-select { - padding: 10px 36px 10px 12px; - border: 1px solid var(--border); - border-radius: 8px; - background-color: rgba(0, 0, 0, 0.12); + padding: 11px 40px 11px 14px; + border: 1px solid var(--border-strong); + border-radius: var(--radius-md); + background-color: var(--bg-accent); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); background-repeat: no-repeat; - background-position: right 10px center; + background-position: right 12px center; font-size: 14px; cursor: pointer; outline: none; appearance: none; - transition: border-color 150ms ease, box-shadow 150ms ease; + transition: + border-color var(--duration-fast) ease, + box-shadow var(--duration-fast) ease; } .cfg-select:focus { border-color: var(--accent); - box-shadow: 0 0 0 3px var(--focus); + box-shadow: var(--focus-ring); } :root[data-theme="light"] .cfg-select { - background-color: #fff; + background-color: white; } /* Segmented Control */ .cfg-segmented { display: inline-flex; - padding: 3px; + padding: 4px; border: 1px solid var(--border); - border-radius: 8px; - background: rgba(0, 0, 0, 0.12); + border-radius: var(--radius-md); + background: var(--bg-accent); } :root[data-theme="light"] .cfg-segmented { - background: rgba(0, 0, 0, 0.04); + background: var(--bg-hover); } .cfg-segmented__btn { - padding: 8px 16px; + padding: 9px 18px; border: none; - border-radius: 6px; + border-radius: var(--radius-sm); background: transparent; color: var(--muted); font-size: 13px; font-weight: 500; cursor: pointer; - transition: background 150ms ease, color 150ms ease, box-shadow 150ms ease; + transition: + background var(--duration-fast) ease, + color var(--duration-fast) ease, + box-shadow var(--duration-fast) ease; } .cfg-segmented__btn:hover:not(:disabled):not(.active) { @@ -818,14 +863,9 @@ } .cfg-segmented__btn.active { - background: rgba(255, 255, 255, 0.12); - color: var(--text); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); -} - -:root[data-theme="light"] .cfg-segmented__btn.active { - background: #fff; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + background: var(--accent); + color: white; + box-shadow: var(--shadow-sm); } .cfg-segmented__btn:disabled { @@ -838,31 +878,33 @@ display: flex; align-items: center; justify-content: space-between; - gap: 16px; - padding: 14px 16px; + gap: 18px; + padding: 16px 18px; border: 1px solid var(--border); - border-radius: 10px; - background: rgba(0, 0, 0, 0.06); + border-radius: var(--radius-lg); + background: var(--bg-accent); cursor: pointer; - transition: background 150ms ease, border-color 150ms ease; + transition: + background var(--duration-fast) ease, + border-color var(--duration-fast) ease; } .cfg-toggle-row:hover:not(.disabled) { - background: rgba(0, 0, 0, 0.1); + background: var(--bg-hover); border-color: var(--border-strong); } .cfg-toggle-row.disabled { - opacity: 0.6; + opacity: 0.55; cursor: not-allowed; } :root[data-theme="light"] .cfg-toggle-row { - background: rgba(255, 255, 255, 0.5); + background: white; } :root[data-theme="light"] .cfg-toggle-row:hover:not(.disabled) { - background: rgba(255, 255, 255, 0.8); + background: var(--bg-hover); } .cfg-toggle-row__content { @@ -879,10 +921,10 @@ .cfg-toggle-row__help { display: block; - margin-top: 2px; + margin-top: 3px; font-size: 12px; color: var(--muted); - line-height: 1.4; + line-height: 1.45; } /* Toggle Switch */ @@ -900,17 +942,19 @@ .cfg-toggle__track { display: block; - width: 48px; + width: 50px; height: 28px; - background: rgba(255, 255, 255, 0.12); - border: 1px solid var(--border); - border-radius: 999px; + background: var(--bg-elevated); + border: 1px solid var(--border-strong); + border-radius: var(--radius-full); position: relative; - transition: background 200ms ease, border-color 200ms ease; + transition: + background var(--duration-normal) ease, + border-color var(--duration-normal) ease; } :root[data-theme="light"] .cfg-toggle__track { - background: rgba(0, 0, 0, 0.1); + background: var(--border); } .cfg-toggle__track::after { @@ -921,53 +965,51 @@ width: 20px; height: 20px; background: var(--text); - border-radius: 50%; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); - transition: transform 200ms ease, background 200ms ease; + border-radius: var(--radius-full); + box-shadow: var(--shadow-sm); + transition: + transform var(--duration-normal) var(--ease-out), + background var(--duration-normal) ease; } .cfg-toggle input:checked + .cfg-toggle__track { - background: rgba(43, 217, 127, 0.25); - border-color: rgba(43, 217, 127, 0.5); + background: var(--ok-subtle); + border-color: rgba(34, 197, 94, 0.4); } .cfg-toggle input:checked + .cfg-toggle__track::after { - transform: translateX(20px); + transform: translateX(22px); background: var(--ok); } .cfg-toggle input:focus + .cfg-toggle__track { - box-shadow: 0 0 0 3px var(--focus); + box-shadow: var(--focus-ring); } /* Object (collapsible) */ .cfg-object { border: 1px solid var(--border); - border-radius: 10px; - background: rgba(0, 0, 0, 0.04); + border-radius: var(--radius-lg); + background: var(--bg-accent); overflow: hidden; } :root[data-theme="light"] .cfg-object { - background: rgba(255, 255, 255, 0.4); + background: white; } .cfg-object__header { display: flex; align-items: center; justify-content: space-between; - padding: 12px 16px; + padding: 14px 18px; cursor: pointer; list-style: none; - transition: background 150ms ease; + transition: background var(--duration-fast) ease; } .cfg-object__header:hover { - background: rgba(255, 255, 255, 0.03); -} - -:root[data-theme="light"] .cfg-object__header:hover { - background: rgba(0, 0, 0, 0.02); + background: var(--bg-hover); } .cfg-object__header::-webkit-details-marker { @@ -984,7 +1026,7 @@ width: 18px; height: 18px; color: var(--muted); - transition: transform 200ms ease; + transition: transform var(--duration-normal) var(--ease-out); } .cfg-object__chevron svg { @@ -997,36 +1039,36 @@ } .cfg-object__help { - padding: 0 16px 12px; + padding: 0 18px 14px; font-size: 12px; color: var(--muted); border-bottom: 1px solid var(--border); } .cfg-object__content { - padding: 16px; + padding: 18px; display: grid; - gap: 16px; + gap: 18px; } /* Array */ .cfg-array { border: 1px solid var(--border); - border-radius: 10px; + border-radius: var(--radius-lg); overflow: hidden; } .cfg-array__header { display: flex; align-items: center; - gap: 12px; - padding: 12px 16px; - background: rgba(0, 0, 0, 0.06); + gap: 14px; + padding: 14px 18px; + background: var(--bg-accent); border-bottom: 1px solid var(--border); } :root[data-theme="light"] .cfg-array__header { - background: rgba(0, 0, 0, 0.02); + background: var(--bg-hover); } .cfg-array__label { @@ -1039,32 +1081,32 @@ .cfg-array__count { font-size: 12px; color: var(--muted); - padding: 3px 8px; - background: rgba(255, 255, 255, 0.06); - border-radius: 999px; + padding: 4px 10px; + background: var(--bg-elevated); + border-radius: var(--radius-full); } :root[data-theme="light"] .cfg-array__count { - background: rgba(0, 0, 0, 0.06); + background: white; } .cfg-array__add { display: inline-flex; align-items: center; gap: 6px; - padding: 6px 12px; + padding: 7px 14px; border: 1px solid var(--border); - border-radius: 6px; - background: rgba(255, 255, 255, 0.05); + border-radius: var(--radius-md); + background: var(--bg-elevated); color: var(--text); font-size: 12px; font-weight: 500; cursor: pointer; - transition: background 150ms ease; + transition: background var(--duration-fast) ease; } .cfg-array__add:hover:not(:disabled) { - background: rgba(255, 255, 255, 0.1); + background: var(--bg-hover); } .cfg-array__add:disabled { @@ -1083,14 +1125,14 @@ } .cfg-array__help { - padding: 10px 16px; + padding: 12px 18px; font-size: 12px; color: var(--muted); border-bottom: 1px solid var(--border); } .cfg-array__empty { - padding: 32px 16px; + padding: 36px 18px; text-align: center; color: var(--muted); font-size: 13px; @@ -1110,13 +1152,13 @@ display: flex; align-items: center; justify-content: space-between; - padding: 10px 16px; - background: rgba(0, 0, 0, 0.04); + padding: 12px 18px; + background: var(--bg-accent); border-bottom: 1px solid var(--border); } :root[data-theme="light"] .cfg-array__item-header { - background: rgba(0, 0, 0, 0.02); + background: var(--bg-hover); } .cfg-array__item-index { @@ -1124,21 +1166,23 @@ font-weight: 600; color: var(--muted); text-transform: uppercase; - letter-spacing: 0.5px; + letter-spacing: 0.05em; } .cfg-array__item-remove { - width: 28px; - height: 28px; + width: 30px; + height: 30px; display: flex; align-items: center; justify-content: center; border: none; - border-radius: 6px; + border-radius: var(--radius-md); background: transparent; color: var(--muted); cursor: pointer; - transition: background 150ms ease, color 150ms ease; + transition: + background var(--duration-fast) ease, + color var(--duration-fast) ease; } .cfg-array__item-remove svg { @@ -1147,7 +1191,7 @@ } .cfg-array__item-remove:hover:not(:disabled) { - background: rgba(255, 92, 92, 0.15); + background: var(--danger-subtle); color: var(--danger); } @@ -1157,13 +1201,13 @@ } .cfg-array__item-content { - padding: 16px; + padding: 18px; } /* Map (custom entries) */ .cfg-map { border: 1px solid var(--border); - border-radius: 10px; + border-radius: var(--radius-lg); overflow: hidden; } @@ -1171,14 +1215,14 @@ display: flex; align-items: center; justify-content: space-between; - gap: 12px; - padding: 12px 16px; - background: rgba(0, 0, 0, 0.06); + gap: 14px; + padding: 14px 18px; + background: var(--bg-accent); border-bottom: 1px solid var(--border); } :root[data-theme="light"] .cfg-map__header { - background: rgba(0, 0, 0, 0.02); + background: var(--bg-hover); } .cfg-map__label { @@ -1191,19 +1235,19 @@ display: inline-flex; align-items: center; gap: 6px; - padding: 6px 12px; + padding: 7px 14px; border: 1px solid var(--border); - border-radius: 6px; - background: rgba(255, 255, 255, 0.05); + border-radius: var(--radius-md); + background: var(--bg-elevated); color: var(--text); font-size: 12px; font-weight: 500; cursor: pointer; - transition: background 150ms ease; + transition: background var(--duration-fast) ease; } .cfg-map__add:hover:not(:disabled) { - background: rgba(255, 255, 255, 0.1); + background: var(--bg-hover); } .cfg-map__add-icon { @@ -1217,7 +1261,7 @@ } .cfg-map__empty { - padding: 24px 16px; + padding: 28px 18px; text-align: center; color: var(--muted); font-size: 13px; @@ -1225,14 +1269,14 @@ .cfg-map__items { display: grid; - gap: 8px; - padding: 12px; + gap: 10px; + padding: 14px; } .cfg-map__item { display: grid; - grid-template-columns: 140px 1fr auto; - gap: 8px; + grid-template-columns: 150px 1fr auto; + gap: 10px; align-items: start; } @@ -1245,17 +1289,19 @@ } .cfg-map__item-remove { - width: 32px; - height: 32px; + width: 34px; + height: 34px; display: flex; align-items: center; justify-content: center; border: none; - border-radius: 6px; + border-radius: var(--radius-md); background: transparent; color: var(--muted); cursor: pointer; - transition: background 150ms ease, color 150ms ease; + transition: + background var(--duration-fast) ease, + color var(--duration-fast) ease; } .cfg-map__item-remove svg { @@ -1264,29 +1310,30 @@ } .cfg-map__item-remove:hover:not(:disabled) { - background: rgba(255, 92, 92, 0.15); + background: var(--danger-subtle); color: var(--danger); } /* Pill variants */ .pill--sm { - padding: 4px 10px; + padding: 5px 12px; font-size: 11px; } .pill--ok { - border-color: rgba(43, 217, 127, 0.4); + border-color: rgba(34, 197, 94, 0.35); color: var(--ok); } .pill--danger { - border-color: rgba(255, 92, 92, 0.4); + border-color: rgba(239, 68, 68, 0.35); color: var(--danger); } /* =========================================== Mobile Responsiveness =========================================== */ + @media (max-width: 768px) { .config-layout { grid-template-columns: 1fr; @@ -1298,21 +1345,21 @@ } .config-sidebar__header { - padding: 12px 16px; + padding: 14px 16px; } .config-nav { display: flex; flex-wrap: nowrap; - gap: 4px; - padding: 8px 12px; + gap: 6px; + padding: 10px 14px; overflow-x: auto; -webkit-overflow-scrolling: touch; } .config-nav__item { flex: 0 0 auto; - padding: 8px 12px; + padding: 9px 14px; white-space: nowrap; } @@ -1326,7 +1373,7 @@ .config-actions { flex-wrap: wrap; - padding: 12px 16px; + padding: 14px 16px; } .config-actions__left, @@ -1336,32 +1383,32 @@ } .config-section-hero { - padding: 12px 16px; - } - - .config-subnav { - padding: 8px 16px 10px; - } - - .config-content { - padding: 16px; - } - - .config-section-card__header { padding: 14px 16px; } + .config-subnav { + padding: 10px 16px 12px; + } + + .config-content { + padding: 18px; + } + + .config-section-card__header { + padding: 16px 18px; + } + .config-section-card__content { - padding: 16px; + padding: 18px; } .cfg-toggle-row { - padding: 12px 14px; + padding: 14px 16px; } .cfg-map__item { grid-template-columns: 1fr; - gap: 8px; + gap: 10px; } .cfg-map__item-remove { @@ -1371,9 +1418,9 @@ @media (max-width: 480px) { .config-nav__icon { - width: 24px; - height: 24px; - font-size: 16px; + width: 26px; + height: 26px; + font-size: 17px; } .config-nav__label { @@ -1381,12 +1428,12 @@ } .config-section-card__icon { - width: 28px; - height: 28px; + width: 30px; + height: 30px; } .config-section-card__title { - font-size: 15px; + font-size: 16px; } .cfg-segmented { @@ -1395,6 +1442,6 @@ .cfg-segmented__btn { flex: 1 0 auto; - min-width: 60px; + min-width: 70px; } } diff --git a/ui/src/styles/layout.css b/ui/src/styles/layout.css index 7f2161449..c2a5c6fe3 100644 --- a/ui/src/styles/layout.css +++ b/ui/src/styles/layout.css @@ -1,10 +1,14 @@ +/* =========================================== + Shell Layout + =========================================== */ + .shell { --shell-pad: 16px; --shell-gap: 16px; --shell-nav-width: 220px; --shell-topbar-height: 56px; - --shell-focus-duration: 220ms; - --shell-focus-ease: cubic-bezier(0.2, 0.85, 0.25, 1); + --shell-focus-duration: 200ms; + --shell-focus-ease: var(--ease-out); height: 100vh; display: grid; grid-template-columns: var(--shell-nav-width) minmax(0, 1fr); @@ -13,7 +17,7 @@ "topbar topbar" "nav content"; gap: 0; - animation: dashboard-enter 0.6s ease-out; + animation: dashboard-enter 0.4s var(--ease-out); transition: grid-template-columns var(--shell-focus-duration) var(--shell-focus-ease); overflow: hidden; } @@ -61,6 +65,10 @@ gap: 0; } +/* =========================================== + Topbar + =========================================== */ + .topbar { grid-area: topbar; position: sticky; @@ -73,8 +81,7 @@ padding: 0 20px; height: var(--shell-topbar-height); border-bottom: 1px solid var(--border); - background: var(--panel); - backdrop-filter: blur(18px); + background: var(--bg); } .topbar-left { @@ -84,49 +91,84 @@ } .topbar .nav-collapse-toggle { - width: 44px; - height: 44px; + width: 36px; + height: 36px; margin-bottom: 0; } .topbar .nav-collapse-toggle__icon { - font-size: 22px; + width: 20px; + height: 20px; } +.topbar .nav-collapse-toggle__icon svg { + width: 20px; + height: 20px; +} + +/* Brand */ .brand { + display: flex; + align-items: center; + gap: 10px; +} + +.brand-logo { + width: 28px; + height: 28px; + flex-shrink: 0; +} + +.brand-logo img { + width: 100%; + height: 100%; + object-fit: contain; +} + +.brand-text { display: flex; flex-direction: column; - gap: 2px; + gap: 1px; } .brand-title { - font-family: var(--font-display); font-size: 16px; - letter-spacing: 1px; - text-transform: uppercase; - font-weight: 600; + font-weight: 700; + letter-spacing: -0.03em; line-height: 1.1; + color: var(--text-strong); } .brand-sub { font-size: 10px; + font-weight: 500; color: var(--muted); - letter-spacing: 0.8px; + letter-spacing: 0.05em; text-transform: uppercase; line-height: 1; } +/* Topbar status */ .topbar-status { display: flex; align-items: center; gap: 8px; } -/* Smaller pill and theme toggle in topbar */ .topbar-status .pill { - padding: 4px 10px; + padding: 6px 10px; gap: 6px; - font-size: 11px; + font-size: 12px; + font-weight: 500; + height: 32px; + box-sizing: border-box; +} + +.topbar-status .pill .mono { + display: flex; + align-items: center; + line-height: 1; + margin-top: 0px; } .topbar-status .statusDot { @@ -135,9 +177,9 @@ } .topbar-status .theme-toggle { - --theme-item: 22px; - --theme-gap: 4px; - --theme-pad: 4px; + --theme-item: 24px; + --theme-gap: 2px; + --theme-pad: 3px; } .topbar-status .theme-icon { @@ -145,17 +187,26 @@ height: 12px; } +/* =========================================== + Navigation Sidebar + =========================================== */ + .nav { grid-area: nav; overflow-y: auto; overflow-x: hidden; - padding: 16px; - border-right: 1px solid var(--border); - background: var(--panel); - backdrop-filter: blur(18px); - transition: width var(--shell-focus-duration) var(--shell-focus-ease), - padding var(--shell-focus-duration) var(--shell-focus-ease); - min-height: 0; /* Allow grid item to shrink and enable scrolling */ + padding: 16px 12px; + background: var(--bg); + scrollbar-width: none; /* Firefox */ + transition: + width var(--shell-focus-duration) var(--shell-focus-ease), + padding var(--shell-focus-duration) var(--shell-focus-ease), + opacity var(--shell-focus-duration) var(--shell-focus-ease); + min-height: 0; +} + +.nav::-webkit-scrollbar { + display: none; /* Chrome/Safari */ } .shell--chat-focus .nav { @@ -164,9 +215,9 @@ border-width: 0; overflow: hidden; pointer-events: none; + opacity: 0; } -/* Collapsed nav sidebar - completely hidden */ .nav--collapsed { width: 0; min-width: 0; @@ -177,7 +228,7 @@ pointer-events: none; } -/* Nav collapse toggle button */ +/* Nav collapse toggle */ .nav-collapse-toggle { width: 32px; height: 32px; @@ -186,71 +237,88 @@ justify-content: center; background: transparent; border: 1px solid transparent; - border-radius: 6px; + border-radius: var(--radius-md); cursor: pointer; - transition: background 150ms ease, border-color 150ms ease; + transition: + background var(--duration-fast) ease, + border-color var(--duration-fast) ease; margin-bottom: 16px; } .nav-collapse-toggle:hover { - background: rgba(255, 255, 255, 0.08); + background: var(--bg-hover); border-color: var(--border); } -:root[data-theme="light"] .nav-collapse-toggle:hover { - background: rgba(0, 0, 0, 0.06); -} - .nav-collapse-toggle__icon { - font-size: 16px; + display: flex; + align-items: center; + justify-content: center; + width: 18px; + height: 18px; color: var(--muted); + transition: color var(--duration-fast) ease; } +.nav-collapse-toggle__icon svg { + width: 18px; + height: 18px; + stroke: currentColor; + fill: none; + stroke-width: 1.5px; + stroke-linecap: round; + stroke-linejoin: round; +} + +.nav-collapse-toggle:hover .nav-collapse-toggle__icon { + color: var(--text); +} + +/* Nav groups */ .nav-group { - margin-bottom: 18px; + margin-bottom: 20px; display: grid; - gap: 6px; - padding-bottom: 12px; - border-bottom: 1px dashed rgba(255, 255, 255, 0.08); + gap: 2px; } .nav-group:last-child { margin-bottom: 0; - padding-bottom: 0; - border-bottom: none; } .nav-group__items { display: grid; - gap: 4px; + gap: 1px; } .nav-group--collapsed .nav-group__items { display: none; } +/* Nav label */ .nav-label { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; - padding: 4px 0; + padding: 6px 10px; font-size: 11px; font-weight: 500; - text-transform: uppercase; - letter-spacing: 1.4px; - color: var(--text); - opacity: 0.7; + color: var(--muted); margin-bottom: 4px; background: transparent; border: none; cursor: pointer; text-align: left; + border-radius: var(--radius-sm); + transition: + color var(--duration-fast) ease, + background var(--duration-fast) ease; } .nav-label:hover { - opacity: 1; + color: var(--text); + background: var(--bg-hover); } .nav-label--static { @@ -258,7 +326,8 @@ } .nav-label--static:hover { - opacity: 0.7; + color: var(--muted); + background: transparent; } .nav-label__text { @@ -266,131 +335,153 @@ } .nav-label__chevron { - font-size: 12px; - opacity: 0.6; + font-size: 10px; + opacity: 0.5; + transition: transform var(--duration-fast) ease; } +.nav-group--collapsed .nav-label__chevron { + transform: rotate(-90deg); +} + +/* Nav items */ .nav-item { position: relative; display: flex; align-items: center; justify-content: flex-start; - gap: 8px; - padding: 10px 12px 10px 14px; - border-radius: 12px; + gap: 10px; + padding: 8px 10px; + border-radius: var(--radius-md); border: 1px solid transparent; background: transparent; color: var(--muted); cursor: pointer; text-decoration: none; - transition: border-color 160ms ease, background 160ms ease, color 160ms ease; + transition: + border-color var(--duration-fast) ease, + background var(--duration-fast) ease, + color var(--duration-fast) ease; } .nav-item__icon { - font-size: 16px; - width: 18px; - height: 18px; + width: 16px; + height: 16px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; + opacity: 0.7; + transition: opacity var(--duration-fast) ease; +} + +.nav-item__icon svg { + width: 16px; + height: 16px; + stroke: currentColor; + fill: none; + stroke-width: 1.5px; + stroke-linecap: round; + stroke-linejoin: round; } .nav-item__text { font-size: 13px; + font-weight: 500; white-space: nowrap; } .nav-item:hover { color: var(--text); - border-color: rgba(255, 255, 255, 0.12); - background: rgba(255, 255, 255, 0.06); + background: var(--bg-hover); + text-decoration: none; } -.nav-item::before { - content: ""; - position: absolute; - left: 0; - top: 50%; - width: 4px; - height: 60%; - border-radius: 0 999px 999px 0; - transform: translateY(-50%); - background: transparent; +.nav-item:hover .nav-item__icon { + opacity: 1; } .nav-item.active { - color: var(--text); - border-color: rgba(245, 159, 74, 0.45); - background: rgba(245, 159, 74, 0.12); + color: var(--text-strong); + background: var(--accent-subtle); } -.nav-item.active::before { - background: var(--accent); - box-shadow: 0 0 12px rgba(245, 159, 74, 0.4); +.nav-item.active .nav-item__icon { + opacity: 1; + color: var(--accent); } +/* =========================================== + Content Area + =========================================== */ + .content { grid-area: content; - padding: 8px 6px 20px; + padding: 12px 16px 32px; display: flex; flex-direction: column; - gap: 20px; + gap: 24px; min-height: 0; - overflow-y: auto; /* Enable vertical scrolling for pages with long content */ + overflow-y: auto; overflow-x: hidden; } -/* Chat handles its own scrolling (chat-thread); avoid double scrollbars. */ +:root[data-theme="light"] .content { + background: var(--bg-content); +} + .content--chat { overflow: hidden; + padding-bottom: 0; } -.shell--chat .content { - /* No-op: keep chat layout consistent with other tabs */ -} - +/* Content header */ .content-header { display: flex; align-items: flex-end; justify-content: space-between; - gap: 12px; - padding: 0 6px; + gap: 16px; + padding: 4px 8px; overflow: hidden; transform-origin: top center; - transition: opacity var(--shell-focus-duration) var(--shell-focus-ease), + transition: + opacity var(--shell-focus-duration) var(--shell-focus-ease), transform var(--shell-focus-duration) var(--shell-focus-ease), max-height var(--shell-focus-duration) var(--shell-focus-ease), padding var(--shell-focus-duration) var(--shell-focus-ease); - max-height: 90px; + max-height: 80px; } .shell--chat-focus .content-header { opacity: 0; - transform: translateY(-10px); + transform: translateY(-8px); max-height: 0px; padding: 0; pointer-events: none; } .page-title { - font-family: var(--font-display); font-size: 26px; - letter-spacing: 0.6px; + font-weight: 700; + letter-spacing: -0.035em; + line-height: 1.15; + color: var(--text-strong); } .page-sub { color: var(--muted); - font-size: 12px; - letter-spacing: 0.4px; + font-size: 14px; + font-weight: 400; + margin-top: 6px; + letter-spacing: -0.01em; } .page-meta { display: flex; - gap: 10px; + gap: 8px; } -/* Chat view: header and controls side by side */ +/* Chat view header adjustments */ .content--chat .content-header { flex-direction: row; align-items: center; @@ -410,9 +501,13 @@ flex-shrink: 0; } +/* =========================================== + Grid Utilities + =========================================== */ + .grid { display: grid; - gap: 18px; + gap: 20px; } .grid-cols-2 { @@ -426,13 +521,13 @@ .stat-grid { display: grid; gap: 14px; - grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } .note-grid { display: grid; - gap: 14px; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 16px; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); } .row { @@ -443,21 +538,25 @@ .stack { display: grid; - gap: 14px; + gap: 12px; + grid-template-columns: minmax(0, 1fr); } .filters { display: flex; flex-wrap: wrap; - gap: 10px; + gap: 8px; align-items: center; } +/* =========================================== + Responsive - Tablet + =========================================== */ + @media (max-width: 1100px) { .shell { --shell-pad: 12px; --shell-gap: 12px; - --shell-nav-col: 1fr; grid-template-columns: 1fr; grid-template-rows: auto auto 1fr; grid-template-areas: @@ -470,17 +569,18 @@ position: static; max-height: none; display: flex; - gap: 16px; + gap: 6px; overflow-x: auto; border-right: none; - padding: 12px; + border-bottom: 1px solid var(--border); + padding: 10px 14px; + background: var(--bg); } .nav-group { grid-auto-flow: column; - grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); - border-bottom: none; - padding-bottom: 0; + grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); + margin-bottom: 0; } .grid-cols-2, @@ -490,13 +590,11 @@ .topbar { position: static; - flex-direction: column; - align-items: flex-start; - gap: 12px; + padding: 12px 14px; + gap: 10px; } .topbar-status { - width: 100%; flex-wrap: wrap; } diff --git a/ui/src/styles/layout.mobile.css b/ui/src/styles/layout.mobile.css index d3cb9d47d..450a83608 100644 --- a/ui/src/styles/layout.mobile.css +++ b/ui/src/styles/layout.mobile.css @@ -1,12 +1,15 @@ -/* Tablet/Mobile nav fix (under 1100px) - single horizontal scroll row */ +/* =========================================== + Mobile Layout + =========================================== */ + +/* Tablet: Horizontal nav */ @media (max-width: 1100px) { - /* Flatten nav into single horizontal scroll */ .nav { display: flex; flex-direction: row; flex-wrap: nowrap; - gap: 6px; - padding: 10px 12px; + gap: 4px; + padding: 10px 14px; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; @@ -16,21 +19,18 @@ display: none; } - /* Nav groups should flow inline, not stack */ .nav-group { - display: contents; /* Flatten group wrapper - items flow directly into .nav */ + display: contents; } .nav-group__items { - display: contents; /* Flatten items wrapper too */ + display: contents; } - /* Hide group labels on tablet/mobile */ .nav-label { display: none; } - /* Don't hide nav items even if group is "collapsed" */ .nav-group--collapsed .nav-group__items { display: contents; } @@ -38,27 +38,22 @@ .nav-item { padding: 8px 14px; font-size: 13px; - border-radius: 10px; + border-radius: var(--radius-md); white-space: nowrap; flex-shrink: 0; } - - .nav-item::before { - display: none; - } } -/* Mobile-specific improvements */ +/* Mobile-specific styles */ @media (max-width: 600px) { .shell { --shell-pad: 8px; --shell-gap: 8px; } - /* Compact topbar for mobile */ + /* Topbar */ .topbar { padding: 10px 12px; - border-radius: 12px; gap: 8px; flex-direction: row; flex-wrap: wrap; @@ -72,8 +67,7 @@ } .brand-title { - font-size: 15px; - letter-spacing: 0.3px; + font-size: 14px; } .brand-sub { @@ -100,11 +94,10 @@ display: none; } - /* Horizontal scrollable nav for mobile */ + /* Nav */ .nav { - padding: 8px; - border-radius: 12px; - gap: 8px; + padding: 8px 10px; + gap: 4px; -webkit-overflow-scrolling: touch; scrollbar-width: none; } @@ -122,18 +115,14 @@ } .nav-item { - padding: 7px 10px; + padding: 6px 10px; font-size: 12px; - border-radius: 8px; + border-radius: var(--radius-md); white-space: nowrap; flex-shrink: 0; } - .nav-item::before { - display: none; - } - - /* Hide page title on mobile - nav already shows where you are */ + /* Content */ .content-header { display: none; } @@ -143,17 +132,17 @@ gap: 12px; } - /* Smaller cards on mobile */ + /* Cards */ .card { padding: 12px; - border-radius: 12px; + border-radius: var(--radius-md); } .card-title { - font-size: 14px; + font-size: 13px; } - /* Stat grid adjustments */ + /* Stats */ .stat-grid { gap: 8px; grid-template-columns: repeat(2, 1fr); @@ -161,24 +150,24 @@ .stat { padding: 10px; - border-radius: 10px; + border-radius: var(--radius-md); } .stat-label { - font-size: 10px; + font-size: 11px; } .stat-value { - font-size: 16px; + font-size: 18px; } - /* Notes grid */ + /* Notes */ .note-grid { grid-template-columns: 1fr; - gap: 10px; + gap: 8px; } - /* Form fields */ + /* Forms */ .form-grid { grid-template-columns: 1fr; gap: 10px; @@ -188,14 +177,14 @@ .field textarea, .field select { padding: 8px 10px; - border-radius: 10px; + border-radius: var(--radius-md); font-size: 14px; } /* Buttons */ .btn { padding: 8px 12px; - font-size: 13px; + font-size: 12px; } /* Pills */ @@ -204,7 +193,7 @@ font-size: 12px; } - /* Chat-specific mobile improvements */ + /* Chat */ .chat-header { flex-direction: column; align-items: stretch; @@ -227,17 +216,16 @@ .chat-thread { margin-top: 8px; - padding: 10px 8px; - border-radius: 12px; + padding: 12px 8px; } .chat-msg { - max-width: 92%; + max-width: 90%; } .chat-bubble { - padding: 8px 10px; - border-radius: 12px; + padding: 8px 12px; + border-radius: var(--radius-md); } .chat-compose { @@ -247,14 +235,14 @@ .chat-compose__field textarea { min-height: 60px; padding: 8px 10px; - border-radius: 12px; + border-radius: var(--radius-md); font-size: 14px; } - /* Log stream mobile */ + /* Log stream */ .log-stream { - border-radius: 10px; - max-height: 400px; + border-radius: var(--radius-md); + max-height: 380px; } .log-row { @@ -279,14 +267,14 @@ font-size: 12px; } - /* List items */ + /* Lists */ .list-item { padding: 10px; - border-radius: 10px; + border-radius: var(--radius-md); } .list-title { - font-size: 14px; + font-size: 13px; } .list-sub { @@ -296,19 +284,91 @@ /* Code blocks */ .code-block { padding: 8px; - border-radius: 10px; + border-radius: var(--radius-md); font-size: 11px; } - /* Theme toggle smaller */ + /* Theme toggle */ .theme-toggle { --theme-item: 24px; - --theme-gap: 4px; - --theme-pad: 4px; + --theme-gap: 2px; + --theme-pad: 3px; } .theme-icon { - width: 14px; - height: 14px; + width: 12px; + height: 12px; + } +} + +/* Small mobile */ +@media (max-width: 400px) { + .shell { + --shell-pad: 4px; + } + + .topbar { + padding: 8px 10px; + } + + .brand-title { + font-size: 13px; + } + + .nav { + padding: 6px 8px; + } + + .nav-item { + padding: 6px 8px; + font-size: 11px; + } + + .content { + padding: 4px 4px 12px; + gap: 10px; + } + + .card { + padding: 10px; + } + + .stat { + padding: 8px; + } + + .stat-value { + font-size: 16px; + } + + .chat-bubble { + padding: 8px 10px; + } + + .chat-compose__field textarea { + min-height: 52px; + padding: 8px 10px; + font-size: 13px; + } + + .btn { + padding: 6px 10px; + font-size: 11px; + } + + .topbar-status .pill { + padding: 3px 6px; + font-size: 10px; + } + + .theme-toggle { + --theme-item: 22px; + --theme-gap: 2px; + --theme-pad: 2px; + } + + .theme-icon { + width: 11px; + height: 11px; } } diff --git a/ui/src/ui/app-channels.ts b/ui/src/ui/app-channels.ts index 3a3921a8d..91ff734ed 100644 --- a/ui/src/ui/app-channels.ts +++ b/ui/src/ui/app-channels.ts @@ -5,32 +5,32 @@ import { waitWhatsAppLogin, } from "./controllers/channels"; import { loadConfig, saveConfig } from "./controllers/config"; -import type { ClawdbotApp } from "./app"; +import type { MoltbotApp } from "./app"; import type { NostrProfile } from "./types"; import { createNostrProfileFormState } from "./views/channels.nostr-profile-form"; -export async function handleWhatsAppStart(host: ClawdbotApp, force: boolean) { +export async function handleWhatsAppStart(host: MoltbotApp, force: boolean) { await startWhatsAppLogin(host, force); await loadChannels(host, true); } -export async function handleWhatsAppWait(host: ClawdbotApp) { +export async function handleWhatsAppWait(host: MoltbotApp) { await waitWhatsAppLogin(host); await loadChannels(host, true); } -export async function handleWhatsAppLogout(host: ClawdbotApp) { +export async function handleWhatsAppLogout(host: MoltbotApp) { await logoutWhatsApp(host); await loadChannels(host, true); } -export async function handleChannelConfigSave(host: ClawdbotApp) { +export async function handleChannelConfigSave(host: MoltbotApp) { await saveConfig(host); await loadConfig(host); await loadChannels(host, true); } -export async function handleChannelConfigReload(host: ClawdbotApp) { +export async function handleChannelConfigReload(host: MoltbotApp) { await loadConfig(host); await loadChannels(host, true); } @@ -49,7 +49,7 @@ function parseValidationErrors(details: unknown): Record { return errors; } -function resolveNostrAccountId(host: ClawdbotApp): string { +function resolveNostrAccountId(host: MoltbotApp): string { const accounts = host.channelsSnapshot?.channelAccounts?.nostr ?? []; return accounts[0]?.accountId ?? host.nostrProfileAccountId ?? "default"; } @@ -59,7 +59,7 @@ function buildNostrProfileUrl(accountId: string, suffix = ""): string { } export function handleNostrProfileEdit( - host: ClawdbotApp, + host: MoltbotApp, accountId: string, profile: NostrProfile | null, ) { @@ -67,13 +67,13 @@ export function handleNostrProfileEdit( host.nostrProfileFormState = createNostrProfileFormState(profile ?? undefined); } -export function handleNostrProfileCancel(host: ClawdbotApp) { +export function handleNostrProfileCancel(host: MoltbotApp) { host.nostrProfileFormState = null; host.nostrProfileAccountId = null; } export function handleNostrProfileFieldChange( - host: ClawdbotApp, + host: MoltbotApp, field: keyof NostrProfile, value: string, ) { @@ -92,7 +92,7 @@ export function handleNostrProfileFieldChange( }; } -export function handleNostrProfileToggleAdvanced(host: ClawdbotApp) { +export function handleNostrProfileToggleAdvanced(host: MoltbotApp) { const state = host.nostrProfileFormState; if (!state) return; host.nostrProfileFormState = { @@ -101,7 +101,7 @@ export function handleNostrProfileToggleAdvanced(host: ClawdbotApp) { }; } -export async function handleNostrProfileSave(host: ClawdbotApp) { +export async function handleNostrProfileSave(host: MoltbotApp) { const state = host.nostrProfileFormState; if (!state || state.saving) return; const accountId = resolveNostrAccountId(host); @@ -167,7 +167,7 @@ export async function handleNostrProfileSave(host: ClawdbotApp) { } } -export async function handleNostrProfileImport(host: ClawdbotApp) { +export async function handleNostrProfileImport(host: MoltbotApp) { const state = host.nostrProfileFormState; if (!state || state.importing) return; const accountId = resolveNostrAccountId(host); diff --git a/ui/src/ui/app-chat.ts b/ui/src/ui/app-chat.ts index 81aae3c88..77149f9ad 100644 --- a/ui/src/ui/app-chat.ts +++ b/ui/src/ui/app-chat.ts @@ -7,12 +7,14 @@ import { setLastActiveSessionKey } from "./app-settings"; import { normalizeBasePath } from "./navigation"; import type { GatewayHelloOk } from "./gateway"; import { parseAgentSessionKey } from "../../../src/sessions/session-key-utils.js"; -import type { ClawdbotApp } from "./app"; +import type { MoltbotApp } from "./app"; +import type { ChatAttachment, ChatQueueItem } from "./ui-types"; type ChatHost = { connected: boolean; chatMessage: string; - chatQueue: Array<{ id: string; text: string; createdAt: number }>; + chatAttachments: ChatAttachment[]; + chatQueue: ChatQueueItem[]; chatRunId: string | null; chatSending: boolean; sessionKey: string; @@ -42,18 +44,20 @@ export function isChatStopCommand(text: string) { export async function handleAbortChat(host: ChatHost) { if (!host.connected) return; host.chatMessage = ""; - await abortChatRun(host as unknown as ClawdbotApp); + await abortChatRun(host as unknown as MoltbotApp); } -function enqueueChatMessage(host: ChatHost, text: string) { +function enqueueChatMessage(host: ChatHost, text: string, attachments?: ChatAttachment[]) { const trimmed = text.trim(); - if (!trimmed) return; + const hasAttachments = Boolean(attachments && attachments.length > 0); + if (!trimmed && !hasAttachments) return; host.chatQueue = [ ...host.chatQueue, { id: generateUUID(), text: trimmed, createdAt: Date.now(), + attachments: hasAttachments ? attachments?.map((att) => ({ ...att })) : undefined, }, ]; } @@ -61,19 +65,31 @@ function enqueueChatMessage(host: ChatHost, text: string) { async function sendChatMessageNow( host: ChatHost, message: string, - opts?: { previousDraft?: string; restoreDraft?: boolean }, + opts?: { + previousDraft?: string; + restoreDraft?: boolean; + attachments?: ChatAttachment[]; + previousAttachments?: ChatAttachment[]; + restoreAttachments?: boolean; + }, ) { resetToolStream(host as unknown as Parameters[0]); - const ok = await sendChatMessage(host as unknown as ClawdbotApp, message); + const ok = await sendChatMessage(host as unknown as MoltbotApp, message, opts?.attachments); if (!ok && opts?.previousDraft != null) { host.chatMessage = opts.previousDraft; } + if (!ok && opts?.previousAttachments) { + host.chatAttachments = opts.previousAttachments; + } if (ok) { setLastActiveSessionKey(host as unknown as Parameters[0], host.sessionKey); } if (ok && opts?.restoreDraft && opts.previousDraft?.trim()) { host.chatMessage = opts.previousDraft; } + if (ok && opts?.restoreAttachments && opts.previousAttachments?.length) { + host.chatAttachments = opts.previousAttachments; + } scheduleChatScroll(host as unknown as Parameters[0]); if (ok && !host.chatRunId) { void flushChatQueue(host); @@ -86,7 +102,7 @@ async function flushChatQueue(host: ChatHost) { const [next, ...rest] = host.chatQueue; if (!next) return; host.chatQueue = rest; - const ok = await sendChatMessageNow(host, next.text); + const ok = await sendChatMessageNow(host, next.text, { attachments: next.attachments }); if (!ok) { host.chatQueue = [next, ...host.chatQueue]; } @@ -104,7 +120,12 @@ export async function handleSendChat( if (!host.connected) return; const previousDraft = host.chatMessage; const message = (messageOverride ?? host.chatMessage).trim(); - if (!message) return; + const attachments = host.chatAttachments ?? []; + const attachmentsToSend = messageOverride == null ? attachments : []; + const hasAttachments = attachmentsToSend.length > 0; + + // Allow sending with just attachments (no message text required) + if (!message && !hasAttachments) return; if (isChatStopCommand(message)) { await handleAbortChat(host); @@ -113,23 +134,28 @@ export async function handleSendChat( if (messageOverride == null) { host.chatMessage = ""; + // Clear attachments when sending + host.chatAttachments = []; } if (isChatBusy(host)) { - enqueueChatMessage(host, message); + enqueueChatMessage(host, message, attachmentsToSend); return; } await sendChatMessageNow(host, message, { previousDraft: messageOverride == null ? previousDraft : undefined, restoreDraft: Boolean(messageOverride && opts?.restoreDraft), + attachments: hasAttachments ? attachmentsToSend : undefined, + previousAttachments: messageOverride == null ? attachments : undefined, + restoreAttachments: Boolean(messageOverride && opts?.restoreDraft), }); } export async function refreshChat(host: ChatHost) { await Promise.all([ - loadChatHistory(host as unknown as ClawdbotApp), - loadSessions(host as unknown as ClawdbotApp), + loadChatHistory(host as unknown as MoltbotApp), + loadSessions(host as unknown as MoltbotApp), refreshChatAvatar(host), ]); scheduleChatScroll(host as unknown as Parameters[0], true); diff --git a/ui/src/ui/app-events.ts b/ui/src/ui/app-events.ts index c058cf73e..eda3a8e16 100644 --- a/ui/src/ui/app-events.ts +++ b/ui/src/ui/app-events.ts @@ -3,4 +3,3 @@ export type EventLogEntry = { event: string; payload?: unknown; }; - diff --git a/ui/src/ui/app-gateway.ts b/ui/src/ui/app-gateway.ts index d9a267a98..b2355709c 100644 --- a/ui/src/ui/app-gateway.ts +++ b/ui/src/ui/app-gateway.ts @@ -23,7 +23,7 @@ import { parseExecApprovalResolved, removeExecApproval, } from "./controllers/exec-approval"; -import type { ClawdbotApp } from "./app"; +import type { MoltbotApp } from "./app"; import type { ExecApprovalRequest } from "./controllers/exec-approval"; import { loadAssistantIdentity } from "./controllers/assistant-identity"; @@ -120,17 +120,23 @@ export function connectGateway(host: GatewayHost) { url: host.settings.gatewayUrl, token: host.settings.token.trim() ? host.settings.token : undefined, password: host.password.trim() ? host.password : undefined, - clientName: "clawdbot-control-ui", + clientName: "moltbot-control-ui", mode: "webchat", onHello: (hello) => { host.connected = true; host.lastError = null; host.hello = hello; applySnapshot(host, hello); - void loadAssistantIdentity(host as unknown as ClawdbotApp); - void loadAgents(host as unknown as ClawdbotApp); - void loadNodes(host as unknown as ClawdbotApp, { quiet: true }); - void loadDevices(host as unknown as ClawdbotApp, { quiet: true }); + // Reset orphaned chat run state from before disconnect. + // Any in-flight run's final event was lost during the disconnect window. + host.chatRunId = null; + (host as unknown as { chatStream: string | null }).chatStream = null; + (host as unknown as { chatStreamStartedAt: number | null }).chatStreamStartedAt = null; + resetToolStream(host as unknown as Parameters[0]); + void loadAssistantIdentity(host as unknown as MoltbotApp); + void loadAgents(host as unknown as MoltbotApp); + void loadNodes(host as unknown as MoltbotApp, { quiet: true }); + void loadDevices(host as unknown as MoltbotApp, { quiet: true }); void refreshActiveTab(host as unknown as Parameters[0]); }, onClose: ({ code, reason }) => { @@ -182,14 +188,14 @@ function handleGatewayEventUnsafe(host: GatewayHost, evt: GatewayEventFrame) { payload.sessionKey, ); } - const state = handleChatEvent(host as unknown as ClawdbotApp, payload); + const state = handleChatEvent(host as unknown as MoltbotApp, payload); if (state === "final" || state === "error" || state === "aborted") { resetToolStream(host as unknown as Parameters[0]); void flushChatQueueForEvent( host as unknown as Parameters[0], ); } - if (state === "final") void loadChatHistory(host as unknown as ClawdbotApp); + if (state === "final") void loadChatHistory(host as unknown as MoltbotApp); return; } @@ -208,7 +214,7 @@ function handleGatewayEventUnsafe(host: GatewayHost, evt: GatewayEventFrame) { } if (evt.event === "device.pair.requested" || evt.event === "device.pair.resolved") { - void loadDevices(host as unknown as ClawdbotApp, { quiet: true }); + void loadDevices(host as unknown as MoltbotApp, { quiet: true }); } if (evt.event === "exec.approval.requested") { diff --git a/ui/src/ui/app-polling.ts b/ui/src/ui/app-polling.ts index b18113c5a..3255bdaeb 100644 --- a/ui/src/ui/app-polling.ts +++ b/ui/src/ui/app-polling.ts @@ -1,7 +1,7 @@ import { loadLogs } from "./controllers/logs"; import { loadNodes } from "./controllers/nodes"; import { loadDebug } from "./controllers/debug"; -import type { ClawdbotApp } from "./app"; +import type { MoltbotApp } from "./app"; type PollingHost = { nodesPollInterval: number | null; @@ -13,7 +13,7 @@ type PollingHost = { export function startNodesPolling(host: PollingHost) { if (host.nodesPollInterval != null) return; host.nodesPollInterval = window.setInterval( - () => void loadNodes(host as unknown as ClawdbotApp, { quiet: true }), + () => void loadNodes(host as unknown as MoltbotApp, { quiet: true }), 5000, ); } @@ -28,7 +28,7 @@ export function startLogsPolling(host: PollingHost) { if (host.logsPollInterval != null) return; host.logsPollInterval = window.setInterval(() => { if (host.tab !== "logs") return; - void loadLogs(host as unknown as ClawdbotApp, { quiet: true }); + void loadLogs(host as unknown as MoltbotApp, { quiet: true }); }, 2000); } @@ -42,7 +42,7 @@ export function startDebugPolling(host: PollingHost) { if (host.debugPollInterval != null) return; host.debugPollInterval = window.setInterval(() => { if (host.tab !== "debug") return; - void loadDebug(host as unknown as ClawdbotApp); + void loadDebug(host as unknown as MoltbotApp); }, 3000); } diff --git a/ui/src/ui/app-render.helpers.ts b/ui/src/ui/app-render.helpers.ts index d5a01755b..22f8d90db 100644 --- a/ui/src/ui/app-render.helpers.ts +++ b/ui/src/ui/app-render.helpers.ts @@ -3,6 +3,7 @@ import { repeat } from "lit/directives/repeat.js"; import type { AppViewState } from "./app-view-state"; import { iconForTab, pathForTab, titleForTab, type Tab } from "./navigation"; +import { icons } from "./icons"; import { loadChatHistory } from "./controllers/chat"; import { syncUrlWithSessionKey } from "./app-settings"; import type { SessionsListResult } from "./types"; @@ -31,7 +32,7 @@ export function renderTab(state: AppViewState, tab: Tab) { }} title=${titleForTab(tab)} > - + ${titleForTab(tab)} `; @@ -108,7 +109,7 @@ export function renderChatControls(state: AppViewState) { ? "Disabled during onboarding" : "Toggle assistant thinking/working output"} > - 🧠 + ${icons.brain}
-
CLAWDBOT
-
Gateway Dashboard
+ +
+
MOLTBOT
+
Gateway Dashboard
+
@@ -174,12 +180,12 @@ export function renderApp(state: AppViewState) { @@ -425,6 +431,7 @@ export function renderApp(state: AppViewState) { onSessionKeyChange: (next) => { state.sessionKey = next; state.chatMessage = ""; + state.chatAttachments = []; state.chatStream = null; state.chatStreamStartedAt = null; state.chatRunId = null; @@ -471,6 +478,8 @@ export function renderApp(state: AppViewState) { }, onChatScroll: (event) => state.handleChatScroll(event), onDraftChange: (next) => (state.chatMessage = next), + attachments: state.chatAttachments, + onAttachmentsChange: (next) => (state.chatAttachments = next), onSend: () => state.handleSendChat(), canAbort: Boolean(state.chatRunId), onAbort: () => void state.handleAbortChat(), diff --git a/ui/src/ui/app-scroll.ts b/ui/src/ui/app-scroll.ts index 5955e80b8..c3c29f479 100644 --- a/ui/src/ui/app-scroll.ts +++ b/ui/src/ui/app-scroll.ts @@ -103,7 +103,7 @@ export function exportLogs(lines: string[], label: string) { const anchor = document.createElement("a"); const stamp = new Date().toISOString().slice(0, 19).replace(/[:T]/g, "-"); anchor.href = url; - anchor.download = `clawdbot-logs-${label}-${stamp}.log`; + anchor.download = `moltbot-logs-${label}-${stamp}.log`; anchor.click(); URL.revokeObjectURL(url); } diff --git a/ui/src/ui/app-settings.ts b/ui/src/ui/app-settings.ts index 0cceff4cb..e269742b2 100644 --- a/ui/src/ui/app-settings.ts +++ b/ui/src/ui/app-settings.ts @@ -16,7 +16,7 @@ import { startThemeTransition, type ThemeTransitionContext } from "./theme-trans import { scheduleChatScroll, scheduleLogsScroll } from "./app-scroll"; import { startLogsPolling, stopLogsPolling, startDebugPolling, stopDebugPolling } from "./app-polling"; import { refreshChat } from "./app-chat"; -import type { ClawdbotApp } from "./app"; +import type { MoltbotApp } from "./app"; type SettingsHost = { settings: UiSettings; @@ -144,15 +144,15 @@ export function setTheme( export async function refreshActiveTab(host: SettingsHost) { if (host.tab === "overview") await loadOverview(host); if (host.tab === "channels") await loadChannelsTab(host); - if (host.tab === "instances") await loadPresence(host as unknown as ClawdbotApp); - if (host.tab === "sessions") await loadSessions(host as unknown as ClawdbotApp); + if (host.tab === "instances") await loadPresence(host as unknown as MoltbotApp); + if (host.tab === "sessions") await loadSessions(host as unknown as MoltbotApp); if (host.tab === "cron") await loadCron(host); - if (host.tab === "skills") await loadSkills(host as unknown as ClawdbotApp); + if (host.tab === "skills") await loadSkills(host as unknown as MoltbotApp); if (host.tab === "nodes") { - await loadNodes(host as unknown as ClawdbotApp); - await loadDevices(host as unknown as ClawdbotApp); - await loadConfig(host as unknown as ClawdbotApp); - await loadExecApprovals(host as unknown as ClawdbotApp); + await loadNodes(host as unknown as MoltbotApp); + await loadDevices(host as unknown as MoltbotApp); + await loadConfig(host as unknown as MoltbotApp); + await loadExecApprovals(host as unknown as MoltbotApp); } if (host.tab === "chat") { await refreshChat(host as unknown as Parameters[0]); @@ -162,16 +162,16 @@ export async function refreshActiveTab(host: SettingsHost) { ); } if (host.tab === "config") { - await loadConfigSchema(host as unknown as ClawdbotApp); - await loadConfig(host as unknown as ClawdbotApp); + await loadConfigSchema(host as unknown as MoltbotApp); + await loadConfig(host as unknown as MoltbotApp); } if (host.tab === "debug") { - await loadDebug(host as unknown as ClawdbotApp); + await loadDebug(host as unknown as MoltbotApp); host.eventLog = host.eventLogBuffer; } if (host.tab === "logs") { host.logsAtBottom = true; - await loadLogs(host as unknown as ClawdbotApp, { reset: true }); + await loadLogs(host as unknown as MoltbotApp, { reset: true }); scheduleLogsScroll( host as unknown as Parameters[0], true, @@ -307,26 +307,26 @@ export function syncUrlWithSessionKey( export async function loadOverview(host: SettingsHost) { await Promise.all([ - loadChannels(host as unknown as ClawdbotApp, false), - loadPresence(host as unknown as ClawdbotApp), - loadSessions(host as unknown as ClawdbotApp), - loadCronStatus(host as unknown as ClawdbotApp), - loadDebug(host as unknown as ClawdbotApp), + loadChannels(host as unknown as MoltbotApp, false), + loadPresence(host as unknown as MoltbotApp), + loadSessions(host as unknown as MoltbotApp), + loadCronStatus(host as unknown as MoltbotApp), + loadDebug(host as unknown as MoltbotApp), ]); } export async function loadChannelsTab(host: SettingsHost) { await Promise.all([ - loadChannels(host as unknown as ClawdbotApp, true), - loadConfigSchema(host as unknown as ClawdbotApp), - loadConfig(host as unknown as ClawdbotApp), + loadChannels(host as unknown as MoltbotApp, true), + loadConfigSchema(host as unknown as MoltbotApp), + loadConfig(host as unknown as MoltbotApp), ]); } export async function loadCron(host: SettingsHost) { await Promise.all([ - loadChannels(host as unknown as ClawdbotApp, false), - loadCronStatus(host as unknown as ClawdbotApp), - loadCronJobs(host as unknown as ClawdbotApp), + loadChannels(host as unknown as MoltbotApp, false), + loadCronStatus(host as unknown as MoltbotApp), + loadCronJobs(host as unknown as MoltbotApp), ]); } diff --git a/ui/src/ui/app-tool-stream.ts b/ui/src/ui/app-tool-stream.ts index 5c83c3a79..2fbe12b7a 100644 --- a/ui/src/ui/app-tool-stream.ts +++ b/ui/src/ui/app-tool-stream.ts @@ -154,13 +154,13 @@ const COMPACTION_TOAST_DURATION_MS = 5000; export function handleCompactionEvent(host: CompactionHost, payload: AgentEventPayload) { const data = payload.data ?? {}; const phase = typeof data.phase === "string" ? data.phase : ""; - + // Clear any existing timer if (host.compactionClearTimer != null) { window.clearTimeout(host.compactionClearTimer); host.compactionClearTimer = null; } - + if (phase === "start") { host.compactionStatus = { active: true, @@ -183,13 +183,13 @@ export function handleCompactionEvent(host: CompactionHost, payload: AgentEventP export function handleAgentEvent(host: ToolStreamHost, payload?: AgentEventPayload) { if (!payload) return; - + // Handle compaction events if (payload.stream === "compaction") { handleCompactionEvent(host as CompactionHost, payload); return; } - + if (payload.stream !== "tool") return; const sessionKey = typeof payload.sessionKey === "string" ? payload.sessionKey : undefined; diff --git a/ui/src/ui/app-view-state.ts b/ui/src/ui/app-view-state.ts index f589c760c..069465e32 100644 --- a/ui/src/ui/app-view-state.ts +++ b/ui/src/ui/app-view-state.ts @@ -19,7 +19,7 @@ import type { SkillStatusReport, StatusSummary, } from "./types"; -import type { ChatQueueItem, CronFormState } from "./ui-types"; +import type { ChatAttachment, ChatQueueItem, CronFormState } from "./ui-types"; import type { EventLogEntry } from "./app-events"; import type { SkillMessage } from "./controllers/skills"; import type { @@ -49,6 +49,7 @@ export type AppViewState = { chatLoading: boolean; chatSending: boolean; chatMessage: string; + chatAttachments: ChatAttachment[]; chatMessages: unknown[]; chatToolMessages: unknown[]; chatStream: string | null; diff --git a/ui/src/ui/app.ts b/ui/src/ui/app.ts index 0e21d283a..d23e543cd 100644 --- a/ui/src/ui/app.ts +++ b/ui/src/ui/app.ts @@ -24,7 +24,7 @@ import type { StatusSummary, NostrProfile, } from "./types"; -import { type ChatQueueItem, type CronFormState } from "./ui-types"; +import { type ChatAttachment, type ChatQueueItem, type CronFormState } from "./ui-types"; import type { EventLogEntry } from "./app-events"; import { DEFAULT_CRON_FORM, DEFAULT_LOG_LEVEL_FILTERS } from "./app-defaults"; import type { @@ -96,8 +96,8 @@ function resolveOnboardingMode(): boolean { return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on"; } -@customElement("clawdbot-app") -export class ClawdbotApp extends LitElement { +@customElement("moltbot-app") +export class MoltbotApp extends LitElement { @state() settings: UiSettings = loadSettings(); @state() password = ""; @state() tab: Tab = "chat"; @@ -129,6 +129,7 @@ export class ClawdbotApp extends LitElement { @state() chatAvatarUrl: string | null = null; @state() chatThinkingLevel: string | null = null; @state() chatQueue: ChatQueueItem[] = []; + @state() chatAttachments: ChatAttachment[] = []; // Sidebar state for tool output viewing @state() sidebarOpen = false; @state() sidebarContent: string | null = null; diff --git a/ui/src/ui/chat-markdown.browser.test.ts b/ui/src/ui/chat-markdown.browser.test.ts index 732e4b86a..cb2011e6c 100644 --- a/ui/src/ui/chat-markdown.browser.test.ts +++ b/ui/src/ui/chat-markdown.browser.test.ts @@ -1,18 +1,18 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; -import { ClawdbotApp } from "./app"; +import { MoltbotApp } from "./app"; -const originalConnect = ClawdbotApp.prototype.connect; +const originalConnect = MoltbotApp.prototype.connect; function mountApp(pathname: string) { window.history.replaceState({}, "", pathname); - const app = document.createElement("clawdbot-app") as ClawdbotApp; + const app = document.createElement("moltbot-app") as MoltbotApp; document.body.append(app); return app; } beforeEach(() => { - ClawdbotApp.prototype.connect = () => { + MoltbotApp.prototype.connect = () => { // no-op: avoid real gateway WS connections in browser tests }; window.__CLAWDBOT_CONTROL_UI_BASE_PATH__ = undefined; @@ -21,7 +21,7 @@ beforeEach(() => { }); afterEach(() => { - ClawdbotApp.prototype.connect = originalConnect; + MoltbotApp.prototype.connect = originalConnect; window.__CLAWDBOT_CONTROL_UI_BASE_PATH__ = undefined; localStorage.clear(); document.body.innerHTML = ""; diff --git a/ui/src/ui/chat/copy-as-markdown.ts b/ui/src/ui/chat/copy-as-markdown.ts index 8786d3546..1309f08b8 100644 --- a/ui/src/ui/chat/copy-as-markdown.ts +++ b/ui/src/ui/chat/copy-as-markdown.ts @@ -1,14 +1,11 @@ import { html, type TemplateResult } from "lit"; -import { renderEmojiIcon, setEmojiIcon } from "../icons"; +import { icons } from "../icons"; const COPIED_FOR_MS = 1500; const ERROR_FOR_MS = 2000; const COPY_LABEL = "Copy as markdown"; const COPIED_LABEL = "Copied"; const ERROR_LABEL = "Copy failed"; -const COPY_ICON = "📋"; -const COPIED_ICON = "✓"; -const ERROR_ICON = "!"; type CopyButtonOptions = { text: () => string; @@ -41,7 +38,7 @@ function createCopyButton(options: CopyButtonOptions): TemplateResult { aria-label=${idleLabel} @click=${async (e: Event) => { const btn = e.currentTarget as HTMLButtonElement | null; - const icon = btn?.querySelector( + const iconContainer = btn?.querySelector( ".chat-copy-btn__icon", ) as HTMLElement | null; @@ -61,30 +58,29 @@ function createCopyButton(options: CopyButtonOptions): TemplateResult { if (!copied) { btn.dataset.error = "1"; setButtonLabel(btn, ERROR_LABEL); - setEmojiIcon(icon, ERROR_ICON); window.setTimeout(() => { if (!btn.isConnected) return; delete btn.dataset.error; setButtonLabel(btn, idleLabel); - setEmojiIcon(icon, COPY_ICON); }, ERROR_FOR_MS); return; } btn.dataset.copied = "1"; setButtonLabel(btn, COPIED_LABEL); - setEmojiIcon(icon, COPIED_ICON); window.setTimeout(() => { if (!btn.isConnected) return; delete btn.dataset.copied; setButtonLabel(btn, idleLabel); - setEmojiIcon(icon, COPY_ICON); }, COPIED_FOR_MS); }} > - ${renderEmojiIcon(COPY_ICON, "chat-copy-btn__icon")} + `; } diff --git a/ui/src/ui/chat/grouped-render.ts b/ui/src/ui/chat/grouped-render.ts index ea1c7ffda..4a9ccec14 100644 --- a/ui/src/ui/chat/grouped-render.ts +++ b/ui/src/ui/chat/grouped-render.ts @@ -13,6 +13,48 @@ import { } from "./message-extract"; import { extractToolCards, renderToolCardSidebar } from "./tool-cards"; +type ImageBlock = { + url: string; + alt?: string; +}; + +function extractImages(message: unknown): ImageBlock[] { + const m = message as Record; + const content = m.content; + const images: ImageBlock[] = []; + + if (Array.isArray(content)) { + for (const block of content) { + if (typeof block !== "object" || block === null) continue; + const b = block as Record; + + if (b.type === "image") { + // Handle source object format (from sendChatMessage) + const source = b.source as Record | undefined; + if (source?.type === "base64" && typeof source.data === "string") { + const data = source.data as string; + const mediaType = (source.media_type as string) || "image/png"; + // If data is already a data URL, use it directly + const url = data.startsWith("data:") + ? data + : `data:${mediaType};base64,${data}`; + images.push({ url }); + } else if (typeof b.url === "string") { + images.push({ url: b.url }); + } + } else if (b.type === "image_url") { + // OpenAI format + const imageUrl = b.image_url as Record | undefined; + if (typeof imageUrl?.url === "string") { + images.push({ url: imageUrl.url }); + } + } + } + } + + return images; +} + export function renderReadingIndicatorGroup(assistant?: AssistantIdentity) { return html`
@@ -163,6 +205,25 @@ function isAvatarUrl(value: string): boolean { ); } +function renderMessageImages(images: ImageBlock[]) { + if (images.length === 0) return nothing; + + return html` +
+ ${images.map( + (img) => html` + ${img.alt window.open(img.url, "_blank")} + /> + `, + )} +
+ `; +} + function renderGroupedMessage( message: unknown, opts: { isStreaming: boolean; showReasoning: boolean }, @@ -179,6 +240,8 @@ function renderGroupedMessage( const toolCards = extractToolCards(message); const hasToolCards = toolCards.length > 0; + const images = extractImages(message); + const hasImages = images.length > 0; const extractedText = extractTextCached(message); const extractedThinking = @@ -207,11 +270,12 @@ function renderGroupedMessage( )}`; } - if (!markdown && !hasToolCards) return nothing; + if (!markdown && !hasToolCards && !hasImages) return nothing; return html`
${canCopyMarkdown ? renderCopyAsMarkdownButton(markdown!) : nothing} + ${renderMessageImages(images)} ${reasoningMarkdown ? html`
${unsafeHTML( toSanitizedMarkdownHtml(reasoningMarkdown), diff --git a/ui/src/ui/chat/tool-cards.ts b/ui/src/ui/chat/tool-cards.ts index 78b5dffec..bf82fa49a 100644 --- a/ui/src/ui/chat/tool-cards.ts +++ b/ui/src/ui/chat/tool-cards.ts @@ -1,6 +1,7 @@ import { html, nothing } from "lit"; import { formatToolDetail, resolveToolDisplay } from "../tool-display"; +import { icons } from "../icons"; import type { ToolCard } from "../types/chat-types"; import { TOOL_INLINE_THRESHOLD } from "./constants"; import { @@ -95,13 +96,13 @@ export function renderToolCardSidebar( >
- ${display.emoji} + ${icons[display.icon]} ${display.label}
${canClick - ? html`${hasText ? "View ›" : "›"}` + ? html`${hasText ? "View" : ""} ${icons.check}` : nothing} - ${isEmpty && !canClick ? html`` : nothing} + ${isEmpty && !canClick ? html`${icons.check}` : nothing}
${detail ? html`
${detail}
` diff --git a/ui/src/ui/controllers/chat.test.ts b/ui/src/ui/controllers/chat.test.ts new file mode 100644 index 000000000..c75ceefc4 --- /dev/null +++ b/ui/src/ui/controllers/chat.test.ts @@ -0,0 +1,99 @@ +import { describe, expect, it } from "vitest"; + +import { + handleChatEvent, + type ChatEventPayload, + type ChatState, +} from "./chat"; + +function createState(overrides: Partial = {}): ChatState { + return { + client: null, + connected: true, + sessionKey: "main", + chatLoading: false, + chatMessages: [], + chatThinkingLevel: null, + chatSending: false, + chatMessage: "", + chatRunId: null, + chatStream: null, + chatStreamStartedAt: null, + lastError: null, + ...overrides, + }; +} + +describe("handleChatEvent", () => { + it("returns null when payload is missing", () => { + const state = createState(); + expect(handleChatEvent(state, undefined)).toBe(null); + }); + + it("returns null when sessionKey does not match", () => { + const state = createState({ sessionKey: "main" }); + const payload: ChatEventPayload = { + runId: "run-1", + sessionKey: "other", + state: "final", + }; + expect(handleChatEvent(state, payload)).toBe(null); + }); + + it("returns null for delta from another run", () => { + const state = createState({ + sessionKey: "main", + chatRunId: "run-user", + chatStream: "Hello", + }); + const payload: ChatEventPayload = { + runId: "run-announce", + sessionKey: "main", + state: "delta", + message: { role: "assistant", content: [{ type: "text", text: "Done" }] }, + }; + expect(handleChatEvent(state, payload)).toBe(null); + expect(state.chatRunId).toBe("run-user"); + expect(state.chatStream).toBe("Hello"); + }); + + it("returns 'final' for final from another run (e.g. sub-agent announce) without clearing state", () => { + const state = createState({ + sessionKey: "main", + chatRunId: "run-user", + chatStream: "Working...", + chatStreamStartedAt: 123, + }); + const payload: ChatEventPayload = { + runId: "run-announce", + sessionKey: "main", + state: "final", + message: { + role: "assistant", + content: [{ type: "text", text: "Sub-agent findings" }], + }, + }; + expect(handleChatEvent(state, payload)).toBe("final"); + expect(state.chatRunId).toBe("run-user"); + expect(state.chatStream).toBe("Working..."); + expect(state.chatStreamStartedAt).toBe(123); + }); + + it("processes final from own run and clears state", () => { + const state = createState({ + sessionKey: "main", + chatRunId: "run-1", + chatStream: "Reply", + chatStreamStartedAt: 100, + }); + const payload: ChatEventPayload = { + runId: "run-1", + sessionKey: "main", + state: "final", + }; + expect(handleChatEvent(state, payload)).toBe("final"); + expect(state.chatRunId).toBe(null); + expect(state.chatStream).toBe(null); + expect(state.chatStreamStartedAt).toBe(null); + }); +}); diff --git a/ui/src/ui/controllers/chat.ts b/ui/src/ui/controllers/chat.ts index 53027c6ea..6a3e68175 100644 --- a/ui/src/ui/controllers/chat.ts +++ b/ui/src/ui/controllers/chat.ts @@ -1,6 +1,7 @@ -import type { GatewayBrowserClient } from "../gateway"; import { extractText } from "../chat/message-extract"; +import type { GatewayBrowserClient } from "../gateway"; import { generateUUID } from "../uuid"; +import type { ChatAttachment } from "../ui-types"; export type ChatState = { client: GatewayBrowserClient | null; @@ -11,6 +12,7 @@ export type ChatState = { chatThinkingLevel: string | null; chatSending: boolean; chatMessage: string; + chatAttachments: ChatAttachment[]; chatRunId: string | null; chatStream: string | null; chatStreamStartedAt: number | null; @@ -43,17 +45,44 @@ export async function loadChatHistory(state: ChatState) { } } -export async function sendChatMessage(state: ChatState, message: string): Promise { +function dataUrlToBase64(dataUrl: string): { content: string; mimeType: string } | null { + const match = /^data:([^;]+);base64,(.+)$/.exec(dataUrl); + if (!match) return null; + return { mimeType: match[1], content: match[2] }; +} + +export async function sendChatMessage( + state: ChatState, + message: string, + attachments?: ChatAttachment[], +): Promise { if (!state.client || !state.connected) return false; const msg = message.trim(); - if (!msg) return false; + const hasAttachments = attachments && attachments.length > 0; + if (!msg && !hasAttachments) return false; const now = Date.now(); + + // Build user message content blocks + const contentBlocks: Array<{ type: string; text?: string; source?: unknown }> = []; + if (msg) { + contentBlocks.push({ type: "text", text: msg }); + } + // Add image previews to the message for display + if (hasAttachments) { + for (const att of attachments) { + contentBlocks.push({ + type: "image", + source: { type: "base64", media_type: att.mimeType, data: att.dataUrl }, + }); + } + } + state.chatMessages = [ ...state.chatMessages, { role: "user", - content: [{ type: "text", text: msg }], + content: contentBlocks, timestamp: now, }, ]; @@ -64,12 +93,29 @@ export async function sendChatMessage(state: ChatState, message: string): Promis state.chatRunId = runId; state.chatStream = ""; state.chatStreamStartedAt = now; + + // Convert attachments to API format + const apiAttachments = hasAttachments + ? attachments + .map((att) => { + const parsed = dataUrlToBase64(att.dataUrl); + if (!parsed) return null; + return { + type: "image", + mimeType: parsed.mimeType, + content: parsed.content, + }; + }) + .filter((a): a is NonNullable => a !== null) + : undefined; + try { await state.client.request("chat.send", { sessionKey: state.sessionKey, message: msg, deliver: false, idempotencyKey: runId, + attachments: apiAttachments, }); return true; } catch (err) { @@ -115,8 +161,17 @@ export function handleChatEvent( ) { if (!payload) return null; if (payload.sessionKey !== state.sessionKey) return null; - if (payload.runId && state.chatRunId && payload.runId !== state.chatRunId) + + // Final from another run (e.g. sub-agent announce): refresh history to show new message. + // See https://github.com/moltbot/moltbot/issues/1909 + if ( + payload.runId && + state.chatRunId && + payload.runId !== state.chatRunId + ) { + if (payload.state === "final") return "final"; return null; + } if (payload.state === "delta") { const next = extractText(payload.message); diff --git a/ui/src/ui/controllers/config/form-utils.ts b/ui/src/ui/controllers/config/form-utils.ts index dea4d7f61..fd40bb5ac 100644 --- a/ui/src/ui/controllers/config/form-utils.ts +++ b/ui/src/ui/controllers/config/form-utils.ts @@ -74,4 +74,3 @@ export function removePathValue( delete (current as Record)[lastKey]; } } - diff --git a/ui/src/ui/controllers/debug.ts b/ui/src/ui/controllers/debug.ts index 78993a3af..5aa1eec43 100644 --- a/ui/src/ui/controllers/debug.ts +++ b/ui/src/ui/controllers/debug.ts @@ -54,4 +54,3 @@ export async function callDebugMethod(state: DebugState) { state.debugCallError = String(err); } } - diff --git a/ui/src/ui/controllers/presence.ts b/ui/src/ui/controllers/presence.ts index 4154307b1..67ac2761d 100644 --- a/ui/src/ui/controllers/presence.ts +++ b/ui/src/ui/controllers/presence.ts @@ -33,4 +33,3 @@ export async function loadPresence(state: PresenceState) { state.presenceLoading = false; } } - diff --git a/ui/src/ui/device-auth.ts b/ui/src/ui/device-auth.ts index 693479ae7..588c574a8 100644 --- a/ui/src/ui/device-auth.ts +++ b/ui/src/ui/device-auth.ts @@ -11,7 +11,7 @@ type DeviceAuthStore = { tokens: Record; }; -const STORAGE_KEY = "clawdbot.device.auth.v1"; +const STORAGE_KEY = "moltbot.device.auth.v1"; function normalizeRole(role: string): string { return role.trim(); diff --git a/ui/src/ui/device-identity.ts b/ui/src/ui/device-identity.ts index 4ad7ceec0..dd2eccb8b 100644 --- a/ui/src/ui/device-identity.ts +++ b/ui/src/ui/device-identity.ts @@ -14,7 +14,7 @@ export type DeviceIdentity = { privateKey: string; }; -const STORAGE_KEY = "clawdbot-device-identity-v1"; +const STORAGE_KEY = "moltbot-device-identity-v1"; function base64UrlEncode(bytes: Uint8Array): string { let binary = ""; diff --git a/ui/src/ui/focus-mode.browser.test.ts b/ui/src/ui/focus-mode.browser.test.ts index ba81456bc..157f8a0ab 100644 --- a/ui/src/ui/focus-mode.browser.test.ts +++ b/ui/src/ui/focus-mode.browser.test.ts @@ -1,18 +1,18 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; -import { ClawdbotApp } from "./app"; +import { MoltbotApp } from "./app"; -const originalConnect = ClawdbotApp.prototype.connect; +const originalConnect = MoltbotApp.prototype.connect; function mountApp(pathname: string) { window.history.replaceState({}, "", pathname); - const app = document.createElement("clawdbot-app") as ClawdbotApp; + const app = document.createElement("moltbot-app") as MoltbotApp; document.body.append(app); return app; } beforeEach(() => { - ClawdbotApp.prototype.connect = () => { + MoltbotApp.prototype.connect = () => { // no-op: avoid real gateway WS connections in browser tests }; window.__CLAWDBOT_CONTROL_UI_BASE_PATH__ = undefined; @@ -21,7 +21,7 @@ beforeEach(() => { }); afterEach(() => { - ClawdbotApp.prototype.connect = originalConnect; + MoltbotApp.prototype.connect = originalConnect; window.__CLAWDBOT_CONTROL_UI_BASE_PATH__ = undefined; localStorage.clear(); document.body.innerHTML = ""; diff --git a/ui/src/ui/format.test.ts b/ui/src/ui/format.test.ts index d7acecebb..f8b1e8e56 100644 --- a/ui/src/ui/format.test.ts +++ b/ui/src/ui/format.test.ts @@ -39,4 +39,3 @@ describe("stripThinkingTags", () => { expect(stripThinkingTags("Hello")).toBe("Hello"); }); }); - diff --git a/ui/src/ui/icons.ts b/ui/src/ui/icons.ts index 4803f223e..eaf8f0e27 100644 --- a/ui/src/ui/icons.ts +++ b/ui/src/ui/icons.ts @@ -1,7 +1,59 @@ import { html, type TemplateResult } from "lit"; -export function renderEmojiIcon(icon: string, className: string): TemplateResult { - return html``; +// Lucide-style SVG icons +// All icons use currentColor for stroke + +export const icons = { + // Navigation icons + messageSquare: html``, + barChart: html``, + link: html``, + radio: html``, + fileText: html``, + zap: html``, + monitor: html``, + settings: html``, + bug: html``, + scrollText: html``, + folder: html``, + + // UI icons + menu: html``, + x: html``, + check: html``, + copy: html``, + search: html``, + brain: html``, + book: html``, + loader: html``, + + // Tool icons + wrench: html``, + fileCode: html``, + edit: html``, + penLine: html``, + paperclip: html``, + globe: html``, + image: html``, + smartphone: html``, + plug: html``, + circle: html``, + puzzle: html``, +} as const; + +export type IconName = keyof typeof icons; + +export function icon(name: IconName): TemplateResult { + return icons[name]; +} + +export function renderIcon(name: IconName, className = "nav-item__icon"): TemplateResult { + return html``; +} + +// Legacy function for compatibility +export function renderEmojiIcon(iconContent: string | TemplateResult, className: string): TemplateResult { + return html``; } export function setEmojiIcon(target: HTMLElement | null, icon: string): void { diff --git a/ui/src/ui/markdown.test.ts b/ui/src/ui/markdown.test.ts index da2c4aca0..396ff0fa5 100644 --- a/ui/src/ui/markdown.test.ts +++ b/ui/src/ui/markdown.test.ts @@ -30,4 +30,3 @@ describe("toSanitizedMarkdownHtml", () => { expect(html).toContain("console.log(1)"); }); }); - diff --git a/ui/src/ui/navigation.browser.test.ts b/ui/src/ui/navigation.browser.test.ts index 0139ec552..7a021e64f 100644 --- a/ui/src/ui/navigation.browser.test.ts +++ b/ui/src/ui/navigation.browser.test.ts @@ -1,13 +1,13 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; -import { ClawdbotApp } from "./app"; +import { MoltbotApp } from "./app"; import "../styles.css"; -const originalConnect = ClawdbotApp.prototype.connect; +const originalConnect = MoltbotApp.prototype.connect; function mountApp(pathname: string) { window.history.replaceState({}, "", pathname); - const app = document.createElement("clawdbot-app") as ClawdbotApp; + const app = document.createElement("moltbot-app") as MoltbotApp; document.body.append(app); return app; } @@ -19,7 +19,7 @@ function nextFrame() { } beforeEach(() => { - ClawdbotApp.prototype.connect = () => { + MoltbotApp.prototype.connect = () => { // no-op: avoid real gateway WS connections in browser tests }; window.__CLAWDBOT_CONTROL_UI_BASE_PATH__ = undefined; @@ -28,7 +28,7 @@ beforeEach(() => { }); afterEach(() => { - ClawdbotApp.prototype.connect = originalConnect; + MoltbotApp.prototype.connect = originalConnect; window.__CLAWDBOT_CONTROL_UI_BASE_PATH__ = undefined; localStorage.clear(); document.body.innerHTML = ""; @@ -53,22 +53,22 @@ describe("control UI routing", () => { }); it("infers nested base paths", async () => { - const app = mountApp("/apps/clawdbot/cron"); + const app = mountApp("/apps/moltbot/cron"); await app.updateComplete; - expect(app.basePath).toBe("/apps/clawdbot"); + expect(app.basePath).toBe("/apps/moltbot"); expect(app.tab).toBe("cron"); - expect(window.location.pathname).toBe("/apps/clawdbot/cron"); + expect(window.location.pathname).toBe("/apps/moltbot/cron"); }); it("honors explicit base path overrides", async () => { - window.__CLAWDBOT_CONTROL_UI_BASE_PATH__ = "/clawdbot"; - const app = mountApp("/clawdbot/sessions"); + window.__CLAWDBOT_CONTROL_UI_BASE_PATH__ = "/moltbot"; + const app = mountApp("/moltbot/sessions"); await app.updateComplete; - expect(app.basePath).toBe("/clawdbot"); + expect(app.basePath).toBe("/moltbot"); expect(app.tab).toBe("sessions"); - expect(window.location.pathname).toBe("/clawdbot/sessions"); + expect(window.location.pathname).toBe("/moltbot/sessions"); }); it("updates the URL when clicking nav items", async () => { @@ -169,7 +169,7 @@ describe("control UI routing", () => { it("hydrates token from URL params even when settings already set", async () => { localStorage.setItem( - "clawdbot.control.settings.v1", + "moltbot.control.settings.v1", JSON.stringify({ token: "existing-token" }), ); const app = mountApp("/ui/overview?token=abc123"); diff --git a/ui/src/ui/navigation.test.ts b/ui/src/ui/navigation.test.ts index 168125d71..7b15deb4a 100644 --- a/ui/src/ui/navigation.test.ts +++ b/ui/src/ui/navigation.test.ts @@ -73,7 +73,7 @@ describe("subtitleForTab", () => { it("returns descriptive subtitles", () => { expect(subtitleForTab("chat")).toContain("chat session"); - expect(subtitleForTab("config")).toContain("clawdbot.json"); + expect(subtitleForTab("config")).toContain("moltbot.json"); }); }); @@ -95,7 +95,7 @@ describe("normalizeBasePath", () => { }); it("handles nested paths", () => { - expect(normalizeBasePath("/apps/clawdbot")).toBe("/apps/clawdbot"); + expect(normalizeBasePath("/apps/moltbot")).toBe("/apps/moltbot"); }); }); @@ -122,7 +122,7 @@ describe("pathForTab", () => { it("prepends base path", () => { expect(pathForTab("chat", "/ui")).toBe("/ui/chat"); - expect(pathForTab("sessions", "/apps/clawdbot")).toBe("/apps/clawdbot/sessions"); + expect(pathForTab("sessions", "/apps/moltbot")).toBe("/apps/moltbot/sessions"); }); }); @@ -139,7 +139,7 @@ describe("tabFromPath", () => { it("handles base paths", () => { expect(tabFromPath("/ui/chat", "/ui")).toBe("chat"); - expect(tabFromPath("/apps/clawdbot/sessions", "/apps/clawdbot")).toBe("sessions"); + expect(tabFromPath("/apps/moltbot/sessions", "/apps/moltbot")).toBe("sessions"); }); it("returns null for unknown path", () => { @@ -164,7 +164,7 @@ describe("inferBasePathFromPathname", () => { it("infers base path from nested paths", () => { expect(inferBasePathFromPathname("/ui/chat")).toBe("/ui"); - expect(inferBasePathFromPathname("/apps/clawdbot/sessions")).toBe("/apps/clawdbot"); + expect(inferBasePathFromPathname("/apps/moltbot/sessions")).toBe("/apps/moltbot"); }); it("handles index.html suffix", () => { diff --git a/ui/src/ui/navigation.ts b/ui/src/ui/navigation.ts index 623764a8f..966abec96 100644 --- a/ui/src/ui/navigation.ts +++ b/ui/src/ui/navigation.ts @@ -1,3 +1,5 @@ +import type { IconName } from "./icons.js"; + export const TAB_GROUPS = [ { label: "Chat", tabs: ["chat"] }, { @@ -98,32 +100,32 @@ export function inferBasePathFromPathname(pathname: string): string { return `/${segments.join("/")}`; } -export function iconForTab(tab: Tab): string { +export function iconForTab(tab: Tab): IconName { switch (tab) { case "chat": - return "💬"; + return "messageSquare"; case "overview": - return "📊"; + return "barChart"; case "channels": - return "🔗"; + return "link"; case "instances": - return "📡"; + return "radio"; case "sessions": - return "📄"; + return "fileText"; case "cron": - return "⏰"; + return "loader"; case "skills": - return "⚡️"; + return "zap"; case "nodes": - return "🖥️"; + return "monitor"; case "config": - return "⚙️"; + return "settings"; case "debug": - return "🐞"; + return "bug"; case "logs": - return "🧾"; + return "scrollText"; default: - return "📁"; + return "folder"; } } @@ -175,7 +177,7 @@ export function subtitleForTab(tab: Tab) { case "chat": return "Direct gateway chat session for quick interventions."; case "config": - return "Edit ~/.clawdbot/clawdbot.json safely."; + return "Edit ~/.clawdbot/moltbot.json safely."; case "debug": return "Gateway snapshots, events, and manual RPC calls."; case "logs": diff --git a/ui/src/ui/presenter.ts b/ui/src/ui/presenter.ts index 9f3df3dcb..ddb99d9c5 100644 --- a/ui/src/ui/presenter.ts +++ b/ui/src/ui/presenter.ts @@ -55,4 +55,3 @@ export function formatCronPayload(job: CronJob) { if (p.kind === "systemEvent") return `System: ${p.text}`; return `Agent: ${p.message}`; } - diff --git a/ui/src/ui/storage.ts b/ui/src/ui/storage.ts index fc410c225..4b1836bfb 100644 --- a/ui/src/ui/storage.ts +++ b/ui/src/ui/storage.ts @@ -1,4 +1,4 @@ -const KEY = "clawdbot.control.settings.v1"; +const KEY = "moltbot.control.settings.v1"; import type { ThemeMode } from "./theme"; diff --git a/ui/src/ui/tool-display.json b/ui/src/ui/tool-display.json index 1b978b4ae..4a6bd0524 100644 --- a/ui/src/ui/tool-display.json +++ b/ui/src/ui/tool-display.json @@ -1,7 +1,7 @@ { "version": 1, "fallback": { - "emoji": "🧩", + "icon": "puzzle", "detailKeys": [ "command", "path", @@ -26,37 +26,37 @@ }, "tools": { "bash": { - "emoji": "🛠️", + "icon": "wrench", "title": "Bash", "detailKeys": ["command"] }, "process": { - "emoji": "🧰", + "icon": "wrench", "title": "Process", "detailKeys": ["sessionId"] }, "read": { - "emoji": "📖", + "icon": "fileText", "title": "Read", "detailKeys": ["path"] }, "write": { - "emoji": "✍️", + "icon": "edit", "title": "Write", "detailKeys": ["path"] }, "edit": { - "emoji": "📝", + "icon": "penLine", "title": "Edit", "detailKeys": ["path"] }, "attach": { - "emoji": "📎", + "icon": "paperclip", "title": "Attach", "detailKeys": ["path", "url", "fileName"] }, "browser": { - "emoji": "🌐", + "icon": "globe", "title": "Browser", "actions": { "status": { "label": "status" }, @@ -95,7 +95,7 @@ } }, "canvas": { - "emoji": "🖼️", + "icon": "image", "title": "Canvas", "actions": { "present": { "label": "present", "detailKeys": ["target", "node", "nodeId"] }, @@ -108,7 +108,7 @@ } }, "nodes": { - "emoji": "📱", + "icon": "smartphone", "title": "Nodes", "actions": { "status": { "label": "status" }, @@ -127,7 +127,7 @@ } }, "cron": { - "emoji": "⏰", + "icon": "loader", "title": "Cron", "actions": { "status": { "label": "status" }, @@ -144,7 +144,7 @@ } }, "gateway": { - "emoji": "🔌", + "icon": "plug", "title": "Gateway", "actions": { "restart": { "label": "restart", "detailKeys": ["reason", "delayMs"] }, @@ -161,7 +161,7 @@ } }, "whatsapp_login": { - "emoji": "🟢", + "icon": "circle", "title": "WhatsApp Login", "actions": { "start": { "label": "start" }, @@ -169,7 +169,7 @@ } }, "discord": { - "emoji": "💬", + "icon": "messageSquare", "title": "Discord", "actions": { "react": { "label": "react", "detailKeys": ["channelId", "messageId", "emoji"] }, @@ -204,7 +204,7 @@ } }, "slack": { - "emoji": "💬", + "icon": "messageSquare", "title": "Slack", "actions": { "react": { "label": "react", "detailKeys": ["channelId", "messageId", "emoji"] }, diff --git a/ui/src/ui/tool-display.ts b/ui/src/ui/tool-display.ts index 02c54b457..4b2de6ecb 100644 --- a/ui/src/ui/tool-display.ts +++ b/ui/src/ui/tool-display.ts @@ -1,4 +1,5 @@ import rawConfig from "./tool-display.json"; +import type { IconName } from "./icons"; type ToolDisplayActionSpec = { label?: string; @@ -6,7 +7,7 @@ type ToolDisplayActionSpec = { }; type ToolDisplaySpec = { - emoji?: string; + icon?: string; title?: string; label?: string; detailKeys?: string[]; @@ -21,7 +22,7 @@ type ToolDisplayConfig = { export type ToolDisplay = { name: string; - emoji: string; + icon: IconName; title: string; label: string; verb?: string; @@ -29,7 +30,7 @@ export type ToolDisplay = { }; const TOOL_DISPLAY_CONFIG = rawConfig as ToolDisplayConfig; -const FALLBACK = TOOL_DISPLAY_CONFIG.fallback ?? { emoji: "🧩" }; +const FALLBACK = TOOL_DISPLAY_CONFIG.fallback ?? { icon: "puzzle" }; const TOOL_MAP = TOOL_DISPLAY_CONFIG.tools ?? {}; function normalizeToolName(name?: string): string { @@ -135,7 +136,7 @@ export function resolveToolDisplay(params: { const name = normalizeToolName(params.name); const key = name.toLowerCase(); const spec = TOOL_MAP[key]; - const emoji = spec?.emoji ?? FALLBACK.emoji ?? "🧩"; + const icon = (spec?.icon ?? FALLBACK.icon ?? "puzzle") as IconName; const title = spec?.title ?? defaultTitle(name); const label = spec?.label ?? name; const actionRaw = @@ -168,7 +169,7 @@ export function resolveToolDisplay(params: { return { name, - emoji, + icon, title, label, verb, @@ -186,9 +187,7 @@ export function formatToolDetail(display: ToolDisplay): string | undefined { export function formatToolSummary(display: ToolDisplay): string { const detail = formatToolDetail(display); - return detail - ? `${display.emoji} ${display.label}: ${detail}` - : `${display.emoji} ${display.label}`; + return detail ? `${display.label}: ${detail}` : display.label; } function shortenHomeInString(input: string): string { diff --git a/ui/src/ui/ui-types.ts b/ui/src/ui/ui-types.ts index 428c4c381..196d6d114 100644 --- a/ui/src/ui/ui-types.ts +++ b/ui/src/ui/ui-types.ts @@ -1,7 +1,14 @@ +export type ChatAttachment = { + id: string; + dataUrl: string; + mimeType: string; +}; + export type ChatQueueItem = { id: string; text: string; createdAt: number; + attachments?: ChatAttachment[]; }; export const CRON_CHANNEL_LAST = "last"; diff --git a/ui/src/ui/uuid.test.ts b/ui/src/ui/uuid.test.ts index 62856f1b6..2d5421bdd 100644 --- a/ui/src/ui/uuid.test.ts +++ b/ui/src/ui/uuid.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { generateUUID } from "./uuid"; @@ -26,8 +26,13 @@ describe("generateUUID", () => { }); it("still returns a v4 UUID when crypto is missing", () => { - const id = generateUUID(null); - expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/); + const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); + try { + const id = generateUUID(null); + expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/); + expect(warnSpy).toHaveBeenCalled(); + } finally { + warnSpy.mockRestore(); + } }); }); - diff --git a/ui/src/ui/uuid.ts b/ui/src/ui/uuid.ts index 7124dbb8f..7c927cda1 100644 --- a/ui/src/ui/uuid.ts +++ b/ui/src/ui/uuid.ts @@ -3,6 +3,8 @@ export type CryptoLike = { getRandomValues?: ((array: Uint8Array) => Uint8Array) | undefined; }; +let warnedWeakCrypto = false; + function uuidFromBytes(bytes: Uint8Array): string { bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4 bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 1 @@ -29,6 +31,12 @@ function weakRandomBytes(): Uint8Array { return bytes; } +function warnWeakCryptoOnce() { + if (warnedWeakCrypto) return; + warnedWeakCrypto = true; + console.warn("[uuid] crypto API missing; falling back to weak randomness"); +} + export function generateUUID(cryptoLike: CryptoLike | null = globalThis.crypto): string { if (cryptoLike && typeof cryptoLike.randomUUID === "function") return cryptoLike.randomUUID(); @@ -38,6 +46,6 @@ export function generateUUID(cryptoLike: CryptoLike | null = globalThis.crypto): return uuidFromBytes(bytes); } + warnWeakCryptoOnce(); return uuidFromBytes(weakRandomBytes()); } - diff --git a/ui/src/ui/views/channels.shared.ts b/ui/src/ui/views/channels.shared.ts index 6238a1e1c..9af0c2ea1 100644 --- a/ui/src/ui/views/channels.shared.ts +++ b/ui/src/ui/views/channels.shared.ts @@ -43,4 +43,3 @@ export function renderChannelAccountCount( if (count < 2) return nothing; return html``; } - diff --git a/ui/src/ui/views/channels.whatsapp.ts b/ui/src/ui/views/channels.whatsapp.ts index b40a533c6..eae3be695 100644 --- a/ui/src/ui/views/channels.whatsapp.ts +++ b/ui/src/ui/views/channels.whatsapp.ts @@ -116,4 +116,3 @@ export function renderWhatsAppCard(params: {
`; } - diff --git a/ui/src/ui/views/chat.test.ts b/ui/src/ui/views/chat.test.ts index 6cd469558..ac68f2494 100644 --- a/ui/src/ui/views/chat.test.ts +++ b/ui/src/ui/views/chat.test.ts @@ -37,7 +37,7 @@ function createProps(overrides: Partial = {}): ChatProps { error: null, sessions: createSessions(), focusMode: false, - assistantName: "Clawdbot", + assistantName: "Moltbot", assistantAvatar: null, onRefresh: () => undefined, onToggleFocusMode: () => undefined, diff --git a/ui/src/ui/views/chat.ts b/ui/src/ui/views/chat.ts index 677c2a183..f5fb6e80b 100644 --- a/ui/src/ui/views/chat.ts +++ b/ui/src/ui/views/chat.ts @@ -1,8 +1,10 @@ import { html, nothing } from "lit"; +import { ref } from "lit/directives/ref.js"; import { repeat } from "lit/directives/repeat.js"; import type { SessionsListResult } from "../types"; -import type { ChatQueueItem } from "../ui-types"; +import type { ChatAttachment, ChatQueueItem } from "../ui-types"; import type { ChatItem, MessageGroup } from "../types/chat-types"; +import { icons } from "../icons"; import { normalizeMessage, normalizeRoleForGrouping, @@ -51,6 +53,9 @@ export type ChatProps = { splitRatio?: number; assistantName: string; assistantAvatar: string | null; + // Image attachments + attachments?: ChatAttachment[]; + onAttachmentsChange?: (attachments: ChatAttachment[]) => void; // Event handlers onRefresh: () => void; onToggleFocusMode: () => void; @@ -67,33 +72,114 @@ export type ChatProps = { const COMPACTION_TOAST_DURATION_MS = 5000; +function adjustTextareaHeight(el: HTMLTextAreaElement) { + el.style.height = "auto"; + el.style.height = `${el.scrollHeight}px`; +} + function renderCompactionIndicator(status: CompactionIndicatorStatus | null | undefined) { if (!status) return nothing; - + // Show "compacting..." while active if (status.active) { return html`
- 🧹 Compacting context... + ${icons.loader} Compacting context...
`; } - + // Show "compaction complete" briefly after completion if (status.completedAt) { const elapsed = Date.now() - status.completedAt; if (elapsed < COMPACTION_TOAST_DURATION_MS) { return html`
- 🧹 Context compacted + ${icons.check} Context compacted
`; } } - + return nothing; } +function generateAttachmentId(): string { + return `att-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; +} + +function handlePaste( + e: ClipboardEvent, + props: ChatProps, +) { + const items = e.clipboardData?.items; + if (!items || !props.onAttachmentsChange) return; + + const imageItems: DataTransferItem[] = []; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + if (item.type.startsWith("image/")) { + imageItems.push(item); + } + } + + if (imageItems.length === 0) return; + + e.preventDefault(); + + for (const item of imageItems) { + const file = item.getAsFile(); + if (!file) continue; + + const reader = new FileReader(); + reader.onload = () => { + const dataUrl = reader.result as string; + const newAttachment: ChatAttachment = { + id: generateAttachmentId(), + dataUrl, + mimeType: file.type, + }; + const current = props.attachments ?? []; + props.onAttachmentsChange?.([...current, newAttachment]); + }; + reader.readAsDataURL(file); + } +} + +function renderAttachmentPreview(props: ChatProps) { + const attachments = props.attachments ?? []; + if (attachments.length === 0) return nothing; + + return html` +
+ ${attachments.map( + (att) => html` +
+ Attachment preview + +
+ `, + )} +
+ `; +} + export function renderChat(props: ChatProps) { const canCompose = props.connected; const isBusy = props.sending || props.stream !== null; @@ -108,8 +194,11 @@ export function renderChat(props: ChatProps) { avatar: props.assistantAvatar ?? props.assistantAvatarUrl ?? null, }; + const hasAttachments = (props.attachments?.length ?? 0) > 0; const composePlaceholder = props.connected - ? "Message (↩ to send, Shift+↩ for line breaks)" + ? hasAttachments + ? "Add a message or paste more images..." + : "Message (↩ to send, Shift+↩ for line breaks, paste images)" : "Connect to the gateway to start chatting…"; const splitRatio = props.splitRatio ?? 0.6; @@ -171,7 +260,7 @@ export function renderChat(props: ChatProps) { aria-label="Exit focus mode" title="Exit focus mode" > - ✕ + ${icons.x} ` : nothing} @@ -216,14 +305,19 @@ export function renderChat(props: ChatProps) { ${props.queue.map( (item) => html`
-
${item.text}
+
+ ${item.text || + (item.attachments?.length + ? `Image (${item.attachments.length})` + : "")} +
`, @@ -234,39 +328,47 @@ export function renderChat(props: ChatProps) { : nothing}
- -
- - + ${renderAttachmentPreview(props)} +
+ +
+ + +
diff --git a/ui/src/ui/views/config-form.node.ts b/ui/src/ui/views/config-form.node.ts index 07cb9f239..9d121d7f1 100644 --- a/ui/src/ui/views/config-form.node.ts +++ b/ui/src/ui/views/config-form.node.ts @@ -120,7 +120,7 @@ export function renderNode(params: { const hasString = normalizedTypes.has("string"); const hasNumber = normalizedTypes.has("number"); const hasBoolean = normalizedTypes.has("boolean"); - + if (hasBoolean && normalizedTypes.size === 1) { return renderNode({ ...params, @@ -383,14 +383,14 @@ function renderObject(params: { const hint = hintForPath(path, hints); const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1))); const help = hint?.help ?? schema.description; - + const fallback = value ?? schema.default; const obj = fallback && typeof fallback === "object" && !Array.isArray(fallback) ? (fallback as Record) : {}; const props = schema.properties ?? {}; const entries = Object.entries(props); - + // Sort by hint order const sorted = entries.sort((a, b) => { const orderA = hintForPath([...path, a[0]], hints)?.order ?? 0; @@ -514,7 +514,7 @@ function renderArray(params: {
${help ? html`
${help}
` : nothing} - + ${arr.length === 0 ? html`
No items yet. Click "Add" to create one. @@ -597,7 +597,7 @@ function renderMapField(params: { Add Entry
- + ${entries.length === 0 ? html`
No custom entries.
` : html` diff --git a/ui/src/ui/views/config-form.render.ts b/ui/src/ui/views/config-form.render.ts index da8d38d8e..2e7dc5f4e 100644 --- a/ui/src/ui/views/config-form.render.ts +++ b/ui/src/ui/views/config-form.render.ts @@ -1,5 +1,6 @@ import { html, nothing } from "lit"; import type { ConfigUiHints } from "../types"; +import { icons } from "../icons"; import { hintForPath, humanize, @@ -93,16 +94,16 @@ function matchesSearch(key: string, schema: JsonSchema, query: string): boolean if (!query) return true; const q = query.toLowerCase(); const meta = SECTION_META[key]; - + // Check key name if (key.toLowerCase().includes(q)) return true; - + // Check label and description if (meta) { if (meta.label.toLowerCase().includes(q)) return true; if (meta.description.toLowerCase().includes(q)) return true; } - + return schemaMatches(schema, q); } @@ -189,10 +190,10 @@ export function renderConfigForm(props: ConfigFormProps) { if (filteredEntries.length === 0) { return html`
-
🔍
+
${icons.search}
- ${searchQuery - ? `No settings match "${searchQuery}"` + ${searchQuery + ? `No settings match "${searchQuery}"` : "No settings in this section"}
diff --git a/ui/src/ui/views/config-form.shared.ts b/ui/src/ui/views/config-form.shared.ts index b37969a93..a6a8e2416 100644 --- a/ui/src/ui/views/config-form.shared.ts +++ b/ui/src/ui/views/config-form.shared.ts @@ -89,4 +89,3 @@ export function isSensitivePath(path: Array): boolean { key.endsWith("key") ); } - diff --git a/ui/src/ui/views/config-form.ts b/ui/src/ui/views/config-form.ts index 0bcfe0a9c..146436ea6 100644 --- a/ui/src/ui/views/config-form.ts +++ b/ui/src/ui/views/config-form.ts @@ -5,4 +5,3 @@ export { } from "./config-form.analyze"; export { renderNode } from "./config-form.node"; export { schemaType, type JsonSchema } from "./config-form.shared"; - diff --git a/ui/src/ui/views/config.ts b/ui/src/ui/views/config.ts index c45849d56..ff6f57f32 100644 --- a/ui/src/ui/views/config.ts +++ b/ui/src/ui/views/config.ts @@ -138,7 +138,7 @@ function computeDiff( ): Array<{ path: string; from: unknown; to: unknown }> { if (!original || !current) return []; const changes: Array<{ path: string; from: unknown; to: unknown }> = []; - + function compare(orig: unknown, curr: unknown, path: string) { if (orig === curr) return; if (typeof orig !== typeof curr) { @@ -164,7 +164,7 @@ function computeDiff( compare(origObj[key], currObj[key], path ? `${path}.${key}` : key); } } - + compare(original, current, ""); return changes; } @@ -258,7 +258,7 @@ export function renderConfig(props: ConfigProps) {
Settings
${validity}
- + - + - + - +
@@ -358,7 +358,7 @@ export function renderConfig(props: ConfigProps) {
- + ${hasChanges && props.formMode === "form" ? html`
diff --git a/ui/src/ui/views/debug.ts b/ui/src/ui/views/debug.ts index 35e2e1af2..822c508a1 100644 --- a/ui/src/ui/views/debug.ts +++ b/ui/src/ui/views/debug.ts @@ -21,6 +21,22 @@ export type DebugProps = { }; export function renderDebug(props: DebugProps) { + const securityAudit = + props.status && typeof props.status === "object" + ? (props.status as { securityAudit?: { summary?: Record } }).securityAudit + : null; + const securitySummary = securityAudit?.summary ?? null; + const critical = securitySummary?.critical ?? 0; + const warn = securitySummary?.warn ?? 0; + const info = securitySummary?.info ?? 0; + const securityTone = critical > 0 ? "danger" : warn > 0 ? "warn" : "success"; + const securityLabel = + critical > 0 + ? `${critical} critical` + : warn > 0 + ? `${warn} warnings` + : "No critical issues"; + return html`
@@ -36,6 +52,12 @@ export function renderDebug(props: DebugProps) {
Status
+ ${securitySummary + ? html`
+ Security audit: ${securityLabel}${info > 0 ? ` · ${info} info` : ""}. Run + moltbot security audit --deep for details. +
` + : nothing}
${JSON.stringify(props.status ?? {}, null, 2)}
diff --git a/ui/src/ui/views/markdown-sidebar.ts b/ui/src/ui/views/markdown-sidebar.ts index 4189efef3..828c524a3 100644 --- a/ui/src/ui/views/markdown-sidebar.ts +++ b/ui/src/ui/views/markdown-sidebar.ts @@ -1,6 +1,7 @@ import { html, nothing } from "lit"; import { unsafeHTML } from "lit/directives/unsafe-html.js"; +import { icons } from "../icons"; import { toSanitizedMarkdownHtml } from "../markdown"; export type MarkdownSidebarProps = { @@ -16,7 +17,7 @@ export function renderMarkdownSidebar(props: MarkdownSidebarProps) {