agent-relay-server 0.112.0 → 0.113.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 (107) hide show
  1. package/docs/openapi.json +1 -1
  2. package/package.json +3 -3
  3. package/public/assets/{MessageBubble-DtLlfBAK.js → MessageBubble-CFujR6_L.js} +3 -3
  4. package/public/assets/{MessageBubble-DtLlfBAK.js.map → MessageBubble-CFujR6_L.js.map} +1 -1
  5. package/public/assets/{PlanGraphPanel-pcKJ4q_4.js → PlanGraphPanel-BfxvqmNS.js} +2 -2
  6. package/public/assets/{PlanGraphPanel-pcKJ4q_4.js.map → PlanGraphPanel-BfxvqmNS.js.map} +1 -1
  7. package/public/assets/{activity-DRAeL0Jq.js → activity-C7-gIF9O.js} +2 -2
  8. package/public/assets/{activity-DRAeL0Jq.js.map → activity-C7-gIF9O.js.map} +1 -1
  9. package/public/assets/{agent-profiles-cnWPANzx.js → agent-profiles-BGPa2-BZ.js} +2 -2
  10. package/public/assets/{agent-profiles-cnWPANzx.js.map → agent-profiles-BGPa2-BZ.js.map} +1 -1
  11. package/public/assets/{agents-DgUjmk_D.js → agents-CWCbBuq6.js} +2 -2
  12. package/public/assets/{agents-DgUjmk_D.js.map → agents-CWCbBuq6.js.map} +1 -1
  13. package/public/assets/{analytics-Q6Dl_CE9.js → analytics-BhGpT0JR.js} +2 -2
  14. package/public/assets/{analytics-Q6Dl_CE9.js.map → analytics-BhGpT0JR.js.map} +1 -1
  15. package/public/assets/automation-DnpahVqs.js +2 -0
  16. package/public/assets/automation-DnpahVqs.js.map +1 -0
  17. package/public/assets/{branch-state-badge-BRjUWyMT.js → branch-state-badge-CcAq2Igv.js} +2 -2
  18. package/public/assets/{branch-state-badge-BRjUWyMT.js.map → branch-state-badge-CcAq2Igv.js.map} +1 -1
  19. package/public/assets/{channels-DOYB1iiA.js → channels-DBP3-Gg0.js} +2 -2
  20. package/public/assets/{channels-DOYB1iiA.js.map → channels-DBP3-Gg0.js.map} +1 -1
  21. package/public/assets/{chat-C7XCmaZz.js → chat-Bue-qNx_.js} +2 -2
  22. package/public/assets/{chat-C7XCmaZz.js.map → chat-Bue-qNx_.js.map} +1 -1
  23. package/public/assets/{connectors-C5CeK-2M.js → connectors-BJxbuxoU.js} +2 -2
  24. package/public/assets/{connectors-C5CeK-2M.js.map → connectors-BJxbuxoU.js.map} +1 -1
  25. package/public/assets/{formatted-body-impl-rLDPgdmJ.js → formatted-body-impl-CTz6wch8.js} +2 -2
  26. package/public/assets/formatted-body-impl-CTz6wch8.js.map +1 -0
  27. package/public/assets/{index-DGyeTLPg.js → index-BMAblAKQ.js} +7 -7
  28. package/public/assets/{index-DGyeTLPg.js.map → index-BMAblAKQ.js.map} +1 -1
  29. package/public/assets/{insights-C0dDL4El.js → insights-CZ_JrJ8M.js} +2 -2
  30. package/public/assets/{insights-C0dDL4El.js.map → insights-CZ_JrJ8M.js.map} +1 -1
  31. package/public/assets/{integrations-CztQfrI7.js → integrations-L75xhZd5.js} +2 -2
  32. package/public/assets/{integrations-CztQfrI7.js.map → integrations-L75xhZd5.js.map} +1 -1
  33. package/public/assets/{maintenance-DkOkcEkb.js → maintenance-Dpb87NaB.js} +2 -2
  34. package/public/assets/{maintenance-DkOkcEkb.js.map → maintenance-Dpb87NaB.js.map} +1 -1
  35. package/public/assets/{managed-agents-C7Czinaz.js → managed-agents-DyGbrKwJ.js} +2 -2
  36. package/public/assets/{managed-agents-C7Czinaz.js.map → managed-agents-DyGbrKwJ.js.map} +1 -1
  37. package/public/assets/{markdown-preview-impl-BZ7sigEc.js → markdown-preview-impl-Bx4AcI8G.js} +2 -2
  38. package/public/assets/{markdown-preview-impl-BZ7sigEc.js.map → markdown-preview-impl-Bx4AcI8G.js.map} +1 -1
  39. package/public/assets/{memory-Bys-M7GK.js → memory-2l6G_m1p.js} +2 -2
  40. package/public/assets/{memory-Bys-M7GK.js.map → memory-2l6G_m1p.js.map} +1 -1
  41. package/public/assets/{messages-COSJ6trN.js → messages-DC-QXNwz.js} +2 -2
  42. package/public/assets/messages-DC-QXNwz.js.map +1 -0
  43. package/public/assets/{orchestrators-BjLl1Z0L.js → orchestrators-D_X3a0dm.js} +2 -2
  44. package/public/assets/{orchestrators-BjLl1Z0L.js.map → orchestrators-D_X3a0dm.js.map} +1 -1
  45. package/public/assets/{overview-BmU7twic.js → overview-76JPEbGZ.js} +2 -2
  46. package/public/assets/{overview-BmU7twic.js.map → overview-76JPEbGZ.js.map} +1 -1
  47. package/public/assets/{pairs-xclXjw-c.js → pairs-IeLXl-Z8.js} +2 -2
  48. package/public/assets/{pairs-xclXjw-c.js.map → pairs-IeLXl-Z8.js.map} +1 -1
  49. package/public/assets/{projects-cOeuiMjl.js → projects-D8ClXDUg.js} +2 -2
  50. package/public/assets/{projects-cOeuiMjl.js.map → projects-D8ClXDUg.js.map} +1 -1
  51. package/public/assets/{prompt-settings-CtQd0nz0.js → prompt-settings-BRduaHOj.js} +2 -2
  52. package/public/assets/{prompt-settings-CtQd0nz0.js.map → prompt-settings-BRduaHOj.js.map} +1 -1
  53. package/public/assets/{registry-CDe6vjKI.js → registry-CXyaPToV.js} +2 -2
  54. package/public/assets/{registry-CDe6vjKI.js.map → registry-CXyaPToV.js.map} +1 -1
  55. package/public/assets/{security-C9mBNQMZ.js → security-8n8VEsf5.js} +2 -2
  56. package/public/assets/{security-C9mBNQMZ.js.map → security-8n8VEsf5.js.map} +1 -1
  57. package/public/assets/{select-BJDKQk4q.js → select-D-wcg_2U.js} +2 -2
  58. package/public/assets/{select-BJDKQk4q.js.map → select-D-wcg_2U.js.map} +1 -1
  59. package/public/assets/{settings-BmZjWJkI.js → settings-BhnCGXgo.js} +2 -2
  60. package/public/assets/{settings-BmZjWJkI.js.map → settings-BhnCGXgo.js.map} +1 -1
  61. package/public/assets/{store-DDWg67Pg.js → store-Du8k5mLJ.js} +3 -3
  62. package/public/assets/{store-DDWg67Pg.js.map → store-Du8k5mLJ.js.map} +1 -1
  63. package/public/assets/{tasks-DNjZKSM2.js → tasks-DEKQqgPB.js} +2 -2
  64. package/public/assets/{tasks-DNjZKSM2.js.map → tasks-DEKQqgPB.js.map} +1 -1
  65. package/public/assets/{teams-KNzmdolE.js → teams-DoRSWZRQ.js} +2 -2
  66. package/public/assets/{teams-KNzmdolE.js.map → teams-DoRSWZRQ.js.map} +1 -1
  67. package/public/assets/{terminal-viewer-impl-DnFJDl9N.js → terminal-viewer-impl-C_o3BOpu.js} +2 -2
  68. package/public/assets/{terminal-viewer-impl-DnFJDl9N.js.map → terminal-viewer-impl-C_o3BOpu.js.map} +1 -1
  69. package/public/assets/types-DSwhOFcJ.js.map +1 -1
  70. package/public/assets/{user-avatar-C8sbjQke.js → user-avatar-BQoM4jPT.js} +2 -2
  71. package/public/assets/{user-avatar-C8sbjQke.js.map → user-avatar-BQoM4jPT.js.map} +1 -1
  72. package/public/assets/work-queue-CWnf1_iE.js +2 -0
  73. package/public/assets/work-queue-CWnf1_iE.js.map +1 -0
  74. package/public/assets/{workspaces-CSNdUg15.js → workspaces-C_fFCoCu.js} +2 -2
  75. package/public/assets/{workspaces-CSNdUg15.js.map → workspaces-C_fFCoCu.js.map} +1 -1
  76. package/public/index.html +2 -2
  77. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  78. package/runner/plugins/claude/skills/task/SKILL.md +28 -0
  79. package/runner/plugins/codex/skills/task/SKILL.md +28 -0
  80. package/src/agent-lifecycle-events.ts +13 -7
  81. package/src/db/mappers.ts +2 -0
  82. package/src/db/migrations.ts +4 -1
  83. package/src/db/provisioning-registry.ts +148 -2
  84. package/src/db/schema.ts +2 -0
  85. package/src/db/task-entity.ts +37 -12
  86. package/src/db/task-memory.ts +1 -1
  87. package/src/mcp/tools-spawn.ts +3 -0
  88. package/src/mcp-dispatch-tools.ts +2 -2
  89. package/src/mcp-task-tools.ts +9 -3
  90. package/src/prompt-resolver.ts +7 -7
  91. package/src/routes/agents-spawn.ts +8 -1
  92. package/src/routes/tasks.ts +6 -2
  93. package/src/services/plan-graph.ts +1 -1
  94. package/src/services/spawn-agent.ts +82 -35
  95. package/src/services/spawn-registration-watchdog.ts +54 -18
  96. package/src/services/task-context-hydration.ts +1 -1
  97. package/src/services/task-issue-weave.ts +2 -2
  98. package/src/services/task-lifecycle.ts +2 -2
  99. package/src/services/task-semantic-events.ts +4 -4
  100. package/src/token-db.ts +1 -1
  101. package/src/validation.ts +1 -1
  102. package/public/assets/automation-CuTlgY1E.js +0 -2
  103. package/public/assets/automation-CuTlgY1E.js.map +0 -1
  104. package/public/assets/formatted-body-impl-rLDPgdmJ.js.map +0 -1
  105. package/public/assets/messages-COSJ6trN.js.map +0 -1
  106. package/public/assets/work-queue-DINvPVPn.js +0 -2
  107. package/public/assets/work-queue-DINvPVPn.js.map +0 -1
@@ -41,7 +41,7 @@ import type { ProjectSkillsPolicy } from "../project-skills";
41
41
  import { applyDefaultSharedMcp, type SharedMcpSpawnOverride } from "../shared-mcp";
42
42
  import { autoSeedProjectForCwd } from "./project";
43
43
  import { hydrateTaskContext, projectTaskHydration } from "./task-context-hydration";
44
- import { recordTaskEntityDispatchDecision } from "./task-entity";
44
+ import { createTaskEntity, recordTaskEntityDispatchDecision } from "./task-entity";
45
45
  import { autoSubscribeTaskCoordinator } from "./task-semantic-events";
46
46
  import { dispatchTask } from "../task-dispatcher";
47
47
  import { hasComponentScope, isComponentAuthorizedFor } from "../security";
@@ -138,6 +138,11 @@ export interface SpawnAgentInput {
138
138
  * work-shape (#839). Absent = today's behavior, byte-identical.
139
139
  */
140
140
  taskId?: number;
141
+ /**
142
+ * #867 — explicit escape hatch for utility/standby spawns that carry a prompt but are not
143
+ * dispatching deliverable work. Known utility profiles are also exempt by default.
144
+ */
145
+ taskless?: boolean;
141
146
  /** #838 — the latest one-line steer; the actionable instruction layered over the hydrated Task context. */
142
147
  steer?: string;
143
148
  /**
@@ -218,6 +223,7 @@ export interface SpawnAgentResult {
218
223
  agentId: string | null;
219
224
  registered: boolean;
220
225
  command: Command;
226
+ task?: { id: number; autoScaffolded: boolean; nudge: string };
221
227
  }
222
228
 
223
229
  /** Reject when a component token's signed resource constraints disallow the target (#323). */
@@ -248,6 +254,30 @@ function taskSpawnLabel(task: { id: number; title: string } | undefined, fallbac
248
254
  return label.length <= 120 ? label : `${label.slice(0, 117)}...`;
249
255
  }
250
256
 
257
+ const WORK_DISPATCH_PROFILES = new Set(["default-relay", "lean-worker", "worker"]);
258
+ const TASKLESS_PROFILES = new Set(["coordinator", "isolated-research", "minimal", "ops-assistant", "reviewer", "vanilla"]);
259
+ const AUTO_SCAFFOLD_TASK_NUDGE = "No taskId was provided, so Relay auto-scaffolded a Task from the spawn brief and bound this spawn to it. Pass taskId for intentional task reuse, or taskless:true for utility/standby spawns.";
260
+
261
+ function spawnBrief(input: SpawnAgentInput): string | undefined {
262
+ const candidates = [input.steer, input.prompt];
263
+ for (const value of candidates) {
264
+ const cleaned = value?.trim();
265
+ if (cleaned) return cleaned;
266
+ }
267
+ return undefined;
268
+ }
269
+
270
+ function taskTitleFromBrief(brief: string): string {
271
+ const firstLine = brief.split(/\r?\n/).map((line) => line.trim()).find(Boolean) ?? "Spawned work";
272
+ return firstLine.length <= 500 ? firstLine : `${firstLine.slice(0, 497)}...`;
273
+ }
274
+
275
+ function shouldAutoScaffoldTask(input: SpawnAgentInput, profileName: string): boolean {
276
+ if (input.taskId !== undefined || input.taskless === true) return false;
277
+ if (TASKLESS_PROFILES.has(profileName)) return false;
278
+ return WORK_DISPATCH_PROFILES.has(profileName) && spawnBrief(input) !== undefined;
279
+ }
280
+
251
281
  /**
252
282
  * Spawn a provider agent. Transport-agnostic: every gate + side effect lives here so the
253
283
  * MCP and HTTP paths cannot drift. Throws `McpAuthError` (→403) on a gate denial,
@@ -260,7 +290,9 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
260
290
  // the #221 gate AND the #328/#331/#324 caller-context inheritance — one lookup, reused.
261
291
  const callerId = ctx.callerAgentId;
262
292
  const caller: AgentCard | undefined = (callerId ? getAgent(callerId) : undefined) ?? undefined;
263
- const linkedTask = input.taskId !== undefined ? getTaskDetail(input.taskId) ?? undefined : undefined;
293
+ const profileName = input.profile ?? DEFAULT_SPAWN_PROFILE_NAME;
294
+ let autoScaffoldedTask: SpawnAgentResult["task"] | undefined;
295
+ let linkedTask = input.taskId !== undefined ? getTaskDetail(input.taskId) ?? undefined : undefined;
264
296
  const taskCreatorAgentId = !callerId ? agentIdFromTaskCreator(linkedTask?.createdBy) : undefined;
265
297
  const lineageAgentId = callerId ?? taskCreatorAgentId;
266
298
  const lineageAgent: AgentCard | undefined = (lineageAgentId ? getAgent(lineageAgentId) : undefined) ?? undefined;
@@ -285,6 +317,53 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
285
317
  const inheritedCwd = callerCwd && isPathWithinBase(callerCwd, orchestrator.baseDir) ? callerCwd : undefined;
286
318
  const resolvedCwd = requestedCwd || inheritedCwd || orchestrator.baseDir;
287
319
 
320
+ // Resolve + validate the named agent profile server-side (was HTTP-only; the MCP path passed
321
+ // the name through unvalidated). Omitted profile uses Relay's spawn default so coordinator
322
+ // spawns still get the bundled Relay context/affordances. Explicit profile always wins.
323
+ let agentProfile = withResolvedProvisioning(getAgentProfile(profileName)?.value, input.provider);
324
+ if (!agentProfile) throw new McpNotFoundError(input.profile ? "agent profile not found" : `default agent profile ${DEFAULT_SPAWN_PROFILE_NAME} not found`);
325
+
326
+ // --- THE GATE (uniform across transports) ---------------------------------
327
+ // Coarse capability: spawning requires command:spawn. The MCP dispatcher already enforced
328
+ // this; the HTTP route did not — asserting here makes it transport-invariant.
329
+ if (!canSpawn(ctx)) {
330
+ throw new McpAuthError(`spawn requires the ${SPAWN_SCOPE} scope`);
331
+ }
332
+ // #221 — a real spawning agent: no grandchildren, and within its maxSpawnedAgents quota.
333
+ // Server/admin/dashboard tokens carry no caller identity → unrestricted by design.
334
+ if (callerId) {
335
+ if (caller?.spawnedBy) {
336
+ throw new McpAuthError("spawned agents cannot spawn further agents (no grandchildren)");
337
+ }
338
+ const quota = ctx.constraints?.maxSpawnedAgents ?? 0;
339
+ const live = countLiveSpawnedAgents(callerId);
340
+ if (live >= quota) {
341
+ throw new ValidationError(`spawn quota reached (${live}/${quota} live children) — shut one down or wait for one to exit`);
342
+ }
343
+ }
344
+ // Gate the child spawn on the token's legit bounds: the target orchestrator AND the spawn cwd
345
+ // (`cwdPrefixes`/`cwd` constraint). Excludes the #323 footguns (spawnRequestIds/policies) — those
346
+ // are self-scoping per-spawn ids the CHILD gets fresh, so gating on them makes the quota
347
+ // unreachable. The cwd bound was enforced by the old HTTP route (resource.cwd) but NOT by the MCP
348
+ // path; unifying here converges UP to the strict union — a scoped dashboard token still can't spawn
349
+ // outside its cwd prefix, and MCP now honors the same bound.
350
+ assertResourceAllowed(ctx, { scope: "agent:write", resource: { orchestratorId: orchestrator.id, cwd: resolvedCwd } });
351
+
352
+ if (shouldAutoScaffoldTask(input, profileName)) {
353
+ const brief = spawnBrief(input)!;
354
+ const taskRequestedBy = callerId ?? input.requestedBy ?? ctx.actor.id;
355
+ linkedTask = createTaskEntity({
356
+ title: taskTitleFromBrief(brief),
357
+ intent: brief,
358
+ latestSteer: input.steer,
359
+ createdBy: taskRequestedBy,
360
+ source: "spawn",
361
+ target: taskRequestedBy,
362
+ });
363
+ input = { ...input, taskId: linkedTask.id };
364
+ autoScaffoldedTask = { id: linkedTask.id, autoScaffolded: true, nudge: AUTO_SCAFFOLD_TASK_NUDGE };
365
+ }
366
+
288
367
  // #838 — spawn-with-task hydration. When a taskId is given, relay assembles the worker's
289
368
  // first-turn context FROM the durable Task (intent + linked-issue spec/acceptance + recorded
290
369
  // decisions + history + latest steer), resolved server-side ONCE, and projects it onto the
@@ -346,13 +425,6 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
346
425
 
347
426
  const spawnRequestId = input.spawnRequestId ?? generateSpawnRequestId();
348
427
 
349
- // Resolve + validate the named agent profile server-side (was HTTP-only; the MCP path passed
350
- // the name through unvalidated). Omitted profile uses Relay's spawn default so coordinator
351
- // spawns still get the bundled Relay context/affordances. Explicit profile always wins.
352
- const profileName = input.profile ?? DEFAULT_SPAWN_PROFILE_NAME;
353
- let agentProfile = withResolvedProvisioning(getAgentProfile(profileName)?.value, input.provider);
354
- if (!agentProfile) throw new McpNotFoundError(input.profile ? "agent profile not found" : `default agent profile ${DEFAULT_SPAWN_PROFILE_NAME} not found`);
355
-
356
428
  // #667 — project `.mcp.json` projection: a per-spawn `projectMcp` overrides the profile
357
429
  // default. The runner does the filesystem read + injection from `agentProfile.projectMcp`
358
430
  // (it owns the spawn cwd), so thread the resolved decision onto the in-flight profile copy
@@ -368,32 +440,6 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
368
440
  // Explicit opt-in still works for zero-host profiles.
369
441
  agentProfile = applyDefaultSharedMcp(agentProfile, input.sharedMcp);
370
442
 
371
- // --- THE GATE (uniform across transports) ---------------------------------
372
- // Coarse capability: spawning requires command:spawn. The MCP dispatcher already enforced
373
- // this; the HTTP route did not — asserting here makes it transport-invariant.
374
- if (!canSpawn(ctx)) {
375
- throw new McpAuthError(`spawn requires the ${SPAWN_SCOPE} scope`);
376
- }
377
- // #221 — a real spawning agent: no grandchildren, and within its maxSpawnedAgents quota.
378
- // Server/admin/dashboard tokens carry no caller identity → unrestricted by design.
379
- if (callerId) {
380
- if (caller?.spawnedBy) {
381
- throw new McpAuthError("spawned agents cannot spawn further agents (no grandchildren)");
382
- }
383
- const quota = ctx.constraints?.maxSpawnedAgents ?? 0;
384
- const live = countLiveSpawnedAgents(callerId);
385
- if (live >= quota) {
386
- throw new ValidationError(`spawn quota reached (${live}/${quota} live children) — shut one down or wait for one to exit`);
387
- }
388
- }
389
- // Gate the child spawn on the token's legit bounds: the target orchestrator AND the spawn cwd
390
- // (`cwdPrefixes`/`cwd` constraint). Excludes the #323 footguns (spawnRequestIds/policies) — those
391
- // are self-scoping per-spawn ids the CHILD gets fresh, so gating on them makes the quota
392
- // unreachable. The cwd bound was enforced by the old HTTP route (resource.cwd) but NOT by the MCP
393
- // path; unifying here converges UP to the strict union — a scoped dashboard token still can't spawn
394
- // outside its cwd prefix, and MCP now honors the same bound.
395
- assertResourceAllowed(ctx, { scope: "agent:write", resource: { orchestratorId: orchestrator.id, cwd: resolvedCwd } });
396
-
397
443
  // #635 — validate and enrich resumeWorkspace before dispatching to the orchestrator.
398
444
  // The relay has the workspace DB records; the orchestrator has disk access. We do the
399
445
  // "is this a real managed workspace and is it free?" check here, the orchestrator
@@ -606,6 +652,7 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
606
652
  agentId,
607
653
  registered: agentId !== null,
608
654
  command,
655
+ ...(autoScaffoldedTask ? { task: autoScaffoldedTask } : {}),
609
656
  };
610
657
  }
611
658
 
@@ -12,6 +12,14 @@ interface SpawnCommandRow {
12
12
  updated_at: number;
13
13
  }
14
14
 
15
+ interface SpawnCommandGroup {
16
+ spawnRequestId: string;
17
+ parent: string;
18
+ provider?: string;
19
+ latestUpdatedAt: number;
20
+ rows: SpawnCommandRow[];
21
+ }
22
+
15
23
  interface SpawnRegistrationWatchdogResult extends Record<string, unknown> {
16
24
  failedSpawnCommandIds: string[];
17
25
  notifiedSpawnCommandIds: string[];
@@ -30,41 +38,69 @@ export function reapUnregisteredSpawnCommands(
30
38
  FROM commands
31
39
  WHERE type = 'agent.spawn'
32
40
  AND status = 'succeeded'
33
- AND updated_at <= ?
34
41
  ORDER BY updated_at ASC
35
- `).all(cutoff) as SpawnCommandRow[];
42
+ `).all() as SpawnCommandRow[];
36
43
 
37
44
  const failedSpawnCommandIds: string[] = [];
38
45
  const notifiedSpawnCommandIds: string[] = [];
39
46
  const silencedSpawnCommandIds: string[] = [];
40
- for (const row of rows) {
41
- const params = parseParams(row.params);
42
- const spawnRequestId = stringParam(params, "spawnRequestId") ?? row.correlation_id ?? undefined;
43
- const parent = stringParam(params, "spawnedBy");
44
- if (!spawnRequestId || !parent) continue;
45
- if (hasEverRegisteredAgent(spawnRequestId)) continue;
47
+ for (const group of groupSpawnCommands(rows)) {
48
+ if (group.latestUpdatedAt > cutoff) continue;
49
+ if (hasEverRegisteredAgent(group.spawnRequestId)) continue;
46
50
 
47
51
  const reason = `spawned agent did not register within ${deadlineMs}ms`;
48
- const failed = updateCommand(row.id, { status: "failed", error: reason });
49
- if (!failed || failed.status !== "failed") continue;
50
- failedSpawnCommandIds.push(failed.id);
51
- if (row.updated_at >= notifyCutoff) {
52
- notifiedSpawnCommandIds.push(failed.id);
52
+ const failedCommands = group.rows
53
+ .map((row) => updateCommand(row.id, { status: "failed", error: reason }))
54
+ .filter((command): command is NonNullable<typeof command> => Boolean(command && command.status === "failed"));
55
+ if (failedCommands.length === 0) continue;
56
+ failedSpawnCommandIds.push(...failedCommands.map((command) => command.id));
57
+ if (group.latestUpdatedAt >= notifyCutoff) {
58
+ notifiedSpawnCommandIds.push(failedCommands[failedCommands.length - 1]!.id);
53
59
  notifyAgentSpawnFailed({
54
- parent,
55
- spawnRequestId,
56
- provider: stringParam(params, "provider"),
60
+ parent: group.parent,
61
+ spawnRequestId: group.spawnRequestId,
62
+ provider: group.provider,
57
63
  reason,
58
64
  });
59
65
  } else {
60
- silencedSpawnCommandIds.push(failed.id);
66
+ silencedSpawnCommandIds.push(...failedCommands.map((command) => command.id));
67
+ }
68
+ for (const failed of failedCommands) {
69
+ emitCommandEvent(failed, "command.failed");
61
70
  }
62
- emitCommandEvent(failed, "command.failed");
63
71
  }
64
72
 
65
73
  return { failedSpawnCommandIds, notifiedSpawnCommandIds, silencedSpawnCommandIds };
66
74
  }
67
75
 
76
+ function groupSpawnCommands(rows: SpawnCommandRow[]): SpawnCommandGroup[] {
77
+ const groups = new Map<string, SpawnCommandGroup>();
78
+ for (const row of rows) {
79
+ const params = parseParams(row.params);
80
+ const spawnRequestId = stringParam(params, "spawnRequestId") ?? row.correlation_id ?? undefined;
81
+ const parent = stringParam(params, "spawnedBy");
82
+ if (!spawnRequestId || !parent) continue;
83
+ const existing = groups.get(spawnRequestId);
84
+ if (existing) {
85
+ existing.rows.push(row);
86
+ if (row.updated_at >= existing.latestUpdatedAt) {
87
+ existing.parent = parent;
88
+ existing.provider = stringParam(params, "provider");
89
+ existing.latestUpdatedAt = row.updated_at;
90
+ }
91
+ continue;
92
+ }
93
+ groups.set(spawnRequestId, {
94
+ spawnRequestId,
95
+ parent,
96
+ provider: stringParam(params, "provider"),
97
+ latestUpdatedAt: row.updated_at,
98
+ rows: [row],
99
+ });
100
+ }
101
+ return [...groups.values()];
102
+ }
103
+
68
104
  function hasEverRegisteredAgent(spawnRequestId: string): boolean {
69
105
  const row = getDb().query(`
70
106
  SELECT 1
@@ -170,7 +170,7 @@ function hasTaskMemory(envelope: TaskMemoryEnvelope): boolean {
170
170
  function renderTaskMemory(envelope: TaskMemoryEnvelope): string | undefined {
171
171
  if (!hasTaskMemory(envelope)) return undefined;
172
172
  const rendered = renderContinuation(boundedTaskMemoryEnvelope(envelope), {
173
- intro: `Task-bound memory — generation ${envelope.generation}. This is Relay-owned memory bound to Task #${envelope.taskId}; carry these dead ends, decisions, and working state forward.`,
173
+ intro: `Task-bound memory — generation ${envelope.generation}. This is Relay-owned memory bound to Task T${envelope.taskId}; carry these dead ends, decisions, and working state forward.`,
174
174
  });
175
175
  return truncateTaskMemorySection(rendered, TASK_MEMORY_SECTION_LIMIT);
176
176
  }
@@ -112,8 +112,8 @@ interface TaskCommentPayload {
112
112
  }
113
113
 
114
114
  const HEADINGS: Record<TaskCommentKind, (p: TaskCommentPayload) => string> = {
115
- discussion: (p) => `💬 **Comment on Task #${p.taskId}**${p.author ? ` — _${p.author}_` : ""}`,
116
- milestone: (p) => `🏁 **Task #${p.taskId} — ${p.milestone ?? "milestone"}**`,
115
+ discussion: (p) => `💬 **Comment on T${p.taskId}**${p.author ? ` — _${p.author}_` : ""}`,
116
+ milestone: (p) => `🏁 **T${p.taskId} — ${p.milestone ?? "milestone"}**`,
117
117
  };
118
118
 
119
119
  // The payload is base64url-encoded inside the marker, NOT raw JSON: a user/route-supplied
@@ -410,8 +410,8 @@ function emitDeliverableErrorReceipt(input: LandedTaskLifecycleInput, outcome: T
410
410
  recipients: [{ agentId: coordinator, matchedRule: "task-stakeholder" }],
411
411
  ttlMs: null,
412
412
  message: {
413
- subject: `Task #${outcome.taskId} land lifecycle failed`,
414
- body: `Task #${outcome.taskId} landed, but Relay could not complete the task lifecycle automation: ${outcome.error}`,
413
+ subject: `Task T${outcome.taskId} land lifecycle failed`,
414
+ body: `Task T${outcome.taskId} landed, but Relay could not complete the task lifecycle automation: ${outcome.error}`,
415
415
  payload: {
416
416
  kind: "task.lifecycle-error",
417
417
  notification: "task-lifecycle-error",
@@ -271,7 +271,7 @@ function compactStatusPulseItems(items: StatusPulseDigestItem[]): StatusPulseDig
271
271
  function taskStatusPulseBody(items: StatusPulseDigestItem[]): string {
272
272
  const lines = ["Task status pulse:"];
273
273
  for (const item of items) {
274
- const bits = [`#${item.taskId}`, item.transition, item.title];
274
+ const bits = [`T${item.taskId}`, item.transition, item.title];
275
275
  const issue = issueLabel(item.issue);
276
276
  if (issue) bits.push(issue);
277
277
  if (item.branch) bits.push(`branch ${item.branch}`);
@@ -303,13 +303,13 @@ function taskNotificationScope(input: TaskSemanticNotificationInput): Notificati
303
303
  }
304
304
 
305
305
  function taskNotificationSubject(input: TaskSemanticNotificationInput): string {
306
- return `Task #${input.task.id} ${input.transition}`;
306
+ return `Task T${input.task.id} ${input.transition}`;
307
307
  }
308
308
 
309
309
  function taskNotificationBody(input: TaskSemanticNotificationInput): string {
310
310
  const issue = issueLabel(primaryIssue(input.task));
311
311
  if (input.transition === "landed-on-main") {
312
- const bits = [`Task #${input.task.id} landed on main: ${input.task.title}`];
312
+ const bits = [`Task T${input.task.id} landed on main: ${input.task.title}`];
313
313
  if (input.sha) bits.push(`sha ${input.sha.slice(0, 12)}`);
314
314
  const gatesPassed = input.outcome?.gatesPassed;
315
315
  if (typeof gatesPassed === "boolean") bits.push(gatesPassed ? "gates passed" : "gates not verified");
@@ -318,7 +318,7 @@ function taskNotificationBody(input: TaskSemanticNotificationInput): string {
318
318
  return bits.join("; ");
319
319
  }
320
320
  const needed = taskNotificationNeeded(input);
321
- return `Task #${input.task.id} ${input.transition}: ${input.task.title}${issue ? ` (${issue})` : ""}${needed ? `\nNeeded: ${needed}` : ""}`;
321
+ return `Task T${input.task.id} ${input.transition}: ${input.task.title}${issue ? ` (${issue})` : ""}${needed ? `\nNeeded: ${needed}` : ""}`;
322
322
  }
323
323
 
324
324
  function taskNotificationNeeded(input: TaskSemanticNotificationInput): string {
package/src/token-db.ts CHANGED
@@ -67,7 +67,7 @@ const BUILT_IN_PROFILES: Array<Omit<TokenProfile, "createdAt" | "updatedAt">> =
67
67
  name: "Orchestrator Host",
68
68
  description: "Host supervisor access for spawn, command, log, and terminal operations.",
69
69
  role: "orchestrator",
70
- scope: ["agent:read", "agent:write", "command:read", "command:write", "task:read", "task:write", "logs:read", "terminal:attach", "quota:write"],
70
+ scope: ["agent:read", "agent:write", "command:read", "command:write", "task:read", "task:write", "logs:read", "terminal:attach", "quota:write", "provisioning:read"],
71
71
  constraints: { logsRead: true, terminalAttach: true },
72
72
  ttlSeconds: 30 * 24 * 60 * 60,
73
73
  builtIn: true,
package/src/validation.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ValidationError } from "./db";
1
+ import { ValidationError } from "./db/connection.ts";
2
2
  import { type TokenConstraints, type WorkspaceAutoMergePolicy, type WorkspaceLandingPolicy, type WorkspaceMetadata, type WorkspaceMode, type WorkspaceProbe, type WorkspaceStatus } from "./types";
3
3
  import { AUTO_MERGE_POLICIES, VALID_WORKSPACE_MODES, isRecord } from "agent-relay-sdk";
4
4
 
@@ -1,2 +0,0 @@
1
- import{r as e}from"./chunk-CilyBKbf.js";import{$n as t,An as n,G as r,Ht as i,N as a,U as o,V as s,fr as c,g as l,j as u}from"./lucide-react-DmbfpRvJ.js";import{c as d,f,l as p,s as m,t as h}from"./store-DDWg67Pg.js";import{p as g}from"./types-DSwhOFcJ.js";import{K as _,h as v}from"./display-_5kFrHJh.js";import{t as y}from"./badge-kdQDWO6e.js";import{t as b}from"./button-BKFvNXa3.js";import{t as x}from"./input-DfN6sUbX.js";import{V as S,b as C,c as w,l as T}from"./index-DGyeTLPg.js";import{o as E,r as D,s as O,t as k}from"./card-B0y1i_2D.js";var A=e(c(),1),j=t(),M={scheduled:`bg-blue-500/10 text-blue-400 border-blue-500/20`,dispatching:`bg-sky-500/10 text-sky-400 border-sky-500/20`,waiting_agent:`bg-yellow-500/10 text-yellow-400 border-yellow-500/20`,running:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`,succeeded:`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`,failed:`bg-red-500/10 text-red-400 border-red-500/20`,canceled:`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`,timed_out:`bg-red-500/10 text-red-400 border-red-500/20`},N=new Set([`scheduled`,`dispatching`,`waiting_agent`,`running`]);function P(e){return e.status===`timed_out`&&e.result?.runtimeBudgetElapsed===!0?`budget elapsed`:e.status}function F(e){return e.status===`timed_out`&&e.result?.runtimeBudgetElapsed===!0?`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`:M[e.status]||``}function I(e){return e.status===`timed_out`&&e.result?.runtimeBudgetElapsed===!0?void 0:e.error}function L(e){return N.has(e.status)}function R(e){return typeof e==`number`&&Number.isFinite(e)?e:void 0}function z(e){return typeof e==`string`&&e.length>0?e:void 0}function B(e){let t=Math.max(0,Math.round(e/1e3)),n=Math.floor(t/3600),r=Math.floor(t%3600/60),i=t%60;return n?`${n}h ${r}m`:r?`${r}m ${i}s`:`${i}s`}function V(e){return R(e.meta?.runtimeBudgetDeadlineAt)??R(e.result?.deadlineAt)}function H(e,t){if(!e.startedAt)return`-`;let n=e.finishedAt||(L(e)?t:e.updatedAt);return B(Number(n)-Number(e.startedAt))}function U(e,t){let n=V(e);if(!(!n||!L(e)))return B(n-t)}function W(e){return e.status===`timed_out`&&e.result?.runtimeBudgetElapsed===!0?`runtime budget elapsed`:e.error}function G(e,t,n){let r=e.targetAgentId||e.spawnedAgentId||z(e.meta?.runtimeAgentId),i=r?t.find(e=>e.id===r):void 0,a=r?n.flatMap(e=>e.managedAgents.map(t=>({orchestrator:e,managedAgent:t}))).find(({managedAgent:e})=>e.agentId===r):void 0,o=z(e.meta?.terminalSession)||z(e.meta?.tmuxSession)||z(e.meta?.sessionName)||z(i?.meta?.terminalSession)||z(i?.meta?.tmuxSession)||z(i?.meta?.sessionName)||a?.managedAgent.terminalSession||a?.managedAgent.tmuxSession||a?.managedAgent.sessionName,s=z(e.meta?.orchestratorId)||a?.orchestrator.id||e.orchestratorId;return o?{orchestratorId:s,session:o}:void 0}function K(e=``,t){let n=p(t);return{name:``,description:``,enabled:!0,schedule:`0 8 * * *`,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone||`UTC`,orchestratorId:e,targetMode:`existing_agent`,provider:n,model:d(n),effort:``,profile:`default-relay`,label:``,tags:``,capabilities:``,ifNoMatch:`fail`,cwd:``,approvalMode:`guarded`,workspaceMode:`inherit`,keepAlive:!1,budgetMinutes:``,warningMinutes:``,warningMessage:``,concurrencyPolicy:`skip`,title:``,body:``,severity:`info`,dedupeKey:``,externalUrl:``}}function q(e){let t=e.split(`,`).map(e=>e.trim()).filter(Boolean);return t.length?t:void 0}function J(e){let t=e.targetPolicy;return{...K(e.orchestratorId),id:e.id,name:e.name,description:e.description||``,enabled:e.enabled,schedule:e.schedule,timezone:e.timezone,orchestratorId:e.orchestratorId,targetMode:t.mode,provider:t.mode===`on_demand_agent`?t.provider:t.selector.provider||g()[0]||``,model:t.mode===`on_demand_agent`?t.model||d(t.provider):``,effort:t.mode===`on_demand_agent`&&t.effort||``,profile:t.mode===`on_demand_agent`&&t.profile||`default-relay`,label:t.mode===`existing_agent`&&t.selector.label||``,tags:t.mode===`existing_agent`?(t.selector.tags||[]).join(`, `):``,capabilities:t.mode===`existing_agent`?(t.selector.capabilities||[]).join(`, `):``,ifNoMatch:t.mode===`existing_agent`&&t.ifNoMatch||`fail`,cwd:t.mode===`on_demand_agent`&&t.cwd||``,approvalMode:t.mode===`on_demand_agent`&&t.approvalMode||`guarded`,workspaceMode:t.mode===`on_demand_agent`&&t.workspaceMode||`inherit`,keepAlive:t.mode===`on_demand_agent`?!!t.keepAlive:!1,budgetMinutes:t.mode===`on_demand_agent`&&t.runtimeBudget?String(Math.round(t.runtimeBudget.maxRuntimeMs/6e4)):``,warningMinutes:t.mode===`on_demand_agent`&&t.runtimeBudget?.warnAtMs!==void 0?String(Math.round(t.runtimeBudget.warnAtMs/6e4)):``,warningMessage:t.mode===`on_demand_agent`&&t.runtimeBudget?.warningMessage||``,concurrencyPolicy:e.concurrencyPolicy,title:e.taskTemplate.title,body:e.taskTemplate.body,severity:e.taskTemplate.severity||`info`,dedupeKey:e.taskTemplate.dedupeKey||``,externalUrl:e.taskTemplate.externalUrl||``}}function Y(e){let t=Number(e.budgetMinutes),n=Number(e.warningMinutes),r=e.targetMode===`on_demand_agent`&&e.budgetMinutes.trim()&&Number.isFinite(t)?{maxRuntimeMs:Math.round(t*6e4),warnAtMs:e.warningMinutes.trim()&&Number.isFinite(n)?Math.round(n*6e4):void 0,warningMessage:e.warningMessage.trim()||void 0}:void 0,i=e.targetMode===`existing_agent`?{mode:`existing_agent`,selector:{provider:e.provider||void 0,label:e.label.trim()||void 0,tags:q(e.tags),capabilities:q(e.capabilities)},ifNoMatch:e.ifNoMatch}:{mode:`on_demand_agent`,provider:e.provider,model:e.model||void 0,effort:e.effort?e.effort:void 0,profile:e.profile||void 0,cwd:e.cwd.trim()||void 0,approvalMode:e.approvalMode,workspaceMode:e.workspaceMode,keepAlive:e.keepAlive,runtimeBudget:r};return{kind:`scheduled_task`,name:e.name.trim(),description:e.description.trim()||void 0,enabled:e.enabled,schedule:e.schedule.trim(),timezone:e.timezone.trim()||`UTC`,catchUpPolicy:`skip`,concurrencyPolicy:e.concurrencyPolicy,orchestratorId:e.orchestratorId,targetPolicy:i,taskTemplate:{title:e.title.trim(),body:e.body.trim(),severity:e.severity,dedupeKey:e.dedupeKey.trim()||void 0,externalUrl:e.externalUrl.trim()||void 0}}}function X(){let e=f(),t=h(e=>e.automations),r=h(e=>e.automationRuns),i=h(e=>e.orchestrators),a=h(e=>e.agents),o=h(e=>e.apiCall),c=h(e=>e.showError),l=h(e=>e.openConfirm),u=h(e=>e.fetchAutomations),d=h(e=>e.fetchAutomationRuns),p=h(e=>e.fetchTasks),m=h(e=>e.fetchProviderCatalog),g=h(e=>e.agentProfiles),[_,v]=(0,A.useState)(``),[x,S]=(0,A.useState)(()=>K(i[0]?.id||``,i[0])),[C,w]=(0,A.useState)(!1),[T,E]=(0,A.useState)(null),D=t.find(e=>e.id===_),O=i.filter(e=>e.status===`online`),k=r.filter(L);(0,A.useEffect)(()=>{Promise.all([u(),d(),m()])},[]),(0,A.useEffect)(()=>{!x.orchestratorId&&i[0]&&S(e=>({...e,orchestratorId:i[0].id}))},[i]);let M=(0,A.useMemo)(()=>{let e=new Map;for(let t of r){let n=e.get(t.automationId)||[];n.push(t),e.set(t.automationId,n)}return e},[r]);function N(){v(``),S(K(i[0]?.id||``,i[0]))}function P(e){v(e.id),S(J(e))}async function F(){w(!0);try{let e=Y(x);if(x.id)await o(`PATCH`,`/automations/`+encodeURIComponent(x.id),e);else{let t=await o(`POST`,`/automations`,e);v(t.id),S(J(t))}await Promise.all([u(),d()])}catch(e){c(`Save Failed`,e.message)}finally{w(!1)}}async function I(e,t){try{await o(`PATCH`,`/automations/`+encodeURIComponent(e.id),t),await u()}catch(e){c(`Update Failed`,e.message)}}async function R(e){try{await o(`POST`,`/automations/`+encodeURIComponent(e.id)+`/run`),await Promise.all([d(),p()])}catch(e){c(`Run Failed`,e.message)}}function z(e){l(`Delete Automation`,`Delete automation "${e.name}"?`,async()=>{try{await o(`DELETE`,`/automations/`+encodeURIComponent(e.id)),_===e.id&&N(),await Promise.all([u(),d()])}catch(e){c(`Delete Failed`,e.message)}})}return(0,j.jsxs)(`div`,{className:`space-y-4`,children:[(0,j.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,j.jsx)(n,{className:`w-5 h-5 text-muted-foreground`}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h2`,{className:`text-lg font-semibold`,children:`Automation`}),(0,j.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Scheduled tasks`})]}),(0,j.jsxs)(`div`,{className:`ml-auto flex flex-wrap items-center gap-2`,children:[(0,j.jsxs)(y,{variant:`outline`,className:`text-xs`,children:[t.length,` schedules`]}),(0,j.jsxs)(y,{variant:`outline`,className:`text-xs`,children:[k.length,` running`]}),(0,j.jsxs)(b,{size:`sm`,className:`gap-1`,onClick:N,children:[(0,j.jsx)(s,{className:`w-3.5 h-3.5`}),`New`]})]})]}),k.length>0&&(0,j.jsx)(Z,{runs:k,automations:t,agents:a,orchestrators:i,now:e,logRunId:T,onToggleLogs:e=>E(t=>t===e?null:e)}),(0,j.jsxs)(`div`,{className:`grid gap-4 xl:grid-cols-[minmax(0,1fr)_420px]`,children:[(0,j.jsx)(`div`,{className:`space-y-3`,children:t.length===0?(0,j.jsx)(`div`,{className:`py-16 text-center text-sm text-muted-foreground`,children:`No automations`}):t.map(t=>(0,j.jsx)(Q,{automation:t,runs:M.get(t.id)||[],now:e,selected:_===t.id,onEdit:()=>P(t),onRun:()=>R(t),onToggle:()=>I(t,{enabled:!t.enabled}),onDelete:()=>z(t)},t.id))}),(0,j.jsx)(ee,{form:x,agentProfiles:g,selected:D,saving:C,orchestrators:O.length?O:i,onChange:e=>S(t=>({...t,...e})),onSave:F})]}),(0,j.jsx)(te,{runs:r,automations:t,agents:a,orchestrators:i,now:e,logRunId:T,onToggleLogs:e=>E(t=>t===e?null:e)})]})}function Z({runs:e,automations:t,agents:n,orchestrators:r,now:a,logRunId:o,onToggleLogs:s}){let c=new Map(t.map(e=>[e.id,e.name]));return(0,j.jsxs)(`div`,{className:`rounded-lg border border-border bg-card/40`,children:[(0,j.jsx)(`div`,{className:`border-b border-border px-3 py-2 text-sm font-medium`,children:`Active runs`}),(0,j.jsx)(`div`,{className:`divide-y divide-border`,children:e.map(e=>{let t=G(e,n,r),l=U(e,a);return(0,j.jsxs)(`div`,{className:`space-y-3 px-3 py-3`,children:[(0,j.jsxs)(`div`,{className:`grid gap-3 text-xs md:grid-cols-[minmax(12rem,1fr)_7rem_7rem_7rem_8rem_auto] md:items-center`,children:[(0,j.jsxs)(`div`,{className:`min-w-0`,children:[(0,j.jsx)(`div`,{className:`truncate text-sm font-medium`,children:c.get(e.automationId)||e.automationId.slice(0,12)}),(0,j.jsx)(`div`,{className:`truncate font-mono text-muted-foreground`,children:e.targetAgentId||e.spawnedAgentId||z(e.meta?.onDemandLabel)||`-`})]}),(0,j.jsx)(y,{variant:`outline`,className:`border text-[10px] w-fit ${F(e)}`,children:P(e)}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`span`,{className:`text-muted-foreground`,children:`elapsed `}),H(e,a)]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`span`,{className:`text-muted-foreground`,children:`left `}),l||`-`]}),(0,j.jsx)(`div`,{className:`font-mono text-muted-foreground`,children:e.taskId?`task #${e.taskId}`:`-`}),t&&(0,j.jsxs)(b,{type:`button`,size:`sm`,variant:`outline`,className:`h-7 gap-1 text-xs`,onClick:()=>s(e.id),children:[(0,j.jsx)(i,{className:`w-3 h-3`}),`Logs`]})]}),o===e.id&&t&&(0,j.jsx)(C,{orchestratorId:t.orchestratorId,session:t.session})]},e.id)})})]})}function Q({automation:e,runs:t,now:n,selected:i,onEdit:a,onRun:s,onToggle:c,onDelete:u}){let d=t[0],f=e.targetPolicy,p=f.mode===`existing_agent`?[f.selector.provider,f.selector.label?`label:${f.selector.label}`:``,...(f.selector.tags||[]).map(e=>`tag:${e}`),...(f.selector.capabilities||[]).map(e=>`cap:${e}`)].filter(Boolean).join(` `):`${f.provider} on demand`;return(0,j.jsxs)(k,{className:`${i?`ring-1 ring-primary/50`:``} ${e.enabled?``:`opacity-60`}`,children:[(0,j.jsx)(E,{className:`px-4 py-3`,children:(0,j.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,j.jsx)(S,{checked:e.enabled,onCheckedChange:c,className:`mt-0.5`}),(0,j.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,j.jsx)(O,{className:`text-sm truncate`,children:e.name}),(0,j.jsxs)(`div`,{className:`mt-1 flex flex-wrap gap-1.5 text-xs text-muted-foreground`,children:[(0,j.jsx)(`span`,{className:`font-mono`,children:e.schedule}),(0,j.jsx)(`span`,{children:e.timezone}),(0,j.jsx)(`span`,{children:e.orchestratorId}),(0,j.jsx)(`span`,{className:`truncate max-w-[28rem]`,children:p})]})]}),(0,j.jsxs)(`div`,{className:`flex gap-1`,children:[(0,j.jsx)(b,{size:`icon`,variant:`ghost`,className:`h-7 w-7`,title:`Run now`,onClick:s,children:(0,j.jsx)(o,{className:`w-3.5 h-3.5`})}),(0,j.jsx)(b,{size:`icon`,variant:`ghost`,className:`h-7 w-7`,title:`Edit`,onClick:a,children:(0,j.jsx)(r,{className:`w-3.5 h-3.5`})}),(0,j.jsx)(b,{size:`icon`,variant:`ghost`,className:`h-7 w-7 text-red-400 hover:text-red-300`,title:`Delete`,onClick:u,children:(0,j.jsx)(l,{className:`w-3.5 h-3.5`})})]})]})}),(0,j.jsx)(D,{className:`px-4 pb-3 pt-0`,children:(0,j.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2 text-xs text-muted-foreground`,children:[(0,j.jsxs)(`span`,{title:e.nextRunAt?v(e.nextRunAt):void 0,children:[`next `,e.nextRunAt?_(n,e.nextRunAt):`disabled`]}),d&&(0,j.jsx)(y,{variant:`outline`,className:`border text-[10px] ${F(d)}`,children:P(d)}),d?.taskId&&(0,j.jsxs)(`span`,{children:[`task #`,d.taskId]}),d&&I(d)&&(0,j.jsx)(`span`,{className:`text-red-400 truncate`,children:I(d)})]})})]})}function ee({form:e,selected:t,saving:n,orchestrators:r,agentProfiles:i,onChange:o,onSave:s}){let c=h(e=>e.providerCatalog),l=r.find(t=>t.id===e.orchestratorId)?.providers||[],f=m(e.provider,c),p=f.find(t=>t.alias===e.model)?.efforts||[];function g(t){let n=r.find(e=>e.id===t)?.providers[0]||e.provider;o({orchestratorId:t,provider:n,model:d(n,c),effort:``})}function _(e){o({provider:e,model:d(e,c),effort:``})}return(0,j.jsxs)(`div`,{className:`rounded-lg border border-border bg-card p-4 space-y-4 h-fit xl:sticky xl:top-4`,children:[(0,j.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,j.jsx)(`h3`,{className:`text-sm font-semibold`,children:t?`Edit schedule`:`New schedule`}),(0,j.jsx)(y,{variant:e.enabled?`default`:`secondary`,className:`ml-auto text-[10px]`,children:e.enabled?`enabled`:`disabled`})]}),(0,j.jsxs)(`div`,{className:`grid gap-3`,children:[(0,j.jsx)($,{label:`Name`,children:(0,j.jsx)(x,{value:e.name,onChange:e=>o({name:e.target.value})})}),(0,j.jsx)($,{label:`Description`,children:(0,j.jsx)(x,{value:e.description,onChange:e=>o({description:e.target.value})})}),(0,j.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,j.jsx)($,{label:`Cron`,children:(0,j.jsx)(x,{value:e.schedule,onChange:e=>o({schedule:e.target.value}),className:`font-mono`})}),(0,j.jsx)($,{label:`Timezone`,children:(0,j.jsx)(x,{value:e.timezone,onChange:e=>o({timezone:e.target.value})})})]}),(0,j.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,j.jsx)($,{label:`Orchestrator`,children:(0,j.jsxs)(`select`,{className:`h-9 w-full rounded-md border border-input bg-background px-3 text-sm`,value:e.orchestratorId,onChange:e=>g(e.target.value),children:[(0,j.jsx)(`option`,{value:``,children:`Select`}),r.map(e=>(0,j.jsxs)(`option`,{value:e.id,children:[e.hostname,` (`,e.id,`)`]},e.id))]})}),(0,j.jsx)($,{label:`Concurrency`,children:(0,j.jsxs)(`select`,{className:`h-9 w-full rounded-md border border-input bg-background px-3 text-sm`,value:e.concurrencyPolicy,onChange:e=>o({concurrencyPolicy:e.target.value}),children:[(0,j.jsx)(`option`,{value:`skip`,children:`skip`}),(0,j.jsx)(`option`,{value:`replace`,children:`replace`}),(0,j.jsx)(`option`,{value:`queue`,children:`queue`})]})})]}),(0,j.jsxs)(`label`,{className:`flex items-center justify-between rounded-md border border-border px-3 py-2`,children:[(0,j.jsx)(`span`,{className:`text-sm`,children:`Enabled`}),(0,j.jsx)(S,{checked:e.enabled,onCheckedChange:e=>o({enabled:e})})]})]}),(0,j.jsxs)(`div`,{className:`space-y-3`,children:[(0,j.jsx)(w,{children:`Target`}),(0,j.jsxs)(`div`,{className:`grid grid-cols-2 gap-2`,children:[(0,j.jsx)(b,{type:`button`,variant:e.targetMode===`existing_agent`?`default`:`outline`,onClick:()=>o({targetMode:`existing_agent`}),children:`Existing`}),(0,j.jsx)(b,{type:`button`,variant:e.targetMode===`on_demand_agent`?`default`:`outline`,onClick:()=>o({targetMode:`on_demand_agent`}),children:`On demand`})]}),(0,j.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,j.jsx)($,{label:`Provider`,children:(0,j.jsxs)(`select`,{className:`h-9 w-full rounded-md border border-input bg-background px-3 text-sm`,value:e.provider,onChange:e=>_(e.target.value),disabled:!l.length,children:[!l.length&&(0,j.jsx)(`option`,{value:e.provider,children:`No providers available`}),l.map(e=>(0,j.jsx)(`option`,{value:e,children:e},e))]})}),e.targetMode===`existing_agent`?(0,j.jsx)($,{label:`If no match`,children:(0,j.jsxs)(`select`,{className:`h-9 w-full rounded-md border border-input bg-background px-3 text-sm`,value:e.ifNoMatch,onChange:e=>o({ifNoMatch:e.target.value}),children:[(0,j.jsx)(`option`,{value:`fail`,children:`fail`}),(0,j.jsx)(`option`,{value:`spawn`,children:`spawn`})]})}):(0,j.jsx)($,{label:`Approval`,children:(0,j.jsxs)(`select`,{className:`h-9 w-full rounded-md border border-input bg-background px-3 text-sm`,value:e.approvalMode,onChange:e=>o({approvalMode:e.target.value}),children:[(0,j.jsx)(`option`,{value:`guarded`,children:`guarded`}),(0,j.jsx)(`option`,{value:`read-only`,children:`read-only`}),(0,j.jsx)(`option`,{value:`open`,children:`open`})]})})]}),e.targetMode===`on_demand_agent`&&(0,j.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,j.jsx)($,{label:`Model`,children:(0,j.jsx)(`select`,{className:`h-9 w-full rounded-md border border-input bg-background px-3 text-sm`,value:e.model,onChange:e=>o({model:e.target.value,effort:``}),children:f.map(e=>(0,j.jsx)(`option`,{value:e.alias,children:e.label},e.alias))})}),(0,j.jsx)($,{label:`Effort`,children:(0,j.jsxs)(`select`,{className:`h-9 w-full rounded-md border border-input bg-background px-3 text-sm`,value:e.effort,onChange:e=>o({effort:e.target.value}),disabled:!p.length,children:[(0,j.jsx)(`option`,{value:``,children:`Default`}),p.map(e=>(0,j.jsx)(`option`,{value:e,children:e},e))]})}),(0,j.jsx)($,{label:`Agent profile`,children:(0,j.jsxs)(`select`,{className:`h-9 w-full rounded-md border border-input bg-background px-3 text-sm`,value:e.profile||`default-relay`,onChange:e=>o({profile:e.target.value}),children:[!i.length&&(0,j.jsx)(`option`,{value:`default-relay`,children:`default-relay`}),i.map(e=>(0,j.jsx)(`option`,{value:e.name,children:e.name},e.name))]})}),(0,j.jsx)($,{label:`Workspace`,children:(0,j.jsxs)(`select`,{className:`h-9 w-full rounded-md border border-input bg-background px-3 text-sm`,value:e.workspaceMode,onChange:e=>o({workspaceMode:e.target.value}),children:[(0,j.jsx)(`option`,{value:`inherit`,children:`inherit (default → isolated worktree)`}),(0,j.jsx)(`option`,{value:`shared`,children:`shared (run in the repo, commit to its branch)`}),(0,j.jsx)(`option`,{value:`isolated`,children:`isolated (fresh git worktree per run)`})]})})]}),e.targetMode===`existing_agent`?(0,j.jsxs)(`div`,{className:`grid gap-3`,children:[(0,j.jsx)($,{label:`Label`,children:(0,j.jsx)(x,{value:e.label,onChange:e=>o({label:e.target.value})})}),(0,j.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,j.jsx)($,{label:`Tags`,children:(0,j.jsx)(x,{value:e.tags,onChange:e=>o({tags:e.target.value})})}),(0,j.jsx)($,{label:`Capabilities`,children:(0,j.jsx)(x,{value:e.capabilities,onChange:e=>o({capabilities:e.target.value})})})]})]}):(0,j.jsxs)(`div`,{className:`grid gap-3`,children:[(0,j.jsx)($,{label:`Cwd`,children:(0,j.jsx)(x,{value:e.cwd,onChange:e=>o({cwd:e.target.value})})}),(0,j.jsxs)(`label`,{className:`flex items-center justify-between rounded-md border border-border px-3 py-2`,children:[(0,j.jsx)(`span`,{className:`text-sm`,children:`Keep alive`}),(0,j.jsx)(S,{checked:e.keepAlive,onCheckedChange:e=>o({keepAlive:e})})]}),(0,j.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,j.jsx)($,{label:`Budget minutes`,children:(0,j.jsx)(x,{type:`number`,min:1,step:1,value:e.budgetMinutes,onChange:e=>o({budgetMinutes:e.target.value})})}),(0,j.jsx)($,{label:`Warning minute`,children:(0,j.jsx)(x,{type:`number`,min:0,step:1,value:e.warningMinutes,onChange:e=>o({warningMinutes:e.target.value})})})]}),(0,j.jsx)($,{label:`Warning message`,children:(0,j.jsx)(T,{value:e.warningMessage,onChange:e=>o({warningMessage:e.target.value}),className:`min-h-20`})})]})]}),(0,j.jsxs)(`div`,{className:`space-y-3`,children:[(0,j.jsx)(w,{children:`Task`}),(0,j.jsx)($,{label:`Title`,children:(0,j.jsx)(x,{value:e.title,onChange:e=>o({title:e.target.value})})}),(0,j.jsx)($,{label:`Body`,children:(0,j.jsx)(T,{value:e.body,onChange:e=>o({body:e.target.value}),className:`min-h-28`})}),(0,j.jsxs)(`div`,{className:`grid grid-cols-3 gap-3`,children:[(0,j.jsx)($,{label:`Severity`,children:(0,j.jsxs)(`select`,{className:`h-9 w-full rounded-md border border-input bg-background px-3 text-sm`,value:e.severity,onChange:e=>o({severity:e.target.value}),children:[(0,j.jsx)(`option`,{value:`info`,children:`info`}),(0,j.jsx)(`option`,{value:`warning`,children:`warning`}),(0,j.jsx)(`option`,{value:`critical`,children:`critical`})]})}),(0,j.jsx)($,{label:`Dedupe key`,children:(0,j.jsx)(x,{value:e.dedupeKey,onChange:e=>o({dedupeKey:e.target.value})})}),(0,j.jsx)($,{label:`External URL`,children:(0,j.jsx)(x,{value:e.externalUrl,onChange:e=>o({externalUrl:e.target.value})})})]})]}),(0,j.jsxs)(b,{className:`w-full gap-2`,onClick:s,disabled:n||!e.name||!e.schedule||!e.orchestratorId||!e.title||!e.body,children:[n?(0,j.jsx)(a,{className:`w-4 h-4 animate-spin`}):(0,j.jsx)(u,{className:`w-4 h-4`}),`Save`]})]})}function $({label:e,children:t}){return(0,j.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,j.jsx)(w,{className:`text-xs text-muted-foreground`,children:e}),t]})}function te({runs:e,automations:t,agents:n,orchestrators:r,now:a,logRunId:o,onToggleLogs:s}){let c=new Map(t.map(e=>[e.id,e.name]));return(0,j.jsxs)(`div`,{className:`space-y-2`,children:[(0,j.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,j.jsx)(`h3`,{className:`text-sm font-semibold`,children:`Runs`}),(0,j.jsx)(y,{variant:`secondary`,className:`text-[10px]`,children:e.length})]}),(0,j.jsxs)(`div`,{className:`rounded-lg border border-border overflow-x-auto`,children:[(0,j.jsxs)(`div`,{className:`grid grid-cols-[minmax(10rem,1fr)_8rem_8rem_7rem_7rem_5rem] gap-3 bg-muted/30 px-3 py-2 text-xs text-muted-foreground`,children:[(0,j.jsx)(`span`,{children:`Automation`}),(0,j.jsx)(`span`,{children:`Status`}),(0,j.jsx)(`span`,{children:`Agent`}),(0,j.jsx)(`span`,{children:`Runtime`}),(0,j.jsx)(`span`,{children:`Updated`}),(0,j.jsx)(`span`,{children:`Logs`})]}),e.length===0?(0,j.jsx)(`div`,{className:`px-3 py-10 text-center text-sm text-muted-foreground`,children:`No runs`}):e.slice(0,80).map(e=>{let t=G(e,n,r),l=U(e,a),u=W(e);return(0,j.jsxs)(`div`,{className:`border-t border-border`,children:[(0,j.jsxs)(`div`,{className:`grid grid-cols-[minmax(10rem,1fr)_8rem_8rem_7rem_7rem_5rem] gap-3 px-3 py-2 text-xs items-center`,children:[(0,j.jsxs)(`div`,{className:`min-w-0`,children:[(0,j.jsx)(`div`,{className:`truncate`,children:c.get(e.automationId)||e.automationId.slice(0,12)}),(0,j.jsxs)(`div`,{className:`truncate text-muted-foreground`,children:[e.taskId?`task #${e.taskId}`:`no task`,l?` · ${l} left`:``,u?` · ${u}`:``]})]}),(0,j.jsx)(y,{variant:`outline`,className:`border text-[10px] w-fit ${F(e)}`,children:P(e)}),(0,j.jsx)(`span`,{className:`truncate font-mono text-muted-foreground`,children:e.targetAgentId||e.spawnedAgentId||`-`}),(0,j.jsx)(`span`,{className:`text-muted-foreground`,children:H(e,a)}),(0,j.jsx)(`span`,{className:`text-muted-foreground`,title:v(e.updatedAt),children:_(a,e.updatedAt)}),t?(0,j.jsx)(b,{type:`button`,size:`sm`,variant:`ghost`,className:`h-7 w-7 p-0`,title:`Open logs`,onClick:()=>s(e.id),children:(0,j.jsx)(i,{className:`w-3.5 h-3.5`})}):(0,j.jsx)(`span`,{className:`text-muted-foreground`,children:`-`})]}),o===e.id&&t&&(0,j.jsx)(`div`,{className:`border-t border-border px-3 py-3`,children:(0,j.jsx)(C,{orchestratorId:t.orchestratorId,session:t.session})})]},e.id)})]})]})}export{X as AutomationView};
2
- //# sourceMappingURL=automation-CuTlgY1E.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"automation-CuTlgY1E.js","names":[],"sources":["../../dashboard/src/components/views/automation.tsx"],"sourcesContent":["import { useEffect, useMemo, useState, type ReactNode } from 'react'\nimport { useRelayStore, useNow } from '@/store'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport { Switch } from '@/components/ui/switch'\nimport { Textarea } from '@/components/ui/textarea'\nimport { fmtTime, timeAgo } from '@/lib/display'\nimport { LogViewer } from '@/components/shared/log-viewer'\nimport { availableModelsFor, defaultModelFor } from '@/store/helpers'\nimport { CalendarClock, FileText, Pencil, Play, Plus, RotateCw, Save, Trash2 } from 'lucide-react'\nimport { getKnownProviderIds } from '@/lib/provider-display'\nimport { firstAvailableProvider } from '@/store/helpers'\nimport type {\n Agent,\n Automation,\n AutomationConcurrencyPolicy,\n AutomationRun,\n AutomationTargetPolicy,\n CreateAutomationInput,\n Orchestrator,\n SpawnApprovalMode,\n SpawnEffort,\n SpawnProvider,\n TaskSeverity,\n WorkspaceMode,\n} from '@/types'\n\nconst RUN_STATUS_COLORS: Record<string, string> = {\n scheduled: 'bg-blue-500/10 text-blue-400 border-blue-500/20',\n dispatching: 'bg-sky-500/10 text-sky-400 border-sky-500/20',\n waiting_agent: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20',\n running: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n succeeded: 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20',\n failed: 'bg-red-500/10 text-red-400 border-red-500/20',\n canceled: 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20',\n timed_out: 'bg-red-500/10 text-red-400 border-red-500/20',\n}\n\nconst ACTIVE_RUN_STATUSES = new Set(['scheduled', 'dispatching', 'waiting_agent', 'running'])\n\nfunction runStatusLabel(run: AutomationRun): string {\n return run.status === 'timed_out' && run.result?.runtimeBudgetElapsed === true ? 'budget elapsed' : run.status\n}\n\nfunction runStatusClass(run: AutomationRun): string {\n if (run.status === 'timed_out' && run.result?.runtimeBudgetElapsed === true) return 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20'\n return RUN_STATUS_COLORS[run.status] || ''\n}\n\nfunction runErrorText(run: AutomationRun): string | undefined {\n return run.status === 'timed_out' && run.result?.runtimeBudgetElapsed === true ? undefined : run.error\n}\n\nfunction isActiveRun(run: AutomationRun): boolean {\n return ACTIVE_RUN_STATUSES.has(run.status)\n}\n\nfunction num(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined\n}\n\nfunction str(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined\n}\n\nfunction duration(ms: number): string {\n const total = Math.max(0, Math.round(ms / 1000))\n const hours = Math.floor(total / 3600)\n const minutes = Math.floor((total % 3600) / 60)\n const seconds = total % 60\n if (hours) return `${hours}h ${minutes}m`\n if (minutes) return `${minutes}m ${seconds}s`\n return `${seconds}s`\n}\n\nfunction runDeadline(run: AutomationRun): number | undefined {\n return num(run.meta?.runtimeBudgetDeadlineAt) ?? num(run.result?.deadlineAt)\n}\n\nfunction elapsedText(run: AutomationRun, now: number): string {\n if (!run.startedAt) return '-'\n const end = run.finishedAt || (isActiveRun(run) ? now : run.updatedAt)\n return duration(Number(end) - Number(run.startedAt))\n}\n\nfunction remainingText(run: AutomationRun, now: number): string | undefined {\n const deadline = runDeadline(run)\n if (!deadline || !isActiveRun(run)) return undefined\n return duration(deadline - now)\n}\n\nfunction shutdownText(run: AutomationRun): string | undefined {\n if (run.status === 'timed_out' && run.result?.runtimeBudgetElapsed === true) return 'runtime budget elapsed'\n return run.error\n}\n\nfunction runLogTarget(run: AutomationRun, agents: Agent[], orchestrators: Orchestrator[]): { orchestratorId: string; session: string } | undefined {\n const agentId = run.targetAgentId || run.spawnedAgentId || str(run.meta?.runtimeAgentId)\n const agent = agentId ? agents.find((candidate) => candidate.id === agentId) : undefined\n const managed = agentId\n ? orchestrators.flatMap((orchestrator) => orchestrator.managedAgents.map((managedAgent) => ({ orchestrator, managedAgent })))\n .find(({ managedAgent }) => managedAgent.agentId === agentId)\n : undefined\n const session = str(run.meta?.terminalSession)\n || str(run.meta?.tmuxSession)\n || str(run.meta?.sessionName)\n || str(agent?.meta?.terminalSession)\n || str(agent?.meta?.tmuxSession)\n || str(agent?.meta?.sessionName)\n || managed?.managedAgent.terminalSession\n || managed?.managedAgent.tmuxSession\n || managed?.managedAgent.sessionName\n const orchestratorId = str(run.meta?.orchestratorId) || managed?.orchestrator.id || run.orchestratorId\n return session ? { orchestratorId, session } : undefined\n}\n\ntype TargetMode = 'existing_agent' | 'on_demand_agent'\n\ninterface AutomationForm {\n id?: string\n name: string\n description: string\n enabled: boolean\n schedule: string\n timezone: string\n orchestratorId: string\n targetMode: TargetMode\n provider: SpawnProvider\n model: string\n effort: string\n profile: string\n label: string\n tags: string\n capabilities: string\n ifNoMatch: 'fail' | 'spawn'\n cwd: string\n approvalMode: SpawnApprovalMode\n workspaceMode: WorkspaceMode\n keepAlive: boolean\n budgetMinutes: string\n warningMinutes: string\n warningMessage: string\n concurrencyPolicy: AutomationConcurrencyPolicy\n title: string\n body: string\n severity: TaskSeverity\n dedupeKey: string\n externalUrl: string\n}\n\nfunction blankForm(orchestratorId = '', orch?: Orchestrator): AutomationForm {\n const provider = firstAvailableProvider(orch)\n return {\n name: '',\n description: '',\n enabled: true,\n schedule: '0 8 * * *',\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC',\n orchestratorId,\n targetMode: 'existing_agent',\n provider,\n model: defaultModelFor(provider),\n effort: '',\n profile: 'default-relay',\n label: '',\n tags: '',\n capabilities: '',\n ifNoMatch: 'fail',\n cwd: '',\n approvalMode: 'guarded',\n workspaceMode: 'inherit',\n keepAlive: false,\n budgetMinutes: '',\n warningMinutes: '',\n warningMessage: '',\n concurrencyPolicy: 'skip',\n title: '',\n body: '',\n severity: 'info',\n dedupeKey: '',\n externalUrl: '',\n }\n}\n\nfunction splitCsv(value: string): string[] | undefined {\n const items = value.split(',').map((item) => item.trim()).filter(Boolean)\n return items.length ? items : undefined\n}\n\nfunction automationToForm(automation: Automation): AutomationForm {\n const policy = automation.targetPolicy\n return {\n ...blankForm(automation.orchestratorId),\n id: automation.id,\n name: automation.name,\n description: automation.description || '',\n enabled: automation.enabled,\n schedule: automation.schedule,\n timezone: automation.timezone,\n orchestratorId: automation.orchestratorId,\n targetMode: policy.mode,\n provider: policy.mode === 'on_demand_agent' ? policy.provider : policy.selector.provider || getKnownProviderIds()[0] || '',\n model: policy.mode === 'on_demand_agent' ? policy.model || defaultModelFor(policy.provider) : '',\n effort: policy.mode === 'on_demand_agent' ? policy.effort || '' : '',\n profile: policy.mode === 'on_demand_agent' ? policy.profile || 'default-relay' : 'default-relay',\n label: policy.mode === 'existing_agent' ? policy.selector.label || '' : '',\n tags: policy.mode === 'existing_agent' ? (policy.selector.tags || []).join(', ') : '',\n capabilities: policy.mode === 'existing_agent' ? (policy.selector.capabilities || []).join(', ') : '',\n ifNoMatch: policy.mode === 'existing_agent' ? policy.ifNoMatch || 'fail' : 'fail',\n cwd: policy.mode === 'on_demand_agent' ? policy.cwd || '' : '',\n approvalMode: policy.mode === 'on_demand_agent' ? policy.approvalMode || 'guarded' : 'guarded',\n workspaceMode: policy.mode === 'on_demand_agent' ? policy.workspaceMode || 'inherit' : 'inherit',\n keepAlive: policy.mode === 'on_demand_agent' ? Boolean(policy.keepAlive) : false,\n budgetMinutes: policy.mode === 'on_demand_agent' && policy.runtimeBudget ? String(Math.round(policy.runtimeBudget.maxRuntimeMs / 60_000)) : '',\n warningMinutes: policy.mode === 'on_demand_agent' && policy.runtimeBudget?.warnAtMs !== undefined ? String(Math.round(policy.runtimeBudget.warnAtMs / 60_000)) : '',\n warningMessage: policy.mode === 'on_demand_agent' ? policy.runtimeBudget?.warningMessage || '' : '',\n concurrencyPolicy: automation.concurrencyPolicy,\n title: automation.taskTemplate.title,\n body: automation.taskTemplate.body,\n severity: automation.taskTemplate.severity || 'info',\n dedupeKey: automation.taskTemplate.dedupeKey || '',\n externalUrl: automation.taskTemplate.externalUrl || '',\n }\n}\n\nfunction formToInput(form: AutomationForm): CreateAutomationInput {\n const budgetMinutes = Number(form.budgetMinutes)\n const warningMinutes = Number(form.warningMinutes)\n const runtimeBudget = form.targetMode === 'on_demand_agent' && form.budgetMinutes.trim() && Number.isFinite(budgetMinutes)\n ? {\n maxRuntimeMs: Math.round(budgetMinutes * 60_000),\n warnAtMs: form.warningMinutes.trim() && Number.isFinite(warningMinutes) ? Math.round(warningMinutes * 60_000) : undefined,\n warningMessage: form.warningMessage.trim() || undefined,\n }\n : undefined\n const targetPolicy: AutomationTargetPolicy = form.targetMode === 'existing_agent'\n ? {\n mode: 'existing_agent',\n selector: {\n provider: form.provider || undefined,\n label: form.label.trim() || undefined,\n tags: splitCsv(form.tags),\n capabilities: splitCsv(form.capabilities),\n },\n ifNoMatch: form.ifNoMatch,\n }\n : {\n mode: 'on_demand_agent',\n provider: form.provider,\n model: form.model || undefined,\n effort: form.effort ? form.effort as SpawnEffort : undefined,\n profile: form.profile || undefined,\n cwd: form.cwd.trim() || undefined,\n approvalMode: form.approvalMode,\n workspaceMode: form.workspaceMode,\n keepAlive: form.keepAlive,\n runtimeBudget,\n }\n\n return {\n kind: 'scheduled_task',\n name: form.name.trim(),\n description: form.description.trim() || undefined,\n enabled: form.enabled,\n schedule: form.schedule.trim(),\n timezone: form.timezone.trim() || 'UTC',\n catchUpPolicy: 'skip',\n concurrencyPolicy: form.concurrencyPolicy,\n orchestratorId: form.orchestratorId,\n targetPolicy,\n taskTemplate: {\n title: form.title.trim(),\n body: form.body.trim(),\n severity: form.severity,\n dedupeKey: form.dedupeKey.trim() || undefined,\n externalUrl: form.externalUrl.trim() || undefined,\n },\n }\n}\n\nexport function AutomationView() {\n const now = useNow()\n const automations = useRelayStore((s) => s.automations)\n const automationRuns = useRelayStore((s) => s.automationRuns)\n const orchestrators = useRelayStore((s) => s.orchestrators)\n const agents = useRelayStore((s) => s.agents)\n const apiCall = useRelayStore((s) => s.apiCall)\n const showError = useRelayStore((s) => s.showError)\n const openConfirm = useRelayStore((s) => s.openConfirm)\n const fetchAutomations = useRelayStore((s) => s.fetchAutomations)\n const fetchAutomationRuns = useRelayStore((s) => s.fetchAutomationRuns)\n const fetchTasks = useRelayStore((s) => s.fetchTasks)\n const fetchProviderCatalog = useRelayStore((s) => s.fetchProviderCatalog)\n const agentProfiles = useRelayStore((s) => s.agentProfiles)\n const [selectedId, setSelectedId] = useState<string>('')\n const [form, setForm] = useState<AutomationForm>(() => blankForm(orchestrators[0]?.id || '', orchestrators[0]))\n const [saving, setSaving] = useState(false)\n const [logRunId, setLogRunId] = useState<string | null>(null)\n\n const selected = automations.find((automation) => automation.id === selectedId)\n const onlineOrchestrators = orchestrators.filter((orchestrator) => orchestrator.status === 'online')\n const activeRuns = automationRuns.filter(isActiveRun)\n\n useEffect(() => {\n void Promise.all([fetchAutomations(), fetchAutomationRuns(), fetchProviderCatalog()])\n }, [])\n\n useEffect(() => {\n if (!form.orchestratorId && orchestrators[0]) {\n setForm((current) => ({ ...current, orchestratorId: orchestrators[0]!.id }))\n }\n }, [orchestrators])\n\n const runsByAutomation = useMemo(() => {\n const grouped = new Map<string, AutomationRun[]>()\n for (const run of automationRuns) {\n const list = grouped.get(run.automationId) || []\n list.push(run)\n grouped.set(run.automationId, list)\n }\n return grouped\n }, [automationRuns])\n\n function newAutomation() {\n setSelectedId('')\n setForm(blankForm(orchestrators[0]?.id || '', orchestrators[0]))\n }\n\n function editAutomation(automation: Automation) {\n setSelectedId(automation.id)\n setForm(automationToForm(automation))\n }\n\n async function saveAutomation() {\n setSaving(true)\n try {\n const input = formToInput(form)\n if (form.id) await apiCall('PATCH', '/automations/' + encodeURIComponent(form.id), input)\n else {\n const created = await apiCall<Automation>('POST', '/automations', input)\n setSelectedId(created.id)\n setForm(automationToForm(created))\n }\n await Promise.all([fetchAutomations(), fetchAutomationRuns()])\n } catch (e) {\n showError('Save Failed', (e as Error).message)\n } finally {\n setSaving(false)\n }\n }\n\n async function patchAutomation(automation: Automation, partial: Partial<Automation>) {\n try {\n await apiCall('PATCH', '/automations/' + encodeURIComponent(automation.id), partial)\n await fetchAutomations()\n } catch (e) {\n showError('Update Failed', (e as Error).message)\n }\n }\n\n async function runNow(automation: Automation) {\n try {\n await apiCall('POST', '/automations/' + encodeURIComponent(automation.id) + '/run')\n await Promise.all([fetchAutomationRuns(), fetchTasks()])\n } catch (e) {\n showError('Run Failed', (e as Error).message)\n }\n }\n\n function deleteAutomation(automation: Automation) {\n openConfirm('Delete Automation', `Delete automation \"${automation.name}\"?`, async () => {\n try {\n await apiCall('DELETE', '/automations/' + encodeURIComponent(automation.id))\n if (selectedId === automation.id) newAutomation()\n await Promise.all([fetchAutomations(), fetchAutomationRuns()])\n } catch (e) {\n showError('Delete Failed', (e as Error).message)\n }\n })\n }\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <CalendarClock className=\"w-5 h-5 text-muted-foreground\" />\n <div>\n <h2 className=\"text-lg font-semibold\">Automation</h2>\n <p className=\"text-xs text-muted-foreground\">Scheduled tasks</p>\n </div>\n <div className=\"ml-auto flex flex-wrap items-center gap-2\">\n <Badge variant=\"outline\" className=\"text-xs\">{automations.length} schedules</Badge>\n <Badge variant=\"outline\" className=\"text-xs\">{activeRuns.length} running</Badge>\n <Button size=\"sm\" className=\"gap-1\" onClick={newAutomation}>\n <Plus className=\"w-3.5 h-3.5\" />\n New\n </Button>\n </div>\n </div>\n\n {activeRuns.length > 0 && (\n <ActiveRuns\n runs={activeRuns}\n automations={automations}\n agents={agents}\n orchestrators={orchestrators}\n now={now}\n logRunId={logRunId}\n onToggleLogs={(runId) => setLogRunId((current) => current === runId ? null : runId)}\n />\n )}\n\n <div className=\"grid gap-4 xl:grid-cols-[minmax(0,1fr)_420px]\">\n <div className=\"space-y-3\">\n {automations.length === 0 ? (\n <div className=\"py-16 text-center text-sm text-muted-foreground\">No automations</div>\n ) : automations.map((automation) => (\n <AutomationCard\n key={automation.id}\n automation={automation}\n runs={runsByAutomation.get(automation.id) || []}\n now={now}\n selected={selectedId === automation.id}\n onEdit={() => editAutomation(automation)}\n onRun={() => runNow(automation)}\n onToggle={() => patchAutomation(automation, { enabled: !automation.enabled })}\n onDelete={() => deleteAutomation(automation)}\n />\n ))}\n </div>\n\n <AutomationEditor\n form={form}\n agentProfiles={agentProfiles}\n selected={selected}\n saving={saving}\n orchestrators={onlineOrchestrators.length ? onlineOrchestrators : orchestrators}\n onChange={(patch) => setForm((current) => ({ ...current, ...patch }))}\n onSave={saveAutomation}\n />\n </div>\n\n <RunHistory\n runs={automationRuns}\n automations={automations}\n agents={agents}\n orchestrators={orchestrators}\n now={now}\n logRunId={logRunId}\n onToggleLogs={(runId) => setLogRunId((current) => current === runId ? null : runId)}\n />\n </div>\n )\n}\n\nfunction ActiveRuns({\n runs,\n automations,\n agents,\n orchestrators,\n now,\n logRunId,\n onToggleLogs,\n}: {\n runs: AutomationRun[]\n automations: Automation[]\n agents: Agent[]\n orchestrators: Orchestrator[]\n now: number\n logRunId: string | null\n onToggleLogs: (runId: string) => void\n}) {\n const names = new Map(automations.map((automation) => [automation.id, automation.name]))\n return (\n <div className=\"rounded-lg border border-border bg-card/40\">\n <div className=\"border-b border-border px-3 py-2 text-sm font-medium\">Active runs</div>\n <div className=\"divide-y divide-border\">\n {runs.map((run) => {\n const logTarget = runLogTarget(run, agents, orchestrators)\n const remaining = remainingText(run, now)\n return (\n <div key={run.id} className=\"space-y-3 px-3 py-3\">\n <div className=\"grid gap-3 text-xs md:grid-cols-[minmax(12rem,1fr)_7rem_7rem_7rem_8rem_auto] md:items-center\">\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium\">{names.get(run.automationId) || run.automationId.slice(0, 12)}</div>\n <div className=\"truncate font-mono text-muted-foreground\">{run.targetAgentId || run.spawnedAgentId || str(run.meta?.onDemandLabel) || '-'}</div>\n </div>\n <Badge variant=\"outline\" className={`border text-[10px] w-fit ${runStatusClass(run)}`}>{runStatusLabel(run)}</Badge>\n <div><span className=\"text-muted-foreground\">elapsed </span>{elapsedText(run, now)}</div>\n <div><span className=\"text-muted-foreground\">left </span>{remaining || '-'}</div>\n <div className=\"font-mono text-muted-foreground\">{run.taskId ? `task #${run.taskId}` : '-'}</div>\n {logTarget && (\n <Button type=\"button\" size=\"sm\" variant=\"outline\" className=\"h-7 gap-1 text-xs\" onClick={() => onToggleLogs(run.id)}>\n <FileText className=\"w-3 h-3\" />\n Logs\n </Button>\n )}\n </div>\n {logRunId === run.id && logTarget && <LogViewer orchestratorId={logTarget.orchestratorId} session={logTarget.session} />}\n </div>\n )\n })}\n </div>\n </div>\n )\n}\n\nfunction AutomationCard({\n automation,\n runs,\n now,\n selected,\n onEdit,\n onRun,\n onToggle,\n onDelete,\n}: {\n automation: Automation\n runs: AutomationRun[]\n now: number\n selected: boolean\n onEdit: () => void\n onRun: () => void\n onToggle: () => void\n onDelete: () => void\n}) {\n const latestRun = runs[0]\n const policy = automation.targetPolicy\n const target = policy.mode === 'existing_agent'\n ? [\n policy.selector.provider,\n policy.selector.label ? `label:${policy.selector.label}` : '',\n ...(policy.selector.tags || []).map((tag) => `tag:${tag}`),\n ...(policy.selector.capabilities || []).map((cap) => `cap:${cap}`),\n ].filter(Boolean).join(' ')\n : `${policy.provider} on demand`\n\n return (\n <Card className={`${selected ? 'ring-1 ring-primary/50' : ''} ${automation.enabled ? '' : 'opacity-60'}`}>\n <CardHeader className=\"px-4 py-3\">\n <div className=\"flex items-start gap-3\">\n <Switch checked={automation.enabled} onCheckedChange={onToggle} className=\"mt-0.5\" />\n <div className=\"min-w-0 flex-1\">\n <CardTitle className=\"text-sm truncate\">{automation.name}</CardTitle>\n <div className=\"mt-1 flex flex-wrap gap-1.5 text-xs text-muted-foreground\">\n <span className=\"font-mono\">{automation.schedule}</span>\n <span>{automation.timezone}</span>\n <span>{automation.orchestratorId}</span>\n <span className=\"truncate max-w-[28rem]\">{target}</span>\n </div>\n </div>\n <div className=\"flex gap-1\">\n <Button size=\"icon\" variant=\"ghost\" className=\"h-7 w-7\" title=\"Run now\" onClick={onRun}>\n <Play className=\"w-3.5 h-3.5\" />\n </Button>\n <Button size=\"icon\" variant=\"ghost\" className=\"h-7 w-7\" title=\"Edit\" onClick={onEdit}>\n <Pencil className=\"w-3.5 h-3.5\" />\n </Button>\n <Button size=\"icon\" variant=\"ghost\" className=\"h-7 w-7 text-red-400 hover:text-red-300\" title=\"Delete\" onClick={onDelete}>\n <Trash2 className=\"w-3.5 h-3.5\" />\n </Button>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"px-4 pb-3 pt-0\">\n <div className=\"flex flex-wrap items-center gap-2 text-xs text-muted-foreground\">\n <span title={automation.nextRunAt ? fmtTime(automation.nextRunAt) : undefined}>\n next {automation.nextRunAt ? timeAgo(now, automation.nextRunAt) : 'disabled'}\n </span>\n {latestRun && (\n <Badge variant=\"outline\" className={`border text-[10px] ${runStatusClass(latestRun)}`}>\n {runStatusLabel(latestRun)}\n </Badge>\n )}\n {latestRun?.taskId && <span>task #{latestRun.taskId}</span>}\n {latestRun && runErrorText(latestRun) && <span className=\"text-red-400 truncate\">{runErrorText(latestRun)}</span>}\n </div>\n </CardContent>\n </Card>\n )\n}\n\nfunction AutomationEditor({\n form,\n selected,\n saving,\n orchestrators,\n agentProfiles,\n onChange,\n onSave,\n}: {\n form: AutomationForm\n selected?: Automation\n saving: boolean\n orchestrators: Array<{ id: string; hostname: string; providers: SpawnProvider[] }>\n agentProfiles: Array<{ name: string }>\n onChange: (patch: Partial<AutomationForm>) => void\n onSave: () => void\n}) {\n const providerCatalog = useRelayStore((s) => s.providerCatalog)\n const selectedOrchestrator = orchestrators.find((orch) => orch.id === form.orchestratorId)\n const providers = selectedOrchestrator?.providers || []\n const models = availableModelsFor(form.provider, providerCatalog)\n const selectedModel = models.find((model) => model.alias === form.model)\n const efforts = selectedModel?.efforts || []\n\n function selectOrchestrator(orchestratorId: string) {\n const orchestrator = orchestrators.find((orch) => orch.id === orchestratorId)\n const provider = orchestrator?.providers[0] || form.provider\n onChange({ orchestratorId, provider, model: defaultModelFor(provider, providerCatalog), effort: '' })\n }\n\n function selectProvider(provider: SpawnProvider) {\n onChange({ provider, model: defaultModelFor(provider, providerCatalog), effort: '' })\n }\n\n return (\n <div className=\"rounded-lg border border-border bg-card p-4 space-y-4 h-fit xl:sticky xl:top-4\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"text-sm font-semibold\">{selected ? 'Edit schedule' : 'New schedule'}</h3>\n <Badge variant={form.enabled ? 'default' : 'secondary'} className=\"ml-auto text-[10px]\">\n {form.enabled ? 'enabled' : 'disabled'}\n </Badge>\n </div>\n\n <div className=\"grid gap-3\">\n <Field label=\"Name\">\n <Input value={form.name} onChange={(e) => onChange({ name: e.target.value })} />\n </Field>\n <Field label=\"Description\">\n <Input value={form.description} onChange={(e) => onChange({ description: e.target.value })} />\n </Field>\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Cron\">\n <Input value={form.schedule} onChange={(e) => onChange({ schedule: e.target.value })} className=\"font-mono\" />\n </Field>\n <Field label=\"Timezone\">\n <Input value={form.timezone} onChange={(e) => onChange({ timezone: e.target.value })} />\n </Field>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Orchestrator\">\n <select className=\"h-9 w-full rounded-md border border-input bg-background px-3 text-sm\" value={form.orchestratorId} onChange={(e) => selectOrchestrator(e.target.value)}>\n <option value=\"\">Select</option>\n {orchestrators.map((orch) => <option key={orch.id} value={orch.id}>{orch.hostname} ({orch.id})</option>)}\n </select>\n </Field>\n <Field label=\"Concurrency\">\n <select className=\"h-9 w-full rounded-md border border-input bg-background px-3 text-sm\" value={form.concurrencyPolicy} onChange={(e) => onChange({ concurrencyPolicy: e.target.value as AutomationConcurrencyPolicy })}>\n <option value=\"skip\">skip</option>\n <option value=\"replace\">replace</option>\n <option value=\"queue\">queue</option>\n </select>\n </Field>\n </div>\n <label className=\"flex items-center justify-between rounded-md border border-border px-3 py-2\">\n <span className=\"text-sm\">Enabled</span>\n <Switch checked={form.enabled} onCheckedChange={(enabled) => onChange({ enabled })} />\n </label>\n </div>\n\n <div className=\"space-y-3\">\n <Label>Target</Label>\n <div className=\"grid grid-cols-2 gap-2\">\n <Button type=\"button\" variant={form.targetMode === 'existing_agent' ? 'default' : 'outline'} onClick={() => onChange({ targetMode: 'existing_agent' })}>\n Existing\n </Button>\n <Button type=\"button\" variant={form.targetMode === 'on_demand_agent' ? 'default' : 'outline'} onClick={() => onChange({ targetMode: 'on_demand_agent' })}>\n On demand\n </Button>\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Provider\">\n <select className=\"h-9 w-full rounded-md border border-input bg-background px-3 text-sm\" value={form.provider} onChange={(e) => selectProvider(e.target.value as SpawnProvider)} disabled={!providers.length}>\n {!providers.length && <option value={form.provider}>No providers available</option>}\n {providers.map((provider) => <option key={provider} value={provider}>{provider}</option>)}\n </select>\n </Field>\n {form.targetMode === 'existing_agent' ? (\n <Field label=\"If no match\">\n <select className=\"h-9 w-full rounded-md border border-input bg-background px-3 text-sm\" value={form.ifNoMatch} onChange={(e) => onChange({ ifNoMatch: e.target.value as 'fail' | 'spawn' })}>\n <option value=\"fail\">fail</option>\n <option value=\"spawn\">spawn</option>\n </select>\n </Field>\n ) : (\n <Field label=\"Approval\">\n <select className=\"h-9 w-full rounded-md border border-input bg-background px-3 text-sm\" value={form.approvalMode} onChange={(e) => onChange({ approvalMode: e.target.value as SpawnApprovalMode })}>\n <option value=\"guarded\">guarded</option>\n <option value=\"read-only\">read-only</option>\n <option value=\"open\">open</option>\n </select>\n </Field>\n )}\n </div>\n\n {form.targetMode === 'on_demand_agent' && (\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Model\">\n <select className=\"h-9 w-full rounded-md border border-input bg-background px-3 text-sm\" value={form.model} onChange={(e) => onChange({ model: e.target.value, effort: '' })}>\n {models.map((model) => <option key={model.alias} value={model.alias}>{model.label}</option>)}\n </select>\n </Field>\n <Field label=\"Effort\">\n <select className=\"h-9 w-full rounded-md border border-input bg-background px-3 text-sm\" value={form.effort} onChange={(e) => onChange({ effort: e.target.value })} disabled={!efforts.length}>\n <option value=\"\">Default</option>\n {efforts.map((level) => <option key={level} value={level}>{level}</option>)}\n </select>\n </Field>\n <Field label=\"Agent profile\">\n <select className=\"h-9 w-full rounded-md border border-input bg-background px-3 text-sm\" value={form.profile || 'default-relay'} onChange={(e) => onChange({ profile: e.target.value })}>\n {!agentProfiles.length && <option value=\"default-relay\">default-relay</option>}\n {agentProfiles.map((profile) => <option key={profile.name} value={profile.name}>{profile.name}</option>)}\n </select>\n </Field>\n <Field label=\"Workspace\">\n <select className=\"h-9 w-full rounded-md border border-input bg-background px-3 text-sm\" value={form.workspaceMode} onChange={(e) => onChange({ workspaceMode: e.target.value as WorkspaceMode })}>\n <option value=\"inherit\">inherit (default → isolated worktree)</option>\n <option value=\"shared\">shared (run in the repo, commit to its branch)</option>\n <option value=\"isolated\">isolated (fresh git worktree per run)</option>\n </select>\n </Field>\n </div>\n )}\n\n {form.targetMode === 'existing_agent' ? (\n <div className=\"grid gap-3\">\n <Field label=\"Label\">\n <Input value={form.label} onChange={(e) => onChange({ label: e.target.value })} />\n </Field>\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Tags\">\n <Input value={form.tags} onChange={(e) => onChange({ tags: e.target.value })} />\n </Field>\n <Field label=\"Capabilities\">\n <Input value={form.capabilities} onChange={(e) => onChange({ capabilities: e.target.value })} />\n </Field>\n </div>\n </div>\n ) : (\n <div className=\"grid gap-3\">\n <Field label=\"Cwd\">\n <Input value={form.cwd} onChange={(e) => onChange({ cwd: e.target.value })} />\n </Field>\n <label className=\"flex items-center justify-between rounded-md border border-border px-3 py-2\">\n <span className=\"text-sm\">Keep alive</span>\n <Switch checked={form.keepAlive} onCheckedChange={(keepAlive) => onChange({ keepAlive })} />\n </label>\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Budget minutes\">\n <Input type=\"number\" min={1} step={1} value={form.budgetMinutes} onChange={(e) => onChange({ budgetMinutes: e.target.value })} />\n </Field>\n <Field label=\"Warning minute\">\n <Input type=\"number\" min={0} step={1} value={form.warningMinutes} onChange={(e) => onChange({ warningMinutes: e.target.value })} />\n </Field>\n </div>\n <Field label=\"Warning message\">\n <Textarea value={form.warningMessage} onChange={(e) => onChange({ warningMessage: e.target.value })} className=\"min-h-20\" />\n </Field>\n </div>\n )}\n </div>\n\n <div className=\"space-y-3\">\n <Label>Task</Label>\n <Field label=\"Title\">\n <Input value={form.title} onChange={(e) => onChange({ title: e.target.value })} />\n </Field>\n <Field label=\"Body\">\n <Textarea value={form.body} onChange={(e) => onChange({ body: e.target.value })} className=\"min-h-28\" />\n </Field>\n <div className=\"grid grid-cols-3 gap-3\">\n <Field label=\"Severity\">\n <select className=\"h-9 w-full rounded-md border border-input bg-background px-3 text-sm\" value={form.severity} onChange={(e) => onChange({ severity: e.target.value as TaskSeverity })}>\n <option value=\"info\">info</option>\n <option value=\"warning\">warning</option>\n <option value=\"critical\">critical</option>\n </select>\n </Field>\n <Field label=\"Dedupe key\">\n <Input value={form.dedupeKey} onChange={(e) => onChange({ dedupeKey: e.target.value })} />\n </Field>\n <Field label=\"External URL\">\n <Input value={form.externalUrl} onChange={(e) => onChange({ externalUrl: e.target.value })} />\n </Field>\n </div>\n </div>\n\n <Button className=\"w-full gap-2\" onClick={onSave} disabled={saving || !form.name || !form.schedule || !form.orchestratorId || !form.title || !form.body}>\n {saving ? <RotateCw className=\"w-4 h-4 animate-spin\" /> : <Save className=\"w-4 h-4\" />}\n Save\n </Button>\n </div>\n )\n}\n\nfunction Field({ label, children }: { label: string; children: ReactNode }) {\n return (\n <div className=\"space-y-1.5\">\n <Label className=\"text-xs text-muted-foreground\">{label}</Label>\n {children}\n </div>\n )\n}\n\nfunction RunHistory({\n runs,\n automations,\n agents,\n orchestrators,\n now,\n logRunId,\n onToggleLogs,\n}: {\n runs: AutomationRun[]\n automations: Automation[]\n agents: Agent[]\n orchestrators: Orchestrator[]\n now: number\n logRunId: string | null\n onToggleLogs: (runId: string) => void\n}) {\n const names = new Map(automations.map((automation) => [automation.id, automation.name]))\n return (\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"text-sm font-semibold\">Runs</h3>\n <Badge variant=\"secondary\" className=\"text-[10px]\">{runs.length}</Badge>\n </div>\n <div className=\"rounded-lg border border-border overflow-x-auto\">\n <div className=\"grid grid-cols-[minmax(10rem,1fr)_8rem_8rem_7rem_7rem_5rem] gap-3 bg-muted/30 px-3 py-2 text-xs text-muted-foreground\">\n <span>Automation</span>\n <span>Status</span>\n <span>Agent</span>\n <span>Runtime</span>\n <span>Updated</span>\n <span>Logs</span>\n </div>\n {runs.length === 0 ? (\n <div className=\"px-3 py-10 text-center text-sm text-muted-foreground\">No runs</div>\n ) : runs.slice(0, 80).map((run) => {\n const logTarget = runLogTarget(run, agents, orchestrators)\n const remaining = remainingText(run, now)\n const shutdown = shutdownText(run)\n return (\n <div key={run.id} className=\"border-t border-border\">\n <div className=\"grid grid-cols-[minmax(10rem,1fr)_8rem_8rem_7rem_7rem_5rem] gap-3 px-3 py-2 text-xs items-center\">\n <div className=\"min-w-0\">\n <div className=\"truncate\">{names.get(run.automationId) || run.automationId.slice(0, 12)}</div>\n <div className=\"truncate text-muted-foreground\">\n {run.taskId ? `task #${run.taskId}` : 'no task'}\n {remaining ? ` · ${remaining} left` : ''}\n {shutdown ? ` · ${shutdown}` : ''}\n </div>\n </div>\n <Badge variant=\"outline\" className={`border text-[10px] w-fit ${runStatusClass(run)}`}>{runStatusLabel(run)}</Badge>\n <span className=\"truncate font-mono text-muted-foreground\">{run.targetAgentId || run.spawnedAgentId || '-'}</span>\n <span className=\"text-muted-foreground\">{elapsedText(run, now)}</span>\n <span className=\"text-muted-foreground\" title={fmtTime(run.updatedAt)}>{timeAgo(now, run.updatedAt)}</span>\n {logTarget ? (\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" className=\"h-7 w-7 p-0\" title=\"Open logs\" onClick={() => onToggleLogs(run.id)}>\n <FileText className=\"w-3.5 h-3.5\" />\n </Button>\n ) : <span className=\"text-muted-foreground\">-</span>}\n </div>\n {logRunId === run.id && logTarget && (\n <div className=\"border-t border-border px-3 py-3\">\n <LogViewer orchestratorId={logTarget.orchestratorId} session={logTarget.session} />\n </div>\n )}\n </div>\n )\n })}\n </div>\n </div>\n )\n}\n"],"mappings":"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"}