mockpit 2.0.0 → 3.1.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.
Files changed (90) hide show
  1. package/README.md +59 -223
  2. package/bin/demoData.js +2 -2
  3. package/bin/initDesign.js +17 -22
  4. package/bin/mockpit.js +626 -216
  5. package/bin/poster.js +434 -0
  6. package/dist/mcp/server.js +127 -29
  7. package/dist/server/answerAdditions.d.ts +7 -0
  8. package/dist/server/answerAdditions.js +76 -0
  9. package/dist/server/apiViews.d.ts +627 -385
  10. package/dist/server/apiViews.js +198 -81
  11. package/dist/server/app.d.ts +7 -2
  12. package/dist/server/app.js +3514 -984
  13. package/dist/server/appIcon.js +15 -5
  14. package/dist/server/appIconPng.js +3 -3
  15. package/dist/server/askNudges.js +1 -1
  16. package/dist/server/cdnInline.d.ts +8 -0
  17. package/dist/server/cdnInline.js +640 -0
  18. package/dist/server/codeWorker.js +2 -2
  19. package/dist/server/contrast.d.ts +4 -0
  20. package/dist/server/contrast.js +157 -0
  21. package/dist/server/decisionList.d.ts +48 -0
  22. package/dist/server/decisionList.js +340 -0
  23. package/dist/server/decisions.d.ts +4 -4
  24. package/dist/server/decisions.js +9 -9
  25. package/dist/server/designGuide.d.ts +14 -7
  26. package/dist/server/designGuide.js +314 -88
  27. package/dist/server/embeds.d.ts +3 -1
  28. package/dist/server/embeds.js +31 -6
  29. package/dist/server/eventLog.d.ts +2 -2
  30. package/dist/server/eventLog.js +11 -11
  31. package/dist/server/events.d.ts +15 -6
  32. package/dist/server/feedbackBatch.d.ts +39 -13
  33. package/dist/server/feedbackBatch.js +129 -47
  34. package/dist/server/fills.d.ts +4 -0
  35. package/dist/server/fills.js +55 -1
  36. package/dist/server/guidance.d.ts +1 -1
  37. package/dist/server/guidance.js +2 -2
  38. package/dist/server/handoff.d.ts +4 -1
  39. package/dist/server/handoff.js +8 -2
  40. package/dist/server/icons.js +1 -1
  41. package/dist/server/index.js +39 -10
  42. package/dist/server/jump.d.ts +21 -0
  43. package/dist/server/jump.js +175 -0
  44. package/dist/server/kits.d.ts +2 -2
  45. package/dist/server/kits.js +54 -1
  46. package/dist/server/knobs.d.ts +3 -3
  47. package/dist/server/knobs.js +6 -6
  48. package/dist/server/mcpHttp.d.ts +7 -1
  49. package/dist/server/mcpHttp.js +29 -12
  50. package/dist/server/mcpSpec.d.ts +27 -27
  51. package/dist/server/mcpSpec.js +196 -56
  52. package/dist/server/parts.js +9 -1
  53. package/dist/server/run.d.ts +1 -1
  54. package/dist/server/run.js +47 -17
  55. package/dist/server/runApi.d.ts +3 -3
  56. package/dist/server/runApi.js +34 -21
  57. package/dist/server/sqlStore.d.ts +148 -103
  58. package/dist/server/sqlStore.js +1052 -181
  59. package/dist/server/standings.d.ts +25 -0
  60. package/dist/server/standings.js +113 -0
  61. package/dist/server/surfacePage.d.ts +8 -9
  62. package/dist/server/surfacePage.js +423 -100
  63. package/dist/server/system.d.ts +234 -0
  64. package/dist/server/system.js +1508 -0
  65. package/dist/server/systemChecks.d.ts +15 -0
  66. package/dist/server/systemChecks.js +224 -0
  67. package/dist/server/systemSpecimen.d.ts +2 -0
  68. package/dist/server/systemSpecimen.js +49 -0
  69. package/dist/server/themes.d.ts +9 -7
  70. package/dist/server/themes.js +43 -90
  71. package/dist/server/types.d.ts +266 -93
  72. package/dist/server/types.js +76 -79
  73. package/extensions/mockpit.js +89 -72
  74. package/guide/AGENT_SETUP.md +15 -1
  75. package/guide/packs/design-craft.md +2 -3
  76. package/guide/packs/foundations.md +35 -0
  77. package/guide/topics/asks.md +108 -63
  78. package/guide/topics/feedback.md +129 -120
  79. package/guide/topics/html.md +18 -25
  80. package/guide/topics/http.md +43 -41
  81. package/guide/topics/knobs.md +7 -10
  82. package/guide/topics/scripts.md +22 -33
  83. package/guide/topics/surfaces.md +39 -48
  84. package/guide/topics/system.md +157 -0
  85. package/opencode/mockpit.js +120 -0
  86. package/opencode/package.json +8 -0
  87. package/package.json +15 -14
  88. package/skills/launch-video/SKILL.md +11 -11
  89. package/skills/mockpit/SKILL.md +33 -26
  90. package/viewer/dist/index.html +10 -9
