pi-delegation-policy 0.3.2 → 0.4.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 +12 -0
- package/README.md +4 -4
- package/SECURITY.md +1 -1
- package/package.json +7 -5
- package/src/delegate-panel.ts +86 -17
- package/src/index.ts +5 -8
- package/src/prompt.ts +43 -17
- package/src/ui.ts +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.4.0 - 2026-08-27
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added a compact effective-policy preview, selector guidance, and available public model metadata to the `/delegate` panel.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Require exact `provider/model` references for delegation guidance instead of relying on a launcher default.
|
|
14
|
+
- Clarified task-fit role selection, preference tie-breaks, dynamic advisory thinking, and current panel and status diagnostics.
|
|
15
|
+
- Raised the Pi peer requirement and explicitly checked baseline to `0.84.3`.
|
|
16
|
+
|
|
5
17
|
## 0.3.2 - 2026-08-26
|
|
6
18
|
|
|
7
19
|
### Documentation
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A local Pi extension that helps the main agent decide **when delegation is worth it** and which exact models to use for the Small, Medium, Large, and optional UI Design roles. It provides guidance; it is not a subagent runner.
|
|
4
4
|
|
|
5
|
-
> **Status:** Version **0.
|
|
5
|
+
> **Status:** Version **0.4.0** is the latest published package. The package requires Pi `>=0.84.3`; Pi `0.84.3` is the explicitly checked baseline (newer versions are not claimed as tested).
|
|
6
6
|
>
|
|
7
7
|
> **Docs:** [Read the documentation site](https://yivas.github.io/pi-delegation-policy/).
|
|
8
8
|
|
|
@@ -27,7 +27,7 @@ pi install npm:pi-delegation-policy
|
|
|
27
27
|
1. Open `/delegate` (or press `Alt+G` in Pi's TUI).
|
|
28
28
|
2. Configure exact, authenticated Small, Medium, and Large provider/model references. UI Design is optional.
|
|
29
29
|
3. Select `normal` or `aggressive`, then choose **Apply changes**.
|
|
30
|
-
4. Run `/delegate status` and inspect the
|
|
30
|
+
4. Run `/delegate status` and inspect the exact role references and their sources. `D:ERR` means an active required role is invalid; no policy is injected.
|
|
31
31
|
5. The applied state affects the **next** agent run. An agent already running is not rewritten.
|
|
32
32
|
|
|
33
33
|
See the [end-to-end getting-started guide](https://yivas.github.io/pi-delegation-policy/getting-started/) and [configuration reference](https://yivas.github.io/pi-delegation-policy/configuration/) for details.
|
|
@@ -43,11 +43,11 @@ See the [end-to-end getting-started guide](https://yivas.github.io/pi-delegation
|
|
|
43
43
|
/delegate reset Reset this branch to off and other fields to global defaults
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
The editor is a bounded, keyboard-first panel. It shows model ID first and `[provider]` last, fuzzy-searches provider, model ID, and display name, and shows at most 10 model rows.
|
|
46
|
+
The editor is a bounded, keyboard-first panel. It shows model ID first and `[provider]` last, fuzzy-searches provider, model ID, and display name, and shows at most 10 model rows. It also shows a compact effective-policy preview, field explanations, and public model metadata when Pi supplies it. Changes are drafts until **Apply changes**; saving effective configuration as defaults updates the global file without applying the draft, and closing a modified draft requires explicit discard.
|
|
47
47
|
|
|
48
48
|
## Configuration and safety
|
|
49
49
|
|
|
50
|
-
Global defaults are stored at `~/.pi/agent/delegation-policy.json` (schema version 2). The file stores optional intensity, preference, exact role references, and an optional UI Design reference; it never stores thinking. A valid active mode requires exact, available, in-scope, authenticated Small, Medium, and Large references. Invalid active configuration fails closed as `D:ERR` with no policy injection; `off` is always `D:OFF`.
|
|
50
|
+
Global defaults are stored at `~/.pi/agent/delegation-policy.json` (schema version 2). The file stores optional intensity, preference, exact role references, and an optional UI Design reference; it never stores thinking. A valid active mode requires exact, available, in-scope, authenticated Small, Medium, and Large references. Before every delegated launch, the policy requires the selected role's exact combined reference as `model: "provider/model"`; it does not rely on an ambient launcher default. Invalid active configuration fails closed as `D:ERR` with no policy injection; `off` is always `D:OFF`.
|
|
51
51
|
|
|
52
52
|
The extension stores only policy settings and provider/model identifiers in local defaults and session entries. Review local configuration before sharing diagnostics, and remove credentials, prompts, personal paths, session files, and unredacted logs from reports. See the [limits and privacy reference](https://yivas.github.io/pi-delegation-policy/limits-and-privacy/) and [security policy](https://github.com/Yivas/pi-delegation-policy/blob/main/SECURITY.md).
|
|
53
53
|
|
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.
|
|
17
|
+
Only the latest published version is supported. Version 0.4.0 is the current supported release.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-delegation-policy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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",
|
|
@@ -36,12 +36,14 @@
|
|
|
36
36
|
"pack:check": "npm pack --dry-run"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@earendil-works/pi-
|
|
39
|
+
"@earendil-works/pi-ai": ">=0.84.3",
|
|
40
|
+
"@earendil-works/pi-coding-agent": ">=0.84.3",
|
|
41
|
+
"@earendil-works/pi-tui": ">=0.84.3"
|
|
40
42
|
},
|
|
41
43
|
"devDependencies": {
|
|
42
|
-
"@earendil-works/pi-ai": "0.84.
|
|
43
|
-
"@earendil-works/pi-coding-agent": "0.84.
|
|
44
|
-
"@earendil-works/pi-tui": "0.84.
|
|
44
|
+
"@earendil-works/pi-ai": "0.84.3",
|
|
45
|
+
"@earendil-works/pi-coding-agent": "0.84.3",
|
|
46
|
+
"@earendil-works/pi-tui": "0.84.3",
|
|
45
47
|
"@eslint/js": "^9.39.5",
|
|
46
48
|
"@types/node": "^22.0.0",
|
|
47
49
|
"ajv": "^8.20.0",
|
package/src/delegate-panel.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type TUI,
|
|
13
13
|
} from "@earendil-works/pi-tui";
|
|
14
14
|
import { resolveDelegateState } from "./config.ts";
|
|
15
|
+
import { buildPolicyPreview } from "./prompt.ts";
|
|
15
16
|
import {
|
|
16
17
|
INTENSITIES,
|
|
17
18
|
PREFERENCES,
|
|
@@ -52,6 +53,7 @@ export interface DelegatePanelOptions {
|
|
|
52
53
|
session: SessionDelegateState;
|
|
53
54
|
candidates: Model<Api>[];
|
|
54
55
|
diagnostics: string[];
|
|
56
|
+
hasRuntimeError: boolean;
|
|
55
57
|
onApply: (draft: SessionDelegateState) => Promise<boolean>;
|
|
56
58
|
onSaveDefaults: (draft: SessionDelegateState) => Promise<GlobalDefaults | undefined>;
|
|
57
59
|
onDone: (result: DelegatePanelResult) => void;
|
|
@@ -74,6 +76,15 @@ const FIELD_LABELS: Record<DelegateField, string> = {
|
|
|
74
76
|
uiDesign: "UI Design",
|
|
75
77
|
};
|
|
76
78
|
|
|
79
|
+
const FIELD_DESCRIPTIONS: Record<DelegateField, string> = {
|
|
80
|
+
intensity: "When delegation is worth considering.",
|
|
81
|
+
preference: "Tie-break only; task fit decides the role first.",
|
|
82
|
+
small: "Bounded, planned, and verifiable execution.",
|
|
83
|
+
medium: "Planning, ambiguity, synthesis, and coordination.",
|
|
84
|
+
large: "Exceptional unblocker for persistent hard problems.",
|
|
85
|
+
uiDesign: "Visual direction and review only; never implementation.",
|
|
86
|
+
};
|
|
87
|
+
|
|
77
88
|
const ACTION_LABELS: Record<PanelAction, string> = {
|
|
78
89
|
apply: "Apply changes",
|
|
79
90
|
"save-defaults": "Save effective configuration as defaults",
|
|
@@ -132,6 +143,17 @@ function sortedModels(models: readonly Model<Api>[]): Model<Api>[] {
|
|
|
132
143
|
});
|
|
133
144
|
}
|
|
134
145
|
|
|
146
|
+
function modelMetadata(model: Model<Api> | undefined): string[] {
|
|
147
|
+
if (!model) return [];
|
|
148
|
+
const details: string[] = [];
|
|
149
|
+
if (model.name !== undefined) details.push(`Model Name: ${model.name}`);
|
|
150
|
+
if (model.api !== undefined) details.push(`API: ${model.api}`);
|
|
151
|
+
if (model.reasoning !== undefined) details.push(`Reasoning: ${model.reasoning ? "yes" : "no"}`);
|
|
152
|
+
if (model.contextWindow !== undefined) details.push(`Context: ${model.contextWindow}`);
|
|
153
|
+
if (model.maxTokens !== undefined) details.push(`Max output: ${model.maxTokens}`);
|
|
154
|
+
return details;
|
|
155
|
+
}
|
|
156
|
+
|
|
135
157
|
function visibleBlockRange(blocks: string[][], selected: number, budget: number): [number, number] {
|
|
136
158
|
if (blocks.length === 0) return [0, 0];
|
|
137
159
|
const safeSelected = Math.max(0, Math.min(selected, blocks.length - 1));
|
|
@@ -169,6 +191,7 @@ export class DelegatePanel implements Component, Focusable {
|
|
|
169
191
|
private readonly theme: Theme;
|
|
170
192
|
private readonly candidates: Model<Api>[];
|
|
171
193
|
private readonly diagnostics: string[];
|
|
194
|
+
private readonly hasRuntimeError: boolean;
|
|
172
195
|
private readonly onApply: DelegatePanelOptions["onApply"];
|
|
173
196
|
private readonly onSaveDefaults: DelegatePanelOptions["onSaveDefaults"];
|
|
174
197
|
private readonly onDone: DelegatePanelOptions["onDone"];
|
|
@@ -191,6 +214,7 @@ export class DelegatePanel implements Component, Focusable {
|
|
|
191
214
|
this.draft = cloneSession(options.session);
|
|
192
215
|
this.candidates = sortedModels(options.candidates);
|
|
193
216
|
this.diagnostics = [...options.diagnostics];
|
|
217
|
+
this.hasRuntimeError = options.hasRuntimeError;
|
|
194
218
|
this.onApply = options.onApply;
|
|
195
219
|
this.onSaveDefaults = options.onSaveDefaults;
|
|
196
220
|
this.onDone = options.onDone;
|
|
@@ -332,9 +356,7 @@ export class DelegatePanel implements Component, Focusable {
|
|
|
332
356
|
];
|
|
333
357
|
const notice =
|
|
334
358
|
this.message ??
|
|
335
|
-
(this.diagnostics.
|
|
336
|
-
? { kind: "error" as const, text: "Warning: global defaults are invalid." }
|
|
337
|
-
: undefined);
|
|
359
|
+
(this.diagnostics[0] ? { kind: "error" as const, text: this.diagnostics[0] } : undefined);
|
|
338
360
|
if (notice) {
|
|
339
361
|
lines.push(
|
|
340
362
|
truncateToWidth(
|
|
@@ -365,6 +387,8 @@ export class DelegatePanel implements Component, Focusable {
|
|
|
365
387
|
|
|
366
388
|
private renderSettings(width: number, budget: number): string[] {
|
|
367
389
|
const effective = resolveDelegateState(this.global, this.draft);
|
|
390
|
+
const preview = this.renderPolicyPreview(width, budget, effective);
|
|
391
|
+
const settingsBudget = Math.max(1, budget - preview.length);
|
|
368
392
|
const blocks = SETTINGS_ITEMS.map((item, index) => {
|
|
369
393
|
const selected = index === this.settingsIndex;
|
|
370
394
|
if (FIELD_IDS.includes(item as DelegateField)) {
|
|
@@ -381,6 +405,7 @@ export class DelegatePanel implements Component, Focusable {
|
|
|
381
405
|
if (width < 48) {
|
|
382
406
|
return [
|
|
383
407
|
selectedLine(this.theme, FIELD_LABELS[field], width, selected),
|
|
408
|
+
...wrapTextWithAnsi(` ${FIELD_DESCRIPTIONS[field]}`, width),
|
|
384
409
|
...wrapTextWithAnsi(` ${value}`, width),
|
|
385
410
|
...details.flatMap((line) =>
|
|
386
411
|
wrapTextWithAnsi(this.theme.fg("dim", ` ${line}`), width),
|
|
@@ -391,7 +416,10 @@ export class DelegatePanel implements Component, Focusable {
|
|
|
391
416
|
const first = `${FIELD_LABELS[field].padEnd(labelWidth)}${value}`;
|
|
392
417
|
return [
|
|
393
418
|
selectedLine(this.theme, first, width, selected),
|
|
394
|
-
...wrapTextWithAnsi(
|
|
419
|
+
...wrapTextWithAnsi(
|
|
420
|
+
this.theme.fg("dim", ` ${FIELD_DESCRIPTIONS[field]} · ${details.join(" · ")}`),
|
|
421
|
+
width,
|
|
422
|
+
),
|
|
395
423
|
];
|
|
396
424
|
}
|
|
397
425
|
const action = item as PanelAction;
|
|
@@ -401,7 +429,25 @@ export class DelegatePanel implements Component, Focusable {
|
|
|
401
429
|
return [disabled ? this.theme.fg("dim", line) : line];
|
|
402
430
|
});
|
|
403
431
|
|
|
404
|
-
return
|
|
432
|
+
return [
|
|
433
|
+
...preview,
|
|
434
|
+
...this.renderBlockViewport(blocks, this.settingsIndex, width, settingsBudget),
|
|
435
|
+
];
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
private renderPolicyPreview(
|
|
439
|
+
width: number,
|
|
440
|
+
budget: number,
|
|
441
|
+
effective: ReturnType<typeof resolveDelegateState>,
|
|
442
|
+
): string[] {
|
|
443
|
+
const lines =
|
|
444
|
+
effective.intensity !== "off" && this.hasRuntimeError
|
|
445
|
+
? ["D:ERR · policy unavailable; fix the reported role diagnostics"]
|
|
446
|
+
: buildPolicyPreview(effective);
|
|
447
|
+
const maximum = width >= 60 && budget >= 8 ? 4 : 2;
|
|
448
|
+
return ["Effective policy preview", ...lines]
|
|
449
|
+
.slice(0, maximum)
|
|
450
|
+
.map((line) => truncateToWidth(this.theme.fg("dim", line), width, ""));
|
|
405
451
|
}
|
|
406
452
|
|
|
407
453
|
private sourceDetails(field: DelegateField): string[] {
|
|
@@ -439,12 +485,30 @@ export class DelegatePanel implements Component, Focusable {
|
|
|
439
485
|
mode: Extract<PanelMode, { kind: "enum" }>,
|
|
440
486
|
): string[] {
|
|
441
487
|
const values = mode.field === "intensity" ? INTENSITIES : PREFERENCES;
|
|
488
|
+
const descriptions =
|
|
489
|
+
mode.field === "intensity"
|
|
490
|
+
? {
|
|
491
|
+
off: "No policy is injected.",
|
|
492
|
+
normal: "Delegate when the expected benefit clearly outweighs overhead.",
|
|
493
|
+
aggressive: "Delegate suitable substantial work by default.",
|
|
494
|
+
}
|
|
495
|
+
: {
|
|
496
|
+
efficient: "Tie-break comparable fits toward Small.",
|
|
497
|
+
standard: "No extra Small or Medium bias.",
|
|
498
|
+
intensive: "Tie-break comparable fits toward Medium.",
|
|
499
|
+
};
|
|
500
|
+
const globalValue =
|
|
501
|
+
this.global[mode.field] ?? (mode.field === "intensity" ? "off" : "standard");
|
|
442
502
|
const options = [
|
|
443
|
-
|
|
444
|
-
|
|
503
|
+
{
|
|
504
|
+
label: `${USE_GLOBAL_DEFAULT} (${globalValue})`,
|
|
505
|
+
description: "Use the current global value.",
|
|
506
|
+
},
|
|
507
|
+
...values.map((value) => ({ label: value, description: descriptions[value] })),
|
|
445
508
|
];
|
|
446
509
|
const blocks = options.map((option, index) => [
|
|
447
|
-
selectedLine(this.theme, option, width, index === mode.selected),
|
|
510
|
+
selectedLine(this.theme, option.label, width, index === mode.selected),
|
|
511
|
+
...wrapTextWithAnsi(this.theme.fg("dim", ` ${option.description}`), width),
|
|
448
512
|
]);
|
|
449
513
|
return this.renderBlockViewport(blocks, mode.selected, width, budget);
|
|
450
514
|
}
|
|
@@ -471,15 +535,20 @@ export class DelegatePanel implements Component, Focusable {
|
|
|
471
535
|
);
|
|
472
536
|
const dividerRows = budget > pinnedLines.length + 3 ? 1 : 0;
|
|
473
537
|
const selectedChoice = choices[mode.selected];
|
|
474
|
-
const
|
|
475
|
-
selectedChoice?.kind === "model"
|
|
476
|
-
?
|
|
538
|
+
const metadata =
|
|
539
|
+
selectedChoice?.kind === "model"
|
|
540
|
+
? modelMetadata(
|
|
541
|
+
this.candidates.find(
|
|
542
|
+
(model) =>
|
|
543
|
+
modelKey({ provider: model.provider, model: model.id }) === selectedChoice.key,
|
|
544
|
+
),
|
|
545
|
+
)
|
|
477
546
|
: [];
|
|
478
|
-
const
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
);
|
|
547
|
+
const fixedRows = 2 + pinnedLines.length + dividerRows;
|
|
548
|
+
const availableDetailRows = Math.max(0, budget - fixedRows - 1);
|
|
549
|
+
const details = metadata.slice(0, availableDetailRows);
|
|
550
|
+
const detailRows = details.length > 0 ? details.length + 1 : 0;
|
|
551
|
+
const availableListRows = Math.max(0, budget - fixedRows - detailRows);
|
|
483
552
|
const listBudget = Math.min(11, availableListRows);
|
|
484
553
|
const modelBlocks = models.map((choice, index) => {
|
|
485
554
|
const combinedIndex = index + pinnedCount;
|
|
@@ -512,7 +581,7 @@ export class DelegatePanel implements Component, Focusable {
|
|
|
512
581
|
...pinnedLines,
|
|
513
582
|
...(dividerRows ? [this.theme.fg("borderMuted", "─".repeat(width))] : []),
|
|
514
583
|
...modelLines,
|
|
515
|
-
...(detailRows ? ["", ...
|
|
584
|
+
...(detailRows ? ["", ...details.map((line) => this.theme.fg("muted", ` ${line}`))] : []),
|
|
516
585
|
].slice(0, budget);
|
|
517
586
|
}
|
|
518
587
|
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
import type { AutocompleteItem } from "@earendil-works/pi-tui";
|
|
7
7
|
import { buildDelegationPolicy } from "./prompt.ts";
|
|
8
8
|
import {
|
|
9
|
+
formatModelRef,
|
|
9
10
|
hasRuntimeError,
|
|
10
11
|
loadRuntime,
|
|
11
12
|
sessionEntry,
|
|
@@ -54,19 +55,15 @@ async function openEditor(pi: ExtensionAPI, ctx: ExtensionContext): Promise<void
|
|
|
54
55
|
updateStatus(ctx, await refresh(ctx));
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
function configuredLabel(value: unknown): string {
|
|
58
|
-
return value ? "configured" : "unset";
|
|
59
|
-
}
|
|
60
|
-
|
|
61
58
|
export function statusText(state: RuntimeState): string {
|
|
62
59
|
const { effective } = state;
|
|
63
60
|
const details = [
|
|
64
61
|
`${statusLabel(state)} intensity=${effective.intensity} (${effective.source.intensity})`,
|
|
65
62
|
`preference=${effective.preference} (${effective.source.preference})`,
|
|
66
|
-
`small=${
|
|
67
|
-
`medium=${
|
|
68
|
-
`large=${
|
|
69
|
-
`ui-design=${
|
|
63
|
+
`small=${formatModelRef(effective.small)} (${effective.source.small})`,
|
|
64
|
+
`medium=${formatModelRef(effective.medium)} (${effective.source.medium})`,
|
|
65
|
+
`large=${formatModelRef(effective.large)} (${effective.source.large})`,
|
|
66
|
+
`ui-design=${formatModelRef(effective.uiDesign)} (${effective.source.uiDesign})`,
|
|
70
67
|
];
|
|
71
68
|
|
|
72
69
|
if (effective.intensity !== "off" && state.runtimeErrors.length > 0) {
|
package/src/prompt.ts
CHANGED
|
@@ -1,35 +1,42 @@
|
|
|
1
1
|
import { hasRuntimeError, type RuntimeState } from "./runtime.ts";
|
|
2
|
-
import type { ModelRef, Preference } from "./types.ts";
|
|
2
|
+
import type { EffectiveDelegateState, ModelRef, Preference } from "./types.ts";
|
|
3
3
|
|
|
4
4
|
const NORMAL_POLICY =
|
|
5
5
|
"Delegate substantial, separable work only when the expected benefit clearly outweighs briefing, supervision, review, and integration cost. Count parallelism as a benefit only when valuable work can advance now or elapsed time matters. A merely possible fresh perspective is not enough by itself. Keep borderline work with the main agent.";
|
|
6
6
|
const AGGRESSIVE_POLICY =
|
|
7
7
|
"Default to delegating substantial, separable, independently checkable work with a clear objective and acceptance criteria. Delegate when the benefit is plausible even if not proven, including a useful independent perspective. Keep work with the main agent when it is poorly bounded, tightly coupled, dominated by integration or final accountability, or has clearly prohibitive delegation overhead.";
|
|
8
8
|
|
|
9
|
-
const ROLE_SELECTION_POLICY = `Choose the role
|
|
10
|
-
-
|
|
11
|
-
- difficulty: clarity, ambiguity, dependencies,
|
|
12
|
-
- quantity: files, modules, systems, sources, and context volume
|
|
13
|
-
|
|
9
|
+
const ROLE_SELECTION_POLICY = `Choose the role by task fit before considering model preference:
|
|
10
|
+
- demand: execute, search, plan, decide, coordinate, or unblock;
|
|
11
|
+
- difficulty: clarity, ambiguity, dependencies, competing hypotheses, and risk;
|
|
12
|
+
- quantity: files, modules, systems, sources, and context volume;
|
|
13
|
+
- error and review cost: what can go wrong, how costly it is to detect, and what evidence is needed.
|
|
14
|
+
No single factor decides the role. Select the smallest role that can satisfy the acceptance criteria and evidence requirements.
|
|
14
15
|
|
|
15
|
-
Use Small
|
|
16
|
+
Use Small for bounded, planned, and verifiable execution: concrete searches, scoped exploration, defined implementation, focused documentation, tests, reviews, mechanical changes, evident bugs, and bounded UI implementation whose design and stack are decided. Difficult but well-defined execution can remain Small with higher thinking.
|
|
16
17
|
|
|
17
|
-
Use Medium directly when the combined
|
|
18
|
+
Use Medium directly when the combined task fit materially requires planning, reducing meaningful ambiguity, broad synthesis, tracing several modules, comparing sources or options, coordinating substantial context, or making difficult decisions. Small does not need to fail first.
|
|
18
19
|
|
|
19
20
|
Use Large only to unblock genuinely stuck work: persistent failures, severe framework conflicts, contradictory hypotheses, or reliable prior evidence that ordinary roles have not produced a trustworthy answer. Do not require ceremonial failed attempts. Large remains exceptional.
|
|
20
21
|
|
|
21
|
-
Large quantities of repetitive, independent work favor multiple Small delegations; volume alone does not justify Medium or Large. Agent type does not determine the model role.
|
|
22
|
+
Large quantities of repetitive, independent work favor multiple Small delegations; volume alone does not justify Medium or Large. Agent type does not determine the model role. Apply preference only when Small and Medium are comparably credible fits.
|
|
22
23
|
|
|
23
24
|
In every intensity, keep global strategy, coordination, integration, final review, and work whose essential context is too costly or risky to transfer with the main agent.`;
|
|
24
25
|
|
|
25
26
|
function preferenceGuidance(preference: Preference): string {
|
|
26
27
|
if (preference === "efficient") {
|
|
27
|
-
return "
|
|
28
|
+
return "Use efficient only as a Small tie-break when Small and Medium are comparably credible. Do not choose Small when Medium is a materially better task fit.";
|
|
28
29
|
}
|
|
29
30
|
if (preference === "intensive") {
|
|
30
|
-
return "
|
|
31
|
+
return "Use intensive only as a Medium tie-break when Small and Medium are comparably credible. Do not choose Medium when Small is the clearly better task fit.";
|
|
31
32
|
}
|
|
32
|
-
return "
|
|
33
|
+
return "Standard adds no Small or Medium bias; follow task fit.";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function preferencePreview(preference: Preference): string {
|
|
37
|
+
if (preference === "efficient") return "efficient breaks comparable fits toward Small";
|
|
38
|
+
if (preference === "intensive") return "intensive breaks comparable fits toward Medium";
|
|
39
|
+
return "standard has no extra bias";
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
function promptString(value: string): string {
|
|
@@ -43,6 +50,25 @@ function formatReference(reference: ModelRef): string {
|
|
|
43
50
|
return `provider=${promptString(reference.provider)} model=${promptString(reference.model)}`;
|
|
44
51
|
}
|
|
45
52
|
|
|
53
|
+
function formatLaunchModel(reference: ModelRef): string {
|
|
54
|
+
return promptString(`${reference.provider}/${reference.model}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function buildPolicyPreview(effective: EffectiveDelegateState): string[] {
|
|
58
|
+
if (effective.intensity === "off") return ["off · no policy injected"];
|
|
59
|
+
|
|
60
|
+
const { small, medium, large } = effective;
|
|
61
|
+
if (!small) return ["active · Small not configured · no policy can be injected"];
|
|
62
|
+
if (!medium) return ["active · Medium not configured · no policy can be injected"];
|
|
63
|
+
if (!large) return ["active · Large not configured · no policy can be injected"];
|
|
64
|
+
|
|
65
|
+
return [
|
|
66
|
+
`${effective.intensity} · task fit first · ${preferencePreview(effective.preference)}`,
|
|
67
|
+
`Small ${formatLaunchModel(small)} · Medium ${formatLaunchModel(medium)} · Large ${formatLaunchModel(large)}`,
|
|
68
|
+
"Every launch must include the selected exact model; thinking stays dynamic.",
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
|
|
46
72
|
export function buildDelegationPolicy(state: RuntimeState): string | undefined {
|
|
47
73
|
if (state.effective.intensity === "off" || hasRuntimeError(state)) return undefined;
|
|
48
74
|
|
|
@@ -51,7 +77,7 @@ export function buildDelegationPolicy(state: RuntimeState): string | undefined {
|
|
|
51
77
|
|
|
52
78
|
const intensityPolicy = effective.intensity === "normal" ? NORMAL_POLICY : AGGRESSIVE_POLICY;
|
|
53
79
|
const uiDesign = effective.uiDesign
|
|
54
|
-
? `\n- UI Design: ${formatReference(effective.uiDesign)}. Use this role only for visual design direction, exploration, or review. Never use it to implement an interface, write code, or run tests.`
|
|
80
|
+
? `\n- UI Design: ${formatReference(effective.uiDesign)}; launch with model: ${formatLaunchModel(effective.uiDesign)}. Use this role only for visual design direction, exploration, or review. Never use it to implement an interface, write code, or run tests.`
|
|
55
81
|
: "";
|
|
56
82
|
|
|
57
83
|
return `<delegation_policy>
|
|
@@ -62,12 +88,12 @@ ${ROLE_SELECTION_POLICY}
|
|
|
62
88
|
|
|
63
89
|
Model preference: ${effective.preference}. ${preferenceGuidance(effective.preference)}
|
|
64
90
|
|
|
65
|
-
|
|
91
|
+
Before every delegated launch, name the selected role, take its exact combined provider/model reference below, and include it in the call as model: "provider/model" using the JSON-escaped value shown for that role. Do not omit model, inherit an ambient launcher default, substitute another model, or invent a fallback model or role. Choose thinking dynamically for each delegation from task demand, difficulty, quantity, risk, review cost, and the selected model's capabilities. Thinking is advisory and is not persisted configuration.
|
|
66
92
|
|
|
67
93
|
Roles:
|
|
68
|
-
- Small: ${formatReference(effective.small)}
|
|
69
|
-
- Medium: ${formatReference(effective.medium)}
|
|
70
|
-
- Large: ${formatReference(effective.large)}${uiDesign}
|
|
94
|
+
- Small: ${formatReference(effective.small)}; launch with model: ${formatLaunchModel(effective.small)}
|
|
95
|
+
- Medium: ${formatReference(effective.medium)}; launch with model: ${formatLaunchModel(effective.medium)}
|
|
96
|
+
- Large: ${formatReference(effective.large)}; launch with model: ${formatLaunchModel(effective.large)}${uiDesign}
|
|
71
97
|
|
|
72
98
|
This is guidance for the main agent. It does not create, execute, route, supervise, or enforce delegated work.
|
|
73
99
|
</delegation_policy>`;
|
package/src/ui.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
writeConfig,
|
|
7
7
|
} from "./config.ts";
|
|
8
8
|
import { DelegatePanel, type DelegatePanelResult } from "./delegate-panel.ts";
|
|
9
|
-
import { loadRuntime, modelCandidates, sessionEntry } from "./runtime.ts";
|
|
9
|
+
import { hasRuntimeError, loadRuntime, modelCandidates, sessionEntry } from "./runtime.ts";
|
|
10
10
|
|
|
11
11
|
export async function openDelegateEditor(ctx: ExtensionContext, pi: ExtensionAPI): Promise<void> {
|
|
12
12
|
if (!ctx.hasUI) return;
|
|
@@ -29,7 +29,8 @@ export async function openDelegateEditor(ctx: ExtensionContext, pi: ExtensionAPI
|
|
|
29
29
|
global: state.global,
|
|
30
30
|
session: state.session,
|
|
31
31
|
candidates,
|
|
32
|
-
diagnostics: state.
|
|
32
|
+
diagnostics: state.runtimeErrors,
|
|
33
|
+
hasRuntimeError: hasRuntimeError(state),
|
|
33
34
|
onApply: async (draft) => {
|
|
34
35
|
const session = structuredClone(draft);
|
|
35
36
|
sessionEntry(pi, { ...state, session });
|