pi-delegation-policy 0.1.2 → 0.2.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 CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.2.0 - 2026-08-26
6
+
7
+ ### Added
8
+
9
+ - Added optional global intensity with session-branch overrides and a built-in `off` fallback.
10
+ - Added **Use global default** to the intensity selector.
11
+
12
+ ### Fixed
13
+
14
+ - Replaced the terminal-ambiguous `Ctrl+Shift+D` shortcut with `Ctrl+Alt+D`.
15
+
5
16
  ## 0.1.2 - 2026-08-26
6
17
 
7
18
  ### Fixed
package/README.md CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  A local Pi extension that lets you choose delegation intensity and exact model references for Small, Medium, Large, and an optional UI Design role. It guides the main agent; it does not run, route, or enforce delegated work.
4
4
 
5
- > **Status:** Version 0.1.2 is available from npm. Every new session starts at `off`.
5
+ > **Status:** Version 0.2.0 is available from npm.
6
6
  >
7
7
  > **Documentation:** Read the [documentation site](https://yivas.github.io/pi-delegation-policy/).
8
8
 
9
9
  ## What it does
10
10
 
11
- - Sets delegation intensity to `off`, `normal`, or `aggressive` for the current session branch.
12
- - Keeps global defaults for model references, preference, and the optional UI Design role.
11
+ - Sets delegation intensity to `off`, `normal`, or `aggressive` globally or for the current session branch.
12
+ - Keeps global defaults for intensity, model references, preference, and the optional UI Design role.
13
13
  - Stores session changes in Pi's session branch, so they survive reload, resume, and tree navigation.
14
14
  - Uses Pi's scoped models when configured, otherwise its available authenticated models.
15
15
  - Injects one policy block through Pi's public `before_agent_start` event when the active configuration is valid.
@@ -39,7 +39,7 @@ It supports Pi `0.84.1`. Restart Pi or run `/reload` after installation. To inst
39
39
  /delegate reset Reset this session branch to off
40
40
  ```
41
41
 
42
- `Ctrl+Shift+D` opens the selector when the shortcut is available. There is no separate off shortcut; use `/delegate off` or choose `off` in the selector. Changes apply to the next agent run. An agent already running keeps the system prompt it started with.
42
+ `Ctrl+Alt+D` opens the selector when the shortcut is available. There is no separate off shortcut; use `/delegate off` or choose `off` in the selector. Changes apply to the next agent run. An agent already running keeps the system prompt it started with.
43
43
 
44
44
  The footer shows `D:OFF`, `D:NORM`, `D:AGG`, or `D:ERR` without replacing Pi's own status.
45
45
 
@@ -51,11 +51,12 @@ Global defaults live at:
51
51
  ~/.pi/agent/delegation-policy.json
52
52
  ```
53
53
 
54
- They use schema version 2. The file stores model references, preference, and an optional UI Design model. It never stores intensity or thinking.
54
+ They use schema version 2. The file stores optional intensity, model references, preference, and an optional UI Design model. It never stores thinking.
55
55
 
56
56
  ```json
57
57
  {
58
58
  "schemaVersion": 2,
59
+ "intensity": "normal",
59
60
  "preference": "standard",
60
61
  "small": {
61
62
  "provider": "example-provider",
@@ -82,9 +83,9 @@ Schema version 1 is inactive and is not migrated automatically. Open `/delegate`
82
83
 
83
84
  ### Global defaults and session branches
84
85
 
85
- A new session with no delegation entry starts at `off`. A branch restores the latest valid delegation entry in its active history, so a fork created after `normal`, `aggressive`, or `off` inherits that state until the branch records another change. Global model references and preference apply until the session branch overrides their matching fields. Selecting **Save effective configuration as defaults** copies the current roles, preference, and UI Design setting to the global file without copying intensity.
86
+ A session branch inherits global intensity, model references, preference, and UI Design until it records matching overrides. If global intensity is absent, the built-in default is `off`. A fork restores the latest valid delegation entry in its active history. The intensity selector can return a branch to **Use global default**.
86
87
 
87
- `/delegate reset` writes a session state with `off` and returns non-intensity fields to their global defaults.
88
+ Selecting **Save effective configuration as defaults** copies the complete effective configuration, including intensity, to the global file. `/delegate reset` remains an explicit safety action: it writes a branch state with `off` and returns every other field to its global default.
88
89
 
89
90
  ### Intensity
90
91
 
@@ -117,7 +118,7 @@ UI Design is optional. When configured, it is limited to visual design direction
117
118
 
118
119
  ## Security and privacy
119
120
 
120
- The extension stores provider and model identifiers in local configuration and session entries. It does not store credentials, send telemetry, or make network requests. Review configuration before using it and remove credentials, prompts, personal paths, session files, and unredacted logs from reports.
121
+ The extension stores intensity, preference, and provider/model identifiers in local configuration and session entries. It does not store credentials, send telemetry, or make network requests. Review configuration before using it and remove credentials, prompts, personal paths, session files, and unredacted logs from reports.
121
122
 
122
123
  Read [`SECURITY.md`](SECURITY.md) for reporting guidance.
123
124
 
package/SECURITY.md CHANGED
@@ -14,4 +14,4 @@ Include the affected version or commit, operating system, Pi version, reproducti
14
14
 
15
15
  ## Supported versions
16
16
 
17
- Only the latest published version is supported. Version 0.1.2 is the current supported release.
17
+ Only the latest published version is supported. Version 0.2.0 is the current supported release.
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
+ "intensity": "normal",
3
4
  "preference": "standard",
4
5
  "small": {
5
6
  "provider": "example-provider",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-delegation-policy",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "private": false,
5
5
  "description": "A Pi extension for configurable delegation intensity and exact subagent role model references.",
6
6
  "type": "module",
@@ -6,6 +6,7 @@
6
6
  "required": ["schemaVersion"],
7
7
  "properties": {
8
8
  "schemaVersion": { "const": 2 },
9
+ "intensity": { "enum": ["off", "normal", "aggressive"] },
9
10
  "preference": { "enum": ["efficient", "standard", "intensive"] },
10
11
  "small": { "$ref": "#/$defs/modelRef" },
11
12
  "medium": { "$ref": "#/$defs/modelRef" },
package/src/config.ts CHANGED
@@ -67,8 +67,17 @@ function copyModelRef(value: ModelRef | undefined): ModelRef | undefined {
67
67
  export function parseConfig(value: unknown): GlobalDefaults | undefined {
68
68
  if (
69
69
  !isRecord(value) ||
70
- !hasOnlyKeys(value, ["schemaVersion", "preference", "small", "medium", "large", "uiDesign"]) ||
70
+ !hasOnlyKeys(value, [
71
+ "schemaVersion",
72
+ "intensity",
73
+ "preference",
74
+ "small",
75
+ "medium",
76
+ "large",
77
+ "uiDesign",
78
+ ]) ||
71
79
  value.schemaVersion !== 2 ||
80
+ (value.intensity !== undefined && !isIntensity(value.intensity)) ||
72
81
  (value.preference !== undefined && !isPreference(value.preference))
73
82
  )
74
83
  return undefined;
@@ -88,6 +97,7 @@ export function parseConfig(value: unknown): GlobalDefaults | undefined {
88
97
 
89
98
  return {
90
99
  schemaVersion: 2,
100
+ ...(value.intensity ? { intensity: value.intensity } : {}),
91
101
  ...(value.preference ? { preference: value.preference } : {}),
92
102
  ...(small ? { small } : {}),
93
103
  ...(medium ? { medium } : {}),
@@ -109,7 +119,7 @@ export function parseSessionState(value: unknown): SessionDelegateState | undefi
109
119
  "uiDesign",
110
120
  ]) ||
111
121
  value.schemaVersion !== 2 ||
112
- !isIntensity(value.intensity) ||
122
+ (value.intensity !== undefined && !isIntensity(value.intensity)) ||
113
123
  (value.preference !== undefined && !isPreference(value.preference))
114
124
  )
115
125
  return undefined;
@@ -132,7 +142,7 @@ export function parseSessionState(value: unknown): SessionDelegateState | undefi
132
142
 
133
143
  return {
134
144
  schemaVersion: 2,
135
- intensity: value.intensity,
145
+ ...(value.intensity ? { intensity: value.intensity } : {}),
136
146
  ...(value.preference ? { preference: value.preference } : {}),
137
147
  ...(small ? { small } : {}),
138
148
  ...(medium ? { medium } : {}),
@@ -218,14 +228,14 @@ export function resolveDelegateState(
218
228
  : copyModelRef(session.uiDesign);
219
229
 
220
230
  return {
221
- intensity: session.intensity,
231
+ intensity: session.intensity ?? defaults.intensity ?? "off",
222
232
  preference: session.preference ?? defaults.preference ?? "standard",
223
233
  small: copyModelRef(session.small ?? defaults.small),
224
234
  medium: copyModelRef(session.medium ?? defaults.medium),
225
235
  large: copyModelRef(session.large ?? defaults.large),
226
236
  ...(uiDesign ? { uiDesign } : {}),
227
237
  source: {
228
- intensity: "session",
238
+ intensity: sourceFor(session.intensity, defaults.intensity),
229
239
  preference: sourceFor(session.preference, defaults.preference),
230
240
  small: sourceFor(session.small, defaults.small),
231
241
  medium: sourceFor(session.medium, defaults.medium),
@@ -238,6 +248,7 @@ export function resolveDelegateState(
238
248
  export function defaultsFromEffectiveState(state: EffectiveDelegateState): GlobalDefaults {
239
249
  return {
240
250
  schemaVersion: 2,
251
+ intensity: state.intensity,
241
252
  preference: state.preference,
242
253
  ...(state.small ? { small: { ...state.small } } : {}),
243
254
  ...(state.medium ? { medium: { ...state.medium } } : {}),
package/src/index.ts CHANGED
@@ -49,14 +49,19 @@ function updateStatus(ctx: ExtensionContext, state: RuntimeState): void {
49
49
  ctx.ui.setStatus(STATUS_KEY, ctx.ui.theme.fg("dim", statusLabel(state)));
50
50
  }
51
51
 
52
+ async function openEditor(pi: ExtensionAPI, ctx: ExtensionContext): Promise<void> {
53
+ await openDelegateEditor(ctx, pi);
54
+ updateStatus(ctx, await refresh(ctx));
55
+ }
56
+
52
57
  function configuredLabel(value: unknown): string {
53
58
  return value ? "configured" : "unset";
54
59
  }
55
60
 
56
- function statusText(state: RuntimeState): string {
61
+ export function statusText(state: RuntimeState): string {
57
62
  const { effective } = state;
58
63
  const details = [
59
- `${statusLabel(state)} intensity=${effective.intensity}`,
64
+ `${statusLabel(state)} intensity=${effective.intensity} (${effective.source.intensity})`,
60
65
  `preference=${effective.preference} (${effective.source.preference})`,
61
66
  `small=${configuredLabel(effective.small)} (${effective.source.small})`,
62
67
  `medium=${configuredLabel(effective.medium)} (${effective.source.medium})`,
@@ -99,7 +104,7 @@ export default function piDelegationPolicy(pi: ExtensionAPI): void {
99
104
  handler: async (args, ctx) => {
100
105
  const action = parseCommand(args);
101
106
  if (action.kind === "open") {
102
- await openDelegateEditor(ctx, pi);
107
+ await openEditor(pi, ctx);
103
108
  return;
104
109
  }
105
110
  if (action.kind === "status") {
@@ -120,9 +125,9 @@ export default function piDelegationPolicy(pi: ExtensionAPI): void {
120
125
  },
121
126
  });
122
127
 
123
- pi.registerShortcut("ctrl+shift+d", {
128
+ pi.registerShortcut("ctrl+alt+d", {
124
129
  description: "Open delegation policy",
125
- handler: async (ctx) => openDelegateEditor(ctx, pi),
130
+ handler: async (ctx) => openEditor(pi, ctx),
126
131
  });
127
132
 
128
133
  pi.on("session_start", async (_event, ctx) => {
package/src/types.ts CHANGED
@@ -24,6 +24,7 @@ export type ModelRef = {
24
24
 
25
25
  export type GlobalDefaults = {
26
26
  schemaVersion: 2;
27
+ intensity?: Intensity;
27
28
  preference?: Preference;
28
29
  small?: ModelRef;
29
30
  medium?: ModelRef;
@@ -33,7 +34,7 @@ export type GlobalDefaults = {
33
34
 
34
35
  export type SessionDelegateState = {
35
36
  schemaVersion: 2;
36
- intensity: Intensity;
37
+ intensity?: Intensity;
37
38
  preference?: Preference;
38
39
  small?: ModelRef;
39
40
  medium?: ModelRef;
@@ -52,7 +53,7 @@ export type EffectiveDelegateState = {
52
53
  large?: ModelRef;
53
54
  uiDesign?: ModelRef;
54
55
  source: {
55
- intensity: "session";
56
+ intensity: ValueSource;
56
57
  preference: ValueSource;
57
58
  small: ValueSource;
58
59
  medium: ValueSource;
@@ -73,5 +74,5 @@ export function emptyGlobalDefaults(): GlobalDefaults {
73
74
  }
74
75
 
75
76
  export function emptySessionState(): SessionDelegateState {
76
- return { schemaVersion: 2, intensity: "off" };
77
+ return { schemaVersion: 2 };
77
78
  }
package/src/ui.ts CHANGED
@@ -13,7 +13,6 @@ import {
13
13
  type RuntimeState,
14
14
  } from "./runtime.ts";
15
15
  import {
16
- emptySessionState,
17
16
  INTENSITIES,
18
17
  MODEL_ROLES,
19
18
  PREFERENCES,
@@ -109,8 +108,13 @@ async function selectUiDesign(ctx: ExtensionContext, draft: SessionDelegateState
109
108
  }
110
109
 
111
110
  async function selectIntensity(ctx: ExtensionContext, draft: SessionDelegateState): Promise<void> {
112
- const selected = await selectOrCancel(ctx, "Delegation intensity", [...INTENSITIES]);
113
- if (selected) draft.intensity = selected as Intensity;
111
+ const selected = await selectOrCancel(ctx, "Delegation intensity", [
112
+ USE_GLOBAL_DEFAULT,
113
+ ...INTENSITIES,
114
+ ]);
115
+ if (!selected) return;
116
+ if (selected === USE_GLOBAL_DEFAULT) delete draft.intensity;
117
+ else draft.intensity = selected as Intensity;
114
118
  }
115
119
 
116
120
  async function selectPreference(ctx: ExtensionContext, draft: SessionDelegateState): Promise<void> {
@@ -194,7 +198,7 @@ export async function openDelegateEditor(ctx: ExtensionContext, pi: ExtensionAPI
194
198
  await writeConfig(getGlobalConfigPath(), defaults);
195
199
  state.global = defaults;
196
200
  state.diagnostics = [];
197
- ctx.ui.notify("Saved effective role settings as global defaults.", "info");
201
+ ctx.ui.notify("Saved effective delegation settings as global defaults.", "info");
198
202
  } catch {
199
203
  ctx.ui.notify(
200
204
  "Could not save global defaults. Session settings were not changed.",
@@ -203,7 +207,7 @@ export async function openDelegateEditor(ctx: ExtensionContext, pi: ExtensionAPI
203
207
  }
204
208
  }
205
209
  if (selected === RESET_SESSION) {
206
- draft = emptySessionState();
210
+ draft = { schemaVersion: 2, intensity: "off" };
207
211
  }
208
212
  }
209
213
  }