@@ -1,8 +1,9 @@
1
1
  // Response shapes shared by every tier (REST, both MCP transports, the CLI), so
2
- // one mock reads the same however an agent asks for it.
2
+ // one design reads the same however an agent asks for it.
3
3
  import { slotKnobs, slotsInSurfaces } from "./fills.js";
4
4
  import { mergeParts, partsInSurfaces } from "./parts.js";
5
- import { draftedCount, inUseVersion, isSandboxedSurfaceKind, isLive, momentsOf, openCount, SURFACE_CONTENT_FIELDS, variantStatus, } from "./types.js";
5
+ import { isKeptVariant, keptVariants } from "./standings.js";
6
+ import { changedTo, currentVersion, draftedCount, inUseVersion, isPlanned, isSandboxedSurfaceKind, isLive, openCount, recommendedVariant, SURFACE_CONTENT_FIELDS, variantStatus, } from "./types.js";
6
7
  export const surfaceRef = (surface, index) => ({
7
8
  id: surface.id,
8
9
  kind: surface.kind,
@@ -25,11 +26,43 @@ export const hydratedSurfaceView = (surface, index) => {
25
26
  delete view[field];
26
27
  return view;
27
28
  };
28
- // The still overviews show for a version: its html page's poster.
29
- const pagePoster = (surfaces) => {
30
- const page = surfaces.find((s) => s.kind === "html");
31
- return page?.kind === "html" ? page.poster : undefined;
29
+ // A still mockpit took shows the page in the mode it was taken in. In the other mode it
30
+ // would be the one picture in the wrong colours among live pages, so it is left out and the
31
+ // page runs as it did before posters. Applied to posts before any view reads them, so every
32
+ // view agrees. Posts come from the store's cache: copied where changed, never written to.
33
+ // The same goes for a still taken under another revision of the project's system.
34
+ const otherMode = (s, mode, rev) => {
35
+ const { posterMode: taken, posterSystem: under } = s;
36
+ if (s.kind !== "html" || taken === undefined)
37
+ return false;
38
+ if (taken !== mode)
39
+ return true;
40
+ if (rev === undefined)
41
+ return false;
42
+ return under === undefined ? rev > 1 : under !== rev;
32
43
  };
44
+ const surfacesFor = (surfaces, mode, rev) => surfaces.some((s) => otherMode(s, mode, rev))
45
+ ? surfaces.map((s) => {
46
+ if (!otherMode(s, mode, rev))
47
+ return s;
48
+ const { poster: _poster, posterMode: _mode, posterSystem: _system, ...rest } = s;
49
+ return rest;
50
+ })
51
+ : surfaces;
52
+ export const postersFor = (posts, mode,
53
+ // The project system's revision; undefined across projects, where it isn't one.
54
+ rev) => posts.map((p) => {
55
+ const surfaces = surfacesFor(p.surfaces, mode, rev);
56
+ const history = p.history.map((h) => {
57
+ const own = surfacesFor(h.surfaces, mode, rev);
58
+ return own === h.surfaces ? h : { ...h, surfaces: own };
59
+ });
60
+ return surfaces === p.surfaces && history.every((h, i) => h === p.history[i])
61
+ ? p
62
+ : { ...p, surfaces, history };
63
+ });
64
+ // The still overviews show for a version: its html page's poster.
65
+ const pagePoster = (surfaces) => surfaces.find((s) => s.kind === "html")?.poster;
33
66
  const posterOf = (surfaces) => {
34
67
  const poster = pagePoster(surfaces);
35
68
  return poster ? { poster } : {};
@@ -46,6 +79,28 @@ const inUsePoster = (p) => {
46
79
  const poster = posterAt(p, p.acceptedVersion);
47
80
  return poster ? { inUsePoster: poster } : {};
48
81
  };
82
+ // The version Home shows when nothing is in use and the newest was hidden.
83
+ const coverVersion = (p) => {
84
+ const at = currentVersion(p);
85
+ if (p.status === "accepted" || at === p.version)
86
+ return {};
87
+ const poster = posterAt(p, at);
88
+ return { coverVersion: at, ...(poster ? { coverPoster: poster } : {}) };
89
+ };
90
+ // A page that draws on a canvas may hold a WebGL context, of which a browser
91
+ // keeps about 16 and drops the oldest past that, so overviews ration these
92
+ // frames and no others. Three.js and the like make their canvas from script.
93
+ // Asked once per version: a read lists every version, with bodies up to 1 MB.
94
+ const CANVAS_PAGE = /<canvas[\s>]|webgl|three(?:\.module|\.min|\.js|@)|['"]three['"]|pixi|babylon/i;
95
+ const canvasPages = new WeakMap();
96
+ const drawsOnCanvas = (surfaces) => {
97
+ let hit = canvasPages.get(surfaces);
98
+ if (hit === undefined) {
99
+ hit = surfaces.some((s) => s.kind === "html" && CANVAS_PAGE.test(s.html));
100
+ canvasPages.set(surfaces, hit);
101
+ }
102
+ return hit;
103
+ };
49
104
  // One past version without its bodies: enough to list versions or pick one to
50
105
  // fetch, at a bounded cost.
51
106
  export const historyMetaView = (version) => ({
@@ -55,8 +110,10 @@ export const historyMetaView = (version) => ({
55
110
  ...(version.from === undefined ? {} : { from: version.from }),
56
111
  ...(version.prompt ? { prompt: version.prompt } : {}),
57
112
  ...(version.author === undefined ? {} : { author: version.author }),
113
+ ...(version.amendedAt ? { amendedAt: version.amendedAt } : {}),
58
114
  surfaceKinds: version.surfaces.map((s) => s.kind),
59
115
  ...posterOf(version.surfaces),
116
+ ...(drawsOnCanvas(version.surfaces) ? { canvas: true } : {}),
60
117
  });
61
118
  export const variantView = (post, opts = {}) => ({
62
119
  postId: post.id,
@@ -71,12 +128,15 @@ export const variantView = (post, opts = {}) => ({
71
128
  ...(post.from === undefined ? {} : { from: post.from }),
72
129
  ...(post.prompt ? { prompt: post.prompt } : {}),
73
130
  ...(post.author === undefined ? {} : { author: post.author }),
131
+ ...(post.amendedAt ? { amendedAt: post.amendedAt } : {}),
132
+ // Frame URLs carry it (`w`): an amended version keeps its number.
133
+ ...(post.rewrites ? { rewrites: post.rewrites } : {}),
74
134
  // A part with fills is a select knob on its part (fills.ts); declared knobs win.
75
135
  ...(slotsInSurfaces(post.surfaces).length || post.knobs
76
136
  ? { knobs: { ...slotKnobs(slotsInSurfaces(post.surfaces)), ...post.knobs } }
77
137
  : {}),
78
138
  ...(slotsInSurfaces(post.surfaces).length ? { fills: slotsInSurfaces(post.surfaces) } : {}),
79
- ...(post.note ? { note: post.note } : {}),
139
+ ...(post.description ? { description: post.description } : {}),
80
140
  // Embedded components as they render now (server/embeds.ts); `publishedWith`
81
141
  // is what this version rendered with when it was published.
82
142
  ...(opts.embeds?.get(post.id)
@@ -84,8 +144,11 @@ export const variantView = (post, opts = {}) => ({
84
144
  : {}),
85
145
  ...(post.embeds.length ? { publishedWith: post.embeds } : {}),
86
146
  ...(post.forkedFrom ? { forkedFrom: post.forkedFrom } : {}),
87
- ...(post.starred ? { starred: true } : {}),
147
+ ...(post.copiedFrom ? { copiedFrom: post.copiedFrom } : {}),
148
+ ...(opts.kept && isKeptVariant(opts.kept, post) ? { kept: true } : {}),
88
149
  ...(post.acceptedVersion === undefined ? {} : { inUseVersion: post.acceptedVersion }),
150
+ ...(changedTo(post) === undefined ? {} : { changedTo: changedTo(post) }),
151
+ ...(post.archiveReason ? { archiveReason: post.archiveReason } : {}),
89
152
  // The part names this variant's own markup marks, so the viewer can tell
90
153
  // which variants render a part (Borrow) without the html bodies.
91
154
  parts: partsInSurfaces(post.surfaces).map((p) => p.name),
@@ -101,97 +164,124 @@ export const variantView = (post, opts = {}) => ({
101
164
  ...(post.from === undefined ? {} : { from: post.from }),
102
165
  ...(post.prompt === undefined ? {} : { prompt: post.prompt }),
103
166
  ...(post.author === undefined ? {} : { author: post.author }),
167
+ ...(post.amendedAt ? { amendedAt: post.amendedAt } : {}),
104
168
  }),
105
169
  ...[...post.history].sort((a, b) => b.version - a.version).map(historyMetaView),
106
170
  ].map((row) => post.hiddenVersions?.includes(row.version) ? { ...row, hidden: true } : row),
107
171
  }
108
172
  : {}),
109
173
  });
110
- const stateOrder = (mock) => (mock.states.length ? mock.states : [null]);
111
- // What the user put in use, per state, in the mock's order; a state with
174
+ const stateOrder = (design) => (design.states.length ? design.states : [null]);
175
+ // What the user put in use, per state, in the design's order; a state with
112
176
  // nothing accepted is absent. Export and embeds resolve to exactly these.
113
- export const inUseByState = (mock, posts) => stateOrder(mock).flatMap((state) => {
177
+ export const inUseByState = (design, posts) => stateOrder(design).flatMap((state) => {
114
178
  const p = posts.find((x) => x.state === state && x.status === "accepted");
115
179
  return p ? [{ state, variant: p.variant, version: inUseVersion(p) }] : [];
116
180
  });
117
181
  // The parts the agent marked, per state, across the variants still in play.
118
- export function partsByState(mock, posts) {
119
- return stateOrder(mock).map((state) => ({
182
+ export function partsByState(design, posts) {
183
+ return stateOrder(design).map((state) => ({
120
184
  state,
121
185
  parts: mergeParts(posts
122
186
  .filter((p) => p.state === state && isLive(p.status))
123
187
  .map((p) => partsInSurfaces(p.surfaces))),
124
188
  }));
125
189
  }
126
- // Where a mock sits in the product and the code that implements it, only once
190
+ // Where a design sits in the product and the code that implements it, only once
127
191
  // declared, so the views of a project that names neither cost nothing.
128
- export const placeOf = (mock) => ({
129
- ...(mock.area ? { area: mock.area } : {}),
130
- ...(mock.source ? { source: mock.source } : {}),
192
+ export const placeOf = (design) => ({
193
+ ...(design.area ? { area: design.area } : {}),
194
+ ...(design.source ? { source: design.source } : {}),
131
195
  });
132
- // What Home shows for a mock: the first state's chosen (else first) variant.
133
- function thumbnail(mock, posts) {
134
- const first = stateOrder(mock)[0];
196
+ // What a design's first state looks like in a list: what's in use, else what the agent
197
+ // recommends, else its first. Same rule as viewer/src/logic.ts screenPicture.
198
+ function thumbnail(design, posts) {
199
+ const first = stateOrder(design)[0];
135
200
  const inState = posts.filter((p) => p.state === first);
201
+ const recommended = recommendedVariant(design.asks, first);
136
202
  const pick = inState.find((p) => p.status === "accepted") ??
203
+ inState.find((p) => p.variant === recommended && isLive(p.status)) ??
137
204
  inState.find((p) => p.status === "open") ??
138
205
  inState[0] ??
139
206
  posts[0];
140
207
  if (!pick)
141
208
  return null;
142
209
  const surface = pick.surfaces.findIndex((s) => isSandboxedSurfaceKind(s.kind));
143
- const version = inUseVersion(pick);
210
+ const version = currentVersion(pick);
144
211
  const poster = posterAt(pick, version);
145
- return { postId: pick.id, surface: Math.max(surface, 0), version, ...(poster ? { poster } : {}) };
212
+ return {
213
+ postId: pick.id,
214
+ surface: Math.max(surface, 0),
215
+ version,
216
+ ...(poster ? { poster } : {}),
217
+ ...(pick.rewrites ? { rewrites: pick.rewrites } : {}),
218
+ };
146
219
  }
147
- // A mock's situations, only once it has any, so the views of mocks without
148
- // them are unchanged.
149
- const situationsOf = (mock) => Object.keys(mock.situations).length ? { situations: mock.situations } : {};
150
- export const mockSummaryView = (mock, posts) => {
151
- const drafted = draftedCount(mock, posts);
220
+ // Only a plan says so, so the views of everything drawn are unchanged.
221
+ const plannedOf = (design, variants) => isPlanned(design, variants) ? { planned: true } : {};
222
+ // Anything the user has yet to send. A stored draft can be an emptied one, which
223
+ // waits on nobody.
224
+ export const draftHolds = (draft) => !!draft &&
225
+ (draft.comments.length > 0 ||
226
+ Object.values(draft.answers).some((a) => !Array.isArray(a) || a.length > 0) ||
227
+ [draft.borrowed, draft.tuned, draft.others ?? {}, draft.askComments ?? {}].some((rec) => Object.keys(rec).length > 0));
228
+ export const designSummaryView = (design, posts) => {
229
+ const drafted = draftedCount(design);
152
230
  return {
153
- id: mock.id,
154
- project: mock.project,
155
- slug: mock.slug,
156
- title: mock.title,
157
- kind: mock.kind,
158
- ...placeOf(mock),
159
- states: mock.states,
160
- ...situationsOf(mock),
161
- stateCount: Math.max(momentsOf(mock.states, mock.situations).length, 1),
231
+ id: design.id,
232
+ project: design.project,
233
+ slug: design.slug,
234
+ title: design.title,
235
+ kind: design.kind,
236
+ ...placeOf(design),
237
+ ...plannedOf(design, posts.length),
238
+ // Text comes with a read of the design; a list only says there is some to read.
239
+ ...(design.notes ? { hasNotes: true } : {}),
240
+ states: design.states,
241
+ stateCount: Math.max(design.states.length, 1),
162
242
  variants: posts.length,
163
- open: openCount(mock, posts),
243
+ open: openCount(design),
164
244
  // Only once the user has started one, so a list with no drafts costs nothing.
165
245
  ...(drafted === null ? {} : { drafted }),
166
- sessionId: mock.sessionId,
167
- closedAt: mock.closedAt,
168
- createdAt: mock.createdAt,
169
- updatedAt: mock.updatedAt,
170
- thumbnail: thumbnail(mock, posts),
246
+ sessionId: design.sessionId,
247
+ archivedAt: design.archivedAt,
248
+ createdAt: design.createdAt,
249
+ updatedAt: design.updatedAt,
250
+ thumbnail: thumbnail(design, posts),
171
251
  };
172
252
  };
173
- // One mock as anyone outside the viewer's own draft sees it: the draft is the
253
+ // One design as anyone outside the viewer's own draft sees it: the draft is the
174
254
  // user's unsent work and never leaves through this view.
175
- export const mockDetailView = (mock, posts, opts = {}) => ({
176
- id: mock.id,
177
- project: mock.project,
178
- slug: mock.slug,
179
- title: mock.title,
180
- kind: mock.kind,
181
- ...placeOf(mock),
182
- states: mock.states,
183
- ...situationsOf(mock),
184
- asks: mock.asks,
185
- open: openCount(mock, posts),
186
- knobs: mock.knobs,
187
- widths: mock.widths,
188
- sessionId: mock.sessionId,
189
- closedAt: mock.closedAt,
190
- createdAt: mock.createdAt,
191
- updatedAt: mock.updatedAt,
192
- variants: posts.map((p) => variantView(p, opts)),
193
- inUse: inUseByState(mock, posts),
194
- parts: partsByState(mock, posts),
255
+ export const designDetailView = (design, posts, opts = {}) => ({
256
+ id: design.id,
257
+ project: design.project,
258
+ slug: design.slug,
259
+ title: design.title,
260
+ kind: design.kind,
261
+ ...placeOf(design),
262
+ ...(design.shortlist ? { shortlist: true } : {}),
263
+ // No agent has written to this shortlist yet: who its replies reach for now.
264
+ ...(opts.repliesGoTo ? { repliesGoTo: opts.repliesGoTo } : {}),
265
+ ...plannedOf(design, posts.length),
266
+ states: design.states,
267
+ ...(design.notes ? { notes: design.notes } : {}),
268
+ ...(Object.keys(design.stateOutlines).length ? { stateOutlines: design.stateOutlines } : {}),
269
+ ...(design.answerAdditions.length ? { answerAdditions: design.answerAdditions } : {}),
270
+ asks: design.asks,
271
+ // By question id: the answers the user kept for later and hid, with why.
272
+ ...(Object.keys(design.standings).length ? { standings: design.standings } : {}),
273
+ ...(design.sets.length ? { sets: design.sets } : {}),
274
+ open: openCount(design),
275
+ knobs: design.knobs,
276
+ widths: design.widths,
277
+ sessionId: design.sessionId,
278
+ archivedAt: design.archivedAt,
279
+ ...(design.archiveReason ? { archiveReason: design.archiveReason } : {}),
280
+ createdAt: design.createdAt,
281
+ updatedAt: design.updatedAt,
282
+ variants: ((kept) => posts.map((p) => variantView(p, { ...opts, kept })))(keptVariants(design, posts)),
283
+ inUse: inUseByState(design, posts),
284
+ parts: partsByState(design, posts),
195
285
  // Every knob value the user has sent (answers' sets and tuning, later replies
196
286
  // winning), never drafts. Later questions render on top of it.
197
287
  settled: opts.settled ?? {},
@@ -200,26 +290,50 @@ export const sessionRowView = (session, postCount) => ({
200
290
  ...session,
201
291
  postCount,
202
292
  });
203
- // One mock as a node of the project tree (GET /api/projects/:name/tree): its
204
- // variants per state with status and lineage, and the mocks it embeds (`embeds`);
293
+ // One design as a node of the project tree (GET /api/projects/:name/tree): its
294
+ // variants per state with status and lineage, and the designs it embeds (`embeds`);
205
295
  // `embeddedIn` is filled in once every node is known.
206
- export const treeNodeView = (mock, posts, extra) => ({
207
- id: mock.id,
208
- slug: mock.slug,
209
- title: mock.title,
210
- kind: mock.kind,
211
- ...placeOf(mock),
212
- open: openCount(mock, posts),
213
- // Home shows a mock's states, never their situations.
214
- states: mock.states.length ? momentsOf(mock.states, mock.situations) : [null],
215
- widths: mock.widths,
216
- createdAt: mock.createdAt,
217
- updatedAt: mock.updatedAt,
218
- closedAt: mock.closedAt,
296
+ // When it left the shelf: archived by hand, or with the last of its variants archived.
297
+ // Designs archived before the date was kept fall back on the design's last change.
298
+ function leftOf(design, posts) {
299
+ if (design.archivedAt)
300
+ return { leftAt: design.archivedAt };
301
+ if (!posts.length || posts.some((p) => isLive(p.status)))
302
+ return {};
303
+ const last = posts.reduce((t, p) => (p.archivedAt && p.archivedAt > t ? p.archivedAt : t), "");
304
+ return { leftAt: last || design.updatedAt };
305
+ }
306
+ export const treeNodeView = (design, posts, extra) => ({
307
+ id: design.id,
308
+ slug: design.slug,
309
+ title: design.title,
310
+ kind: design.kind,
311
+ ...placeOf(design),
312
+ ...(design.shortlist ? { shortlist: true } : {}),
313
+ ...plannedOf(design, posts.length),
314
+ open: openCount(design),
315
+ // Any question ever asked, answered or not: it tells an idea from a document.
316
+ ...(design.asks.length ? { asked: design.asks.length } : {}),
317
+ // Later than every variant: the user has spoken and it is the agent's turn.
318
+ ...(extra.lastReplyAt ? { lastReplyAt: extra.lastReplyAt } : {}),
319
+ ...(extra.lastSend ? { lastSend: extra.lastSend } : {}),
320
+ ...(extra.drafting ? { drafting: true } : {}),
321
+ states: stateOrder(design),
322
+ widths: design.widths,
323
+ createdAt: design.createdAt,
324
+ updatedAt: design.updatedAt,
325
+ archivedAt: design.archivedAt,
326
+ ...leftOf(design, posts),
327
+ ...(design.archiveReason ? { archiveReason: design.archiveReason } : {}),
219
328
  settled: extra.settled,
220
329
  embeds: extra.embeds,
221
330
  embeddedIn: [],
222
- inUse: inUseByState(mock, posts),
331
+ inUse: inUseByState(design, posts),
332
+ // What the agent recommends where nothing is in use: a cover shows it.
333
+ recommended: stateOrder(design).flatMap((state) => {
334
+ const variant = recommendedVariant(design.asks, state);
335
+ return variant ? [{ state, variant }] : [];
336
+ }),
223
337
  variants: posts.map((p) => ({
224
338
  postId: p.id,
225
339
  state: p.state,
@@ -227,13 +341,16 @@ export const treeNodeView = (mock, posts, extra) => ({
227
341
  status: variantStatus(p.status),
228
342
  version: p.version,
229
343
  updatedAt: p.updatedAt,
344
+ ...(p.rewrites ? { rewrites: p.rewrites } : {}),
230
345
  surface: p.surfaces.findIndex((x) => isSandboxedSurfaceKind(x.kind)),
231
346
  ...posterOf(p.surfaces),
232
347
  ...inUsePoster(p),
233
- ...(p.note ? { note: p.note } : {}),
348
+ ...coverVersion(p),
349
+ ...(p.description && !extra.noDescriptions ? { description: p.description } : {}),
234
350
  ...(p.forkedFrom ? { forkedFrom: p.forkedFrom } : {}),
235
- ...(p.starred ? { starred: true } : {}),
236
351
  ...(p.acceptedVersion === undefined ? {} : { inUseVersion: p.acceptedVersion }),
352
+ ...(changedTo(p) === undefined ? {} : { changedTo: changedTo(p) }),
353
+ ...(p.archiveReason ? { archiveReason: p.archiveReason } : {}),
237
354
  ...(extra.embedViews.get(p.id) ? { embedsKey: extra.embedViews.get(p.id).key } : {}),
238
355
  })),
239
356
  });
@@ -30,6 +30,10 @@ export interface AppOptions {
30
30
  maxHoldConnections?: number;
31
31
  executor?: Executor;
32
32
  runLimits?: Partial<RunLimits>;
33
+ networkHosts?: () => {
34
+ localOnly: boolean;
35
+ hosts: string[];
36
+ };
33
37
  log?: LogSink;
34
38
  }
35
39
  export interface LatestRelease {
@@ -38,12 +42,13 @@ export interface LatestRelease {
38
42
  }
39
43
  export interface CommentWait {
40
44
  sessionId?: string;
41
- mockId?: string;
45
+ designId?: string;
42
46
  postId?: string;
43
47
  author?: string;
44
48
  afterSeq?: number;
45
49
  waitSeconds: number;
46
50
  agent?: boolean;
51
+ peek?: boolean;
47
52
  }
48
53
  export interface FlowResult {
49
54
  status: number;
@@ -56,4 +61,4 @@ export interface FlowContext {
56
61
  request?: Request;
57
62
  signal?: AbortSignal;
58
63
  }
59
- export declare function createApp({ store, viewerHtml, setupText, topics, packs, userRules, authenticate, authToken, basePath, publicRead, screenshots, version, upgradeCommand, renderRev, fetchLatestRelease, onEvent, maxHoldConnections, executor, runLimits, log, }: AppOptions): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
64
+ export declare function createApp({ store, viewerHtml, setupText, topics, packs, userRules, authenticate, authToken, basePath, publicRead, screenshots, version, upgradeCommand, renderRev, fetchLatestRelease, onEvent, maxHoldConnections, executor, runLimits, networkHosts, log, }: AppOptions): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;