@stigmer/react 3.6.0 → 3.8.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 (260) hide show
  1. package/billing/CreditLedgerTable.d.ts.map +1 -1
  2. package/billing/CreditLedgerTable.js +2 -7
  3. package/billing/CreditLedgerTable.js.map +1 -1
  4. package/channel/AgentChannelsPanel.d.ts.map +1 -1
  5. package/channel/AgentChannelsPanel.js +21 -2
  6. package/channel/AgentChannelsPanel.js.map +1 -1
  7. package/channel/ChannelConversationsDialog.d.ts +12 -4
  8. package/channel/ChannelConversationsDialog.d.ts.map +1 -1
  9. package/channel/ChannelConversationsDialog.js +18 -24
  10. package/channel/ChannelConversationsDialog.js.map +1 -1
  11. package/channel/providers.d.ts +23 -0
  12. package/channel/providers.d.ts.map +1 -1
  13. package/channel/providers.js +4 -0
  14. package/channel/providers.js.map +1 -1
  15. package/channel/useSaveAgentChannel.d.ts +5 -4
  16. package/channel/useSaveAgentChannel.d.ts.map +1 -1
  17. package/channel/useSaveAgentChannel.js +18 -4
  18. package/channel/useSaveAgentChannel.js.map +1 -1
  19. package/composer/ComposerToolbar.d.ts +6 -1
  20. package/composer/ComposerToolbar.d.ts.map +1 -1
  21. package/composer/ComposerToolbar.js +2 -2
  22. package/composer/ComposerToolbar.js.map +1 -1
  23. package/composer/SessionComposer.d.ts +9 -0
  24. package/composer/SessionComposer.d.ts.map +1 -1
  25. package/composer/SessionComposer.js +13 -2
  26. package/composer/SessionComposer.js.map +1 -1
  27. package/conversation/ConversationAttentionBanner.d.ts +22 -0
  28. package/conversation/ConversationAttentionBanner.d.ts.map +1 -0
  29. package/conversation/ConversationAttentionBanner.js +25 -0
  30. package/conversation/ConversationAttentionBanner.js.map +1 -0
  31. package/conversation/ConversationComposer.d.ts +41 -0
  32. package/conversation/ConversationComposer.d.ts.map +1 -0
  33. package/conversation/ConversationComposer.js +57 -0
  34. package/conversation/ConversationComposer.js.map +1 -0
  35. package/conversation/ConversationControlBanner.d.ts +52 -0
  36. package/conversation/ConversationControlBanner.d.ts.map +1 -0
  37. package/conversation/ConversationControlBanner.js +59 -0
  38. package/conversation/ConversationControlBanner.js.map +1 -0
  39. package/conversation/ConversationListPane.d.ts +68 -0
  40. package/conversation/ConversationListPane.d.ts.map +1 -0
  41. package/conversation/ConversationListPane.js +120 -0
  42. package/conversation/ConversationListPane.js.map +1 -0
  43. package/conversation/ConversationTimelineView.d.ts +46 -0
  44. package/conversation/ConversationTimelineView.d.ts.map +1 -0
  45. package/conversation/ConversationTimelineView.js +166 -0
  46. package/conversation/ConversationTimelineView.js.map +1 -0
  47. package/conversation/ConversationsWorkbench.d.ts +63 -0
  48. package/conversation/ConversationsWorkbench.d.ts.map +1 -0
  49. package/conversation/ConversationsWorkbench.js +165 -0
  50. package/conversation/ConversationsWorkbench.js.map +1 -0
  51. package/conversation/conversationPresentation.d.ts +137 -0
  52. package/conversation/conversationPresentation.d.ts.map +1 -0
  53. package/conversation/conversationPresentation.js +230 -0
  54. package/conversation/conversationPresentation.js.map +1 -0
  55. package/conversation/index.d.ts +26 -0
  56. package/conversation/index.d.ts.map +1 -0
  57. package/conversation/index.js +14 -0
  58. package/conversation/index.js.map +1 -0
  59. package/conversation/polling.d.ts +39 -0
  60. package/conversation/polling.d.ts.map +1 -0
  61. package/conversation/polling.js +39 -0
  62. package/conversation/polling.js.map +1 -0
  63. package/conversation/useConversation.d.ts +61 -0
  64. package/conversation/useConversation.d.ts.map +1 -0
  65. package/conversation/useConversation.js +153 -0
  66. package/conversation/useConversation.js.map +1 -0
  67. package/conversation/useConversationList.d.ts +98 -0
  68. package/conversation/useConversationList.d.ts.map +1 -0
  69. package/conversation/useConversationList.js +200 -0
  70. package/conversation/useConversationList.js.map +1 -0
  71. package/conversation/useConversationParticipation.d.ts +63 -0
  72. package/conversation/useConversationParticipation.d.ts.map +1 -0
  73. package/conversation/useConversationParticipation.js +84 -0
  74. package/conversation/useConversationParticipation.js.map +1 -0
  75. package/conversation/useConversationTimeline.d.ts +71 -0
  76. package/conversation/useConversationTimeline.d.ts.map +1 -0
  77. package/conversation/useConversationTimeline.js +152 -0
  78. package/conversation/useConversationTimeline.js.map +1 -0
  79. package/conversation/useConversationsWantsHumanCount.d.ts +42 -0
  80. package/conversation/useConversationsWantsHumanCount.d.ts.map +1 -0
  81. package/conversation/useConversationsWantsHumanCount.js +40 -0
  82. package/conversation/useConversationsWantsHumanCount.js.map +1 -0
  83. package/execution/useCreateAgentExecution.d.ts +14 -0
  84. package/execution/useCreateAgentExecution.d.ts.map +1 -1
  85. package/execution/useCreateAgentExecution.js +5 -0
  86. package/execution/useCreateAgentExecution.js.map +1 -1
  87. package/index.d.ts +6 -2
  88. package/index.d.ts.map +1 -1
  89. package/index.js +14 -5
  90. package/index.js.map +1 -1
  91. package/internal/Pagination.d.ts +15 -0
  92. package/internal/Pagination.d.ts.map +1 -0
  93. package/internal/Pagination.js +20 -0
  94. package/internal/Pagination.js.map +1 -0
  95. package/internal/tooltip.d.ts.map +1 -1
  96. package/internal/tooltip.js +7 -1
  97. package/internal/tooltip.js.map +1 -1
  98. package/internal/useAutoScroll.d.ts +25 -4
  99. package/internal/useAutoScroll.d.ts.map +1 -1
  100. package/internal/useAutoScroll.js +55 -15
  101. package/internal/useAutoScroll.js.map +1 -1
  102. package/internal/useFetch.d.ts.map +1 -1
  103. package/internal/useFetch.js +14 -4
  104. package/internal/useFetch.js.map +1 -1
  105. package/models/ModelSelector.d.ts +45 -1
  106. package/models/ModelSelector.d.ts.map +1 -1
  107. package/models/ModelSelector.js +49 -6
  108. package/models/ModelSelector.js.map +1 -1
  109. package/models/index.d.ts +2 -0
  110. package/models/index.d.ts.map +1 -1
  111. package/models/index.js +1 -0
  112. package/models/index.js.map +1 -1
  113. package/models/registry.d.ts +11 -0
  114. package/models/registry.d.ts.map +1 -1
  115. package/models/registry.js +3 -0
  116. package/models/registry.js.map +1 -1
  117. package/models/service-tier.d.ts +27 -0
  118. package/models/service-tier.d.ts.map +1 -0
  119. package/models/service-tier.js +34 -0
  120. package/models/service-tier.js.map +1 -0
  121. package/package.json +4 -4
  122. package/schedule/ScheduleDetailView.d.ts +31 -8
  123. package/schedule/ScheduleDetailView.d.ts.map +1 -1
  124. package/schedule/ScheduleDetailView.js +441 -29
  125. package/schedule/ScheduleDetailView.js.map +1 -1
  126. package/schedule/ScheduleForm.d.ts +7 -4
  127. package/schedule/ScheduleForm.d.ts.map +1 -1
  128. package/schedule/ScheduleForm.js +94 -8
  129. package/schedule/ScheduleForm.js.map +1 -1
  130. package/schedule/ScheduleRunsTable.d.ts +72 -0
  131. package/schedule/ScheduleRunsTable.d.ts.map +1 -0
  132. package/schedule/ScheduleRunsTable.js +161 -0
  133. package/schedule/ScheduleRunsTable.js.map +1 -0
  134. package/schedule/index.d.ts +6 -0
  135. package/schedule/index.d.ts.map +1 -1
  136. package/schedule/index.js +3 -0
  137. package/schedule/index.js.map +1 -1
  138. package/schedule/useScheduleRuns.d.ts +44 -0
  139. package/schedule/useScheduleRuns.d.ts.map +1 -0
  140. package/schedule/useScheduleRuns.js +48 -0
  141. package/schedule/useScheduleRuns.js.map +1 -0
  142. package/schedule/useTriggerSchedule.d.ts +23 -10
  143. package/schedule/useTriggerSchedule.d.ts.map +1 -1
  144. package/schedule/useTriggerSchedule.js +28 -9
  145. package/schedule/useTriggerSchedule.js.map +1 -1
  146. package/schedule/useUpdateScheduleSpec.d.ts +49 -0
  147. package/schedule/useUpdateScheduleSpec.d.ts.map +1 -0
  148. package/schedule/useUpdateScheduleSpec.js +70 -0
  149. package/schedule/useUpdateScheduleSpec.js.map +1 -0
  150. package/session/useNewSessionFlow.d.ts.map +1 -1
  151. package/session/useNewSessionFlow.js +1 -0
  152. package/session/useNewSessionFlow.js.map +1 -1
  153. package/session/useSessionConversation.d.ts +11 -0
  154. package/session/useSessionConversation.d.ts.map +1 -1
  155. package/session/useSessionConversation.js +1 -0
  156. package/session/useSessionConversation.js.map +1 -1
  157. package/session/useSessionPageFlow.d.ts.map +1 -1
  158. package/session/useSessionPageFlow.js +1 -0
  159. package/session/useSessionPageFlow.js.map +1 -1
  160. package/sharing/useSaveAgentShare.d.ts +10 -1
  161. package/sharing/useSaveAgentShare.d.ts.map +1 -1
  162. package/sharing/useSaveAgentShare.js +9 -0
  163. package/sharing/useSaveAgentShare.js.map +1 -1
  164. package/sidebar/WorkspaceSidebar.d.ts +12 -4
  165. package/sidebar/WorkspaceSidebar.d.ts.map +1 -1
  166. package/sidebar/WorkspaceSidebar.js +19 -7
  167. package/sidebar/WorkspaceSidebar.js.map +1 -1
  168. package/src/billing/CreditLedgerTable.tsx +3 -42
  169. package/src/channel/AgentChannelsPanel.tsx +33 -2
  170. package/src/channel/ChannelConversationsDialog.tsx +22 -25
  171. package/src/channel/__tests__/AgentChannelsPanel.test.tsx +36 -5
  172. package/src/channel/__tests__/ChannelConversationsDialog.test.tsx +38 -1
  173. package/src/channel/__tests__/useSaveAgentChannel.test.tsx +26 -0
  174. package/src/channel/providers.ts +27 -0
  175. package/src/channel/useSaveAgentChannel.ts +18 -4
  176. package/src/composer/ComposerToolbar.tsx +9 -0
  177. package/src/composer/SessionComposer.tsx +24 -1
  178. package/src/composer/__tests__/SessionComposer-serviceTier.test.tsx +157 -0
  179. package/src/conversation/ConversationAttentionBanner.tsx +77 -0
  180. package/src/conversation/ConversationComposer.tsx +156 -0
  181. package/src/conversation/ConversationControlBanner.tsx +193 -0
  182. package/src/conversation/ConversationListPane.tsx +459 -0
  183. package/src/conversation/ConversationTimelineView.tsx +435 -0
  184. package/src/conversation/ConversationsWorkbench.tsx +378 -0
  185. package/src/conversation/__tests__/ConversationAttentionBanner.test.tsx +82 -0
  186. package/src/conversation/__tests__/ConversationComposer.test.tsx +160 -0
  187. package/src/conversation/__tests__/ConversationControlBanner.test.tsx +190 -0
  188. package/src/conversation/__tests__/ConversationListPane.test.tsx +372 -0
  189. package/src/conversation/__tests__/ConversationTimelineView.layout.test.tsx +160 -0
  190. package/src/conversation/__tests__/ConversationTimelineView.test.tsx +405 -0
  191. package/src/conversation/__tests__/ConversationsWorkbench.test.tsx +860 -0
  192. package/src/conversation/__tests__/a11y/conversation-surfaces.a11y.test.tsx +235 -0
  193. package/src/conversation/__tests__/a11y/harness.tsx +100 -0
  194. package/src/conversation/__tests__/conversation-tooltips.layout.test.tsx +167 -0
  195. package/src/conversation/__tests__/conversationPollingComposition.test.tsx +138 -0
  196. package/src/conversation/__tests__/conversationPresentation.test.ts +289 -0
  197. package/src/conversation/__tests__/useConversation.test.tsx +243 -0
  198. package/src/conversation/__tests__/useConversationList.test.tsx +492 -0
  199. package/src/conversation/__tests__/useConversationParticipation.test.tsx +190 -0
  200. package/src/conversation/__tests__/useConversationTimeline.test.tsx +258 -0
  201. package/src/conversation/__tests__/useConversationsWantsHumanCount.test.tsx +102 -0
  202. package/src/conversation/conversationPresentation.ts +305 -0
  203. package/src/conversation/index.ts +80 -0
  204. package/src/conversation/polling.ts +41 -0
  205. package/src/conversation/useConversation.ts +221 -0
  206. package/src/conversation/useConversationList.ts +328 -0
  207. package/src/conversation/useConversationParticipation.ts +178 -0
  208. package/src/conversation/useConversationTimeline.ts +244 -0
  209. package/src/conversation/useConversationsWantsHumanCount.ts +86 -0
  210. package/src/execution/useCreateAgentExecution.ts +18 -0
  211. package/src/index.ts +76 -4
  212. package/src/internal/Pagination.tsx +72 -0
  213. package/src/internal/__tests__/fake-timer-slices.ts +29 -0
  214. package/src/internal/__tests__/useAutoScroll.layout.test.tsx +143 -0
  215. package/src/internal/__tests__/useAutoScroll.test.tsx +62 -5
  216. package/src/internal/__tests__/useFetch-poll-under-render-pressure.test.tsx +79 -0
  217. package/src/internal/tooltip.tsx +6 -1
  218. package/src/internal/useAutoScroll.ts +66 -20
  219. package/src/internal/useFetch.ts +15 -4
  220. package/src/models/ModelSelector.tsx +133 -4
  221. package/src/models/__tests__/ModelSelector-serviceTier.test.tsx +256 -0
  222. package/src/models/__tests__/useModelRegistry.test.tsx +15 -0
  223. package/src/models/index.ts +2 -0
  224. package/src/models/registry.ts +17 -0
  225. package/src/models/service-tier.ts +49 -0
  226. package/src/schedule/ScheduleDetailView.tsx +1179 -111
  227. package/src/schedule/ScheduleForm.tsx +218 -7
  228. package/src/schedule/ScheduleRunsTable.tsx +424 -0
  229. package/src/schedule/__tests__/ScheduleDetailView.test.tsx +431 -8
  230. package/src/schedule/__tests__/scheduleCreation.test.tsx +197 -0
  231. package/src/schedule/__tests__/scheduleHooks.test.tsx +94 -7
  232. package/src/schedule/index.ts +12 -0
  233. package/src/schedule/useScheduleRuns.ts +93 -0
  234. package/src/schedule/useTriggerSchedule.ts +39 -14
  235. package/src/schedule/useUpdateScheduleSpec.ts +108 -0
  236. package/src/session/useNewSessionFlow.ts +1 -0
  237. package/src/session/useSessionConversation.ts +12 -0
  238. package/src/session/useSessionPageFlow.ts +1 -0
  239. package/src/sharing/__tests__/useSaveAgentShare.test.tsx +55 -1
  240. package/src/sharing/__tests__/useShareToolReadiness.test.tsx +1 -0
  241. package/src/sharing/useSaveAgentShare.ts +19 -1
  242. package/src/sidebar/WorkspaceSidebar.tsx +62 -3
  243. package/src/sidebar/__tests__/WorkspaceSidebar.test.tsx +56 -1
  244. package/src/workflow/__tests__/inspector-forms.test.tsx +119 -2
  245. package/src/workflow/inspector/forms/AgentCallForm.tsx +260 -37
  246. package/src/workflow/inspector/tabs/RuntimeTab.tsx +72 -33
  247. package/src/workflow/starter-workflow-yaml.ts +2 -1
  248. package/styles.css +1 -1
  249. package/workflow/inspector/forms/AgentCallForm.d.ts +18 -2
  250. package/workflow/inspector/forms/AgentCallForm.d.ts.map +1 -1
  251. package/workflow/inspector/forms/AgentCallForm.js +87 -9
  252. package/workflow/inspector/forms/AgentCallForm.js.map +1 -1
  253. package/workflow/inspector/tabs/RuntimeTab.d.ts +2 -1
  254. package/workflow/inspector/tabs/RuntimeTab.d.ts.map +1 -1
  255. package/workflow/inspector/tabs/RuntimeTab.js +31 -5
  256. package/workflow/inspector/tabs/RuntimeTab.js.map +1 -1
  257. package/workflow/starter-workflow-yaml.d.ts +1 -1
  258. package/workflow/starter-workflow-yaml.d.ts.map +1 -1
  259. package/workflow/starter-workflow-yaml.js +2 -1
  260. package/workflow/starter-workflow-yaml.js.map +1 -1
