Skip to content

fix: migration ModelName lookup, reasoning_content, shell regex, loop boundary#932

Merged
4 commits merged intosipeed:mainfrom
putueddy:fix/bugfixes
Mar 7, 2026
Merged

fix: migration ModelName lookup, reasoning_content, shell regex, loop boundary#932
4 commits merged intosipeed:mainfrom
putueddy:fix/bugfixes

Conversation

@putueddy
Copy link
Contributor

@putueddy putueddy commented Mar 1, 2026

Summary

  • migration.go: Set ModelName to userModel when the user's configured provider matches during ConvertProvidersToModelList. Previously the migration created entries with the provider name as ModelName (e.g. "moonshot") but GetModelConfig() looked up using the model name (e.g. "k2p5"), causing "model not found" errors and preventing gateway startup.

  • openai_compat/provider.go: Preserve reasoning_content in conversation history sent back to APIs. Thinking models (e.g. Kimi K2, DeepSeek-R1) return reasoning_content which must be echoed back in subsequent requests. Without it, APIs return 400: "thinking is enabled but reasoning_content is missing in assistant tool call message".

  • shell.go: Fix deny pattern regex for format/mkfs/diskpart — use (?:^|\s) instead of \b to avoid false-positive matches on flags like --format. Fix path extraction regex to use submatch groups to avoid matching flags (e.g. -rf) as filesystem paths.

  • loop.go: Adjust forceCompression mid-point to avoid splitting tool-call/result message pairs. When the mid-point lands on a tool role message, advance it by one to keep the assistant→tool pair together, preventing API errors from orphaned tool results.

Test plan

  • Verified with Kimi K2 P5 model: gateway starts, heartbeat completes, Telegram messages get responses
  • go build ./pkg/... passes
  • go vet ./pkg/... passes
  • Verify --format flag in shell commands no longer triggers deny pattern
  • Verify forceCompression doesn't split tool pairs under context pressure

1. migration.go: Set ModelName to userModel when provider matches so
   GetModelConfig(userModel) can find the entry. Previously the migration
   created entries with the provider name as ModelName (e.g. "moonshot")
   but lookup used the model name (e.g. "k2p5"), causing "model not found".

2. openai_compat/provider.go: Preserve reasoning_content in conversation
   history. Thinking models (e.g. Kimi K2, DeepSeek-R1) return
   reasoning_content which must be echoed back. Without it, APIs return
   400: "thinking is enabled but reasoning_content is missing".

3. shell.go: Fix deny pattern regex for format/mkfs/diskpart to use
   (?:^|\s) instead of \b to avoid matching --format flags.
   Fix path extraction regex to use submatch to avoid matching flags
   like -rf as paths.

4. loop.go: Adjust forceCompression mid-point to avoid splitting
   tool-call/result message pairs, which causes API errors.
Copilot AI review requested due to automatic review settings March 1, 2026 01:44
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses several startup/runtime correctness issues across config migration, OpenAI-compatible provider interoperability, shell command safety guarding, and agent history compression.

Changes:

  • Fix provider→model_list migration so the user-selected model can be found via GetModelConfig().
  • Preserve reasoning_content in outgoing OpenAI-compatible conversation history for “thinking” models.
  • Refine shell safety regexes and adjust history compression midpoint to avoid orphaning tool-result messages.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
pkg/config/migration.go Updates migrated ModelName to match the user-selected model for correct lookup.
pkg/providers/openai_compat/provider.go Adds reasoning_content to the OpenAI-compatible wire message format.
pkg/tools/shell.go Adjusts deny-pattern and path extraction regex used by the exec tool safety guard.
pkg/agent/loop.go Tweaks forced compression midpoint logic to avoid splitting tool call/result pairs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Deny regex: expand left boundary to match shell separators (;, &&, ||)
  to prevent bypass via chained commands like ";format c:"
- Path regex: add "." to initial char class to catch hidden dirs (/.ssh),
  add "=" to left boundary to catch flag-attached paths (--file=/etc/passwd)
- Add test: ModelName must match user model for GetModelConfig lookup
- Add test: stripSystemParts preserves reasoning_content in wire format
- Add test: forceCompression avoids orphaning tool result messages
- Add test: deny pattern blocks disk-wiping commands with shell separators
  while allowing legitimate --format flags

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sipeed-bot sipeed-bot bot added the type: bug Something isn't working label Mar 3, 2026
Copy link

@nikolasdehor nikolasdehor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Four well-targeted fixes, each with regression tests:

  1. migration.go ModelName fix: Sets mc.ModelName = userModel so GetModelConfig(userModel) can find migrated entries. Fixes gateway startup failure when user model name differs from provider name.

  2. openai_compat reasoning_content: Adds ReasoningContent field to openaiMessage so thinking models (Kimi K2, DeepSeek-R1) get their reasoning echoed back in API calls. Without this, APIs return 400 errors.

  3. shell.go deny pattern fix: Replaces word-boundary regex with shell-separator-aware pattern to prevent --format flags from triggering the disk-wipe deny. Path extraction regex also fixed to use submatch groups.

  4. loop.go forceCompression: Advances mid-point by 1 when it lands on a tool-result message, keeping assistant(tool_call)+tool pairs together and preventing orphaned tool result API errors.

All four are independent, well-tested, and address real user-facing failures.

Resolve conflicts:
- provider.go: keep upstream's serializeMessages (supersedes stripSystemParts)
- provider_test.go: keep upstream's serializeMessages tests
- loop_test.go: add slices import needed by upstream tests
- shell.go: merge PR's --format deny fix with upstream's block device
  pattern, safePaths, and absolutePathPattern
- shell_test.go: include tests from both branches

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 3, 2026 14:55
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…test

Address Copilot review feedback:
- Use guardCommand() instead of Execute() to avoid running dangerous
  commands (format, mkfs, diskpart) on the host if regex regresses
- Assert error message contains "blocked" for blocked commands
- Replace "go fmt ./..." with "echo go fmt ./..." to avoid accidental
  file modification

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@alexhoshina alexhoshina closed this pull request by merging all changes into sipeed:main in 826f92c Mar 7, 2026
dj-oyu pushed a commit to dj-oyu/picoclaw that referenced this pull request Mar 8, 2026
- Deny regex: expand left boundary to match shell separators (;, &&, ||)
  to prevent bypass via chained commands like ";format c:"
- Path regex: add "." to initial char class to catch hidden dirs (/.ssh),
  add "=" to left boundary to catch flag-attached paths (--file=/etc/passwd)
- Add test: ModelName must match user model for GetModelConfig lookup
- Add test: stripSystemParts preserves reasoning_content in wire format
- Add test: forceCompression avoids orphaning tool result messages
- Add test: deny pattern blocks disk-wiping commands with shell separators
  while allowing legitimate --format flags

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fishtrees pushed a commit to fishtrees/picoclaw that referenced this pull request Mar 12, 2026
- Deny regex: expand left boundary to match shell separators (;, &&, ||)
  to prevent bypass via chained commands like ";format c:"
- Path regex: add "." to initial char class to catch hidden dirs (/.ssh),
  add "=" to left boundary to catch flag-attached paths (--file=/etc/passwd)
- Add test: ModelName must match user model for GetModelConfig lookup
- Add test: stripSystemParts preserves reasoning_content in wire format
- Add test: forceCompression avoids orphaning tool result messages
- Add test: deny pattern blocks disk-wiping commands with shell separators
  while allowing legitimate --format flags

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

Labels

type: bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants