Commit Graph
1631 Commits
Author SHA1 Message Date
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
Goon 3e29b4f019 Merge remote-tracking branch 'origin/dev' into codex/usage-cap-budget-controls
# Conflicts:
#	docs/project-changelog.md
2026-05-23 08:23:52 +07:00
Duy /zuey/ 8af0e4ae09 feat(chat): debounce rapid web messages 2026-05-23 07:47:05 +07:00
Goon ade1c1b838 fix(store): avoid usage cap migration keyword 2026-05-23 00:39:14 +07:00
Goon 1f51f988a3 Merge remote-tracking branch 'origin/dev' into codex/usage-cap-budget-controls 2026-05-23 00:17:20 +07:00
Goon 9ff5a629d2 feat(usage): add AI budget usage caps 2026-05-23 00:16:03 +07:00
Duy /zuey/ 8b661951c0 fix(deploy): detach gateway upgrades from service (#45) 2026-05-22 21:53:10 +07:00
Duy /zuey/ 43049d3b3f feat(http): unblock CLI P6 backend APIs (#44)
* feat(api): unblock trace follow and provider reconnect

* test(cron): stabilize run log execution test

* feat(http): add CLI P6 backend endpoints
2026-05-22 20:59:47 +07:00
Duy /zuey/ ef829c1ab4 fix(bitrix24): resolve PR 8-10 stack findings
Resolve the PR #8/#9/#10 stack on current dev, including Bitrix24 install callback hardening, migration renumbering, duplicate-domain fail-closed routing, UI textarea/mobile cleanup, and review hardening.
2026-05-22 20:53:01 +07:00
Duy /zuey/ faa195156e feat(ci): deploy zuey beta releases (#40)
* feat(ci): deploy zuey beta releases

Add automatic zuey VPS deployment to the dev beta release workflow after prerelease assets are published. Publish beta checksums and make the host upgrade script tolerate beta asset naming and checksum fallback.

* fix(deploy): allow active beta release reruns

Treat an existing target release as success only when it is already the active release and still contains the expected binary and migrations. This keeps automated beta deploy reruns from failing after a prior successful deploy while preserving fail-closed behavior for stale or partial release directories.
2026-05-22 19:49:43 +07:00