mockpit 1.1.0 → 1.3.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 (62) hide show
  1. package/README.md +13 -6
  2. package/bin/mockpit.js +139 -18
  3. package/dist/mcp/server.js +30 -10
  4. package/dist/server/apiViews.d.ts +129 -25
  5. package/dist/server/apiViews.js +86 -23
  6. package/dist/server/app.d.ts +2 -1
  7. package/dist/server/app.js +1258 -200
  8. package/dist/server/appIcon.d.ts +22 -0
  9. package/dist/server/appIcon.js +43 -0
  10. package/dist/server/appIconPng.d.ts +1 -0
  11. package/dist/server/appIconPng.js +6 -0
  12. package/dist/server/askNudges.d.ts +3 -0
  13. package/dist/server/askNudges.js +40 -0
  14. package/dist/server/base64.d.ts +1 -0
  15. package/dist/server/base64.js +8 -0
  16. package/dist/server/decisions.d.ts +23 -0
  17. package/dist/server/decisions.js +99 -0
  18. package/dist/server/designGuide.js +53 -16
  19. package/dist/server/embeds.d.ts +25 -0
  20. package/dist/server/embeds.js +118 -0
  21. package/dist/server/eventLog.js +2 -1
  22. package/dist/server/feedbackBatch.d.ts +28 -5
  23. package/dist/server/feedbackBatch.js +78 -11
  24. package/dist/server/fills.d.ts +41 -0
  25. package/dist/server/fills.js +328 -0
  26. package/dist/server/handoff.d.ts +6 -0
  27. package/dist/server/handoff.js +38 -0
  28. package/dist/server/index.js +15 -1
  29. package/dist/server/knobs.d.ts +6 -2
  30. package/dist/server/knobs.js +13 -4
  31. package/dist/server/mcpHttp.d.ts +5 -2
  32. package/dist/server/mcpHttp.js +14 -5
  33. package/dist/server/mcpSpec.d.ts +21 -13
  34. package/dist/server/mcpSpec.js +99 -9
  35. package/dist/server/postSurfaces.js +3 -12
  36. package/dist/server/richRender.js +3 -1
  37. package/dist/server/run.js +6 -2
  38. package/dist/server/runApi.d.ts +1 -1
  39. package/dist/server/runApi.js +5 -4
  40. package/dist/server/sqlStore.d.ts +92 -5
  41. package/dist/server/sqlStore.js +188 -30
  42. package/dist/server/surfacePage.d.ts +6 -2
  43. package/dist/server/surfacePage.js +142 -10
  44. package/dist/server/themes.d.ts +2 -0
  45. package/dist/server/themes.js +23 -3
  46. package/dist/server/types.d.ts +95 -10
  47. package/dist/server/types.js +89 -6
  48. package/dist/server/viewerRequest.d.ts +2 -0
  49. package/dist/server/viewerRequest.js +14 -0
  50. package/extensions/mockpit.js +1 -0
  51. package/guide/topics/asks.md +69 -10
  52. package/guide/topics/feedback.md +104 -16
  53. package/guide/topics/html.md +5 -1
  54. package/guide/topics/http.md +2 -1
  55. package/guide/topics/knobs.md +4 -3
  56. package/guide/topics/surfaces.md +32 -16
  57. package/package.json +4 -13
  58. package/skills/launch-video/SKILL.md +1 -1
  59. package/skills/mockpit/SKILL.md +14 -4
  60. package/viewer/dist/index.html +9 -9
  61. package/dist/server/slots.d.ts +0 -14
  62. package/dist/server/slots.js +0 -71
@@ -1,5 +1,5 @@
1
1
  import { type PartInfo } from "./parts.ts";
