@saadnvd1/agent-os 0.3.488 → 0.3.493

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.
@@ -11,12 +11,19 @@ interface Remembered {
11
11
  }
12
12
 
13
13
  export const sidebarUi = proxy<
14
- Remembered & { query: string; donePages: number; hydrated: boolean }
14
+ Remembered & {
15
+ query: string;
16
+ donePages: number;
17
+ hydrated: boolean;
18
+ // Filter to one orchestrator's work; session-only, resets on reload.
19
+ orchestratorId: string | null;
20
+ }
15
21
  >({
16
22
  workspaceId: null,
17
23
  projectId: null,
18
- doneCollapsed: false,
24
+ doneCollapsed: true,
19
25
  query: "",
26
+ orchestratorId: null,
20
27
  donePages: 0,
21
28
  hydrated: false,
22
29
  });
@@ -35,7 +42,7 @@ export const sidebarUiActions = {
35
42
  const saved = read();
36
43
  sidebarUi.workspaceId = saved.workspaceId ?? null;
37
44
  sidebarUi.projectId = saved.projectId ?? null;
38
- sidebarUi.doneCollapsed = !!saved.doneCollapsed;
45
+ sidebarUi.doneCollapsed = saved.doneCollapsed ?? true;
39
46
  sidebarUi.hydrated = true;
40
47
  subscribe(sidebarUi, () => {
41
48
  try {
@@ -52,12 +59,17 @@ export const sidebarUiActions = {
52
59
  setWorkspace: (id: string | null) => {
53
60
  sidebarUi.workspaceId = id;
54
61
  sidebarUi.projectId = null;
62
+ sidebarUi.orchestratorId = null;
55
63
  sidebarUi.donePages = 0;
56
64
  },
57
65
  setProject: (id: string | null) => {
58
66
  sidebarUi.projectId = id;
59
67
  sidebarUi.donePages = 0;
60
68
  },
69
+ setOrchestrator: (id: string | null) => {
70
+ sidebarUi.orchestratorId = id;
71
+ sidebarUi.donePages = 0;
72
+ },
61
73
  setQuery: (query: string) => {
62
74
  sidebarUi.query = query;
63
75
  },
@@ -1,201 +0,0 @@
1
- /**
2
- * The shell commands an orchestrator may run, read before the shell sees
3
- * them. Its permissions allow `aos` commands and `git`; this makes sure each
4
- * is one plain command (no `;`, `&&`, pipes, redirects, substitutions or
5
- * globs a shell would act on), and that a git command works only in one of
6
- * its own quick changes' worktrees (`-C <path>`), does nothing that runs
7
- * other code or writes outside it, and never pushes: quick_pr pushes, and
8
- * never to the default branch.
9
- */
10
-
11
- // What git may do in a quick worktree: look, stage, undo. Committing is
12
- // quick_pr's alone: it runs with hooks off, where a shell commit would run
13
- // whatever the project (or the orchestrator's own edit) put in its hooks.
14
- const GIT_ALLOWED = new Set([
15
- "status",
16
- "diff",
17
- "log",
18
- "show",
19
- "add",
20
- "rm",
21
- "restore",
22
- "reset",
23
- "rev-parse",
24
- "ls-files",
25
- ]);
26
-
27
- // Git config can run programs (core.fsmonitor, diff.external, filter and
28
- // textconv drivers), so git runs only at the worktree's own root, where it
29
- // finds the real git dir, never in a folder holding a repository the
30
- // orchestrator wrote (the write guard refuses HEAD, .git/ and .gitattributes
31
- // inside quick worktrees). A .gitattributes already committed can name a
32
- // driver, but a driver's command lives in config, which only a repository
33
- // the orchestrator can't write defines.
34
- export const GIT_LAYOUT_FILE =
35
- /(^|\/)(HEAD|\.gitattributes)$|(^|\/)\.git(\/|$)/i;
36
-
37
- // `aos` commands carry free text (notify, send): only what a shell would
38
- // chain, redirect or substitute is refused, not ? ! # * or a plain $.
39
- export function aosRefusal(command: string): string | null {
40
- const bad =
41
- "use one plain aos command: no ;, &&, pipes, redirects, backticks or $( ) outside single quotes";
42
- let q: string | null = null;
43
- for (let i = 0; i < command.length; i++) {
44
- const c = command[i];
45
- if (q === "'") {
46
- if (c === "'") q = null;
47
- continue;
48
- }
49
- if (c === "\\") {
50
- // zsh deletes a backslash-newline before it parses, joining $ to what
51
- // follows; and its ${(e)...} evaluates what an escaped \$ or \` left.
52
- const n = command[i + 1] ?? "";
53
- if (/[\n\r]/.test(n) || (q === '"' && /[$`]/.test(n)))
54
- return `Refused: ${bad}.`;
55
- i++;
56
- continue;
57
- }
58
- if (c === "`") return `Refused: ${bad}.`;
59
- // After a $ only a plain variable name (or $?, $#, $@, $*, $-, a digit)
60
- // or a literal $ may follow: ${..}, $[..], $'..' and the like evaluate
61
- // or quote differently in the shell than here.
62
- if (c === "$") {
63
- const n = command[i + 1] ?? "";
64
- if (!/^[\sA-Za-z0-9_?#@*-]?$/.test(n) && !(q === '"' && n === '"'))
65
- return `Refused: ${bad}.`;
66
- }
67
- if (q === '"') {
68
- if (c === '"') q = null;
69
- continue;
70
- }
71
- if (c === "'" || c === '"') q = c;
72
- else if (/[;&|<>()\n\r]/.test(c)) return `Refused: ${bad}.`;
73
- }
74
- return q ? "Refused: an unclosed quote." : null;
75
- }
76
-
77
- // Options that write a file somewhere else, read a command to run, or read
78
- // outside the repository. Git accepts any unique prefix of a long option
79
- // (--ext-di, --textc), so this refuses the prefixes, not the spellings.
80
- const GIT_REFUSED_ARG = /^--(ou|ex|te|no-i)/;
81
-
82
- // A shell word, with its quotes taken off, or why the command isn't one
83
- // plain command.
84
- export function shellWords(command: string): string[] | string {
85
- const words: string[] = [];
86
- let word: string | null = null;
87
- const plain =
88
- "use one plain command: no ;, &&, ||, pipes, redirects, $, backticks, globs or comments outside quotes (quote anything with those)";
89
- for (let i = 0; i < command.length; i++) {
90
- const c = command[i];
91
- if (c === " " || c === "\t") {
92
- if (word !== null) words.push(word);
93
- word = null;
94
- continue;
95
- }
96
- if (c === "'") {
97
- const end = command.indexOf("'", i + 1);
98
- if (end < 0) return "an unclosed ' quote";
99
- word = (word ?? "") + command.slice(i + 1, end);
100
- i = end;
101
- continue;
102
- }
103
- if (c === '"') {
104
- let text = "";
105
- let j = i + 1;
106
- for (; j < command.length && command[j] !== '"'; j++) {
107
- const d = command[j];
108
- if (d === "$" || d === "`") return plain;
109
- if (d === "\\" && j + 1 < command.length) {
110
- const n = command[++j];
111
- text += /["\\$`\n]/.test(n) ? n : `\\${n}`;
112
- continue;
113
- }
114
- text += d;
115
- }
116
- if (j >= command.length) return 'an unclosed " quote';
117
- word = (word ?? "") + text;
118
- i = j;
119
- continue;
120
- }
121
- if (c === "\\") {
122
- if (i + 1 >= command.length || command[i + 1] === "\n") return plain;
123
- word = (word ?? "") + command[++i];
124
- continue;
125
- }
126
- if (/[;&|<>()$`\n\r*?[\]{}!#]/.test(c)) return plain;
127
- // zsh expands a word starting with = or ~name; only ~ and ~/ are read
128
- // (as the home folder), by the caller.
129
- if (word === null && c === "=") return plain;
130
- if (word === null && c === "~" && !/^~($|[\s/])/.test(command.slice(i)))
131
- return plain;
132
- word = (word ?? "") + c;
133
- }
134
- if (word !== null) words.push(word);
135
- return words;
136
- }
137
-
138
- export type GitCommand =
139
- | { refusal: string }
140
- | { refusal: null; dir: string | null; sub: string | null };
141
-
142
- // A shell command read for the guard: why it's refused outright, or, for a
143
- // git command, the folder it runs in (from -C) to check against its quick
144
- // worktrees. `dir` and `sub` are null for a command that isn't git.
145
- export function readCommand(command: unknown): GitCommand {
146
- if (typeof command !== "string" || !command.trim())
147
- return { refusal: "No command to check." };
148
- if (/^aos(\s|$)/.test(command.trim())) {
149
- const refusal = aosRefusal(command.trim());
150
- return refusal ? { refusal } : { refusal: null, dir: null, sub: null };
151
- }
152
- const words = shellWords(command.trim());
153
- if (typeof words === "string") return { refusal: `Refused: ${words}.` };
154
- if (words[0] !== "git") {
155
- // `VAR=value git …` sets an environment variable the shell would act on
156
- // (GIT_DIR, GIT_INDEX_FILE, GIT_WORK_TREE), which can redirect git
157
- // outside its quick worktree.
158
- if (words[0].includes("="))
159
- return {
160
- refusal:
161
- "Refused: no inline VAR=value before a command; environment variables can point git outside your worktree.",
162
- };
163
- return { refusal: null, dir: null, sub: null };
164
- }
165
- const usage =
166
- "git runs only in one of your quick changes' worktrees, as `git -C <its path> <command>`";
167
- let dir: string | null = null;
168
- let i = 1;
169
- for (; i < words.length; i++) {
170
- const w = words[i];
171
- if (w === "-C") {
172
- if (dir !== null || i + 1 >= words.length)
173
- return { refusal: `Refused: ${usage}, with one -C.` };
174
- dir = words[++i];
175
- continue;
176
- }
177
- if (w === "--no-pager" || w === "--no-optional-locks") continue;
178
- if (w.startsWith("-"))
179
- return {
180
- refusal: `Refused: git's ${w} option isn't allowed here; ${usage}.`,
181
- };
182
- break;
183
- }
184
- if (dir === null) return { refusal: `Refused: ${usage}.` };
185
- const sub = words[i] ?? null;
186
- if (sub === "push")
187
- return {
188
- refusal:
189
- "Refused: quick_pr pushes a quick change's branch and opens its PR; nothing pushes from the shell, and nothing ever pushes to the default branch.",
190
- };
191
- if (!sub || !GIT_ALLOWED.has(sub))
192
- return {
193
- refusal: `Refused: git ${sub ?? ""} isn't allowed here. In a quick worktree git may only ${[...GIT_ALLOWED].join(", ")}; quick_pr commits, pushes and opens the PR.`,
194
- };
195
- const bad = words.slice(i + 1).find((w) => GIT_REFUSED_ARG.test(w));
196
- if (bad)
197
- return {
198
- refusal: `Refused: git's ${bad} option writes or runs something outside the worktree.`,
199
- };
200
- return { refusal: null, dir, sub };
201
- }