pi-sage 0.2.3 → 0.2.4
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/.pi/extensions/sage/index.ts +2 -1
- package/.pi/extensions/sage/policy.ts +8 -2
- package/AGENTS.md +1 -1
- package/docs/SAGE_SPEC.md +14 -11
- package/package.json +1 -1
|
@@ -335,6 +335,7 @@ function buildCallerContext(lastInputSource: InputSource | undefined, hasUI: boo
|
|
|
335
335
|
const isRpcSource = lastInputSource === "rpc";
|
|
336
336
|
const isSubagent = process.env.PI_SAGE_SUBAGENT === "1";
|
|
337
337
|
const interactive = hasUI && lastInputSource === "interactive";
|
|
338
|
+
const isInteractiveSupervisor = interactive && roleHint === "supervisor";
|
|
338
339
|
|
|
339
340
|
return {
|
|
340
341
|
session: {
|
|
@@ -343,7 +344,7 @@ function buildCallerContext(lastInputSource: InputSource | undefined, hasUI: boo
|
|
|
343
344
|
agent: {
|
|
344
345
|
role: roleHint ?? "primary",
|
|
345
346
|
isSubagent,
|
|
346
|
-
isRpcOrchestrated: isRpcSource || Boolean(roleHint && roleHint !== "primary")
|
|
347
|
+
isRpcOrchestrated: isRpcSource || Boolean(roleHint && roleHint !== "primary" && !isInteractiveSupervisor)
|
|
347
348
|
},
|
|
348
349
|
runtime: {
|
|
349
350
|
mode: process.env.CI ? "ci" : hasUI ? (isRpcSource ? "rpc" : "interactive") : "non-interactive"
|
|
@@ -22,11 +22,17 @@ export function isEligibleCaller(ctx: CallerContext | null | undefined): CallerD
|
|
|
22
22
|
return { ok: false, blockCode: "subagent", reason: "Sage disabled for subagents" };
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
const interactiveSupervisor =
|
|
26
|
+
ctx.agent.role === "supervisor" && ctx.session.interactive === true && ctx.runtime.mode === "interactive";
|
|
27
|
+
|
|
28
|
+
if (ctx.agent.role !== "primary" && !interactiveSupervisor) {
|
|
26
29
|
return { ok: false, blockCode: "ineligible-caller", reason: "Only primary agent may invoke Sage" };
|
|
27
30
|
}
|
|
28
31
|
|
|
29
|
-
return {
|
|
32
|
+
return {
|
|
33
|
+
ok: true,
|
|
34
|
+
reason: interactiveSupervisor ? "eligible (interactive supervisor)" : "eligible"
|
|
35
|
+
};
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
export function isHardCostCapExceeded(settings: SageSettings, budgetState: SageBudgetState): boolean {
|
package/AGENTS.md
CHANGED
|
@@ -11,7 +11,7 @@ Sage should improve decision quality for complex tasks while preserving strict s
|
|
|
11
11
|
## 2) Product Invariants (Do Not Violate)
|
|
12
12
|
|
|
13
13
|
1. Sage invocation is restricted to **interactive top-level primary** sessions.
|
|
14
|
-
2. RPC-orchestrated roles
|
|
14
|
+
2. RPC-orchestrated roles cannot invoke Sage. Exception: an interactive top-level `supervisor` context may invoke Sage when treated as the user-facing primary session.
|
|
15
15
|
3. Sage is **single-shot** per call.
|
|
16
16
|
4. Sage cannot recursively invoke Sage.
|
|
17
17
|
5. Sage is **advisory-only** (analysis/recommendations, not implementation execution).
|
package/docs/SAGE_SPEC.md
CHANGED
|
@@ -182,8 +182,8 @@ Primary agent guidance to append:
|
|
|
182
182
|
- `autonomousEnabled: boolean` (applies only in eligible interactive primary sessions; default: true there)
|
|
183
183
|
- `explicitRequestAlwaysAllowed: boolean` (default: true)
|
|
184
184
|
- `invocationScope: "interactive-primary-only"` (default and recommended)
|
|
185
|
-
- `sage_consult` is callable only from
|
|
186
|
-
- `sage_consult` is blocked for non-interactive/CI contexts and RPC-orchestrated
|
|
185
|
+
- `sage_consult` is callable only from top-level interactive user-facing sessions.
|
|
186
|
+
- `sage_consult` is blocked for non-interactive/CI contexts and RPC-orchestrated roles. Exception: interactive top-level `supervisor` may be treated as primary.
|
|
187
187
|
|
|
188
188
|
## 8.2 Soft limits (defaults)
|
|
189
189
|
Soft limits are policy/budget controls and are bypassable for explicit user requests (`force=true`).
|
|
@@ -213,7 +213,7 @@ Implement a deterministic `isEligibleCaller(context)` check before any policy/bu
|
|
|
213
213
|
|
|
214
214
|
Required conditions (all must be true):
|
|
215
215
|
1. `context.session.interactive === true`
|
|
216
|
-
2. `context.agent.role === "primary"`
|
|
216
|
+
2. `context.agent.role === "primary"` OR (`context.agent.role === "supervisor"` in interactive top-level context)
|
|
217
217
|
3. `context.agent.isSubagent !== true`
|
|
218
218
|
4. `context.agent.isRpcOrchestrated !== true`
|
|
219
219
|
5. `context.runtime.mode !== "ci"`
|
|
@@ -241,20 +241,23 @@ function isEligibleCaller(ctx: CallerContext): { ok: boolean; blockCode?: string
|
|
|
241
241
|
if (ctx.agent.isSubagent === true) {
|
|
242
242
|
return { ok: false, blockCode: "subagent", reason: "Sage disabled for subagents" };
|
|
243
243
|
}
|
|
244
|
-
|
|
244
|
+
const interactiveSupervisor =
|
|
245
|
+
ctx.agent.role === "supervisor" && ctx.session.interactive === true && ctx.runtime.mode === "interactive";
|
|
246
|
+
if (ctx.agent.role !== "primary" && !interactiveSupervisor) {
|
|
245
247
|
return { ok: false, blockCode: "ineligible-caller", reason: "Only primary agent may invoke Sage" };
|
|
246
248
|
}
|
|
247
|
-
return { ok: true, reason: "eligible" };
|
|
249
|
+
return { ok: true, reason: interactiveSupervisor ? "eligible (interactive supervisor)" : "eligible" };
|
|
248
250
|
}
|
|
249
251
|
```
|
|
250
252
|
|
|
251
253
|
## 8.7 RPC role mapping guidance (orchestration frameworks)
|
|
252
|
-
For orchestrated multi-agent frameworks, map caller role metadata so these roles are
|
|
253
|
-
- `supervisor`
|
|
254
|
+
For orchestrated multi-agent frameworks, map caller role metadata so these roles are ineligible by default:
|
|
254
255
|
- `worker`
|
|
255
256
|
- `reviewer`
|
|
256
257
|
- `merger`
|
|
257
258
|
|
|
259
|
+
`supervisor` is ineligible unless it is the interactive top-level user-facing session.
|
|
260
|
+
|
|
258
261
|
If runtime only provides a session name/string, derive role via conservative matching and deny on ambiguity.
|
|
259
262
|
|
|
260
263
|
---
|
|
@@ -372,7 +375,7 @@ On tool-policy/data-policy block:
|
|
|
372
375
|
|
|
373
376
|
1. Primary agent autonomously invokes Sage at least once in a complex debug scenario without user explicitly asking (interactive top-level primary session).
|
|
374
377
|
2. In CI/non-interactive mode, Sage invocation is blocked (non-bypassable caller-scope gate).
|
|
375
|
-
3. RPC-orchestrated agents
|
|
378
|
+
3. RPC-orchestrated agents cannot invoke `sage_consult` (interactive top-level `supervisor` exception).
|
|
376
379
|
4. User phrase “get a second opinion” in an eligible interactive primary session causes at least one Sage consultation in same task flow.
|
|
377
380
|
5. Explicit user-requested Sage consultation can bypass soft limits, but still respects hard safety limits and caller-scope restrictions.
|
|
378
381
|
6. No `/sage` command exists.
|
|
@@ -410,7 +413,7 @@ On tool-policy/data-policy block:
|
|
|
410
413
|
6. Add system-prompt guidance injection for autonomous invocation.
|
|
411
414
|
7. Add budget gates and policy telemetry.
|
|
412
415
|
8. Add rendering polish and acceptance tests.
|
|
413
|
-
9. Add a caller-context + tool-policy test matrix (interactive primary allowed; CI blocked;
|
|
416
|
+
9. Add a caller-context + tool-policy test matrix (interactive primary allowed; interactive supervisor allowed; CI blocked; rpc-orchestrated roles blocked; subagent blocked; unknown context blocked; disallowed tool attempts blocked).
|
|
414
417
|
|
|
415
418
|
---
|
|
416
419
|
|
|
@@ -470,12 +473,12 @@ On tool-policy/data-policy block:
|
|
|
470
473
|
- [ ] Show effective scope/source (project/global/default) and save target.
|
|
471
474
|
|
|
472
475
|
### 17.6 Test checklist
|
|
473
|
-
- [ ] Unit: `isEligibleCaller` allows
|
|
476
|
+
- [ ] Unit: `isEligibleCaller` allows interactive primary (and interactive supervisor exception); blocks CI, non-interactive, subagent, rpc-role, unknown context.
|
|
474
477
|
- [ ] Unit: `resolveToolPolicy` defaults to `read-only-lite` and strips/blocks disallowed custom tools.
|
|
475
478
|
- [ ] Unit: `isPathAllowed` blocks denylisted paths and non-workspace paths.
|
|
476
479
|
- [ ] Unit: `checkVolumeCaps` trips correctly on call/file/byte overages.
|
|
477
480
|
- [ ] Integration: explicit user request with `force=true` bypasses soft limits but not caller gate.
|
|
478
|
-
- [ ] Integration: RPC roles
|
|
481
|
+
- [ ] Integration: RPC-orchestrated roles receive structured blocked result (interactive supervisor exception).
|
|
479
482
|
- [ ] Integration: Sage subprocess cannot call `sage_consult` (recursion test).
|
|
480
483
|
- [ ] Integration: tool usage metadata is present in successful responses.
|
|
481
484
|
- [ ] Integration: blocked results include `blockCode` + human-readable reason.
|