@crouton-kit/crouter 0.3.37 → 0.3.39

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 (143) hide show
  1. package/dist/build-root.js +1 -0
  2. package/dist/{builtin-personas/runtime-base.md → builtin-memory/00-runtime-base.md} +7 -0
  3. package/dist/{builtin-personas/spine/has-manager.md → builtin-memory/01-spine/00-has-manager.md} +8 -0
  4. package/dist/builtin-memory/01-spine/01-no-manager.md +10 -0
  5. package/dist/{builtin-personas/lifecycle/terminal.md → builtin-memory/02-lifecycle/00-terminal.md} +8 -0
  6. package/dist/{builtin-personas/lifecycle/resident.md → builtin-memory/02-lifecycle/01-resident.md} +8 -0
  7. package/dist/{builtin-personas/waiting.md → builtin-memory/03-waiting.md} +7 -0
  8. package/dist/{builtin-personas/orchestration-kernel.md → builtin-memory/04-orchestration-kernel.md} +8 -0
  9. package/dist/{builtin-personas/advisor/PERSONA.md → builtin-memory/05-kinds/advisor/00-base.md} +5 -2
  10. package/dist/{builtin-personas/design/PERSONA.md → builtin-memory/05-kinds/design/00-base.md} +5 -2
  11. package/dist/{builtin-personas/design/orchestrator.md → builtin-memory/05-kinds/design/01-orchestrator.md} +5 -4
  12. package/dist/{builtin-personas/developer/PERSONA.md → builtin-memory/05-kinds/developer/00-base.md} +5 -2
  13. package/dist/{builtin-personas/developer/orchestrator.md → builtin-memory/05-kinds/developer/01-orchestrator.md} +5 -4
  14. package/dist/{builtin-personas/explore/PERSONA.md → builtin-memory/05-kinds/explore/00-base.md} +5 -2
  15. package/dist/{builtin-personas/explore/orchestrator.md → builtin-memory/05-kinds/explore/01-orchestrator.md} +5 -3
  16. package/dist/{builtin-personas/general/PERSONA.md → builtin-memory/05-kinds/general/00-base.md} +5 -2
  17. package/dist/{builtin-personas/general/orchestrator.md → builtin-memory/05-kinds/general/01-orchestrator.md} +5 -3
  18. package/dist/{builtin-personas/plan/PERSONA.md → builtin-memory/05-kinds/plan/00-base.md} +5 -2
  19. package/dist/{builtin-personas/plan/orchestrator.md → builtin-memory/05-kinds/plan/01-orchestrator.md} +5 -4
  20. package/dist/{builtin-personas/plan/reviewers/architecture-fit/PERSONA.md → builtin-memory/05-kinds/plan/reviewers/architecture-fit.md} +5 -2
  21. package/dist/{builtin-personas/plan/reviewers/code-smells/PERSONA.md → builtin-memory/05-kinds/plan/reviewers/code-smells.md} +5 -2
  22. package/dist/{builtin-personas/plan/reviewers/pattern-consistency/PERSONA.md → builtin-memory/05-kinds/plan/reviewers/pattern-consistency.md} +5 -2
  23. package/dist/{builtin-personas/plan/reviewers/requirements-coverage/PERSONA.md → builtin-memory/05-kinds/plan/reviewers/requirements-coverage.md} +5 -2
  24. package/dist/{builtin-personas/plan/reviewers/security/PERSONA.md → builtin-memory/05-kinds/plan/reviewers/security.md} +5 -2
  25. package/dist/{builtin-personas/product/PERSONA.md → builtin-memory/05-kinds/product/00-base.md} +5 -2
  26. package/dist/{builtin-personas/product/orchestrator.md → builtin-memory/05-kinds/product/01-orchestrator.md} +5 -4
  27. package/dist/{builtin-personas/product/teardown/PERSONA.md → builtin-memory/05-kinds/product/teardown.md} +5 -3
  28. package/dist/{builtin-personas/review/PERSONA.md → builtin-memory/05-kinds/review/00-base.md} +5 -2
  29. package/dist/{builtin-personas/review/orchestrator.md → builtin-memory/05-kinds/review/01-orchestrator.md} +5 -3
  30. package/dist/{builtin-personas/spec/PERSONA.md → builtin-memory/05-kinds/spec/00-base.md} +5 -2
  31. package/dist/{builtin-personas/spec/orchestrator.md → builtin-memory/05-kinds/spec/01-orchestrator.md} +5 -4
  32. package/dist/{builtin-personas/spec/requirements/PERSONA.md → builtin-memory/05-kinds/spec/requirements.md} +5 -2
  33. package/dist/builtin-memory/internal/INDEX.md +1 -1
  34. package/dist/builtin-memory/internal/examples/imessage-assistant.md +2 -2
  35. package/dist/builtin-views/prompt-review/core.mjs +6 -134
  36. package/dist/builtin-views/settings/core.mjs +26 -94
  37. package/dist/builtin-views/settings/text.mjs +0 -5
  38. package/dist/builtin-views/settings/tui.mjs +1 -7
  39. package/dist/builtin-views/settings/web.jsx +3 -49
  40. package/dist/clients/attach/attach-cmd.js +416 -416
  41. package/dist/commands/memory/read.js +3 -3
  42. package/dist/commands/memory/shared.d.ts +13 -8
  43. package/dist/commands/memory/shared.js +22 -8
  44. package/dist/commands/memory/write.js +5 -3
  45. package/dist/commands/node.js +40 -11
  46. package/dist/commands/profile/add-project.d.ts +1 -0
  47. package/dist/commands/profile/add-project.js +42 -0
  48. package/dist/commands/profile/delete.d.ts +1 -0
  49. package/dist/commands/profile/delete.js +39 -0
  50. package/dist/commands/profile/list.d.ts +1 -0
  51. package/dist/commands/profile/list.js +35 -0
  52. package/dist/commands/profile/new.d.ts +1 -0
  53. package/dist/commands/profile/new.js +50 -0
  54. package/dist/commands/profile/remove-project.d.ts +1 -0
  55. package/dist/commands/profile/remove-project.js +42 -0
  56. package/dist/commands/profile/rename.d.ts +1 -0
  57. package/dist/commands/profile/rename.js +42 -0
  58. package/dist/commands/profile/show.d.ts +1 -0
  59. package/dist/commands/profile/show.js +50 -0
  60. package/dist/commands/profile.d.ts +2 -0
  61. package/dist/commands/profile.js +33 -0
  62. package/dist/commands/sys/config.js +6 -21
  63. package/dist/commands/sys/prompt-review.js +90 -17
  64. package/dist/core/canvas/canvas.js +10 -9
  65. package/dist/core/canvas/db.js +11 -0
  66. package/dist/core/canvas/history.js +1 -0
  67. package/dist/core/canvas/types.d.ts +10 -0
  68. package/dist/core/config.d.ts +44 -1
  69. package/dist/core/config.js +155 -12
  70. package/dist/core/hearth/provider.d.ts +8 -0
  71. package/dist/core/hearth/providers/__tests__/sweep-and-release.test.d.ts +1 -0
  72. package/dist/core/hearth/providers/__tests__/sweep-and-release.test.js +254 -0
  73. package/dist/core/hearth/providers/blaxel-home.d.ts +39 -0
  74. package/dist/core/hearth/providers/blaxel-home.js +271 -21
  75. package/dist/core/hearth/providers/blaxel.d.ts +5 -0
  76. package/dist/core/hearth/providers/blaxel.js +86 -4
  77. package/dist/core/hearth/providers/types.d.ts +16 -0
  78. package/dist/core/memory-resolver.d.ts +26 -15
  79. package/dist/core/memory-resolver.js +158 -40
  80. package/dist/core/profiles/manifest.d.ts +33 -0
  81. package/dist/core/profiles/manifest.js +332 -0
  82. package/dist/core/profiles/select.d.ts +11 -0
  83. package/dist/core/profiles/select.js +115 -0
  84. package/dist/core/runtime/bearings.d.ts +9 -0
  85. package/dist/core/runtime/bearings.js +31 -0
  86. package/dist/core/runtime/front-door.js +9 -2
  87. package/dist/core/runtime/launch.d.ts +25 -7
  88. package/dist/core/runtime/launch.js +42 -55
  89. package/dist/core/runtime/nodes.d.ts +7 -0
  90. package/dist/core/runtime/nodes.js +6 -0
  91. package/dist/core/runtime/persona.js +18 -32
  92. package/dist/core/runtime/spawn.d.ts +13 -0
  93. package/dist/core/runtime/spawn.js +25 -0
  94. package/dist/core/scope.d.ts +14 -7
  95. package/dist/core/scope.js +76 -20
  96. package/dist/core/substrate/index.d.ts +3 -3
  97. package/dist/core/substrate/index.js +5 -3
  98. package/dist/core/substrate/on-read.js +3 -2
  99. package/dist/core/substrate/render.d.ts +16 -11
  100. package/dist/core/substrate/render.js +249 -62
  101. package/dist/core/substrate/schema.d.ts +11 -3
  102. package/dist/core/substrate/schema.js +25 -0
  103. package/dist/core/substrate/subject.d.ts +15 -2
  104. package/dist/core/substrate/subject.js +24 -2
  105. package/dist/hearth/control-plane/__tests__/error-serialization.test.d.ts +1 -0
  106. package/dist/hearth/control-plane/__tests__/error-serialization.test.js +29 -0
  107. package/dist/hearth/control-plane/__tests__/oauth-serving-marker.test.d.ts +1 -0
  108. package/dist/hearth/control-plane/__tests__/oauth-serving-marker.test.js +44 -0
  109. package/dist/hearth/control-plane/__tests__/wake-roll.test.d.ts +1 -0
  110. package/dist/hearth/control-plane/__tests__/wake-roll.test.js +230 -0
  111. package/dist/hearth/control-plane/db.js +27 -0
  112. package/dist/hearth/control-plane/hearth-target.d.ts +23 -0
  113. package/dist/hearth/control-plane/hearth-target.js +68 -0
  114. package/dist/hearth/control-plane/registry.d.ts +6 -1
  115. package/dist/hearth/control-plane/registry.js +7 -0
  116. package/dist/hearth/control-plane/relay.d.ts +4 -0
  117. package/dist/hearth/control-plane/relay.js +72 -3
  118. package/dist/hearth/control-plane/secrets.d.ts +14 -0
  119. package/dist/hearth/control-plane/secrets.js +21 -0
  120. package/dist/hearth/control-plane/server.js +140 -4
  121. package/dist/hearth/control-plane/serving.d.ts +15 -0
  122. package/dist/hearth/control-plane/serving.js +106 -0
  123. package/dist/hearth/control-plane/types.d.ts +20 -0
  124. package/dist/hearth/control-plane/wake.d.ts +8 -2
  125. package/dist/hearth/control-plane/wake.js +241 -3
  126. package/dist/pi-extensions/canvas-doc-substrate.js +2 -3
  127. package/dist/types.d.ts +57 -1
  128. package/dist/types.js +83 -1
  129. package/dist/web-client/assets/{index-MSKSWwV0.js → index-CbO8L0mN.js} +4 -4
  130. package/dist/web-client/assets/index-DwO46Cs5.css +2 -0
  131. package/dist/web-client/index.html +2 -2
  132. package/package.json +2 -2
  133. package/dist/builtin-memory/crouter-development/personas/base-prompt.md +0 -56
  134. package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +0 -57
  135. package/dist/builtin-memory/crouter-development/personas.md +0 -112
  136. package/dist/builtin-personas/spine/no-manager.md +0 -2
  137. package/dist/core/personas/index.d.ts +0 -13
  138. package/dist/core/personas/index.js +0 -11
  139. package/dist/core/personas/loader.d.ts +0 -159
  140. package/dist/core/personas/loader.js +0 -327
  141. package/dist/core/personas/resolve.d.ts +0 -59
  142. package/dist/core/personas/resolve.js +0 -376
  143. package/dist/web-client/assets/index-DUThOUzU.css +0 -2
