@hyperdreamer/pi-webui 1.11.0-beta.5 → 1.11.0-beta.7

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.
Files changed (35) hide show
  1. package/README.md +1 -1
  2. package/dist/client/assets/{CodeViewer-BJQFgK3S.js → CodeViewer-DnVESWeo.js} +1 -1
  3. package/dist/client/assets/{UnifiedDiffViewer-DfJJrNLr.js → UnifiedDiffViewer-DtcErd2T.js} +1 -1
  4. package/dist/client/assets/{index-CsVTzPPD.js → index-BnqpOnKq.js} +584 -516
  5. package/dist/client/index.html +1 -1
  6. package/dist/server/sessiond.js +6 -1
  7. package/dist/server/sessiond.js.map +1 -1
  8. package/dist/server/sessions/modelPolicyCapability.js +88 -0
  9. package/dist/server/sessions/modelPolicyCapability.js.map +1 -0
  10. package/dist/server/sessions/modelPolicyTool.js +31 -0
  11. package/dist/server/sessions/modelPolicyTool.js.map +1 -0
  12. package/dist/server/sessions/piSessionService.js +877 -290
  13. package/dist/server/sessions/piSessionService.js.map +1 -1
  14. package/dist/server/sessions/sessionDefaultsRoutes.js +39 -0
  15. package/dist/server/sessions/sessionDefaultsRoutes.js.map +1 -1
  16. package/dist/server/sessions/sessionDefaultsService.js +36 -6
  17. package/dist/server/sessions/sessionDefaultsService.js.map +1 -1
  18. package/dist/server/sessions/starterModelPolicyPreferenceStore.js +164 -0
  19. package/dist/server/sessions/starterModelPolicyPreferenceStore.js.map +1 -0
  20. package/dist/shared/apiTypes.d.ts +23 -3
  21. package/dist/shared/apiTypes.js +1 -0
  22. package/dist/shared/apiTypes.js.map +1 -1
  23. package/dist/shared/capabilities.js +3 -0
  24. package/dist/shared/capabilities.js.map +1 -1
  25. package/docs/config.md +12 -5
  26. package/optional-skills/deterministic-subagent-driven-development/SKILL.md +15 -15
  27. package/optional-skills/deterministic-subagent-driven-development/pi-webui-skill.json +2 -2
  28. package/optional-skills/deterministic-subagent-driven-development/references/capability-contract.md +16 -16
  29. package/optional-skills/deterministic-subagent-driven-development/references/plan-contract.md +4 -4
  30. package/optional-skills/deterministic-subagent-driven-development/references/state-machine.md +3 -3
  31. package/optional-skills/deterministic-subagent-driven-development/scripts/lib/plan-policy.mjs +18 -9
  32. package/optional-skills/deterministic-subagent-driven-development/scripts/lib/prompt-renderer.mjs +3 -3
  33. package/optional-skills/deterministic-subagent-driven-development/scripts/lib/state-machine.mjs +6 -5
  34. package/optional-skills/deterministic-subagent-driven-development/scripts/sdd-state.mjs +1 -0
  35. package/package.json +7 -7
@@ -16,6 +16,12 @@ Frontier.
16
16
  A zero-parameter, read-only tool. It never mutates policy, never applies a tier,
17
17
  and never returns credentials or endpoints.
18
18
 
19
+ It advertises no tier slash commands. An earlier draft of this file listed
20
+ `/tier-economy`...`/tier-frontier` plus `/tier-up` and `/tier-down`. No such
21
+ command is registered in the runtime: tier selection is the typed `tier` field on
22
+ `spawn_subsession` and nothing else. The rendered `Model tier: <tier>` line is a
23
+ human-readable consistency label, not a command.
24
+
19
25
  ```ts
20
26
  type ModelTier = "economy" | "fast" | "standard" | "advanced" | "capable" | "frontier";
21
27
 
@@ -35,13 +41,6 @@ interface GetModelPolicyV1 {
35
41
  blockedReason: string | null;
36
42
  };
37
43
  ladder: { valid: boolean; revision: string | null; blockedReason: string | null };
38
- tierCommands: {
39
- contractVersion: 1;
40
- absolute: readonly ["/tier-economy", "/tier-fast", "/tier-standard", "/tier-advanced", "/tier-capable", "/tier-frontier"];
41
- relative: readonly ["/tier-up", "/tier-down"];
42
- leadingOnly: true;
43
- exactOutcome: "ignored-exact";
44
- };
45
44
  trackedDispatch: {
46
45
  contractVersion: 1;
47
46
  tierField: true;
@@ -59,10 +58,11 @@ thinkingLevel }` and carry model identity and supported thinking only.
59
58
 
