@gotgenes/pi-subagents 7.4.0 → 7.5.0
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/CHANGELOG.md +19 -0
- package/README.md +50 -17
- package/docs/architecture/architecture.md +3 -1
- package/package.json +1 -1
- package/src/lifecycle/agent-runner.ts +11 -0
- package/src/lifecycle/permission-bridge.ts +63 -0
- package/src/ui/agent-menu.ts +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [7.5.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v7.4.0...pi-subagents-v7.5.0) (2026-05-26)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add permission bridge for cross-extension registration ([#101](https://github.com/gotgenes/pi-packages/issues/101)) ([1827720](https://github.com/gotgenes/pi-packages/commit/18277203f7ee10e56f90a0d4db587a4aa95376ab))
|
|
14
|
+
* register child sessions with permission system ([#101](https://github.com/gotgenes/pi-packages/issues/101)) ([0487828](https://github.com/gotgenes/pi-packages/commit/04878286d7da6660362360482fb916b1b3743ce3))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* resolve pre-existing lint errors in pi-autoformat and pi-permission-system ([68fd516](https://github.com/gotgenes/pi-packages/commit/68fd516e33ddbb9a5e37ef19e949ee9ecdc37252))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Documentation
|
|
23
|
+
|
|
24
|
+
* document permission-bridge in architecture ([#101](https://github.com/gotgenes/pi-packages/issues/101)) ([d0120ab](https://github.com/gotgenes/pi-packages/commit/d0120abdf049e2aeba14ba75071ed55b24e23dbe))
|
|
25
|
+
* update subagent integration docs for native permission bridge ([#101](https://github.com/gotgenes/pi-packages/issues/101)) ([0bd456b](https://github.com/gotgenes/pi-packages/commit/0bd456befa8ea6918e74f4393d844868795edc77))
|
|
26
|
+
|
|
8
27
|
## [7.4.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v7.3.2...pi-subagents-v7.4.0) (2026-05-25)
|
|
9
28
|
|
|
10
29
|
|
package/README.md
CHANGED
|
@@ -421,27 +421,57 @@ disallowed_tools: write, edit
|
|
|
421
421
|
|
|
422
422
|
This is useful for creating agents that inherit extension tools but should not have write access.
|
|
423
423
|
|
|
424
|
+
## Permission System Integration
|
|
425
|
+
|
|
426
|
+
When [`@gotgenes/pi-permission-system`](https://github.com/gotgenes/pi-permission-system) is installed, this extension integrates automatically:
|
|
427
|
+
|
|
428
|
+
- **Per-agent permission policies** — define `permission:` in agent YAML frontmatter to set allow/ask/deny rules per agent type.
|
|
429
|
+
The permission system resolves the agent name from the `<active_agent>` tag in the child system prompt.
|
|
430
|
+
- **Tool filtering** — the permission system's `before_agent_start` handler removes denied tools from the child session before the agent starts.
|
|
431
|
+
- **`ask`-state forwarding** — when a child session triggers an `ask` permission, the prompt forwards to the parent session's UI.
|
|
432
|
+
The parent approves or denies, and the child resumes.
|
|
433
|
+
- **Deterministic child detection** — every child session registers with the permission system's `SubagentSessionRegistry` before `bindExtensions()` fires, so detection does not rely on env vars or filesystem heuristics.
|
|
434
|
+
|
|
435
|
+
No configuration is required.
|
|
436
|
+
When `@gotgenes/pi-permission-system` is not installed, the registration calls are silent no-ops.
|
|
437
|
+
|
|
424
438
|
## Architecture
|
|
425
439
|
|
|
440
|
+
See `docs/architecture/architecture.md` for the full architecture document with domain decomposition, Mermaid diagrams, and improvement roadmap.
|
|
441
|
+
|
|
426
442
|
```text
|
|
427
443
|
src/
|
|
428
|
-
index.ts
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
444
|
+
index.ts # Extension entry: tool/command registration, rendering
|
|
445
|
+
runtime.ts # Session-scoped state bag with methods
|
|
446
|
+
types.ts # Shared type definitions
|
|
447
|
+
settings.ts # Persistent settings (concurrency, turn limits)
|
|
448
|
+
config/ # Agent type registry and configuration
|
|
449
|
+
agent-types.ts # Unified agent registry (defaults + custom)
|
|
450
|
+
default-agents.ts # Embedded default agent configs
|
|
451
|
+
custom-agents.ts # Load user-defined agents from .pi/agents/*.md
|
|
452
|
+
invocation-config.ts # Per-call merge of tool params + agent config
|
|
453
|
+
session/ # Pure session assembly
|
|
454
|
+
session-config.ts # Session configuration assembler
|
|
455
|
+
prompts.ts # Config-driven system prompt builder
|
|
456
|
+
context.ts # Parent conversation context for inherit_context
|
|
457
|
+
skill-loader.ts # Preload skills from Pi-standard + Agent Skills spec
|
|
458
|
+
env.ts # Environment detection (git, platform)
|
|
459
|
+
model-resolver.ts # Fuzzy model matching
|
|
460
|
+
lifecycle/ # Agent execution and state tracking
|
|
461
|
+
agent-manager.ts # Spawn, queue, abort, resume, concurrency
|
|
462
|
+
agent-runner.ts # Session creation, turn loop, tool filtering
|
|
463
|
+
agent-record.ts # Status state machine
|
|
464
|
+
parent-snapshot.ts # Immutable spawn-time parent state
|
|
465
|
+
permission-bridge.ts # Optional bridge to pi-permission-system registry
|
|
466
|
+
worktree.ts # Git worktree isolation
|
|
467
|
+
observation/ # Progress tracking and notification
|
|
468
|
+
record-observer.ts # Session-event stats observer
|
|
469
|
+
notification.ts # Completion nudges
|
|
470
|
+
service/ # Cross-extension API boundary
|
|
471
|
+
service.ts # SubagentsService interface + Symbol.for() accessors
|
|
472
|
+
service-adapter.ts # SubagentsService wrapper around AgentManager
|
|
473
|
+
tools/ # LLM-facing tools
|
|
474
|
+
ui/ # Widget, conversation viewer, /agents menu
|
|
445
475
|
```
|
|
446
476
|
|
|
447
477
|
## Deviations from upstream
|
|
@@ -458,6 +488,9 @@ Each has a corresponding upstream PR:
|
|
|
458
488
|
3. **`<active_agent>` system-prompt tag** (`src/prompts.ts`) — `buildAgentPrompt` prepends `<active_agent name="${config.name}"/>` to every assembled child system prompt (both `replace` and `append` modes).
|
|
459
489
|
Downstream extensions like [`@gotgenes/pi-permission-system`](https://github.com/gotgenes/pi-permission-system) parse this tag to resolve per-agent `permission:` frontmatter inside the child session.
|
|
460
490
|
Upstream PR: [tintinweb/pi-subagents#73](https://github.com/tintinweb/pi-subagents/pull/73).
|
|
491
|
+
4. **Permission-system registration** (`src/lifecycle/permission-bridge.ts`) — `runAgent` registers every child session with `@gotgenes/pi-permission-system`'s `SubagentSessionRegistry` before `bindExtensions()` and unregisters in the `finally` block.
|
|
492
|
+
This enables deterministic child detection and `ask`-state forwarding to the parent UI.
|
|
493
|
+
No upstream equivalent — this feature is specific to the `@gotgenes` fork.
|
|
461
494
|
|
|
462
495
|
The upstream `vitest` suite plus tests added for each patch all pass on every commit.
|
|
463
496
|
|
|
@@ -259,6 +259,7 @@ src/
|
|
|
259
259
|
│ ├── agent-record.ts status state machine
|
|
260
260
|
│ ├── parent-snapshot.ts immutable spawn-time parent state
|
|
261
261
|
│ ├── execution-state.ts session/output phase state
|
|
262
|
+
│ ├── permission-bridge.ts optional bridge to pi-permission-system registry
|
|
262
263
|
│ ├── worktree.ts git worktree isolation
|
|
263
264
|
│ ├── worktree-state.ts worktree phase state
|
|
264
265
|
│ └── usage.ts token usage tracking
|
|
@@ -333,7 +334,8 @@ They declare this package as an optional peer dependency and use dynamic import
|
|
|
333
334
|
|
|
334
335
|
- The three tools: `Agent`, `get_subagent_result`, `steer_subagent`.
|
|
335
336
|
- `AgentManager` — spawn, queue, abort, resume, concurrency control.
|
|
336
|
-
- `agent-runner` — session creation, turn loop, tool filtering, extension binding (Patches 2 and 3).
|
|
337
|
+
- `agent-runner` — session creation, turn loop, tool filtering, extension binding (Patches 2 and 3), permission-system registration.
|
|
338
|
+
- `permission-bridge` — optional cross-extension bridge to `@gotgenes/pi-permission-system`; registers each child session with `SubagentSessionRegistry` before `bindExtensions()` so the permission system detects in-process children deterministically.
|
|
337
339
|
- `session-config` — pure configuration assembler (extracted from `agent-runner`).
|
|
338
340
|
- `SubagentRuntime` — session-scoped state bag with methods.
|
|
339
341
|
- `ParentSnapshot` — immutable snapshot of parent session state, captured once at spawn time.
|
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import type { AgentConfigLookup } from "#src/config/agent-types";
|
|
12
12
|
import type { ParentSessionInfo } from "#src/lifecycle/agent-manager";
|
|
13
13
|
import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
|
|
14
|
+
import { registerChildSession, unregisterChildSession } from "#src/lifecycle/permission-bridge";
|
|
14
15
|
import { extractAssistantContent } from "#src/session/content-items";
|
|
15
16
|
import { extractText } from "#src/session/context";
|
|
16
17
|
import type { EnvInfo } from "#src/session/env";
|
|
@@ -347,6 +348,15 @@ export async function runAgent(
|
|
|
347
348
|
session.setActiveToolsByName(filtered);
|
|
348
349
|
}
|
|
349
350
|
|
|
351
|
+
// Register with pi-permission-system's SubagentSessionRegistry before
|
|
352
|
+
// bindExtensions() so isSubagentExecutionContext() hits the registry on the
|
|
353
|
+
// first check during child extension initialization. Unregistered in the
|
|
354
|
+
// finally block below to guarantee cleanup on both success and error paths.
|
|
355
|
+
registerChildSession(sessionDir, {
|
|
356
|
+
agentName: type,
|
|
357
|
+
parentSessionId: options.context.parentSession?.parentSessionId,
|
|
358
|
+
});
|
|
359
|
+
|
|
350
360
|
// Bind extensions so that session_start fires and extensions can initialize
|
|
351
361
|
// (e.g. loading credentials, setting up state). Placed after tool filtering
|
|
352
362
|
// so extension-provided skills/prompts from extendResourcesFromExtensions()
|
|
@@ -406,6 +416,7 @@ export async function runAgent(
|
|
|
406
416
|
unsubTurns();
|
|
407
417
|
collector.unsubscribe();
|
|
408
418
|
cleanupAbort();
|
|
419
|
+
unregisterChildSession(sessionDir);
|
|
409
420
|
}
|
|
410
421
|
|
|
411
422
|
const responseText =
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* permission-bridge.ts — Cross-extension bridge to @gotgenes/pi-permission-system.
|
|
3
|
+
*
|
|
4
|
+
* pi-subagents does not import pi-permission-system directly. Instead it
|
|
5
|
+
* accesses the published PermissionsService via a process-global Symbol.for()
|
|
6
|
+
* key, the same mechanism pi-permission-system uses to publish itself.
|
|
7
|
+
*
|
|
8
|
+
* When pi-permission-system is not installed, getPermissionsService() returns
|
|
9
|
+
* undefined and all registration calls are silent no-ops.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The two PermissionsService methods pi-subagents needs.
|
|
14
|
+
*
|
|
15
|
+
* Follows ISP — does not expose the full PermissionsService surface
|
|
16
|
+
* (checkPermission, getToolPermission, etc.) to avoid coupling.
|
|
17
|
+
*/
|
|
18
|
+
interface PermissionsServiceConsumer {
|
|
19
|
+
registerSubagentSession(
|
|
20
|
+
sessionKey: string,
|
|
21
|
+
info: { parentSessionId?: string; agentName: string },
|
|
22
|
+
): void;
|
|
23
|
+
unregisterSubagentSession(sessionKey: string): void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const PERMISSION_SERVICE_KEY = Symbol.for(
|
|
27
|
+
"@gotgenes/pi-permission-system:service",
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
function getPermissionsService(): PermissionsServiceConsumer | undefined {
|
|
31
|
+
return (globalThis as Record<symbol, unknown>)[
|
|
32
|
+
PERMISSION_SERVICE_KEY
|
|
33
|
+
] as PermissionsServiceConsumer | undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Register a child session with pi-permission-system's SubagentSessionRegistry.
|
|
38
|
+
*
|
|
39
|
+
* Must be called after deriving sessionDir but before session.bindExtensions()
|
|
40
|
+
* so isSubagentExecutionContext() hits the registry on the first check during
|
|
41
|
+
* child extension initialization.
|
|
42
|
+
*
|
|
43
|
+
* @param sessionKey - The session directory path (unique per session).
|
|
44
|
+
* @param info - Agent name and optional parent session ID for forwarding.
|
|
45
|
+
*/
|
|
46
|
+
export function registerChildSession(
|
|
47
|
+
sessionKey: string,
|
|
48
|
+
info: { parentSessionId?: string; agentName: string },
|
|
49
|
+
): void {
|
|
50
|
+
getPermissionsService()?.registerSubagentSession(sessionKey, info);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Unregister a child session from pi-permission-system's SubagentSessionRegistry.
|
|
55
|
+
*
|
|
56
|
+
* Must be called in a finally block so cleanup happens on both success and
|
|
57
|
+
* error paths.
|
|
58
|
+
*
|
|
59
|
+
* @param sessionKey - The session directory path used during registration.
|
|
60
|
+
*/
|
|
61
|
+
export function unregisterChildSession(sessionKey: string): void {
|
|
62
|
+
getPermissionsService()?.unregisterSubagentSession(sessionKey);
|
|
63
|
+
}
|
package/src/ui/agent-menu.ts
CHANGED
|
@@ -72,9 +72,9 @@ export class AgentsMenuHandler {
|
|
|
72
72
|
private readonly registry: AgentTypeRegistry,
|
|
73
73
|
private readonly agentActivity: AgentActivityReader,
|
|
74
74
|
private readonly settings: AgentMenuSettings,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
fileOps: AgentFileOps,
|
|
76
|
+
personalAgentsDir: string,
|
|
77
|
+
projectAgentsDir: string,
|
|
78
78
|
) {
|
|
79
79
|
this.editor = new AgentConfigEditor(
|
|
80
80
|
fileOps,
|