@@ -17,8 +17,24 @@ const inputClass =
17
17
  * Specialized configuration form for `agent_call` tasks.
18
18
  *
19
19
  * Organizes agent-specific fields into semantic sections: Agent identity,
20
- * harness selection, message prompt, environment variables, execution
21
- * config (model, timeout, temperature, cost cap), and structured output.
20
+ * harness selection, message prompt, environment variables, run config
21
+ * (model, budget per run), and structured output.
22
+ *
23
+ * The run config edits the shared `run_config` block (the
24
+ * agentexecution RunConfig vocabulary, stigmer/stigmer#358): model
25
+ * override plus a per-run USD budget that the runner actually
26
+ * enforces. `max_tool_rounds` is API-reachable but deliberately absent
27
+ * from the form (DD-018 D-5: an implementation knob, not a user
28
+ * concept). Assembly follows the schedule form's empty-means-omit rule:
29
+ * a blank field must not become a zero override.
30
+ *
31
+ * Workspace and environments edit the shared vocabulary's
32
+ * `workspace_entries` (git-only at this surface) and `environment_refs`
33
+ * as controlled structured rows. The schedule form's richer components
34
+ * (WorkspaceEditor with the GitHub repo picker, EnvironmentPicker) are
35
+ * data-connected and need org context this inspector tree does not
36
+ * carry yet — adopting them here is a named follow-up, not a silent
37
+ * divergence.
22
38
  *
23
39
  * Composes primitive field controls rather than rendering a generic
24
40
  * schema form — following the research report guidance that "the forms
@@ -31,7 +47,7 @@ export const AgentCallForm = memo(function AgentCallForm({
31
47
  onFieldChange,
32
48
  }: AgentCallFormProps) {
33
49
  const config = node.config as Record<string, unknown>;
34
- const execConfig = (config.config ?? {}) as Record<string, unknown>;
50
+ const runConfig = (config.run_config ?? {}) as Record<string, unknown>;
35
51
  const outputContract = (config.output ?? {}) as Record<string, unknown>;
36
52
  const hasOutput = Object.keys(outputContract).length > 0;
37
53
  const [showOutput, setShowOutput] = useState(hasOutput);
@@ -43,14 +59,18 @@ export const AgentCallForm = memo(function AgentCallForm({
43
59
  [onFieldChange],
44
60
  );
45
61
 
46
- const handleExecConfigChange = useCallback(
62
+ const handleRunConfigChange = useCallback(
47
63
  (field: string, value: unknown) => {
48
- onFieldChange("config", {
49
- ...execConfig,
64
+ const next: Record<string, unknown> = {
65
+ ...runConfig,
50
66
  [field]: value === "" ? undefined : value,
51
- });
67
+ };
68
+ for (const key of Object.keys(next)) {
69
+ if (next[key] === undefined) delete next[key];
70
+ }
71
+ onFieldChange("run_config", Object.keys(next).length > 0 ? next : undefined);
52
72
  },
53
- [execConfig, onFieldChange],
73
+ [runConfig, onFieldChange],
54
74
  );
55
75
 
56
76
  const handleOutputChange = useCallback(
@@ -165,54 +185,45 @@ export const AgentCallForm = memo(function AgentCallForm({
165
185
  </section>
166
186
  )}
167
187
 
168
- {/* Execution config */}
188
+ {/* Run config */}
169
189
  <section className="flex flex-col gap-2">
170
190
  <SectionLabel>Execution</SectionLabel>
171
191
  <FieldRow label="Model" hint="Override the agent's default model">
172
192
  <input
173
193
  type="text"
174
- value={typeof execConfig.model === "string" ? execConfig.model : ""}
175
- onChange={(e) => handleExecConfigChange("model", e.target.value)}
194
+ value={typeof runConfig.model_name === "string" ? runConfig.model_name : ""}
195
+ onChange={(e) => handleRunConfigChange("model_name", e.target.value)}
176
196
  placeholder="Agent default"
177
197
  className={inputClass}
178
198
  data-testid="agent-call-model-input"
179
199
  />
180
200
  </FieldRow>
181
- <FieldRow label="Timeout" hint="Seconds (1–3600)">
182
- <input
183
- type="number"
184
- value={typeof execConfig.timeout === "number" ? execConfig.timeout : ""}
185
- onChange={(e) => handleExecConfigChange("timeout", e.target.value ? parseInt(e.target.value, 10) : undefined)}
186
- placeholder="300"
187
- min={1}
188
- max={3600}
189
- className={inputClass}
190
- />
191
- </FieldRow>
192
- <FieldRow label="Temperature" hint="0.0 (deterministic) to 1.0 (creative)">
193
- <input
194
- type="number"
195
- value={typeof execConfig.temperature === "number" ? execConfig.temperature : ""}
196
- onChange={(e) => handleExecConfigChange("temperature", e.target.value ? parseFloat(e.target.value) : undefined)}
197
- placeholder="0.7"
198
- min={0}
199
- max={1}
200
- step={0.1}
201
- className={inputClass}
202
- />
203
- </FieldRow>
204
- <FieldRow label="Max cost" hint="Per-task cap in micro-USD (1 USD = 1,000,000)">
201
+ <FieldRow label="Budget per run (USD)" hint="The call stops when its estimated cost reaches this amount">
205
202
  <input
206
203
  type="number"
207
- value={typeof execConfig.max_cost_micros === "number" ? execConfig.max_cost_micros : ""}
208
- onChange={(e) => handleExecConfigChange("max_cost_micros", e.target.value ? parseInt(e.target.value, 10) : undefined)}
204
+ value={typeof runConfig.max_cost_usd === "number" ? runConfig.max_cost_usd : ""}
205
+ onChange={(e) => handleRunConfigChange("max_cost_usd", e.target.value ? parseFloat(e.target.value) : undefined)}
209
206
  placeholder="No limit"
210
207
  min={0}
208
+ step={0.05}
211
209
  className={inputClass}
210
+ data-testid="agent-call-budget-input"
212
211
  />
213
212
  </FieldRow>
214
213
  </section>
215
214
 
215
+ {/* Workspace (git-only at this surface) */}
216
+ <WorkspaceEntriesSection
217
+ entries={config.workspace_entries}
218
+ onChange={(entries) => onFieldChange("workspace_entries", entries)}
219
+ />
220
+
221
+ {/* Environments */}
222
+ <EnvironmentRefsSection
223
+ refs={config.environment_refs}
224
+ onChange={(refs) => onFieldChange("environment_refs", refs)}
225
+ />
226
+
216
227
  {/* Structured output */}
217
228
  <section className="flex flex-col gap-2">
218
229
  <div className="flex items-center gap-2">
@@ -278,6 +289,218 @@ export const AgentCallForm = memo(function AgentCallForm({
278
289
  );
279
290
  });
280
291
 
292
+ // ---------------------------------------------------------------------------
293
+ // Workspace entries (git-only structured rows)
294
+ // ---------------------------------------------------------------------------
295
+
296
+ interface WorkspaceEntryRow {
297
+ readonly name?: string;
298
+ readonly source?: { readonly git_repo?: { readonly url?: string; readonly branch?: string } };
299
+ }
300
+
301
+ /**
302
+ * Edits `workspace_entries` as structured URL + branch rows. Only git
303
+ * sources exist at this surface (write-time validation rejects
304
+ * local_path — no client is connected when a workflow task fires), so
305
+ * the row model is deliberately git-shaped. Private repos need an
306
+ * org-visibility Environment holding GITHUB_TOKEN bound under
307
+ * Environments below (DD-018 D-4).
308
+ */
309
+ function WorkspaceEntriesSection({
310
+ entries,
311
+ onChange,
312
+ }: {
313
+ entries: unknown;
314
+ onChange: (entries: WorkspaceEntryRow[] | undefined) => void;
315
+ }) {
316
+ const rows = useMemo<readonly WorkspaceEntryRow[]>(
317
+ () => (Array.isArray(entries) ? (entries as WorkspaceEntryRow[]) : []),
318
+ [entries],
319
+ );
320
+
321
+ const commit = useCallback(
322
+ (next: WorkspaceEntryRow[]) => {
323
+ onChange(next.length > 0 ? next : undefined);
324
+ },
325
+ [onChange],
326
+ );
327
+
328
+ const updateRow = useCallback(
329
+ (index: number, url: string, branch: string) => {
330
+ const next = rows.map((row, i) => {
331
+ if (i !== index) return row;
332
+ const gitRepo: { url: string; branch?: string } = { url };
333
+ if (branch) gitRepo.branch = branch;
334
+ return { ...(row.name ? { name: row.name } : {}), source: { git_repo: gitRepo } };
335
+ });
336
+ commit([...next]);
337
+ },
338
+ [rows, commit],
339
+ );
340
+
341
+ return (
342
+ <section className="flex flex-col gap-2">
343
+ <div className="flex items-center justify-between">
344
+ <SectionLabel>Workspace</SectionLabel>
345
+ <button
346
+ type="button"
347
+ onClick={() => commit([...rows, { source: { git_repo: { url: "" } } }])}
348
+ className="text-[10px] font-medium text-[var(--stgm-primary,#6366f1)]"
349
+ data-testid="agent-call-workspace-add"
350
+ >
351
+ + Add repository
352
+ </button>
353
+ </div>
354
+ {rows.length === 0 ? (
355
+ <p className="text-[10px] leading-tight text-[var(--stgm-muted-foreground,#737373)]">
356
+ Git repositories the agent works on. Private repos need an org-shared
357
+ environment holding GITHUB_TOKEN bound under Environments.
358
+ </p>
359
+ ) : (
360
+ <div className="flex flex-col gap-1.5">
361
+ {rows.map((row, i) => {
362
+ const gitRepo = row.source?.git_repo ?? {};
363
+ return (
364
+ <div key={i} className="flex items-start gap-1">
365
+ <input
366
+ type="text"
367
+ value={gitRepo.url ?? ""}
368
+ onChange={(e) => updateRow(i, e.target.value, gitRepo.branch ?? "")}
369
+ placeholder="https://github.com/org/repo"
370
+ className={`${inputClass} flex-1`}
371
+ data-testid={`agent-call-workspace-url-${i}`}
372
+ />
373
+ <input
374
+ type="text"
375
+ value={gitRepo.branch ?? ""}
376
+ onChange={(e) => updateRow(i, gitRepo.url ?? "", e.target.value)}
377
+ placeholder="branch"
378
+ className={`${inputClass} w-1/4`}
379
+ data-testid={`agent-call-workspace-branch-${i}`}
380
+ />
381
+ <button
382
+ type="button"
383
+ onClick={() => commit(rows.filter((_, j) => j !== i))}
384
+ aria-label={`Remove workspace entry ${i + 1}`}
385
+ className="mt-1 text-[var(--stgm-muted-foreground,#737373)] hover:text-[var(--stgm-destructive,#ef4444)]"
386
+ >
387
+ ×
388
+ </button>
389
+ </div>
390
+ );
391
+ })}
392
+ </div>
393
+ )}
394
+ </section>
395
+ );
396
+ }
397
+
398
+ // ---------------------------------------------------------------------------
399
+ // Environment references (structured rows)
400
+ // ---------------------------------------------------------------------------
401
+
402
+ interface EnvironmentRefRow {
403
+ readonly org?: string;
404
+ readonly slug?: string;
405
+ readonly kind?: string;
406
+ }
407
+
408
+ /**
409
+ * Edits `environment_refs` as slug rows (org optional — empty means the
410
+ * workflow's own org). These are how a tool-using agent becomes callable
411
+ * from a workflow: the task binds the credentials its child runs need
412
+ * without touching the agent. Resolved server-side at execution create;
413
+ * an unresolvable ref fails the run closed.
414
+ */
415
+ function EnvironmentRefsSection({
416
+ refs,
417
+ onChange,
418
+ }: {
419
+ refs: unknown;
420
+ onChange: (refs: EnvironmentRefRow[] | undefined) => void;
421
+ }) {
422
+ const rows = useMemo<readonly EnvironmentRefRow[]>(
423
+ () => (Array.isArray(refs) ? (refs as EnvironmentRefRow[]) : []),
424
+ [refs],
425
+ );
426
+
427
+ const commit = useCallback(
428
+ (next: EnvironmentRefRow[]) => {
429
+ onChange(next.length > 0 ? next : undefined);
430
+ },
431
+ [onChange],
432
+ );
433
+
434
+ const updateRow = useCallback(
435
+ (index: number, field: "org" | "slug", value: string) => {
436
+ const next = rows.map((row, i) => {
437
+ if (i !== index) return row;
438
+ const updated: Record<string, string> = {};
439
+ const org = field === "org" ? value : row.org ?? "";
440
+ const slug = field === "slug" ? value : row.slug ?? "";
441
+ if (org) updated.org = org;
442
+ updated.slug = slug;
443
+ return updated as EnvironmentRefRow;
444
+ });
445
+ commit([...next]);
446
+ },
447
+ [rows, commit],
448
+ );
449
+
450
+ return (
451
+ <section className="flex flex-col gap-2">
452
+ <div className="flex items-center justify-between">
453
+ <SectionLabel>Environments</SectionLabel>
454
+ <button
455
+ type="button"
456
+ onClick={() => commit([...rows, { slug: "" }])}
457
+ className="text-[10px] font-medium text-[var(--stgm-primary,#6366f1)]"
458
+ data-testid="agent-call-envref-add"
459
+ >
460
+ + Bind environment
461
+ </button>
462
+ </div>
463
+ {rows.length === 0 ? (
464
+ <p className="text-[10px] leading-tight text-[var(--stgm-muted-foreground,#737373)]">
465
+ Environment resources whose values are provided to this call&apos;s runs
466
+ — credentials bind here, not on the agent.
467
+ </p>
468
+ ) : (
469
+ <div className="flex flex-col gap-1.5">
470
+ {rows.map((row, i) => (
471
+ <div key={i} className="flex items-start gap-1">
472
+ <input
473
+ type="text"
474
+ value={row.org ?? ""}
475
+ onChange={(e) => updateRow(i, "org", e.target.value)}
476
+ placeholder="org (optional)"
477
+ className={`${inputClass} w-1/3`}
478
+ data-testid={`agent-call-envref-org-${i}`}
479
+ />
480
+ <input
481
+ type="text"
482
+ value={row.slug ?? ""}
483
+ onChange={(e) => updateRow(i, "slug", e.target.value)}
484
+ placeholder="environment slug"
485
+ className={`${inputClass} flex-1`}
486
+ data-testid={`agent-call-envref-slug-${i}`}
487
+ />
488
+ <button
489
+ type="button"
490
+ onClick={() => commit(rows.filter((_, j) => j !== i))}
491
+ aria-label={`Remove environment reference ${i + 1}`}
492
+ className="mt-1 text-[var(--stgm-muted-foreground,#737373)] hover:text-[var(--stgm-destructive,#ef4444)]"
493
+ >
494
+ ×
495
+ </button>
496
+ </div>
497
+ ))}
498
+ </div>
499
+ )}
500
+ </section>
501
+ );
502
+ }
503
+
281
504
  // ---------------------------------------------------------------------------
282
505
  // Output schema editor (JSON textarea with parse validation)
283
506
  // ---------------------------------------------------------------------------
@@ -20,7 +20,8 @@ const INVOCATION_KINDS = new Set(["http_call", "grpc_call", "activity_call", "ru
20
20
  * Runtime tab — timeout, retry, budget, and execution policies.
21
21
  *
22
22
  * Content varies by task kind category:
23
- * - AI kinds: model, timeout, temperature, max cost
23
+ * - agent_call: model + budget per run (the shared run_config block)
24
+ * - llm_call / eval: timeout, max cost
24
25
  * - Invocation kinds: timeout
25
26
  * - Container kinds: concurrency, join policy
26
27
  *
@@ -43,8 +44,12 @@ export const RuntimeTab = memo(function RuntimeTab({
43
44
 
44
45
  return (
45
46
  <div className="flex flex-col gap-4 px-3 py-3">
46
- {isAi && (
47
- <AgentRuntimeSection config={config} kindString={kindString} onChange={handleChange} />
47
+ {kindString === "agent_call" && (
48
+ <AgentCallRuntimeSection config={config} onChange={handleChange} />
49
+ )}
50
+
51
+ {isAi && kindString !== "agent_call" && (
52
+ <AgentRuntimeSection config={config} onChange={handleChange} />
48
53
  )}
49
54
 
50
55
  {isInvocation && (
@@ -69,16 +74,77 @@ export const RuntimeTab = memo(function RuntimeTab({
69
74
  });
70
75
 
71
76
  // ---------------------------------------------------------------------------
72
- // Agent/LLM runtime
77
+ // Agent call runtime (shared run_config vocabulary)
78
+ // ---------------------------------------------------------------------------
79
+
80
+ /**
81
+ * Edits the agent_call `run_config` block (the shared agentexecution
82
+ * RunConfig, stigmer/stigmer#358): model override + per-run USD budget,
83
+ * both enforced by the runner. `max_tool_rounds` stays off the form
84
+ * (DD-018 D-5). Empty means omit — a blank field never becomes a zero
85
+ * override.
86
+ */
87
+ function AgentCallRuntimeSection({
88
+ config,
89
+ onChange,
90
+ }: {
91
+ config: Record<string, unknown>;
92
+ onChange: (field: string, value: unknown) => void;
93
+ }) {
94
+ const runConfig = (config.run_config ?? {}) as Record<string, unknown>;
95
+
96
+ const handleRunConfigChange = useCallback(
97
+ (field: string, value: unknown) => {
98
+ const next: Record<string, unknown> = {
99
+ ...runConfig,
100
+ [field]: value === "" ? undefined : value,
101
+ };
102
+ for (const key of Object.keys(next)) {
103
+ if (next[key] === undefined) delete next[key];
104
+ }
105
+ onChange("run_config", Object.keys(next).length > 0 ? next : undefined);
106
+ },
107
+ [runConfig, onChange],
108
+ );
109
+
110
+ return (
111
+ <section className="flex flex-col gap-3">
112
+ <SectionLabel>Execution</SectionLabel>
113
+
114
+ <FieldRow label="Model" hint="Override agent default model">
115
+ <input
116
+ type="text"
117
+ value={typeof runConfig.model_name === "string" ? runConfig.model_name : ""}
118
+ onChange={(e) => handleRunConfigChange("model_name", e.target.value || undefined)}
119
+ placeholder="Agent default"
120
+ className={inputClass}
121
+ />
122
+ </FieldRow>
123
+
124
+ <FieldRow label="Budget per run (USD)" hint="The call stops when its estimated cost reaches this amount">
125
+ <input
126
+ type="number"
127
+ value={typeof runConfig.max_cost_usd === "number" ? runConfig.max_cost_usd : ""}
128
+ onChange={(e) => handleRunConfigChange("max_cost_usd", e.target.value ? parseFloat(e.target.value) : undefined)}
129
+ placeholder="No limit"
130
+ min={0}
131
+ step={0.05}
132
+ className={inputClass}
133
+ />
134
+ </FieldRow>
135
+ </section>
136
+ );
137
+ }
138
+
139
+ // ---------------------------------------------------------------------------
140
+ // LLM/eval runtime
73
141
  // ---------------------------------------------------------------------------
74
142
 
75
143
  function AgentRuntimeSection({
76
144
  config,
77
- kindString,
78
145
  onChange,
79
146
  }: {
80
147
  config: Record<string, unknown>;
81
- kindString: string;
82
148
  onChange: (field: string, value: unknown) => void;
83
149
  }) {
84
150
  const nestedConfig = (config.config ?? {}) as Record<string, unknown>;
@@ -94,18 +160,6 @@ function AgentRuntimeSection({
94
160
  <section className="flex flex-col gap-3">
95
161
  <SectionLabel>Execution</SectionLabel>
96
162
 
97
- {kindString === "agent_call" && (
98
- <FieldRow label="Model" hint="Override agent default model">
99
- <input
100
- type="text"
101
- value={typeof nestedConfig.model === "string" ? nestedConfig.model : ""}
102
- onChange={(e) => handleNestedChange("model", e.target.value || undefined)}
103
- placeholder="Agent default"
104
- className={inputClass}
105
- />
106
- </FieldRow>
107
- )}
108
-
109
163
  <FieldRow label="Timeout" hint="Seconds before the task is cancelled">
110
164
  <input
111
165
  type="number"
@@ -118,21 +172,6 @@ function AgentRuntimeSection({
118
172
  />
119
173
  </FieldRow>
120
174
 
121
- {kindString === "agent_call" && (
122
- <FieldRow label="Temperature" hint="0.0 (deterministic) to 1.0 (creative)">
123
- <input
124
- type="number"
125
- value={typeof nestedConfig.temperature === "number" ? nestedConfig.temperature : ""}
126
- onChange={(e) => handleNestedChange("temperature", e.target.value ? parseFloat(e.target.value) : undefined)}
127
- placeholder="0.7"
128
- min={0}
129
- max={1}
130
- step={0.1}
131
- className={inputClass}
132
- />
133
- </FieldRow>
134
- )}
135
-
136
175
  <FieldRow label="Max cost" hint="Per-task cost cap in micro-USD">
137
176
  <input
138
177
  type="number"
@@ -23,7 +23,8 @@ spec:
23
23
  - name: step_1
24
24
  kind: agent_call
25
25
  task_config:
26
- agent_ref: "org/agent-slug"
26
+ agent: "org/agent-slug"
27
+ message: "Describe what this step should do"
27
28
  flow:
28
29
  then: end
29
30
  `;