Commit Graph
433 Commits
Author SHA1 Message Date
Ben HoverterandGitHub f67c4e8754 fix(channels): key router default-agent map on user_id, not channel_id (#1123)
* fix(channels): key router on user, not channel (Discord/Slack)

Discord and Slack adapters set sender.platform_id to the channel/conversation
ID (needed for the send path), so router.resolve(channel, sender.platform_id, ..)
was matching peer_id bindings against the channel ID and never finding the
user-keyed binding. The sender_user_id() helper already existed but only the
rate-limit/authz paths used it; the routing reads did not.

Read-path fix:
- discord.rs / slack.rs: stash author/user ID in metadata["sender_user_id"]
- bridge.rs: route the text and audio paths through sender_user_id(message)
- bridge.rs: thread user_id through handle_command() so the 6 CLI slash-command
  resolves (/new, /compact, /model, /stop, /usage, /think) also key on user.
  Tests updated for the new signature.

Write-path follow-up (set_user_default + broadcast routing) deferred to a
separate commit so this change can be validated in isolation.

* fix(channels): close write-path keying gap; broadcast user-scoped

Completes the router keying fix started in 6a90aa0. The read path
resolves on user_id, but four write sites and the broadcast lookup
were still keyed on sender.platform_id (channel ID on Discord/Slack),
producing the split-keying state that surfaced in GAP-008.

- 4 x set_user_default writes (text/audio fallback, /agent existing,
  /agent spawned) now key on sender_user_id(message)
- 2 x broadcast lookups (has_broadcast / resolve_broadcast) switched
  to sender_user_id(message), matching the upstream test's intent
  (router.rs:521-547 keys on "vip_user", not a channel)
- boot-time log warning on Discord/Slack adapter start: any
  pre-existing /agent default may need to be re-run once
- new test test_handle_command_agent_select_keys_on_user_id_not_
  platform_id locks in the round-trip
2026-04-29 14:40:36 +03:00
Jaber JaberandGitHub 3b237ac526 Merge pull request #1090 from chrisyoung2005/fix/streaming-heartbeat-touch
Stamp last_active in streaming agent loop to prevent heartbeat false-positives
2026-04-29 14:39:28 +03:00
Jaber JaberandGitHub 96c572df32 Merge pull request #1082 from octo-patch/fix/issue-1081-lark-websocket-region
fix(feishu): respect region setting for WebSocket endpoint URL
2026-04-29 14:38:12 +03:00
Jaber JaberandGitHub 17e0d519ca Merge pull request #1080 from pandego/fix/1079-minimax-init
fix: expose MiniMax in openfang init
2026-04-29 14:30:05 +03:00
Charles HakesandGitHub 37c233d489 fix(flake): NixOS build — nativeBuildInputs, wrapGAppsHook3, libayatana-appindicator runtime closure (#1063)
Closes #1092

Four fixes that together make `nix build .#openfang-cli` and `nix build .#openfang-desktop` work on NixOS:

1. perl / clang / pkg-config moved to nativeBuildInputs (fixes openssl-src build failure — this is the bug filed in #1092)
2. openfang-desktop nativeBuildInputs gets pkg-config + wrapGAppsHook3 (GTK runtime wrappers + webkit2gtk-4.1 .pc discovery)
3. libayatana-appindicator added to desktop buildInputs (tray.rs dlopen at runtime)
4. preFixup hook prefixes LD_LIBRARY_PATH so the dlopen-only library actually ends up in the runtime closure

Authored by @Aypex.

Supersedes #1086 (which only fixed item 1).
2026-04-29 14:29:24 +03:00
92f7e996de feat(media): add audio_base_url override for local OpenAI-compat Whisper (#1124)
Adds an optional `audio_base_url` field to `MediaConfig` that overrides
the hardcoded provider URLs in `media_understanding::transcribe_audio`,
allowing the same OpenAI-compatible multipart wire format to be sent to
a local Whisper service (speaches, faster-whisper-server, LM Studio,
etc.) instead of api.openai.com / api.groq.com.

Closes #1051.

## Why

Self-hosted, sovereignty-conscious, or rate-limited deployments often
need to route audio transcription to a local Whisper backend while
keeping `media_transcribe` / `speech_to_text` working as native tools
(no helper scripts, no shell_exec workarounds). Today the URLs in
`media_understanding.rs:118-128` are literal `&'static str` so neither
`OPENAI_BASE_URL` nor `provider_urls` (which the LLM drivers do
respect) is read for audio. The same problem existed for embeddings
and was already addressable via `provider_urls`, so this change keeps
the pattern symmetric for media at the simplest possible surface area.

## Wire format

The endpoint shape and Authorization header remain identical:

  POST <audio_base_url>/v1/audio/transcriptions
  Authorization: Bearer $<provider>_API_KEY
  Content-Type: multipart/form-data
  fields: file (binary), model, response_format=text

This means **any OpenAI-compatible Whisper server is drop-in**
(Speaches, faster-whisper-server, LM Studio's Whisper server, etc.).
Local servers typically accept any non-empty bearer string, so users
can keep `OPENAI_API_KEY=anything` for the auth header.

## Configuration

```toml
[media]
audio_provider = "openai"
audio_base_url = "http://127.0.0.1:8000"
# → POST http://127.0.0.1:8000/v1/audio/transcriptions
```

Or for Groq-compatible local servers:

```toml
[media]
audio_provider = "groq"
audio_base_url = "http://127.0.0.1:9000"
# → POST http://127.0.0.1:9000/v1/audio/transcriptions
```

Trailing slash on the user-supplied base is stripped to avoid double
slashes in the final URL.

## Backward compatibility

- `MediaConfig` already uses `#[serde(default)]`, so existing
  configs without `audio_base_url` deserialize as `None` and behave
  exactly as before (cloud provider URLs).
- `Default` impl extended; `audio_base_url: None`.
- `parakeet-mlx` provider path unaffected (it's a separate code branch).
- No new dependencies, no breaking changes to public API.

## Tests

- `test_media_config_default` extended to assert `audio_base_url.is_none()`.
- `test_media_config_audio_base_url_serde_roundtrip` — set + JSON roundtrip.
- `test_media_config_backward_compat_no_audio_base_url` — legacy JSON
  parses with the new field as None.
- `test_audio_base_url_override_logic` — pure-function test that
  exercises the URL building branch (default URLs preserved when
  unset, override applied for both providers, trailing-slash strip).

The runtime branch in `transcribe_audio` was kept as a straight
`if Some/else default` rather than a helper function to minimize the
diff and keep the patch obviously safe to review.

## Operational note

This change does not affect anyone running the cloud provider URLs
out of the box. The override is opt-in via a single optional config
field. Useful for users like myself running a local Speaches container
behind a reverse proxy and a chat-only LLM key (z.ai Coding Plan)
that can't satisfy openai.com's audio endpoint.

Linked: #1051 (Configurable STT/TTS/image URLs and local backends).

Co-authored-by: Miguel Guerrero <kortux@gmail.com>
2026-04-29 14:28:04 +03:00
chris-youngandClaude Sonnet 4.6 f2587995a2 Stamp last_active in streaming agent loop to prevent heartbeat false-positives
Fixes #1089

run_agent_loop_streaming skipped the touch_agent() call that the
non-streaming run_agent_loop performs before every LLM request. On slow
local inference (e.g. Ollama qwen3.5:35b, multi-minute generations),
last_active went stale and the heartbeat monitor flagged the agent as
unresponsive, triggering crash recovery mid-stream. With multiple agents
sharing one Ollama instance, queued agents appeared frozen while the
active one generated.

Mirror the non-streaming behavior: stamp last_active immediately before
stream_with_retry so the heartbeat window covers the full LLM call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 20:59:47 -07:00
jaberjaber23 e6bab993ae bump v0.6.0 v0.6.0 2026-04-19 22:57:55 +03:00
jaberjaber23 a39a675ba9 skill config ui 2026-04-19 22:27:23 +03:00
jaberjaber23 0ce390e09f cron delivery ui 2026-04-19 21:53:20 +03:00
jaberjaber23 88eeaa6a4d commands ui 2026-04-19 21:42:33 +03:00
jaberjaber23 3db5d3a825 cron delivery 2026-04-19 17:10:23 +03:00
jaberjaber23 5a1f372612 command registry 2026-04-19 17:08:11 +03:00
jaberjaber23 a9f15b2d23 skill config 2026-04-19 16:54:11 +03:00
octo-patch 45e7ea7948 fix(feishu): respect region setting for WebSocket endpoint URL
When using Lark international (open.larksuite.com) with WebSocket mode,
the adapter was hardcoding the Chinese Feishu endpoint URL and also
ignoring the configured region entirely when constructing the adapter.

Two bugs fixed:
1. FEISHU_WS_ENDPOINT_URL was hardcoded to open.feishu.cn — international
   Lark apps could not authenticate because their credentials are only
   valid on open.larksuite.com. Changed to FEISHU_WS_ENDPOINT_PATH and
   compute the full URL using self.region.domain() at call time.
2. new_websocket() in FeishuAdapter always set region = FeishuRegion::Cn.
   Added new_websocket_with_region() that accepts an explicit region, and
   updated the call site in channel_bridge.rs to pass the parsed region.

Fixes #1081
2026-04-19 11:23:46 +08:00
pandego 8d3d77dd99 fix: expose MiniMax in init provider lists 2026-04-18 13:26:28 +02:00
jaberjaber23 d3d9fa842d release: v0.5.10
Bump workspace version to 0.5.10 and refresh docs.

Bundles the 7 fixes merged on main since v0.5.9:
- #1034 auth fail-closed (#1071)
- #980  channel agent name prefix (#1072)
- #1043 multimodal text+images (#1073)
- #809  openfang hand config subcommand (#1074)
- #843  context.md re-read per turn (#1075)
- #905  config get default_model.base_url (#1076)
- #1069 scheduler unification and migration (#1077)

README: fix stale 0.3.30 badge and March 2026 header to 0.5.10 and April 2026,
drop em dashes throughout.

CHANGELOG: new 0.5.10 section with the above, plus notes on #818 and #819
which were closed as invalid.
v0.5.10
2026-04-17 22:54:56 +03:00
Jaber JaberandGitHub ff44cfbe87 fix(scheduler): route schedule_* tools and /api/schedules through kernel cron scheduler (#1069) (#1077)
The schedule_create tool, its sibling schedule_list and schedule_delete,
and the matching /api/schedules HTTP routes were all writing to a
shared-memory key that no executor ever read. Jobs registered that way
silently never fired.

Route all three tools and all /api/schedules endpoints through the real
cron scheduler in openfang-kernel. Add a one-shot idempotent migration
at kernel startup that imports legacy __openfang_schedules entries into
the cron scheduler and clears the old key.

Tests:
- Unit tests for sanitize_schedule_name and sanitize_cron_job_name
- Tool wrapper tests using a fake KernelHandle that verify
  schedule_create/list/delete route into cron_create/list/cancel
- Migration tests cover the happy path, idempotency via the marker key,
  and skipping entries whose target agent is not in the registry

Quality gates: cargo check + test + clippy -D warnings + fmt clean on
openfang-kernel, openfang-runtime, openfang-api.

Made-with: Cursor
2026-04-17 22:47:11 +03:00
Jaber JaberandGitHub ce89d05987 fix(runtime): combine text and image blocks in multimodal user messages (#1043) (#1073)
When an upload supplied image content blocks alongside the user's text, the agent loop pushed only the image blocks and dropped the text. The LLM saw images with no accompanying prompt.

Build the user turn through a single helper that combines text and image blocks into one multimodal message when both are present, and keeps the existing single-mode representation when only one is supplied. Both the streaming and non-streaming paths now share the same builder.

Closes #1043

Made-with: Cursor
2026-04-17 22:47:08 +03:00
Jaber JaberandGitHub 00c0ff60de feat(channels): optional agent name prefix on outbound messages (#980) (#1072)
Adds an opt-in per-channel knob prefix_agent_name on ChannelOverrides
with styles Off (default), Bracket ([agent] text) and BoldBracket
(**[agent]** text).

The bridge wraps the final outbound text once in dispatch_message,
dispatch_with_blocks and the auto-reply path. Off is byte-identical
to pre-feature behavior so existing configs are unaffected.

Platform-native identity overrides (Slack per-message username,
Discord embed author field) are intentionally out of scope here and
tracked as a follow-up.

Made-with: Cursor
2026-04-17 22:47:04 +03:00
Jaber JaberandGitHub 07af248a07 fix(cli): config get returns base_url from default_model (#905) (#1076)
Extract the dotted-key lookup in `openfang config get` into a pure
`lookup_config_value` helper with clear outcomes (scalar value, non-scalar
section, or key not found) so the behaviour is covered by unit tests.

Previously the command only had integration coverage, so regressions where
`config get default_model.base_url` silently returned an empty string could
slip through. Tests now pin down every `[default_model]` scalar, including
`base_url`, plus unset, missing, numeric, boolean, and section cases.

Also distinguishes a section-valued key (e.g. `config get default_model`)
from a scalar instead of printing a debug-style `{}`.

Made-with: Cursor
2026-04-17 22:46:08 +03:00
Jaber JaberandGitHub 6ab07d155e fix(runtime): re-read agent context.md per turn so external updates take effect (#843) (#1075)
When an agent had a context.md file updated externally (e.g. a cron job
refreshing live market data), the updated content never reached the LLM
during an active session. The file was effectively cached for the
lifetime of the conversation.

The runtime now reads context.md from the agent workspace once per turn,
right before the system prompt is built, and injects it as a dedicated
'Live Context' section. Agents that still want the old cache-at-start
behaviour can opt back in with 'cache_context = true' on the manifest.

- new openfang-runtime::agent_context module with a small per-path cache
- if a re-read fails after a previous success, fall back to the cached
  content with a warning instead of dropping context mid-conversation
- new PromptContext.context_md field wired up in both kernel streaming
  and non-streaming paths
- one small disk read per agent turn (not per streaming token); file
  size capped at 32 KB like the other identity files

Made-with: Cursor
2026-04-17 22:46:05 +03:00
Jaber JaberandGitHub e2b0a54720 feat(cli): add hand config subcommand (#809) (#1074)
Docs reference `openfang hand config browser --set headless=true` but
the CLI never shipped that command. Add it as a thin wrapper over the
existing GET/PUT `/api/hands/{id}/settings` routes so the documented
example works.

- `openfang hand config <id>` prints the current settings merged with
  schema defaults.
- `--get KEY` prints one value, `--set KEY=VAL` (repeatable) updates
  values, `--unset KEY` removes them. Empty keys are rejected up front.
- When no instance is active, the daemon's existing 404 is surfaced
  with a hint to run `openfang hand activate <id>` first.

Unit tests cover the KEY=VAL parser (including empty keys, urls with
equals signs, and blank values). Integration test runs the registry
update_config path end-to-end through a persist/load round-trip so
the CLI semantics match what the daemon stores.

Made-with: Cursor
2026-04-17 22:46:01 +03:00
Jaber JaberandGitHub 6f519b9122 fix(api): reject unauth requests from non-loopback by default (#1034) (#1071)
Empty api_key used to skip auth for everyone. Now it only skips auth for
loopback origins. Non-loopback requests get 401 unless the operator opts
in with OPENFANG_ALLOW_NO_AUTH=1.

- middleware: check ConnectInfo, fail closed on LAN/public origins
- ws: same fail-closed logic for WebSocket upgrades
- server: loud startup warning when bound to non-loopback with no key
- AuthState: new allow_no_auth flag
- tests: 8 new unit tests covering loopback, LAN, public, missing info

Made-with: Cursor
2026-04-17 22:45:58 +03:00
Jaber JaberandGitHub 983519c8e8 Merge pull request #1041 from Hypn0sis/chore/security-deps
fix(deps): upgrade wasmtime 41->43 and rumqttc 0.24->0.25 to resolve active CVEs

Addresses RUSTSEC advisories surfaced by cargo-audit on main. wasmtime
jump required matching adjustments in sandbox.rs error types; rumqttc
switched to `default-features = false` + `use-native-tls` to drop
the vulnerable `rustls-webpki 0.102` path. CI green across Check/
Test/Clippy/Format/Security-Audit on all three platforms.
2026-04-17 21:21:17 +03:00
Matteo De Agazio 890ab8c177 style: apply cargo fmt 2026-04-14 15:11:29 +02:00
Matteo De Agazio 1b9a68dc0b fix: resolve remaining clippy warnings blocking CI (ptr_arg, dead_code, map_or, collapsible_if) 2026-04-14 15:06:54 +02:00
Matteo De Agazio 2b6286e469 fix: suppress pre-existing dead code and clippy warnings in openfang-runtime 2026-04-12 00:07:13 +02:00
Matteo De Agazio 589f32c8e5 style: apply cargo fmt 2026-04-11 23:55:38 +02:00
Matteo De Agazio 528a7b9ff7 fix(deps): upgrade wasmtime 41->43 and rumqttc 0.24->0.25 to resolve CVEs (RUSTSEC-2026-0049, 0085-0096) 2026-04-11 23:50:55 +02:00
jaberjaber23 6851bbab09 bump v0.5.9 v0.5.9 2026-04-10 21:19:33 +03:00
jaberjaber23 9323edccf4 fix config persistence, PowerShell bypass, WS 404 race, feishu panic, Revolt self-hosted 2026-04-10 20:51:27 +03:00
Jaber JaberandGitHub 0bccba10cf Merge pull request #1009 from normal-coder/feat-ux-optimize
Refactor UI navigation and improve visual consistency with SVGs
2026-04-10 20:01:16 +03:00
Jaber JaberandGitHub 2daaf92ad7 Merge pull request #923 from smitb/feat/add-aws-bedrock-llm-provider-support-with-token-auth
feat: Add AWS Bedrock LLM provider support with token auth
2026-04-10 20:00:44 +03:00
Jaber JaberandGitHub 80359b4487 Merge pull request #945 from felix307253927/pr-main-utf8
fix: Fix panic due to UTF-8 character boundary errors
2026-04-10 20:00:33 +03:00
Jaber JaberandGitHub b866bb6b21 Merge pull request #965 from chrisyoung2005/fix/ghcr-package-visibility
Set GHCR package visibility to public on every release
2026-04-10 20:00:14 +03:00
Jaber JaberandGitHub 3fa8c6c527 Merge pull request #928 from Alex-wuhu/novita-integration
feat: add Novita AI as LLM provider
2026-04-10 19:59:41 +03:00
Jaber JaberandGitHub e322afbebd Merge pull request #956 from RightNow-AI/dependabot/cargo/cron-0.16.0
chore(deps): bump cron from 0.15.0 to 0.16.0
2026-04-10 19:59:26 +03:00
jaberjaber23 a4c6c038a0 bump v0.5.8 v0.5.8 2026-04-10 19:57:59 +03:00
Jaber JaberandGitHub 864e957261 Merge pull request #1015 from AlexZander85/feature/russian-i18n-translation
feat(i18n): add Russian localization and i18n framework
2026-04-10 19:37:20 +03:00
Jaber JaberandGitHub f9921780e2 Merge pull request #1017 from dmbutko/fix/copilot-oauth-device-flow
fix: rewrite Copilot driver with OAuth device flow authentication
2026-04-10 19:37:10 +03:00
Jaber JaberandGitHub 1c049d06c1 Merge pull request #922 from Myshkouski/build/armv7
Add armv7-unknown-linux-gnueabihf target
2026-04-10 19:36:36 +03:00
dependabot[bot]andGitHub 6f86f7a857 build(deps): bump cron from 0.15.0 to 0.16.0
Bumps [cron](https://github.com/zslayton/cron) from 0.15.0 to 0.16.0.
- [Release notes](https://github.com/zslayton/cron/releases)
- [Commits](https://github.com/zslayton/cron/commits)

---
updated-dependencies:
- dependency-name: cron
  dependency-version: 0.16.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-10 16:17:45 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c47e15c1ce chore(deps): bump prost 0.13 -> 0.14
Bumps [prost](https://github.com/tokio-rs/prost) from 0.13.5 to 0.14.3.
- [Release notes](https://github.com/tokio-rs/prost/releases)
- [Changelog](https://github.com/tokio-rs/prost/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/prost/compare/v0.13.5...v0.14.3)

---
updated-dependencies:
- dependency-name: prost
  dependency-version: 0.14.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-10 19:16:03 +03:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
185ebc429f chore(deps): bump lettre 0.11.19 -> 0.11.20
Bumps [lettre](https://github.com/lettre/lettre) from 0.11.19 to 0.11.20.
- [Release notes](https://github.com/lettre/lettre/releases)
- [Changelog](https://github.com/lettre/lettre/blob/master/CHANGELOG.md)
- [Commits](https://github.com/lettre/lettre/compare/v0.11.19...v0.11.20)

---
updated-dependencies:
- dependency-name: lettre
  dependency-version: 0.11.20
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-10 19:15:56 +03:00
Jaber JaberandGitHub 87626ae1c9 Merge pull request #966 from t4min0/fix/nix-perl-buildinput
fix(nix): add perl to buildInputs for openssl-sys vendored build
2026-04-10 19:14:20 +03:00
Jaber JaberandGitHub 4a8af88fe8 Merge pull request #1002 from lc-soft/fix/first-chat-bug
fix(chat): prevent duplicate message keys on first chat welcome message
2026-04-10 19:14:11 +03:00
Jaber JaberandGitHub 2ef5704132 Merge pull request #1006 from lc-soft/fix/comms-api-base-url
fix(comms): OpenFangAPI.baseUrl is undefined
2026-04-10 19:14:02 +03:00
Jaber JaberandGitHub 747314b19b Merge pull request #1022 from lc-soft/fix/bar-width-style
fix(analytics): correct bar style attribute binding
2026-04-10 19:13:53 +03:00
Jaber JaberandGitHub 3787c13fb1 Merge pull request #1024 from Hypn0sis/fix/crypto-discord-free-response
fix: crypto provider, silent failure debug, Discord free_response_channels
2026-04-10 19:13:47 +03:00