@@ -85,6 +85,8 @@ function execRequest(bin, args, stdin) {
85
85
 
86
86
  /** @typedef {{kind:string, whenToUse:string}} KindInfo */
87
87
 
88
+ /** @typedef {{config:PromptReviewConfig, layers:PromptLayer[], assembled:string, total:{tokens:number, chars:number}, sources:PromptSource[]}} ReviewPayload */
89
+
88
90
  /**
89
91
  * @typedef {Object} PromptReviewList
90
92
  * @property {KindInfo[]} kinds
@@ -155,19 +157,6 @@ function normalizeConfig(config) {
155
157
  };
156
158
  }
157
159
 
158
- /** @param {PromptLayer[]} layers @returns {PromptSource[]} */
159
- function dedupeSources(layers) {
160
- /** @type {PromptSource[]} */
161
- const out = [];
162
- const seen = new Set();
163
- for (const layer of layers) {
164
- if (!layer.sourcePath || seen.has(layer.sourcePath)) continue;
165
- seen.add(layer.sourcePath);
166
- out.push({ id: layer.id, label: layer.label, path: layer.sourcePath, scope: layer.scope });
167
- }
168
- return out;
169
- }
170
-
171
160
  /** @param {string} s @returns {string} */
172
161
  function normalizeText(s) {
173
162
  return str(s).replace(/\r\n/g, '\n');
@@ -184,12 +173,6 @@ function chars(text) { return normalizeText(text).length; }
184
173
  /** @param {string} text @returns {number} */
185
174
  function tokens(text) { return approxTokens(chars(text)); }
186
175
 
187
- /** @param {string} id @param {string} label @param {LayerGroup} group @param {string} source @param {string|null} sourcePath @param {LayerScope} scope @param {boolean} included @param {string|null} condition @param {string} text @returns {PromptLayer} */
188
- function makeLayer(id, label, group, source, sourcePath, scope, included, condition, text) {
189
- const norm = normalizeText(text);
190
- return { id, label, group, source, sourcePath, scope, included, condition, text: norm, tokens: tokens(norm), chars: chars(norm) };
191
- }
192
-
193
176
  /** @param {string} kind @returns {string} */
194
177
  function kindLabel(kind) { return kind || DEFAULT_KIND; }
195
178
 
@@ -217,112 +200,9 @@ function layerLabel(id) {
217
200
  return map[id] || id.replace(/[-_]/g, ' ');
218
201
  }
219
202
 
220
- /** @param {PromptReviewConfig} config @returns {PromptLayer[]} */
221
- function buildReviewLayers(config) {
222
- const c = normalizeConfig(config);
223
- const base = 'System prompt composition and agent runtime rules.';
224
- const spineHas = c.hasManager;
225
- const spineNo = !c.hasManager;
226
- const lifecycleTerminal = c.lifecycle === 'terminal';
227
- const lifecycleResident = !lifecycleTerminal;
228
- const baseText = joinPieces([
229
- '# Runtime base',
230
- base,
231
- ]);
232
- const spineHasText = joinPieces([
233
- '# Spine with manager',
234
- 'You have a manager. Report upward and keep the spine active.',
235
- ]);
236
- const spineNoText = joinPieces([
237
- '# Spine without manager',
238
- 'You have no manager. Work independently and do not assume an upstream reviewer.',
239
- ]);
240
- const lifecycleTerminalText = joinPieces([
241
- '# Terminal lifecycle',
242
- 'This node must finish with a final report when its work is done.',
243
- ]);
244
- const lifecycleResidentText = joinPieces([
245
- '# Resident lifecycle',
246
- 'This node stays resident and waits for inbound events instead of always owing a final.',
247
- ]);
248
- const waitingText = joinPieces([
249
- '# Waiting',
250
- 'When you are blocked on a future event, stop and wait instead of busy-looping.',
251
- ]);
252
- const personaBody = joinPieces([
253
- `# ${kindLabel(c.kind)} persona`,
254
- `Kind-specific prompt body for ${c.kind}.`,
255
- ]);
256
- const orchestrationKernel = joinPieces([
257
- '# Orchestration kernel',
258
- 'Kernel guidance for orchestrator mode.',
259
- ]);
260
-
261
- /** @type {PromptLayer[]} */
262
- const layers = [
263
- makeLayer('runtime-base', 'Runtime base', 'protocol', 'personas/runtime-base.md', '/abs/src/builtin-personas/runtime-base.md', 'builtin', true, null, baseText),
264
- makeLayer('spine-has-manager', 'Spine (has manager)', 'protocol', 'personas/spine/has-manager.md', '/abs/src/builtin-personas/spine/has-manager.md', 'builtin', spineHas, spineHas ? null : 'hasManager == false', spineHasText),
265
- makeLayer('spine-no-manager', 'Spine (no manager)', 'protocol', 'personas/spine/no-manager.md', '/abs/src/builtin-personas/spine/no-manager.md', 'builtin', spineNo, spineNo ? null : 'hasManager == true', spineNoText),
266
- makeLayer('lifecycle-terminal', 'Lifecycle (terminal)', 'protocol', 'personas/lifecycle/terminal.md', '/abs/src/builtin-personas/lifecycle/terminal.md', 'builtin', lifecycleTerminal, lifecycleTerminal ? null : 'lifecycle == resident', lifecycleTerminalText),
267
- makeLayer('lifecycle-resident', 'Lifecycle (resident)', 'protocol', 'personas/lifecycle/resident.md', '/abs/src/builtin-personas/lifecycle/resident.md', 'builtin', lifecycleResident, lifecycleResident ? null : 'lifecycle == terminal', lifecycleResidentText),
268
- makeLayer('waiting', 'Waiting', 'protocol', 'personas/waiting.md', '/abs/src/builtin-personas/waiting.md', 'builtin', true, null, waitingText),
269
- makeLayer('persona-body', 'Persona body', 'persona', c.mode === 'base' ? `personas/${c.kind}/PERSONA.md` : `personas/${c.kind}/orchestrator.md`, c.mode === 'base' ? `/abs/src/builtin-personas/${c.kind}/PERSONA.md` : `/abs/src/builtin-personas/${c.kind}/orchestrator.md`, 'builtin', true, null, c.mode === 'base' ? personaBody : joinPieces([personaBody, orchestrationKernel])),
270
- makeLayer('subpersona-menu', 'Sub-persona menu', 'generated', `generated:${c.kind}:sub-personas`, null, 'generated', true, null, joinPieces([
271
- `Generated sub-persona menu for ${c.kind}.`,
272
- 'This layer is synthesized from the available sub-personas and their source files.',
273
- ])),
274
- ];
275
-
276
- if (c.node) {
277
- layers.push(
278
- makeLayer('substrate-preferences', 'Preferences substrate', 'substrate', 'runtime:preferences', null, 'runtime', true, null, joinPieces([
279
- '# Preferences substrate',
280
- 'Node-scoped preferences are spliced into the system prompt here.',
281
- ])),
282
- makeLayer('substrate-memory-guidance', 'Memory guidance substrate', 'substrate', 'runtime:memory-guidance', null, 'runtime', true, null, joinPieces([
283
- '# Memory guidance',
284
- 'Runtime-spliced memory guidance shown here because a node was supplied.',
285
- ])),
286
- makeLayer('substrate-knowledge', 'Context message — not system prompt', 'substrate', 'runtime:knowledge', null, 'runtime', true, null, joinPieces([
287
- '# Context message',
288
- 'This node-specific knowledge block is not part of the system prompt.',
289
- ])),
290
- );
291
- }
292
-
293
- return layers;
294
- }
295
-
296
- /** @param {PromptReviewConfig} config @returns {string} */
297
- function composeAssembled(config) {
298
- const c = normalizeConfig(config);
299
- const layers = buildReviewLayers(c);
300
- const protocol = layers.filter((layer) => layer.group !== 'substrate');
301
- const substrate = layers.filter((layer) => layer.group === 'substrate');
302
- const protocolText = joinPieces(protocol.filter((layer) => layer.included).map((layer) => layer.text));
303
- if (c.node && substrate.length) {
304
- return joinPieces([protocolText, ...substrate.map((layer) => layer.text)]);
305
- }
306
- return protocolText;
307
- }
308
-
309
- /** @param {PromptReviewConfig} config @param {PromptLayer[]} layers @returns {PromptLayer[]} */
310
- function maybeAddNodeLayers(config, layers) {
311
- if (!config.node) return layers;
312
- return layers.concat(buildReviewLayers(config).filter((layer) => layer.group === 'substrate'));
313
- }
314
-
315
203
  /** @param {PromptLayer} layer @returns {string} */
316
204
  function sourceForLayer(layer) { return layer.sourcePath || layer.source; }
317
205
 
318
- /** @param {PromptReviewConfig} config @param {PromptLayer[]} layers @returns {{config:PromptReviewConfig, layers:PromptLayer[], assembled:string, total:{tokens:number, chars:number}, sources:PromptSource[]}} */
319
- function reviewPayload(config, layers) {
320
- const assembled = composeAssembled(config);
321
- const totalChars = chars(assembled);
322
- const totalTokens = tokens(assembled);
323
- return { config: normalizeConfig(config), layers, assembled, total: { tokens: totalTokens, chars: totalChars }, sources: dedupeSources(layers) };
324
- }
325
-
326
206
  /** @param {unknown} value @returns {value is PromptReviewConfig} */
327
207
  function looksLikeConfig(value) {
328
208
  return !!value && typeof value === 'object' && 'kind' in /** @type {any} */ (value) && 'mode' in /** @type {any} */ (value) && 'lifecycle' in /** @type {any} */ (value);
@@ -388,11 +268,6 @@ function diffReviews(left, right) {
388
268
  return out;
389
269
  }
390
270
 
391
- /** @param {PromptReviewConfig} config @param {PromptLayer[]} layers @returns {{config:PromptReviewConfig, layers:PromptLayer[], assembled:string, total:{tokens:number, chars:number}, sources:PromptSource[]}} */
392
- function makeReview(config, layers) {
393
- return reviewPayload(config, layers);
394
- }
395
-
396
271
  /** @type {import('../../core/view/contract.js').Source<PromptReviewList>} */
397
272
  export const listSource = {
398
273
  id: 'prompt-review-list',
@@ -414,7 +289,7 @@ export const listSource = {
414
289
  },
415
290
  };
416
291
 
417
- /** @type {import('../../core/view/contract.js').Source<ReturnType<typeof makeReview>, PromptReviewConfig>} */
292
+ /** @type {import('../../core/view/contract.js').Source<ReviewPayload, PromptReviewConfig>} */
418
293
  export const reviewSource = {
419
294
  id: 'prompt-review-review',
420
295
  request: (config) => {
@@ -464,7 +339,7 @@ export const reviewSource = {
464
339
  tokens: typeof data.total.tokens === 'number' ? data.total.tokens : approxTokens(chars(assembled)),
465
340
  chars: typeof data.total.chars === 'number' ? data.total.chars : chars(assembled),
466
341
  };
467
- return ok(/** @type {ReturnType<typeof makeReview>} */ ({ config, layers, assembled, total, sources }));
342
+ return ok(/** @type {ReviewPayload} */ ({ config, layers, assembled, total, sources }));
468
343
  },
469
344
  };
470
345
 
@@ -507,7 +382,7 @@ export const exportCommand = {
507
382
  },
508
383
  };
509
384
 
510
- /** @param {PromptReviewConfig} config @param {PromptReviewConfig|null} compareConfig @returns {Promise<{review: ReturnType<typeof makeReview>, compareReview: ReturnType<typeof makeReview> | null, list: PromptReviewList | null, liveNodes: Array<{node_id:string, name:string, kind:string, mode:string, lifecycle:string, status:string, cwd:string, parent:string|null, created:string, enterable:boolean}> | null}>} */
385
+ /** @param {PromptReviewConfig} config @param {PromptReviewConfig|null} compareConfig @returns {Promise<{review: ReviewPayload, compareReview: ReviewPayload | null, list: PromptReviewList | null, liveNodes: Array<{node_id:string, name:string, kind:string, mode:string, lifecycle:string, status:string, cwd:string, parent:string|null, created:string, enterable:boolean}> | null}>} */
511
386
  async function loadAll(ctx, config, compareConfig) {
512
387
  const tasks = [ctx.resolve(listSource), ctx.resolve(reviewSource, config), ctx.resolve(nodeListSource)];
513
388
  if (compareConfig) tasks.push(ctx.resolve(reviewSource, compareConfig));
@@ -546,7 +421,7 @@ function cursorFor(review, id) {
546
421
  return idx >= 0 ? idx : Math.max(0, review.findIndex((layer) => layer.included) >= 0 ? review.findIndex((layer) => layer.included) : 0);
547
422
  }
548
423
 
549
- /** @param {ReviewState} state @param {PromptReviewConfig} config @param {PromptReviewConfig|null} compareConfig @param {ReturnType<typeof makeReview>|null} reviewPayloadObj @param {ReturnType<typeof makeReview>|null} comparePayloadObj @returns {ReviewState} */
424
+ /** @param {ReviewState} state @param {PromptReviewConfig} config @param {PromptReviewConfig|null} compareConfig @param {ReviewPayload|null} reviewPayloadObj @param {ReviewPayload|null} comparePayloadObj @returns {ReviewState} */
550
425
  function materialize(state, config, compareConfig, reviewPayloadObj, comparePayloadObj) {
551
426
  const review = reviewPayloadObj ? reviewPayloadObj.layers : null;
552
427
  const compareReview = comparePayloadObj ? comparePayloadObj.layers : null;
@@ -852,11 +727,8 @@ export {
852
727
  diffReviews,
853
728
  layerLabel,
854
729
  makeDefaultCompare,
855
- reviewPayload,
856
730
  selectedLayer,
857
731
  compareSelectedLayer,
858
- composeAssembled,
859
- buildReviewLayers,
860
732
  kindLabel,
861
733
  };
862
734
 
@@ -83,21 +83,19 @@ function makeConfigSetCommand(key, value) {
83
83
  };
84
84
  }
85
85
 
86
- /** @typedef {{ kind:'defaultProvider' } | { kind:'matrix'; strength:ModelStrength } | { kind:'persona'; persona:string } | { kind:'addPersona' }} Row */
86
+ /** @typedef {{ kind:'defaultProvider' } | { kind:'matrix'; strength:ModelStrength }} Row */
87
87
 
88
88
  /** @typedef {Object} EditState
89
- * @property {'defaultProvider'|'matrixCell'|'personaStrength'|'personaNew'} kind
89
+ * @property {'defaultProvider'|'matrixCell'} kind
90
90
  * @property {string} draft
91
91
  * @property {string} value
92
92
  * @property {string} label
93
93
  * @property {ModelStrength} [strength]
94
94
  * @property {ModelProvider} [provider]
95
- * @property {string} [persona]
96
95
  */
97
96
 
98
97
  /** @typedef {Object} SettingsState
99
98
  * @property {ModelLaddersConfig|null} modelLadders
100
- * @property {Record<string, ModelStrength>} personaStrengths
101
99
  * @property {Record<ModelStrength, ModelProvider>} matrixSelection
102
100
  * @property {number} cursor
103
101
  * @property {number} scroll
@@ -109,10 +107,8 @@ function makeConfigSetCommand(key, value) {
109
107
  /** @param {SettingsState} state @returns {Row[]} */
110
108
  export function buildRows(state) {
111
109
  if (!state.modelLadders) return [];
112
- const rows = [{ kind: 'defaultProvider' }];
113
- for (const strength of STRENGTHS) rows.push({ kind: 'matrix', strength });
114
- for (const persona of Object.keys(state.personaStrengths).sort((a, b) => a.localeCompare(b))) rows.push({ kind: 'persona', persona });
115
- rows.push({ kind: 'addPersona' });
110
+ const rows = [{ kind: /** @type {'defaultProvider'} */ ('defaultProvider') }];
111
+ for (const strength of STRENGTHS) rows.push({ kind: /** @type {'matrix'} */ ('matrix'), strength });
116
112
  return rows;
117
113
  }
118
114
 
@@ -131,67 +127,30 @@ function currentRow(state) {
131
127
  export function settingsRowLabel(state, row) {
132
128
  if (!row) return '';
133
129
  if (row.kind === 'defaultProvider') return 'Default provider';
134
- if (row.kind === 'matrix') return `${row.strength} strength`;
135
- if (row.kind === 'persona') return row.persona;
136
- return 'Add persona strength';
130
+ return `${row.strength} strength`;
137
131
  }
138
132
 
139
- /** @param {string} draft @returns {ModelStrength | null} */
140
- function parseStrength(draft) {
141
- const s = draft.trim();
142
- return s === 'ultra' || s === 'strong' || s === 'medium' || s === 'light' ? s : null;
143
- }
144
-
145
- /** @param {string} draft @returns {{persona:string, strength:ModelStrength}|null} */
146
- function parsePersonaPair(draft) {
147
- const s = draft.trim();
148
- if (!s) return null;
149
- const eq = s.indexOf('=');
150
- if (eq > 0) {
151
- const persona = s.slice(0, eq).trim();
152
- const strength = parseStrength(s.slice(eq + 1));
153
- return persona && strength ? { persona, strength } : null;
154
- }
155
- const parts = s.split(/\s+/).filter(Boolean);
156
- if (parts.length >= 2) {
157
- const strength = parseStrength(parts[parts.length - 1] || '');
158
- const persona = parts.slice(0, -1).join(' ').trim();
159
- return persona && strength ? { persona, strength } : null;
160
- }
161
- return null;
162
- }
163
-
164
- /** @param {SettingsState} state @param {Row} row @returns {{ kind:'defaultProvider'|'matrixCell'|'personaStrength'|'personaNew', draft:string, value:string, label:string, strength?:ModelStrength, provider?:ModelProvider, persona?:string }} */
133
+ /** @param {SettingsState} state @param {Row} row @returns {{ kind:'defaultProvider'|'matrixCell', draft:string, value:string, label:string, strength?:ModelStrength, provider?:ModelProvider }} */
165
134
  export function settingsTargetForRow(state, row) {
166
135
  if (row.kind === 'defaultProvider') {
167
136
  return { kind: 'defaultProvider', draft: '', value: state.modelLadders?.defaultProvider ?? 'anthropic', label: 'default provider' };
168
137
  }
169
- if (row.kind === 'matrix') {
170
- const provider = state.matrixSelection[row.strength] ?? DEFAULT_MATRIX_SELECTION[row.strength];
171
- return {
172
- kind: 'matrixCell',
173
- strength: row.strength,
174
- provider,
175
- draft: '',
176
- value: state.modelLadders?.[provider]?.[row.strength] ?? '',
177
- label: `${provider} / ${row.strength}`,
178
- };
179
- }
180
- if (row.kind === 'persona') {
181
- return { kind: 'personaStrength', persona: row.persona, draft: '', value: state.personaStrengths[row.persona] ?? 'strong', label: row.persona };
182
- }
183
- return { kind: 'personaNew', draft: '', value: '', label: 'new persona strength' };
138
+ const provider = state.matrixSelection[row.strength] ?? DEFAULT_MATRIX_SELECTION[row.strength];
139
+ return {
140
+ kind: 'matrixCell',
141
+ strength: row.strength,
142
+ provider,
143
+ draft: '',
144
+ value: state.modelLadders?.[provider]?.[row.strength] ?? '',
145
+ label: `${provider} / ${row.strength}`,
146
+ };
184
147
  }
185
148
 
186
- /** @param {Ctx} ctx @returns {Promise<{ok:true, data:{ladders: ModelLaddersConfig, personaStrengths: Record<string, ModelStrength>}} | {ok:false, error:SourceError}>} */
149
+ /** @param {Ctx} ctx @returns {Promise<{ok:true, data:{ladders: ModelLaddersConfig}} | {ok:false, error:SourceError}>} */
187
150
  async function loadConfig(ctx) {
188
- const [ladders, personaStrengths] = await Promise.all([
189
- ctx.resolve(makeConfigGetSource('modelLadders')),
190
- ctx.resolve(makeConfigGetSource('personaStrengths')),
191
- ]);
151
+ const ladders = await ctx.resolve(makeConfigGetSource('modelLadders'));
192
152
  if (!ladders.ok) return ladders;
193
- if (!personaStrengths.ok) return personaStrengths;
194
- return ok({ ladders: /** @type {ModelLaddersConfig} */ (ladders.data), personaStrengths: /** @type {Record<string, ModelStrength>} */ (personaStrengths.data) });
153
+ return ok({ ladders: /** @type {ModelLaddersConfig} */ (ladders.data) });
195
154
  }
196
155
 
197
156
  /** @param {Ctx} ctx @param {string} msg @param {SourceError['display']['level']} [level='error'] */
@@ -200,7 +159,7 @@ function setBanner(ctx, msg, level = 'error') {
200
159
  else ctx.signal.setBanner(msg, level);
201
160
  }
202
161
 
203
- /** @typedef {{ kind:'defaultProvider'|'matrixCell'|'personaStrength'|'personaNew', value:string, strength?:ModelStrength, provider?:ModelProvider, persona?:string }} PersistTarget */
162
+ /** @typedef {{ kind:'defaultProvider'|'matrixCell', value:string, strength?:ModelStrength, provider?:ModelProvider }} PersistTarget */
204
163
 
205
164
  /** Validate a target, write it via `crtr sys config set`, fold the result into
206
165
  * local state, and re-pull. Shared by submitEdit (text fields) and applyValue
@@ -212,24 +171,13 @@ async function persist(ctx, target) {
212
171
  if (value !== 'anthropic' && value !== 'openai') { setBanner(ctx, 'default provider must be anthropic or openai', 'action'); return false; }
213
172
  } else if (target.kind === 'matrixCell') {
214
173
  if (!value) { setBanner(ctx, 'model id cannot be empty', 'action'); return false; }
215
- } else if (target.kind === 'personaStrength') {
216
- if (!parseStrength(value)) { setBanner(ctx, 'persona strength must be ultra, strong, medium, or light', 'action'); return false; }
217
- } else if (target.kind === 'personaNew') {
218
- if (!parsePersonaPair(value)) { setBanner(ctx, 'use "kind strength" or "kind=strength"', 'action'); return false; }
219
174
  }
220
175
 
221
176
  ctx.signal.setStatus('Saving…');
222
177
  let key = '';
223
- let writeValue = '';
224
- if (target.kind === 'defaultProvider') { key = 'modelLadders.defaultProvider'; writeValue = value; }
225
- else if (target.kind === 'matrixCell') { key = `modelLadders.${target.provider}.${target.strength}`; writeValue = value; }
226
- else if (target.kind === 'personaStrength') { key = `personaStrengths.${target.persona}`; writeValue = value; }
227
- else {
228
- const pair = parsePersonaPair(value);
229
- if (!pair) return false;
230
- key = `personaStrengths.${pair.persona}`;
231
- writeValue = pair.strength;
232
- }
178
+ const writeValue = value;
179
+ if (target.kind === 'defaultProvider') { key = 'modelLadders.defaultProvider'; }
180
+ else { key = `modelLadders.${target.provider}.${target.strength}`; }
233
181
 
234
182
  const r = await ctx.execute(makeConfigSetCommand(key, writeValue));
235
183
  if (!r.ok) {
@@ -241,11 +189,6 @@ async function persist(ctx, target) {
241
189
  const next = { ...s, edit: null };
242
190
  if (target.kind === 'defaultProvider' && next.modelLadders) next.modelLadders = { ...next.modelLadders, defaultProvider: value };
243
191
  if (target.kind === 'matrixCell' && next.modelLadders) next.modelLadders = { ...next.modelLadders, [target.provider]: { ...next.modelLadders[target.provider], [target.strength]: value } };
244
- if (target.kind === 'personaStrength') next.personaStrengths = { ...next.personaStrengths, [target.persona]: /** @type {ModelStrength} */ (value) };
245
- if (target.kind === 'personaNew') {
246
- const pair = parsePersonaPair(value);
247
- if (pair) next.personaStrengths = { ...next.personaStrengths, [pair.persona]: pair.strength };
248
- }
249
192
  return next;
250
193
  });
251
194
  ctx.signal.setStatus('Saved');
@@ -258,14 +201,13 @@ const core = {
258
201
  manifest: {
259
202
  id: 'settings',
260
203
  title: 'Model Ladders',
261
- subtitle: 'provider × strength matrix and persona strengths',
262
- description: 'edit model ladders and personaStrengths through crtr commands',
204
+ subtitle: 'provider × strength matrix',
205
+ description: 'edit model ladders through crtr commands',
263
206
  },
264
207
 
265
208
  init() {
266
209
  return {
267
210
  modelLadders: null,
268
- personaStrengths: {},
269
211
  matrixSelection: { ...DEFAULT_MATRIX_SELECTION },
270
212
  cursor: 0,
271
213
  scroll: 0,
@@ -277,7 +219,6 @@ const core = {
277
219
 
278
220
  sources: {
279
221
  modelLadders: makeConfigGetSource('modelLadders'),
280
- personaStrengths: makeConfigGetSource('personaStrengths'),
281
222
  },
282
223
 
283
224
  intents: {
@@ -297,7 +238,6 @@ const core = {
297
238
  const next = {
298
239
  ...s,
299
240
  modelLadders: r.data.ladders,
300
- personaStrengths: r.data.personaStrengths,
301
241
  sourceError: null,
302
242
  lastFetch: Date.now(),
303
243
  };
@@ -343,11 +283,7 @@ const core = {
343
283
  const cur = s.modelLadders.defaultProvider ?? 'anthropic';
344
284
  return { ...s, edit: { kind: 'defaultProvider', draft: cur, value: cur, label: 'default provider' } };
345
285
  }
346
- if (p.kind === 'persona' && typeof p.persona === 'string') {
347
- const cur = s.personaStrengths[p.persona] ?? 'strong';
348
- return { ...s, edit: { kind: 'personaStrength', persona: p.persona, draft: cur, value: cur, label: p.persona } };
349
- }
350
- return { ...s, edit: { kind: 'personaNew', draft: '', value: '', label: 'new persona strength' } };
286
+ return s;
351
287
  }),
352
288
 
353
289
  setDraft: (ctx, draft) => ctx.set((s) => (s.edit ? { ...s, edit: { ...s.edit, draft: typeof draft === 'string' ? draft : '' } } : s)),
@@ -357,7 +293,7 @@ const core = {
357
293
  async submitEdit(ctx) {
358
294
  const edit = ctx.state.edit;
359
295
  if (!edit) return;
360
- await persist(ctx, { kind: edit.kind, value: edit.draft, strength: edit.strength, provider: edit.provider, persona: edit.persona });
296
+ await persist(ctx, { kind: edit.kind, value: edit.draft, strength: edit.strength, provider: edit.provider });
361
297
  },
362
298
 
363
299
  // One-click save for enum fields (web chips): no editor round-trip. Payload
@@ -368,16 +304,12 @@ const core = {
368
304
  await persist(ctx, p);
369
305
  },
370
306
 
371
- jumpAddPersona: (ctx) => ctx.set((s) => ({ ...s, cursor: Math.max(0, buildRows(s).length - 1) })),
372
-
373
307
  selectRow: (ctx, payload) => ctx.set((s) => {
374
308
  const p = payload && typeof payload === 'object' ? /** @type {Record<string, unknown>} */ (payload) : {};
375
309
  const rows = buildRows(s);
376
310
  const target = rows.findIndex((row) => {
377
311
  if (row.kind === 'defaultProvider' && p.kind === 'defaultProvider') return true;
378
312
  if (row.kind === 'matrix' && p.kind === 'matrix' && p.strength === row.strength) return true;
379
- if (row.kind === 'persona' && p.kind === 'persona' && p.persona === row.persona) return true;
380
- if (row.kind === 'addPersona' && p.kind === 'addPersona') return true;
381
313
  return false;
382
314
  });
383
315
  if (target < 0) return s;
@@ -30,11 +30,6 @@ export function dump(state, ctx) {
30
30
  lines.push(`${row.strength}: anthropic=${state.modelLadders.anthropic?.[row.strength] ?? '—'} | openai=${state.modelLadders.openai?.[row.strength] ?? '—'}${provider ? ` [selected ${provider}]` : ''}`);
31
31
  continue;
32
32
  }
33
- if (row.kind === 'persona') {
34
- lines.push(`${row.persona}: ${state.personaStrengths[row.persona] ?? 'strong'}`);
35
- continue;
36
- }
37
- lines.push('+ add persona strength');
38
33
  }
39
34
  return lines.join('\n');
40
35
  }
@@ -37,11 +37,6 @@ function rows(state) {
37
37
  });
38
38
  continue;
39
39
  }
40
- if (row.kind === 'persona') {
41
- out.push({ spans: [{ text: row.persona, style: { bold: true } }, { text: ' → ' }, { text: state.personaStrengths[row.persona] ?? 'strong', style: { fg: '33' } }] });
42
- continue;
43
- }
44
- out.push({ spans: [{ text: '+ add persona strength', style: { fg: '32', bold: true } }] });
45
40
  }
46
41
  return out;
47
42
  }
@@ -67,7 +62,7 @@ export function render(state, draw, content) {
67
62
 
68
63
  const header = state.edit
69
64
  ? `Replacing ${state.edit.label} — Enter saves, Esc cancels`
70
- : 'j/k move · h/l switch matrix cell · enter edit · n add persona · g refresh';
65
+ : 'j/k move · h/l switch matrix cell · enter edit · g refresh';
71
66
  draw.text(content.row, content.col, 'Model ladders', { bold: true });
72
67
  draw.text(content.row + 1, content.col, header, { dim: true });
73
68
  if (state.edit) {
@@ -85,7 +80,6 @@ export const keymap = [
85
80
  { keys: ['k', 'up'], intent: 'cursorUp', when: (s) => !s.edit },
86
81
  { keys: ['h', 'left'], intent: 'matrixLeft', when: (s) => !s.edit, hint: { keys: 'h/l', label: 'switch cell' } },
87
82
  { keys: ['l', 'right'], intent: 'matrixRight', when: (s) => !s.edit },
88
- { keys: ['n'], intent: 'jumpAddPersona', when: (s) => !s.edit, hint: { keys: 'n', label: 'add persona' } },
89
83
  { keys: ['g'], intent: 'refresh', when: (s) => !s.edit, hint: { keys: 'g', label: 'refresh' } },
90
84
  { keys: ['q'], intent: 'quit', hint: { keys: 'q', label: 'quit' } },
91
85
  { capture: 'setDraft', when: (s) => !!s.edit },
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * Model ladders settings — web presenter for the `settings` view.
4
4
  *
5
- * Mouse-first: enum fields (default provider, persona strength) are click-to-pick
6
- * chips that save immediately; free-text fields (matrix model ids, new persona)
7
- * open an inline editor prefilled with the current value, with Save/Cancel.
5
+ * Mouse-first: the enum field (default provider) is a click-to-pick chip that
6
+ * saves immediately; free-text fields (matrix model ids) open an inline editor
7
+ * prefilled with the current value, with Save/Cancel.
8
8
  *
9
9
  * @module settings/web
10
10
  */
@@ -16,33 +16,6 @@ import { Loading, NotReady, ErrorState } from '@crouton-kit/crouter/web';
16
16
  const STRENGTHS = ['ultra', 'strong', 'medium', 'light'];
17
17
  const PROVIDERS = ['anthropic', 'openai'];
18
18
 
19
- /** Inline editor for a free-text field (matrix model id, new persona pair). */
20
- /** @param {{ state: SettingsState, dispatch: (intent:string, payload?:unknown)=>void }} props */
21
- function Editor({ state, dispatch }) {
22
- if (!state.edit) return null;
23
- return (
24
- <div className="rounded border border-cyan-300 bg-cyan-50 p-3">
25
- <div className="mb-2 text-sm text-slate-600">Editing <span className="font-semibold text-slate-900">{state.edit.label}</span></div>
26
- <div className="flex items-center gap-2">
27
- <input
28
- autoFocus
29
- value={state.edit.draft}
30
- placeholder={state.edit.kind === 'personaNew' ? 'kind strength' : 'model id'}
31
- onChange={(e) => dispatch('setDraft', e.target.value)}
32
- onKeyDown={(e) => {
33
- if (e.key === 'Enter') { e.preventDefault(); dispatch('submitEdit'); }
34
- else if (e.key === 'Escape') { e.preventDefault(); dispatch('cancelEdit'); }
35
- }}
36
- className="flex-1 rounded border border-slate-300 bg-white px-3 py-2 font-mono text-sm outline-none focus:border-cyan-500"
37
- />
38
- <button type="button" onClick={() => dispatch('submitEdit')} className="rounded bg-cyan-600 px-3 py-2 text-sm font-semibold text-white hover:bg-cyan-700">Save</button>
39
- <button type="button" onClick={() => dispatch('cancelEdit')} className="rounded border border-slate-300 bg-white px-3 py-2 text-sm text-slate-600 hover:bg-slate-100">Cancel</button>
40
- </div>
41
- {state.edit.kind === 'personaNew' ? <div className="mt-2 text-xs text-slate-500">Format: <code>kind strength</code> or <code>kind=strength</code> (strength = ultra·strong·medium·light).</div> : null}
42
- </div>
43
- );
44
- }
45
-
46
19
  /** A small segmented control of fixed choices that saves on click. */
47
20
  /** @param {{ choices: string[], value: string, onPick: (c:string)=>void }} props */
48
21
  function Segmented({ choices, value, onPick }) {
@@ -80,8 +53,6 @@ export default function Settings({ state, dispatch }) {
80
53
  }
81
54
 
82
55
  const ladders = state.modelLadders;
83
- const personas = Object.keys(state.personaStrengths).sort((a, b) => a.localeCompare(b));
84
- const editingPersonaNew = !!state.edit && state.edit.kind === 'personaNew';
85
56
 
86
57
  return (
87
58
  <div className="h-full overflow-auto text-sm">
@@ -144,23 +115,6 @@ export default function Settings({ state, dispatch }) {
144
115
  </div>
145
116
  ))}
146
117
  </div>
147
-
148
- {/* Persona strengths — enum per persona, click to pick. */}
149
- <div className="space-y-2">
150
- <div className="font-semibold">Persona strengths</div>
151
- {personas.length === 0 ? <div className="text-xs text-slate-400">None set yet.</div> : null}
152
- {personas.map((persona) => (
153
- <div key={persona} className="flex items-center gap-3">
154
- <span className="w-40 truncate font-mono text-slate-800">{persona}</span>
155
- <Segmented choices={STRENGTHS} value={state.personaStrengths[persona] ?? 'strong'} onPick={(s) => dispatch('applyValue', { kind: 'personaStrength', persona, value: s })} />
156
- </div>
157
- ))}
158
- {editingPersonaNew ? (
159
- <Editor state={state} dispatch={dispatch} />
160
- ) : (
161
- <button type="button" onClick={() => dispatch('beginEditFor', { kind: 'addPersona' })} className="rounded border border-dashed border-emerald-400 px-3 py-1.5 text-sm font-semibold text-emerald-700 hover:bg-emerald-50">+ add persona strength</button>
162
- )}
163
- </div>
164
118
  </div>
165
119
  </div>
166
120
  );