60
59
  | Condition | Requirement |
61
60
  | --- | --- |
62
- | Exact mode | `currentTier` is `null`; an invalid ladder is permitted; `currentRuntime` and `nextRequestResolved` are both non-null and equal |
61
+ | Exact mode | `currentTier` is `null`; `currentRuntime` and `nextRequestResolved` are both non-null and equal |
63
62
  | Valid tiered mode | `currentTier` is non-null; ladder is complete and valid; latest resolved tuple is non-null |
64
63
  | Invalid tiered mode | `nextRequestResolved` may be `null` only when `ladder.blockedReason` is a non-empty actionable reason; this state is capability-blocking |
65
64
  | Any policy blocked reason | Capability-blocking regardless of mode |
65
+ | Invalid ladder, either mode | Capability-blocking. Children are dispatched *by tier* whatever the parent's mode, and reviewer/fixer tiers are derived by formula, so any unresolvable rung can fail a later round |
66
66
 
67
67
  A capability-blocking state yields `CAPABILITY_BLOCKED` before any worktree
68
68
  mutation, plan mutation, or dispatch.
@@ -102,14 +102,14 @@ without creating a child and without substituting a neighbouring tier. An omitte
102
102
  `tier` inherits the parent's model.
103
103
 
104
104
  Prompt text never selects a model. The runtime does not scan prompt bytes for
105
- slash directives, so a `/tier-*` line is a human-readable echo with zero control
106
- effect. A test fake that recovers a tier by splitting the prompt is exercising a
107
- channel the runtime does not implement, and cannot detect a child that ignored
108
- the directive.
105
+ model-selection commands, so a `Model tier: <tier>` line is a human-readable echo
106
+ with zero control effect. A test fake that recovers a tier from this label is
107
+ exercising a channel the runtime does not implement, and cannot detect a child
108
+ that ignored the typed field.
109
109
 
110
- The one exception is a guard, not a mechanism: a leading `/tier-*` line that
111
- *disagrees* with the typed `tier` is rejected before child creation, so a stale
112
- echoed directive cannot silently imply a tier that was not requested.
110
+ The one exception is a guard, not a mechanism: a leading label that *disagrees*
111
+ with the typed `tier` is rejected before child creation, so a stale rendered
112
+ prompt cannot silently imply a tier that was not requested.
113
113
 
114
114
  ### No dispatch idempotency
115
115
 
@@ -129,7 +129,7 @@ rely on is therefore **detectable** non-idempotency, not prevented duplication:
129
129
  ### Fail-closed conditions
130
130
 
131
131
  Missing required fields, unknown tier values, a tier absent from the configured
132
- ladder, an unavailable model, and a leading directive disagreeing with the typed
132
+ ladder, an unavailable model, and a leading tier label disagreeing with the typed
133
133
  tier all fail before a child is created.
134
134
 
135
135
  ## Recovery-input properties
@@ -100,10 +100,10 @@ re-reviewer formula takes no round. That is pinned in tests as a deliberate
100
100
  decision; changing it requires changing this contract.
101
101
 
102
102
  `tier` is the binding channel. The rendered prompt also opens with
103
- `/tier-<lowercase>` as a human-readable echo. The echo carries no control effect —
104
- the runtime never parses prompt text to select a model — but PI WEBUI does reject
105
- a leading directive that *disagrees* with the typed tier, so the echo works as a
106
- cross-check. An absent echo is not an error; a disagreeing one is.
103
+ `Model tier: <lowercase>` as a human-readable echo. The echo carries no control
104
+ effect — the runtime never parses prompt text to select a model — but PI WEBUI
105
+ rejects a leading label that *disagrees* with the typed tier, so the echo works as
106
+ a cross-check. An absent echo is not an error; a disagreeing one is.
107
107
 
