@bud.sh/app 0.5.0 → 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-ChANGtl-.js → index-DtISWZp9.js} +2 -2
  10. package/dist/client/assets/pages-BJ-tNJ4J.js +1 -0
  11. package/dist/client/assets/{project-branches-DO897zM6.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-Lay351p4.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-Avb2RO4w.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-V36732ER.js → canvas-AII16n9i.js} +97 -6
  22. package/dist/hosted/assets/{comment-data-CF2ZV_3k.js → comment-data-BXfMElfx.js} +2004 -402
  23. package/dist/hosted/assets/dist-BCSTeNf4.js +4785 -0
  24. package/dist/hosted/assets/{files-DjSCjfU7.js → files-D_1yeAVO.js} +10 -5
  25. package/dist/hosted/assets/{pages-BzjzeuPu.js → pages-CN_SbKWA.js} +3 -3
  26. package/dist/hosted/assets/{project-branches-BT97J9i7.js → project-branches-Bxp0DCwg.js} +1 -1
  27. package/dist/hosted/assets/{project-thread-links-BRLgByEe.js → project-thread-links-KqQoKBLE.js} +76 -4
  28. package/dist/{node-host/assets/screen-CZXWJEds.js → hosted/assets/screen-DpAcDUKR.js} +29 -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 -13
  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-Dc1mYCwQ.js → node-host/assets/canvas-BeDoufy1.js} +97 -7
  49. package/dist/node-host/assets/{comment-data-DyIZNpfG.js → comment-data-DE2T9yTj.js} +1980 -376
  50. package/dist/node-host/assets/dist-BCSTeNf4.js +4785 -0
  51. package/dist/node-host/assets/{files-Dh2TiQZq.js → files-Dl-PTS09.js} +11 -6
  52. package/dist/node-host/assets/{pages-BxpjbYhs.js → pages-DL2sk6_U.js} +3 -3
  53. package/dist/node-host/assets/{project-branches-B7HUGiVK.js → project-branches-DGp5ELct.js} +1 -1
  54. package/dist/{server/assets/project-thread-links-CBOpf6zk.js → node-host/assets/project-thread-links-CgBV3Jcm.js} +76 -5
  55. package/dist/{hosted/assets/screen-CKp2C7kx.js → node-host/assets/screen-B55yyVfb.js} +29 -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-TRUfxfL5.js → server/assets/canvas-CJWQ-jjv.js} +98 -6
  61. package/dist/server/assets/{comment-data-CsjukwAW.js → comment-data-Rg42ToTW.js} +1666 -47
  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-E9mSFeoc.js → files-CPJqKeMo.js} +12 -7
  65. package/dist/server/assets/{listener-DN5TyOjJ.js → listener-D61O6PzF.js} +708 -12
  66. package/dist/server/assets/{pages-BBBXyJWV.js → pages-KJY_Ba92.js} +4 -4
  67. package/dist/server/assets/{project-branches-B0YIK6Ur.js → project-branches-qG9ACZvo.js} +2 -2
  68. package/dist/{node-host/assets/project-thread-links-CauJF0Cz.js → server/assets/project-thread-links-CqK0lqCF.js} +77 -4
  69. package/dist/{node-host/assets/tasks-request-ZWmbc8fE.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-BIQA2mr4.js → screen-CBrHPFug.js} +30 -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-Cu6R6xxT.js +0 -1
  98. package/dist/client/assets/difference-worker-jfTSn-Tw.js +0 -8
  99. package/dist/client/assets/files-js6ElfkU.js +0 -1
  100. package/dist/client/assets/i18n-DxqFr2mi.js +0 -1
  101. package/dist/client/assets/index-CFFxP0gS.css +0 -2
  102. package/dist/client/assets/pages-6_lhiQmz.js +0 -1
  103. package/dist/client/assets/project-comments-DFFKVpia.js +0 -1
  104. package/dist/client/assets/project-thread-links-NLFWQRD2.js +0 -1
  105. package/dist/client/assets/project-work-du0Y-L5Y.js +0 -1
  106. package/dist/client/assets/tasks-CA-E8bbK.js +0 -1
  107. package/dist/client/assets/tasks-request-C55IeuiP.js +0 -1
  108. package/dist/client/assets/workspace-Cw5HvX5d.js +0 -2
  109. package/dist/hosted/assets/tasks-Dj7AyBJB.js +0 -649
  110. package/dist/hosted/assets/tasks-request-DZob623r.js +0 -113
  111. package/dist/node-host/assets/tasks-Bs1IOpeU.js +0 -651
  112. package/dist/server/assets/project-work-qSKFO5yj.js +0 -88
  113. package/dist/server/assets/tasks-CiXodsLN.js +0 -652
  114. package/dist/server/assets/tasks-request-DhwCzpRi.js +0 -115
  115. package/dist/types/assets/picker-D1Bw3ndx.d.ts +0 -14
