7 Commits
Author SHA1 Message Date
fchengyan 52ced3710f fix(build): embed commit SHA for release provenance (#1571 part 2) (#1590)
* fix(build): embed commit SHA for release provenance (#1571 part 2)

Docker builds exclude .git via .dockerignore, so buildvcs cannot read
VCS metadata and published images carry no commit information. A running
image cannot be lined up with the source commit it was built from.

Embed cmd.CommitSHA at link time (maintainer-endorsed option 2) and
surface it in goclaw version output:

- cmd: add CommitSHA var, print commit in version cmd when injected
- Makefile: pass git rev-parse HEAD via LDFLAGS
- Dockerfile: accept COMMIT_SHA build arg (default unknown)
- docker-compose.yml: pass GOCLAW_COMMIT_SHA through as build arg
- release workflows: inject github.sha into release and dev-beta builds

Backward compatible: binaries built without the flag keep the existing
version output format.

* fix(build): pass COMMIT_SHA to docker image builds, surface it in doctor/upgrade

Review follow-up for PR #1590:

- Add COMMIT_SHA=${{ github.sha }} to the build-args of every
  docker/build-push-action step (release.yaml, dev-beta-release.yaml,
  release-beta.yaml, fork-image.yaml) so published images — the
  artifact issue #1571 is about — carry the commit, not just release
  tarball binaries.
- Surface the commit in doctor and upgrade output (App version line)
  via a shared commitSuffix() helper, so operators can read provenance
  from logs without exec'ing goclaw version (review suggestion #2).
- version cmd refactored onto the same helper; output unchanged.
2026-09-30 15:38:53 +07:00
fchengyan 4e99816a87 fix(channels): deliver NO_REPLY placeholder cleanup signal to channels (#1586)
The agent loop signals silent replies (NO_REPLY, cancelled runs, suppressed
errors) by publishing an outbound message with empty content plus the inbound
routing metadata (placeholder_key / local_key). Slack, Telegram and Discord
each implement an empty-content branch in Send() that deletes their streamed
'Thinking...' placeholder — the partial draft left visible in the thread when
delivery is suppressed.

deliverOutbound skipped every text-only empty outbound, so that cleanup
signal never reached channel.Send and the stray partial draft stayed
published (issue #1475). Pass the signal through when placeholder routing
metadata is present; keep skipping bare empty messages so channels without
an empty-content branch never render empty bubbles.

Fixes #1475
2026-09-29 07:07:12 +02:00
fchengyan a1913f813f fix(gateway): admit operator.provision keys on tenants.create and ten… (#1584)
* fix(gateway): admit operator.provision keys on tenants.create and tenants.users.add

The CVE #866 fail-closed hardening regressed operator.provision: the
role-only router check maps provision-only API keys to viewer and
rejects tenants.create / tenants.users.add with 'requires admin role',
even though the tenant handlers explicitly admit ScopeProvision on
exactly those two methods (issue #1524).

Restore the intended least-privilege provisioning path without any role
promotion: the router now allows credentials carrying ScopeProvision on
exactly the two tenant-provisioning RPCs (permissions.IsProvisionMethod).
Every other admin/write surface stays denied, and viewers without the
provision scope gain nothing.

Regression tests (internal/gateway/router_test.go) prove:
- provision-only succeeds on tenants.create and tenants.users.add
- provision-only stays denied on tenants.update and agents.create
- plain viewers stay denied on the provisioning methods
- unauthenticated clients stay denied

* refactor(gateway): route provisionScopeAllowed through permissions.HasProvisionScope

Address review feedback on #1584: HasProvisionScope was exported and
tested but had no production caller — the router used Client.HasScope
directly. Wire the router to the permissions helper so the provision
scope check has a single source of truth.
2026-09-28 14:04:35 +07:00
fcy 6063d975e8 fix(http): restore ACP executable-path validation, bypass SSRF URL check (#1481)
ACP providers use api_base to store an executable command/path, not a URL.
A previous SSRF-hardening refactor incorrectly classified ACP as a local-URL
provider type, causing create/update to reject valid ACP configs with
"provider URL must use http or https scheme".

Changes:
- Remove ProviderACP from localURLProviderTypes.
- Add validateACPExecutablePath mirroring the runtime registration logic in
cmd/gateway_providers.go (allows empty, claude/codex/gemini, or absolute path;
rejects URLs and relative paths).
- Route ACP through the new validator inside validateProviderURL.
- Add unit tests for validateACPExecutablePath.
- Update existing local-type tests that assumed ACP was URL-based.
- Add create/update HTTP handler regression tests for ACP binary, empty binary,
and URL rejection.

Bug-first verification: without the fix, ACP api_base values like "gemini" or
absolute paths fail URL validation, while URLs are incorrectly accepted; with
the fix the behavior is reversed to match the intended executable-path model.

Fixes nextlevelbuilder/goclaw#1481.
2026-09-27 12:42:14 +08:00
fchengyan 0fef6ff596 fix(providers): skip temperature for base gpt-5 and gpt-5-chat models (#1555)
OpenAI rejects non-default temperature on gpt-5 / gpt-5-chat with
HTTP 400 ("Unsupported value: 'temperature' does not support 0.7
with this model. Only the default (1) value is supported.").

skipTemp in buildRequestBody only covered gpt-5-mini / gpt-5-nano and
the o-series, so base gpt-5 and gpt-5-chat fell through and received
config.DefaultTemperature (0.7). On channel integrations the inbound
error is suppressed, leaving the agent silently dead.

The gpt-5.X flagship variants (gpt-5.1, gpt-5.4, gpt-5.5) keep their
temperature — only the base gpt-5 and gpt-5-chat aliases are locked
to the default.

Regression cases added for gpt-5, gpt-5-chat, gpt-5-chat-latest and
provider-prefixed forms; gpt-5 moved to the locked list in
TestBuildRequestBody_TemperatureKeptForNonReasoningModels per the
maintainer triage on #1460.

Fixes #1460
2026-09-08 10:31:37 +07:00
fchengyan a1e550f0f0 fix(skills): retry pip installs without --break-system-packages when unsupported
pip >= 23.0 added the PEP 668 --break-system-packages flag. On older pip
builds (e.g. macOS Command Line Tools Python) and on the pip `list`
subcommand of every version, the flag is rejected with
"no such option: --break-system-packages", breaking skill dependency
install/check paths entirely.

- dep_installer: route pip installs through pipRunInstall, which retries
  once without the flag when pip rejects it (issue #956)
- pip_update_executor: same retry for the upgrade path
- pip_update_checker: drop the flag from `pip3 list --outdated` - list is
  read-only, PEP 668 never applies, and no pip accepts the flag there
- add pip_flags.go helpers + unit tests, and legacy-pip integration tests
  reproducing the original failure

Fixes #956
2026-09-03 10:18:12 +08:00
fchengyan be573f411b fix(vault): sanitize context window to prevent invalid UTF-8 (#1536)
ExtractWikilinks builds a ±25-byte context window around each wikilink
match. When the content contains multi-byte UTF-8 characters (Vietnamese,
CJK, emoji), the byte-offset start/end can fall on a continuation byte,
producing invalid UTF-8 that PostgreSQL rejects with SQLSTATE 22021
('invalid byte sequence for encoding UTF8').

Fix: wrap the byte-offset slice with strings.ToValidUTF8, which strips
any leading/trailing partial runes. This is a minimal one-line change
that preserves the existing context semantics while ensuring the output
is always valid UTF-8.

Available since Go 1.21 (this project uses Go 1.26).

Closes #1472
2026-08-27 19:12:48 +07:00