108
108
  ## Identity pinning
109
109
 
@@ -157,9 +157,9 @@ An intent stores the exact rendered prompt bytes, bounded at 384 KiB, before any
157
157
  session exists. Recovery reissues those bytes verbatim and never re-renders,
158
158
  because re-rendering couples recovery to renderer output.
159
159
 
160
- The typed `tier` selects the model. A leading `/tier-*` line is a human-readable
161
- echo with no control effect: absent is fine, and disagreeing with the typed tier
162
- is reported as renderer/formula divergence.
160
+ The typed `tier` selects the model. A leading `Model tier: <tier>` line is a
161
+ human-readable echo with no control effect: absent is fine, and disagreement with
162
+ the typed tier is reported as renderer/formula divergence.
163
163
 
164
164
  ## Recovery authority
165
165
 
@@ -100,15 +100,21 @@ export function finalReviewerTier() {
100
100
  }
101
101
 
102
102
  /**
103
- * The human-readable echo for a tier.
103
+ * The human-readable tier echo.
104
104
  *
105
105
  * This is display text, not a control channel. `spawn_subsession` selects a
106
- * model from its typed `tier` parameter; a `/tier-*` line in a rendered prompt
107
- * has no effect on which model runs. It exists so a human reading a transcript
108
- * can see the intended tier, and so renderer/formula divergence is detectable.
106
+ * model from its typed `tier` parameter; a `Model tier: <tier>` line in a
107
+ * rendered prompt has no effect on which model runs. It exists so a human
108
+ * reading a transcript can see the intended tier, and so renderer/formula
109
+ * divergence is detectable.
109
110
  */
111
+ export function tierEcho(tier) {
112
+ return `Model tier: ${TIERS[tierIndex(tier)]}`;
113
+ }
114
+
115
+ /** @deprecated Use `tierEcho`; retained for older controller instructions. */
110
116
  export function tierDirective(tier) {
111
- return `/tier-${TIERS[tierIndex(tier)]}`;
117
+ return tierEcho(tier);
112
118
  }
113
119
 