@@ -1,10 +1,11 @@
1
- import { Dn as tryPromise, Dt as callback, Ft as fail$1, Gt as map, Kt as mapError, Lt as flatMap, Mn as void_, Nt as die, On as try_, Pt as ensuring, Sn as sync, St as andThen, Ut as gen, Xt as orElseSucceed, Zt as promise, g as resolveScreenRoutes, gn as scoped, gr as TaggedError, h as defaultScreens, qt as match, s as Previews, t as BranchPreviews, u as Git, v as themeStyle, vt as acquireRelease, y as LOGO_TYPES } from "./branch-previews-ClkoJ0lM.js";
2
- import { C as selected$1, I as WorkError, L as ScreenModules, O as AdapterRegistry, S as ready, T as LiveChannel, V as taskDesignModuleId, a as FileMutations, b as read, c as FileReview, f as Files, g as StoreActivity, h as ReviewActionError, m as FilesInputError, r as FileSaves, t as FileReviewActions, u as FileSearch, w as validate, x as readWorkspace, y as inspect } from "./server-DxK-UB_Z.js";
3
- import { At as parseReviewSourceQuery, Ct as parseReviewDiffQuery, F as parseThreadId, Ft as parseFileSearchQuery, I as parseThreadInput, Kt as parseFileIndexQuery, L as parseThreadLinkInput, N as parseThreadDeleteInput, On as parseMessageAuthor, P as parseThreadFilter, R as parseThreadReplyInput, Sn as parseWorkUpdate, Tt as parseReviewHistoryQuery, V as parseThreadStatusInput, Xt as parseFilePath, _ as taskRoutes, bn as parseWorkSync, cn as parseSectionData, dt as maxFileSaveBytes, fn as parseWorkCreate, ft as parseMutationApply, g as pageRoutes, gn as parseWorkMutationResult, h as fileRoutes, hn as parseWorkItemRef, i as routes, in as parseElementAnchor, it as parseReviewActionIntent, j as parseThread, ln as parseWorkActionInput, lt as parseFileSaveRequest, pn as parseWorkDetail, pt as parseMutationIntent, rn as MAX_CANVAS_Y, rt as parseReviewActionApply, vn as parseWorkQuery, xn as parseWorkSyncResult, xt as parseReviewComparison, z as parseThreadSnapshot } from "./events-iHpLGr3k.js";
4
- import { $ as openShellTab, B as parseTaskDesign, Bt as Button, Cn as hasMessage, Dn as useLocaleSource, En as t, J as FieldCardContent, On as Text, Q as initialShellTabs, Rt as Loader, Sn as writePreference, Tn as locale, X as activeTab, Y as FieldCardHeader, Z as closeShellTab, _ as requestComments, _n as LiveStatus, _t as taskHref, at as tabsCookieValue, et as parseShellTabs, gt as selectedTask, ht as readConversationLocation, it as tabsCookieName, lt as conversationDestination, mn as ShellTabBar, n as unavailableComments, nt as stepShellTab, ot as visitShellTab, pt as legacyConversationHref, q as FieldCard, r as followThreads, rt as tabHref, t as commentsUnavailable, tt as readTabsCookie, v as projectLive, wn as isLocale, xn as readPreference, xt as legacyFileHref, yn as AppShell } from "./comment-data-CsjukwAW.js";
1
+ import { $t as map, At as andThen, C as themeStyle, Cn as scoped, Cr as TaggedError, E as LOGO_TYPES, En as succeed, Et as acquireRelease, Fn as try_, Ft as callback, Gt as flatMap, Ht as ensuring, Pn as tryPromise, Qt as ignore, Rt as catch_, S as resolveScreenRoutes, T as FACTORY_STATUS_ROLES, Ut as fail$1, Vt as die, Zt as gen, an as promise, en as mapError, in as orElseSucceed, kn as sync, m as Git, qt as forEach, s as Previews, t as BranchPreviews, tn as match, w as FACTORY_STATUS_CATEGORIES, x as defaultScreens, zn as void_ } from "./branch-previews-DedrpnVR.js";
2
+ import { C as inspect, D as selected$1, E as ready, F as AdapterRegistry, H as WorkError, M as Attachments, O as validate, T as readWorkspace, U as ScreenModules, a as FileMutations, b as StoreActivity, c as FileReview, f as Files, g as FactoryRuns, h as ReviewActionError, k as LiveChannel, m as FilesInputError, q as taskDesignModuleId, r as FileSaves, t as FileReviewActions, u as FileSearch, w as read } from "./server-Dkbp5stG.js";
3
+ import { An as fieldByRole, B as parseThreadStatusInput, Dn as parseMessageAuthor, F as parseThreadInput, Gt as parseFileIndexQuery, I as parseThreadLinkInput, L as parseThreadReplyInput, M as parseThreadDeleteInput, N as parseThreadFilter, P as parseThreadId, Pt as parseFileSearchQuery, R as parseThreadSnapshot, Sn as valueByRole, St as parseReviewDiffQuery, Yt as parseFilePath, _ as taskRoutes, _n as parseWorkQuery, bn as parseWorkSyncResult, bt as parseReviewComparison, cn as parseWorkActionInput, ct as parseFileSaveRequest, dn as parseWorkCreate, dt as parseMutationApply, fn as parseWorkDetail, ft as parseMutationIntent, g as pageRoutes, h as fileRoutes, hn as parseWorkMutationResult, i as routes, j as parseThread, kt as parseReviewSourceQuery, mn as parseWorkItemRef, nn as MAX_CANVAS_Y, nt as parseReviewActionApply, rn as parseElementAnchor, rt as parseReviewActionIntent, sn as parseSectionData, ut as maxFileSaveBytes, wt as parseReviewHistoryQuery, xn as parseWorkUpdate, yn as parseWorkSync } from "./events-BH_rAu7O.js";
4
+ import { An as hasMessage, Ct as taskHref, En as AppShell, Fn as Text, Gt as Loader, H as parseTaskDesign, Mn as locale, Nn as t, On as readPreference, Ot as legacyFileHref, Pn as useLocaleSource, Q as FieldCardHeader, X as FieldCardContent, Y as FieldCard, _ as requestComments, _t as legacyConversationHref, at as readTabsCookie, bt as agentViewOpen, ct as tabsCookieName, et as activeTab, it as parseShellTabs, jn as isLocale, kn as writePreference, lt as tabsCookieValue, n as unavailableComments, nt as initialShellTabs, ot as stepShellTab, pt as conversationDestination, qt as Button, r as followThreads, rt as openShellTab, st as tabHref, t as commentsUnavailable, tt as closeShellTab, ut as visitShellTab, v as projectLive, wn as LiveStatus, xn as ShellTabBar, xt as selectedTask, yt as readConversationLocation } from "./comment-data-Rg42ToTW.js";
5
5
  import { d as parsePreviewHash, f as parsePreviewId, l as parsePreviewBranch, m as parsePreviewViewport } from "./parsers-CsiSkwTT.js";
6
- import { t as projectWork } from "./project-work-qSKFO5yj.js";
6
+ import { t as projectWork } from "./project-work-DoIwxWMm.js";
7
7
  import { n as showWorkspaceTab } from "./workspace-tab-DAUjkJzZ.js";
8
+ import { c as parseTaskContext, l as parseTaskContextInput } from "./run-BJ8r5jSh.js";
8
9
  import { createHash } from "node:crypto";
9
10
  import { extname, isAbsolute, relative, resolve } from "node:path";
10
11
  import { createController, createRouter } from "remix/router";
