Skip to content

feat: add configuration persistence for model selections#4

Merged
adamdotdevin merged 1 commit intoanomalyco:devfrom
rekram1-node:feat-persist-model-selection
May 14, 2025
Merged

feat: add configuration persistence for model selections#4
adamdotdevin merged 1 commit intoanomalyco:devfrom
rekram1-node:feat-persist-model-selection

Conversation

@rekram1-node
Copy link
Copy Markdown
Collaborator

Fixes #3

I originally brought this up when I added model selection in the first place, I have these changes in open pr and figured I would copy it over to here since it is the new repo.

@adamdotdevin adamdotdevin merged commit 797045e into anomalyco:dev May 14, 2025
@adamdotdevin
Copy link
Copy Markdown
Member

love it, thanks!

burgercrisis added a commit to burgercrisis/opencode that referenced this pull request Jan 8, 2026
…d dank

Windows Command Execution Fixes:
- Add detectCommandShell() and parseCommand() functions for shell detection
- Implement direct PowerShell execution bypassing cmd.exe wrapper
- Add shell built-ins detection and needsShellExecution function
- Fix stream draining to prevent race conditions (Promise.all)
- Remove duplicate abort listeners

Edit Tool Improvements:
- Add newString validation guard (handles undefined/null/empty)
- Add UnicodeNormalizedReplacer for smart quotes and em-dashes
- Fix multi-line pattern matching with empty lines (Issue anomalyco#26)
- Add unique match identification for replaceFirst functionality
- Improve block anchor matching with variable gap handling

Documentation:
- Add verified-fixes-summary.md documenting all fixed issues
- Add windows-command-execution-issues.md comprehensive analysis
- Add linux-unix-mac-compatibility-analysis.md for cross-platform impact

Fixes: Issues anomalyco#2, anomalyco#3, anomalyco#4, anomalyco#5, anomalyco#7, anomalyco#8, anomalyco#9, anomalyco#15, anomalyco#19, anomalyco#26
randomm referenced this pull request in randomm/opencode Jan 9, 2026
… (#4)

* feat: implement Remory MCP integration (#3)

- Add comprehensive TDD test suite for configuration validation
- Update opencode.json to use Remory via Docker exec command
- Add infrastructure tests for container health and connectivity
- Include placeholder tests for memory operations and semantic search

Following GitHub issue #3 requirements for enhanced memory capabilities.

* test: enhance Remory integration test coverage (#3)

- Add comprehensive test suite for Remory MCP integration
- Validate Docker container health and connectivity
- Test semantic search capabilities and performance
- Verify memory tool compatibility with Remory backend
- 100% test coverage for configuration validation
- All tests passing with 7/7 success rate

* docs: add comprehensive Remory memory service documentation (#3)

- Document enhanced memory capabilities with semantic search
- Add Docker container setup instructions
- Detail 5-15x performance improvements over basic memory server
- Include technical architecture and configuration examples
- Document memory operations and performance benefits
- Complete documentation requirements for issue #3

* docs: verify MCP configuration for local Remory instance

The MCP memory configuration in opencode.json is already correctly set up to use the local Remory instance running in Docker. No changes needed.
adolago referenced this pull request in adolago/zee Jan 10, 2026
Merged Snyk PRs:
- PR #9: node 25.2.1-bullseye-slim → 25.2.1-trixie-slim
- PR #4: node 20-slim → 20.19.6-trixie-slim
- PR #3: node 20 → 20.19.6-trixie-slim

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
kryptobaseddev added a commit to kryptobaseddev/opencode that referenced this pull request Feb 22, 2026
… leaks

Addresses the 4 remaining high-priority memory leak issues from the
work plan (I-9385-A, I-7046-A, PR-14635, I-7046-C partial).

I-9385-A (CRITICAL, Priority anomalyco#1) — tool/task.ts: call Session.remove()
after extracting subagent task output. This fires the session.deleted
event, which triggers cleanupSessionCaches() in the event-reducer —
freeing all in-memory messages, parts, diffs, permissions, and status
for the subagent session. The task_id in the output becomes a dead
reference; if the LLM tries to resume, Session.get() fails gracefully
and a fresh session is created. Validated: the cleanup infrastructure
already existed but was never invoked for subagent sessions.

I-7046-A (CRITICAL, Priority anomalyco#3) — session/compaction.ts: clear
part.state.output and part.state.attachments when pruning compacted
tool parts. Previously, prune() set time.compacted but left the full
output string in both the DB row and the in-memory store.
toModelMessages already substituted "[Old tool result content cleared]"
for compacted parts — this change aligns stored data with that
behavior, freeing the large strings from memory and disk.

PR-14635 (HIGH, Priority anomalyco#4) — TUI event listener cleanup:
- app.tsx: save the unsubscribe functions returned by all 6
  sdk.event.on() calls; call them in a single onCleanup() handler.
  Previously, onCleanup was not even imported.
- routes/session/index.tsx: save and clean up the message.part.updated
  listener. This component mounts/unmounts during session navigation,
  so each navigation previously added a duplicate listener.
- component/prompt/index.tsx: save and clean up the PromptAppend
  listener. Same mount/unmount pattern as the session component.

I-7046-C (partial) — the TUI event listener fixes above cover the
most impactful instances of the missing-dispose pattern. A full audit
of all subscribe() call sites remains as follow-up work.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
praxstack referenced this pull request in praxstack/opencode Feb 25, 2026
…r details

- retry.ts: respect isRetryable:false and 4xx status in JSON error bodies (Issue #7)
- provider.ts: cap Bedrock Anthropic models at 200K context (Issue #4)
- batch.ts: include per-tool error details in failure output (Issue #9)
- retry.test.ts: 5 new tests for catch-all retry classification
tamarazuk added a commit to tamarazuk/opencode that referenced this pull request Mar 3, 2026
…odies

- Refresh VCS state before idempotency check in create() to avoid TOCTOU race (anomalyco#4, anomalyco#12)
- Use refreshed branch/defaultBranch in fallback PR return (anomalyco#9)
- Validate base branch exists before attempting gh pr create (anomalyco#22)
- Wrap review comment bodies in fenced blocks for structural separation (anomalyco#8)
sjawhar added a commit to sjawhar/opencode that referenced this pull request Mar 8, 2026
- Finding #1: re-sync active sessions after disposal by saving fullSyncedSessions
  before clear and calling result.session.sync() after successful bootstrap
- Finding #2: cap retries at 5 with exponential backoff (2s/4s/8s/16s/30s) and
  generation counter to cancel stale retries when newer disposal arrives
- Finding anomalyco#3: move Instance.provide() inside while loop so each event stream
  reconnect iteration gets fresh context
- Finding anomalyco#4: remove unconditional startEventStream(process.cwd()); add events RPC
  method; thread calls it only when not in external transport mode
- Finding anomalyco#5: replace manual new Promise+setTimeout with sleep() from node:timers/promises
- Finding anomalyco#6: reload() aborts current event stream and conditionally restarts it
sjawhar added a commit to sjawhar/opencode that referenced this pull request Mar 8, 2026
- Finding #1: re-sync active sessions after disposal by saving fullSyncedSessions
  before clear and calling result.session.sync() after successful bootstrap
- Finding #2: cap retries at 5 with exponential backoff (2s/4s/8s/16s/30s) and
  generation counter to cancel stale retries when newer disposal arrives
- Finding anomalyco#3: move Instance.provide() inside while loop so each event stream
  reconnect iteration gets fresh context
- Finding anomalyco#4: remove unconditional startEventStream(process.cwd()); add events RPC
  method; thread calls it only when not in external transport mode
- Finding anomalyco#5: replace manual new Promise+setTimeout with sleep() from node:timers/promises
- Finding anomalyco#6: reload() aborts current event stream and conditionally restarts it
sjawhar added a commit to sjawhar/opencode that referenced this pull request Mar 8, 2026
- Finding #1: re-sync active sessions after disposal by saving fullSyncedSessions
  before clear and calling result.session.sync() after successful bootstrap
- Finding #2: cap retries at 5 with exponential backoff (2s/4s/8s/16s/30s) and
  generation counter to cancel stale retries when newer disposal arrives
- Finding anomalyco#3: move Instance.provide() inside while loop so each event stream
  reconnect iteration gets fresh context
- Finding anomalyco#4: remove unconditional startEventStream(process.cwd()); add events RPC
  method; thread calls it only when not in external transport mode
- Finding anomalyco#5: replace manual new Promise+setTimeout with sleep() from node:timers/promises
- Finding anomalyco#6: reload() aborts current event stream and conditionally restarts it
tamarazuk added a commit to tamarazuk/opencode that referenced this pull request Mar 21, 2026
…odies

- Refresh VCS state before idempotency check in create() to avoid TOCTOU race (anomalyco#4, anomalyco#12)
- Use refreshed branch/defaultBranch in fallback PR return (anomalyco#9)
- Validate base branch exists before attempting gh pr create (anomalyco#22)
- Wrap review comment bodies in fenced blocks for structural separation (anomalyco#8)
ESRE-dev added a commit to ESRE-dev/opencode that referenced this pull request Mar 27, 2026
    * Restructure watchdogTick so idle-detection sweeps
      SessionActivity.list() independently of stuck-tool
      query results — fixes Bug anomalyco#4 (idle gated behind
      stuck-tool results) and Bug anomalyco#5 (leaf.length === 0
      early return skipping idle detection entirely)
    * Add pre-cancel map cleanup in the idle sweep to
      prevent unbounded growth of stale entries
    * Define CancelRequested bus event in processor and
      publish from abortChildren so child sessions get
      in-memory cancel alongside the DB updates (Bug anomalyco#7)
    * Add SessionPrompt.init() subscribing to the event,
      called from bootstrap alongside SessionActivity
    * Wrap AI SDK iterator consumption in try/finally to
      call iter.return() on abort, preventing leaked HTTP
      connections (Bug anomalyco#8)
    * Check abort signal before retry continue to stop
      cancelled sessions from restarting loops (Bug anomalyco#9)
    * Remove shadowed abort promise in permission-check
      loop so pre-aborted signals reject immediately
      instead of hanging (Bug anomalyco#10)
    * Update watchdog tests for independent idle sweep
      and add cancel-propagation event tests
ESRE-dev added a commit to ESRE-dev/opencode that referenced this pull request Mar 27, 2026
    * Restructure watchdogTick so idle-detection sweeps
      SessionActivity.list() independently of stuck-tool
      query results — fixes Bug anomalyco#4 (idle gated behind
      stuck-tool results) and Bug anomalyco#5 (leaf.length === 0
      early return skipping idle detection entirely)
    * Add pre-cancel map cleanup in the idle sweep to
      prevent unbounded growth of stale entries
    * Define CancelRequested bus event in processor and
      publish from abortChildren so child sessions get
      in-memory cancel alongside the DB updates (Bug anomalyco#7)
    * Add SessionPrompt.init() subscribing to the event,
      called from bootstrap alongside SessionActivity
    * Wrap AI SDK iterator consumption in try/finally to
      call iter.return() on abort, preventing leaked HTTP
      connections (Bug anomalyco#8)
    * Check abort signal before retry continue to stop
      cancelled sessions from restarting loops (Bug anomalyco#9)
    * Remove shadowed abort promise in permission-check
      loop so pre-aborted signals reject immediately
      instead of hanging (Bug anomalyco#10)
    * Update watchdog tests for independent idle sweep
      and add cancel-propagation event tests
andreipromarketing-dev pushed a commit to andreipromarketing-dev/opencode that referenced this pull request Apr 7, 2026
…t-errors-another-one

Fix markdownlint errors (MD038, MD058, MD025, MD034)
andreipromarketing-dev pushed a commit to andreipromarketing-dev/opencode that referenced this pull request Apr 7, 2026
* feat: add pending instinct TTL pruning and /prune command

Pending instincts generated by the observer accumulate indefinitely
with no cleanup mechanism. This adds lifecycle management:

- `instinct-cli.py prune` — delete pending instincts older than 30 days
  (configurable via --max-age). Supports --dry-run and --quiet flags.
- Enhanced `status` command — shows pending count, warns at 5+,
  highlights instincts expiring within 7 days.
- `observer-loop.sh` — runs prune before each analysis cycle.
- `/prune` slash command — user-facing command for manual pruning.

Design rationale: council consensus (4/4) rejected auto-promote in
favor of TTL-based garbage collection. Frequency of observation does
not establish correctness. Unreviewed pending instincts auto-delete
after 30 days; if the pattern is real, the observer will regenerate it.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>

* fix: remove duplicate functions, broaden extension filter, fix prune output

- Remove duplicate _collect_pending_dirs and _parse_created_date defs
- Use ALLOWED_INSTINCT_EXTENSIONS (.md/.yaml/.yml) instead of .md-only
- Track actually-deleted items separately from expired for accurate output
- Update README.md and AGENTS.md command counts: 59 → 60

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>

* fix: address Copilot and CodeRabbit review findings

- Use is_dir() instead of exists() for pending path checks
- Change > to >= for --max-age boundary (--max-age 0 now prunes all)
- Use CLV2_PYTHON_CMD env var in observer-loop.sh prune call
- Remove unused source_dupes variable
- Remove extraneous f-string prefix on static string

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>

* fix: update AGENTS.md project structure command count 59 → 60

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: address cubic and coderabbit review findings

- Fix status early return skipping pending instinct warnings (cubic anomalyco#1)
- Exclude already-expired items from expiring-soon filter (cubic anomalyco#2)
- Warn on unparseable pending instinct age instead of silent skip (cubic anomalyco#4)
- Log prune failures to observer.log instead of silencing (cubic anomalyco#5)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: YAML single-quote unescaping, f-string cleanup, add /prune to README

- Fix single-quoted YAML unescaping: use '' doubling (YAML spec) not
  backslash escaping which only applies to double-quoted strings (greptile P1)
- Remove extraneous f-string prefix on static string (coderabbit)
- Add /prune to README command catalog and file tree (cubic)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Happy <yesreply@happy.engineering>
guazi04 pushed a commit to guazi04/opencode that referenced this pull request Apr 8, 2026
fix: 压缩核心工具描述(bash/todowrite/task),每条消息节省 ~4k tokens

Closes anomalyco#4

See merge request iself-team/opencode!8
zhangdw156 added a commit to zhangdw156/opencode that referenced this pull request Apr 16, 2026
* chore: bootstrap benchmark runner type contracts

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>

* fix: improve adapter contract test with realistic params and assertions

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>

* feat: add benchmark hijack service with synthetic stream

* fix: pass explicit enabled flag in hijack tests, clear queue on unbind

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>

* feat: wire benchmark hijack branch into llm run pipeline

* feat: add benchmark runner core utilities and scoring

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>

* feat: add LongMemEval and LongBench-v2 benchmark adapters

* feat: add BEAM adapter and end-to-end benchmark runner skeleton

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>

* feat: wire end-to-end benchmark evaluation with hijack Phase A/B flow

- Promote BenchmarkHijack to Effect Service for DI across server routes and LLM
- Add /benchmark/* HTTP routes for hijack control (bind/unbind/enqueue/clear/status)
- Create benchmark-server.ts minimal entry point (avoids TUI/react deps)
- Rewrite anchor-eval.ts with full Phase A (hijack replay) + Phase B (real LLM probe)
- Implement HijackClient HTTP fetch wrapper
- Fix tryStream to return null on empty queue (prevents title-gen race condition)
- Skip hijack for small=true LLM calls (title generation)
- Add run-all-plugins.ts for 7-plugin comparison evaluation
- Verified end-to-end: all 7 plugins complete with LongMemEval (550 turns, 1 probe)

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>

* feat: refactor memory plugins to use shared LLM/embedding config and simplify implementations

Remove regex-based fallbacks and hardcoded logic across all memory plugins,
wiring them to use the shared config for LLM and embedding calls. Update
benchmark runner and test harness accordingly.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>

* fix: sanitize FTS5 queries and auto-load plugin env in benchmark runner

- Sanitize FTS5 MATCH queries in all 7 plugins: strip special chars
  (?, *, ", etc.), quote each token, join with OR. This fixes the root
  cause of 0% benchmark accuracy — FTS5 syntax errors on probe questions
  containing special characters were silently swallowed by catch blocks.
- Remove regex fallback and embedding null guards from plugin index files,
  making LLM/embedding required (per shared config refactor).
- Auto-load plugins/opencode/.env in run-all-plugins.ts so benchmark
  runner works without manual `set -a; source .env`.
- Verified: BEAM benchmark limit=1 on mem0 achieves 5% accuracy (1/20
  probes correct), confirming end-to-end memory storage and retrieval.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>

---------

Co-authored-by: claude <claude@example.com>
Co-authored-by: Claude Opus 4 <noreply@anthropic.com>
Rwanbt added a commit to Rwanbt/opencode that referenced this pull request Apr 19, 2026
…LLM paths

A panic while holding any of our Mutex<Option<Child>> / Mutex<Option<Engine>>
locks was poisoning the state permanently — the next lock() would panic
again, so TTS/STT/LLM were dead until the app restarted.

Introduce MutexSafe trait in util.rs (same spirit as mobile speech.rs
lock_safe) and migrate all 33 call sites in llm.rs, speech.rs and lib.rs
to state.child.lock_safe(). Poisoned-mutex cases now log a warning and
keep serving — the guarded state (process handles, load flags) is
idempotent across calls so resuming is safe.

Closes anomalyco#4/21.
sorted-ai-bot pushed a commit to sorted-ai/opencode that referenced this pull request Apr 25, 2026
Bridge was dying on startup because Hatch MCP client called tools/list
before Java core finished registering capabilities. Added poll loop
(max 20s) that waits for loaded_count > 0 before opening session and
exec'ing bridge.

Also records: MCPHUB alpha architecture rework pending (Go daemon
scrap → Java-centric). Spec deviated from Proposal §4.3 'thin wrapper'
intent. Post-rework, bridge connection issues #1/#3/anomalyco#4/anomalyco#5/anomalyco#8 are
expected to be structurally eliminated.

Hatch-side backlog unchanged:
- HMD-03 VT-013/014: 50-turn verification (in progress)
- HMD-02: deferred hack deletion (blocked on HMD-03)
- anomalyco#6 websearch empty responses: Hatch adapter issue
- anomalyco#7 webfetch HTML bloat: Hatch adapter issue
kitlangton added a commit that referenced this pull request May 1, 2026
… gap #4 phase 1)

Adds the parallel `runNative()` path inside `session/llm.ts` so a narrow
slice of sessions can flow through `@opencode-ai/llm` instead of the AI
SDK `streamText`. Behavior is gated and shipped off by default; only
callers that opt in see any difference.

The full migration plan (audit gap #4) is parallel-path-with-flag,
prove parity test-by-test, flip default last. This commit is phase 1:
get the wire-up in place behind a flag with one protocol so we can see
whether the design holds before committing to the full migration.

Wire-up summary:

- New flag `OPENCODE_EXPERIMENTAL_LLM_NATIVE` (also enabled by the
  umbrella `OPENCODE_EXPERIMENTAL`). Off by default.
- The session-LLM `live` layer now consumes `RequestExecutor.Service`,
  and the `defaultLayer` provides `RequestExecutor.defaultLayer` so a
  Node fetch HTTP client backs every native stream.
- `runNative(input)` returns `Stream<Event> | undefined`. `undefined`
  means "fall through to AI SDK." It returns a real stream only when
  every gate passes: the flag is set, the caller populated
  `input.nativeMessages` (the bridge needs typed `MessageV2.WithParts`,
  not the AI SDK `messages` array), the session has zero tools (Phase
  2 will lift this), and the bridge routes the model to a protocol in
  `NATIVE_PROTOCOLS`.
- `NATIVE_PROTOCOLS` is a single-entry set today: `anthropic-messages`.
  Other adapters are imported and registered with the client so the
  Phase 2 expansion is a one-line edit, not an architecture change.
- Stream wiring: client.stream(req) -> Stream.flatMap(event ->
  fromIterable(map.map(event))) -> Stream.concat(suspended
  fromIterable(map.flush())) -> Stream.provideService(
  RequestExecutor.Service, executor). The flush stream is built lazily
  with `Stream.unwrap(Effect.sync(...))` so it observes the mapper
  final state after every upstream event has been mapped.
- The mapper (`LLMNativeEvents.mapper`) emits AI-SDK-shaped session
  events from `LLMEvent` so downstream consumers see one shape.

What this does NOT do (deferred to later phases):

- No tool support on the native path (skipped, falls through).
- No parity harness yet; Phase 2 builds it.
- No production traffic; flag is off by default and no production
  caller populates `nativeMessages`.
- No reasoning/cache/multi-modal coverage. Anthropic supports reasoning
  and cache via existing patches, so those start working as soon as a
  caller routes a real session through.

Verification: opencode typecheck clean, bridge tests still green
(33/0/0 across llm-native.test.ts + llm-bridge.test.ts); LLM package
tests green (123/0/0).
kitlangton added a commit that referenced this pull request May 1, 2026
…phase 2)

Adds `test/session/llm-native-stream.test.ts` — one focused test that
proves the end-to-end wire-up `runNative` relies on actually produces
session events from a scripted Anthropic SSE response.

The test stays self-contained:

- Builds a fake Anthropic `Provider.Info` + `Provider.Model` via
  `ProviderTest`.
- Builds an `LLMRequest` via `LLMNative.request(...)` from a
  `MessageV2.WithParts` user message — the same call shape `runNative`
  uses inside `session/llm.ts`.
- Creates an `LLMClient` with the same adapters list + `ProviderPatch.defaults`
  list as `runNative`. The adapters are imported directly from
  `@opencode-ai/llm`; if `runNative`'s `NATIVE_ADAPTERS` array changes,
  this test's `adapters` constant has to follow (commented).
- Provides a single fixed-response HTTP layer that returns a scripted
  Anthropic SSE body. The layer helper is inlined (12 lines) rather
  than imported from `packages/llm/test/lib/http.ts` so the test
  doesn't reach across package boundaries.
- Pipes the LLM stream through `LLMNativeEvents.mapper()` exactly as
  `runNative` does (`Stream.flatMap` + lazy `Stream.concat` for
  flush), runs it to completion, and asserts the key session events:
  `text-start` precedes `text-delta`, `finish-step` carries
  `finishReason: "stop"`, and `finish` carries the merged usage totals.

This does NOT test the dispatch gate inside `session/llm.ts`
(`!Flag.OPENCODE_EXPERIMENTAL_LLM_NATIVE`, missing `nativeMessages`,
tools present, non-Anthropic protocol). Those are simple boolean
conditions and don't need separate coverage. It also does not exercise
the production `Service` layer — that's deferred to Phase 2 step 2
(tool support) and Phase 2 step 3 (production caller wiring).

What the test buys: confidence that the conversion pipeline works and
catches regressions in `LLMNative.request`, the LLM adapter set, or
`LLMNativeEvents.mapper` before they would surface in a real session.

Verification: 34/0/0 across the three bridge-area tests
(`llm-native.test.ts` + `llm-native-stream.test.ts` +
`llm-bridge.test.ts`); opencode typecheck clean.
kitlangton added a commit that referenced this pull request May 1, 2026
…hase 2 step 2a)

Adds opt-in `nativeTools?: ReadonlyArray<Tool.Def>` to `LLM.StreamInput`
so callers that route through the native path can attach typed
opencode tool definitions alongside the AI SDK `tools` record. The
gate in `runNative` widens accordingly: a session can use the native
path when it has zero tools (existing behavior) OR when it explicitly
provides `nativeTools` matching its AI SDK `tools` (new opt-in). When
`nativeTools` reaches `LLMNative.request`, the existing
`toolDefinition` converter folds each `Tool.Def` into the request's
`tools` array and the LLM core lowers it onto the wire.

This commit deliberately does NOT include the dispatch loop. A
session that opts in by setting `nativeTools` and that triggers a
`tool-call` from the model will see the call event but no
`tool-result` because the native path has no execute handler yet.
That's why no production caller populates `nativeTools`: phase 2
step 2b will land the dispatch loop and only then will real
production sessions route through here.

What this lays in place:

- `StreamInput.nativeTools` typed against `Tool.Def[]` from `@/tool`.
  Aliased to `OpenCodeTool` at the import to dodge a clash with the
  AI SDK `Tool` type that the same file already imports.
- The `runNative` gate flips from "no tools allowed" to "either no
  tools, or `nativeTools` is supplied". An AI SDK tool count > 0
  with `nativeTools` undefined still falls through, so existing
  production sessions are unaffected.
- `LLMNative.request` already accepted `tools: ReadonlyArray<Tool.Def>`
  and converts via `toolDefinition`. We just forward the input
  through; no LLM-bridge change.

Smoke coverage: a new test in `llm-native-stream.test.ts` builds a
typed `Tool.Def` (Effect Schema parameters), routes it through
`LLMNative.request` + `LLMClient.prepare`, and asserts the prepared
Anthropic target carries the tool as an `input_schema` block with
the expected JSON Schema shape. This validates the conversion path
that phase 2 step 2b will exercise from inside `runNative`.

Verification: opencode typecheck clean; 35/0/0 across the three
bridge-area tests (`llm-native.test.ts`, `llm-native-stream.test.ts`,
`llm-bridge.test.ts`).
kitlangton added a commit that referenced this pull request May 1, 2026
…ative path (audit gap #4 phase 2 step 2b)

Lands the streaming-dispatch tool loop for the LLM-native path. When
the gate-passing session has `nativeTools` populated, the native
runner forks an AI SDK `tool.execute(...)` the moment a `tool-call`
event arrives mid-stream and injects a synthetic `tool-result` event
back into the same stream when the handler resolves. Long-running
tools no longer block subsequent tool-call streaming; the user sees
each result land as soon as that specific handler completes.

The driver loops across rounds: when a round ends with `reason:
"tool-calls"` AND the dispatchers produced at least one result, the
runner builds a continuation `LLMRequest` (assistant message echoing
text/reasoning/tool-call content + tool messages with results) and
recurses. Stops on a non-`tool-calls` finish, when `maxSteps`
(default 10, mirrors `ToolRuntime.run`) is reached, or when the
underlying scope is interrupted.

New file `session/llm-native-tools.ts`:

- `runWithTools({ client, request, tools, abort, maxSteps? })` is the
  public entry point. Returns a `Stream<LLMEvent, LLMError,
  RequestExecutor.Service>` of merged model events + synthetic tool
  results, ready to flow through `LLMNativeEvents.mapper` for
  consumption by the existing session processor.
- `runOneRound` is the internal building block. It opens an unbounded
  `Queue<LLMEvent, LLMError | Cause.Done>`, forks a producer that
  streams the model and pushes each event to the queue, and forks a
  dispatcher (via a scope-bound `FiberSet`) for every
  non-provider-executed `tool-call`. Each dispatcher's result is
  pushed back into the same queue. After the model stream completes,
  the producer awaits `FiberSet.awaitEmpty` and ends the queue;
  consumers see end-of-stream. A `Deferred<RoundState>` resolves
  alongside so the multi-round driver can decide whether to recurse.
- `dispatchTool` wraps the AI SDK `tool.execute(input, { toolCallId,
  messages, abortSignal })` call. Unknown-tool and execute-throws
  paths produce `tool-error` events instead of failing the stream
  (mirrors `ToolRuntime.run`'s defect-vs-recoverable boundary), so
  the model can self-correct on the next round.

Wired into `runNative` (`session/llm.ts`): when `input.nativeTools`
is non-empty, the upstream becomes `LLMNativeTools.runWithTools(...)`
instead of `nativeClient.stream(...)`; the AI SDK `tools` record
flows in as the dispatch table. Zero-tool sessions still take the
direct-stream path (one round, no dispatch overhead).

Mapper update (`session/llm-native-events.ts`): `tool-result` events
whose `result.value` matches the opencode `Tool.ExecuteResult` shape
(`{ output: string, title?: string, metadata?: object }`) now flow
through to the AI-SDK-shaped session event with their `title` and
`metadata` preserved. Provider-executed and synthetic results that
don't match still fall back to `stringifyResult`. Without this, the
session processor would see every native tool result as
`{ title: "", metadata: {}, output: <JSON of the whole record> }`.

Smoke test (`test/session/llm-native-stream.test.ts`): scripts a
two-round Anthropic SSE backend — round 1 issues a `lookup` tool
call, round 2 replies with text after the tool result feeds back.
Asserts the full event sequence threads through `runWithTools`,
the dispatcher, and the mapper:

- `tool-call` event has the streamed JSON input parsed.
- `tool-result` event carries the `ExecuteResult` shape with
  `title` + `output` populated (proving the mapper update works).
- Round 2 text-delta arrives after the synthetic tool-result.
- Final `finish` event has `finishReason: "stop"` (loop terminated).

What this still does NOT do (deferred to step 3):

- No production caller populates `nativeTools` yet; that's the
  `prompt.ts:resolveTools` change. Until that lands, the gate keeps
  every real session on the AI SDK path.
- No parity harness comparing native + AI SDK event sequences for
  the same scripted session. That's step 4.

Verification: opencode typecheck clean; 36/0/0 across the three
bridge-area tests; 125/0/0 across the LLM package.
kitlangton added a commit that referenced this pull request May 1, 2026
…sions can route through the native path (audit gap #4 phase 2 step 3)

Wires the prompt-side tool resolver to also surface opencode-native
`Tool.Def[]` alongside the AI SDK record it already builds. With
`OPENCODE_EXPERIMENTAL_LLM_NATIVE=1` set, real production sessions
that satisfy the gate now stream through `LLMNativeTools.runWithTools`
instead of `streamText` — the LLM-native path goes from
"plumbing-only" to "actually used."

Changes:

- `prompt.ts:resolveTools` collects `Tool.Def[]` from the registry
  loop and tracks a feasibility flag. MCP tools (which only have AI
  SDK shape) flip the flag off; the synthesized `StructuredOutput`
  tool that the json_schema branch injects also flips it. The return
  shape becomes `{ tools, nativeTools }` where `nativeTools` is
  `undefined` whenever any non-registry tool source contributes —
  callers fall through to the AI SDK path automatically. The
  registry path stays in sync because every `tools[item.id] =
  tool({...})` is paired with a `nativeTools.push(item)` at the same
  loop iteration.

- The single caller (`prompt.ts:1396`) destructures the new shape
  and passes `nativeTools` through to `handle.process(...)`. The
  json_schema branch sets `nativeTools = undefined` after injecting
  `StructuredOutput` so the gate falls through for structured-output
  sessions.

- `runNative` (in `session/llm.ts`) gains two safety nets that work
  regardless of caller behavior:

    1. Coverage check: if AI SDK tools are non-empty, every key must
       have a matching `Tool.Def` in `nativeTools`. A partial set
       falls through. Defends against future callers that might
       emit a partial native list.

    2. Filter parity: `runNative` now calls the existing
       `resolveTools(input)` (the in-file permission/user-disabled
       filter) and intersects its keys with `nativeTools`, then
       feeds the filtered AI SDK record to the dispatcher and the
       filtered native list to `LLMNative.request`. Without this,
       sessions could see permission-disabled tools advertised on
       one path but not the other.

- The dispatch path uses the filtered AI SDK tools record as the
  execute table: `LLMNativeTools.runWithTools({ tools:
  filteredAITools, ... })`. Tool definitions sent to the model are
  the filtered native list. Every tool the model sees can dispatch.

What this enables: a session opted into the experimental flag, with
a clean toolset (registry-only, no MCP, no structured output),
running an Anthropic model, now exercises the streaming-dispatch
loop end-to-end. Tool calls fire as soon as the model finishes
streaming each tool's input; results land in the stream the moment
each handler resolves. Multi-round behavior matches phase 2 step 2b.

What this still does NOT do (deferred to step 4):

- Parity test harness comparing native vs AI SDK event sequences for
  the same scripted session. Until that lands, broader confidence
  comes from running real sessions with the flag set.
- MCP support on the native path. Sessions with MCP servers
  configured stay on AI SDK indefinitely.
- Native support for the synthesized `StructuredOutput` tool.

Verification: opencode typecheck clean for `src/session/*` (the
TUI-side errors visible in the working tree are Kit's parallel
work, untouched here); bridge area tests 36/0/0 across
`llm-native.test.ts` + `llm-native-stream.test.ts` +
`llm-bridge.test.ts`; `prompt.test.ts` still 47/0/0 (no regression
from the resolveTools shape change).
zoulukuang pushed a commit to zoulukuang/deskfox that referenced this pull request May 3, 2026
anomalyco#1 Rust write_text_file 接 root+path 用 PathBuf::join,因为
    opencode 的 pathFromTab 返回相对 workspace root 的路径,
    之前直接传给 std::fs::write 会相对进程 cwd 解析失败
    (Phase 2 时用绝对路径测试,没暴露此 bug)

anomalyco#2 saveEdit 写盘成功后 await file.load(p, { force: true })
    强制从磁盘重读,否则 UI 显示改前 stale 内容

user 已亲验 anomalyco#1 修复("保存生效"),anomalyco#2 待本 commit rebuild 后验。
详见 改动日志.md anomalyco#4。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
zoulukuang pushed a commit to zoulukuang/deskfox that referenced this pull request May 3, 2026
…e force 真生效 [feat: file-tree-dnd]

新文件:
- packages/app/src/context/file/undo-stack.ts:createUndoStack 容量 20,push move/copy entry,pop 用注入的 reverter 跑反向
- docs/features/file-tree-dnd/4-test-checklist.md:全套 32 条测试清单(A-G 七组),user 每次 build 对照

Tauri 改动:
- text_file.rs: write_binary_file_absolute_base64(path, base64) — 解码 base64 + fs::write,500MB 上限,已存在则 already_exists
- lib.rs: 注册新命令到 invoke_handler

前端改动:
- tree-store.ts(关键 fix):force=true 必须绕过 inflight check,否则等到旧 stale promise → 拖放后源父目录不刷新
- file.tsx:挂 undoStack 到 useFile()
- file-tree.tsx:
  - handleMoveDrop / pasteTo push undo entry
  - 新增 handleExternalDrop(File[]) 走 FileReader → base64 → Tauri 写盘(Tauri webview 不暴露 file.path,只能读内容)
  - undoLast: 从栈 pop 执行反向 rename/trash + 刷新涉及目录
  - Ctrl+Z 接入 useFileTreeShortcuts(只在 level 0 注册)
  - dropHandlers 接受外部 OS files(dataTransfer.types 含 "Files"),onDrop 优先处理 dataTransfer.files

详见 docs/features/file-tree-dnd/3-changelog.md "Commit anomalyco#4" 段。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
zoulukuang pushed a commit to zoulukuang/deskfox that referenced this pull request May 3, 2026
…: file-tree-dnd]

- 改动日志.md 加 file-tree-dnd 索引行(4 笔 commit hash 列出)
- docs/features/INDEX.md 把 file-tree-dnd 标 done
- 4 个三文档全部 status: in-progress → done
- docs/features/file-tree-dnd/3-changelog.md 回填 commit anomalyco#4 hash b9a4acc
- .gitignore 加 .obsidian/(user 用 Obsidian 浏览 docs/features/,本地配置不入库)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
zoulukuang pushed a commit to zoulukuang/deskfox that referenced this pull request May 3, 2026
审计发现 spec / plan 还停留在"规划阶段方案"层面,跟实际落地的最终方案有偏差。
本笔把 1-spec / 2-plan / 3-changelog 全部对齐到当前已 commit 的状态。

主要更新:
- 1-spec.md:验收标准从 unchecked 占位改成 [x] 已通过;架构选型增加"外部文件 drop 终选方案"小节(撤销最初的 onDragDropEvent + dragDropEnabled 方案,改 FileReader → base64 → write_binary_file_absolute_base64)
- 2-plan.md:Commit anomalyco#4 实施步骤改成 FileReader 对应实际方案;决策轨迹补 D4-D9(路径分隔符 / tree-store force fix / hook 多注册 / {...rest} 覆盖 / 外部 drop 终选 / build rename Defender 锁)
- 3-changelog.md:总体 review 自检勾上 + 加 Commit anomalyco#5 / anomalyco#6 条目 + 回退方法填实际 hash

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
dgokeeffe pushed a commit to dgokeeffe/opencode-databricks that referenced this pull request May 5, 2026
… reproducer context

Code Reviewer subagent (fresh-context, with codebase access) flagged:
- AI SDK line ref off by 18 (~L6283 → L6265 in ai@6.0.158, the resolved
  version per node_modules symlink)
- Quirks anomalyco#3/anomalyco#6/anomalyco#7/anomalyco#8 not in transform.ts as the opener implied (anomalyco#7 in
  processor.ts, anomalyco#8 in llm.ts, anomalyco#6 in provider discovery)
- "Two full days" / "every quirk within an hour" framing reads as venting
  to a PM audience; strip
- Asks anomalyco#3+anomalyco#4 should be merged into one Responses-API-parity workstream
- Reference-client ask should be #2 (highest-leverage deliverable)
- Missing reproducer context: package versions, endpoint name

Apply all of the above. Add direct GitHub links from the SDK-package quirks
to upstream source lines and from the workaround text to llm.ts:407–461 so
internal recipients can hand off to the SDK-package team without re-reading
this doc. Add a versions-tested header.

Co-authored-by: Isaac
Rwanbt added a commit to Rwanbt/opencode that referenced this pull request May 5, 2026
…LLM paths

A panic while holding any of our Mutex<Option<Child>> / Mutex<Option<Engine>>
locks was poisoning the state permanently — the next lock() would panic
again, so TTS/STT/LLM were dead until the app restarted.

Introduce MutexSafe trait in util.rs (same spirit as mobile speech.rs
lock_safe) and migrate all 33 call sites in llm.rs, speech.rs and lib.rs
to state.child.lock_safe(). Poisoned-mutex cases now log a warning and
keep serving — the guarded state (process handles, load flags) is
idempotent across calls so resuming is safe.

Closes anomalyco#4/21.
dgokeeffe pushed a commit to dgokeeffe/opencode-databricks that referenced this pull request May 6, 2026
…xTools cap

Add script/probe-aigw-quirks.ts — a reproducible per-family test of every
catalogued quirk against AI Gateway, run once and committed so future
sessions can re-verify in seconds. Replaces the partly-inferred AI Gateway
status column with on-the-wire evidence.

Findings (running against the aigw workspace, Claude / GPT / Gemini where
applicable):

  #1 schema strict — STILL ACTIVE on all 3 families (server-side, harmless
     to opencode because our adapters generate proper schemas)
  #2/anomalyco#11 oversized itemId — STILL ACTIVE (GPT only)
  anomalyco#3 asymmetric streaming events — GONE (perfectly symmetric on aigw)
  anomalyco#4 reasoning_effort — works correctly (reasoning_tokens returned)
  anomalyco#5 cache_control — works correctly (verified with two-call populate+hit
     test; my earlier "fail" reading was a 1212-token prompt under the
     effective cache threshold)
  anomalyco#6 task=null on Codex/GPT-5 metadata — GONE (all 3 families now return
     task: "llm/v1/chat" correctly)
  anomalyco#7 finish_reason "other" — STILL ACTIVE (GPT)
  anomalyco#8 per-endpoint 89-tool cap — GONE on AI Gateway (verified all 3
     families accept 89 tools; cap was a model-serving constraint)
  anomalyco#9, anomalyco#10 npm package lifecycle bugs — INERT on AI Gateway (we use
     @ai-sdk/openai, bundled provider not loaded)
  anomalyco#12 mismatched item IDs — STILL ACTIVE (GPT)

Net: of 12 quirks, 4 still bite on AI Gateway (#1, #2/anomalyco#11, anomalyco#7, anomalyco#12),
4 are gone (anomalyco#3, anomalyco#6, anomalyco#8, plus anomalyco#4 always worked), anomalyco#5 works correctly, and
2 are bypassed by adapter choice. Anthropic and Gemini paths through
@ai-sdk/anthropic and @ai-sdk/google are clean on AI Gateway.

Code change consequence: drop the maxTools: 16 cap in familyDefaults for
gpt/codex when on the AI Gateway path (anomalyco#8 is gone there). Cap stays on
the model-serving path. 3-class regression test passes 3/3 on both
surfaces after the change.

Doc: replace the inferred AI Gateway status column with the empirical
matrix, citing the probe script and the specific evidence per quirk.

Co-authored-by: Isaac
zoulukuang added a commit to zoulukuang/deskfox that referenced this pull request May 7, 2026
… md-export-pdf-word] [override-blacklist: bun.lock 自动重生 — 本 feat 范围内一次性]

bun install 触发的 lockfile 自动重排(3453 ins / 3417 del),不是手动改。
wrapper 不可行性:bun.lock 是 bun 工具链生成,无法 wrapper / 改写;所有"加 dep" feat 都触动。
风险:0(纯 lockfile 重排,运行时行为不变,内容是依赖树规范化)。
本笔 R4 override 范围:仅本 feat anomalyco#1 commit 一笔;后续 anomalyco#2/anomalyco#3/anomalyco#4 不再触动 lockfile。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
zoulukuang added a commit to zoulukuang/deskfox that referenced this pull request May 7, 2026
…rite 加 allow_overwrite [feat: md-export-pdf-word]

根因:write_binary_file_absolute_base64 是给文件拖入用的(commit anomalyco#4 file-tree-dnd),
故意校验"不存在"防止覆盖。导出 Word 复用此命令但 save dialog 已让用户确认替换,
后端拦截导致 toast "already_exists: /path/to.docx" + 文件实际未替换(数据风险)。

修:rust 加 allow_overwrite: Option<bool> 参数(default false 保拖入场景语义不变),
导出端 invoke 传 allowOverwrite: true。其他调用点(markdown-editor-extensions / file-tree)
不传 → undefined → unwrap_or(false) → 仍校验 already_exists,无回归。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
schaefer-iib-tu-darmstadt added a commit to schaefer-iib-tu-darmstadt/iibcode that referenced this pull request May 7, 2026
IIBCODE_QUICKSTART.md becomes README.md so GitHub renders the
fork-specific docs first. The upstream English README + 21 translations
move to docs/upstream-readme/ (git mv preserves their history).

.gitattributes marks both paths merge=ours so 'git merge upstream/dev'
auto-keeps our version. Requires a one-time per-clone setup since the
driver itself isn't committed:

    git config merge.ours.driver true

CLAUDE.md gotchas anomalyco#4 and anomalyco#5 document this and how to handle a newly
added upstream translation file (the rule only fires for existing
paths, so a brand-new README.cs.md slips through and needs a manual
git mv).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: model config persistence

2 participants