@bud.sh/app 0.5.1 → 0.6.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 (115) hide show
  1. package/README.md +41 -31
  2. package/dist/client/assets/bud/design.js +1 -1
  3. package/dist/client/assets/canvas-Bih9EdKJ.js +1 -0
  4. package/dist/client/assets/difference-worker-Bzg-rizy.js +8 -0
  5. package/dist/client/assets/dist-CdE5wBkg.js +5 -0
  6. package/dist/client/assets/files-1dLuoM2f.js +1 -0
  7. package/dist/client/assets/i18n-CkO6VFI0.js +1 -0
  8. package/dist/client/assets/index-Cn2mxroU.css +2 -0
  9. package/dist/client/assets/{index-Dn2pfmXf.js → index-DtISWZp9.js} +2 -2
  10. package/dist/client/assets/pages-BJ-tNJ4J.js +1 -0
  11. package/dist/client/assets/{project-branches-CMGRmrqr.js → project-branches-Ce3rXrkc.js} +1 -1
  12. package/dist/client/assets/project-comments-CPUONfs8.js +1 -0
  13. package/dist/client/assets/project-thread-links-C-TGkq59.js +1 -0
  14. package/dist/client/assets/{project-threads-ZbKDh78d.js → project-threads-Bv7IDBgi.js} +105 -105
  15. package/dist/client/assets/project-work-DAE9-olg.js +1 -0
  16. package/dist/client/assets/{task-design-BPpc4Gd7.js → task-design-CwySIAOl.js} +3 -3
  17. package/dist/client/assets/tasks-KWsCLi_h.js +3 -0
  18. package/dist/client/assets/wasm-inline-DSUyFn1L.js +1 -0
  19. package/dist/client/assets/workspace-CVdcfaaB.js +2 -0
  20. package/dist/client/index.html +3 -3
  21. package/dist/hosted/assets/{canvas-BYTOp1p7.js → canvas-AII16n9i.js} +97 -6
  22. package/dist/hosted/assets/{comment-data-Cn5y2VxV.js → comment-data-BXfMElfx.js} +1998 -381
  23. package/dist/hosted/assets/dist-BCSTeNf4.js +4785 -0
  24. package/dist/hosted/assets/{files-B1P2_24q.js → files-D_1yeAVO.js} +10 -5
  25. package/dist/hosted/assets/{pages-B0ZlVdci.js → pages-CN_SbKWA.js} +3 -3
  26. package/dist/hosted/assets/{project-branches-DlCWmfRo.js → project-branches-Bxp0DCwg.js} +1 -1
  27. package/dist/hosted/assets/{project-thread-links-BZXQ200n.js → project-thread-links-KqQoKBLE.js} +76 -4
  28. package/dist/{node-host/assets/screen-DapOVrga.js → hosted/assets/screen-DpAcDUKR.js} +25 -1
  29. package/dist/hosted/assets/tasks-D66LlwxM.js +1423 -0
  30. package/dist/hosted/assets/wasm-inline-Dl9tqabx.js +4 -0
  31. package/dist/hosted/hosted.js +7885 -6219
  32. package/dist/node/assets/Stream-CAm1eJ2n.js +16604 -0
  33. package/dist/{server/assets/bridge-B_Blt55E.js → node/assets/bridge-DQSBYHmw.js} +124 -290
  34. package/dist/node/assets/config-9_jWwNBh.js +1067 -0
  35. package/dist/node/assets/load-config.server-CMVtRVmO.js +261 -0
  36. package/dist/node/assets/module-3GgtT4Xs.js +308 -0
  37. package/dist/node/assets/server-DTQFuoX2.js +846 -0
  38. package/dist/node/assets/src-D-kG1GxB.js +2 -0
  39. package/dist/node/assets/{src-CerrKjID.js → src-Dwe9APUx.js} +93 -3
  40. package/dist/node/assets/src-DysjfbM8.js +8592 -0
  41. package/dist/node/bridge.js +1 -845
  42. package/dist/node/cli.js +206 -0
  43. package/dist/node/design.js +811 -8
  44. package/dist/node/index.js +2 -739
  45. package/dist/node/plugins.js +5711 -0
  46. package/dist/node/server.js +1 -6746
  47. package/dist/node/vite.js +121 -0
  48. package/dist/{server/assets/canvas-CkMK4hoN.js → node-host/assets/canvas-BeDoufy1.js} +97 -7
  49. package/dist/node-host/assets/{comment-data-yfWEdDQL.js → comment-data-DE2T9yTj.js} +1974 -355
  50. package/dist/node-host/assets/dist-BCSTeNf4.js +4785 -0
  51. package/dist/node-host/assets/{files-anHi-apm.js → files-Dl-PTS09.js} +11 -6
  52. package/dist/node-host/assets/{pages-CW4aF6dK.js → pages-DL2sk6_U.js} +3 -3
  53. package/dist/node-host/assets/{project-branches-D7rVCxQX.js → project-branches-DGp5ELct.js} +1 -1
  54. package/dist/{server/assets/project-thread-links-lLS_rq7c.js → node-host/assets/project-thread-links-CgBV3Jcm.js} +76 -5
  55. package/dist/{hosted/assets/screen-CfdC3NbR.js → node-host/assets/screen-B55yyVfb.js} +25 -1
  56. package/dist/node-host/assets/tasks-DZRNkoxe.js +1425 -0
  57. package/dist/node-host/assets/wasm-inline-Dl9tqabx.js +4 -0
  58. package/dist/node-host/node-host.js +6127 -4438
  59. package/dist/server/assets/{branch-previews-ClkoJ0lM.js → branch-previews-DedrpnVR.js} +1555 -673
  60. package/dist/{node-host/assets/canvas-DxmcFZCh.js → server/assets/canvas-CJWQ-jjv.js} +98 -6
  61. package/dist/server/assets/{comment-data-NLeHG89b.js → comment-data-Rg42ToTW.js} +1660 -26
  62. package/dist/server/assets/dist-BCSTeNf4.js +4785 -0
  63. package/dist/server/assets/{events-iHpLGr3k.js → events-BH_rAu7O.js} +155 -20
  64. package/dist/server/assets/{files-CRdl489E.js → files-CPJqKeMo.js} +12 -7
  65. package/dist/server/assets/{listener-BAYn9Q-u.js → listener-D61O6PzF.js} +708 -12
  66. package/dist/server/assets/{pages-DXVlJlBC.js → pages-KJY_Ba92.js} +4 -4
  67. package/dist/server/assets/{project-branches-CwTsf6WQ.js → project-branches-qG9ACZvo.js} +2 -2
  68. package/dist/{node-host/assets/project-thread-links-m-OLpsD9.js → server/assets/project-thread-links-CqK0lqCF.js} +77 -4
  69. package/dist/{node-host/assets/tasks-request-B0aeNocI.js → server/assets/project-work-DoIwxWMm.js} +87 -2
  70. package/dist/server/assets/run-BJ8r5jSh.js +572 -0
  71. package/dist/server/assets/{screen-B1zH_1O3.js → screen-CBrHPFug.js} +26 -2
  72. package/dist/server/assets/{server-DxK-UB_Z.js → server-Dkbp5stG.js} +1521 -793
  73. package/dist/server/assets/{services-BRrgXYlT.js → services-BTfig8A8.js} +234 -997
  74. package/dist/server/assets/tasks-Bgu5Lg9Y.js +1427 -0
  75. package/dist/server/assets/wasm-inline-Dl9tqabx.js +4 -0
  76. package/dist/server/capture.js +1 -2
  77. package/dist/server/migration.js +569 -12
  78. package/dist/server/runtime.js +7 -5
  79. package/dist/server/server.js +3 -3
  80. package/dist/types/assets/{config-DIC2cf_S.d.ts → config-CvcYmE2s.d.ts} +188 -50
  81. package/dist/types/assets/{contract-CAT-XFNq.d.ts → contract-B7yuuqSn.d.ts} +44 -2
  82. package/dist/types/assets/{items-CaVkByHk.d.ts → items-BQdaVksf.d.ts} +12 -1
  83. package/dist/types/assets/memory-state-BBW-FAVA.d.ts +236 -0
  84. package/dist/types/assets/picker-Be5Gx4WR.d.ts +9 -0
  85. package/dist/types/assets/{task-design-xJOhRNWh.d.ts → task-design-BDHKFHpp.d.ts} +3 -1
  86. package/dist/types/bridge.d.ts +1 -1
  87. package/dist/types/companion.d.ts +3 -3
  88. package/dist/types/design.d.ts +2 -2
  89. package/dist/types/hosted.d.ts +3 -3
  90. package/dist/types/index.d.ts +2 -2
  91. package/dist/types/node-host.d.ts +2 -2
  92. package/dist/types/plugins.d.ts +445 -0
  93. package/dist/types/screen.d.ts +2 -2
  94. package/dist/types/server.d.ts +2 -228
  95. package/dist/types/vite.d.ts +16 -0
  96. package/package.json +19 -3
  97. package/dist/client/assets/canvas-8GXJIuDK.js +0 -1
  98. package/dist/client/assets/difference-worker-C99ychYw.js +0 -8
  99. package/dist/client/assets/files-DkWt1l9H.js +0 -1
  100. package/dist/client/assets/i18n-hH_Ba5QO.js +0 -1
  101. package/dist/client/assets/index-tpHYVXma.css +0 -2
  102. package/dist/client/assets/pages-BVd6MQmu.js +0 -1
  103. package/dist/client/assets/project-comments-D1IIQwsW.js +0 -1
  104. package/dist/client/assets/project-thread-links-DKwe_SYb.js +0 -1
  105. package/dist/client/assets/project-work-D4DZqBCZ.js +0 -1
  106. package/dist/client/assets/tasks-Cn0VOZ9Z.js +0 -1
  107. package/dist/client/assets/tasks-request-BLyOzokZ.js +0 -1
  108. package/dist/client/assets/workspace-DGErsSg6.js +0 -2
  109. package/dist/hosted/assets/tasks-_yKh2Z6q.js +0 -649
  110. package/dist/hosted/assets/tasks-request-CFPLvLc7.js +0 -113
  111. package/dist/node-host/assets/tasks-BBK_CybR.js +0 -651
  112. package/dist/server/assets/project-work-CSZlCwH-.js +0 -88
  113. package/dist/server/assets/tasks-CGdz-uox.js +0 -652
  114. package/dist/server/assets/tasks-request-D2NBjCTl.js +0 -115
  115. package/dist/types/assets/picker-D1Bw3ndx.d.ts +0 -14