114
120
  const ROLES = Object.freeze({
@@ -120,10 +126,12 @@ const ROLES = Object.freeze({
120
126
  });
121
127
 
122
128
  /**
123
- * Resolve one role's tier and its directive.
129
+ * Resolve one role's tier and its display echo.
124
130
  *
125
- * The round argument is required for the fixer role and rejected for every other
126
- * role, so a caller cannot silently pass a round that has no effect.
131
+ * The round argument is required for the fixer role and rejected for every
132
+ * other role, so a caller cannot silently pass a round that has no effect.
133
+ * `directive` is a compatibility alias for controllers written before the
134
+ * display line stopped looking like a slash command.
127
135
  */
128
136
  export function roleTier({ implementer, role, round }) {
129
137
  const definition = ROLES[role];
@@ -139,7 +147,8 @@ export function roleTier({ implementer, role, round }) {
139
147
  const tier = definition.needsRound
140
148
  ? definition.resolve(implementer, round)
141
149
  : definition.resolve(implementer);
142
- return { tier, directive: tierDirective(tier) };
150
+ const echo = tierEcho(tier);
151
+ return { tier, echo, directive: echo };
143
152
  }
144
153
 
145
154
  class PlanError extends Error {
@@ -7,7 +7,7 @@
7
7
  * template evaluation.
8
8
  *
9
9
  * There is deliberately no general-purpose templating here. A template engine
10
- * would let a context value introduce a directive, a heading, or another
10
+ * would let a context value introduce a tier label, a heading, or another
11
11
  * placeholder, and the prompt is the one artifact a child treats as instructions.
12
12
  * Role contracts are static files copied verbatim; context is emitted as a
13
13
  * validated, escaped key/value list.
@@ -242,7 +242,7 @@ const validateContext = (role, context) => {
242
242
  *
243
243
  * Byte layout, fixed by contract because dispatch stores and reissues these bytes:
244
244
  *
245
- * /tier-<tier>\n
245
+ * Model tier: <tier>\n
246
246
  * \n
247
247
  * <role contract, trailing whitespace trimmed>\n
248
248
  * \n
@@ -263,7 +263,7 @@ export function renderPrompt({ tier, role, context, skillRoot }) {
263
263
  const template = readFileSync(join(skillRoot, "prompts", definition.template), "utf8");
264
264
 
265
265
  const rendered = [
266
- `/tier-${tier}`,
266
+ `Model tier: ${tier}`,
267
267
  "",
268
268
  template.trimEnd(),
269
269
  "",
@@ -494,13 +494,14 @@ const buildIntent = (state, event, intentPhase) => {
494
494
  fail(`renderedPrompt exceeds ${String(MAX_PROMPT_BYTES)} bytes (384 KiB)`);
495
495
  }
496
496
 
497
- // A leading `/tier-*` line is a human-readable echo with no control effect.
498
- // Its absence is fine; a disagreement with the typed tier means the renderer
499
- // and the contract have diverged and must not be papered over.
500
- const echo = /^\/tier-([a-z]+)[ \t]*(?:\r?\n|$)/u.exec(event.renderedPrompt);
497
+ // A leading `Model tier: <tier>` line is a human-readable echo with no
498
+ // control effect. Its absence is fine; a disagreement with the typed tier
499
+ // means the renderer and the contract have diverged and must not be papered
500
+ // over.
501
+ const echo = /^Model tier: ([a-z]+)[ \t]*(?:\r?\n|$)/u.exec(event.renderedPrompt);
501
502
  if (echo !== null && echo[1] !== event.tier) {
502
503
  fail(
503
- `renderer/formula divergence: prompt echoes /tier-${echo[1]} while the typed tier is ${event.tier}`,
504
+ `renderer/formula divergence: prompt says Model tier: ${echo[1]} while the typed tier is ${event.tier}`,
504
505
  );
505
506
  }
506
507
 
@@ -75,6 +75,7 @@ export {
75
75
  reviewerTier,
76
76
  roleTier,
77
77
  tierDirective,
78
+ tierEcho,
78
79
  tierLabel,
79
80
  TIERS,
80
81
  } from "./lib/plan-policy.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperdreamer/pi-webui",
3
- "version": "1.11.0-beta.5",
3
+ "version": "1.11.0-beta.7",
4
4
  "description": "Web UI for persistent Pi Coding Agent sessions in real workspaces.",
5
5
  "license": "MIT",
6
6
  "author": "Federico Jaramillo Martinez and HyperDreamer",
@@ -84,9 +84,9 @@
84
84
  },
85
85
  "devDependencies": {
86
86
  "@changesets/cli": "^2.31.0",
87
- "@earendil-works/pi-agent-core": "^0.82.1",
88
- "@earendil-works/pi-ai": "^0.82.1",
89
- "@earendil-works/pi-coding-agent": "^0.82.1",
87
+ "@earendil-works/pi-agent-core": "^0.83.0",
88
+ "@earendil-works/pi-ai": "^0.83.0",
89
+ "@earendil-works/pi-coding-agent": "^0.83.0",
90
90
  "@eslint/js": "^10.0.1",
91
91
  "@types/node": "^24.13.3",
92
92
  "@types/ws": "^8.18.1",
@@ -116,9 +116,9 @@
116
116
  "homepage": "https://pi-webui.dev/",
117
117
  "packageManager": "npm@11.11.0",
118
118
  "peerDependencies": {
119
- "@earendil-works/pi-agent-core": ">=0.82.1 <0.83",
120
- "@earendil-works/pi-ai": ">=0.82.1 <0.83",
121
- "@earendil-works/pi-coding-agent": ">=0.82.1 <0.83"
119
+ "@earendil-works/pi-agent-core": ">=0.83.0 <0.84",
120
+ "@earendil-works/pi-ai": ">=0.83.0 <0.84",
121
+ "@earendil-works/pi-coding-agent": ">=0.83.0 <0.84"
122
122
  },
123
123
  "keywords": [
124
124
  "pi-package",