@saadnvd1/agent-os 0.3.752 → 0.3.756

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/README.md CHANGED
@@ -17,7 +17,7 @@ and check on all of it from your phone.
17
17
  - **Each kind of work on the right model.** Planning, review, implementation and titles each run on their own model, set in Settings → Models. [More](docs/reference/model-routing.md)
18
18
  - **Nothing starts until you send.** ⌥N opens a draft with project, machine, agent and branch as chips. [More](docs/guides/sessions.md)
19
19
  - **Other machines in the same list.** Link another machine that runs AgentOS and start or move work there. [More](docs/reference/machines.md)
20
- - **Built for your phone.** The whole app works on a phone; pair it with a QR code. [More](docs/guides/phone.md)
20
+ - **Built for your phone.** The whole app works on a phone; pair it with a QR code, or get the [iPhone app](https://apps.apple.com/us/app/agentos-coding-agents/id6820980797) from the App Store. [More](docs/guides/phone.md)
21
21
  - **Phone to orchestrator.** A paired phone can send a shake report or a shared page or file to a workspace's orchestrator (`POST /api/orchestrators/:workspaceId/intake`, saved under `intake/` beside the database, shared content marked untrusted), and fetch names to improve dictation (`GET /api/voice/vocab?workspace=`). [More](docs/guides/phone.md)
22
22
  - **An analyst beside your coding agent.** Pair a chat with a second one that reviews each turn, explains it in plain English, flags issues and drafts the next prompt, which you send, edit or dismiss. It runs on Claude unless you pick another model, local ones included. Side by side on a desktop, a switch on a phone. [More](docs/decisions/2026-10-21-analyst-sessions.md)
23
23
  - **Agents that talk to each other.** Sessions message each other with `aos`, and you see every conversation in Messages. [More](docs/guides/agent-network.md)
@@ -46,6 +46,10 @@ and check on all of it from your phone.
46
46
 
47
47
  ## Install
48
48
 
49
+ [<img src="docs/assets/app-store-badge.svg" alt="Download on the App Store" height="40">](https://apps.apple.com/us/app/agentos-coding-agents/id6820980797)
50
+
51
+ The iPhone app, [AgentOS: Coding Agents](https://apps.apple.com/us/app/agentos-coding-agents/id6820980797), is on the App Store.
52
+
49
53
  You need Node.js 22+, tmux, and at least one agent CLI (the installer offers to set up what's missing).
50
54
 
51
55
  ```bash
@@ -71,6 +75,8 @@ Connect (in development) lets you reach AgentOS from anywhere, without a tailnet
71
75
  agent-os connect # links this machine with the code from runagentos.com
72
76
  ```
73
77
 
78
+ Reach it from your phone with the [AgentOS iPhone app](https://apps.apple.com/us/app/agentos-coding-agents/id6820980797), now on the App Store.
79
+
74
80
  The machine gets its own address at `https://m-<id>.runagentos.com`, served through a Cloudflare tunnel that AgentOS starts and stops with itself. Its key is made on the machine and never leaves it, and a device still has to be paired before it gets in. Connect also hosts the read-only links made with Share. How it works, and what it does and doesn't trust, is in [docs/reference/security.md](docs/reference/security.md#connect).
75
81
 
76
82
  ## Security
@@ -61,7 +61,7 @@ export function WorkspaceSettings() {
61
61
  />
62
62
  <SettingToggle
63
63
  title="Orchestrator full access"
64
- detail="Its shell and file writes aren't checked: it can push, commit, edit any repository or file on this machine, and merge a PR directly. Off: the deny list, write limits, quick-change cap and merge-only-through-the-gates apply."
64
+ detail="Its shell and file writes aren't checked: it can push, commit, edit any repository or file on this machine, and merge a PR directly, and edit .claude files (skills, settings, hooks). Applies when its conversation next restarts. Off: the deny list, write limits, quick-change cap and merge-only-through-the-gates apply."
65
65
  checked={w.orch_full_access}
66
66
  onChange={(on) => update.mutate({ id: w.id, orchFullAccess: on })}
67
67
  />
@@ -78,9 +78,15 @@ export const ENV_CLASSES: readonly EnvClass[] = [
78
78
  id: "local-permission",
79
79
  label: "Local permission denied",
80
80
  scope: "shell",
81
- match: [/Permission denied/, /\bEACCES\b/, /Operation not permitted/],
81
+ // A quoted phrase is source code (`error: "Permission denied"`), not an error.
82
+ match: [
83
+ /(?<!["'`])Permission denied/,
84
+ /(?<!["'`])\bEACCES\b/,
85
+ /(?<!["'`])Operation not permitted/,
86
+ ],
87
+ // Another machine or a container: not this machine's permissions.
82
88
  except: {
83
- command: /\b(?:ssh|scp|sftp|rsync|kubectl|docker\s+exec)\b/,
89
+ command: /\b(?:ssh|scp|sftp|rsync|kubectl|docker|podman)\b/,
84
90
  output: /\(publickey/,
85
91
  },
86
92
  fix: null,
@@ -28,11 +28,48 @@ function commandOf(tool: string, input: unknown): string {
28
28
  return typeof file === "string" ? `${tool} ${genericPath(file)}` : tool;
29
29
  }
30
30
 
31
+ // The command's parts, split on pipes, `;`, `&&` and newlines outside quotes,
32
+ // so `grep -E "a|b"` stays one part.
33
+ function commandParts(command: string): string[] {
34
+ const parts: string[] = [];
35
+ let cur = "";
36
+ let quote = "";
37
+ for (let i = 0; i < command.length; i++) {
38
+ const ch = command[i];
39
+ if (quote) {
40
+ if (ch === "\\" && quote === '"') cur += ch + (command[++i] ?? "");
41
+ else {
42
+ if (ch === quote) quote = "";
43
+ cur += ch;
44
+ }
45
+ continue;
46
+ }
47
+ if (ch === '"' || ch === "'") quote = ch;
48
+ // A lone `&` (`2>&1`, a background job) doesn't end a part.
49
+ const split =
50
+ ch === "\n" ||
51
+ ch === ";" ||
52
+ ch === "|" ||
53
+ (ch === "&" && command[i + 1] === "&");
54
+ if (split) {
55
+ if (command[i + 1] === ch && ch !== "\n" && ch !== ";") i++;
56
+ parts.push(cur);
57
+ cur = "";
58
+ continue;
59
+ }
60
+ cur += ch;
61
+ }
62
+ parts.push(cur);
63
+ return parts;
64
+ }
65
+
66
+ // Leading `VAR=value` assignments, as in `W=/path cd $W`.
67
+ const ASSIGNMENTS = /^(?:[A-Za-z_][A-Za-z0-9_]*=(?:"[^"]*"|'[^']*'|\S*)\s*)+/;
68
+
31
69
  // True when every part of the command only reads (after `cd`).
32
70
  function onlyReads(command: string): boolean {
33
- const parts = command
34
- .split(/\n|&&|\|\||;|\|/)
35
- .map((p) => p.trim())
71
+ const parts = commandParts(command)
72
+ .map((p) => p.trim().replace(ASSIGNMENTS, ""))
36
73
  .filter((p) => p && !/^cd\s/.test(p));
37
74
  return parts.length > 0 && parts.every((p) => READERS.test(p));
38
75
  }
@@ -10,6 +10,7 @@
10
10
  */
11
11
 
12
12
  import fs from "fs";
13
+ import type { PermissionMode } from "@anthropic-ai/claude-agent-sdk";
13
14
  import os from "os";
14
15
  import path from "path";
15
16
  import { ORCHESTRATOR_PERMISSIONS } from "./tool-names";
@@ -26,12 +27,19 @@ export const toolPrefix = (server: string) =>
26
27
 
27
28
  // The permissions for an orchestrator whose workspace allows these servers:
28
29
  // a bare mcp__<server> rule allows all of that server's tools.
29
- export function orchestratorPermissions(servers: string[]) {
30
+ //
31
+ // With full access on, the mode is bypassPermissions: Claude Code always asks
32
+ // before writing under .claude/ (skills, settings, hooks), and in dontAsk
33
+ // mode that ask is a denial. Off keeps dontAsk and the write guard.
34
+ export function orchestratorPermissions(servers: string[], fullAccess = false) {
30
35
  const rules = [...new Set(servers.map((s) => s.trim()).filter(Boolean))].map(
31
36
  toolPrefix
32
37
  );
33
38
  return {
34
39
  ...ORCHESTRATOR_PERMISSIONS,
40
+ permissionMode: (fullAccess
41
+ ? "bypassPermissions"
42
+ : ORCHESTRATOR_PERMISSIONS.permissionMode) as PermissionMode,
35
43
  allowedTools: [...ORCHESTRATOR_PERMISSIONS.allowedTools, ...rules],
36
44
  };
37
45
  }
@@ -12,6 +12,7 @@ import {
12
12
  } from "./tools";
13
13
  import type { RoleExtras } from "../chat/worker/extras";
14
14
  import { refreshSettings } from "../chat/refresh";
15
+ import { orchestratorFullAccess } from "./full-access";
15
16
  import { orchestratorToolRefusal } from "./write-guard";
16
17
 
17
18
  // What an orchestrator's chat worker starts with on top of any chat: its
@@ -22,7 +23,8 @@ export async function orchestratorExtras(
22
23
  ): Promise<RoleExtras> {
23
24
  const baseUrl = agentEnv(session.id).AGENTOS_URL;
24
25
  const permissions = orchestratorPermissions(
25
- getWorkspace(session.workspace_id ?? "")?.orch_mcp_servers ?? []
26
+ getWorkspace(session.workspace_id ?? "")?.orch_mcp_servers ?? [],
27
+ orchestratorFullAccess(session)
26
28
  );
27
29
  return {
28
30
  systemAppend: await loadOrchestratorBrief(session),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saadnvd1/agent-os",
3
- "version": "0.3.752",
3
+ "version": "0.3.756",
4
4
  "description": "Self-hosted web UI for managing Claude Code sessions",
5
5
  "bin": {
6
6
  "agent-os": "scripts/agent-os"