-
Notifications
You must be signed in to change notification settings - Fork 0
recover permission defaults and solo team investigations #35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8612096
033daa4
c70c46a
06f0ef3
57d3f6e
a0a4be1
3b99880
e681b90
081109c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -42,7 +42,10 @@ type Stat = { | |||||||||||||||||
|
|
||||||||||||||||||
| type Client = { | ||||||||||||||||||
| session: { | ||||||||||||||||||
| create(input: { body: { parentID: string; title: string }; query: { directory: string } }): Promise<{ data: { id: string } }> | ||||||||||||||||||
| create(input: { | ||||||||||||||||||
| body: { parentID: string; title: string } | ||||||||||||||||||
| query: { directory: string } | ||||||||||||||||||
| }): Promise<{ data: { id: string } }> | ||||||||||||||||||
| promptAsync(input: { | ||||||||||||||||||
| path: { id: string } | ||||||||||||||||||
| query: { directory: string } | ||||||||||||||||||
|
|
@@ -158,9 +161,10 @@ function big(text: string) { | |||||||||||||||||
| const data = text.trim() | ||||||||||||||||||
| if (!data) return false | ||||||||||||||||||
| const plan = (data.match(/^\s*([-*]|\d+\.)\s+/gm) ?? []).length | ||||||||||||||||||
| const impl = /(implement|implementation|build|create|add|fix|refactor|rewrite|patch|parallel|subagent|team|background|worker|修正|実装|追加|改修|並列|サブエージェント|チーム)/i.test( | ||||||||||||||||||
| data, | ||||||||||||||||||
| ) | ||||||||||||||||||
| const impl = | ||||||||||||||||||
| /(implement|implementation|build|create|add|fix|refactor|rewrite|patch|parallel|subagent|team|background|worker|修正|実装|追加|改修|並列|サブエージェント|チーム)/i.test( | ||||||||||||||||||
| data, | ||||||||||||||||||
| ) | ||||||||||||||||||
| const wide = | ||||||||||||||||||
| data.length >= 500 || | ||||||||||||||||||
| plan >= 3 || | ||||||||||||||||||
|
|
@@ -237,19 +241,24 @@ async function save(dir: string, run: Run) { | |||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| async function load(dir: string, id: string) { | ||||||||||||||||||
| const data = await Bun.file(file(dir, id)).json().catch(() => undefined) | ||||||||||||||||||
| const data = await Bun.file(file(dir, id)) | ||||||||||||||||||
| .json() | ||||||||||||||||||
| .catch(() => undefined) | ||||||||||||||||||
| return isRun(data) ? data : undefined | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| async function scan(dir: string) { | ||||||||||||||||||
| await mkdir(root(dir), { recursive: true }) | ||||||||||||||||||
| const list = await readdir(root(dir)).catch(() => []) | ||||||||||||||||||
| return Promise.all(list.filter((item) => item.endsWith(".json")).map((item) => Bun.file(path.join(root(dir), item)).json().catch(() => undefined))).then( | ||||||||||||||||||
| (list) => | ||||||||||||||||||
| list | ||||||||||||||||||
| .filter(isRun) | ||||||||||||||||||
| .toSorted((a, b) => String(b.updated_at).localeCompare(String(a.updated_at))), | ||||||||||||||||||
| ) | ||||||||||||||||||
| return Promise.all( | ||||||||||||||||||
| list | ||||||||||||||||||
| .filter((item) => item.endsWith(".json")) | ||||||||||||||||||
| .map((item) => | ||||||||||||||||||
| Bun.file(path.join(root(dir), item)) | ||||||||||||||||||
| .json() | ||||||||||||||||||
| .catch(() => undefined), | ||||||||||||||||||
| ), | ||||||||||||||||||
| ).then((list) => list.filter(isRun).toSorted((a, b) => String(b.updated_at).localeCompare(String(a.updated_at)))) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| async function yardadd(dir: string, id: string) { | ||||||||||||||||||
|
|
@@ -365,11 +374,7 @@ async function stop(client: Client, run: Run) { | |||||||||||||||||
| ).catch(() => undefined) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| export default async function team(input: { | ||||||||||||||||||
| client: Client | ||||||||||||||||||
| worktree: string | ||||||||||||||||||
| directory: string | ||||||||||||||||||
| }) { | ||||||||||||||||||
| export default async function team(input: { client: Client; worktree: string; directory: string }) { | ||||||||||||||||||
| const job = async (ctx: Ctx, run: Run, item: Step) => { | ||||||||||||||||||
| const push = write(item.prompt, item.write) | ||||||||||||||||||
| const box = push && item.worktree ? await yardadd(ctx.worktree, `${run.id}-${item.id}`) : ctx.directory | ||||||||||||||||||
|
|
@@ -467,7 +472,10 @@ export default async function team(input: { | |||||||||||||||||
| }, | ||||||||||||||||||
| async execute(args, ctx) { | ||||||||||||||||||
| defs(args.tasks) | ||||||||||||||||||
| if (args.tasks.length < 2) throw new Error("team requires at least two tasks") | ||||||||||||||||||
| if (!args.tasks.length) throw new Error("team requires at least one task") | ||||||||||||||||||
| if (args.tasks.length < 2 && args.tasks.some((item) => write(item.prompt, item.write))) { | ||||||||||||||||||
| throw new Error("team requires at least two tasks when any task can mutate files") | ||||||||||||||||||
|
Comment on lines
+475
to
+477
|
||||||||||||||||||
| if (!args.tasks.length) throw new Error("team requires at least one task") | |
| if (args.tasks.length < 2 && args.tasks.some((item) => write(item.prompt, item.write))) { | |
| throw new Error("team requires at least two tasks when any task can mutate files") | |
| if (args.tasks.length < 2) { | |
| throw new Error("team requires at least two tasks") |
Copilot
AI
Apr 4, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This injected guidance still says to “fan out at least two worker tasks” unconditionally, but team.execute now allows a single task when all tasks are read-only. To avoid confusing users/agents (especially investigation flows that are allowed to proceed with 1 read-only task), update this message to reflect the new rule (e.g., 1 task allowed for read-only investigations; 2+ required when any task/tool call can mutate).
Copilot
AI
Apr 4, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parallel gate check for background currently calls write(prompt, out.args.write === true). This coerces write to a boolean and treats undefined as false, which disables the heuristic detection in write() and lets write-capable background runs slip through the gate whenever the caller omits write: true.
To keep enforcement consistent with the background tool itself (which infers mutability when write is omitted), pass through out.args.write only when it’s actually a boolean; otherwise let write() infer from the prompt.
| if (!write(String(out.args.prompt ?? ""), out.args.write === true)) return | |
| if ( | |
| !write( | |
| String(out.args.prompt ?? ""), | |
| typeof out.args.write === "boolean" ? out.args.write : undefined, | |
| ) | |
| ) | |
| return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
readdenies.env.*/**/.env.*, which will also deny.env.example. In other configs/defaults in this repo (e.g., Agent defaults use*.env.example: allow),.env.exampleis treated as safe to read. If the intent is to mirror that behavior, add an explicit allow for.env.example(and**/.env.exampleif you want to cover nested cases) ahead of the broader.env.*deny rules.