@@ -0,0 +1,572 @@
1
+ import { rn as parseElementAnchor } from "./events-BH_rAu7O.js";
2
+ //#region app/task-context.ts
3
+ var hash = /^[a-f0-9]{64}$/;
4
+ function object(value) {
5
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
6
+ }
7
+ function point(value) {
8
+ let input = object(value);
9
+ if (!input) return null;
10
+ let { x, y } = input;
11
+ return typeof x === "number" && typeof y === "number" && Number.isFinite(x) && Number.isFinite(y) ? {
12
+ x,
13
+ y
14
+ } : null;
15
+ }
16
+ /**
17
+ * Validates a stored task context.
18
+ * @param value - Untrusted stored data.
19
+ * @returns The context, or null when there is none or it can't be read.
20
+ */
21
+ function parseTaskContext(value) {
22
+ let input = object(value);
23
+ if (!input || input.version !== 1 || typeof input.thread !== "string") return null;
24
+ let page = object(input.page);
25
+ let code = object(input.code);
26
+ let element = null;
27
+ try {
28
+ element = input.element ? parseElementAnchor(input.element) : null;
29
+ } catch {}
30
+ return {
31
+ version: 1,
32
+ thread: input.thread.slice(0, 200),
33
+ page: page && typeof page.id === "string" && typeof page.path === "string" ? {
34
+ id: page.id,
35
+ path: page.path,
36
+ title: typeof page.title === "string" ? page.title : page.path
37
+ } : null,
38
+ viewport: typeof input.viewport === "number" ? input.viewport : null,
39
+ branch: typeof input.branch === "string" ? input.branch : null,
40
+ point: point(input.point),
41
+ element,
42
+ image: typeof input.image === "string" && hash.test(input.image) ? input.image : null,
43
+ code: code && typeof code.path === "string" && typeof code.startLine === "number" && typeof code.endLine === "number" ? {
44
+ path: code.path,
45
+ startLine: code.startLine,
46
+ endLine: code.endLine,
47
+ commit: typeof code.commit === "string" ? code.commit : null
48
+ } : null
49
+ };
50
+ }
51
+ /**
52
+ * Validates what a browser sends with Make task.
53
+ * @param value - The request's `context`.
54
+ * @returns The screenshot's bytes, the element and the point, each when given and valid.
55
+ */
56
+ function parseTaskContextInput(value) {
57
+ let input = object(value);
58
+ if (!input) return {
59
+ image: null,
60
+ element: null,
61
+ point: null
62
+ };
63
+ let image = null;
64
+ if (typeof input.image === "string" && input.image.length <= 3e6) try {
65
+ image = Uint8Array.from(atob(input.image), (character) => character.charCodeAt(0));
66
+ } catch {}
67
+ let element = null;
68
+ try {
69
+ element = input.element ? parseElementAnchor(input.element) : null;
70
+ } catch {}
71
+ return {
72
+ image,
73
+ element,
74
+ point: point(input.point)
75
+ };
76
+ }
77
+ //#endregion
78
+ //#region ../factory/src/lib/run.ts
79
+ /** Every run state, in the order a run passes them. */
80
+ var RUN_STATES = [
81
+ "provisioning",
82
+ "working",
83
+ "review",
84
+ "needs_context",
85
+ "changes",
86
+ "approved",
87
+ "pushing",
88
+ "pushed",
89
+ "merged",
90
+ "failed",
91
+ "discarded"
92
+ ];
93
+ /** States in which a run waits for people, and its sandbox sleeps after a while. */
94
+ var RUN_WAITING_STATES = [
95
+ "review",
96
+ "needs_context",
97
+ "failed"
98
+ ];
99
+ /** The line an agent ends its reply with to ask a person for what it needs. */
100
+ var NEEDS_CONTEXT_MARKER = "NEEDS CONTEXT:";
101
+ /**
102
+ * The question an agent's reply asks a person, from its `NEEDS CONTEXT:` line.
103
+ * @param reply - The agent's last reply.
104
+ * @returns The question, or null when the agent didn't ask one.
105
+ */
106
+ function askedQuestion(reply) {
107
+ let lines = reply.split("\n");
108
+ for (let index = lines.length - 1; index >= 0; index--) {
109
+ let line = lines[index].trim().replace(/^[*_`>\s]+/, "");
110
+ if (line.toUpperCase().startsWith("NEEDS CONTEXT:")) {
111
+ let question = [line.slice(14), ...lines.slice(index + 1)].join("\n").replace(/^[*_`\s]+|[*_`\s]+$/g, "");
112
+ return question ? question.slice(0, 2e3) : null;
113
+ }
114
+ }
115
+ return null;
116
+ }
117
+ /** States a run can't leave; a new run starts the task again. */
118
+ var ENDED_RUN_STATES = [
119
+ "pushed",
120
+ "merged",
121
+ "discarded"
122
+ ];
123
+ /** A step the run's state doesn't allow, such as approving a run that is still working. */
124
+ var RunTransitionError = class extends Error {
125
+ state;
126
+ event;
127
+ constructor(state, event) {
128
+ super(`A run can't ${event} while it is ${state ?? "not started"}.`);
129
+ this.state = state;
130
+ this.event = event;
131
+ this.name = "RunTransitionError";
132
+ }
133
+ };
134
+ var MAX_ACTIVITY = 50;
135
+ var MAX_ACTIVITY_TEXT = 500;
136
+ var MAX_PREVIOUS = 10;
137
+ var MAX_SUMMARY = 1e4;
138
+ /**
139
+ * The branch a task's run works on: the prefix, the task id and a short slug of its title.
140
+ * @param prefix - Such as `dev/`.
141
+ * @param taskId - The task's id.
142
+ * @param title - The task's title.
143
+ * @returns Such as `dev/fw-123-make-the-heading-larger`.
144
+ */
145
+ function runBranch(prefix, taskId, title) {
146
+ let slug = title.normalize("NFKD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40).replace(/-+$/, "");
147
+ return `${prefix}${taskId.toLowerCase().replace(/[^a-z0-9._-]+/g, "-")}${slug ? `-${slug}` : ""}`;
148
+ }
149
+ /**
150
+ * Whether a run is still going: started and neither pushed nor discarded.
151
+ * @param run - The task's run, if any.
152
+ * @returns Whether it is active.
153
+ */
154
+ function runActive(run) {
155
+ return Boolean(run && !ENDED_RUN_STATES.includes(run.state));
156
+ }
157
+ function withActivity(run, entry, at) {
158
+ let line = {
159
+ at,
160
+ kind: entry.kind,
161
+ text: entry.text.slice(0, MAX_ACTIVITY_TEXT)
162
+ };
163
+ return {
164
+ ...run,
165
+ activity: [...run.activity, line].slice(-50)
166
+ };
167
+ }
168
+ function refuse(run, event) {
169
+ throw new RunTransitionError(run?.state ?? null, event);
170
+ }
171
+ /**
172
+ * Starts a task's run, keeping a short note of the run before it.
173
+ * @param previous - The task's last run, which must have ended or failed.
174
+ * @param start - What the run starts from.
175
+ * @param at - The time.
176
+ * @returns The new run, the task's status and the sandbox to set up.
177
+ */
178
+ function startRun(previous, start, at) {
179
+ if (previous && runActive(previous) && previous.state !== "failed") refuse(previous, "start");
180
+ let earlier = previous ? [{
181
+ id: previous.id,
182
+ state: previous.state,
183
+ endedAt: at
184
+ }, ...previous.previous] : [];
185
+ let run = {
186
+ version: 1,
187
+ id: start.id,
188
+ state: "provisioning",
189
+ failedIn: null,
190
+ provider: start.provider,
191
+ sandbox: null,
192
+ harness: start.harness,
193
+ model: start.model,
194
+ session: null,
195
+ ports: {
196
+ dev: null,
197
+ agent: null
198
+ },
199
+ asleep: false,
200
+ branch: runBranch(start.branchPrefix, start.taskId, start.title),
201
+ base: {
202
+ branch: start.base,
203
+ commit: null
204
+ },
205
+ head: null,
206
+ pushed: null,
207
+ summary: null,
208
+ question: null,
209
+ activity: [],
210
+ error: null,
211
+ startedBy: start.by,
212
+ approvedBy: null,
213
+ createdAt: at,
214
+ updatedAt: at,
215
+ previous: earlier.slice(0, MAX_PREVIOUS)
216
+ };
217
+ let effects = previous?.state === "failed" ? [{ type: "destroy" }] : [];
218
+ return {
219
+ run: withActivity(run, {
220
+ kind: "status",
221
+ text: "Started"
222
+ }, at),
223
+ status: "inProgress",
224
+ effects: [...effects, { type: "provision" }]
225
+ };
226
+ }
227
+ /**
228
+ * The next step of a run: the state machine of the factory.
229
+ * @param run - The run as stored.
230
+ * @param event - What happened.
231
+ * @param at - The time.
232
+ * @returns The run to save, the task status that follows and what Bud does next.
233
+ */
234
+ function advanceRun(run, event, at) {
235
+ let next = {
236
+ ...run,
237
+ updatedAt: at
238
+ };
239
+ switch (event.type) {
240
+ case "ready":
241
+ if (run.state !== "provisioning") refuse(run, "become ready");
242
+ return {
243
+ run: withActivity({
244
+ ...next,
245
+ state: "working",
246
+ sandbox: event.sandbox,
247
+ session: event.session,
248
+ ports: event.ports,
249
+ base: {
250
+ ...run.base,
251
+ commit: event.commit
252
+ }
253
+ }, {
254
+ kind: "status",
255
+ text: "Agent working"
256
+ }, at),
257
+ effects: []
258
+ };
259
+ case "created":
260
+ if (run.state !== "provisioning") refuse(run, "record its sandbox");
261
+ return {
262
+ run: {
263
+ ...next,
264
+ sandbox: event.sandbox
265
+ },
266
+ effects: []
267
+ };
268
+ case "activity":
269
+ if (![
270
+ "provisioning",
271
+ "working",
272
+ "changes"
273
+ ].includes(run.state)) refuse(run, "report activity");
274
+ return {
275
+ run: withActivity(next, event.entry, at),
276
+ effects: []
277
+ };
278
+ case "idle": {
279
+ if (run.state !== "working" && run.state !== "changes") refuse(run, "finish");
280
+ let question = askedQuestion(event.summary);
281
+ if (question) return {
282
+ run: withActivity({
283
+ ...next,
284
+ state: "needs_context",
285
+ question,
286
+ summary: event.summary.slice(0, MAX_SUMMARY),
287
+ head: event.head ?? run.head
288
+ }, {
289
+ kind: "status",
290
+ text: "Needs context"
291
+ }, at),
292
+ effects: [{ type: "doze" }]
293
+ };
294
+ return {
295
+ run: withActivity({
296
+ ...next,
297
+ state: "review",
298
+ question: null,
299
+ summary: event.summary.slice(0, MAX_SUMMARY),
300
+ head: event.head ?? run.head
301
+ }, {
302
+ kind: "status",
303
+ text: "Needs approval"
304
+ }, at),
305
+ status: "needsApproval",
306
+ effects: [{ type: "doze" }]
307
+ };
308
+ }
309
+ case "answer":
310
+ if (run.state !== "needs_context") refuse(run, "take an answer");
311
+ return {
312
+ run: withActivity({
313
+ ...next,
314
+ state: "changes",
315
+ question: null
316
+ }, {
317
+ kind: "message",
318
+ text: `${event.by.name}: ${event.message}`
319
+ }, at),
320
+ effects: [{
321
+ type: "send",
322
+ message: event.message
323
+ }]
324
+ };
325
+ case "changes":
326
+ if (run.state !== "review") refuse(run, "take changes");
327
+ return {
328
+ run: withActivity({
329
+ ...next,
330
+ state: "changes"
331
+ }, {
332
+ kind: "message",
333
+ text: `${event.by.name}: ${event.message}`
334
+ }, at),
335
+ status: "inProgress",
336
+ effects: [{
337
+ type: "send",
338
+ message: event.message
339
+ }]
340
+ };
341
+ case "approve":
342
+ if (run.state !== "review") refuse(run, "be approved");
343
+ return {
344
+ run: withActivity({
345
+ ...next,
346
+ state: "pushing",
347
+ approvedBy: event.by
348
+ }, {
349
+ kind: "status",
350
+ text: `Approved by ${event.by.name}`
351
+ }, at),
352
+ effects: [{ type: "push" }]
353
+ };
354
+ case "pushed":
355
+ if (run.state !== "pushing" && run.state !== "approved") refuse(run, "be pushed");
356
+ return {
357
+ run: withActivity({
358
+ ...next,
359
+ state: "pushed",
360
+ pushed: event.commit,
361
+ error: null,
362
+ asleep: true
363
+ }, {
364
+ kind: "status",
365
+ text: `Pushed ${run.branch}`
366
+ }, at),
367
+ status: "readyToMerge",
368
+ effects: [{ type: "stop" }]
369
+ };
370
+ case "merged":
371
+ if (run.state !== "pushed") refuse(run, "be merged");
372
+ return {
373
+ run: withActivity({
374
+ ...next,
375
+ state: "merged"
376
+ }, {
377
+ kind: "status",
378
+ text: `Merged ${run.branch}`
379
+ }, at),
380
+ status: "merged",
381
+ effects: [{ type: "destroy" }]
382
+ };
383
+ case "discard":
384
+ if (![
385
+ "review",
386
+ "needs_context",
387
+ "failed",
388
+ "provisioning",
389
+ "working",
390
+ "changes"
391
+ ].includes(run.state)) refuse(run, "be discarded");
392
+ return {
393
+ run: withActivity({
394
+ ...next,
395
+ state: "discarded"
396
+ }, {
397
+ kind: "status",
398
+ text: "Discarded"
399
+ }, at),
400
+ status: "open",
401
+ effects: [{ type: "destroy" }]
402
+ };
403
+ case "fail":
404
+ if (ENDED_RUN_STATES.includes(run.state) || run.state === "failed") refuse(run, "fail");
405
+ return {
406
+ run: withActivity({
407
+ ...next,
408
+ state: "failed",
409
+ failedIn: run.state,
410
+ error: {
411
+ message: event.message.slice(0, MAX_ACTIVITY_TEXT),
412
+ at,
413
+ retryable: event.retryable
414
+ }
415
+ }, {
416
+ kind: "error",
417
+ text: event.message
418
+ }, at),
419
+ effects: run.sandbox && !run.asleep ? [{ type: "doze" }] : []
420
+ };
421
+ case "sleep":
422
+ if (run.asleep || !RUN_WAITING_STATES.includes(run.state)) refuse(run, "sleep");
423
+ return {
424
+ run: withActivity({
425
+ ...next,
426
+ asleep: true
427
+ }, {
428
+ kind: "status",
429
+ text: "Sandbox asleep"
430
+ }, at),
431
+ effects: [{ type: "stop" }]
432
+ };
433
+ case "wake":
434
+ if (!run.asleep || ENDED_RUN_STATES.includes(run.state)) refuse(run, "wake");
435
+ return {
436
+ run: withActivity({
437
+ ...next,
438
+ asleep: false
439
+ }, {
440
+ kind: "status",
441
+ text: "Sandbox awake"
442
+ }, at),
443
+ effects: []
444
+ };
445
+ case "retry": {
446
+ if (run.state !== "failed" || !run.failedIn || !run.error?.retryable) refuse(run, "be retried");
447
+ let state = run.failedIn;
448
+ return {
449
+ run: withActivity({
450
+ ...next,
451
+ state,
452
+ failedIn: null,
453
+ error: null
454
+ }, {
455
+ kind: "status",
456
+ text: "Retrying"
457
+ }, at),
458
+ effects: [...state === "provisioning" && run.sandbox ? [{ type: "destroy" }] : [], {
459
+ type: "resume",
460
+ state
461
+ }]
462
+ };
463
+ }
464
+ }
465
+ }
466
+ function isObject(value) {
467
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
468
+ }
469
+ function text(value, field, max, nullable = false) {
470
+ if (nullable && value === null) return value;
471
+ if (typeof value !== "string" || value.length > max) throw new TypeError(`Invalid run ${field}.`);
472
+ return value;
473
+ }
474
+ function state(value, field) {
475
+ if (!RUN_STATES.includes(value)) throw new TypeError(`Invalid run ${field}.`);
476
+ return value;
477
+ }
478
+ function port(value) {
479
+ if (value === null || value === void 0) return null;
480
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > 65535) throw new TypeError("Invalid run port.");
481
+ return value;
482
+ }
483
+ function ports(value) {
484
+ if (value === void 0) return {
485
+ dev: null,
486
+ agent: null
487
+ };
488
+ if (!isObject(value)) throw new TypeError("Invalid run ports.");
489
+ let terminal = port(value.terminal);
490
+ let preview = port(value.preview);
491
+ return {
492
+ dev: port(value.dev),
493
+ agent: port(value.agent),
494
+ ...terminal ? { terminal } : {},
495
+ ...preview ? { preview } : {}
496
+ };
497
+ }
498
+ function person(value, field) {
499
+ if (!isObject(value)) throw new TypeError(`Invalid run ${field}.`);
500
+ return {
501
+ name: text(value.name, field, 1e3),
502
+ email: text(value.email, field, 1e3)
503
+ };
504
+ }
505
+ /**
506
+ * Validates a run record read from a task's metadata.
507
+ * @param value - Untrusted stored data.
508
+ * @returns The run.
509
+ */
510
+ function parseRunRecord(value) {
511
+ if (!isObject(value) || value.version !== 1) throw new TypeError("Invalid run.");
512
+ let base = value.base;
513
+ if (!isObject(base)) throw new TypeError("Invalid run base.");
514
+ let error = value.error;
515
+ if (error !== null && !isObject(error)) throw new TypeError("Invalid run error.");
516
+ if (!Array.isArray(value.activity) || value.activity.length > MAX_ACTIVITY) throw new TypeError("Invalid run activity.");
517
+ if (!Array.isArray(value.previous) || value.previous.length > MAX_PREVIOUS) throw new TypeError("Invalid earlier runs.");
518
+ return {
519
+ version: 1,
520
+ id: text(value.id, "id", 100),
521
+ state: state(value.state, "state"),
522
+ failedIn: value.failedIn === null ? null : state(value.failedIn, "failedIn"),
523
+ provider: text(value.provider, "provider", 200),
524
+ sandbox: text(value.sandbox, "sandbox", 500, true),
525
+ harness: text(value.harness, "harness", 200),
526
+ model: text(value.model, "model", 200),
527
+ session: text(value.session, "session", 500, true),
528
+ ports: ports(value.ports),
529
+ asleep: value.asleep === true,
530
+ branch: text(value.branch, "branch", 300),
531
+ base: {
532
+ branch: text(base.branch, "base", 300),
533
+ commit: text(base.commit, "base", 100, true)
534
+ },
535
+ head: text(value.head, "head", 100, true),
536
+ pushed: text(value.pushed, "pushed", 100, true),
537
+ summary: text(value.summary, "summary", MAX_SUMMARY, true),
538
+ question: value.question === void 0 ? null : text(value.question, "question", 2e3, true),
539
+ activity: value.activity.map((entry) => {
540
+ if (!isObject(entry) || ![
541
+ "status",
542
+ "message",
543
+ "tool",
544
+ "error"
545
+ ].includes(entry.kind)) throw new TypeError("Invalid run activity.");
546
+ return {
547
+ at: text(entry.at, "activity", 64),
548
+ kind: entry.kind,
549
+ text: text(entry.text, "activity", MAX_ACTIVITY_TEXT)
550
+ };
551
+ }),
552
+ error: error === null ? null : {
553
+ message: text(error.message, "error", MAX_ACTIVITY_TEXT),
554
+ at: text(error.at, "error", 64),
555
+ retryable: error.retryable === true
556
+ },
557
+ startedBy: person(value.startedBy, "startedBy"),
558
+ approvedBy: value.approvedBy === null ? null : person(value.approvedBy, "approvedBy"),
559
+ createdAt: text(value.createdAt, "createdAt", 64),
560
+ updatedAt: text(value.updatedAt, "updatedAt", 64),
561
+ previous: value.previous.map((entry) => {
562
+ if (!isObject(entry)) throw new TypeError("Invalid earlier run.");
563
+ return {
564
+ id: text(entry.id, "previous", 100),
565
+ state: state(entry.state, "previous"),
566
+ endedAt: text(entry.endedAt, "previous", 64)
567
+ };
568
+ })
569
+ };
570
+ }
571
+ //#endregion
572
+ export { parseRunRecord as a, parseTaskContext as c, advanceRun as i, parseTaskContextInput as l, RUN_WAITING_STATES as n, runActive as o, RunTransitionError as r, startRun as s, NEEDS_CONTEXT_MARKER as t };
@@ -1,5 +1,5 @@
1
- import { F as parseThreadId, P as parseThreadFilter, b as predictThreadReply, i as routes, j as parseThread, v as predictThreadCreate, x as predictThreadStatus, y as predictThreadDelete, z as parseThreadSnapshot } from "./events-iHpLGr3k.js";
2
- import { En as t, _ as requestComments, a as saveThreads, b as writeOptimistically, bn as toast, g as CommentsRequestError, i as saveThread, l as projectStore, n as unavailableComments, o as threadMatches, x as writeRows, y as plainRow } from "./comment-data-NLeHG89b.js";
1
+ import { N as parseThreadFilter, P as parseThreadId, R as parseThreadSnapshot, b as predictThreadReply, i as routes, j as parseThread, v as predictThreadCreate, x as predictThreadStatus, y as predictThreadDelete } from "./events-BH_rAu7O.js";
2
+ import { Dn as toast, Nn as t, _ as requestComments, a as saveThreads, b as writeOptimistically, g as CommentsRequestError, i as saveThread, l as projectStore, n as unavailableComments, o as threadMatches, x as writeRows, y as plainRow } from "./comment-data-Rg42ToTW.js";
3
3
  //#region app/client/project-comments.ts
4
4
  var viewers = /* @__PURE__ */ new Map();
5
5
  function parseViewer(value) {
@@ -11,6 +11,13 @@ function parseViewer(value) {
11
11
  };
12
12
  throw new TypeError("Invalid viewer.");
13
13
  }
14
+ function parseMadeTask(value) {
15
+ if (!value || typeof value !== "object" || !("item" in value) || !("thread" in value) || !value.item || typeof value.item !== "object" || !("id" in value.item) || typeof value.item.id !== "string" || !value.item.id || value.item.id.length > 256) throw new TypeError("Invalid task.");
16
+ return {
17
+ item: { id: value.item.id },
18
+ thread: parseThread(value.thread)
19
+ };
20
+ }
14
21
  function parseDeletion(value) {
15
22
  if (!value || typeof value !== "object" || !("thread" in value)) throw new TypeError("Invalid deletion.");
16
23
  return { thread: value.thread === null ? null : parseThread(value.thread) };
@@ -386,6 +393,23 @@ function createProjectComments(options) {
386
393
  };
387
394
  },
388
395
  /**
396
+ * Turns a discussion into a task in the task store, linked to it.
397
+ * @param threadId - The discussion.
398
+ * @param context - What the canvas found around the comment: a screenshot and its element.
399
+ * @returns The new task's id.
400
+ */
401
+ async makeTask(threadId, context) {
402
+ requireCapability("link");
403
+ let id = parseThreadId(threadId);
404
+ let made;
405
+ await operate(routes.comments.makeTask.href({ id }), context ? { context } : {}, parseMadeTask, (result) => {
406
+ if (result.thread.id !== id) throw new CommentsRequestError(t("app.client.projectComments.refreshBeforeTryingAgain"), 409, "uncertain");
407
+ made = result.item;
408
+ return merge(result.thread);
409
+ }, t("app.client.projectComments.taskCreated"), id);
410
+ return made;
411
+ },
412
+ /**
389
413
  * Deletes one of the viewer's messages; its first message deletes the whole discussion.
390
414
  * @param threadId - The discussion.
391
415
  * @param messageId - The message to delete.