@gotgenes/pi-permission-system 5.1.2 → 5.2.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
CHANGED
|
@@ -5,6 +5,29 @@ 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
|
+
## [5.2.1](https://github.com/gotgenes/pi-permission-system/compare/v5.2.0...v5.2.1) (2026-05-05)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
|
|
13
|
+
* document subagent extension coexistence ([#97](https://github.com/gotgenes/pi-permission-system/issues/97)) ([9bf2972](https://github.com/gotgenes/pi-permission-system/commit/9bf29726de98d44d7cfd8963f666be37fe807c9a))
|
|
14
|
+
* plan subagent extension coexistence documentation ([#97](https://github.com/gotgenes/pi-permission-system/issues/97)) ([4cf975f](https://github.com/gotgenes/pi-permission-system/commit/4cf975f505081c5df46b20a2dd2d65e9a9a877f9))
|
|
15
|
+
* **retro:** add retro notes for issue [#96](https://github.com/gotgenes/pi-permission-system/issues/96) ([8757ffc](https://github.com/gotgenes/pi-permission-system/commit/8757ffc3f186d137c65aea7abde9a383335584f8))
|
|
16
|
+
|
|
17
|
+
## [5.2.0](https://github.com/gotgenes/pi-permission-system/compare/v5.1.2...v5.2.0) (2026-05-05)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* add SUBAGENT_PARENT_SESSION_ENV_CANDIDATES, iterate in resolver ([#96](https://github.com/gotgenes/pi-permission-system/issues/96)) ([ac6831d](https://github.com/gotgenes/pi-permission-system/commit/ac6831d3418db0aee5d5ed4757d5833730a6e130))
|
|
23
|
+
* broaden SUBAGENT_ENV_HINT_KEYS for nicobailon + HazAT extensions ([#96](https://github.com/gotgenes/pi-permission-system/issues/96)) ([8adafdb](https://github.com/gotgenes/pi-permission-system/commit/8adafdb45af66cf99b000b3ad011bee5a2c90476))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Documentation
|
|
27
|
+
|
|
28
|
+
* plan broaden subagent env hint keys ([#96](https://github.com/gotgenes/pi-permission-system/issues/96)) ([9fa97b7](https://github.com/gotgenes/pi-permission-system/commit/9fa97b7385e5b9203a35c80d15f980ac4501f788))
|
|
29
|
+
* update target-architecture subagent detection for [#96](https://github.com/gotgenes/pi-permission-system/issues/96) ([64cce35](https://github.com/gotgenes/pi-permission-system/commit/64cce3569c09cede690858af41d2f35611a8705f))
|
|
30
|
+
|
|
8
31
|
## [5.1.2](https://github.com/gotgenes/pi-permission-system/compare/v5.1.1...v5.1.2) (2026-05-05)
|
|
9
32
|
|
|
10
33
|
|
package/README.md
CHANGED
|
@@ -519,6 +519,63 @@ When a delegated or routed subagent runs without direct UI access, `ask` permiss
|
|
|
519
519
|
|
|
520
520
|
This keeps `ask` policies usable even when the original permission check happens inside a non-UI execution context.
|
|
521
521
|
|
|
522
|
+
### Coexistence with Subagent Extensions
|
|
523
|
+
|
|
524
|
+
Several pi-subagent extensions implement their own tool restriction mechanisms.
|
|
525
|
+
These compose correctly with the permission system because the two operate at different layers: **visibility** (subagent extension) and **policy** (permission system).
|
|
526
|
+
|
|
527
|
+
#### The two-layer model
|
|
528
|
+
|
|
529
|
+
```text
|
|
530
|
+
┌─────────────────────────────────────────────────────┐
|
|
531
|
+
│ Layer 1 – Visibility (subagent extension) │
|
|
532
|
+
│ Controls which tools are registered / active │
|
|
533
|
+
│ before the agent session starts. │
|
|
534
|
+
├─────────────────────────────────────────────────────┤
|
|
535
|
+
│ Layer 2 – Policy (pi-permission-system) │
|
|
536
|
+
│ Controls allow / ask / deny decisions on every │
|
|
537
|
+
│ tool call, bash command, MCP operation, etc. │
|
|
538
|
+
└─────────────────────────────────────────────────────┘
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
#### Known subagent extensions and their mechanisms
|
|
542
|
+
|
|
543
|
+
|Extension|Mechanism|Frontmatter key|
|
|
544
|
+
|----|----------|----------|
|
|
545
|
+
|[nicobailon/pi-subagents](https://github.com/nicobailon/pi-subagents)|`--tools` CLI allowlist passed to subprocess|`tools:` (CSV allowlist)|
|
|
546
|
+
|[tintinweb/pi-subagents](https://github.com/tintinweb/pi-subagents)|`session.setActiveToolsByName()` in-process filter|`disallowed_tools:` (CSV denylist)|
|
|
547
|
+
|[HazAT/pi-interactive-subagents](https://github.com/HazAT/pi-interactive-subagents)|`PI_DENY_TOOLS` env var + `--tools` CLI allowlist|`deny-tools:` (CSV denylist), `spawning:` (bool)|
|
|
548
|
+
|
|
549
|
+
#### Interaction rules
|
|
550
|
+
|
|
551
|
+
1. **Hidden tool → permission system never sees it.**
|
|
552
|
+
If a subagent extension removes a tool from the active set, the permission system receives no registration or call event for that tool.
|
|
553
|
+
The permission policy for that tool is irrelevant — it is already gone.
|
|
554
|
+
2. **Denied tool → hidden regardless of the subagent extension's allowlist.**
|
|
555
|
+
If the permission system denies a tool (via `deny` policy or tool filtering), it is removed from the active set before the agent starts.
|
|
556
|
+
A `tools:` allowlist in a subagent extension cannot restore a tool that the permission system has already hidden.
|
|
557
|
+
3. **The two denylist mechanisms are additive, not conflicting.**
|
|
558
|
+
A tool blocked by either layer stays blocked.
|
|
559
|
+
Neither layer can silently re-enable what the other has blocked.
|
|
560
|
+
|
|
561
|
+
#### `permission:` frontmatter is exclusive to this extension
|
|
562
|
+
|
|
563
|
+
The `permission:` key in an agent's YAML frontmatter is read exclusively by `pi-permission-system`.
|
|
564
|
+
It has no interaction with the `tools:`, `disallowed_tools:`, or `deny-tools:` keys consumed by subagent extensions.
|
|
565
|
+
You can freely use both in the same agent file:
|
|
566
|
+
|
|
567
|
+
```yaml
|
|
568
|
+
---
|
|
569
|
+
# Subagent extension: allow only bash and read_file in the subprocess
|
|
570
|
+
tools: bash,read_file
|
|
571
|
+
# pi-permission-system: still enforce ask on bash within those allowed tools
|
|
572
|
+
permission:
|
|
573
|
+
bash: ask
|
|
574
|
+
---
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
In this example the subagent extension restricts visibility to `bash` and `read_file`, and the permission system then gates every `bash` call with an `ask` prompt — both rules apply independently.
|
|
578
|
+
|
|
522
579
|
### Logging
|
|
523
580
|
|
|
524
581
|
When the extension prompts, denies, or forwards permission requests, it can append structured JSONL entries under:
|
package/package.json
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
PERMISSION_FORWARDING_POLL_INTERVAL_MS,
|
|
19
19
|
PERMISSION_FORWARDING_TIMEOUT_MS,
|
|
20
20
|
resolvePermissionForwardingTargetSessionId,
|
|
21
|
+
SUBAGENT_PARENT_SESSION_ENV_CANDIDATES,
|
|
21
22
|
} from "../permission-forwarding";
|
|
22
23
|
import { isSubagentExecutionContext } from "../subagent-context";
|
|
23
24
|
|
|
@@ -110,7 +111,10 @@ export async function waitForForwardedPermissionApproval(
|
|
|
110
111
|
if (!targetSessionId) {
|
|
111
112
|
logPermissionForwardingError(
|
|
112
113
|
deps.logger,
|
|
113
|
-
|
|
114
|
+
`Permission forwarding target session could not be resolved. ` +
|
|
115
|
+
`Checked env vars: ${SUBAGENT_PARENT_SESSION_ENV_CANDIDATES.join(", ")}. ` +
|
|
116
|
+
`If you are using nicobailon/pi-subagents or HazAT/pi-interactive-subagents, ` +
|
|
117
|
+
`parent-session forwarding is not yet supported for those extensions (see issue #98).`,
|
|
114
118
|
);
|
|
115
119
|
return { approved: false, state: "denied" };
|
|
116
120
|
}
|
|
@@ -5,12 +5,30 @@ import type { PermissionDecisionState } from "./permission-dialog";
|
|
|
5
5
|
export const PERMISSION_FORWARDING_POLL_INTERVAL_MS = 250;
|
|
6
6
|
export const PERMISSION_FORWARDING_TIMEOUT_MS = 10 * 60 * 1000;
|
|
7
7
|
export const SUBAGENT_ENV_HINT_KEYS = [
|
|
8
|
+
// pi-agent-router (original)
|
|
8
9
|
"PI_IS_SUBAGENT",
|
|
9
10
|
"PI_SUBAGENT_SESSION_ID",
|
|
10
11
|
"PI_AGENT_ROUTER_SUBAGENT",
|
|
12
|
+
// nicobailon/pi-subagents
|
|
13
|
+
"PI_SUBAGENT_CHILD",
|
|
14
|
+
"PI_SUBAGENT_RUN_ID",
|
|
15
|
+
"PI_SUBAGENT_CHILD_AGENT",
|
|
16
|
+
"PI_SUBAGENT_DEPTH",
|
|
17
|
+
// HazAT/pi-interactive-subagents
|
|
18
|
+
"PI_SUBAGENT_NAME",
|
|
19
|
+
"PI_SUBAGENT_ID",
|
|
20
|
+
"PI_SUBAGENT_SESSION",
|
|
21
|
+
"PI_SUBAGENT_ACTIVITY_FILE",
|
|
11
22
|
] as const;
|
|
23
|
+
/** Ordered list of env var names to check for the parent session ID. First match wins. */
|
|
24
|
+
export const SUBAGENT_PARENT_SESSION_ENV_CANDIDATES: readonly string[] = [
|
|
25
|
+
// pi-agent-router (original)
|
|
26
|
+
"PI_AGENT_ROUTER_PARENT_SESSION_ID",
|
|
27
|
+
] as const;
|
|
28
|
+
|
|
29
|
+
/** @deprecated Use SUBAGENT_PARENT_SESSION_ENV_CANDIDATES */
|
|
12
30
|
export const SUBAGENT_PARENT_SESSION_ENV_KEY =
|
|
13
|
-
|
|
31
|
+
SUBAGENT_PARENT_SESSION_ENV_CANDIDATES[0];
|
|
14
32
|
|
|
15
33
|
const SESSION_FORWARDING_ROOT_DIRECTORY_NAME = "sessions";
|
|
16
34
|
const SESSION_FORWARDING_REQUESTS_DIRECTORY_NAME = "requests";
|
|
@@ -106,9 +124,12 @@ export function resolvePermissionForwardingTargetSessionId(options: {
|
|
|
106
124
|
return null;
|
|
107
125
|
}
|
|
108
126
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
127
|
+
const env = options.env ?? process.env;
|
|
128
|
+
for (const key of SUBAGENT_PARENT_SESSION_ENV_CANDIDATES) {
|
|
129
|
+
const resolved = normalizePermissionForwardingSessionId(env[key]);
|
|
130
|
+
if (resolved) return resolved;
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
112
133
|
}
|
|
113
134
|
|
|
114
135
|
export function isForwardedPermissionRequestForSession(
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
resolvePermissionForwardingTargetSessionId,
|
|
4
|
+
SUBAGENT_PARENT_SESSION_ENV_CANDIDATES,
|
|
5
|
+
SUBAGENT_PARENT_SESSION_ENV_KEY,
|
|
6
|
+
} from "../src/permission-forwarding";
|
|
7
|
+
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
vi.unstubAllEnvs();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe("SUBAGENT_PARENT_SESSION_ENV_CANDIDATES", () => {
|
|
13
|
+
test("is an array containing PI_AGENT_ROUTER_PARENT_SESSION_ID", () => {
|
|
14
|
+
expect(Array.isArray(SUBAGENT_PARENT_SESSION_ENV_CANDIDATES)).toBe(true);
|
|
15
|
+
expect(SUBAGENT_PARENT_SESSION_ENV_CANDIDATES).toContain(
|
|
16
|
+
"PI_AGENT_ROUTER_PARENT_SESSION_ID",
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("deprecated SUBAGENT_PARENT_SESSION_ENV_KEY equals the first candidate", () => {
|
|
21
|
+
expect(SUBAGENT_PARENT_SESSION_ENV_KEY).toBe(
|
|
22
|
+
SUBAGENT_PARENT_SESSION_ENV_CANDIDATES[0],
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe("resolvePermissionForwardingTargetSessionId", () => {
|
|
28
|
+
test("hasUI=true returns the current session ID (UI host owns forwarding)", () => {
|
|
29
|
+
expect(
|
|
30
|
+
resolvePermissionForwardingTargetSessionId({
|
|
31
|
+
hasUI: true,
|
|
32
|
+
isSubagent: false,
|
|
33
|
+
currentSessionId: "parent-session-abc",
|
|
34
|
+
env: {},
|
|
35
|
+
}),
|
|
36
|
+
).toBe("parent-session-abc");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("hasUI=true with isSubagent=true still returns current session ID", () => {
|
|
40
|
+
expect(
|
|
41
|
+
resolvePermissionForwardingTargetSessionId({
|
|
42
|
+
hasUI: true,
|
|
43
|
+
isSubagent: true,
|
|
44
|
+
currentSessionId: "session-xyz",
|
|
45
|
+
env: { PI_AGENT_ROUTER_PARENT_SESSION_ID: "other" },
|
|
46
|
+
}),
|
|
47
|
+
).toBe("session-xyz");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("hasUI=false, isSubagent=false returns null", () => {
|
|
51
|
+
expect(
|
|
52
|
+
resolvePermissionForwardingTargetSessionId({
|
|
53
|
+
hasUI: false,
|
|
54
|
+
isSubagent: false,
|
|
55
|
+
currentSessionId: "session-xyz",
|
|
56
|
+
env: { PI_AGENT_ROUTER_PARENT_SESSION_ID: "parent-session-abc" },
|
|
57
|
+
}),
|
|
58
|
+
).toBeNull();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("isSubagent=true, no candidates set returns null", () => {
|
|
62
|
+
expect(
|
|
63
|
+
resolvePermissionForwardingTargetSessionId({
|
|
64
|
+
hasUI: false,
|
|
65
|
+
isSubagent: true,
|
|
66
|
+
currentSessionId: "session-xyz",
|
|
67
|
+
env: {},
|
|
68
|
+
}),
|
|
69
|
+
).toBeNull();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("isSubagent=true, PI_AGENT_ROUTER_PARENT_SESSION_ID set returns its value", () => {
|
|
73
|
+
expect(
|
|
74
|
+
resolvePermissionForwardingTargetSessionId({
|
|
75
|
+
hasUI: false,
|
|
76
|
+
isSubagent: true,
|
|
77
|
+
currentSessionId: "session-xyz",
|
|
78
|
+
env: { PI_AGENT_ROUTER_PARENT_SESSION_ID: "parent-session-abc" },
|
|
79
|
+
}),
|
|
80
|
+
).toBe("parent-session-abc");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("isSubagent=true, first candidate absent but second set returns second value", () => {
|
|
84
|
+
// Inject a second candidate at test-time to validate the iteration logic
|
|
85
|
+
// without waiting for a real extension to adopt the convention.
|
|
86
|
+
const originalCandidates = [...SUBAGENT_PARENT_SESSION_ENV_CANDIDATES];
|
|
87
|
+
// Mutate the array via index-assignment through a cast so we can test
|
|
88
|
+
// multi-candidate iteration without changing the exported constant type.
|
|
89
|
+
// This is test-only; production code never mutates the array.
|
|
90
|
+
(SUBAGENT_PARENT_SESSION_ENV_CANDIDATES as unknown as string[]).push(
|
|
91
|
+
"PI_SUBAGENT_PARENT_SESSION_ID_TEST_ONLY",
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
expect(
|
|
96
|
+
resolvePermissionForwardingTargetSessionId({
|
|
97
|
+
hasUI: false,
|
|
98
|
+
isSubagent: true,
|
|
99
|
+
currentSessionId: "session-xyz",
|
|
100
|
+
env: {
|
|
101
|
+
PI_SUBAGENT_PARENT_SESSION_ID_TEST_ONLY: "parent-from-second",
|
|
102
|
+
},
|
|
103
|
+
}),
|
|
104
|
+
).toBe("parent-from-second");
|
|
105
|
+
} finally {
|
|
106
|
+
// Restore original array contents.
|
|
107
|
+
(SUBAGENT_PARENT_SESSION_ENV_CANDIDATES as unknown as string[]).length =
|
|
108
|
+
originalCandidates.length;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("isSubagent=true, candidate value is empty string returns null", () => {
|
|
113
|
+
expect(
|
|
114
|
+
resolvePermissionForwardingTargetSessionId({
|
|
115
|
+
hasUI: false,
|
|
116
|
+
isSubagent: true,
|
|
117
|
+
currentSessionId: "session-xyz",
|
|
118
|
+
env: { PI_AGENT_ROUTER_PARENT_SESSION_ID: "" },
|
|
119
|
+
}),
|
|
120
|
+
).toBeNull();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("isSubagent=true, candidate value is 'unknown' returns null", () => {
|
|
124
|
+
expect(
|
|
125
|
+
resolvePermissionForwardingTargetSessionId({
|
|
126
|
+
hasUI: false,
|
|
127
|
+
isSubagent: true,
|
|
128
|
+
currentSessionId: "session-xyz",
|
|
129
|
+
env: { PI_AGENT_ROUTER_PARENT_SESSION_ID: "unknown" },
|
|
130
|
+
}),
|
|
131
|
+
).toBeNull();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("env defaults to process.env when omitted", () => {
|
|
135
|
+
vi.stubEnv("PI_AGENT_ROUTER_PARENT_SESSION_ID", "env-session-abc");
|
|
136
|
+
expect(
|
|
137
|
+
resolvePermissionForwardingTargetSessionId({
|
|
138
|
+
hasUI: false,
|
|
139
|
+
isSubagent: true,
|
|
140
|
+
}),
|
|
141
|
+
).toBe("env-session-abc");
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -61,11 +61,86 @@ describe("isSubagentExecutionContext — env hint detection", () => {
|
|
|
61
61
|
).toBe(true);
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
// nicobailon/pi-subagents keys
|
|
65
|
+
test("returns true when PI_SUBAGENT_CHILD is set", () => {
|
|
66
|
+
vi.stubEnv("PI_SUBAGENT_CHILD", "1");
|
|
67
|
+
expect(
|
|
68
|
+
isSubagentExecutionContext(makeCtx(null), "/sessions/subagents"),
|
|
69
|
+
).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("returns true when PI_SUBAGENT_RUN_ID is set", () => {
|
|
73
|
+
vi.stubEnv("PI_SUBAGENT_RUN_ID", "run-abc");
|
|
74
|
+
expect(
|
|
75
|
+
isSubagentExecutionContext(makeCtx(null), "/sessions/subagents"),
|
|
76
|
+
).toBe(true);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("returns true when PI_SUBAGENT_CHILD_AGENT is set", () => {
|
|
80
|
+
vi.stubEnv("PI_SUBAGENT_CHILD_AGENT", "worker");
|
|
81
|
+
expect(
|
|
82
|
+
isSubagentExecutionContext(makeCtx(null), "/sessions/subagents"),
|
|
83
|
+
).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("returns true when PI_SUBAGENT_DEPTH is set", () => {
|
|
87
|
+
vi.stubEnv("PI_SUBAGENT_DEPTH", "1");
|
|
88
|
+
expect(
|
|
89
|
+
isSubagentExecutionContext(makeCtx(null), "/sessions/subagents"),
|
|
90
|
+
).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("returns true when PI_SUBAGENT_DEPTH is zero (depth-0 is still a subagent context)", () => {
|
|
94
|
+
vi.stubEnv("PI_SUBAGENT_DEPTH", "0");
|
|
95
|
+
expect(
|
|
96
|
+
isSubagentExecutionContext(makeCtx(null), "/sessions/subagents"),
|
|
97
|
+
).toBe(true);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// HazAT/pi-interactive-subagents keys
|
|
101
|
+
test("returns true when PI_SUBAGENT_NAME is set", () => {
|
|
102
|
+
vi.stubEnv("PI_SUBAGENT_NAME", "my-agent");
|
|
103
|
+
expect(
|
|
104
|
+
isSubagentExecutionContext(makeCtx(null), "/sessions/subagents"),
|
|
105
|
+
).toBe(true);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("returns true when PI_SUBAGENT_ID is set", () => {
|
|
109
|
+
vi.stubEnv("PI_SUBAGENT_ID", "id-xyz");
|
|
110
|
+
expect(
|
|
111
|
+
isSubagentExecutionContext(makeCtx(null), "/sessions/subagents"),
|
|
112
|
+
).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("returns true when PI_SUBAGENT_SESSION is set", () => {
|
|
116
|
+
vi.stubEnv("PI_SUBAGENT_SESSION", "session-xyz");
|
|
117
|
+
expect(
|
|
118
|
+
isSubagentExecutionContext(makeCtx(null), "/sessions/subagents"),
|
|
119
|
+
).toBe(true);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("returns true when PI_SUBAGENT_ACTIVITY_FILE is set", () => {
|
|
123
|
+
vi.stubEnv("PI_SUBAGENT_ACTIVITY_FILE", "/tmp/activity.json");
|
|
124
|
+
expect(
|
|
125
|
+
isSubagentExecutionContext(makeCtx(null), "/sessions/subagents"),
|
|
126
|
+
).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("covers all declared SUBAGENT_ENV_HINT_KEYS", () => {
|
|
65
130
|
// Verify the keys we test match what the module declares.
|
|
66
131
|
expect(SUBAGENT_ENV_HINT_KEYS).toContain("PI_IS_SUBAGENT");
|
|
67
132
|
expect(SUBAGENT_ENV_HINT_KEYS).toContain("PI_SUBAGENT_SESSION_ID");
|
|
68
133
|
expect(SUBAGENT_ENV_HINT_KEYS).toContain("PI_AGENT_ROUTER_SUBAGENT");
|
|
134
|
+
// nicobailon/pi-subagents
|
|
135
|
+
expect(SUBAGENT_ENV_HINT_KEYS).toContain("PI_SUBAGENT_CHILD");
|
|
136
|
+
expect(SUBAGENT_ENV_HINT_KEYS).toContain("PI_SUBAGENT_RUN_ID");
|
|
137
|
+
expect(SUBAGENT_ENV_HINT_KEYS).toContain("PI_SUBAGENT_CHILD_AGENT");
|
|
138
|
+
expect(SUBAGENT_ENV_HINT_KEYS).toContain("PI_SUBAGENT_DEPTH");
|
|
139
|
+
// HazAT/pi-interactive-subagents
|
|
140
|
+
expect(SUBAGENT_ENV_HINT_KEYS).toContain("PI_SUBAGENT_NAME");
|
|
141
|
+
expect(SUBAGENT_ENV_HINT_KEYS).toContain("PI_SUBAGENT_ID");
|
|
142
|
+
expect(SUBAGENT_ENV_HINT_KEYS).toContain("PI_SUBAGENT_SESSION");
|
|
143
|
+
expect(SUBAGENT_ENV_HINT_KEYS).toContain("PI_SUBAGENT_ACTIVITY_FILE");
|
|
69
144
|
});
|
|
70
145
|
|
|
71
146
|
test("returns false when env hint value is empty string", () => {
|