324 Commits

Author SHA1 Message Date
Goon 591d809779 Merge remote-tracking branch 'upstream/dev' into dev
# Conflicts:
#	internal/cron/service.go
2026-06-15 14:14:16 +07:00
Goon 540dc11301 fix(providers): send Kimi headers when listing models 2026-06-15 10:16:47 +07:00
Raihan Afiandi b844af8d25 feat(providers): add kimi_coding provider with fixed User-Agent + temp lock
Moonshot's Kimi Coding endpoint is OpenAI-compatible on the wire but
has two non-standard rules:

  1. Every request must carry `User-Agent: claude-code/0.1.0` — without
     it the upstream rejects the call outright.
  2. `temperature` is locked to the server default; passing any other
     value returns HTTP 400 `invalid temperature: only 1 is allowed for
     this model`.

Rather than special-case either, this commit generalises both:

  - WithExtraHeaders on OpenAIProvider — static headers attached to
    every outgoing request. Reusable by any future provider that needs
    pinned identity headers; mirrored in adapter_openai.ToRequest so
    callers using the adapter path see the same shape.
  - The existing skipTemp branch in openai_request.go gets a
    provider_type check — kimi_coding joins o1/o3/o4/gpt-5-mini in
    omitting `temperature` from the request body.

Provider wiring:
  - store.ProviderKimiCoding constant + ValidProviderTypes entry +
    KimiCoding{DefaultAPIBase,DefaultModel,RequiredUserAgent}.
  - case store.ProviderKimiCoding in both registration switches
    (cmd/gateway_providers.go and internal/http/providers.go).
  - UI dropdown entry with the API base pre-filled.

5 unit tests cover: real outgoing header injection, adapter-path
header mirroring, empty-map WithExtraHeaders no-op, kimi_coding
strips temperature, and the negative control (other providers still
forward temperature).