2
- import { type Mock, type Post, type PostVersion, type Session, type Surface } from "./types.ts";
2
+ import { type Embed, type KnobValue, type Mock, type Post, type PostVersion, type Session, type Surface } from "./types.ts";
3
3
  export declare const surfaceRef: (surface: Pick<Surface, "id" | "kind">, index: number) => {
4
4
  id: string | undefined;
5
5
  kind: "html" | "diff" | "image" | "markdown" | "terminal" | "mermaid" | "json" | "code";
@@ -56,7 +56,7 @@ export declare const fullSurfaceView: (surface: Surface, index: number) => {
56
56
  lineStart?: number;
57
57
  id?: string;
58
58
  };
59
- export declare const hydratedSurfaceView: (surface: Surface, index: number) => Record<string, unknown> | {
59
+ export declare const hydratedSurfaceView: (surface: Surface, index: number) => {
60
60
  index: number;
61
61
  kind: "html";
62
62
  html: string;
@@ -106,7 +106,7 @@ export declare const hydratedSurfaceView: (surface: Surface, index: number) => R
106
106
  title?: string;
107
107
  lineStart?: number;
108
108
  id?: string;
109
- };
109
+ } | Record<string, unknown>;
110
110
  export declare const historyMetaView: (version: PostVersion) => {
111
111
  surfaceKinds: ("html" | "diff" | "image" | "markdown" | "terminal" | "mermaid" | "json" | "code")[];
112
112
  author?: string | undefined;
@@ -119,9 +119,17 @@ export declare const historyMetaView: (version: PostVersion) => {
119
119
  export interface VariantViewOptions {
120
120
  body?: boolean;
121
121
  history?: boolean;
122
+ embeds?: Map<string, EmbedView>;
123
+ }
124
+ export interface EmbedView {
125
+ key: string;
126
+ list: (Embed & {
127
+ latest: number;
128
+ versions: number[];
129
+ })[];
122
130
  }
123
131
  export declare const variantView: (post: Post, opts?: VariantViewOptions) => {
124
- history?: {
132
+ history?: ({
125
133
  surfaceKinds: ("html" | "diff" | "image" | "markdown" | "terminal" | "mermaid" | "json" | "code")[];
126
134
  author?: string | undefined;
127
135
  prompt?: string | undefined;
@@ -129,9 +137,18 @@ export declare const variantView: (post: Post, opts?: VariantViewOptions) => {
129
137
  version: number;
130
138
  title: string;
131
139
  at: string;
132
- }[] | undefined;
140
+ } | {
141
+ hidden: true;
142
+ surfaceKinds: ("html" | "diff" | "image" | "markdown" | "terminal" | "mermaid" | "json" | "code")[];
143
+ author?: string | undefined;
144
+ prompt?: string | undefined;
145
+ from?: number | undefined;
146
+ version: number;
147
+ title: string;
148
+ at: string;
149
+ })[] | undefined;
133
150
  parts: string[];
134
- surfaces: (Record<string, unknown> | {
151
+ surfaces: ({
135
152
  index: number;
136
153
  kind: "html";
137
154
  html: string;
@@ -181,9 +198,21 @@ export declare const variantView: (post: Post, opts?: VariantViewOptions) => {
181
198
  title?: string;
182
199
  lineStart?: number;
183
200
  id?: string;
184
- })[];
185
- slots?: import("./types.ts").Slot[] | undefined;
186
- knobs?: import("./types.ts").Knobs | undefined;
201
+ } | Record<string, unknown>)[];
202
+ acceptedVersion?: number | undefined;
203
+ starred?: boolean | undefined;
204
+ forkedFrom?: import("./types.ts").ForkRef | undefined;
205
+ publishedWith?: Embed[] | undefined;
206
+ embeds?: (Embed & {
207
+ latest: number;
208
+ versions: number[];
209
+ })[] | undefined;
210
+ embedsKey?: string | undefined;
211
+ note?: string | undefined;
212
+ slots?: import("./fills.ts").SlotInfo[] | undefined;
213
+ knobs?: {
214
+ [x: string]: import("./types.ts").KnobConfig;
215
+ } | undefined;
187
216
  author?: string | undefined;
188
217
  prompt?: string | undefined;
189
218
  from?: number | undefined;
@@ -197,12 +226,27 @@ export declare const variantView: (post: Post, opts?: VariantViewOptions) => {
197
226
  createdAt: string;
198
227
  updatedAt: string;
199
228
  };
229
+ export declare const inUseByState: (mock: Mock, posts: Post[]) => {
230
+ state: string | null;
231
+ variant: string;
232
+ version: number;
233
+ }[];
200
234
  export interface StateParts {
201
235
  state: string | null;
202
236
  parts: PartInfo[];
203
237
  }
204
238
  export declare function partsByState(mock: Mock, posts: Post[]): StateParts[];
205
239
  export declare const mockSummaryView: (mock: Mock, posts: Post[]) => {
240
+ sessionId: string | null;
241
+ resolvedAt: string | null;
242
+ createdAt: string;
243
+ updatedAt: string;
244
+ thumbnail: {
245
+ postId: string;
246
+ surface: number;
247
+ version: number;
248
+ } | null;
249
+ drafted?: number | undefined;
206
250
  id: string;
207
251
  project: string;
208
252
  slug: string;
@@ -212,17 +256,9 @@ export declare const mockSummaryView: (mock: Mock, posts: Post[]) => {
212
256
  stateCount: number;
213
257
  variants: number;
214
258
  open: number;
215
- sessionId: string | null;
216
- createdAt: string;
217
- updatedAt: string;
218
- thumbnail: {
219
- postId: string;
220
- surface: number;
221
- version: number;
222
- } | null;
223
259
  };
224
260
  export declare const mockDetailView: (mock: Mock, posts: Post[], opts?: VariantViewOptions & {
225
- tuned?: Record<string, unknown>;
261
+ settled?: Record<string, KnobValue>;
226
262
  }) => {
227
263
  id: string;
228
264
  project: string;
@@ -233,11 +269,13 @@ export declare const mockDetailView: (mock: Mock, posts: Post[], opts?: VariantV
233
269
  asks: import("./types.ts").Ask[];
234
270
  open: number;
235
271
  knobs: import("./types.ts").Knobs;
272
+ widths: number[];
236
273
  sessionId: string | null;
274
+ resolvedAt: string | null;
237
275
  createdAt: string;
238
276
  updatedAt: string;
239
277
  variants: {
240
- history?: {
278
+ history?: ({
241
279
  surfaceKinds: ("html" | "diff" | "image" | "markdown" | "terminal" | "mermaid" | "json" | "code")[];
242
280
  author?: string | undefined;
243
281
  prompt?: string | undefined;
@@ -245,9 +283,18 @@ export declare const mockDetailView: (mock: Mock, posts: Post[], opts?: VariantV
245
283
  version: number;
246
284
  title: string;
247
285
  at: string;
248
- }[] | undefined;
286
+ } | {
287
+ hidden: true;
288
+ surfaceKinds: ("html" | "diff" | "image" | "markdown" | "terminal" | "mermaid" | "json" | "code")[];
289
+ author?: string | undefined;
290
+ prompt?: string | undefined;
291
+ from?: number | undefined;
292
+ version: number;
293
+ title: string;
294
+ at: string;
295
+ })[] | undefined;
249
296
  parts: string[];
250
- surfaces: (Record<string, unknown> | {
297
+ surfaces: ({
251
298
  index: number;
252
299
  kind: "html";
253
300
  html: string;
@@ -297,9 +344,21 @@ export declare const mockDetailView: (mock: Mock, posts: Post[], opts?: VariantV
297
344
  title?: string;
298
345
  lineStart?: number;
299
346
  id?: string;
300
- })[];
301
- slots?: import("./types.ts").Slot[] | undefined;
302
- knobs?: import("./types.ts").Knobs | undefined;
347
+ } | Record<string, unknown>)[];
348
+ acceptedVersion?: number | undefined;
349
+ starred?: boolean | undefined;
350
+ forkedFrom?: import("./types.ts").ForkRef | undefined;
351
+ publishedWith?: Embed[] | undefined;
352
+ embeds?: (Embed & {
353
+ latest: number;
354
+ versions: number[];
355
+ })[] | undefined;
356
+ embedsKey?: string | undefined;
357
+ note?: string | undefined;
358
+ slots?: import("./fills.ts").SlotInfo[] | undefined;
359
+ knobs?: {
360
+ [x: string]: import("./types.ts").KnobConfig;
361
+ } | undefined;
303
362
  author?: string | undefined;
304
363
  prompt?: string | undefined;
305
364
  from?: number | undefined;
@@ -313,8 +372,13 @@ export declare const mockDetailView: (mock: Mock, posts: Post[], opts?: VariantV
313
372
  createdAt: string;
314
373
  updatedAt: string;
315
374
  }[];
375
+ inUse: {
376
+ state: string | null;
377
+ variant: string;
378
+ version: number;
379
+ }[];
316
380
  parts: StateParts[];
317
- tuned: Record<string, unknown>;
381
+ settled: Record<string, KnobValue>;
318
382
  };
319
383
  export declare const sessionRowView: (session: Session, postCount: number) => {
320
384
  postCount: number;
@@ -326,4 +390,44 @@ export declare const sessionRowView: (session: Session, postCount: number) => {
326
390
  lastActiveAt: string;
327
391
  agentSeq: number;
328
392
  project: string | null;
393
+ agentActiveAt: string | null;
394
+ keyed: boolean;
395
+ };
396
+ export declare const treeNodeView: (mock: Mock, posts: Post[], extra: {
397
+ settled: Record<string, KnobValue>;
398
+ uses: string[];
399
+ embeds: Map<string, EmbedView>;
400
+ }) => {
401
+ id: string;
402
+ slug: string;
403
+ title: string;
404
+ kind: import("./types.ts").MockKind;
405
+ open: number;
406
+ states: string[] | null[];
407
+ widths: number[];
408
+ createdAt: string;
409
+ updatedAt: string;
410
+ resolvedAt: string | null;
411
+ settled: Record<string, KnobValue>;
412
+ uses: string[];
413
+ usedIn: string[];
414
+ inUse: {
415
+ state: string | null;
416
+ variant: string;
417
+ version: number;
418
+ }[];
419
+ variants: {
420
+ embedsKey?: string | undefined;
421
+ acceptedVersion?: number | undefined;
422
+ starred?: boolean | undefined;
423
+ forkedFrom?: import("./types.ts").ForkRef | undefined;
424
+ note?: string | undefined;
425
+ postId: string;
426
+ state: string | null;
427
+ variant: string;
428
+ status: import("./types.ts").PostStatus;
429
+ version: number;
430
+ updatedAt: string;
431
+ surface: number;
432
+ }[];
329
433
  };
@@ -1,7 +1,8 @@
1
1
  // Response shapes shared by every tier (REST, both MCP transports, the CLI), so
2
2
  // one mock reads the same however an agent asks for it.
3
+ import { slotKnobs, slotsInSurfaces } from "./fills.js";
3
4
  import { mergeParts, partsInSurfaces } from "./parts.js";
4
- import { isSandboxedSurfaceKind, openCount, SURFACE_CONTENT_FIELDS, } from "./types.js";
5
+ import { draftedCount, inUseVersion, isSandboxedSurfaceKind, isLive, openCount, SURFACE_CONTENT_FIELDS, } from "./types.js";
5
6
  export const surfaceRef = (surface, index) => ({
6
7
  id: surface.id,
7
8
  kind: surface.kind,
@@ -48,8 +49,21 @@ export const variantView = (post, opts = {}) => ({
48
49
  ...(post.from === undefined ? {} : { from: post.from }),
49
50
  ...(post.prompt ? { prompt: post.prompt } : {}),
50
51
  ...(post.author === undefined ? {} : { author: post.author }),
51
- ...(post.knobs ? { knobs: post.knobs } : {}),
52
- ...(post.slots.length ? { slots: post.slots } : {}),
52
+ // A slot is a select knob on its part (fills.ts); declared knobs win.
53
+ ...(slotsInSurfaces(post.surfaces).length || post.knobs
54
+ ? { knobs: { ...slotKnobs(slotsInSurfaces(post.surfaces)), ...post.knobs } }
55
+ : {}),
56
+ ...(slotsInSurfaces(post.surfaces).length ? { slots: slotsInSurfaces(post.surfaces) } : {}),
57
+ ...(post.note ? { note: post.note } : {}),
58
+ // Embedded components as they render now (server/embeds.ts); `publishedWith`
59
+ // is what this version rendered with when it was published.
60
+ ...(opts.embeds?.get(post.id)
61
+ ? { embeds: opts.embeds.get(post.id).list, embedsKey: opts.embeds.get(post.id).key }
62
+ : {}),
63
+ ...(post.embeds.length ? { publishedWith: post.embeds } : {}),
64
+ ...(post.forkedFrom ? { forkedFrom: post.forkedFrom } : {}),
65
+ ...(post.starred ? { starred: true } : {}),
66
+ ...(post.acceptedVersion === undefined ? {} : { acceptedVersion: post.acceptedVersion }),
53
67
  // The part names this variant's own markup marks, so the viewer can tell
54
68
  // which looks render a part (Mix) without the html bodies.
55
69
  parts: partsInSurfaces(post.surfaces).map((p) => p.name),
@@ -67,17 +81,23 @@ export const variantView = (post, opts = {}) => ({
67
81
  ...(post.author === undefined ? {} : { author: post.author }),
68
82
  }),
69
83
  ...[...post.history].sort((a, b) => b.version - a.version).map(historyMetaView),
70
- ],
84
+ ].map((row) => post.hiddenVersions?.includes(row.version) ? { ...row, hidden: true } : row),
71
85
  }
72
86
  : {}),
73
87
  });
74
88
  const stateOrder = (mock) => (mock.states.length ? mock.states : [null]);
89
+ // What the user put in use, per state, in the mock's order; a state with
90
+ // nothing accepted is absent. Export and embeds resolve to exactly these.
91
+ export const inUseByState = (mock, posts) => stateOrder(mock).flatMap((state) => {
92
+ const p = posts.find((x) => x.state === state && x.status === "accepted");
93
+ return p ? [{ state, variant: p.variant, version: inUseVersion(p) }] : [];
94
+ });
75
95
  // The parts the agent marked, per state, across the variants still in play.
76
96
  export function partsByState(mock, posts) {
77
97
  return stateOrder(mock).map((state) => ({
78
98
  state,
79
99
  parts: mergeParts(posts
80
- .filter((p) => p.state === state && p.status !== "archived")
100
+ .filter((p) => p.state === state && isLive(p.status))
81
101
  .map((p) => partsInSurfaces(p.surfaces))),
82
102
  }));
83
103
  }
@@ -92,23 +112,29 @@ function thumbnail(mock, posts) {
92
112
  if (!pick)
93
113
  return null;
94
114
  const surface = pick.surfaces.findIndex((s) => isSandboxedSurfaceKind(s.kind));
95
- return { postId: pick.id, surface: Math.max(surface, 0), version: pick.version };
115
+ return { postId: pick.id, surface: Math.max(surface, 0), version: inUseVersion(pick) };
96
116
  }
97
- export const mockSummaryView = (mock, posts) => ({
98
- id: mock.id,
99
- project: mock.project,
100
- slug: mock.slug,
101
- title: mock.title,
102
- kind: mock.kind,
103
- states: mock.states,
104
- stateCount: Math.max(mock.states.length, 1),
105
- variants: posts.length,
106
- open: openCount(mock, posts),
107
- sessionId: mock.sessionId,
108
- createdAt: mock.createdAt,
109
- updatedAt: mock.updatedAt,
110
- thumbnail: thumbnail(mock, posts),
111
- });
117
+ export const mockSummaryView = (mock, posts) => {
118
+ const drafted = draftedCount(mock, posts);
119
+ return {
120
+ id: mock.id,
121
+ project: mock.project,
122
+ slug: mock.slug,
123
+ title: mock.title,
124
+ kind: mock.kind,
125
+ states: mock.states,
126
+ stateCount: Math.max(mock.states.length, 1),
127
+ variants: posts.length,
128
+ open: openCount(mock, posts),
129
+ // Only once the user has started one, so a list with no drafts costs nothing.
130
+ ...(drafted === null ? {} : { drafted }),
131
+ sessionId: mock.sessionId,
132
+ resolvedAt: mock.resolvedAt,
133
+ createdAt: mock.createdAt,
134
+ updatedAt: mock.updatedAt,
135
+ thumbnail: thumbnail(mock, posts),
136
+ };
137
+ };
112
138
  // One mock as anyone outside the viewer's own draft sees it: the draft is the
113
139
  // user's unsent work and never leaves through this view.
114
140
  export const mockDetailView = (mock, posts, opts = {}) => ({
@@ -121,15 +147,52 @@ export const mockDetailView = (mock, posts, opts = {}) => ({
121
147
  asks: mock.asks,
122
148
  open: openCount(mock, posts),
123
149
  knobs: mock.knobs,
150
+ widths: mock.widths,
124
151
  sessionId: mock.sessionId,
152
+ resolvedAt: mock.resolvedAt,
125
153
  createdAt: mock.createdAt,
126
154
  updatedAt: mock.updatedAt,
127
155
  variants: posts.map((p) => variantView(p, opts)),
156
+ inUse: inUseByState(mock, posts),
128
157
  parts: partsByState(mock, posts),
129
- // The knob values of the latest reply — what the user last sent, not drafts.
130
- tuned: opts.tuned ?? {},
158
+ // Every knob value the user has sent (answers' sets and tuning, later replies
159
+ // winning), never drafts. Later questions render on top of it.
160
+ settled: opts.settled ?? {},
131
161
  });
132
162
  export const sessionRowView = (session, postCount) => ({
133
163
  ...session,
134
164
  postCount,
135
165
  });
166
+ // One mock as a node of the project tree (GET /api/projects/:name/tree): its
167
+ // variants per state with status and lineage, and the mocks it embeds (`uses`);
168
+ // `usedIn` is filled in once every node is known.
169
+ export const treeNodeView = (mock, posts, extra) => ({
170
+ id: mock.id,
171
+ slug: mock.slug,
172
+ title: mock.title,
173
+ kind: mock.kind,
174
+ open: openCount(mock, posts),
175
+ states: mock.states.length ? mock.states : [null],
176
+ widths: mock.widths,
177
+ createdAt: mock.createdAt,
178
+ updatedAt: mock.updatedAt,
179
+ resolvedAt: mock.resolvedAt,
180
+ settled: extra.settled,
181
+ uses: extra.uses,
182
+ usedIn: [],
183
+ inUse: inUseByState(mock, posts),
184
+ variants: posts.map((p) => ({
185
+ postId: p.id,
186
+ state: p.state,
187
+ variant: p.variant,
188
+ status: p.status,
189
+ version: p.version,
190
+ updatedAt: p.updatedAt,
191
+ surface: p.surfaces.findIndex((x) => isSandboxedSurfaceKind(x.kind)),
192
+ ...(p.note ? { note: p.note } : {}),
193
+ ...(p.forkedFrom ? { forkedFrom: p.forkedFrom } : {}),
194
+ ...(p.starred ? { starred: true } : {}),
195
+ ...(p.acceptedVersion === undefined ? {} : { acceptedVersion: p.acceptedVersion }),
196
+ ...(extra.embeds.get(p.id) ? { embedsKey: extra.embeds.get(p.id).key } : {}),
197
+ })),
198
+ });
@@ -21,6 +21,7 @@ export interface AppOptions {
21
21
  screenshots?: boolean;
22
22
  version?: string;
23
23
  upgradeCommand?: string;
24
+ renderRev?: string;
24
25
  fetchLatestRelease?: () => Promise<LatestRelease | null>;
25
26
  onEvent?: (event: FeedEvent) => void;
26
27
  maxHoldConnections?: number;
@@ -52,4 +53,4 @@ export interface FlowContext {
52
53
  request?: Request;
53
54
  signal?: AbortSignal;
54
55
  }
55
- export declare function createApp({ store, viewerHtml, setupText, topics, authenticate, authToken, basePath, publicRead, screenshots, version, upgradeCommand, fetchLatestRelease, onEvent, maxHoldConnections, executor, runLimits, log, }: AppOptions): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
56
+ export declare function createApp({ store, viewerHtml, setupText, topics, authenticate, authToken, basePath, publicRead, screenshots, version, upgradeCommand, renderRev, fetchLatestRelease, onEvent, maxHoldConnections, executor, runLimits, log, }: AppOptions): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;