loopable-cli 0.2.1 → 0.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 (134) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/_connectorId-CDCuSgpN.js +1 -0
  3. package/.output/public/assets/_loopId-DOnIsVTW.js +1 -0
  4. package/.output/public/assets/_taskId-BO5MZnlZ.js +1 -0
  5. package/.output/public/assets/agents-CBozi7oi.js +1 -0
  6. package/.output/public/assets/alert-Bn_wjgTX.js +1 -0
  7. package/.output/public/assets/button-K3ypIhAZ.js +1 -0
  8. package/.output/public/assets/card-DiVnaTIE.js +1 -0
  9. package/.output/public/assets/chevron-right-BrctLtFK.js +1 -0
  10. package/.output/public/assets/connection-status-DKWelvLO.js +1 -0
  11. package/.output/public/assets/connector-icon-CEbdE71G.js +1 -0
  12. package/.output/public/assets/connectors-JyX3IzJj.js +1 -0
  13. package/.output/public/assets/gaps-PjmcbaWo.js +1 -0
  14. package/.output/public/assets/inbox-CujRR0Cz.js +1 -0
  15. package/.output/public/assets/index-C9SQ_Baq.js +16 -0
  16. package/.output/public/assets/loop-form-Dw3cuTzw.js +1 -0
  17. package/.output/public/assets/loops-DcEfZa2_.js +1 -0
  18. package/.output/public/assets/manifests-yJpevyye.js +1 -0
  19. package/.output/public/assets/new-CwsnNuRW.js +1 -0
  20. package/.output/public/assets/page-bvnfRMum.js +1 -0
  21. package/.output/public/assets/play-yHLs4DjK.js +1 -0
  22. package/.output/public/assets/plus-Ber25lrU.js +1 -0
  23. package/.output/public/assets/preload-helper-BoU_7EQC.js +3 -0
  24. package/.output/public/assets/{refresh-cw-C3xBnKfy.js → refresh-cw-BzSY7M8I.js} +1 -1
  25. package/.output/public/assets/rotate-ccw-DLv9EfCX.js +1 -0
  26. package/.output/public/assets/runners-DzX-RU3i.js +2 -0
  27. package/.output/public/assets/select-C9grIFwG.js +1 -0
  28. package/.output/public/assets/setting-fields-DUc3npgh.js +3 -0
  29. package/.output/public/assets/styles-B9B98zLk.css +2 -0
  30. package/.output/public/assets/switch-DwABVI6d.js +1 -0
  31. package/.output/public/assets/task-list-Ddk8-qt7.js +1 -0
  32. package/.output/public/assets/textarea-Ds-gIYUQ.js +1 -0
  33. package/.output/public/assets/time-goBWd-5J.js +1 -0
  34. package/.output/public/assets/use-live-tasks-DZNNuECy.js +1 -0
  35. package/.output/public/assets/useCompositeListItem-P4Nmmn7j.js +1 -0
  36. package/.output/public/assets/usePositioner-B7i9Ejzh.js +1 -0
  37. package/.output/server/_chunks/ssr-renderer.mjs +1 -1
  38. package/.output/server/{_connectorId-C9yydJrC.mjs → _connectorId-COuQN02a.mjs} +198 -188
  39. package/.output/server/_libs/@base-ui/react+[...].mjs +1410 -804
  40. package/.output/server/_libs/@tanstack/react-router+[...].mjs +1 -1
  41. package/.output/server/_libs/lucide-react.mjs +93 -84
  42. package/.output/server/_libs/next-themes.mjs +1 -1
  43. package/.output/server/_libs/sonner.mjs +1 -1
  44. package/.output/server/_loopId-CZh6_Yhr.mjs +310 -0
  45. package/.output/server/_ssr/{agents-DG643yoJ.mjs → agents-B_qo1gpL.mjs} +195 -6
  46. package/.output/server/_ssr/{agents-C7B6RJ6-.mjs → agents-Bz10gz_m.mjs} +8 -8
  47. package/.output/server/_ssr/agents-CjbOUp3Z.mjs +206 -0
  48. package/.output/server/_ssr/{alert-DQvzE7bu.mjs → alert-C2WBWfzo.mjs} +2 -2
  49. package/.output/server/_ssr/card-r1cV-PCN.mjs +36 -0
  50. package/.output/server/_ssr/{client-BtVRVhQ2.mjs → client-Dk3SD8Il.mjs} +5 -1
  51. package/.output/server/_ssr/connection-status-BogAL-Fj.mjs +32 -0
  52. package/.output/server/_ssr/{connections-Df-aDpfJ.mjs → connections-Cqvb6Gw2.mjs} +372 -325
  53. package/.output/server/_ssr/connector-icon-DrhVbhs6.mjs +190 -0
  54. package/.output/server/_ssr/connectors-BfS5tQKr.mjs +81 -0
  55. package/.output/server/_ssr/{connectors-e0mf2pMs.mjs → connectors-DJI-rzPU.mjs} +10 -10
  56. package/.output/server/_ssr/{createServerRpc-DPDrnFdH.mjs → createServerRpc-DDrp2nLT.mjs} +2 -2
  57. package/.output/server/_ssr/{gaps-Cz76ZicM.mjs → gaps-CcvHiLcj.mjs} +15 -7
  58. package/.output/server/_ssr/inbox-D1ef761v.mjs +178 -0
  59. package/.output/server/_ssr/loop-form-DWwOR05R.mjs +346 -0
  60. package/.output/server/_ssr/{loops-D8WuK7Ke.mjs → loops-6WhUe_lc.mjs} +37 -27
  61. package/.output/server/_ssr/loops-DHPjzG-t.mjs +217 -0
  62. package/.output/server/_ssr/new-ChKBdcXJ.mjs +245 -0
  63. package/.output/server/_ssr/page-DZVxKvdn.mjs +120 -0
  64. package/.output/server/_ssr/{router-54IDWTVz.mjs → router-DdwREiOf.mjs} +162 -489
  65. package/.output/server/_ssr/runners-CK9Cx9fS.mjs +217 -0
  66. package/.output/server/_ssr/runners-CrznFPHt.mjs +140 -0
  67. package/.output/server/_ssr/{runners-Cj6z9Mqh.mjs → runners-W-pgo5yo.mjs} +5 -5
  68. package/.output/server/_ssr/{secrets-CRnpwGBh.mjs → secrets-CV5scFlt.mjs} +2 -2
  69. package/.output/server/_ssr/select-BtndHhHb.mjs +76 -0
  70. package/.output/server/_ssr/{setting-fields-DzBlslan.mjs → setting-fields-LNgywAW7.mjs} +25 -20
  71. package/.output/server/_ssr/ssr.mjs +43 -42
  72. package/.output/server/_ssr/{start-Vq3Oan3v.mjs → start-C_j-e4vF.mjs} +9 -4
  73. package/.output/server/_ssr/{switch-1pnd_bPJ.mjs → switch-odxNO_S4.mjs} +3 -3
  74. package/.output/server/_ssr/task-list-Dnmq_WeE.mjs +150 -0
  75. package/.output/server/_ssr/{tasks-Qtml5TlK.mjs → tasks-D-kXLgVP.mjs} +13 -13
  76. package/.output/server/_ssr/{tasks-BDqznm57.mjs → tasks-D7aUvA33.mjs} +44 -17
  77. package/.output/server/_ssr/{textarea-CHJTKtsJ.mjs → textarea-D2c_vTIC.mjs} +2 -2
  78. package/.output/server/_ssr/time-Cy8TZP0Z.mjs +13 -0
  79. package/.output/server/_ssr/use-live-tasks-BzhTkEOL.mjs +40 -0
  80. package/.output/server/_tanstack-start-manifest_v-2HUdn-2E.mjs +166 -0
  81. package/.output/server/_taskId-D3KkNJW1.mjs +263 -0
  82. package/.output/server/index.mjs +199 -164
  83. package/README.md +36 -6
  84. package/dist/cli.mjs +161 -8
  85. package/dist/dispatcher.mjs +671 -285
  86. package/dist/runner.mjs +333 -46
  87. package/drizzle/0025_prompt_tasks_done.sql +2 -0
  88. package/drizzle/0026_guidance_into_prompt.sql +3 -0
  89. package/drizzle/0027_relative_folders.sql +5 -0
  90. package/drizzle/0028_runner_version.sql +5 -0
  91. package/drizzle/meta/_journal.json +29 -1
  92. package/package.json +4 -2
  93. package/.output/public/assets/_connectorId-CkygNLny.js +0 -1
  94. package/.output/public/assets/_loopId-Cshlc5Ja.js +0 -1
  95. package/.output/public/assets/_taskId-BKDIJ_Fg.js +0 -1
  96. package/.output/public/assets/alert-BT2EVMJP.js +0 -1
  97. package/.output/public/assets/arrow-left-CWUZt6k2.js +0 -1
  98. package/.output/public/assets/arrow-right-dy_oOg_c.js +0 -1
  99. package/.output/public/assets/connection-status-DCqqmY_6.js +0 -1
  100. package/.output/public/assets/connector-icon-BUoK6Cru.js +0 -1
  101. package/.output/public/assets/connectors-CeLBtEtg.js +0 -1
  102. package/.output/public/assets/external-link-DC11orFA.js +0 -1
  103. package/.output/public/assets/gaps-BtFoD7mF.js +0 -1
  104. package/.output/public/assets/inbox-BYNjkNDJ.js +0 -1
  105. package/.output/public/assets/index-0yP3Jn_G.js +0 -18
  106. package/.output/public/assets/loader-B4Epchm_.js +0 -1
  107. package/.output/public/assets/loop-form-DnSJNc1k.js +0 -1
  108. package/.output/public/assets/loops-1CvbqO6m.js +0 -1
  109. package/.output/public/assets/new-m5NlqYQT.js +0 -1
  110. package/.output/public/assets/play-DD4xQgvi.js +0 -1
  111. package/.output/public/assets/preload-helper-CfdAYKD7.js +0 -3
  112. package/.output/public/assets/rolldown-runtime-CbXtAM7H.js +0 -1
  113. package/.output/public/assets/runners-D8QVl-Y6.js +0 -1
  114. package/.output/public/assets/select-Cppu-hti.js +0 -1
  115. package/.output/public/assets/setting-fields-bJ_gTG7Q.js +0 -3
  116. package/.output/public/assets/styles-ClTGaXU0.css +0 -2
  117. package/.output/public/assets/switch-hRIbNGOj.js +0 -1
  118. package/.output/public/assets/task-list-BAxjcVgr.js +0 -1
  119. package/.output/public/assets/textarea-Fyuzi6Vp.js +0 -1
  120. package/.output/public/assets/trash-2-BXZzBsoV.js +0 -1
  121. package/.output/public/assets/useValueChanged-VVyq4hb9.js +0 -1
  122. package/.output/server/_loopId-B0VG-eBC.mjs +0 -301
  123. package/.output/server/_ssr/connection-status-BZr3Y-IC.mjs +0 -31
  124. package/.output/server/_ssr/connector-icon-WvxwZ5TF.mjs +0 -56
  125. package/.output/server/_ssr/connectors-BU0IiRge.mjs +0 -107
  126. package/.output/server/_ssr/inbox-CAUBa1I3.mjs +0 -114
  127. package/.output/server/_ssr/loop-form-FqSaXiHl.mjs +0 -413
  128. package/.output/server/_ssr/loops-Dqoebe8q.mjs +0 -222
  129. package/.output/server/_ssr/new-bBsVaWD6.mjs +0 -169
  130. package/.output/server/_ssr/runners-D2UAdLyj.mjs +0 -106
  131. package/.output/server/_ssr/runners-DGWaVvWi.mjs +0 -116
  132. package/.output/server/_ssr/task-list-_qgBS9LU.mjs +0 -187
  133. package/.output/server/_tanstack-start-manifest_v-CvheE7ZF.mjs +0 -131
  134. package/.output/server/_taskId-V4QLBO2N.mjs +0 -256
@@ -0,0 +1,346 @@
1
+ import { i as __toESM } from "../_runtime.mjs";
2
+ import { K as require_jsx_runtime, q as require_react } from "../_libs/@base-ui/react+[...].mjs";
3
+ import { p as useNavigate } from "../_libs/@tanstack/react-router+[...].mjs";
4
+ import { b as writerName, f as isInboxOnly, l as connectorWorkflow, o as connectorAction, s as connectorManifest, x as writers } from "./connections-Cqvb6Gw2.mjs";
5
+ import { t as AGENT_MANIFESTS } from "./agents-B_qo1gpL.mjs";
6
+ import { F as ChevronRight } from "../_libs/lucide-react.mjs";
7
+ import { n as toast } from "../_libs/sonner.mjs";
8
+ import { C as saveLoop, g as Input$1, v as Button$1 } from "./router-DdwREiOf.mjs";
9
+ import { n as Field, s as Section } from "./page-DZVxKvdn.mjs";
10
+ import { a as SelectValue, i as SelectTrigger, n as SelectContent, r as SelectItem, t as Select$1 } from "./select-BtndHhHb.mjs";
11
+ import { t as Textarea } from "./textarea-D2c_vTIC.mjs";
12
+ import { n as initialFieldValues, t as SettingFieldInputs } from "./setting-fields-LNgywAW7.mjs";
13
+ import { n as AlertDescription, r as AlertTitle, t as Alert } from "./alert-C2WBWfzo.mjs";
14
+ //#region node_modules/.nitro/vite/services/ssr/assets/loop-form-DWwOR05R.js
15
+ var import_react = /* @__PURE__ */ __toESM(require_react());
16
+ var import_jsx_runtime = require_jsx_runtime();
17
+ var DEFAULT_AGENT = "__default";
18
+ /** Zero is stored as null: "as often as the dispatcher does" is not a duration. */
19
+ var POLL_CHOICES = [
20
+ {
21
+ value: "0",
22
+ label: "Every couple of minutes"
23
+ },
24
+ {
25
+ value: String(6e4),
26
+ label: "Every minute"
27
+ },
28
+ {
29
+ value: String(6e5),
30
+ label: "Every 10 minutes"
31
+ },
32
+ {
33
+ value: String(18e5),
34
+ label: "Every 30 minutes"
35
+ },
36
+ {
37
+ value: String(36e5),
38
+ label: "Every hour"
39
+ },
40
+ {
41
+ value: String(144e5),
42
+ label: "Every 4 hours"
43
+ }
44
+ ];
45
+ var RUNS_IN = {
46
+ checkout: "A fresh clone of the repository",
47
+ folder: "The folder this loop names",
48
+ temp: "A scratch folder with only what it was given"
49
+ };
50
+ var ANSWER = {
51
+ review: "A summary, plus comments on lines",
52
+ code: "A change to the code",
53
+ text: "One block of text"
54
+ };
55
+ /**
56
+ * The parts of a workflow a loop cannot change: what it asks the service,
57
+ * where the agent runs, and how the answer is read. Folded away, since they
58
+ * are worth checking once and never editing.
59
+ */
60
+ function WorkflowDetails({ workflow }) {
61
+ const rows = [];
62
+ if (workflow.watches) rows.push(["Looks for", /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", {
63
+ className: "font-mono text-xs",
64
+ children: workflow.watches
65
+ })]);
66
+ rows.push(["Runs in", RUNS_IN[workflow.runsIn ?? "temp"]]);
67
+ rows.push(["Answer", ANSWER[workflow.answer]]);
68
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", {
69
+ suppressHydrationWarning: true,
70
+ className: "group text-sm",
71
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("summary", {
72
+ className: "flex w-fit cursor-pointer list-none items-center gap-1 text-xs text-muted-foreground hover:text-foreground",
73
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronRight, { className: "size-3.5 transition-transform group-open:rotate-90" }), "Fixed by the trigger"]
74
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dl", {
75
+ className: "mt-2 grid grid-cols-[6rem_1fr] gap-x-3 gap-y-1.5 rounded-lg bg-muted/50 p-3 text-xs",
76
+ children: rows.map(([label, value]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
77
+ className: "contents",
78
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
79
+ className: "text-muted-foreground",
80
+ children: label
81
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: value })]
82
+ }, label))
83
+ })]
84
+ });
85
+ }
86
+ /**
87
+ * A loop is one of a connector's workflows with its own answers to the
88
+ * questions it asks: what narrows it, which agent runs it and with what
89
+ * words, and where the answer goes.
90
+ *
91
+ * Serves a loop that exists and one that does not. A loop with no id has been
92
+ * chosen and not saved, so leaving this page is the end of it.
93
+ */
94
+ function LoopForm({ loop, trigger }) {
95
+ const navigate = useNavigate();
96
+ const fresh = loop.id === null;
97
+ const connector = connectorManifest(loop.connectorId);
98
+ const workflow = connectorWorkflow(loop.connectorId, loop.workflowId);
99
+ const [name, setName] = (0, import_react.useState)(loop.name);
100
+ const [prompt, setPrompt] = (0, import_react.useState)(loop.prompt);
101
+ const [folder, setFolder] = (0, import_react.useState)(() => {
102
+ const saved = typeof loop.settings.folder === "string" ? loop.settings.folder : "";
103
+ return saved.startsWith("~/") ? saved.slice(2) : saved;
104
+ });
105
+ const [agentId, setAgentId] = (0, import_react.useState)(loop.agentId ?? DEFAULT_AGENT);
106
+ const [settings, setSettings] = (0, import_react.useState)(() => initialFieldValues(workflow?.settings ?? [], loop.settings));
107
+ const [pollEveryMs, setPollEveryMs] = (0, import_react.useState)(String(loop.pollEveryMs ?? 0));
108
+ const [actionConnectorId, setActionConnectorId] = (0, import_react.useState)(loop.actionConnectorId);
109
+ const [actionId, setActionId] = (0, import_react.useState)(loop.actionId);
110
+ const [actionTarget, setActionTarget] = (0, import_react.useState)(() => initialFieldValues(connectorAction(loop.actionConnectorId, loop.actionId)?.target ?? [], loop.actionTarget));
111
+ const [saving, setSaving] = (0, import_react.useState)(false);
112
+ const [written] = (0, import_react.useState)(loop.prompt.trim() !== "");
113
+ const action = connectorAction(actionConnectorId, actionId);
114
+ const sourceName = connector?.name ?? loop.connectorId;
115
+ const chooseDestination = (connectorId, id) => {
116
+ setActionConnectorId(connectorId);
117
+ setActionId(id);
118
+ setActionTarget(initialFieldValues(connectorAction(connectorId, id)?.target ?? [], {}));
119
+ };
120
+ if (!workflow) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Alert, {
121
+ variant: "destructive",
122
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertTitle, { children: "This workflow is no longer offered" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AlertDescription, { children: [
123
+ sourceName,
124
+ " no longer has ",
125
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: loop.workflowId }),
126
+ ", so this loop cannot run or be edited. You can still delete it."
127
+ ] })]
128
+ });
129
+ const save = async () => {
130
+ setSaving(true);
131
+ try {
132
+ const saved = await saveLoop({ data: {
133
+ id: loop.id,
134
+ name,
135
+ connectorId: loop.connectorId,
136
+ workflowId: loop.workflowId,
137
+ prompt,
138
+ guidance: null,
139
+ agentId: agentId === DEFAULT_AGENT ? null : agentId,
140
+ settings: workflow.runsIn === "folder" ? {
141
+ ...settings,
142
+ folder
143
+ } : settings,
144
+ actionConnectorId,
145
+ actionId,
146
+ actionTarget,
147
+ pollEveryMs: Number(pollEveryMs) || null,
148
+ enabled: loop.enabled
149
+ } });
150
+ toast.success(fresh ? `Added ${saved.name}` : `Saved ${saved.name}`);
151
+ await navigate({
152
+ to: "/loops/$loopId",
153
+ params: { loopId: saved.id }
154
+ });
155
+ } catch (error) {
156
+ toast.error(error instanceof Error ? error.message : String(error));
157
+ } finally {
158
+ setSaving(false);
159
+ }
160
+ };
161
+ const crosses = action && actionConnectorId !== loop.connectorId;
162
+ const flattens = action && workflow.answer === "review" && !action.accepts.includes("review");
163
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
164
+ className: "flex flex-col",
165
+ children: [
166
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Section, {
167
+ title: "When",
168
+ description: `When ${workflow.trigger}.`,
169
+ children: [
170
+ trigger,
171
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, {
172
+ label: "Name",
173
+ htmlFor: "loop-name",
174
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input$1, {
175
+ id: "loop-name",
176
+ value: name,
177
+ onChange: (event) => setName(event.target.value)
178
+ })
179
+ }),
180
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingFieldInputs, {
181
+ fields: workflow.settings,
182
+ values: settings,
183
+ idPrefix: "setting-",
184
+ onChange: (key, value) => setSettings((prev) => ({
185
+ ...prev,
186
+ [key]: value
187
+ }))
188
+ }),
189
+ loop.connectorId === "schedule" ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, {
190
+ label: "Check",
191
+ htmlFor: "loop-every",
192
+ className: "sm:max-w-xs",
193
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
194
+ value: pollEveryMs,
195
+ onValueChange: (value) => value && setPollEveryMs(value),
196
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
197
+ id: "loop-every",
198
+ className: "w-full",
199
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { children: (value) => POLL_CHOICES.find((choice) => choice.value === value)?.label ?? `Every ${Math.round(Number(value) / 6e4)} minutes` })
200
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: POLL_CHOICES.map((choice) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
201
+ value: choice.value,
202
+ children: choice.label
203
+ }, choice.value)) })]
204
+ })
205
+ }),
206
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkflowDetails, { workflow })
207
+ ]
208
+ }),
209
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Section, {
210
+ title: "Agent",
211
+ children: [
212
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, {
213
+ label: "Run with",
214
+ htmlFor: "loop-agent",
215
+ className: "sm:max-w-xs",
216
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
217
+ value: agentId,
218
+ onValueChange: (value) => value && setAgentId(value),
219
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
220
+ id: "loop-agent",
221
+ className: "w-full",
222
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { children: (value) => value === DEFAULT_AGENT ? "Default agent" : AGENT_MANIFESTS.find((entry) => entry.id === value)?.name ?? value })
223
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
224
+ value: DEFAULT_AGENT,
225
+ children: "Default agent"
226
+ }), AGENT_MANIFESTS.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
227
+ value: entry.id,
228
+ children: entry.name
229
+ }, entry.id))] })]
230
+ })
231
+ }),
232
+ workflow.runsIn === "folder" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, {
233
+ label: "Folder",
234
+ htmlFor: "loop-folder",
235
+ hint: "Inside the runner's home folder. The agent works here.",
236
+ className: "sm:max-w-sm",
237
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
238
+ className: "flex items-center rounded-lg border border-input focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50",
239
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
240
+ className: "pl-2.5 font-mono text-sm text-muted-foreground",
241
+ children: "~/"
242
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input$1, {
243
+ id: "loop-folder",
244
+ value: folder,
245
+ placeholder: "code/web",
246
+ onChange: (event) => setFolder(event.target.value),
247
+ className: "border-0 pl-1 font-mono shadow-none focus-visible:ring-0"
248
+ })]
249
+ })
250
+ }) : null,
251
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, {
252
+ label: "Prompt",
253
+ htmlFor: "loop-prompt",
254
+ hint: written ? `Starts as “${workflow.name}”. Changes stay on this loop.` : void 0,
255
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Textarea, {
256
+ id: "loop-prompt",
257
+ rows: 10,
258
+ className: "font-mono text-xs",
259
+ placeholder: "What should the agent do?",
260
+ value: prompt,
261
+ onChange: (event) => setPrompt(event.target.value)
262
+ })
263
+ })
264
+ ]
265
+ }),
266
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Section, {
267
+ title: "Answer",
268
+ children: [
269
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
270
+ className: "grid gap-4 sm:grid-cols-2",
271
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, {
272
+ label: "Send to",
273
+ htmlFor: "loop-writer",
274
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
275
+ value: actionConnectorId,
276
+ onValueChange: (value) => value && chooseDestination(value, connectorManifest(value).actions[0].id),
277
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
278
+ id: "loop-writer",
279
+ className: "w-full",
280
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { children: (value) => writerName(value) })
281
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: writers().map((writer) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
282
+ value: writer.id,
283
+ children: writerName(writer.id)
284
+ }, writer.id)) })]
285
+ })
286
+ }), isInboxOnly(actionConnectorId, actionId) ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, {
287
+ label: "As",
288
+ htmlFor: "loop-action",
289
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
290
+ value: actionId,
291
+ onValueChange: (value) => value && chooseDestination(actionConnectorId, value),
292
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
293
+ id: "loop-action",
294
+ className: "w-full",
295
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { children: (value) => connectorAction(actionConnectorId, value)?.name ?? value })
296
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: (connectorManifest(actionConnectorId)?.actions ?? []).map((entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
297
+ value: entry.id,
298
+ children: entry.name
299
+ }, entry.id)) })]
300
+ })
301
+ })]
302
+ }),
303
+ crosses && !isInboxOnly(actionConnectorId, actionId) ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
304
+ className: "-mt-2 text-xs text-muted-foreground",
305
+ children: [
306
+ sourceName,
307
+ " and ",
308
+ writerName(actionConnectorId),
309
+ " both need a connection."
310
+ ]
311
+ }) : flattens ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
312
+ className: "-mt-2 text-xs text-muted-foreground",
313
+ children: "Line comments arrive as text, with file and line written in."
314
+ }) : null,
315
+ action && action.target.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingFieldInputs, {
316
+ fields: action.target,
317
+ values: actionTarget,
318
+ idPrefix: "target-",
319
+ onChange: (key, value) => setActionTarget((prev) => ({
320
+ ...prev,
321
+ [key]: value
322
+ }))
323
+ }) : null
324
+ ]
325
+ }),
326
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
327
+ className: "flex items-center gap-2 border-t pt-6 md:pl-52",
328
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
329
+ onClick: save,
330
+ disabled: saving,
331
+ children: saving ? "Saving..." : fresh ? "Add loop" : "Save changes"
332
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
333
+ variant: "ghost",
334
+ onClick: () => navigate(fresh ? { to: "/loops/new" } : {
335
+ to: "/loops/$loopId",
336
+ params: { loopId: loop.id }
337
+ }),
338
+ disabled: saving,
339
+ children: "Cancel"
340
+ })]
341
+ })
342
+ ]
343
+ });
344
+ }
345
+ //#endregion
346
+ export { LoopForm as t };
@@ -1,16 +1,16 @@
1
- import { n as createServerFn } from "./ssr.mjs";
1
+ import { r as createServerFn } from "./ssr.mjs";
2
2
  import { d as and, f as eq, l as asc, p as inArray, v as sql } from "../_libs/drizzle-orm.mjs";
3
- import { a as connections, c as loops, h as signals, s as db } from "./client-BtVRVhQ2.mjs";
4
- import { a as connectorAction, c as connectorWorkflow, l as credentialForConnector, o as connectorManifest, s as connectorRuntime } from "./connections-Df-aDpfJ.mjs";
5
- import { i as listAgents, n as agentManifest, r as agentRuntime, s as settingsFor } from "./agents-DG643yoJ.mjs";
6
- import { t as availableAgentIds } from "./runners-D2UAdLyj.mjs";
7
- import { i as enqueueSignal, n as defaultAgentFor } from "./tasks-BDqznm57.mjs";
8
- import { t as createServerRpc } from "./createServerRpc-DPDrnFdH.mjs";
3
+ import { a as connections, c as loops, h as signals, s as db } from "./client-Dk3SD8Il.mjs";
4
+ import { c as connectorRuntime, l as connectorWorkflow, o as connectorAction, s as connectorManifest, u as credentialForConnector } from "./connections-Cqvb6Gw2.mjs";
5
+ import { n as availableAgentIds } from "./runners-CK9Cx9fS.mjs";
6
+ import { i as listAgents, n as agentManifest, r as agentRuntime, s as settingsFor } from "./agents-B_qo1gpL.mjs";
7
+ import { i as enqueueSignal, n as defaultAgentFor, o as folderIsLegacy, p as normalizeFolder } from "./tasks-D7aUvA33.mjs";
8
+ import { t as createServerRpc } from "./createServerRpc-DDrp2nLT.mjs";
9
9
  import { randomUUID } from "node:crypto";
10
10
  import { mkdtempSync } from "node:fs";
11
11
  import { join } from "node:path";
12
12
  import { tmpdir } from "node:os";
13
- //#region node_modules/.nitro/vite/services/ssr/assets/loops-D8WuK7Ke.js
13
+ //#region node_modules/.nitro/vite/services/ssr/assets/loops-6WhUe_lc.js
14
14
  var NAME_LIMIT = 80;
15
15
  var PROMPT_LIMIT = 8e3;
16
16
  var GUIDANCE_LIMIT = 4e3;
@@ -76,16 +76,22 @@ function validate(draft) {
76
76
  if (prompt.length > PROMPT_LIMIT) throw new Error(`Keep what you ask under ${PROMPT_LIMIT} characters.`);
77
77
  const guidance = draft.guidance?.trim() || null;
78
78
  if (guidance && guidance.length > GUIDANCE_LIMIT) throw new Error(`Keep the guidance under ${GUIDANCE_LIMIT} characters.`);
79
+ let folder;
79
80
  if (workflow.runsIn === "folder") {
80
- const folder = draft.settings.folder;
81
- if (typeof folder !== "string" || folder.trim() === "") throw new Error("Say which folder this should work in.");
81
+ const given = typeof draft.settings.folder === "string" ? draft.settings.folder : "";
82
+ folder = normalizeFolder(given);
83
+ if (!folder) throw new Error("Say which folder this should work in.");
84
+ if (folderIsLegacy(folder)) throw new Error("Give the folder relative to the runner's home, such as code/web.");
82
85
  }
83
86
  return {
84
87
  ...draft,
85
88
  name,
86
89
  prompt,
87
90
  guidance,
88
- settings: cleanConditions(workflow.settings, draft.settings),
91
+ settings: {
92
+ ...cleanConditions(workflow.settings, draft.settings),
93
+ ...folder ? { folder } : {}
94
+ },
89
95
  actionTarget: cleanConditions(action.target, draft.actionTarget)
90
96
  };
91
97
  }
