mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
feat: enable permission-aware subagents
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
---
|
||||
description: "Loads alpha+beta, selects only alpha via ext:. Flip mutes beta."
|
||||
extensions: "./ext-alpha.mjs, ./ext-beta.mjs"
|
||||
tools: "*, ext:ext-alpha.mjs"
|
||||
expect_tools_present: "read, bash, alpha_read, alpha_write"
|
||||
expect_tools_absent: "beta_tool"
|
||||
---
|
||||
e2e template: a single ext: selector flips extension tools to an allowlist;
|
||||
alpha is selected (all its tools surface), beta is loaded but muted.
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
description: "Selects beta via ext:; the loaded alpha extension is muted."
|
||||
extensions: "./ext-alpha.mjs, ./ext-beta.mjs"
|
||||
tools: "*, ext:ext-beta.mjs"
|
||||
expect_tools_present: "read, beta_tool"
|
||||
expect_tools_absent: "alpha_read, alpha_write"
|
||||
---
|
||||
e2e template: mirror of all-and-alpha-selected — selecting beta proves the flip
|
||||
mutes the *other* loaded extension (alpha) regardless of which one is named.
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
description: "Selects alpha but denylists alpha_write via disallowed_tools."
|
||||
extensions: "./ext-alpha.mjs, ./ext-beta.mjs"
|
||||
tools: "*, ext:ext-alpha.mjs"
|
||||
disallowed_tools: "alpha_write"
|
||||
expect_tools_present: "read, alpha_read"
|
||||
expect_tools_absent: "alpha_write, beta_tool"
|
||||
---
|
||||
e2e template: disallowed_tools removes an extension tool even when the ext:
|
||||
selector would otherwise surface it.
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
description: "An ext: selector cannot resurrect an excluded extension."
|
||||
extensions: "./ext-alpha.mjs, ./ext-beta.mjs"
|
||||
exclude_extensions: ext-beta.mjs
|
||||
tools: "*, ext:ext-beta.mjs"
|
||||
expect_tools_present: "read"
|
||||
expect_tools_absent: "beta_tool, alpha_read, alpha_write"
|
||||
---
|
||||
e2e template: exclude_extensions beats a tools: ext: selector — beta never
|
||||
loads, so ext:ext-beta.mjs is an orphan (warns, does not pull beta back in).
|
||||
Alpha tools are also absent because any ext: entry flips extension tools to an
|
||||
explicit allowlist and alpha is not selected.
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
description: "Loads alpha+beta, excludes beta via exclude_extensions."
|
||||
extensions: "./ext-alpha.mjs, ./ext-beta.mjs"
|
||||
exclude_extensions: ext-beta.mjs
|
||||
tools: "*"
|
||||
expect_tools_present: "read, alpha_read, alpha_write"
|
||||
expect_tools_absent: "beta_tool"
|
||||
---
|
||||
e2e template: exclude_extensions removes an extension after the include set is
|
||||
computed — alpha surfaces normally, beta's tools never register. (Excluding a
|
||||
name that the extensions: list also loads warns "in both — exclude wins"; the
|
||||
exclusion still applies.)
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
description: "extensions:false — no extension tools at all."
|
||||
extensions: false
|
||||
tools: "*"
|
||||
expect_tools_present: "read, bash, edit, write, grep, find, ls"
|
||||
expect_tools_absent: "alpha_read, alpha_write, beta_tool"
|
||||
---
|
||||
e2e template: extensions are disabled, so only the built-in tools are active.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
description: "tools value format: YAML flow array."
|
||||
tools: [read, grep, find]
|
||||
expect_tools_present: "read, grep, find"
|
||||
expect_tools_absent: "bash, edit, write, ls"
|
||||
---
|
||||
e2e template (format check 3/3): YAML array. Per the README, `[a, b]` == `"a, b"`,
|
||||
so this must yield the same active tool set as the CSV forms.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
description: "tools value format: quoted CSV string."
|
||||
tools: "read, grep, find"
|
||||
expect_tools_present: "read, grep, find"
|
||||
expect_tools_absent: "bash, edit, write, ls"
|
||||
---
|
||||
e2e template (format check 2/3): quoted CSV string. Same result as the unquoted
|
||||
and array forms.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
description: "tools value format: unquoted CSV."
|
||||
tools: read, grep, find
|
||||
expect_tools_present: "read, grep, find"
|
||||
expect_tools_absent: "bash, edit, write, ls"
|
||||
---
|
||||
e2e template (format check 1/3): unquoted CSV. Must be equivalent to the quoted
|
||||
and array forms — see fmt-quoted-csv.md and fmt-array.md.
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
description: "isolated:true forces built-ins only, overriding extensions and ext:."
|
||||
isolated: true
|
||||
extensions: "./ext-alpha.mjs, ./ext-beta.mjs"
|
||||
tools: "*, ext:ext-alpha.mjs"
|
||||
expect_tools_present: "read, bash, edit, write, grep, find, ls"
|
||||
expect_tools_absent: "alpha_read, alpha_write, beta_tool"
|
||||
---
|
||||
e2e template: per the README, isolated:true is hermetic — it forces
|
||||
extensions:false + skills:false and drops ext: selectors, leaving only built-ins,
|
||||
even though this template also sets extensions and an ext: selector.
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
description: "ext: selecting the lazy extension surfaces its session_start tool."
|
||||
extensions: "./ext-lazy.mjs, ./ext-alpha.mjs"
|
||||
tools: "*, ext:ext-lazy.mjs"
|
||||
expect_tools_present: "read, bash, lazy_tool"
|
||||
expect_tools_absent: "alpha_read, alpha_write"
|
||||
---
|
||||
e2e template: the case a static allowlist can never express. `lazy_tool` does not
|
||||
exist when the session is constructed, so it cannot be listed up front — scope
|
||||
has to be re-derived once the extension registers it.
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
description: "A lazy extension left out of the ext: flip stays muted."
|
||||
extensions: "./ext-lazy.mjs, ./ext-alpha.mjs"
|
||||
tools: "*, ext:ext-alpha.mjs"
|
||||
expect_tools_present: "read, bash, alpha_read, alpha_write"
|
||||
expect_tools_absent: "lazy_tool"
|
||||
---
|
||||
e2e template: the mirror of lazy-ext-selected — admitting late tools must not
|
||||
mean admitting ALL late tools. ext-lazy loads and its session_start handler runs,
|
||||
but the `ext:` flip did not select it, so `lazy_tool` never becomes active.
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
description: "A tool registered at session_start reaches the subagent (#125)."
|
||||
extensions: "./ext-lazy.mjs"
|
||||
tools: "*"
|
||||
expect_tools_present: "read, bash, lazy_tool"
|
||||
---
|
||||
e2e template: ext-lazy registers `lazy_tool` from `session_start`, i.e. AFTER
|
||||
loader.reload() has already run. Any scoping that snapshots the tool set at
|
||||
construction drops it permanently — this asserts it survives.
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
description: "memory + disallowed_tools — a denied write tool must not count as write capability."
|
||||
memory: project
|
||||
tools: read, write
|
||||
disallowed_tools: write
|
||||
expect_tools_present: "read"
|
||||
expect_tools_absent: "write, edit, bash, grep"
|
||||
expect_prompt_contains: "Agent Memory (read-only), Memory scope: project"
|
||||
expect_prompt_absent: "persistent memory directory"
|
||||
---
|
||||
README: "The `disallowed_tools` field is respected when determining write
|
||||
capability — an agent with `tools: write` + `disallowed_tools: write` correctly
|
||||
gets read-only memory."
|
||||
|
||||
This is the combination neither `memory-readonly.md` (no write tool at all) nor
|
||||
`memory-readwrite.md` (write tool, nothing denied) exercises: the write tool IS
|
||||
in the `tools:` set, so a naive capability check sees it and hands the agent the
|
||||
read-write memory prompt — which also appends `write`/`edit` to the tool names,
|
||||
widening the set the agent asked for. The denylist still filters the registry, so
|
||||
the visible symptom is an agent instructed to write memory files with no tool to
|
||||
do it; the invisible one is the tool-name widening.
|
||||
|
||||
Scope `project` is safe here precisely because the read-only branch creates no
|
||||
memory directory — if this fixture ever flips to the read-write branch, it would
|
||||
also start writing into the repo, which is the loudest possible signal.
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
description: "memory read-only — an agent without write tools gets a read-only memory block."
|
||||
memory: project
|
||||
tools: read, grep
|
||||
expect_tools_present: "read, grep"
|
||||
expect_tools_absent: "write, edit"
|
||||
expect_prompt_contains: "Agent Memory (read-only), Memory scope: project"
|
||||
expect_prompt_absent: "persistent memory directory"
|
||||
---
|
||||
A read-only memory agent. Per the README, agents without write/edit tools
|
||||
auto-get a read-only memory fallback: existing memory is injected, no write
|
||||
access is granted, and no memory directory is created (so `project` scope is
|
||||
safe here — nothing is written into the repo).
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
description: "memory read-write — an agent with write tools gets a writable memory block."
|
||||
memory: user
|
||||
tools: read, write
|
||||
expect_tools_present: "read, write, edit"
|
||||
expect_prompt_contains: "persistent memory directory, Memory scope: user"
|
||||
expect_prompt_absent: "(read-only)"
|
||||
---
|
||||
A write-capable memory agent. Per the README, agents with write/edit tools get
|
||||
full read-write memory; the memory tool set is completed (edit is auto-added) and
|
||||
a writable memory block is injected into the system prompt. Scope `user` so the
|
||||
memory dir is created under the hermetic HOME, never in the repo.
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
description: "Minimal agent — only description and body; every other field omitted."
|
||||
expect_tools_present: "read, bash, edit, write, grep, find, ls"
|
||||
expect_tools_absent: "alpha_read, alpha_write, beta_tool"
|
||||
---
|
||||
A minimal agent. Per the README defaults: omitted `tools` => all 7 built-ins;
|
||||
omitted `extensions` => true (all *discovered* extensions load — none exist in
|
||||
this hermetic fixture, so no extension tools surface). expect_* are test-harness
|
||||
annotations and are ignored by the agent loader.
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
description: "Narrows alpha to a single tool via ext:ext-alpha.mjs/alpha_read."
|
||||
extensions: "./ext-alpha.mjs, ./ext-beta.mjs"
|
||||
tools: "*, ext:ext-alpha.mjs/alpha_read"
|
||||
expect_tools_present: "read, alpha_read"
|
||||
expect_tools_absent: "alpha_write, beta_tool"
|
||||
---
|
||||
e2e template: ext:<ext>/<tool> narrows alpha to just alpha_read; alpha_write
|
||||
and the unselected beta extension are both muted.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
description: "No ext: selector, so all loaded extensions' tools surface."
|
||||
extensions: "./ext-alpha.mjs, ./ext-beta.mjs"
|
||||
tools: "*"
|
||||
expect_tools_present: "read, alpha_read, alpha_write, beta_tool"
|
||||
---
|
||||
e2e template: with no ext: entry there is no flip, so every loaded extension's
|
||||
tools surface alongside the built-ins.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
description: "prompt_mode append — body appended to the parent's prompt."
|
||||
prompt_mode: append
|
||||
expect_tools_present: "read"
|
||||
expect_prompt_contains: "PARENT_PROMPT_MARKER, APPEND_BODY_MARKER"
|
||||
---
|
||||
APPEND_BODY_MARKER — in append mode the parent prompt flows in verbatim, so the
|
||||
real session's system prompt contains BOTH the parent marker and this body.
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
description: "prompt_mode replace (default) — body is the full system prompt."
|
||||
prompt_mode: replace
|
||||
expect_tools_present: "read"
|
||||
expect_prompt_contains: "REPLACE_BODY_MARKER"
|
||||
expect_prompt_absent: "PARENT_PROMPT_MARKER"
|
||||
---
|
||||
REPLACE_BODY_MARKER — in replace mode the parent prompt is NOT inherited, so the
|
||||
real session's system prompt contains this body but not the parent's marker.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
description: "skills: preloads a named skill into the system prompt."
|
||||
skills: probe-skill
|
||||
expect_tools_present: "read"
|
||||
expect_prompt_contains: "Preloaded Skill: probe-skill, SKILL_BODY_MARKER"
|
||||
---
|
||||
A skill-preloading agent. The `skills: probe-skill` entry must inject
|
||||
test/fixtures/.pi/skills/probe-skill.md into the real session's system prompt.
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
description: "tools narrowed to two built-ins; extensions omitted (=> true)."
|
||||
tools: read, grep
|
||||
expect_tools_present: "read, grep"
|
||||
expect_tools_absent: "bash, edit, write, find, ls, alpha_read, alpha_write, beta_tool"
|
||||
---
|
||||
e2e template: a plain built-in allowlist narrows to exactly the listed tools.
|
||||
extensions is omitted (defaults to true); none are discovered here, so no
|
||||
extension tools surface.
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
description: "tools:none => zero built-ins; loaded extension tools still surface."
|
||||
extensions: "./ext-alpha.mjs, ./ext-beta.mjs"
|
||||
tools: none
|
||||
expect_tools_present: "alpha_read, alpha_write, beta_tool"
|
||||
expect_tools_absent: "read, bash, edit, write, grep, find, ls"
|
||||
---
|
||||
e2e template: `tools: none` yields zero built-ins. With extensions loaded and no
|
||||
ext: selector, all extension tools still surface — only the built-ins are dropped.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
description: "tools omitted (=> all built-ins) with extensions explicitly loaded."
|
||||
extensions: "./ext-alpha.mjs, ./ext-beta.mjs"
|
||||
expect_tools_present: "read, bash, edit, write, grep, find, ls, alpha_read, alpha_write, beta_tool"
|
||||
---
|
||||
e2e template: omitting `tools` yields all 7 built-ins; with extensions loaded and
|
||||
no ext: selector there is no flip, so every loaded extension tool also surfaces.
|
||||
@@ -0,0 +1,5 @@
|
||||
# Probe Skill
|
||||
|
||||
SKILL_BODY_MARKER — distinctive content used by the template-driven e2e to
|
||||
prove that a `skills:` frontmatter entry is preloaded into the real subagent
|
||||
system prompt.
|
||||
Reference in New Issue
Block a user