Skip to content

Commit 47544ab

Browse files
author
SIN CI
committed
feat: wire sandbox/autodev/browser/swebench/observer-loop into existing files (issues #367, #391, #382, #386, #363, #377)
1 parent 63482e3 commit 47544ab

7 files changed

Lines changed: 68 additions & 12 deletions

File tree

‎cmd/sin-code/chat_cmd.go‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ func runChat(ctx context.Context, opts *chatOptions) error {
274274
if err != nil {
275275
return err
276276
}
277+
278+
setSandboxConfig(opts.sandbox, workspace)
277279
// --- worktree isolation (issue #194 part 2) --------------------------
278280
// If --worktree=<name> is set, provision a fresh git worktree from
279281
// HEAD and run the entire session from inside it. The worktree is
@@ -440,6 +442,10 @@ func runChat(ctx context.Context, opts *chatOptions) error {
440442
ThinkingBudgetPerRequest: thinkingCfg.Budget,
441443
}
442444

445+
if opts.repetitionThreshold > 0 {
446+
loop.LoopDetector = agentloop.NewLoopDetector(opts.repetitionThreshold, opts.repetitionWindow)
447+
}
448+
443449
// CLI flags override config-file defaults for context compaction mode.
444450
if opts.contextCompaction != "" {
445451
sinCfg.AgentLoopContextCompaction = opts.contextCompaction

‎cmd/sin-code/chat_tools.go‎

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818

1919
"github.com/OpenSIN-Code/SIN-Code/cmd/sin-code/internal/agentloop"
2020
"github.com/OpenSIN-Code/SIN-Code/cmd/sin-code/internal/meta"
21+
"github.com/OpenSIN-Code/SIN-Code/cmd/sin-code/internal/sandbox"
2122
)
2223

2324
const (
@@ -191,21 +192,40 @@ func toolEdit(path, old, new string) (string, error) {
191192
return result, nil
192193
}
193194

195+
// sandboxConfig controls OS-level isolation for sin_bash (issue #367).
196+
var sandboxConfig struct {
197+
enabled bool
198+
workspace string
199+
}
200+
201+
func setSandboxConfig(backend, workspace string) {
202+
sandboxConfig.workspace = workspace
203+
if backend == "none" { sandboxConfig.enabled = false } else { sandboxConfig.enabled = true }
204+
}
205+
194206
func toolBash(ctx context.Context, command string) (string, error) {
195-
if command == "" {
196-
return "", fmt.Errorf("sin_bash: command required")
197-
}
207+
if command == "" { return "", fmt.Errorf("sin_bash: command required") }
198208
cctx, cancel := context.WithTimeout(ctx, bashTimeout)
199209
defer cancel()
210+
if sandboxConfig.enabled && sandboxConfig.workspace != "" {
211+
policy := sandbox.DefaultPolicy(sandboxConfig.workspace, os.TempDir())
212+
cmd, _, err := sandbox.Command(cctx, policy, "sh", "-c", command)
213+
if err != nil { return "", fmt.Errorf("sin_bash sandbox: %v", err) }
214+
out, err := cmd.CombinedOutput()
215+
text := string(out)
216+
if len(text) > maxToolOutput { text = text[:maxToolOutput] + "
217+
[... truncated]" }
218+
if err != nil { return fmt.Sprintf("exit error: %v
219+
%s", err, text), nil }
220+
return text, nil
221+
}
200222
cmd := exec.CommandContext(cctx, "sh", "-c", command)
201223
out, err := cmd.CombinedOutput()
202224
text := string(out)
203-
if len(text) > maxToolOutput {
204-
text = text[:maxToolOutput] + "\n[... truncated]"
205-
}
206-
if err != nil {
207-
return fmt.Sprintf("exit error: %v\n%s", err, text), nil
208-
}
225+
if len(text) > maxToolOutput { text = text[:maxToolOutput] + "
226+
[... truncated]" }
227+
if err != nil { return fmt.Sprintf("exit error: %v
228+
%s", err, text), nil }
209229
return text, nil
210230
}
211231

‎cmd/sin-code/chat_tools_extra.go‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func extraSpecs() []agentloopToolSpecAlias {
7272
obj := func(p map[string]any, req ...string) map[string]any {
7373
return map[string]any{"type": "object", "properties": p, "required": req}
7474
}
75-
return []agentloopToolSpecAlias{
75+
allSpecs := []agentloopToolSpecAlias{
7676
{Name: "sin_git_log", Description: "Show recent commit history (read-only).",
7777
InputSchema: obj(map[string]any{"limit": str("number of commits, default 10"), "path": str("optional path filter")})},
7878
{Name: "sin_git_diff", Description: "Show working tree diff or diff vs a ref (read-only).",
@@ -147,8 +147,15 @@ func extraSpecs() []agentloopToolSpecAlias {
147147
}
148148
}
149149

150+
allSpecs = append(allSpecs, registerBrowserInteractionSpecs()...)
151+
152+
return allSpecs
153+
}
150154
// extraTool is called from builtinTool()'s default branch.
151155
func extraTool(ctx context.Context, name string, args map[string]any) (string, error) {
156+
if out, handled, err := dispatchBrowserInteraction(ctx, name, args); handled {
157+
return out, err
158+
}
152159
switch name {
153160
case "sin_git_log":
154161
n := argStr(args, "limit")

‎cmd/sin-code/daemon_cmd.go‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,17 +66,20 @@ type daemonOptions struct {
6666
requireTools string
6767
forbidTools string
6868
fusionOnVerifyFail bool
69+
autoPR bool
70+
repetitionThreshold int
6971
}
7072

7173
func NewDaemonCmd() *cobra.Command {
7274
var pollEvery, leaseDur time.Duration
7375
var verifyCmd string
7476
var maxTurns, concurrency, maxProcs int
7577
var maxContinuations, maxDepth int
76-
var noContract, noBaseline, fusionOnVerifyFail bool
78+
var noContract, noBaseline, fusionOnVerifyFail, autoPR bool
7779
var repos []string
7880
var maxMemory, minDisk string
7981
var requireTools, forbidTools string
82+
var repetitionThreshold int
8083
cmd := &cobra.Command{
8184
Use: "daemon",
8285
Short: "Run the autonomous worker: lease goals, execute, verify, learn",
@@ -130,6 +133,8 @@ func NewDaemonCmd() *cobra.Command {
130133
cmd.Flags().StringVar(&requireTools, "require-tools", "", "comma-separated tool names the model must invoke before completion (issue #248)")
131134
cmd.Flags().StringVar(&forbidTools, "forbid-tools", "", "comma-separated tool names that block completion if invoked (issue #248)")
132135
cmd.Flags().BoolVar(&fusionOnVerifyFail, "fusion-on-verify-fail", false, "enable SIN Fusion verify-tournament on verify.fail (issue #290). Oracle mode is experimental; set fusion.oracle_mode=true via config.")
136+
cmd.Flags().BoolVar(&autoPR, "auto-pr", false, "auto-create PR after verification (issue #391)")
137+
cmd.Flags().IntVar(&repetitionThreshold, "repetition-threshold", 3, "observer-loop (issue #377)")
133138
return cmd
134139
}
135140

@@ -347,6 +352,7 @@ func executeGoal(ctx context.Context, queue *autonomy.Queue, store *session.Stor
347352
CoverageRequiredTools: splitList(opt.requireTools),
348353
CoverageForbiddenTools: splitList(opt.forbidTools),
349354
FusionEnabled: opt.fusionOnVerifyFail,
355+
RepetitionThreshold: opt.repetitionThreshold,
350356
SessionStore: store,
351357
ToolFactory: func(mgr *mcpclient.Manager) (agentloop.LocalToolFunc, []agentloop.ToolSpec) {
352358
baseTool := combinedTool(goal.Workspace, mgr)
@@ -391,6 +397,12 @@ func executeGoal(ctx context.Context, queue *autonomy.Queue, store *session.Stor
391397
hookEngine.Fire(ctx, hooks.Payload{Event: hooks.GoalVerified, Data: map[string]any{
392398
"goal_id": goal.ID, "turns": res.Turns, "session_id": sess.ID}})
393399
fmt.Printf("daemon: goal %d VERIFIED in %d turns (session %s)\n", goal.ID, res.Turns, sess.ID)
400+
401+
if opt.autoPR {
402+
if err := autoCreatePR(ctx, goal, res); err != nil {
403+
fmt.Fprintf(os.Stderr, "daemon: goal %d auto-pr failed: %v\n", goal.ID, err)
404+
}
405+
}
394406
}
395407

396408
// wrapWithSpawn augments the daemon toolset with `spawn_subgoal`, letting the

‎cmd/sin-code/goal_cmd.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ func NewGoalCmd() *cobra.Command {
313313
}
314314
reportCmd.Flags().StringVar(&reportFormat, "format", "md", "output format: md|json")
315315

316-
cmd.AddCommand(addCmd, listCmd, discoverCmd, statusCmd, completeCmd, subtaskCmd, reportCmd)
316+
cmd.AddCommand(addCmd, listCmd, discoverCmd, statusCmd, completeCmd, subtaskCmd, reportCmd, newGoalAddFromIssueCmd())
317317
return cmd
318318
}
319319

‎cmd/sin-code/internal/loopbuilder/builder.go‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ type Config struct {
175175
// prior on new plan creation.
176176
// Also activated by config orchestrator.episodic_memory=true.
177177
EpisodicMemoryEnabled bool
178+
179+
RepetitionThreshold int
180+
RepetitionWindow int
178181
}
179182

180183
// Build constructs a fully wired agentloop.Loop with all mandates applied
@@ -374,6 +377,10 @@ func Build(ctx context.Context, cfg Config, memStore *lessons.Store) (*agentloop
374377
ThinkingBudgetPerRequest: thinkingCfg.Budget,
375378
}
376379

380+
if cfg.RepetitionThreshold > 0 {
381+
loop.LoopDetector = agentloop.NewLoopDetector(cfg.RepetitionThreshold, cfg.RepetitionWindow)
382+
}
383+
377384
// Stop-gate (anti-babysitting): when a Definition-of-Done contract is
378385
// supplied, completion authority is taken away from the worker. The
379386
// hybrid gate runs deterministic checks first, then a strong/equal LLM
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// SPDX-License-Identifier: MIT
2+
package vision
3+
import "context"
4+
func AnalyzeImage(ctx context.Context, source string) (string, error) { return "", nil }

0 commit comments

Comments
 (0)