Admin flow: Providers → Add → "Kimi Coding (Moonshot)" → paste API
key → save.
2026-06-15 10:16:47 +07:00
Duy /zuey/ c32d6b3279 Merge pull request #176 from digitopvn/codex/issue-107-usage-analytics-plan
feat(usage): add event analytics dashboard
2026-06-12 18:28:35 +07:00
Goon 06877365c4 merge: sync dev into usage analytics branch 2026-06-12 17:58:22 +07:00
Duy /zuey/ 4f87b93dbd feat: add qwen3.7-plus to Bailian Coding catalog
Closes #169
2026-06-12 17:57:48 +07:00
Goon 4002bb4c13 feat(usage): add event analytics dashboard 2026-06-12 17:51:12 +07:00
Duy /zuey/ 515596c44c Merge pull request #167 from digitopvn/codex/issue-161-skill-self-evolution
feat(skills): add skill self-evolution metrics
2026-06-12 15:55:18 +07:00
Goon dd6ee64f23 fix(skills): harden evolution mutations 2026-06-12 15:38:23 +07:00
Goon 1e0ea7615d fix(skills): guard suggestion status updates by skill 2026-06-12 15:04:29 +07:00
Goon 248d591f52 fix: resolve dev merge conflicts 2026-06-12 15:01:38 +07:00
Goon 7692939d09 Merge remote-tracking branch 'origin/dev' into codex/issue-161-skill-self-evolution
# Conflicts:
#	cmd/skills_cmd.go
#	docs/project-changelog.md
#	internal/http/skills.go
#	internal/store/sqlitestore/schema.go
2026-06-12 14:57:09 +07:00
Goon f45bfa860c feat(skills): add skill self-evolution metrics 2026-06-12 14:47:51 +07:00
Goon c320de5a87 feat: grant first setup agent gateway operator access 2026-06-12 10:40:59 +07:00
Goon 0bf7f88054 feat(skills): add lifecycle API and CLI 2026-06-12 10:38:53 +07:00
Duy /zuey/ 167b80e778 feat(traces): add search and advanced filters
Closes #152
2026-06-12 00:14:40 +07:00
Goon 94d7bbc28a fix(vault): enforce team access on document updates 2026-06-09 23:04:59 +07:00
Goon 59a1773a18 Merge remote-tracking branch 'origin/dev' into codex/merge-nextlevelbuilder-goclaw 2026-06-09 22:49:30 +07:00
Goon a63080714a Merge remote-tracking branch 'upstream/dev' into codex/merge-nextlevelbuilder-goclaw
# Conflicts:
#	internal/pipeline/think_stage.go
#	internal/pipeline/tool_stage.go
#	tests/integration/git_adapter_ssh_test.go
2026-06-09 22:38:32 +07:00
Thieu Nguyen 43837afca3 fix(security): consolidate & enhance batched security fixes (#1155, #967, #972, #974, #989, #973) (#1185)
* fix(sandbox): avoid shell in FsBridge writes

Replace sh -c with interpolated path by shell-free 'tee -- <path>' argv form,
piping content via stdin. Prevents command injection through filenames
containing shell metacharacters inside the sandbox container.

Co-authored-by: evgyur <evgyur@gmail.com>

* fix(security): fail-closed on pairing DB errors across channels

On IsPaired lookup error, deny instead of granting access. Covers the shared
CheckDMPolicy/CheckGroupPolicy helpers (Slack/Discord/Feishu/WhatsApp/Zalo) and
the four inline Telegram pairing checks.

Co-authored-by: Srini <srinis.k@gmail.com>

* fix(security): harden provider URL validation against SSRF

Enforce scheme check for all provider types; restrict local types (ollama,
claude_cli, acp) to an explicit localhost allowlist instead of skipping checks;
resolve remote hostnames and reject any IP in a private/reserved range via the
shared security.IsBlocked CIDR list (covers loopback, link-local, metadata,
multicast, and unspecified 0.0.0.0/::). Closes the wildcard-DNS bypass and the
local-type escape hatch. Operator opt-in via GOCLAW_ALLOW_PRIVATE_PROVIDER_URLS.

Exports security.IsBlocked as the single source of truth for blocked ranges.

Co-authored-by: Linh Vo Van <linh.vo@e-cq.net>

* feat(pipeline): add fail-closed tool call authorization gate

Gate tool execution against the server-side AllowedTools allowlist built from the
RBAC/tenant-aware filtered tool set. Resolve the tool-call prefix before the
allowlist lookup so prefixed agents are not wrongly blocked, re-check deny on lazy
MCP activation, and expand IsDenied to cover aliased tool names.

Co-authored-by: Huy Doan <tui@pm.me>

* fix(security): expand file-serve deny-list defense-in-depth

Add absolute-path deny prefixes (/home, /Users, /srv, /var/lib, /var/www, /opt)
and an explicit fail-closed log when no file-serving boundary is configured.

Co-authored-by: Linh Vo Van <linh.vo@e-cq.net>

* fix(providers): allow claude cli executable paths

Refs: #1185

---------

Co-authored-by: evgyur <evgyur@gmail.com>
Co-authored-by: Srini <srinis.k@gmail.com>
Co-authored-by: Linh Vo Van <linh.vo@e-cq.net>
Co-authored-by: Huy Doan <tui@pm.me>
2026-06-05 00:48:38 +07:00
Duy /zuey/ 53ef912441 fix(channels): add reasoning delivery modes (#135) 2026-06-03 11:01:14 +07:00
Goon 8fb3149142 fix(release): address official ship blockers 2026-05-31 22:05:59 +07:00
Goon 02d7b6f3de fix: stabilize agent git access 2026-05-31 19:42:05 +07:00
Goon 994acfe3a4 feat(cli-credentials): add agent-scoped git credentials 2026-05-31 16:49:23 +07:00
Goon d78f97b9d7 feat(channel-memory): merge dev for passive extraction 2026-05-31 14:01:29 +07:00
Duy /zuey/ 2cd750ac44 feat(channels): add context capability admin surface
Squash merge PR #115 after resolving changelog and SQLite migration-map conflicts with current dev. Renumbered channel-context PostgreSQL migration to 000075 and bumped PG required schema to 75 plus SQLite schema to 44 so it follows the run timeline migration. Local checks passed: go test ./..., go build ./..., go build -tags sqliteonly ./..., go vet ./..., and pnpm -C ui/web build. PR CI run 26705617311 passed release-versioning, go, and web.
2026-05-31 13:50:31 +07:00
Duy /zuey/ 269e2618ae feat: add archived run timeline
Squash merge PR #113 after resolving the project changelog conflict with current dev. Local checks passed: Go store/http/gateway/agent/pipeline tests, SQLite-tagged tests, both Go builds, web Vitest, and web build. PR CI run 26705098712 passed release-versioning, go, and web.
2026-05-31 13:22:07 +07:00
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/ 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/ df8a03df48 fix: allow skill_manage companion files
Squash merge PR #104 after validation.
2026-05-31 12:24:22 +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/ 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
Goon 8d45168b35 feat(channel-memory): run passive extraction pipeline 2026-05-29 20:48:19 +07:00
Goon c7aac69ca0 fix(channels): harden context capability admin 2026-05-29 20:04:33 +07:00
Goon bd34e2917e feat(channels): manage context scoped capabilities 2026-05-29 19:29:31 +07:00
Goon f20d776ebb feat(channels): expose context capability matrix 2026-05-29 19:17:18 +07:00
Goon 29f3eb3874 feat(channels): expose read-only channel contexts 2026-05-29 19:11:17 +07:00
Aaron Vu 9edcc00dbc fix(vault): surface close errors + clarify slog label + drop test dup
Trivial nits from thieung's review of ba59e7b0:

- writeDocumentContent now closes the file explicitly and propagates
  any Close() error so a delayed FS failure (e.g. disk-full at flush)
  doesn't return success.
- Renamed slog `site=lexical` to `site=path_traversal` — the lexical
  prefix check catches "../" / absolute-path escapes, not symlinks.
  Umbrella key `security.vault_symlink_escape` stays.
- Non-master-tenant test now reuses newJSONRequest + WithContext,
  dropping the duplicate mustJSON helper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 20:55:23 +07:00
Aaron Vu 1fad4e0b57 fix(vault): non-master tenant 500, O_NOFOLLOW, slog, *string content
Addresses second review on #1174 (thieung):

**B1 (blocking)** — first JSON content write into a fresh non-master
tenant returned 500 because workspace/tenants/{slug}/ doesn't exist
yet and EvalSymlinks fails with ErrNotExist. writeDocumentContent now
MkdirAlls the workspace before resolving (mirrors handleUpload). Added
a regression test that drives a non-master tenant context end-to-end
and asserts the file lands under workspace/tenants/{slug}/.

**I1** — replaced the Lstat→WriteFile sequence (TOCTOU window) with
os.OpenFile(O_WRONLY|O_CREATE|O_TRUNC|O_NOFOLLOW). The Lstat short-fail
stays as defense-in-depth + cleaner error logging. Portability handled
via build-tagged constants in vault_handler_documents_nofollow_{unix,
windows}.go — O_NOFOLLOW is no-op on Windows (desktop edition is
single-user with no untrusted tenants).

**I3** — every symlink/path-escape rejection now emits
slog.Warn("security.vault_symlink_escape", ...) with the rejection
site, resolved path and workspace, matching the storage.go pattern
so oncall can alert on attempted escapes.

**I4** — POST `content` field changed from `string` to `*string` to
match PUT. Nil pointer = "no write" (metadata-only stub); empty string
= "write a 0-byte file + fire event"; non-empty = write bytes. Updated
handler godoc to spell out the symmetry. Switched `werr == os.ErrInvalid`
to `errors.Is` per repo conventions.

**Tests added** — non-master tenant first content write (B1), PUT with
nil content (locks "no change" contract), handler-level ".." path
rejection, POST content:"" writes empty file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 20:55:23 +07:00
Aaron Vu 5d5be0a64f fix(vault): harden symlink containment + mirror upload enrich-progress
Addresses review on #1174:

- writeDocumentContent now resolves symlinks before writing. A symlink
  living inside the workspace but pointing outside (e.g. workspace/link
  -> /etc) previously passed the lexical prefix check and os.WriteFile
  followed it out of bounds. We now EvalSymlinks the workspace root and
  the deepest existing ancestor of the target, re-check containment
  before any mkdir/write, and refuse a symlink planted at the final
  component via Lstat.
- publishDocUpserted starts enrichment progress (enrichProgress.Start)
  before publishing EventVaultDocUpserted, matching handleUpload's
  ordering so the worker pool can't drain the event before the progress
  tracker registers it.
- Add regression tests: symlink dir escape, final-component symlink,
  parent escape, empty content, disallowed extension, event-after-upsert
  ordering, metadata-only no-write, and upsert-failure orphan behaviour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 20:55:23 +07:00
Aaron Vu fc105d31a1 fix(vault): accept and persist content on POST/PUT vault/documents
Previously POST /v1/{agents/{id}/}vault/documents and the matching PUT
endpoint declared a body struct without a `content` field, so callers
passing JSON `{"path": ..., "title": ..., "content": "..."}` got back
HTTP 201 with a doc id, but the bytes were silently discarded:

  - bindJSON ignored the `content` key (no matching field)
  - the document row was inserted with content_hash="" and no file on disk
  - the enrichment pipeline (summary, embeddings, links) was never
    triggered because no EventVaultDocUpserted was published

The endpoint was effectively a metadata-only create — there was no client
path that combined "register doc + supply bytes" via JSON. /v1/vault/upload
exists, but it requires multipart, flattens paths to basename, and is
inconvenient for programmatic clients that already have UTF-8 text in
memory (CI jobs, agent skills, MCP tools).

This change makes `content` an optional field on both handlers:

  - POST: when content is non-empty, validate the file extension against
    the same allowedUploadExts whitelist used by /v1/vault/upload, write
    bytes to <tenant-workspace>/<path>, compute SHA-256, store it as
    content_hash on the doc, and publish EventVaultDocUpserted so the
    enrichment worker picks it up — exactly the same downstream
    behaviour as /v1/vault/upload.
  - PUT: same logic; nil content = no change, non-nil content = rewrite
    the file on disk (and an empty string truncates it).
  - Omitting `content` preserves the historical metadata-only behaviour
    (e.g. for callers that materialise files via filesystem rescan).
  - Path validation reuses the existing rules: no `..`, no absolute
    paths; an extra abs-path check rejects symlink escapes from the
    workspace root.

Two small helpers (writeDocumentContent, publishDocUpserted) factor out
the disk write + event publish so both handlers share the same code,
and so they can be unit-tested in isolation later.

The fix is backwards-compatible: existing callers that don't send
`content` continue to work unchanged. Existing docs created without
content remain metadata-only stubs; a subsequent PUT with `content`
will materialise them.

Verified live on a development instance:
  POST → row inserted, file at <workspace>/<path>, SHA-256 matches,
         enrichment event published.
  PUT  → file rewritten, content_hash updated, updated_at refreshed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 20:55:23 +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
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
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
Goon 1296031d5a feat(skills): configure upload size limits 2026-05-24 15:57:21 +07:00
Goon 3294fccbad feat(cli): split env visibility for credentials 2026-05-24 15:14:43 +07:00
Goon 7fee3073df merge(dev): resolve usage cap conflicts 2026-05-24 13:49:40 +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