@gotgenes/pi-permission-system 8.3.0 → 8.3.1
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 +14 -0
- package/package.json +1 -1
- package/src/index.ts +2 -2
- package/src/subagent-registry.ts +37 -0
- package/test/subagent-registry.test.ts +45 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ 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.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [8.3.1](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v8.3.0...pi-permission-system-v8.3.1) (2026-06-01)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* add process-global SubagentSessionRegistry accessor ([#296](https://github.com/gotgenes/pi-packages/issues/296)) ([d3fd3b0](https://github.com/gotgenes/pi-packages/commit/d3fd3b04223b2d276873094ad8c14f239654b8c8))
|
|
14
|
+
* share SubagentSessionRegistry across parent and child sessions ([#296](https://github.com/gotgenes/pi-packages/issues/296)) ([fed676a](https://github.com/gotgenes/pi-packages/commit/fed676aaa485abe8db158e522ba898705f3dff94))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Documentation
|
|
18
|
+
|
|
19
|
+
* explain process-global subagent registry across session buses ([#296](https://github.com/gotgenes/pi-packages/issues/296)) ([1804dbb](https://github.com/gotgenes/pi-packages/commit/1804dbbb766d7b7fbc0e49da877f3238f5c3e8dc))
|
|
20
|
+
* use ADR-NNNN with links docs-wide ([c6b6431](https://github.com/gotgenes/pi-packages/commit/c6b6431c004f324931f23be46cf2e47e8fdac919))
|
|
21
|
+
|
|
8
22
|
## [8.3.0](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v8.2.1...pi-permission-system-v8.3.0) (2026-06-01)
|
|
9
23
|
|
|
10
24
|
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
import { createSessionLogger } from "./session-logger";
|
|
30
30
|
import { isSubagentExecutionContext } from "./subagent-context";
|
|
31
31
|
import { subscribeSubagentLifecycle } from "./subagent-lifecycle-events";
|
|
32
|
-
import {
|
|
32
|
+
import { getSubagentSessionRegistry } from "./subagent-registry";
|
|
33
33
|
import { ToolInputFormatterRegistry } from "./tool-input-formatter-registry";
|
|
34
34
|
import {
|
|
35
35
|
canResolveAskPermissionRequest,
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
|
|
39
39
|
export default function piPermissionSystemExtension(pi: ExtensionAPI): void {
|
|
40
40
|
const runtime = createExtensionRuntime();
|
|
41
|
-
const subagentRegistry =
|
|
41
|
+
const subagentRegistry = getSubagentSessionRegistry();
|
|
42
42
|
const formatterRegistry = new ToolInputFormatterRegistry();
|
|
43
43
|
registerBuiltinToolInputFormatters(formatterRegistry);
|
|
44
44
|
|
package/src/subagent-registry.ts
CHANGED
|
@@ -10,8 +10,45 @@
|
|
|
10
10
|
* The registry is keyed by session directory path, which is unique per
|
|
11
11
|
* session and available to both producer and consumer via
|
|
12
12
|
* `ctx.sessionManager.getSessionDir()`.
|
|
13
|
+
*
|
|
14
|
+
* The single registry instance is stored on `globalThis` (via `Symbol.for()`)
|
|
15
|
+
* so that the parent's permission-system instance (which registers children
|
|
16
|
+
* on the parent's event bus) and each child's separate jiti instance (which
|
|
17
|
+
* reads the registry to detect itself and resolve its forwarding target) share
|
|
18
|
+
* one store across per-session event buses. See `getSubagentSessionRegistry()`.
|
|
13
19
|
*/
|
|
14
20
|
|
|
21
|
+
/** Process-global key for the shared registry slot. */
|
|
22
|
+
const SUBAGENT_SESSION_REGISTRY_KEY = Symbol.for(
|
|
23
|
+
"@gotgenes/pi-permission-system:subagent-registry",
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Return the process-global SubagentSessionRegistry, creating it on first call.
|
|
28
|
+
*
|
|
29
|
+
* Backed by `globalThis` + `Symbol.for()` so the parent's permission-system
|
|
30
|
+
* instance (which registers children on the parent event bus) and each child's
|
|
31
|
+
* separate jiti instance (which reads the registry to detect itself and resolve
|
|
32
|
+
* its forwarding target) share one store across per-session event buses.
|
|
33
|
+
*
|
|
34
|
+
* Intentionally has no shutdown/unpublish hook — a child's `session_shutdown`
|
|
35
|
+
* must not be able to wipe the parent's registrations. Entries are added and
|
|
36
|
+
* removed exclusively by the parent's `subagents:child:session-created` /
|
|
37
|
+
* `subagents:child:disposed` subscription.
|
|
38
|
+
*/
|
|
39
|
+
export function getSubagentSessionRegistry(): SubagentSessionRegistry {
|
|
40
|
+
const store = globalThis as Record<symbol, unknown>;
|
|
41
|
+
const existing = store[SUBAGENT_SESSION_REGISTRY_KEY] as
|
|
42
|
+
| SubagentSessionRegistry
|
|
43
|
+
| undefined;
|
|
44
|
+
if (existing) {
|
|
45
|
+
return existing;
|
|
46
|
+
}
|
|
47
|
+
const registry = new SubagentSessionRegistry();
|
|
48
|
+
store[SUBAGENT_SESSION_REGISTRY_KEY] = registry;
|
|
49
|
+
return registry;
|
|
50
|
+
}
|
|
51
|
+
|
|
15
52
|
/** Signal stored per registered in-process subagent session. */
|
|
16
53
|
export interface SubagentSessionInfo {
|
|
17
54
|
/** Parent session ID for permission forwarding. Omit when unknown. */
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import { describe, expect, test } from "vitest";
|
|
1
|
+
import { afterEach, describe, expect, test } from "vitest";
|
|
2
2
|
import {
|
|
3
|
+
getSubagentSessionRegistry,
|
|
3
4
|
type SubagentSessionInfo,
|
|
4
5
|
SubagentSessionRegistry,
|
|
5
6
|
} from "#src/subagent-registry";
|
|
6
7
|
|
|
8
|
+
const REGISTRY_KEY = Symbol.for(
|
|
9
|
+
"@gotgenes/pi-permission-system:subagent-registry",
|
|
10
|
+
);
|
|
11
|
+
|
|
7
12
|
function makeInfo(
|
|
8
13
|
overrides: Partial<SubagentSessionInfo> = {},
|
|
9
14
|
): SubagentSessionInfo {
|
|
@@ -92,3 +97,42 @@ describe("SubagentSessionRegistry", () => {
|
|
|
92
97
|
expect(registry.has("/sessions/task-2")).toBe(true);
|
|
93
98
|
});
|
|
94
99
|
});
|
|
100
|
+
|
|
101
|
+
// ── process-global accessor ────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
describe("getSubagentSessionRegistry (process-global accessor)", () => {
|
|
104
|
+
afterEach(() => {
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- Symbol-keyed global property; Map.delete() is not applicable
|
|
106
|
+
delete (globalThis as Record<symbol, unknown>)[REGISTRY_KEY];
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("returns a SubagentSessionRegistry instance", () => {
|
|
110
|
+
const registry = getSubagentSessionRegistry();
|
|
111
|
+
expect(registry).toBeInstanceOf(SubagentSessionRegistry);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("returns the same instance on repeated calls", () => {
|
|
115
|
+
const first = getSubagentSessionRegistry();
|
|
116
|
+
const second = getSubagentSessionRegistry();
|
|
117
|
+
expect(first).toBe(second);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("state registered through one call is visible through another call", () => {
|
|
121
|
+
const writer = getSubagentSessionRegistry();
|
|
122
|
+
writer.register("/sessions/child-tasks", {
|
|
123
|
+
agentName: "Explore",
|
|
124
|
+
parentSessionId: "parent-abc",
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const reader = getSubagentSessionRegistry();
|
|
128
|
+
expect(reader.has("/sessions/child-tasks")).toBe(true);
|
|
129
|
+
expect(reader.get("/sessions/child-tasks")?.parentSessionId).toBe(
|
|
130
|
+
"parent-abc",
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("starts empty on first call", () => {
|
|
135
|
+
const registry = getSubagentSessionRegistry();
|
|
136
|
+
expect(registry.has("/sessions/any-key")).toBe(false);
|
|
137
|
+
});
|
|
138
|
+
});
|