@@ -86,10 +87,10 @@ function ProjectLiveStatus(handle) {
86
87
  //#endregion
87
88
  //#region app/client/screens.ts
88
89
  var builtIns = {
89
- "bud:pages": () => import("./pages-BBBXyJWV.js"),
90
- "bud:canvas": () => import("./canvas-Dc1mYCwQ.js"),
91
- "bud:tasks": () => import("./tasks-CiXodsLN.js"),
92
- "bud:files": () => import("./files-E9mSFeoc.js")
90
+ "bud:pages": () => import("./pages-KJY_Ba92.js"),
91
+ "bud:canvas": () => import("./canvas-CJWQ-jjv.js"),
92
+ "bud:tasks": () => import("./tasks-Bgu5Lg9Y.js"),
93
+ "bud:files": () => import("./files-CPJqKeMo.js")
93
94
  };
94
95
  /**
95
96
  * Imports a screen module: a built-in by its key, any other by the URL the server gave it.
@@ -365,6 +366,14 @@ function Workspace(handle) {
365
366
  icon: screen.icon
366
367
  };
367
368
  }
369
+ if (agentViewOpen(href)) {
370
+ let task = selectedTask(href);
371
+ if (task) return {
372
+ href,
373
+ title: t("app.client.workspace.agentView", { task }),
374
+ icon: "terminal"
375
+ };
376
+ }
368
377
  let last = (match && match.root !== "/" ? pathname.slice(match.root.length) : pathname).split("/").filter(Boolean).at(-1);
369
378
  return {
370
379
  href,
@@ -730,6 +739,97 @@ function createAppController(template, project, runtime) {
730
739
  } });
731
740
  }
732
741
  //#endregion
742
+ //#region app/server/comment-task.ts
743
+ var titleLength = 120;
744
+ /**
745
+ * A task's title and description for a discussion: what it asks, where it was made, what it points
746
+ * at and how to get back to it, in the request's language.
747
+ * @param thread - The discussion, with its messages loaded.
748
+ * @param schema - The task store's fields; title and description go where their roles are.
749
+ * @param routes - The project's routes, for page titles.
750
+ * @param found - The element at the comment's point, when the comment didn't keep one.
751
+ * @returns Field values for a new task.
752
+ */
753
+ function commentTaskValues(thread, schema, routes, found = null) {
754
+ let first = thread.messages[0];
755
+ let place = commentPlace(thread, routes);
756
+ let line = first?.body.split("\n").find((text) => text.trim())?.trim() ?? "";
757
+ let title = (line.length > titleLength ? `${line.slice(0, 119).trimEnd()}…` : line) || t("app.server.commentTask.title", { place });
758
+ let values = {};
759
+ let titleField = fieldByRole(schema, "title");
760
+ if (titleField) values[titleField.id] = title;
761
+ let descriptionField = fieldByRole(schema, "description");
762
+ if (descriptionField) values[descriptionField.id] = commentTaskDescription(thread, place, found);
763
+ return values;
764
+ }
765
+ function commentPlace(thread, routes) {
766
+ let anchor = thread.anchor;
767
+ if (!anchor) return t("app.server.commentTask.linkedTasks");
768
+ if ("type" in anchor) return `${anchor.path}:${anchor.startLine}–${anchor.endLine}`;
769
+ let route = routes.find((candidate) => candidate.id === anchor.pageId);
770
+ return route && route.title !== anchor.pagePath ? `${route.title} (${anchor.pagePath})` : anchor.pagePath;
771
+ }
772
+ function commentTaskDescription(thread, place, found) {
773
+ let first = thread.messages[0];
774
+ let lines = [];
775
+ if (first) {
776
+ lines.push(...first.body.split("\n").map((text) => `> ${text}`));
777
+ lines.push(`>`, `> — ${first.author.name}, ${first.createdAt.slice(0, 10)}`, "");
778
+ }
779
+ let anchor = thread.anchor;
780
+ let where = [place];
781
+ if (anchor && "type" in anchor) where.push(anchor.commit ? anchor.commit.slice(0, 8) : t("app.server.commentTask.uncommitted"));
782
+ else if (anchor) {
783
+ where.push(`${anchor.viewport}px`);
784
+ where.push(anchor.branch ?? t("app.server.commentTask.workingTree"));
785
+ }
786
+ lines.push(`**${t("app.server.commentTask.where")}:** ${where.join(" · ")}`);
787
+ let element = (anchor && !("type" in anchor) ? anchor.element : null) ?? found;
788
+ if (element) {
789
+ let label = element.label || element.breadcrumb;
790
+ lines.push(`**${t("app.server.commentTask.element")}:** ${label} (\`${element.tagName}\`)`);
791
+ let source = element.source;
792
+ if (source?.file) lines.push(`**${t("app.server.commentTask.source")}:** \`${source.file}${source.line ? `:${source.line}` : ""}\`${source.component ? ` (${source.component})` : ""}`);
793
+ }
794
+ if (anchor && "type" in anchor && anchor.context.selected.length) lines.push("", "```", ...anchor.context.selected, "```");
795
+ if (thread.messageCount > 1) lines.push(`**${t("app.server.commentTask.replies")}:** ${t("app.server.commentTask.replyCount", { count: thread.messageCount - 1 })}`);
796
+ lines.push(`**${t("app.server.commentTask.discussion")}:** [/comments/${thread.id}](/comments/${encodeURIComponent(thread.id)})`);
797
+ return lines.join("\n");
798
+ }
799
+ /**
800
+ * Where a task made from a discussion came from, kept with the task for its page and its agent.
801
+ * @param thread - The discussion.
802
+ * @param routes - The project's routes, for page titles.
803
+ * @param given - What the canvas found: the element and point, and the screenshot's hash.
804
+ * @returns The task's context.
805
+ */
806
+ function commentTaskContext(thread, routes, given) {
807
+ let anchor = thread.anchor;
808
+ let code = anchor && "type" in anchor ? anchor : null;
809
+ let canvas = anchor && !("type" in anchor) ? anchor : null;
810
+ let route = canvas ? routes.find((candidate) => candidate.id === canvas.pageId) : void 0;
811
+ return {
812
+ version: 1,
813
+ thread: thread.id,
814
+ page: canvas ? {
815
+ id: canvas.pageId,
816
+ path: canvas.pagePath,
817
+ title: route?.title ?? canvas.pagePath
818
+ } : null,
819
+ viewport: canvas?.viewport ?? null,
820
+ branch: canvas?.branch ?? null,
821
+ point: canvas ? given.point : null,
822
+ element: (canvas?.element ?? null) || given.element,
823
+ image: canvas ? given.image : null,
824
+ code: code ? {
825
+ path: code.path,
826
+ startLine: code.startLine,
827
+ endLine: code.endLine,
828
+ commit: code.commit ?? null
829
+ } : null
830
+ };
831
+ }
832
+ //#endregion
733
833
  //#region app/server/comments.ts
734
834
  /** A comment request the server refuses, with the HTTP status to answer it with. */
735
835
  var CommentError = class extends TaggedError("CommentError") {
@@ -914,7 +1014,7 @@ function projectApi(options) {
914
1014
  if (!runtime) return jsonError("The project services are not running. Restart Bud.", 503);
915
1015
  return runtime.runPromise(program(project).pipe(match({
916
1016
  onFailure: options.failure,
917
- onSuccess: (value) => Response.json(value, { headers })
1017
+ onSuccess: (value) => value instanceof Response ? value : Response.json(value, { headers })
918
1018
  })), { signal: request.signal });
919
1019
  };
920
1020
  }
@@ -949,7 +1049,7 @@ function parseQuery(request, schema, allowed) {
949
1049
  }
950
1050
  //#endregion
951
1051
  //#region app/actions/comments/controller.ts
952
- var statuses$5 = {
1052
+ var statuses$6 = {
953
1053
  input: 400,
954
1054
  unavailable: 503,
955
1055
  "not-found": 404,
@@ -1015,7 +1115,7 @@ function createCommentsController(project, runtime) {
1015
1115
  project,
1016
1116
  runtime,
1017
1117
  missing: "Connect a project with the Bud Vite plugin to use comments.",
1018
- failure: (error) => error._tag === "WorkError" ? jsonError(error.message, statuses$5[error.reason], {
1118
+ failure: (error) => error._tag === "WorkError" ? jsonError(error.message, statuses$6[error.reason], {
1019
1119
  code: error.reason,
1020
1120
  ...error.currentRevision ? { currentRevision: error.currentRevision } : {}
1021
1121
  }) : jsonError(error.message, error.status)
@@ -1114,6 +1214,39 @@ function createCommentsController(project, runtime) {
1114
1214
  unlink({ request, params }) {
1115
1215
  return respond(request, (project) => written(link(request, project, params.id, true)));
1116
1216
  },
1217
+ makeTask({ request, params }) {
1218
+ return respond(request, (project) => written(gen(function* () {
1219
+ let id = yield* input$2(() => parseThreadId(params.id));
1220
+ let body = yield* readProjectJsonBody(request, { maxBytes: 32e5 });
1221
+ let given = parseTaskContextInput(body && typeof body === "object" && "context" in body ? body.context : null);
1222
+ let threads = yield* writable(project.root, "link");
1223
+ let thread = yield* threads.get(project.root, id).pipe(flatMap((value) => result(() => parseThread(value))));
1224
+ let image = given.image ? yield* (yield* Attachments).put(given.image).pipe(orElseSucceed(() => null)) : null;
1225
+ let context = commentTaskContext(thread, project.config.routes, {
1226
+ ...given,
1227
+ image
1228
+ });
1229
+ let tasks = yield* selected$1(project.root);
1230
+ let work = yield* ready(tasks);
1231
+ if (!tasks.schema.capabilities.create) return yield* new WorkError({
1232
+ reason: "refused",
1233
+ message: "The task store does not support creating tasks."
1234
+ });
1235
+ let draft = yield* input$2(() => parseWorkCreate({ values: commentTaskValues(thread, tasks.schema, project.config.routes, context.element) }, tasks.schema));
1236
+ let created = yield* work.create(project.root, draft).pipe(flatMap((value) => result(() => parseWorkMutationResult(value, tasks.schema), true)));
1237
+ let task = created.item.id;
1238
+ if (work.writeItemData && created.item.revision) yield* ignore(work.writeItemData(project.root, task, "budContext", context, created.item.revision));
1239
+ let linked = yield* threads.link(project.root, id, [task], thread.revision ?? "").pipe(flatMap((value) => result(() => parseThread(value), true)), mapError((cause) => new WorkError({
1240
+ reason: "uncertain",
1241
+ message: `Task ${task} was created, but linking it to this discussion could not be confirmed. Link it from the task before trying again.`,
1242
+ cause
1243
+ })));
1244
+ return {
1245
+ item: created.item,
1246
+ thread: linked
1247
+ };
1248
+ })));
1249
+ },
1117
1250
  deleteMessage({ request, params }) {
1118
1251
  return respond(request, (project) => written(gen(function* () {
1119
1252
  let id = yield* input$2(() => parseThreadId(params.id));
@@ -1156,6 +1289,544 @@ function createCommentsController(project, runtime) {
1156
1289
  } });
1157
1290
  }
1158
1291
  //#endregion
1292
+ //#region app/server/agent-tools.ts
1293
+ /** The task database's tools, as MCP lists them; all of them only read. */
1294
+ var AGENT_TOOLS = [
1295
+ {
1296
+ name: "search_tasks",
1297
+ description: "Searches the project's tasks by words in their title or description, and by status. Use it to find related tasks, duplicates and earlier decisions.",
1298
+ inputSchema: {
1299
+ type: "object",
1300
+ properties: {
1301
+ query: {
1302
+ type: "string",
1303
+ description: "Words to look for."
1304
+ },
1305
+ status: {
1306
+ type: "string",
1307
+ description: "Only tasks with this status, such as open."
1308
+ },
1309
+ limit: {
1310
+ type: "number",
1311
+ description: "At most this many; 20 by default, 50 at most."
1312
+ }
1313
+ }
1314
+ }
1315
+ },
1316
+ {
1317
+ name: "get_task",
1318
+ description: "Reads one task: its fields, relations and the discussions linked to it.",
1319
+ inputSchema: {
1320
+ type: "object",
1321
+ properties: { id: {
1322
+ type: "string",
1323
+ description: "The task id."
1324
+ } },
1325
+ required: ["id"]
1326
+ }
1327
+ },
1328
+ {
1329
+ name: "list_discussions",
1330
+ description: "Lists the project's discussions, such as comments on a page of the site, with their first message and the tasks they link.",
1331
+ inputSchema: {
1332
+ type: "object",
1333
+ properties: {
1334
+ page: {
1335
+ type: "string",
1336
+ description: "Only comments on this page path, such as /pricing."
1337
+ },
1338
+ task: {
1339
+ type: "string",
1340
+ description: "Only discussions linked to this task id."
1341
+ }
1342
+ }
1343
+ }
1344
+ },
1345
+ {
1346
+ name: "recent_changes",
1347
+ description: "Lists the tasks changed most recently, newest first.",
1348
+ inputSchema: {
1349
+ type: "object",
1350
+ properties: { limit: {
1351
+ type: "number",
1352
+ description: "At most this many; 20 by default."
1353
+ } }
1354
+ }
1355
+ }
1356
+ ];
1357
+ function text(value, max = 2e3) {
1358
+ return typeof value === "string" ? value.slice(0, max) : void 0;
1359
+ }
1360
+ function brief(item, schema) {
1361
+ return {
1362
+ id: item.id,
1363
+ title: text(valueByRole(item, schema, "title"), 300),
1364
+ status: text(valueByRole(item, schema, "status"), 100),
1365
+ updated: text(valueByRole(item, schema, "updated"), 100)
1366
+ };
1367
+ }
1368
+ function discussion(thread, messages = 1) {
1369
+ let anchor = thread.anchor;
1370
+ return {
1371
+ id: thread.id,
1372
+ status: thread.status,
1373
+ page: anchor && !("type" in anchor) ? anchor.pagePath : void 0,
1374
+ file: anchor && "type" in anchor ? anchor.path : void 0,
1375
+ tasks: thread.itemIds,
1376
+ messageCount: thread.messageCount,
1377
+ messages: thread.messages.slice(0, messages).map((message) => ({
1378
+ author: message.author.name,
1379
+ at: message.createdAt,
1380
+ body: message.body.slice(0, 4e3)
1381
+ }))
1382
+ };
1383
+ }
1384
+ function count(value, fallback, max) {
1385
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? Math.min(value, max) : fallback;
1386
+ }
1387
+ /**
1388
+ * Runs one of the task database's tools.
1389
+ * @param stores - The project's stores.
1390
+ * @param name - The tool.
1391
+ * @param args - Its arguments, untrusted.
1392
+ * @returns What it found, as JSON-ready data.
1393
+ */
1394
+ function callAgentTool(stores, name, args) {
1395
+ let { root, work, threads } = stores;
1396
+ return gen(function* () {
1397
+ let schema = yield* work.schema(root);
1398
+ if (name === "search_tasks") return { tasks: (yield* work.list(root, {
1399
+ ...text(args.query, 200) ? { search: text(args.query, 200) } : {},
1400
+ ...text(args.status, 100) ? { status: text(args.status, 100) } : {},
1401
+ limit: count(args.limit, 20, 50)
1402
+ })).items.map((item) => brief(item, schema)) };
1403
+ if (name === "get_task") {
1404
+ let id = text(args.id, 200);
1405
+ if (!id) return yield* new WorkError({
1406
+ reason: "input",
1407
+ message: "Name the task with id."
1408
+ });
1409
+ let detail = yield* work.get(root, id);
1410
+ let linked = threads ? (yield* threads.list(root, { itemId: id })).threads : [];
1411
+ return {
1412
+ ...brief(detail.item, schema),
1413
+ description: text(valueByRole(detail.item, schema, "description"), 2e4),
1414
+ fields: Object.fromEntries(schema.fields.filter((field) => detail.item.values[field.id] !== void 0).map((field) => [field.label, detail.item.values[field.id]])),
1415
+ relations: detail.item.relations,
1416
+ discussions: linked.map((thread) => discussion(thread, 20))
1417
+ };
1418
+ }
1419
+ if (name === "list_discussions") {
1420
+ if (!threads) return { discussions: [] };
1421
+ let page = text(args.page, 1e3);
1422
+ let task = text(args.task, 200);
1423
+ return { discussions: (yield* threads.list(root, {
1424
+ ...page ? { pagePath: page } : {},
1425
+ ...task ? { itemId: task } : {}
1426
+ })).threads.slice(0, 100).map((thread) => discussion(thread)) };
1427
+ }
1428
+ if (name === "recent_changes") return { tasks: (yield* work.list(root, { limit: 500 })).items.map((item) => brief(item, schema)).sort((left, right) => (right.updated ?? "").localeCompare(left.updated ?? "")).slice(0, count(args.limit, 20, 100)) };
1429
+ return yield* new WorkError({
1430
+ reason: "input",
1431
+ message: `No tool ${name}.`
1432
+ });
1433
+ });
1434
+ }
1435
+ function answer(id, result) {
1436
+ return {
1437
+ jsonrpc: "2.0",
1438
+ id,
1439
+ result
1440
+ };
1441
+ }
1442
+ function failure$3(id, code, message) {
1443
+ return {
1444
+ jsonrpc: "2.0",
1445
+ id: id ?? null,
1446
+ error: {
1447
+ code,
1448
+ message
1449
+ }
1450
+ };
1451
+ }
1452
+ /**
1453
+ * Answers MCP's JSON-RPC messages for the task database: initialize, ping, the tool list and
1454
+ * tool calls. Notifications get no answer.
1455
+ * @param stores - The project's stores.
1456
+ * @param body - The request body, untrusted.
1457
+ * @returns The response body, or null when every message was a notification.
1458
+ */
1459
+ function answerAgentTools(stores, body) {
1460
+ let one = (message) => {
1461
+ if (!message || typeof message !== "object" || typeof message.method !== "string") return succeed(failure$3(message?.id, -32600, "Invalid request."));
1462
+ let id = message.id;
1463
+ let notification = id === void 0;
1464
+ let params = message.params ?? {};
1465
+ switch (message.method) {
1466
+ case "initialize": return succeed(answer(id, {
1467
+ protocolVersion: typeof params.protocolVersion === "string" ? params.protocolVersion : "2025-06-18",
1468
+ capabilities: { tools: { listChanged: false } },
1469
+ serverInfo: {
1470
+ name: "bud-tasks",
1471
+ version: "1.0.0"
1472
+ },
1473
+ instructions: "Read-only access to this project's tasks and discussions, for finding related work and earlier decisions."
1474
+ }));
1475
+ case "ping": return succeed(notification ? null : answer(id, {}));
1476
+ case "tools/list": return succeed(answer(id, { tools: AGENT_TOOLS }));
1477
+ case "tools/call": return callAgentTool(stores, typeof params.name === "string" ? params.name : "", params.arguments && typeof params.arguments === "object" ? params.arguments : {}).pipe(match({
1478
+ onSuccess: (result) => answer(id, { content: [{
1479
+ type: "text",
1480
+ text: JSON.stringify(result, null, 2)
1481
+ }] }),
1482
+ onFailure: (error) => answer(id, {
1483
+ content: [{
1484
+ type: "text",
1485
+ text: error.message
1486
+ }],
1487
+ isError: true
1488
+ })
1489
+ }));
1490
+ default: return succeed(notification ? null : failure$3(id, -32601, `No method ${message.method}.`));
1491
+ }
1492
+ };
1493
+ if (Array.isArray(body)) return map(forEach(body.slice(0, 20), one), (answers) => {
1494
+ let given = answers.filter((value) => value !== null);
1495
+ return given.length ? given : null;
1496
+ });
1497
+ return one(body);
1498
+ }
1499
+ //#endregion
1500
+ //#region app/actions/agent-api/controller.ts
1501
+ var json = (body, status = 200) => Response.json(body, {
1502
+ status,
1503
+ headers: { "Cache-Control": "no-store" }
1504
+ });
1505
+ /**
1506
+ * Serves the task database agents read in their sandboxes, as an MCP server over HTTP: only a
1507
+ * running run's token lets an agent in, and it can only read.
1508
+ * @param project - The trusted consuming project.
1509
+ * @param runtime - The companion's runtime with its stores and runs.
1510
+ * @returns The agent controller.
1511
+ */
1512
+ function createAgentController(project, runtime) {
1513
+ return createController(routes.agentApi, { actions: {
1514
+ tasksStream() {
1515
+ return new Response(null, {
1516
+ status: 405,
1517
+ headers: { Allow: "POST" }
1518
+ });
1519
+ },
1520
+ tasks({ request }) {
1521
+ if (!project || !runtime) return json({ error: "No project is connected." }, 404);
1522
+ let token = /^Bearer (\S{1,2000})$/.exec(request.headers.get("Authorization") ?? "")?.[1];
1523
+ if (!token) return json({ error: "Send the run’s token." }, 401);
1524
+ return runtime.runPromise(gen(function* () {
1525
+ if (!(yield* (yield* FactoryRuns).agentTask(token))) return json({ error: "This token’s run is over or unknown." }, 401);
1526
+ let { work, threads } = yield* AdapterRegistry;
1527
+ if (!work) return json({ error: "Task storage is off." }, 503);
1528
+ let body = yield* readProjectJsonBody(request, { maxBytes: 64e3 });
1529
+ let answer = yield* answerAgentTools({
1530
+ root: project.root,
1531
+ work,
1532
+ threads
1533
+ }, body);
1534
+ return answer === null ? new Response(null, { status: 202 }) : json(answer);
1535
+ }).pipe(catch_((error) => succeed(json({ error: error.message }, "status" in error ? error.status : 500)))), { signal: request.signal });
1536
+ }
1537
+ } });
1538
+ }
1539
+ //#endregion
1540
+ //#region app/actions/factory-api/controller.ts
1541
+ var TERMINAL_SESSIONS = [
1542
+ "agent",
1543
+ "dev",
1544
+ "shell"
1545
+ ];
1546
+ var statuses$5 = {
1547
+ input: 400,
1548
+ unavailable: 503,
1549
+ "not-found": 404,
1550
+ conflict: 409,
1551
+ refused: 422,
1552
+ timeout: 504,
1553
+ uncertain: 409,
1554
+ storage: 500
1555
+ };
1556
+ /**
1557
+ * Compares the factory's statuses with the task store's.
1558
+ * @param factory - The factory config.
1559
+ * @param schema - The task store's schema.
1560
+ * @param canAdd - Whether the store can add statuses.
1561
+ * @returns What is missing and whether adding fixes it.
1562
+ */
1563
+ function factoryReadiness(factory, schema, canAdd) {
1564
+ let problems = [];
1565
+ for (let role of FACTORY_STATUS_ROLES) {
1566
+ let value = factory.statuses[role];
1567
+ let category = FACTORY_STATUS_CATEGORIES[role];
1568
+ let status = schema.statuses.find((entry) => entry.value === value);
1569
+ if (!status) problems.push({
1570
+ role,
1571
+ value,
1572
+ category,
1573
+ reason: "missing"
1574
+ });
1575
+ else if (status.category !== category) problems.push({
1576
+ role,
1577
+ value,
1578
+ category,
1579
+ reason: "category"
1580
+ });
1581
+ }
1582
+ return {
1583
+ enabled: true,
1584
+ ready: !problems.length,
1585
+ message: null,
1586
+ problems,
1587
+ canAdd: canAdd && problems.length > 0 && problems.every((problem) => problem.reason === "missing"),
1588
+ statuses: factory.statuses
1589
+ };
1590
+ }
1591
+ var off = {
1592
+ enabled: false,
1593
+ ready: false,
1594
+ message: null,
1595
+ problems: [],
1596
+ canAdd: false,
1597
+ statuses: null
1598
+ };
1599
+ /**
1600
+ * Serves the factory's readiness and sets up its statuses in the task store.
1601
+ * @param project - The trusted consuming project.
1602
+ * @param runtime - The companion's runtime with the selected adapters.
1603
+ * @returns The factory controller.
1604
+ */
1605
+ function createFactoryController(project, runtime) {
1606
+ let api = projectApi({
1607
+ project,
1608
+ runtime,
1609
+ missing: "Connect a project with the Bud Vite plugin to use the factory.",
1610
+ failure: (error) => error._tag === "WorkError" ? jsonError(error.message, statuses$5[error.reason], { code: error.reason }) : jsonError(error.message, error.status)
1611
+ });
1612
+ let taskId = (value) => /^[\w.:-]{1,200}$/.test(value) ? succeed(value) : fail$1(new WorkError({
1613
+ reason: "input",
1614
+ message: "Invalid task id."
1615
+ }));
1616
+ let person = (root) => map(commentAuthor(root), (author) => ({
1617
+ name: author.name,
1618
+ email: author.email
1619
+ }));
1620
+ function act(request, id, operation) {
1621
+ return api(request, (project) => gen(function* () {
1622
+ let task = yield* taskId(id);
1623
+ return { run: yield* operation(yield* FactoryRuns, project, task) };
1624
+ }));
1625
+ }
1626
+ function readiness(project) {
1627
+ return gen(function* () {
1628
+ let factory = project.config.factory;
1629
+ if (!factory) return off;
1630
+ let tasks = yield* selected$1(project.root);
1631
+ if (!tasks.service || tasks.connection.state !== "ready") return {
1632
+ ...off,
1633
+ enabled: true,
1634
+ message: tasks.connection.state === "ready" ? "Task storage is off, so the factory has nowhere to keep its tasks." : tasks.connection.message ?? "Task storage is unavailable."
1635
+ };
1636
+ return factoryReadiness(factory, tasks.schema, Boolean(tasks.service.addStatuses));
1637
+ });
1638
+ }
1639
+ let terminals = /* @__PURE__ */ new Map();
1640
+ let bridge = (task, session) => gen(function* () {
1641
+ if (!TERMINAL_SESSIONS.includes(session)) return yield* new WorkError({
1642
+ reason: "not-found",
1643
+ message: "No such terminal."
1644
+ });
1645
+ let cached = terminals.get(task);
1646
+ let terminal = cached && cached.until > Date.now() ? cached.value : yield* (yield* FactoryRuns).terminal(task);
1647
+ if (!terminal) return yield* new WorkError({
1648
+ reason: "not-found",
1649
+ message: "This run has no terminal; it starts with the run’s sandbox."
1650
+ });
1651
+ terminals.set(task, {
1652
+ value: terminal,
1653
+ until: Date.now() + 3e4
1654
+ });
1655
+ let call = (path, init = {}) => tryPromise({
1656
+ try: async () => {
1657
+ let url = new URL(terminal.url);
1658
+ let [pathname, query = ""] = path.split("?");
1659
+ url.pathname = `${url.pathname.replace(/\/$/, "")}${pathname}`;
1660
+ for (let [key, value] of new URLSearchParams(query)) url.searchParams.set(key, value);
1661
+ let response = await fetch(url, {
1662
+ ...init,
1663
+ headers: {
1664
+ ...terminal.headers,
1665
+ Authorization: `Basic ${btoa(`bud:${terminal.password}`)}`,
1666
+ ...init.headers
1667
+ }
1668
+ });
1669
+ if (!response.ok && response.status !== 204) throw new Error(`The terminal answered ${response.status}.`);
1670
+ return response;
1671
+ },
1672
+ catch: (cause) => {
1673
+ terminals.delete(task);
1674
+ return new WorkError({
1675
+ reason: "unavailable",
1676
+ message: "The terminal isn’t answering; its sandbox may be waking.",
1677
+ cause
1678
+ });
1679
+ }
1680
+ });
1681
+ return {
1682
+ name: session,
1683
+ terminal,
1684
+ call
1685
+ };
1686
+ });
1687
+ let size = (value, fallback) => typeof value === "number" && Number.isInteger(value) && value >= 2 && value <= 1e3 ? value : fallback;
1688
+ return createController(routes.factoryApi, { actions: {
1689
+ status({ request }) {
1690
+ return api(request, readiness);
1691
+ },
1692
+ run({ request, params }) {
1693
+ return act(request, params.id, (runs, _project, task) => runs.get(task));
1694
+ },
1695
+ start({ request, params }) {
1696
+ return act(request, params.id, (runs, project, task) => flatMap(person(project.root), (by) => runs.start(task, by)));
1697
+ },
1698
+ changes({ request, params }) {
1699
+ return act(request, params.id, (runs, project, task) => gen(function* () {
1700
+ let body = yield* readProjectJsonBody(request, { maxBytes: 64e3 });
1701
+ let message = body && typeof body === "object" && "message" in body && typeof body.message === "string" ? body.message.trim() : "";
1702
+ if (!message || message.length > 1e4) return yield* new WorkError({
1703
+ reason: "input",
1704
+ message: "Write what should change."
1705
+ });
1706
+ return yield* runs.changes(task, message, yield* person(project.root));
1707
+ }));
1708
+ },
1709
+ answer({ request, params }) {
1710
+ return act(request, params.id, (runs, project, task) => gen(function* () {
1711
+ let body = yield* readProjectJsonBody(request, { maxBytes: 64e3 });
1712
+ let message = body && typeof body === "object" && "message" in body && typeof body.message === "string" ? body.message.trim() : "";
1713
+ if (!message || message.length > 1e4) return yield* new WorkError({
1714
+ reason: "input",
1715
+ message: "Write your answer."
1716
+ });
1717
+ return yield* runs.answer(task, message, yield* person(project.root));
1718
+ }));
1719
+ },
1720
+ approve({ request, params }) {
1721
+ return act(request, params.id, (runs, project, task) => flatMap(person(project.root), (by) => runs.approve(task, by)));
1722
+ },
1723
+ discard({ request, params }) {
1724
+ return act(request, params.id, (runs, _project, task) => runs.discard(task));
1725
+ },
1726
+ retry({ request, params }) {
1727
+ return act(request, params.id, (runs, _project, task) => runs.retry(task));
1728
+ },
1729
+ wake({ request, params }) {
1730
+ return act(request, params.id, (runs, _project, task) => runs.wake(task));
1731
+ },
1732
+ preview({ request, params }) {
1733
+ return api(request, () => gen(function* () {
1734
+ let task = yield* taskId(params.id);
1735
+ return { preview: yield* (yield* FactoryRuns).preview(task) };
1736
+ }));
1737
+ },
1738
+ touch({ request, params }) {
1739
+ return api(request, () => gen(function* () {
1740
+ yield* (yield* FactoryRuns).touch(yield* taskId(params.id));
1741
+ return { ok: true };
1742
+ }));
1743
+ },
1744
+ terminal({ request, params }) {
1745
+ return api(request, () => gen(function* () {
1746
+ let task = yield* taskId(params.id);
1747
+ let { name, terminal, call } = yield* bridge(task, params.session);
1748
+ let session = terminal.sessions[name];
1749
+ if (!session) return yield* new WorkError({
1750
+ reason: "not-found",
1751
+ message: name === "agent" ? "This run’s agent has no interface to attach to." : "This run has no dev server."
1752
+ });
1753
+ let query = new URL(request.url).searchParams;
1754
+ yield* call("/open", {
1755
+ method: "POST",
1756
+ headers: { "Content-Type": "application/json" },
1757
+ body: JSON.stringify({
1758
+ id: name,
1759
+ command: session.command,
1760
+ env: session.env,
1761
+ cols: size(Number(query.get("cols")), 80),
1762
+ rows: size(Number(query.get("rows")), 24)
1763
+ })
1764
+ });
1765
+ yield* ignore((yield* FactoryRuns).touch(task));
1766
+ let stream = yield* call(`/stream?id=${name}&from=${Math.max(0, Number(query.get("from")) || 0)}`, { signal: request.signal });
1767
+ return new Response(stream.body, { headers: {
1768
+ "Content-Type": "text/event-stream",
1769
+ "Cache-Control": "no-store",
1770
+ "X-Content-Type-Options": "nosniff"
1771
+ } });
1772
+ }));
1773
+ },
1774
+ terminalInput({ request, params }) {
1775
+ return api(request, () => gen(function* () {
1776
+ let task = yield* taskId(params.id);
1777
+ let body = yield* readProjectJsonBody(request, { maxBytes: 64e3 });
1778
+ let data = body && typeof body === "object" && "data" in body ? body.data : null;
1779
+ if (typeof data !== "string" || data.length > 32e3) return yield* new WorkError({
1780
+ reason: "input",
1781
+ message: "Invalid input."
1782
+ });
1783
+ let { name, call } = yield* bridge(task, params.session);
1784
+ yield* call(`/input?id=${name}`, {
1785
+ method: "POST",
1786
+ body: data
1787
+ });
1788
+ return { ok: true };
1789
+ }));
1790
+ },
1791
+ terminalResize({ request, params }) {
1792
+ return api(request, () => gen(function* () {
1793
+ let task = yield* taskId(params.id);
1794
+ let body = yield* readProjectJsonBody(request, { maxBytes: 1e3 });
1795
+ let { name, call } = yield* bridge(task, params.session);
1796
+ yield* call(`/resize?id=${name}`, {
1797
+ method: "POST",
1798
+ headers: { "Content-Type": "application/json" },
1799
+ body: JSON.stringify({
1800
+ cols: size(body?.cols, 80),
1801
+ rows: size(body?.rows, 24)
1802
+ })
1803
+ });
1804
+ return { ok: true };
1805
+ }));
1806
+ },
1807
+ addStatuses({ request }) {
1808
+ return api(request, (project) => gen(function* () {
1809
+ let current = yield* readiness(project);
1810
+ if (!current.enabled) return yield* new WorkError({
1811
+ reason: "refused",
1812
+ message: "The factory is off."
1813
+ });
1814
+ if (current.ready) return current;
1815
+ if (!current.canAdd) return yield* new WorkError({
1816
+ reason: "refused",
1817
+ message: current.message ?? "The task store can’t add these statuses; set them up in the store or map other ones in factory.statuses."
1818
+ });
1819
+ let tasks = yield* selected$1(project.root);
1820
+ yield* (yield* ready(tasks)).addStatuses(project.root, current.problems.map((problem) => ({
1821
+ value: problem.value,
1822
+ category: problem.category
1823
+ }))).pipe(ensuring(StoreActivity.use((activity) => activity.notify("work"))));
1824
+ return yield* readiness(project);
1825
+ }));
1826
+ }
1827
+ } });
1828
+ }
1829
+ //#endregion
1159
1830
  //#region app/actions/tasks-api/controller.ts
1160
1831
  var statuses$4 = {
1161
1832
  input: 400,
@@ -1242,6 +1913,29 @@ function createTasksController(project, runtime) {
1242
1913
  }));
1243
1914
  }
1244
1915
  return createController(routes.tasksApi, { actions: {
1916
+ context({ request, params }) {
1917
+ return respond(request, (project) => gen(function* () {
1918
+ let id = yield* validate(() => parseWorkItemRef({ id: params.id }).id, "input");
1919
+ let current = yield* selected$1(project.root);
1920
+ let service = yield* ready(current);
1921
+ if (!service.readItemData) return { context: null };
1922
+ let { value } = yield* service.readItemData(project.root, id, "budContext");
1923
+ return { context: parseTaskContext(value) };
1924
+ }));
1925
+ },
1926
+ attachment({ request, params }) {
1927
+ return respond(request, () => gen(function* () {
1928
+ let bytes = yield* (yield* Attachments).get(params.hash);
1929
+ if (!bytes) return yield* new WorkError({
1930
+ reason: "not-found",
1931
+ message: "No such image."
1932
+ });
1933
+ return new Response(bytes, { headers: {
1934
+ "Content-Type": "image/jpeg",
1935
+ "Cache-Control": "private, max-age=31536000, immutable"
1936
+ } });
1937
+ }));
1938
+ },
1245
1939
  schema({ request }) {
1246
1940
  return respond(request, (project) => read(project.root, (reader) => inspect(reader).pipe(map(({ schema }) => schema))));
1247
1941
  },
@@ -1957,6 +2651,8 @@ function createApp(template, publicDirectory, project, runtime) {
1957
2651
  router.map(routes, createAppController(template, project, runtime));
1958
2652
  router.map(routes.comments, createCommentsController(project, runtime));
1959
2653
  router.map(routes.tasksApi, createTasksController(project, runtime));
2654
+ router.map(routes.factoryApi, createFactoryController(project, runtime));
2655
+ router.map(routes.agentApi, createAgentController(project, runtime));
1960
2656
  router.map(routes.filesApi, createFilesController(project, runtime));
1961
2657
  router.map(routes.fileSearchApi, createFileSearchController(project, runtime));
1962
2658
  router.map(routes.fileReviewApi, createFileReviewController(project, runtime));