Commit Graph
1640 Commits
Author SHA1 Message Date
Duy /zuey/ 990fabf94b fix(config): persist shell deny group disables
Squash merge PR #112 after resolving the project changelog conflict with current dev. Local checks passed: config gateway tests, provider/http/tools deny-pattern tests, go build ./..., and go build -tags sqliteonly ./.... PR CI run 26704832350 passed release-versioning, go, and web.
2026-05-31 13:08:24 +07:00
Duy /zuey/ 9ab3d6dfe5 feat(tools): local-first document extraction for read_document
Squash merge PR #111 after resolving docs/changelog conflicts. Local checks covered tools/config and both Go builds; PR CI run 26704622503 passed release-versioning, go, and web.
2026-05-31 12:55:38 +07:00
Duy /zuey/ 80893a1e6c feat(skills): add selected skill downloads
Squash merge PR #110 after resolving dev changelog conflict. Local checks covered Go http/store, full web test/build; PR CI run 26704448786 passed release-versioning, go, and web.
2026-05-31 12:44:29 +07:00
Duy /zuey/ 8196680093 fix(packages): report system package record failures
Squash merge PR #108 after validation.
2026-05-31 12:33:28 +07:00
Duy /zuey/ 1d9436350d fix(discord): backfill thread history attachments
Squash merge PR #106 after resolving dev changelog conflict. PR CI run 26704244687 passed release-versioning, go, and web.
2026-05-31 12:33:15 +07:00
Duy /zuey/ ff0b3dec56 fix(ci): ship bundled skills in release tarball
Squash merge PR #105 after validation.
2026-05-31 12:24:43 +07:00
Duy /zuey/ df8a03df48 fix: allow skill_manage companion files
Squash merge PR #104 after validation.
2026-05-31 12:24:22 +07:00
Duy /zuey/ 484bcc7b71 fix: harden parallel tool scheduling
Squash merge PR #103 after validation.
2026-05-31 12:24:00 +07:00
Duy /zuey/ 4de94c1d7c fix(telegram): repair voice transcription routing
Squash merge PR #102 after resolving dev changelog conflict. PR CI run 26704061639 passed release-versioning, go, and web.
2026-05-31 12:23:40 +07:00
Duy /zuey/ cf66bda618 fix(rapidapi): add cron credential diagnostics
Squash merge PR #101 after resolving dev conflicts and fixing cron GetJob snapshot race. PR CI run 26703877225 passed release-versioning, go, and web.
2026-05-31 12:13:14 +07:00
Duy /zuey/ 4ed4eb9509 fix(sandbox): isolate tenant workspace mounts
Squash merge PR #100 after resolving changelog conflict with current dev. PR CI run 26703558537 passed release-versioning, go, and web.
2026-05-31 11:55:10 +07:00
Duy /zuey/ f0f39ce31f feat(channels): add human-like chat behavior
Squash merge PR #99 after resolving conflicts with current dev. PR CI run 26703381807 passed release-versioning, go, and web.
2026-05-31 11:44:56 +07:00
Duy /zuey/ 3569f8d681 fix(channels): load WhatsApp QR channel on create
Squash merge PR #98 in priority order after #97. Existing PR checks were green; branch workflow status will be tracked.
2026-05-31 11:33:41 +07:00
Duy /zuey/ 361f2abbf5 fix(packages): use writable scratch dir for github updates
Squash merge PR #97 after resolving changelog conflict with current dev. PR CI run 26703171763 passed release-versioning, go, and web.
2026-05-31 11:33:12 +07:00
Duy /zuey/ c4e0bd2be5 fix(cli-credentials): stabilize git preset creation
Squash merge PR #96 after PR #92. PR checks were green; dev beta CI will be monitored after merge.
2026-05-31 11:05:47 +07:00
Duy /zuey/ 1d75e38ac2 fix(ci): restrict zuey beta deploy repo
Squash merge PR #92 before the beta PR queue so dev push workflows use the corrected deploy repository guard.
2026-05-31 10:48:03 +07:00
Goon 5017f7ca5d test(ci): isolate beta version output env 2026-05-29 10:11:30 +07:00
Goon ea107aebc9 test(ci): guard semantic beta versioning 2026-05-29 10:10:07 +07:00
Goon a7b58c6933 fix(ci): push beta repair tags to origin 2026-05-29 09:35:14 +07:00
Goon 812adeec6a fix(ci): bump beta above latest stable release 2026-05-28 19:44:56 +07:00
Goon 6d0ed4eee2 test(integration): serialize abort provider leak checks 2026-05-28 18:57:23 +07:00
Duy /zuey/ f771cff77c fix(channels): coalesce multi-attachment inbounds (#63) (#90)
Implements 3 coalescing layers to handle rapid multi-attachment inbounds:
- Bus debouncer: delays inbound messages 1s, merges duplicates
- Web chat debouncer: buffers client-side inbound frames for batch RPC
- Telegram album aggregator: collects album members via AfterFunc+Stop timer

Drops media-bypass shortcut (forces 1s media floor). Aggregator enforces:
- AfterFunc+Stop timer discipline with ordered drain on stop
- 2-tuple (album_id, sender) keying for isolation
- Dual DoS caps: max 10 albums per sender, max 100 messages per album
- merged_message_ids dedup seeding across all 3 surfaces

Closes #63
2026-05-28 18:30:34 +07:00
Duy /zuey/ a591473546 feat(secure-cli): CLI credential adapters framework + git adapter (#82) (#89)
* feat(secure-cli): Phase 1 schema + storage delta (issue #82)

Adds `adapter_name` column to secure_cli_binaries and `credential_type` + `host_scope` columns to secure_cli_user_credentials. LookupByBinary LEFT JOIN now projects AdapterName, UserCredentialType, and UserHostScope. Extends SecureCLIStore with SetUserCredentialsTyped(ctx, binaryID, userID, env, credType, hostScope); legacy SetUserCredentials delegates to typed variant with nil/nil for backward compat.

PG migration 73 + RequiredSchemaVersion bumped to 73. SQLite incremental migrations (versions 39–41) + SchemaVersion 42. 7 SQLite + 6 PG integration tests covering schema, round-trip, NULL legacy, LookupByBinary projection.

Fixes #82

* feat(secure-cli): Phase 2 CredentialAdapter framework + passthrough (issue #82)

Adds CredentialAdapter interface + Injection{ArgvPrefix,Env,Cleanup,ScrubValues} struct. Registry resolves by name; falls back to passthrough for empty/unknown. passthroughAdapter is default no-op — all existing presets (gh/aws/gcloud/kubectl/terraform/gws) behave bit-for-bit identically.

Per-request WithScrubBag(ctx) + AddScrubValuesCtx + ScrubCredentialsCtx for multi-tenant secret isolation (replaces package-global slice). Hook in executeCredentialed between env merge and exec: resolve adapter by bin.AdapterName, reject non-passthrough in sandbox, call Prepare, splice ArgvPrefix, merge Env, defer Cleanup, register ScrubValues.

Audit log security.system_env_injection records adapter name + env key names + argv_prefix_len + sha256(host_scope) — NEVER values. CLIPreset.AdapterName field added; empty default for legacy presets.

12 unit tests covering passthrough no-op, registry fallback + nil safety, Injection shape, hashHostScope determinism, sortedKeys, scrub bag per-request isolation + concurrent + short-value guard.

Fixes #82

* feat(secure-cli): Phase 2b extensibility helpers + psql stub adapter (issue #82)

Proves CredentialAdapter framework generalizes beyond git. Adds materializeEphemeral(ctx, content, prefix) shared helper — 0600 tmpfile + idempotent atomic.Bool cleanup latch; memfd intentionally rejected (resolves "self" against child process).

Adds psqlAdapter consuming framework end-to-end via PGPASSFILE pattern; libpq-spec .pgpass escaping for `:` and `\`. Registers psql preset with AdapterName: "psql" (production UI for typed creds lands in v2).

Interface validation gate passes: Injection shape unchanged, hook is psql-agnostic, no special branch needed.

Tests: ephemeral write/cleanup/concurrent/zero-content; psql routing/content/escaping/error paths/registration.

Fixes #82

* feat(secure-cli): git adapter PAT + SSH implementation (issue #82)

Phase 3 — PAT path
- gitAdapter PAT branch via GIT_CONFIG_COUNT/KEY_0/VALUE_0 env (git 2.31+)
  so the token never lands on argv, .git/config, or remote URL
- Host-scope enforcement with IDN normalization (golang.org/x/net/idna)
  and embedded-userinfo rejection in URL parsing
- CVE-2018-17456 mitigation: resolve remote URLs via `git config --get`
  not `git remote get-url` to dodge ext::sh protocol handler injection
- Case-insensitive DenyArgs blocking `-c http.`, `-c credential.`,
  `-c core.sshcommand`, `config --global/--system`, `credential-helper`,
  bare `daemon`
- New WithExecCwd / ExecCwdFromContext context helpers — fixes a latent
  design gap where the adapter's pre-flight `git config --get` ran in
  goclaw's daemon CWD instead of the agent's repo
- 14 unit tests covering subcommand routing, host normalization,
  scp-form parsing, userinfo rejection, CRLF token rejection,
  CVE-2018-17456 regression, DenyArgs preset coverage
- 3 integration tests against a local TLS git-http-backend server
  proving end-to-end clone + fetch + host-mismatch rejection with
  zero token leakage into the cloned .git/config

Phase 4 — SSH path
- gitAdapter ssh_key branch materializes per-call 0600 tmpfile via
  the Phase 2b materializeEphemeral helper, injects GIT_SSH_COMMAND
  with -o IdentitiesOnly=yes -o BatchMode=yes
  -o StrictHostKeyChecking=accept-new, idempotent cleanup
- ValidateSSHKey using golang.org/x/crypto/ssh rejects
  passphrase-protected keys via ErrSSHKeyPassphraseUnsupported sentinel
- 8 unit tests covering passphrase rejection, env shape, cleanup
  lifecycle, host-mismatch reuse, malformed-blob rejection
- 3 integration tests proving tmpfile 0600 lifecycle, env propagation
  to child process, cleanup-on-exec-failure, no-orphan-on-rejection
- 2 new i18n keys (en/vi/zh) for SSH-passphrase and SSH-key-invalid

Verification: go vet clean, go build ./... clean,
go build -tags sqliteonly ./... clean, go test -race
./internal/tools/ pass, go test -tags integration
./tests/integration/ -run TestGitAdapter pass.

Refs #82

* feat(secure-cli): UI presets + typed credential HTTP path + i18n (issue #82)

Phase 5. Typed PUT envelope with {error:{code,message}, error_key} for
field-level errors. CliCredentialGitFields React component (PAT/SSH picker,
host_scope input, CRLF→LF normalization, masked-edit). 17 i18n keys × 3
locales (backend + frontend). i18n parity test.

* feat(secure-cli): audit log schema + adapter framework docs (issue #82)

Phase 6. emitSystemEnvInjectionAudit helper centralizes
security.system_env_injection slog with host_scope_hash (SHA-256 8 hex,
plaintext hostname omitted for PII safety). Audit shape pinned by
TestEmitSystemEnvInjectionAudit_*. New docs: git-credential-adapter.md
(user guide), credential-adapter-playbook.md (R1 implementer guide w/
kubectl/docker/npm/aws/psql worked mappings). 09-security.md § 14
trust-boundary diagram + SSH TOFU + SIGKILL caveats. 03-tools-system.md
§ 8a. Changelog entry.

* docs(journal): issue #82 CLI credential adapters shipped

Retrospective covering 6 commits across phases 1-6: framework, git PAT/SSH, psql stub, UI/i18n, audit log schema, docs. Notes memfd-drop rationale, TOFU/SIGKILL caveats, sentinel-length lesson from audit-shape tests.

* fix(secure-cli): honor per-request scrub bag on success+failure paths (#82)

Sandbox and host exec paths called the non-Ctx ScrubCredentials, so
adapter ScrubValues registered into the per-request bag during Prepare
(e.g. GitLab glpat-, Bitbucket app-passwords, Azure DevOps PATs, Gitea
tokens, SSH key tmpfile paths) were ignored on stdout/stderr returned
to the agent. Only the package-global regex pass ran — covering ghp_
but nothing else.

Switch all four exec/sandbox call sites to ScrubCredentialsCtx so the
bag is consulted. Also scrub the slog adapter_cleanup_failed line —
os.Remove errors embed the full tmpfile path.

Add 5 regression tests that pin success path, failure path, negative
control (proves the bag is what catches the sentinel), classic-PAT
sanity, and timeout path no-leak.

Locks AC6 against non-GitHub PAT providers.

* fix(secure-cli): address review-pr #89 findings

- ui/web: SSH key Textarea was 'text-xs' on all viewports, triggering
  iOS Safari auto-zoom on focus. Switch to 'text-base md:text-xs' so
  mobile renders 16px (no zoom) while desktop keeps compact mono font.
- psql adapter: add on-disk pgpass tmpfile path to ScrubValues. psql
  echoes 'could not open password file "<path>"' on IO errors, so
  the path needs scrubbing alongside the password. Mirrors the git SSH
  adapter pattern. Update test assertion accordingly.
- psql adapter: replace literal 'nil' context with 'context.TODO()'
  to silence staticcheck SA1012.
2026-05-28 18:17:49 +07:00
Duy /zuey/ bc3bc25c98 fix(mcp): eliminate spurious grant-revoked errors (tool filter + session reset + system-user bypass) (#87)
* feat(mcp): filter tools at registration + detect FastMCP session reset with force-reconnect

Two foundational MCP reliability improvements:

(1) Tool allow/deny filtering at BridgeTool registration: Previously the runtime grant-check at execute time surfaced "grant revoked" errors when the LLM called a tool it wasn't allowed to call. Filter upfront in both the per-agent registration path (manager_connect.go) and per-user registration path (loop_mcp_user.go). Adds tool_filter.go with IsToolAllowed + tests. This eliminates the "registered then runtime-denied" loop.

(2) FastMCP session reset detection + force-reconnect: FastMCP/Python mcp servers reject tools/call as "invalid during session initialization" when the upstream session lifecycle resets but our pool still holds the old Mcp-Session-Id. Detector matches three known phrasings (FastMCP, mcp-go, mcp-go transport ErrSessionTerminated) in session_reset.go. On detection, BridgeTool.Execute requests a force-reconnect via atomic CAS dedup so N concurrent failing calls collapse to one reconnect. Health loops skip ping while pending so a server answering ping in "initializing" state cannot clobber connected=true before the fresh Initialize completes. Includes 30s timeout, structured slog telemetry, concurrent CAS dedup test.

Files: tool_filter.go + tool_filter_test.go (new), session_reset.go + session_reset_test.go (new), manager_connect.go (connectServer/connectViaPool signatures + registerBridgeTools/registerPoolBridgeTools filter logic + reconnPending skip), manager.go (connectAndFilter + connectServer call signature changes), loop_mcp_user.go (filter-at-register block + WithForceReconnect wiring), pool.go (reconnPending skip), bridge_tool.go (session reset detection + WithForceReconnect callback).

* fix(mcp): self-heal grant cache + bypass per-user grant for system/empty userID

Two production fixes for "MCP tool: grant revoked" recurring on song-nhi-v2.

(1) System-user bypass in ListAccessible: Registration uses LoadForAgent(ctx, agentID, "") while execute uses IsAllowed(ctx, agentID, "system", ...). The LEFT JOIN on mcp_user_grants could match a stale disabled row keyed user_id='system' and silently filter the server out only at execute. Skip the join entirely for synthetic owner identities (userID="" or "system") so registration and execute see the same set. Applied to both PostgreSQL (mcp_servers_access.go) and SQLite (mcp_servers_access.go).

(2) Grant-checker no-cache on empty allowByServer: grant_checker.loadEntry now skips the cache write when allowByServer is empty. Without this, a single transient empty result pinned permanent denial until a bus invalidate fired. Re-queries until the empty condition clears, then caches normally. Includes TestStoreGrantChecker_EmptyEntryNotCached.
2026-05-28 14:04:04 +07:00
Duy /zuey/ af10f5d708 feat(skills): add workspace-organizing built-in skill (#86)
* feat(skills): add workspace-organizing built-in skill

Enforces purpose-based folder convention for agent workspaces with two
modes (flat for ad-hoc work, project for named multi-file work),
per-agent namespacing under shared/<agent_key>/ to prevent collisions
in team workspaces, and pre-write discovery via vault_search /
memory_search / knowledge_graph_search to surface related files and
avoid duplicates. Vault integration documents which folders are
Vault-indexable and how scope mirrors workspace scope.

Closes #71

* docs(changelog): add workspace-organizing skill entry

Refs #71
2026-05-28 13:39:05 +07:00
Duy /zuey/ 74930a1625 fix(ci): base64-encode zuey SSH key secret to dodge libcrypto error (#79)
* fix(ci): base64-encode zuey SSH key secret to dodge libcrypto error

First real deploy_zuey_beta run failed at the new 'Sync zuey ops scripts
to VPS' step with 'Load key: error in libcrypto' / Permission denied,
because GitHub Secrets storage normalized newlines inside the multi-line
PEM block.

Switch the secret to ZUEY_SSH_PRIVATE_KEY_B64 (single-line base64) and
base64 -d it on the runner. Single-line storage is immune to whitespace
mangling. Add a ssh-keygen -y -f pre-flight check that fails fast with
a clear remediation message if either the base64 or the decoded key is
malformed.

Refs: workflow run 26499549166

* fix(ci): create SSH key at 0600 from birth via umask 077

Defense-in-depth from review of PR #79: wrap the base64-decode pipeline
in a subshell that sets umask 077 so the decoded private key file is
created at 0600 directly, instead of relying on a chmod 0600 after the
fact (which left a brief 0644 window).
2026-05-27 17:59:08 +07:00
Goon 17845305dc feat(deploy): track zuey ops scripts in repo + CI auto-sync
- Move scripts/goclaw-upgrade-release.sh -> scripts/zuey/goclaw-upgrade-release.sh
- Add scripts/zuey/goclaw-deploy.sh (canonical source for /usr/local/bin/goclaw-deploy)
  with self-loop symlink guard fix: readlink -f ... 2>/dev/null || true + warn
  when previous resolves to empty. Without this, /opt/goclaw/current -> current
  aborts set -euo pipefail before ln -sfn fixes the symlink (observed 2026-05-27,
  silently failing every deploy_zuey_beta CI run).
- Wire Sync zuey ops scripts to VPS step in dev-beta-release.yaml:
  scp + sudo install both scripts before triggering gateway upgrade endpoint.
  Backup-if-changed via cmp -s; root:root 0755; bash -n syntax check; key shred.
  Gracefully skips with warning if ZUEY_SSH_PRIVATE_KEY or ZUEY_SUDO_PASS unset.
- Document required secrets and manual sync recipe in docs/deployment-guide.md.
- Changelog entry 2026-05-27.
2026-05-27 15:18:08 +07:00
Duy /zuey/ d4463cde6f fix(exec): surface real cwd error + auto-normalize stale workspace paths (#77)
* fix(exec): surface chdir error instead of misleading fork/exec

Linux Go forkAndExecInChild conflates chdir + execve failures into one
PathError naming only argv0. When agent workspace points to a stale
directory, exec reports "fork/exec /usr/bin/gh: no such file" even though
the binary exists — the real failure is chdir on cmd.Dir.

Two fixes:
- loop_context.go: fall back to l.workspace when per-user MkdirAll fails
  (3 sites: user workspace, dispatched team, auto-resolved team)
- shell.go + credentialed_exec.go: preflight cwd via validateExecCwd
  before exec.Command, so the error names the directory, not the binary

Regression test TestValidateExecCwd covers empty / existing / missing /
file-instead-of-dir.

* feat(upgrade): auto-normalize stale agent workspace paths

Detects non-portable workspace values left by deployment migrations
(Docker → bare-metal, host path drift) and rewrites them to the current
configured base. Registered as data hook 072_normalize_agent_workspaces;
runs automatically after `migrate up`.

Stale patterns:
- /app/workspace/* (Docker container path persisted on bare-metal)
- ~ prefix (Go doesn't expand tildes; MkdirAll creates literal ~ dir)

Base resolved with gateway precedence: GOCLAW_WORKSPACE env >
config.Agents.Defaults.Workspace > default ~/.goclaw/workspace.
Idempotent (skips rows already at proposed value). Conservative
(leaves absolute non-stale paths untouched as intentional custom config).
2026-05-27 11:52:25 +07:00
Duy /zuey/ 93461e9863 Merge pull request #38 from digitopvn/codex/configurable-exec-timeout
feat(tools): make exec timeout configurable
2026-05-24 20:25:34 +07:00
Goon 87b9e296b1 Merge remote-tracking branch 'origin/dev' into codex/configurable-exec-timeout
# Conflicts:
#	docs/project-changelog.md
2026-05-24 20:19:40 +07:00
Duy /zuey/ 6825424001 Merge pull request #57 from digitopvn/codex/issue-48-google-workspace-cli
feat(runtime): add Google Workspace CLI support
2026-05-24 19:03:41 +07:00
Goon 3a749ff91c Merge remote-tracking branch 'origin/dev' into codex/issue-48-google-workspace-cli
# Conflicts:
#	docs/project-changelog.md
2026-05-24 18:57:47 +07:00
Duy /zuey/ 34a2cb24da Merge pull request #62 from digitopvn/codex/issue-21-slash-skill-commands
feat(skills): add slash command activation
2026-05-24 16:46:14 +07:00
Goon c99f7e1fa4 feat(skills): add slash command activation 2026-05-24 16:39:44 +07:00
Duy /zuey/ 14b32bd219 Merge pull request #61 from digitopvn/codex/issue-28-skill-upload-size
feat(skills): configure upload size limits
2026-05-24 16:03:56 +07:00
Goon 1296031d5a feat(skills): configure upload size limits 2026-05-24 15:57:21 +07:00
Duy /zuey/ 5d62877550 Merge pull request #60 from digitopvn/codex/issue-41-env-sensitive-values
feat(cli): split env visibility for credentials
2026-05-24 15:20:05 +07:00
Goon 3294fccbad feat(cli): split env visibility for credentials 2026-05-24 15:14:43 +07:00
Duy /zuey/ 2bb736cf18 feat(usage): add AI budget usage caps
Merge usage-cap budget controls with conflict resolution against dev.
2026-05-24 13:57:31 +07:00
Duy /zuey/ 61fdcdcb81 feat(tools): add command keyword allowlist
Closes #42
2026-05-24 13:52:23 +07:00
Goon 7fee3073df merge(dev): resolve usage cap conflicts 2026-05-24 13:49:40 +07:00
Duy /zuey/ 5ae5815738 fix(telegram): preserve archive upload paths (#58) 2026-05-24 12:48:51 +07:00
Goon 12fb1baf79 feat(runtime): add Google Workspace CLI support 2026-05-24 12:12:16 +07:00
Duy /zuey/ 68684e4859 feat(browser): add scoped cookie sync
Fixes #49
2026-05-24 11:27:02 +07:00
Goon d64a31ebdb fix(usage): enforce caps on auxiliary llm calls 2026-05-24 11:13:03 +07:00
Duy /zuey/ f9440baca2 fix(cron): preserve SecureCLI credential context
Closes #54
2026-05-24 09:42:44 +07:00
Duy /zuey/ 501800743f fix(telegram): use upload limit for outbound media (#53) 2026-05-24 08:16:41 +07:00
Duy /zuey/ 2c699f31de fix(chat): support zero debounce and agent overrides 2026-05-23 14:02:34 +07:00
Goon e4acb147d6 feat(usage): edit cap policies and trace decisions 2026-05-23 09:34:29 +07:00
Goon a9a6463f71 feat(usage): bridge agent monthly budget caps 2026-05-23 08:51:04 +07:00