@@ -176,13 +182,15 @@ async function loopReadiness() {
176
182
  const connected = await db().selectDistinct({ connectorId: connections.connectorId }).from(connections).all();
177
183
  const available = await availableAgentIds();
178
184
  const host = await availableAgentIds(true);
185
+ const folder = await availableAgentIds(false, 2);
179
186
  const chosen = agents.find((agent) => agent.isDefault)?.agentId ?? null;
180
187
  const defaultAgentId = (chosen && available.includes(chosen) ? chosen : null) ?? (available.length === 1 ? available[0] : null);
181
188
  return {
182
189
  connectedConnectorIds: connected.map((row) => row.connectorId),
183
190
  defaultAgentId,
184
191
  availableAgentIds: available,
185
- hostAgentIds: host
192
+ hostAgentIds: host,
193
+ folderAgentIds: folder
186
194
  };
187
195
  }
188
196
  var NOTHING_ASKED = "nothing in it was being asked of you";
@@ -299,12 +307,13 @@ async function pollLoop(loop, claimed) {
299
307
  try {
300
308
  const manifest = connectorManifest(loop.connectorId);
301
309
  if (!manifest) throw new Error(`Unknown connector: ${loop.connectorId}`);
302
- if (!connectorWorkflow(loop.connectorId, loop.workflowId)) throw new Error(`${manifest.name} no longer offers ${loop.workflowId}.`);
310
+ const workflow = connectorWorkflow(loop.connectorId, loop.workflowId);
311
+ if (!workflow) throw new Error(`${manifest.name} no longer offers ${loop.workflowId}.`);
303
312
  const runtime = connectorRuntime(loop.connectorId);
304
313
  if (!runtime.poll) throw new Error(`${manifest.name} cannot watch for anything yet.`);
305
314
  const { credential } = await credentialForConnector(loop.connectorId);
306
315
  const answer = await runtime.poll({
307
- workflowId: loop.workflowId,
316
+ workflowId: workflow.triggerId,
308
317
  settings: loop.settings,
309
318
  credential,
310
319
  cursor: loop.pollCursor ?? null
@@ -398,14 +407,15 @@ async function loopPollState(loopId) {
398
407
  };
399
408
  }
400
409
  /**
401
- * Run everything the loop is holding. Asked for explicitly, because it is the
402
- * one moment a loop does a month of work at once, and because something held
403
- * back for being too big is being run against the connector's advice.
410
+ * Run what the loop is holding: one item when `key` is given, or all of it.
411
+ * Asked for explicitly, because something held back for being too big is
412
+ * being run against the connector's advice.
404
413
  */
405
- async function runBacklog(loopId) {
414
+ async function runBacklog(loopId, key) {
406
415
  const loop = await db().select().from(loops).where(eq(loops.id, loopId)).get();
407
416
  if (!loop) throw new Error("Loop not found");
408
- const waiting = await db().select().from(signals).where(and(eq(signals.loopId, loopId), inArray(signals.outcome, [...WAITING]))).all();
417
+ const waiting = await db().select().from(signals).where(and(eq(signals.loopId, loopId), inArray(signals.outcome, [...WAITING]), key === void 0 ? void 0 : eq(signals.key, key))).all();
418
+ if (key !== void 0 && waiting.length === 0) throw new Error("That item is no longer waiting.");
409
419
  for (const row of waiting) {
410
420
  const task = await enqueueSignal({
411
421
  loop,
@@ -451,13 +461,13 @@ var getLoopById_createServerFn_handler = createServerRpc({
451
461
  name: "getLoopById",
452
462
  filename: "src/server/functions/loops.ts"
453
463
  }, (opts) => getLoopById.__executeServer(opts));
454
- var getLoopById = createServerFn({ method: "GET" }).inputValidator((data) => data).handler(getLoopById_createServerFn_handler, async ({ data }) => await getLoop(data.id));
464
+ var getLoopById = createServerFn({ method: "GET" }).validator((data) => data).handler(getLoopById_createServerFn_handler, async ({ data }) => await getLoop(data.id));
455
465
  var saveLoop_createServerFn_handler = createServerRpc({
456
466
  id: "e91fc2fd51633c7ca9184fea4beb66fe775ed0b4e6323111a4690da916a78b00",
457
467
  name: "saveLoop",
458
468
  filename: "src/server/functions/loops.ts"
459
469
  }, (opts) => saveLoop.__executeServer(opts));
460
- var saveLoop = createServerFn({ method: "POST" }).inputValidator(edit).handler(saveLoop_createServerFn_handler, async ({ data }) => {
470
+ var saveLoop = createServerFn({ method: "POST" }).validator(edit).handler(saveLoop_createServerFn_handler, async ({ data }) => {
461
471
  const { id, ...values } = data;
462
472
  return id ? await updateLoop(id, values) : await createLoop(values);
463
473
  });
@@ -466,13 +476,13 @@ var toggleLoop_createServerFn_handler = createServerRpc({
466
476
  name: "toggleLoop",
467
477
  filename: "src/server/functions/loops.ts"
468
478
  }, (opts) => toggleLoop.__executeServer(opts));
469
- var toggleLoop = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(toggleLoop_createServerFn_handler, async ({ data }) => await setLoopEnabled(data.id, data.enabled));
479
+ var toggleLoop = createServerFn({ method: "POST" }).validator((data) => data).handler(toggleLoop_createServerFn_handler, async ({ data }) => await setLoopEnabled(data.id, data.enabled));
470
480
  var removeLoop_createServerFn_handler = createServerRpc({
471
481
  id: "b5a89ca2d07f4ccf2827b5132ca52e7c7a42d584e702b0ab3963b2d5763c35b0",
472
482
  name: "removeLoop",
473
483
  filename: "src/server/functions/loops.ts"
474
484
  }, (opts) => removeLoop.__executeServer(opts));
475
- var removeLoop = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(removeLoop_createServerFn_handler, async ({ data }) => {
485
+ var removeLoop = createServerFn({ method: "POST" }).validator((data) => data).handler(removeLoop_createServerFn_handler, async ({ data }) => {
476
486
  await deleteLoop(data.id);
477
487
  return await listLoops();
478
488
  });
@@ -481,24 +491,24 @@ var reorderLoop_createServerFn_handler = createServerRpc({
481
491
  name: "reorderLoop",
482
492
  filename: "src/server/functions/loops.ts"
483
493
  }, (opts) => reorderLoop.__executeServer(opts));
484
- var reorderLoop = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(reorderLoop_createServerFn_handler, async ({ data }) => await moveLoop(data.id, data.direction));
494
+ var reorderLoop = createServerFn({ method: "POST" }).validator((data) => data).handler(reorderLoop_createServerFn_handler, async ({ data }) => await moveLoop(data.id, data.direction));
485
495
  var getLoopPollState_createServerFn_handler = createServerRpc({
486
496
  id: "4fd3855e3e9ed954e102071a55b98d91651c9b1e353161f7223768ed6f2b8c28",
487
497
  name: "getLoopPollState",
488
498
  filename: "src/server/functions/loops.ts"
489
499
  }, (opts) => getLoopPollState.__executeServer(opts));
490
- var getLoopPollState = createServerFn({ method: "GET" }).inputValidator((data) => data).handler(getLoopPollState_createServerFn_handler, async ({ data }) => await loopPollState(data.id));
500
+ var getLoopPollState = createServerFn({ method: "GET" }).validator((data) => data).handler(getLoopPollState_createServerFn_handler, async ({ data }) => await loopPollState(data.id));
491
501
  var runLoopBacklog_createServerFn_handler = createServerRpc({
492
502
  id: "65494cbfd06143dbd9a012808fbba21163d9902e387869503fb8b68b0674902b",
493
503
  name: "runLoopBacklog",
494
504
  filename: "src/server/functions/loops.ts"
495
505
  }, (opts) => runLoopBacklog.__executeServer(opts));
496
- var runLoopBacklog = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(runLoopBacklog_createServerFn_handler, async ({ data }) => ({ queued: await runBacklog(data.id) }));
506
+ var runLoopBacklog = createServerFn({ method: "POST" }).validator((data) => data).handler(runLoopBacklog_createServerFn_handler, async ({ data }) => ({ queued: await runBacklog(data.id, data.key) }));
497
507
  var lookLoopNow_createServerFn_handler = createServerRpc({
498
508
  id: "965ff21ee2f1e1f5a1cb78d50570cbad6f58ccdbdcc7955f02f816a9c0684f8e",
499
509
  name: "lookLoopNow",
500
510
  filename: "src/server/functions/loops.ts"
501
511
  }, (opts) => lookLoopNow.__executeServer(opts));
502
- var lookLoopNow = createServerFn({ method: "POST" }).inputValidator((data) => data).handler(lookLoopNow_createServerFn_handler, async ({ data }) => await lookNow(data.id));
512
+ var lookLoopNow = createServerFn({ method: "POST" }).validator((data) => data).handler(lookLoopNow_createServerFn_handler, async ({ data }) => await lookNow(data.id));
503
513
  //#endregion
504
514
  export { getLoopById_createServerFn_handler, getLoopPollState_createServerFn_handler, getLoopReadiness_createServerFn_handler, getLoopsPage_createServerFn_handler, lookLoopNow_createServerFn_handler, removeLoop_createServerFn_handler, reorderLoop_createServerFn_handler, runLoopBacklog_createServerFn_handler, saveLoop_createServerFn_handler, toggleLoop_createServerFn_handler };