@geravant/sinain 1.23.4 → 1.23.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geravant/sinain",
3
- "version": "1.23.4",
3
+ "version": "1.23.6",
4
4
  "description": "Context OS — captures what you see and hear, distills it into a private knowledge graph for AI-powered work",
5
5
  "type": "module",
6
6
  "bin": {
@@ -56,7 +56,7 @@
56
56
  "allowedTools": "mcp__sinain ToolSearch",
57
57
  "escAllowedTools": "${allowedTools} Bash(git:*) Edit Write Read Glob Grep LS",
58
58
  "spawnAllowedTools": "${allowedTools} Bash(git:*) Edit Write Read Glob Grep LS",
59
- "autoApproveTools": "Read Glob Grep Ls Cat mcp__sinain* ToolSearch",
59
+ "autoApproveTools": "Read Glob Grep Ls Cat Edit Write MultiEdit NotebookEdit WebFetch WebSearch TaskCreate TaskUpdate TaskGet TaskList TaskOutput TaskStop LSP Skill Monitor BashOutput KillBash mcp__sinain* ToolSearch",
60
60
 
61
61
  "analyzer": {
62
62
  "debounceMs": 6000,
@@ -291,7 +291,15 @@ export function loadConfig(): CoreConfig {
291
291
  const autoApproveRaw = fromCfgStr(
292
292
  agentsCfg?.autoApproveTools,
293
293
  "SINAIN_AUTO_APPROVE_TOOLS",
294
- "Read Glob Grep Ls Cat mcp__sinain* ToolSearch",
294
+ // Default auto-approve covers the Claude Code core tools the escalation
295
+ // flow realistically uses without prompting. Bash is intentionally NOT
296
+ // included — users wanting scoped shell commands without prompts should
297
+ // add patterns like "Bash(git:*) Bash(npm:*)" to agents.json.
298
+ "Read Glob Grep Ls Cat Edit Write MultiEdit NotebookEdit " +
299
+ "WebFetch WebSearch " +
300
+ "TaskCreate TaskUpdate TaskGet TaskList TaskOutput TaskStop " +
301
+ "LSP Skill Monitor BashOutput KillBash " +
302
+ "mcp__sinain* ToolSearch",
295
303
  );
296
304
  const permissionsConfig = {
297
305
  autoApproveTools: autoApproveRaw.split(/\s+/).filter((t) => t.length > 0),
@@ -1616,15 +1616,16 @@ function readBody(req: IncomingMessage, maxBytes: number): Promise<string> {
1616
1616
  /** Pending spawn questions/permissions — resolve callbacks keyed by "ask:{taskId}" or "perm:{taskId}" */
1617
1617
  const pendingSpawnQuestions = new Map<string, (answer: string) => void>();
1618
1618
 
1619
- // YOLO mode: "allow all" for the current agent session. Keyed on the openclaude
1620
- // session_id from the PreToolUse hook input (stable across tool calls within
1621
- // one invoke_agent run, discarded when that run ends). User enters YOLO by
1622
- // clicking the YOLO button on any permission prompt. Session id is cleared
1623
- // implicitly when the bare agent restarts (new session_id on next run).
1624
- const yoloSessions = new Set<string>();
1625
- // Map permission-request id (perm-<ts>) -> session id it came from. Used so
1626
- // that /spawn/permission-reply can flag the right session as YOLO when the
1627
- // user picks the YOLO button. Cleaned on resolve/timeout.
1619
+ // YOLO mode: "allow all permissions" until sinain-core restarts. Previously
1620
+ // keyed on the openclaude session_id, but `claude -p ...` generates a fresh
1621
+ // session_id every invocation, so the per-session YOLO never persisted across
1622
+ // escalation calls and the user got prompted again on the very next tool use.
1623
+ // A single process-global flag matches what users actually want from a YOLO
1624
+ // button ("stop asking until I restart"), and is reset on every sinain-core
1625
+ // start so it can't outlive a session unintentionally.
1626
+ let yoloActive = false;
1627
+ // Map permission-request id (perm-<ts>) -> session id it came from. Kept for
1628
+ // debug/logging visibility into which agent invocation triggered YOLO.
1628
1629
  const permissionToSession = new Map<string, string>();
1629
1630
 
1630
1631
  export function createAppServer(deps: ServerDeps) {
@@ -2630,14 +2631,16 @@ export function createAppServer(deps: ServerDeps) {
2630
2631
  return;
2631
2632
  }
2632
2633
 
2633
- // YOLO short-circuit: if this session previously clicked YOLO, auto-allow
2634
- // without routing to overlay. No user interaction needed.
2635
- if (sessionId && yoloSessions.has(sessionId)) {
2634
+ // YOLO short-circuit: if the user previously clicked YOLO on any
2635
+ // permission prompt, auto-allow everything until core restarts. The
2636
+ // flag is process-global (not per-session) because claude -p creates
2637
+ // a fresh session_id per invocation and per-session YOLO never stuck.
2638
+ if (yoloActive) {
2636
2639
  res.end(JSON.stringify({
2637
2640
  hookSpecificOutput: {
2638
2641
  hookEventName: "PreToolUse",
2639
2642
  permissionDecision: "allow",
2640
- permissionDecisionReason: "YOLO mode active for this session",
2643
+ permissionDecisionReason: "YOLO mode active (process-global)",
2641
2644
  },
2642
2645
  }));
2643
2646
  return;
@@ -2715,13 +2718,13 @@ export function createAppServer(deps: ServerDeps) {
2715
2718
  const resolve = pendingSpawnQuestions.get(key);
2716
2719
  if (resolve) {
2717
2720
  pendingSpawnQuestions.delete(key);
2718
- // YOLO: flag the session so subsequent permission requests for the
2719
- // same openclaude invocation auto-allow without routing to overlay.
2720
- // Session id was captured in permissionToSession when we broadcast
2721
- // the request; it's cleared by the /spawn/approve handler after resolve.
2721
+ // YOLO: flip the process-global flag so every subsequent permission
2722
+ // request auto-allows until sinain-core restarts. Logs the triggering
2723
+ // session id (if known) for debug visibility.
2722
2724
  if (decision === "yolo") {
2725
+ yoloActive = true;
2723
2726
  const sid = permissionToSession.get(taskId);
2724
- if (sid) yoloSessions.add(sid);
2727
+ log(TAG, `YOLO mode activated (triggered by sessionId=${sid || "<none>"})`);
2725
2728
  }
2726
2729
  resolve(decision || "deny");
2727
2730
  res.end(JSON.stringify({ ok: true }));