@tt-a1i/hive 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.en.md +21 -0
  3. package/README.md +8 -0
  4. package/dist/src/cli/team.js +17 -0
  5. package/dist/src/server/agent-run-starter.d.ts +6 -7
  6. package/dist/src/server/agent-run-starter.js +3 -3
  7. package/dist/src/server/agent-runtime-contract.d.ts +10 -0
  8. package/dist/src/server/agent-runtime-stop-run.d.ts +1 -1
  9. package/dist/src/server/agent-runtime-stop-run.js +4 -1
  10. package/dist/src/server/agent-runtime.d.ts +2 -1
  11. package/dist/src/server/agent-runtime.js +10 -5
  12. package/dist/src/server/agent-startup-instructions.d.ts +7 -8
  13. package/dist/src/server/agent-startup-instructions.js +5 -3
  14. package/dist/src/server/agent-stdin-dispatcher.d.ts +20 -7
  15. package/dist/src/server/agent-stdin-dispatcher.js +22 -10
  16. package/dist/src/server/feature-flags.d.ts +42 -0
  17. package/dist/src/server/feature-flags.js +24 -0
  18. package/dist/src/server/hive-team-guidance.d.ts +4 -3
  19. package/dist/src/server/hive-team-guidance.js +14 -13
  20. package/dist/src/server/recovery-summary.d.ts +5 -6
  21. package/dist/src/server/recovery-summary.js +3 -2
  22. package/dist/src/server/report-outbox-store.d.ts +36 -0
  23. package/dist/src/server/report-outbox-store.js +33 -0
  24. package/dist/src/server/restart-policy-support.d.ts +4 -5
  25. package/dist/src/server/restart-policy.d.ts +5 -1
  26. package/dist/src/server/restart-policy.js +51 -33
  27. package/dist/src/server/routes-settings.js +3 -3
  28. package/dist/src/server/routes-tasks.js +23 -0
  29. package/dist/src/server/routes-workspaces.js +5 -0
  30. package/dist/src/server/runtime-restart-policy.d.ts +3 -3
  31. package/dist/src/server/runtime-restart-policy.js +2 -3
  32. package/dist/src/server/runtime-store-contract.d.ts +3 -0
  33. package/dist/src/server/runtime-store-helpers.d.ts +2 -0
  34. package/dist/src/server/runtime-store-helpers.js +14 -9
  35. package/dist/src/server/runtime-store-workflows.js +8 -0
  36. package/dist/src/server/runtime-store.js +1 -0
  37. package/dist/src/server/sqlite-schema.js +13 -0
  38. package/dist/src/server/task-deps.d.ts +32 -0
  39. package/dist/src/server/task-deps.js +40 -0
  40. package/dist/src/server/tasks-file-watcher.d.ts +6 -7
  41. package/dist/src/server/tasks-file-watcher.js +3 -2
  42. package/dist/src/server/tasks-file.d.ts +2 -1
  43. package/dist/src/server/tasks-file.js +3 -2
  44. package/dist/src/server/team-authz.d.ts +1 -1
  45. package/dist/src/server/team-authz.js +1 -0
  46. package/dist/src/server/team-operations.d.ts +7 -1
  47. package/dist/src/server/team-operations.js +81 -19
  48. package/dist/src/server/webhook-notifier.d.ts +34 -0
  49. package/dist/src/server/webhook-notifier.js +47 -0
  50. package/dist/src/server/workflow-output-schema.d.ts +18 -0
  51. package/dist/src/server/workflow-output-schema.js +41 -0
  52. package/dist/src/server/workflow-runner.js +11 -1
  53. package/package.json +1 -1
  54. package/web/dist/assets/{AddWorkerDialog-CcC-7kgG.js → AddWorkerDialog-CGbaxu0T.js} +2 -2
  55. package/web/dist/assets/{AddWorkspaceDialog-BDpOTfmt.js → AddWorkspaceDialog-CNgExu6b.js} +1 -1
  56. package/web/dist/assets/{FirstRunWizard-BYX_ocQn.js → FirstRunWizard-DxGApUNc.js} +1 -1
  57. package/web/dist/assets/{MarketplaceDrawer-DUxSk7db.js → MarketplaceDrawer-Bk6cpukn.js} +1 -1
  58. package/web/dist/assets/{WhatsNewDialog-B_RlCXcV.js → WhatsNewDialog-CSGzk-2U.js} +1 -1
  59. package/web/dist/assets/{WorkerModal-D9-7YfZZ.js → WorkerModal-i2F3n3nZ.js} +1 -1
  60. package/web/dist/assets/{WorkspaceTaskDrawer-BCKoF7qc.js → WorkspaceTaskDrawer-C_Ta_K13.js} +1 -1
  61. package/web/dist/assets/WorkspaceTerminalPanels-VdDxtrQF.js +1 -0
  62. package/web/dist/assets/index-5zh61jMg.css +1 -0
  63. package/web/dist/assets/index-CAgGM6nb.js +75 -0
  64. package/web/dist/index.html +2 -2
  65. package/web/dist/sw.js +1 -1
  66. package/web/dist/assets/WorkspaceTerminalPanels-Dq8y91t2.js +0 -1
  67. package/web/dist/assets/index-BiOvKIVw.css +0 -1
  68. package/web/dist/assets/index-DMRUklT3.js +0 -73
@@ -0,0 +1,40 @@
1
+ const TASK_LINE = /^\s*[-*]\s+\[([ xX])\]\s+(.*)$/;
2
+ const NEEDS = /\[needs:\s*([#\d,\s]+)\]\s*$/i;
3
+ export const parseTasksWithDeps = (markdown) => {
4
+ const tasks = [];
5
+ let index = 0;
6
+ for (const line of markdown.split('\n')) {
7
+ const match = TASK_LINE.exec(line);
8
+ if (!match)
9
+ continue;
10
+ index += 1;
11
+ const done = (match[1] ?? ' ').toLowerCase() === 'x';
12
+ let text = (match[2] ?? '').trim();
13
+ const needs = [];
14
+ const needsMatch = NEEDS.exec(text);
15
+ if (needsMatch) {
16
+ for (const token of (needsMatch[1] ?? '').split(',')) {
17
+ const parsed = Number.parseInt(token.replace('#', '').trim(), 10);
18
+ if (Number.isInteger(parsed) && parsed > 0)
19
+ needs.push(parsed);
20
+ }
21
+ // Drop the trailing annotation from the human-readable text.
22
+ text = text.slice(0, needsMatch.index).trim();
23
+ }
24
+ tasks.push({ index, done, text, needs });
25
+ }
26
+ return tasks;
27
+ };
28
+ /**
29
+ * Tasks that can be worked right now: not yet done, and every `[needs:]`
30
+ * dependency is a task that exists AND is checked off. A dependency on a
31
+ * non-existent `#n`, or on a task that isn't done, withholds the task.
32
+ */
33
+ export const computeRunnableTasks = (markdown) => {
34
+ const tasks = parseTasksWithDeps(markdown);
35
+ const doneByIndex = new Map(tasks.map((task) => [task.index, task.done]));
36
+ return tasks
37
+ .filter((task) => !task.done)
38
+ .filter((task) => task.needs.every((dep) => doneByIndex.get(dep) === true))
39
+ .map((task) => ({ index: task.index, text: task.text }));
40
+ };
@@ -1,4 +1,5 @@
1
1
  import { type ChokidarOptions } from 'chokidar';
2
+ import { type FeatureFlags } from './feature-flags.js';
2
3
  import type { WorkflowCliPolicy } from './workflow-cli-policy.js';
3
4
  /**
4
5
  * Watcher configuration. The atomic-save option matters on Windows: VS
@@ -31,16 +32,14 @@ export interface TasksFileWatcher {
31
32
  start: (workspaceId: string, workspacePath: string) => Promise<void>;
32
33
  stop: (workspaceId: string) => Promise<void>;
33
34
  }
34
- export declare const createTasksFileWatcher: ({ onTasksUpdated, getWorkflowCliPolicy, getWorkflowsEnabled, getAutostaffEnabled, }: {
35
+ export declare const createTasksFileWatcher: ({ onTasksUpdated, getWorkflowCliPolicy, getFlags, }: {
35
36
  onTasksUpdated: (workspaceId: string, content: string) => void;
36
37
  /** Lets the freshly-written `.hive/PROTOCOL.md` state the workspace's
37
38
  * workflow CLI default + allowlist. Optional: omitted → the doc renders
38
39
  * the unrestricted default. */
39
40
  getWorkflowCliPolicy?: () => WorkflowCliPolicy;
40
- /** Whether the experimental workflow feature is on. Off → PROTOCOL.md omits
41
- * the workflow DSL + `team workflow` commands entirely. Defaults to off. */
42
- getWorkflowsEnabled?: () => boolean;
43
- /** Whether the experimental auto-staff feature is on (default on). Off
44
- * PROTOCOL.md omits the team-sizing rule. */
45
- getAutostaffEnabled?: () => boolean;
41
+ /** Resolves the live experimental flags. PROTOCOL.md omits the workflow DSL
42
+ * + `team workflow` commands when `workflowsEnabled` is off, and the
43
+ * team-sizing rule when `autostaffEnabled` is off. Omitted → all off. */
44
+ getFlags?: () => FeatureFlags;
46
45
  }) => TasksFileWatcher;
@@ -2,6 +2,7 @@ import { existsSync } from 'node:fs';
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import { basename, dirname, normalize } from 'node:path';
4
4
  import chokidar from 'chokidar';
5
+ import { FEATURE_FLAGS_ALL_OFF } from './feature-flags.js';
5
6
  import { ensureProtocolFile, ensureTasksFile, getTasksFilePath, TASKS_FILE_NAME, } from './tasks-file.js';
6
7
  const DEBOUNCE_MS = 100;
7
8
  const WATCHER_RETRY_MS = 5000;
@@ -66,7 +67,7 @@ const isTasksFileEvent = (tasksPath, changedPath) => {
66
67
  const text = Buffer.isBuffer(changedPath) ? changedPath.toString() : changedPath;
67
68
  return normalize(text) === normalize(tasksPath) || basename(text) === TASKS_FILE_NAME;
68
69
  };
69
- export const createTasksFileWatcher = ({ onTasksUpdated, getWorkflowCliPolicy, getWorkflowsEnabled, getAutostaffEnabled, }) => {
70
+ export const createTasksFileWatcher = ({ onTasksUpdated, getWorkflowCliPolicy, getFlags, }) => {
70
71
  const watchers = new Map();
71
72
  const timers = new Map();
72
73
  const retryTimers = new Map();
@@ -145,7 +146,7 @@ export const createTasksFileWatcher = ({ onTasksUpdated, getWorkflowCliPolicy, g
145
146
  closed = false;
146
147
  await stop(workspaceId);
147
148
  ensureTasksFile(workspacePath);
148
- ensureProtocolFile(workspacePath, getWorkflowCliPolicy?.(), getWorkflowsEnabled?.() ?? false, getAutostaffEnabled?.() ?? false);
149
+ ensureProtocolFile(workspacePath, getWorkflowCliPolicy?.(), getFlags?.() ?? FEATURE_FLAGS_ALL_OFF);
149
150
  const tasksPath = getTasksFilePath(workspacePath);
150
151
  const watcher = chokidar.watch(dirname(tasksPath), buildTasksWatcherOptions(workspacePath));
151
152
  const scheduleEmit = (changedPath) => {
@@ -1,3 +1,4 @@
1
+ import { type FeatureFlags } from './feature-flags.js';
1
2
  import type { WorkflowCliPolicy } from './workflow-cli-policy.js';
2
3
  interface TasksFileService {
3
4
  readTasks: (workspacePath: string) => string;
@@ -17,6 +18,6 @@ export declare const ensureTasksFile: (workspacePath: string) => string;
17
18
  * on every workspace open means a Hive version bump that changes the rules
18
19
  * propagates without manual intervention.
19
20
  */
20
- export declare const ensureProtocolFile: (workspacePath: string, cliPolicy?: WorkflowCliPolicy, workflowsEnabled?: boolean, autostaffEnabled?: boolean) => string;
21
+ export declare const ensureProtocolFile: (workspacePath: string, cliPolicy?: WorkflowCliPolicy, flags?: FeatureFlags) => string;
21
22
  export declare const createTasksFileService: () => TasksFileService;
22
23
  export type { TasksFileService };
@@ -1,5 +1,6 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import { dirname, join } from 'node:path';
3
+ import { FEATURE_FLAGS_ALL_OFF } from './feature-flags.js';
3
4
  import { buildProtocolDoc } from './hive-team-guidance.js';
4
5
  export const HIVE_DIR_NAME = '.hive';
5
6
  export const TASKS_FILE_NAME = 'tasks.md';
@@ -50,10 +51,10 @@ export const ensureTasksFile = (workspacePath) => {
50
51
  * on every workspace open means a Hive version bump that changes the rules
51
52
  * propagates without manual intervention.
52
53
  */
53
- export const ensureProtocolFile = (workspacePath, cliPolicy, workflowsEnabled = false, autostaffEnabled = false) => {
54
+ export const ensureProtocolFile = (workspacePath, cliPolicy, flags = FEATURE_FLAGS_ALL_OFF) => {
54
55
  ensureTasksDir(workspacePath);
55
56
  const protocolFilePath = getProtocolFilePath(workspacePath);
56
- const desired = buildProtocolDoc(cliPolicy, workflowsEnabled, autostaffEnabled);
57
+ const desired = buildProtocolDoc(cliPolicy, flags);
57
58
  const current = existsSync(protocolFilePath)
58
59
  ? runRetryableTasksFileOperation(() => readFileSync(protocolFilePath, 'utf8'))
59
60
  : null;
@@ -1,5 +1,5 @@
1
1
  import type { AgentSummary } from '../shared/types.js';
2
- export type TeamCommand = 'send' | 'list' | 'report' | 'status' | 'cancel' | 'help' | 'spawn' | 'dismiss' | 'workflow';
2
+ export type TeamCommand = 'send' | 'list' | 'next' | 'report' | 'status' | 'cancel' | 'help' | 'spawn' | 'dismiss' | 'workflow';
3
3
  export declare const commandAllowedForRole: (role: AgentSummary["role"], command: TeamCommand) => boolean;
4
4
  interface AuthenticateInput {
5
5
  fromAgentId: string | undefined;
@@ -2,6 +2,7 @@ import { ForbiddenError, UnauthorizedError } from './http-errors.js';
2
2
  const ORCHESTRATOR_COMMANDS = new Set([
3
3
  'send',
4
4
  'list',
5
+ 'next',
5
6
  'cancel',
6
7
  'help',
7
8
  'spawn',
@@ -2,6 +2,8 @@ import type { TeamListItem } from '../shared/types.js';
2
2
  import type { AgentRuntime } from './agent-runtime.js';
3
3
  import type { DispatchRecord } from './dispatch-ledger-store.js';
4
4
  import type { MessageLogHandle, MessageLogRecord } from './message-log-store.js';
5
+ import type { ReportOutboxStore } from './report-outbox-store.js';
6
+ import type { WebhookEvent } from './webhook-notifier.js';
5
7
  import type { WorkflowDispatchAwaiter } from './workflow-dispatch-awaiter.js';
6
8
  import type { WorkspaceStore } from './workspace-store.js';
7
9
  export declare const formatUnknownWorkerError: (workerName: string, roster: readonly TeamListItem[]) => string;
@@ -35,6 +37,9 @@ export interface TeamOperationsInput {
35
37
  workspaceId: string;
36
38
  }) => DispatchRecord | undefined;
37
39
  markDispatchSubmitted: (dispatchId: string) => void;
40
+ reportOutbox: ReportOutboxStore;
41
+ /** Fire an outbound completion webhook (best-effort) when a worker reports. */
42
+ notifyWebhook?: (event: WebhookEvent) => void;
38
43
  workflowDispatchAwaiter: WorkflowDispatchAwaiter;
39
44
  workspaceStore: WorkspaceStore;
40
45
  /** Auto-dismiss an ephemeral orchestrator-spawned worker after its
@@ -71,13 +76,14 @@ export interface ReportTaskResult {
71
76
  forwardError: string | null;
72
77
  forwarded: boolean;
73
78
  }
74
- export declare const createTeamOperations: ({ agentRuntime, createDispatch, deleteDispatch, deleteMessage, findOpenDispatch, findOpenDispatchById, insertMessage, markDispatchCancelled, markDispatchReportedByWorker, markDispatchSubmitted, workflowDispatchAwaiter, workspaceStore, dismissEphemeralWorker, }: TeamOperationsInput) => {
79
+ export declare const createTeamOperations: ({ agentRuntime, createDispatch, deleteDispatch, deleteMessage, findOpenDispatch, findOpenDispatchById, insertMessage, markDispatchCancelled, markDispatchReportedByWorker, markDispatchSubmitted, reportOutbox, notifyWebhook, workflowDispatchAwaiter, workspaceStore, dismissEphemeralWorker, }: TeamOperationsInput) => {
75
80
  cancelTask(workspaceId: string, dispatchId: string, input: CancelTaskInput): {
76
81
  dispatch: DispatchRecord;
77
82
  forwardError: string | null;
78
83
  forwarded: boolean;
79
84
  };
80
85
  dispatchTask: (workspaceId: string, workerId: string, text: string, input?: DispatchTaskInput) => Promise<DispatchRecord>;
86
+ drainReportOutbox: (workspaceId: string) => void;
81
87
  dispatchTaskByWorkerName(workspaceId: string, workerName: string, text: string, input?: DispatchTaskInput): Promise<DispatchRecord & {
82
88
  restartedWorker: boolean;
83
89
  }>;
@@ -1,4 +1,5 @@
1
- import { ConflictError, PtyInactiveError } from './http-errors.js';
1
+ import { buildOrchestratorReportPayload } from './agent-stdin-dispatcher.js';
2
+ import { ConflictError } from './http-errors.js';
2
3
  import { createReportMessage, createSendMessage, createStatusMessage, createUserInputMessage, } from './runtime-message-builders.js';
3
4
  import { getWorkflowAgentId } from './workspace-store-support.js';
4
5
  /* Roster snapshot embedded in the 409 the orchestrator sees when it
@@ -24,7 +25,27 @@ export const formatUnknownWorkerError = (workerName, roster) => {
24
25
  ].join('\n');
25
26
  };
26
27
  const reportForwardErrorMessage = (error) => error instanceof Error ? error.message : String(error);
27
- export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispatch, deleteMessage, findOpenDispatch, findOpenDispatchById, insertMessage, markDispatchCancelled, markDispatchReportedByWorker, markDispatchSubmitted, workflowDispatchAwaiter, workspaceStore, dismissEphemeralWorker, }) => {
28
+ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispatch, deleteMessage, findOpenDispatch, findOpenDispatchById, insertMessage, markDispatchCancelled, markDispatchReportedByWorker, markDispatchSubmitted, reportOutbox, notifyWebhook, workflowDispatchAwaiter, workspaceStore, dismissEphemeralWorker, }) => {
29
+ // Best-effort redelivery of reports a prior orchestrator outage stranded.
30
+ // Called when a fresh report confirms the orchestrator is reachable and
31
+ // when the orchestrator polls `team list` (its natural post-restart wakeup).
32
+ // An entry is marked delivered only after its PTY write actually resolves,
33
+ // so a still-down orchestrator just leaves the backlog pending.
34
+ const drainReportOutbox = (workspaceId) => {
35
+ const orchestratorId = `${workspaceId}:orchestrator`;
36
+ if (!agentRuntime.getActiveRunByAgentId(workspaceId, orchestratorId))
37
+ return;
38
+ for (const entry of reportOutbox.listPending(workspaceId, orchestratorId)) {
39
+ void agentRuntime
40
+ .deliverSystemMessageToAgent(workspaceId, orchestratorId, entry.payload, {
41
+ requireActiveRun: true,
42
+ })
43
+ .then(() => reportOutbox.markDelivered(entry.id))
44
+ .catch((error) => {
45
+ console.error('[hive] swallowed:teamReport.outboxDrain', error);
46
+ });
47
+ }
48
+ };
28
49
  const ensureWorkerRun = async (workspaceId, workerId, hivePort) => {
29
50
  if (agentRuntime.getActiveRunByAgentId(workspaceId, workerId)) {
30
51
  return;
@@ -171,6 +192,7 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
171
192
  return { dispatch, forwardError, forwarded };
172
193
  },
173
194
  dispatchTask,
195
+ drainReportOutbox,
174
196
  async dispatchTaskByWorkerName(workspaceId, workerName, text, input = {}) {
175
197
  /* Build the roster once so a missing-name path can surface it without
176
198
  a second store call. We deliberately don't go through
@@ -235,15 +257,6 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
235
257
  if (!openDispatch) {
236
258
  throw new ConflictError(`No open dispatch for worker: ${worker.name}`);
237
259
  }
238
- const isWorkflowDispatch = openDispatch.fromAgentId === getWorkflowAgentId(workspaceId);
239
- // Pre-check the orchestrator PTY only when the report is heading there.
240
- // Workflow-sourced dispatches don't need an orchestrator: the runner
241
- // is in-process and will resolve its awaiter directly.
242
- if (input.requireActiveRun === true && !isWorkflowDispatch) {
243
- if (!agentRuntime.getActiveRunByAgentId(workspaceId, `${workspaceId}:orchestrator`)) {
244
- throw new PtyInactiveError(`No active run for agent: ${workspaceId}:orchestrator`);
245
- }
246
- }
247
260
  const messageHandle = insertMessage(createReportMessage(workspaceId, workerId, text, status, artifacts));
248
261
  try {
249
262
  const dispatch = markDispatchReportedByWorker({
@@ -277,16 +290,65 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
277
290
  }
278
291
  return { dispatch, forwardError, forwarded };
279
292
  }
293
+ // Real worker reported (not a workflow-internal step) — fire the
294
+ // outbound completion webhook. Best-effort; never blocks the report.
295
+ notifyWebhook?.({
296
+ type: 'report_received',
297
+ workspaceId,
298
+ agentId: workerId,
299
+ agentName: worker.name,
300
+ summary: text.slice(0, 280),
301
+ at: Date.now(),
302
+ });
280
303
  if (input.requireActiveRun === true) {
281
- try {
282
- agentRuntime.writeReportPrompt(workspaceId, worker.name, workerId, text, artifacts, {
283
- requireActiveRun: input.requireActiveRun,
284
- });
285
- forwarded = true;
304
+ const orchestratorId = `${workspaceId}:orchestrator`;
305
+ // A fresh report proves the orchestrator is reachable — flush any
306
+ // backlog a prior outage stranded first, in arrival order, before
307
+ // this one (both ride the dispatcher's per-agent serial queue).
308
+ drainReportOutbox(workspaceId);
309
+ const payload = buildOrchestratorReportPayload(worker.name, text, artifacts);
310
+ if (agentRuntime.getActiveRunByAgentId(workspaceId, orchestratorId)) {
311
+ try {
312
+ const delivery = agentRuntime.deliverReportToOrchestrator(workspaceId, worker.name, text, artifacts, { requireActiveRun: true });
313
+ forwarded = true;
314
+ // The dispatch is already marked reported. If the PTY dies
315
+ // mid-write the ledger would say "reported" while the
316
+ // orchestrator never received it — persist for redelivery so it
317
+ // isn't silently lost.
318
+ void delivery.catch((error) => {
319
+ console.error('[hive] swallowed:teamReport.forward', error);
320
+ reportOutbox.enqueue({
321
+ workspaceId,
322
+ targetAgentId: orchestratorId,
323
+ dispatchId: dispatch.id,
324
+ payload,
325
+ });
326
+ });
327
+ }
328
+ catch (error) {
329
+ // TOCTOU: orchestrator vanished between the active-run check and
330
+ // the write. Same fix — queue it.
331
+ console.error('[hive] swallowed:teamReport.forward', error);
332
+ forwardError = reportForwardErrorMessage(error);
333
+ reportOutbox.enqueue({
334
+ workspaceId,
335
+ targetAgentId: orchestratorId,
336
+ dispatchId: dispatch.id,
337
+ payload,
338
+ });
339
+ }
286
340
  }
287
- catch (error) {
288
- forwardError = reportForwardErrorMessage(error);
289
- console.error('[hive] swallowed:teamReport.forward', error);
341
+ else {
342
+ // Orchestrator is down. Queue the report instead of dropping it;
343
+ // the CLI surfaces forwarded:false and the backlog drains on the
344
+ // orchestrator's next `team list` after it restarts.
345
+ forwardError = 'Orchestrator is not running; report queued for delivery.';
346
+ reportOutbox.enqueue({
347
+ workspaceId,
348
+ targetAgentId: orchestratorId,
349
+ dispatchId: dispatch.id,
350
+ payload,
351
+ });
290
352
  }
291
353
  }
292
354
  // M11: if this worker was spawned with `team spawn --ephemeral`, this
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Outbound completion/attention webhook. The user supplies a single URL (stored
3
+ * in app_state); the runtime POSTs a small JSON payload on server-side lifecycle
4
+ * events so they can wire their own Slack / Discord / Feishu / ntfy / Telegram
5
+ * without Hive taking on a relay, mobile app, or account system.
6
+ *
7
+ * Best-effort by design: fire-and-forget with a timeout, all errors swallowed —
8
+ * a flaky webhook must never block or fail a report/exit. This is a personal,
9
+ * local-trust setting: the URL is whatever the user typed, and a 127.0.0.1-bound
10
+ * runtime can reach localhost/intranet, so we only enforce an http(s) scheme and
11
+ * leave the rest to the operator (documented in the Settings UI).
12
+ */
13
+ export declare const WEBHOOK_URL_KEY = "notifications.webhook-url";
14
+ export type WebhookEventType = 'report_received' | 'agent_stopped' | 'workflow_finished';
15
+ export interface WebhookEvent {
16
+ type: WebhookEventType;
17
+ workspaceId: string;
18
+ agentId?: string;
19
+ agentName?: string;
20
+ summary?: string;
21
+ at: number;
22
+ }
23
+ export declare const readWebhookUrl: (raw: string | null | undefined) => string | null;
24
+ interface WebhookNotifierOptions {
25
+ getUrl: () => string | null;
26
+ /** Injectable for tests; defaults to global fetch. */
27
+ fetchImpl?: typeof fetch;
28
+ timeoutMs?: number;
29
+ }
30
+ export declare const createWebhookNotifier: ({ getUrl, fetchImpl, timeoutMs, }: WebhookNotifierOptions) => {
31
+ notify: (event: WebhookEvent) => void;
32
+ };
33
+ export type WebhookNotifier = ReturnType<typeof createWebhookNotifier>;
34
+ export {};
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Outbound completion/attention webhook. The user supplies a single URL (stored
3
+ * in app_state); the runtime POSTs a small JSON payload on server-side lifecycle
4
+ * events so they can wire their own Slack / Discord / Feishu / ntfy / Telegram
5
+ * without Hive taking on a relay, mobile app, or account system.
6
+ *
7
+ * Best-effort by design: fire-and-forget with a timeout, all errors swallowed —
8
+ * a flaky webhook must never block or fail a report/exit. This is a personal,
9
+ * local-trust setting: the URL is whatever the user typed, and a 127.0.0.1-bound
10
+ * runtime can reach localhost/intranet, so we only enforce an http(s) scheme and
11
+ * leave the rest to the operator (documented in the Settings UI).
12
+ */
13
+ export const WEBHOOK_URL_KEY = 'notifications.webhook-url';
14
+ export const readWebhookUrl = (raw) => {
15
+ const trimmed = raw?.trim();
16
+ return trimmed ? trimmed : null;
17
+ };
18
+ const isHttpUrl = (raw) => {
19
+ try {
20
+ const url = new URL(raw);
21
+ return url.protocol === 'http:' || url.protocol === 'https:';
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ };
27
+ export const createWebhookNotifier = ({ getUrl, fetchImpl = fetch, timeoutMs = 5000, }) => {
28
+ const notify = (event) => {
29
+ const url = readWebhookUrl(getUrl());
30
+ if (!url || !isHttpUrl(url))
31
+ return;
32
+ const controller = new AbortController();
33
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
34
+ void fetchImpl(url, {
35
+ method: 'POST',
36
+ headers: { 'content-type': 'application/json' },
37
+ body: JSON.stringify(event),
38
+ signal: controller.signal,
39
+ })
40
+ .catch(() => {
41
+ // Personal-trust, best-effort: a dead or slow webhook must not affect
42
+ // the report/exit path that triggered it.
43
+ })
44
+ .finally(() => clearTimeout(timer));
45
+ };
46
+ return { notify };
47
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Opt-in structured output for the workflow DSL `agent()` call. When a script
3
+ * passes `outputSchema`, the runner appends an instruction asking the worker to
4
+ * end its report with a fenced ```json block, then parses that block so the
5
+ * script gets an object instead of brittle free-text. Kept deliberately LIGHT:
6
+ * no schema validation library, no retry — the keys are advisory, and a parse
7
+ * miss falls back to `{ text }` in the runner so a script can always branch on
8
+ * a missing field (the documented safe-default discipline).
9
+ */
10
+ /**
11
+ * Extract and parse the LAST fenced json block in a report. Workers often emit
12
+ * a reasoning block then a final answer block, so the last one wins. Returns
13
+ * null on no block / invalid JSON / a non-object payload (so the caller's
14
+ * `?? { text }` fallback fires) rather than throwing.
15
+ */
16
+ export declare const extractJsonBlock: (text: string) => Record<string, unknown> | null;
17
+ /** The terse instruction appended to the worker prompt when a schema is set. */
18
+ export declare const buildSchemaInstruction: (schema: Record<string, unknown>) => string;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Opt-in structured output for the workflow DSL `agent()` call. When a script
3
+ * passes `outputSchema`, the runner appends an instruction asking the worker to
4
+ * end its report with a fenced ```json block, then parses that block so the
5
+ * script gets an object instead of brittle free-text. Kept deliberately LIGHT:
6
+ * no schema validation library, no retry — the keys are advisory, and a parse
7
+ * miss falls back to `{ text }` in the runner so a script can always branch on
8
+ * a missing field (the documented safe-default discipline).
9
+ */
10
+ /**
11
+ * Extract and parse the LAST fenced json block in a report. Workers often emit
12
+ * a reasoning block then a final answer block, so the last one wins. Returns
13
+ * null on no block / invalid JSON / a non-object payload (so the caller's
14
+ * `?? { text }` fallback fires) rather than throwing.
15
+ */
16
+ export const extractJsonBlock = (text) => {
17
+ const matches = [...text.matchAll(/```(?:json)?[ \t]*\r?\n?([\s\S]*?)```/g)];
18
+ const last = matches.at(-1)?.[1];
19
+ if (last === undefined)
20
+ return null;
21
+ try {
22
+ const parsed = JSON.parse(last.trim());
23
+ return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)
24
+ ? parsed
25
+ : null;
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ };
31
+ /** The terse instruction appended to the worker prompt when a schema is set. */
32
+ export const buildSchemaInstruction = (schema) => {
33
+ const keys = Object.keys(schema);
34
+ const keyList = keys.length > 0 ? keys.join(', ') : '(see the task)';
35
+ return [
36
+ '',
37
+ 'When you are done, end your report with a single fenced ```json block as the LAST thing in your message.',
38
+ `That JSON object should use these keys: ${keyList}.`,
39
+ 'If you cannot determine a field, omit it — the caller treats a missing field as the safe default.',
40
+ ].join('\n');
41
+ };
@@ -3,6 +3,7 @@ import { cpus } from 'node:os';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { assertWindowsSafeFilename } from './windows-filename.js';
5
5
  import { resolveWorkflowCli } from './workflow-cli-policy.js';
6
+ import { buildSchemaInstruction, extractJsonBlock } from './workflow-output-schema.js';
6
7
  import { loadWorkflowScriptFile, loadWorkflowScriptSource } from './workflow-script-loader.js';
7
8
  import { getWorkflowAgentId } from './workspace-store-support.js';
8
9
  // TIER 2 #2 + #11 — runtime caps. Match Claude Code's documented caps
@@ -150,7 +151,10 @@ export const createWorkflowRunner = (deps) => {
150
151
  spawnedWorkers.push(worker.id);
151
152
  try {
152
153
  await store.startAgent(workspaceId, worker.id, { hivePort });
153
- const dispatch = await store.dispatchTaskByWorkerName(workspaceId, name, prompt, {
154
+ const dispatchPrompt = opts.outputSchema
155
+ ? prompt + buildSchemaInstruction(opts.outputSchema)
156
+ : prompt;
157
+ const dispatch = await store.dispatchTaskByWorkerName(workspaceId, name, dispatchPrompt, {
154
158
  fromAgentId: workflowAgentId,
155
159
  hivePort,
156
160
  workflowRunId: run.id,
@@ -159,6 +163,12 @@ export const createWorkflowRunner = (deps) => {
159
163
  label: opts.label ?? name,
160
164
  });
161
165
  const report = await awaiter.awaitReport(dispatch.id, opts.timeoutMs);
166
+ // Structured output: hand back the parsed object, or { text } on a
167
+ // parse miss so the script can still branch (treating an absent field
168
+ // as the safe default).
169
+ if (opts.outputSchema) {
170
+ return extractJsonBlock(report.text) ?? { text: report.text };
171
+ }
162
172
  return report.text;
163
173
  }
164
174
  finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tt-a1i/hive",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Browser-native hive-mind for CLI coding agents — Claude Code, Codex, Gemini, and OpenCode collaborate as real PTY processes via a team protocol.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.30.3",
@@ -1,2 +1,2 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/MarketplaceDrawer-DUxSk7db.js","assets/index-DMRUklT3.js","assets/index-BiOvKIVw.css"])))=>i.map(i=>d[i]);
2
- import{c as W,j as e,u as j,r as c,e as z,k as M,n as X,A as Y,B as Z,F as R,G as ee,H as te,a as ae,P as re,O as se,C as ne,b as le,D as oe,d as ie,_ as de}from"./index-DMRUklT3.js";const ce=[["path",{d:"M12 7v6",key:"lw1j43"}],["path",{d:"M15 10H9",key:"o6yqo3"}],["path",{d:"M17 3a2 2 0 0 1 2 2v15a1 1 0 0 1-1.496.868l-4.512-2.578a2 2 0 0 0-1.984 0l-4.512 2.578A1 1 0 0 1 5 20V5a2 2 0 0 1 2-2z",key:"oz39mx"}]],ue=W("bookmark-plus",ce);const xe=[["rect",{width:"12",height:"12",x:"2",y:"10",rx:"2",ry:"2",key:"6agr2n"}],["path",{d:"m17.92 14 3.5-3.5a2.24 2.24 0 0 0 0-3l-5-4.92a2.24 2.24 0 0 0-3 0L10 6",key:"1o487t"}],["path",{d:"M6 18h.01",key:"uhywen"}],["path",{d:"M10 14h.01",key:"ssrbsk"}],["path",{d:"M15 6h.01",key:"cblpky"}],["path",{d:"M18 9h.01",key:"2061c0"}]],me=W("dices",xe);const pe=[["path",{d:"m21 21-4.34-4.34",key:"14j7rj"}],["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}]],he=W("search",pe);const fe=[["path",{d:"m7 11 2-2-2-2",key:"1lz0vl"}],["path",{d:"M11 13h4",key:"1p7l4v"}],["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2",key:"1m3agn"}]],be=W("square-terminal",fe);const ge=[["path",{d:"M15 21v-5a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v5",key:"slp6dd"}],["path",{d:"M17.774 10.31a1.12 1.12 0 0 0-1.549 0 2.5 2.5 0 0 1-3.451 0 1.12 1.12 0 0 0-1.548 0 2.5 2.5 0 0 1-3.452 0 1.12 1.12 0 0 0-1.549 0 2.5 2.5 0 0 1-3.77-3.248l2.889-4.184A2 2 0 0 1 7 2h10a2 2 0 0 1 1.653.873l2.895 4.192a2.5 2.5 0 0 1-3.774 3.244",key:"o0xfot"}],["path",{d:"M4 10.95V19a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8.05",key:"wn3emo"}]],ke=W("store",ge),ve=[{value:"coder"},{value:"reviewer"},{value:"tester"},{value:"custom",dashed:!0}],F=t=>`role.${t}`,C=({children:t})=>e.jsx("span",{className:"text-sm font-medium text-sec",children:t}),je=({active:t,spec:a,onSelect:l})=>{const{t:n}=j();return e.jsxs("button",{type:"button",onClick:l,"aria-pressed":t,"data-testid":`role-card-${a.value}`,className:`selectable-card${a.dashed?" selectable-card--dashed":""} flex items-center gap-3 px-3 py-2`,children:[e.jsx(R,{role:a.value,size:20}),e.jsx("span",{className:"flex-1 text-left text-base font-medium text-pri",children:n(F(a.value))}),t?e.jsx(M,{size:14,className:"shrink-0 text-accent","aria-hidden":!0}):null]})},ye=({onRoleChange:t,workerRole:a})=>{const{t:l}=j();return e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx(C,{children:l("addWorker.role")}),e.jsx("div",{className:"grid grid-cols-2 gap-2",children:ve.map(n=>e.jsx(je,{active:a===n.value,spec:n,onSelect:()=>t(n.value)},n.value))})]})},Ne=({customTemplates:t,disabledReason:a,onDeleteTemplate:l,onSelect:n,selectedTemplateId:i})=>{const{t:o}=j(),[f,x]=c.useState(!1),[m,u]=c.useState(""),[b,k]=c.useState(null),y=c.useRef(null),g=c.useMemo(()=>t.find(r=>r.id===i)??null,[t,i]),v=c.useMemo(()=>{const r=m.trim().toLowerCase();return r?t.filter(s=>s.name.toLowerCase().includes(r)||s.description.toLowerCase().includes(r)):t},[t,m]);return c.useEffect(()=>{if(!f)return;const r=h=>{h.key==="Escape"&&x(!1)},s=h=>{const w=y.current;w&&!w.contains(h.target)&&x(!1)};return document.addEventListener("keydown",r),document.addEventListener("pointerdown",s),()=>{document.removeEventListener("keydown",r),document.removeEventListener("pointerdown",s)}},[f]),e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx(C,{children:o("addWorker.template")}),e.jsxs("div",{ref:y,className:"relative",children:[e.jsxs("button",{type:"button","aria-haspopup":"listbox","aria-expanded":f,"data-testid":"role-template-picker-trigger",onClick:()=>x(r=>!r),className:"flex w-full items-center justify-between gap-2 rounded border px-3 py-2 text-left text-sm transition-colors hover:bg-3",style:{borderColor:"var(--border)",background:"var(--bg-1)"},children:[e.jsx("span",{className:"min-w-0 flex-1 truncate text-pri",children:g?g.name:o("addWorker.templatePickPlaceholder")}),e.jsx(z,{size:14,className:"shrink-0 text-ter","aria-hidden":!0})]}),f?e.jsxs("div",{role:"listbox","aria-label":o("addWorker.template"),"data-testid":"role-template-picker-menu",className:"elev-2 absolute left-0 right-0 top-full z-30 mt-1 flex max-h-72 flex-col overflow-hidden rounded border",style:{background:"var(--bg-elevated)",borderColor:"var(--border-bright)"},children:[e.jsxs("div",{className:"flex items-center gap-2 border-b px-2 py-1.5",style:{borderColor:"var(--border)"},children:[e.jsx(he,{size:14,className:"text-ter","aria-hidden":!0}),e.jsx("input",{value:m,onChange:r=>u(r.currentTarget.value),placeholder:o("addWorker.templateSearchPlaceholder"),"data-testid":"role-template-search-input",className:"w-full bg-transparent text-sm text-pri outline-none placeholder:text-ter",spellCheck:!1})]}),e.jsx("div",{className:"flex-1 overflow-y-auto py-1",children:t.length===0?e.jsx("div",{"data-testid":"role-template-empty-state",className:"px-3 py-3 text-center text-sm text-ter",children:o("addWorker.templateEmpty")}):v.length===0?e.jsx("div",{className:"px-3 py-3 text-center text-sm text-ter",children:o("addWorker.templateNoMatch")}):v.map(r=>{const s=r.id===i;return e.jsxs("div",{className:"relative",children:[e.jsxs("button",{type:"button",role:"option","aria-selected":s,"data-testid":`role-template-option-${r.id}`,onClick:()=>{n(r.id),x(!1),u("")},className:"flex w-full items-center gap-2 px-3 py-1.5 pr-9 text-left text-sm text-pri hover:bg-3",style:s?{background:"var(--bg-3)"}:void 0,children:[e.jsx("span",{className:"min-w-0 flex-1 truncate",children:r.name}),s?e.jsx(M,{size:14,className:"shrink-0 text-accent","aria-hidden":!0}):null]}),e.jsx("button",{type:"button","aria-label":o("addWorker.templateDeleteAria",{name:r.name}),"data-testid":`role-template-delete-${r.id}`,disabled:!!a,title:a??void 0,onClick:h=>{h.preventDefault(),h.stopPropagation(),!a&&k(r)},className:"absolute right-1 top-1/2 flex h-6 w-6 -translate-y-1/2 items-center justify-center rounded text-ter transition-colors hover:bg-3 hover:text-pri",children:e.jsx(X,{size:14,"aria-hidden":!0})})]},r.id)})}),i!==null?e.jsx("button",{type:"button","data-testid":"role-template-clear",onClick:()=>{n(null),x(!1),u("")},className:"border-t px-3 py-1.5 text-left text-sm text-ter transition-colors hover:bg-3 hover:text-pri",style:{borderColor:"var(--border)"},children:o("addWorker.templateClear")}):null]}):null]}),e.jsx(Y,{open:b!==null,onOpenChange:r=>{r||k(null)},title:o("addWorker.templateDeleteTitle"),description:b?o("addWorker.templateDeleteConfirm",{name:b.name}):"",confirmLabel:o("addWorker.templateDeleteConfirmLabel"),confirmKind:"danger",onConfirm:()=>{if(!b||a)return;const r=b.id;k(null),l(r)}})]})},Ce=({canSaveAsTemplate:t,modified:a,onChange:l,onReset:n,onSaveAsTemplate:i,roleDescription:o,templateBusy:f,workerRole:x,writeDisabledReason:m})=>{const{t:u}=j(),[b,k]=c.useState(!1),[y,g]=c.useState(!1),[v,r]=c.useState("");return c.useEffect(()=>{(x==="custom"||a)&&k(!0)},[a,x]),c.useEffect(()=>{t||(g(!1),r(""))},[t]),e.jsxs("details",{open:b,onToggle:s=>k(s.currentTarget.open),className:"group flex flex-col gap-2",children:[e.jsxs("summary",{className:"flex cursor-pointer select-none items-center justify-between gap-2 list-none",children:[e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsx(z,{size:12,"aria-hidden":!0,className:"-rotate-90 text-ter transition-transform duration-150 group-open:rotate-0"}),e.jsx(C,{children:u("addWorker.roleInstructions")}),a?e.jsxs("span",{className:"text-sm text-ter",children:["· ",u("addWorker.modifiedFrom",{role:u(F(x))})]}):null]}),e.jsxs("div",{className:"flex items-center gap-1",children:[t&&!y?e.jsxs("button",{type:"button","data-testid":"role-template-save",disabled:!!m,title:m??void 0,onClick:s=>{s.preventDefault(),s.stopPropagation(),g(!0)},className:"flex items-center gap-1 rounded px-2 py-0.5 text-xs font-medium transition-colors hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50",style:{color:"var(--accent)",background:"color-mix(in oklab, var(--accent) 14%, transparent)"},children:[e.jsx(ue,{size:12,"aria-hidden":!0}),u("addWorker.saveAsTemplate")]}):null,a?e.jsxs("button",{type:"button",className:"flex items-center gap-1 rounded px-1.5 py-0.5 text-xs text-ter transition-colors hover:bg-3 hover:text-sec",onClick:s=>{s.preventDefault(),s.stopPropagation(),n()},children:[e.jsx(Z,{size:12,"aria-hidden":!0}),u("addWorker.reset")]}):null]})]}),e.jsx("textarea",{"aria-label":"Role instructions",id:"add-worker-role-instructions",value:o,rows:5,onChange:s=>l(s.currentTarget.value),placeholder:x==="custom"?u("addWorker.customPlaceholder"):void 0,title:u("addWorker.roleInstructionsTitle"),className:"input mono resize-y text-sm",style:{minHeight:150},"data-testid":"role-instructions-textarea"}),t&&y?e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("input",{autoFocus:!0,value:v,onChange:s=>r(s.currentTarget.value),placeholder:u("addWorker.templateNamePlaceholder"),"data-testid":"role-template-save-name",className:"input flex-1 text-sm"}),e.jsx("button",{type:"button",disabled:f||!v.trim()||!!m,title:m??void 0,"data-testid":"role-template-save-confirm",onClick:async()=>{if(m)return;const s=v.trim();if(s)try{await i(s),g(!1),r("")}catch{}},className:"icon-btn icon-btn--primary text-xs",children:u("addWorker.templateSaveConfirm")}),e.jsx("button",{type:"button","data-testid":"role-template-save-cancel",onClick:()=>{g(!1),r("")},className:"icon-btn text-xs",children:u("common.cancel")})]}):null]})},D=({active:t,command:a,displayName:l,logoPresetId:n,notFound:i=!1,testId:o,onSelect:f})=>{const{t:x}=j(),m=e.jsx("span",{className:"inline-flex h-5 w-5 shrink-0 items-center justify-center rounded border border-border bg-surface-1 text-ter","data-testid":`${o}-generic-icon`,"aria-hidden":!0,children:e.jsx(be,{size:13})});return e.jsxs("button",{type:"button",onClick:f,"aria-pressed":t,"data-testid":o,className:"selectable-card flex items-center justify-between gap-3 px-3 py-2",children:[e.jsxs("span",{className:"flex min-w-0 items-center gap-3",children:[e.jsx(ee,{commandPresetId:n,fallback:m,size:22}),e.jsxs("span",{className:"flex min-w-0 flex-col items-start gap-0.5",children:[e.jsx("span",{className:"truncate text-base font-medium text-pri",children:l}),e.jsxs("span",{className:"mono truncate text-xs text-ter",children:[a,i?` · ${x("addWorker.agentNotFound")}`:""]})]})]}),t?e.jsx(M,{size:14,className:"shrink-0 text-accent","aria-hidden":!0}):null]})},We=({active:t,preset:a,onSelect:l})=>e.jsx(D,{active:t,command:a.command,displayName:a.displayName,logoPresetId:a.id,notFound:a.available===!1,testId:`agent-radio-${a.id}`,onSelect:l}),we=({commandPresetId:t,commandPresets:a,onPresetChange:l})=>e.jsx(Se,{commandPresetId:t,commandPresets:a,onPresetChange:l}),Se=({commandPresetId:t,commandPresets:a,onPresetChange:l})=>{const{t:n}=j();return e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx(C,{children:n("addWorker.agentCli")}),a.length===0?e.jsx("div",{className:"text-sm text-ter",children:n("addWorker.loadingPresets")}):e.jsxs("div",{className:"grid grid-cols-2 gap-2",children:[a.map(i=>e.jsx(We,{active:t===i.id,preset:i,onSelect:()=>l(i.id)},i.id)),e.jsx(D,{active:t==="",command:n("addWorker.genericCommand"),displayName:n("addWorker.genericAgent"),testId:"agent-radio-generic",onSelect:()=>l("")})]})]})},ze=({onChange:t,value:a})=>{const{t:l}=j(),n=a.trim();return e.jsxs("details",{className:"group flex flex-col gap-2",children:[e.jsx("summary",{className:"flex cursor-pointer select-none items-center justify-between gap-2 list-none",children:e.jsxs("span",{className:"flex min-w-0 items-center gap-1.5",children:[e.jsx(z,{size:12,"aria-hidden":!0,className:"-rotate-90 shrink-0 text-ter transition-transform duration-150 group-open:rotate-0"}),e.jsx(C,{children:l("addWorker.startupCommand")}),n?e.jsxs("span",{className:"truncate text-sm text-ter",children:["· ",l("addWorker.startupOverrides")]}):null]})}),e.jsxs("div",{className:"flex flex-col gap-2 rounded border bg-2 p-3",style:{borderColor:"var(--border)"},children:[e.jsx("input",{"aria-label":"Startup command",value:a,onChange:i=>t(i.currentTarget.value),placeholder:"qwen --model qwen3-coder",className:"input mono text-sm",spellCheck:!1}),e.jsx("p",{className:"text-sm leading-5 text-ter",children:l("addWorker.startupHelp",{example:"claude --resume <session-id>"})})]})]})},Me=c.lazy(()=>de(()=>import("./MarketplaceDrawer-DUxSk7db.js"),__vite__mapDeps([0,1,2])).then(t=>({default:t.MarketplaceDrawer}))),_e=({commandPresets:t,commandPresetId:a,creating:l=!1,customTemplates:n,onApplyMarketplaceImport:i,onClose:o,onDeleteTemplate:f,onNameChange:x,onPresetChange:m,onRandomName:u,onRoleDescriptionChange:b,onRoleDescriptionReset:k,onRoleChange:y,onSaveAsTemplate:g,onStartupCommandChange:v,onSubmit:r,onTemplateChange:s,roleDescription:h,roleDescriptionDefault:w,selectedTemplateId:_,startupCommand:P,templateBusy:I,workerName:A,workerRole:S,writeDisabledReason:N})=>{const{t:d}=j(),L=te(),[B,O]=c.useState(!1),[q,H]=c.useState(!1),K=c.useMemo(()=>new Set(n.map(p=>p.name)),[n]),V=p=>{i(p),L.show({kind:"success",message:d("marketplace.imported",{name:p.name})})},G=p=>{p||o()},Q=h!==w,E=t.find(p=>p.id===a),$=P.trim(),J=()=>N||(A.trim()?!a&&!$?d("addWorker.pickCliOrStartup"):E?.available===!1&&!$?d("addWorker.unavailable",{name:E.displayName}):h.trim()?null:d("addWorker.emptyInstructions"):d("addWorker.enterName")),U=p=>{const T=J();if(T){p.preventDefault(),L.show({kind:"warning",message:T});return}r(p)};return e.jsxs(ae,{open:!0,onOpenChange:G,children:[e.jsxs(re,{children:[e.jsx(se,{"data-testid":"add-worker-overlay",className:"app-overlay fixed inset-0 z-40"}),e.jsx("div",{className:"pointer-events-none fixed inset-0 z-50 grid place-items-center p-4",children:e.jsx(ne,{"data-testid":"add-worker-content",className:"dialog-scale-pop elev-2 pointer-events-auto flex max-h-[calc(100vh-32px)] w-[560px] max-w-full flex-col rounded-lg border",style:{background:"var(--bg-elevated)",borderColor:"var(--border-bright)"},children:e.jsxs("form",{onSubmit:U,"aria-label":d("addWorker.title"),className:"flex flex-col",children:[e.jsxs("div",{className:"flex shrink-0 flex-col gap-0.5 border-b px-5 py-4",style:{borderColor:"var(--border)"},children:[e.jsx(le,{className:"text-lg font-semibold text-pri",children:d("addWorker.title")}),e.jsx(oe,{className:"text-sm text-ter",children:d("addWorker.description",{command:"team send"})})]}),e.jsxs("div",{className:"flex flex-col gap-4 overflow-y-auto px-5 py-4",children:[e.jsxs("label",{className:"flex flex-col gap-2",children:[e.jsxs("div",{className:"flex items-baseline justify-between gap-2",children:[e.jsx(C,{children:d("addWorker.name")}),e.jsx(ie,{label:d("addWorker.randomTooltip"),children:e.jsxs("button",{type:"button","aria-label":d("addWorker.randomAria"),className:"flex cursor-pointer items-center gap-1 rounded px-1.5 py-0.5 text-xs text-ter transition-colors hover:bg-3 hover:text-sec",onClick:u,"data-testid":"random-worker-name",children:[e.jsx(me,{size:12,"aria-hidden":!0}),d("addWorker.random")]})})]}),e.jsx("input",{autoFocus:!0,value:A,onChange:p=>x(p.target.value),placeholder:d("addWorker.namePlaceholder"),className:"input"})]}),e.jsx(ye,{workerRole:S,onRoleChange:y}),e.jsxs("button",{type:"button",onClick:()=>{H(!0),O(!0)},"data-testid":"open-marketplace",className:"marketplace-browse-btn flex cursor-pointer items-center gap-2 self-start rounded-md border px-3 py-1.5 text-xs text-sec outline-none transition-colors focus-visible:ring-2",style:{background:"var(--bg-0)",borderColor:"var(--border-bright)","--tw-ring-color":"color-mix(in oklab, var(--accent) 45%, transparent)"},children:[e.jsx(ke,{size:14,"aria-hidden":!0}),d("marketplace.openFromAddWorker")]}),S==="custom"?e.jsx(Ne,{customTemplates:n,disabledReason:N,onDeleteTemplate:f,onSelect:s,selectedTemplateId:_}):null,e.jsx(Ce,{canSaveAsTemplate:S==="custom"&&!_&&h.trim().length>0,modified:Q,onChange:b,onReset:k,onSaveAsTemplate:g,roleDescription:h,templateBusy:I,workerRole:S,writeDisabledReason:N}),e.jsx(we,{commandPresetId:a,commandPresets:t,onPresetChange:m}),e.jsx(ze,{value:P,onChange:v})]}),e.jsxs("div",{className:"flex shrink-0 items-center justify-end gap-2 border-t px-5 py-3",style:{borderColor:"var(--border)",background:"var(--bg-2)"},children:[e.jsx("button",{type:"button",onClick:o,className:"icon-btn","data-testid":"add-worker-cancel",children:d("addWorker.cancel")}),e.jsx("button",{type:"submit",disabled:l||!!N,title:N??void 0,className:"icon-btn icon-btn--primary","data-testid":"add-worker-submit",children:d(l?"addWorker.creating":"addWorker.create")})]})]})})})]}),q?e.jsx(c.Suspense,{fallback:null,children:e.jsx(Me,{open:B,onClose:()=>O(!1),onImport:V,importedNames:K})}):null]})},Ae=Object.freeze(Object.defineProperty({__proto__:null,AddWorkerDialog:_e},Symbol.toStringTag,{value:"Module"}));export{Ae as A,he as S};
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/MarketplaceDrawer-Bk6cpukn.js","assets/index-CAgGM6nb.js","assets/index-5zh61jMg.css"])))=>i.map(i=>d[i]);
2
+ import{c as W,j as e,u as j,r as c,e as z,k as M,n as X,A as Y,B as Z,F as R,G as ee,H as te,a as ae,P as re,O as se,C as ne,b as le,D as oe,d as ie,_ as de}from"./index-CAgGM6nb.js";const ce=[["path",{d:"M12 7v6",key:"lw1j43"}],["path",{d:"M15 10H9",key:"o6yqo3"}],["path",{d:"M17 3a2 2 0 0 1 2 2v15a1 1 0 0 1-1.496.868l-4.512-2.578a2 2 0 0 0-1.984 0l-4.512 2.578A1 1 0 0 1 5 20V5a2 2 0 0 1 2-2z",key:"oz39mx"}]],ue=W("bookmark-plus",ce);const xe=[["rect",{width:"12",height:"12",x:"2",y:"10",rx:"2",ry:"2",key:"6agr2n"}],["path",{d:"m17.92 14 3.5-3.5a2.24 2.24 0 0 0 0-3l-5-4.92a2.24 2.24 0 0 0-3 0L10 6",key:"1o487t"}],["path",{d:"M6 18h.01",key:"uhywen"}],["path",{d:"M10 14h.01",key:"ssrbsk"}],["path",{d:"M15 6h.01",key:"cblpky"}],["path",{d:"M18 9h.01",key:"2061c0"}]],me=W("dices",xe);const pe=[["path",{d:"m21 21-4.34-4.34",key:"14j7rj"}],["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}]],he=W("search",pe);const fe=[["path",{d:"m7 11 2-2-2-2",key:"1lz0vl"}],["path",{d:"M11 13h4",key:"1p7l4v"}],["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2",key:"1m3agn"}]],be=W("square-terminal",fe);const ge=[["path",{d:"M15 21v-5a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v5",key:"slp6dd"}],["path",{d:"M17.774 10.31a1.12 1.12 0 0 0-1.549 0 2.5 2.5 0 0 1-3.451 0 1.12 1.12 0 0 0-1.548 0 2.5 2.5 0 0 1-3.452 0 1.12 1.12 0 0 0-1.549 0 2.5 2.5 0 0 1-3.77-3.248l2.889-4.184A2 2 0 0 1 7 2h10a2 2 0 0 1 1.653.873l2.895 4.192a2.5 2.5 0 0 1-3.774 3.244",key:"o0xfot"}],["path",{d:"M4 10.95V19a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8.05",key:"wn3emo"}]],ke=W("store",ge),ve=[{value:"coder"},{value:"reviewer"},{value:"tester"},{value:"custom",dashed:!0}],F=t=>`role.${t}`,C=({children:t})=>e.jsx("span",{className:"text-sm font-medium text-sec",children:t}),je=({active:t,spec:a,onSelect:l})=>{const{t:n}=j();return e.jsxs("button",{type:"button",onClick:l,"aria-pressed":t,"data-testid":`role-card-${a.value}`,className:`selectable-card${a.dashed?" selectable-card--dashed":""} flex items-center gap-3 px-3 py-2`,children:[e.jsx(R,{role:a.value,size:20}),e.jsx("span",{className:"flex-1 text-left text-base font-medium text-pri",children:n(F(a.value))}),t?e.jsx(M,{size:14,className:"shrink-0 text-accent","aria-hidden":!0}):null]})},ye=({onRoleChange:t,workerRole:a})=>{const{t:l}=j();return e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx(C,{children:l("addWorker.role")}),e.jsx("div",{className:"grid grid-cols-2 gap-2",children:ve.map(n=>e.jsx(je,{active:a===n.value,spec:n,onSelect:()=>t(n.value)},n.value))})]})},Ne=({customTemplates:t,disabledReason:a,onDeleteTemplate:l,onSelect:n,selectedTemplateId:i})=>{const{t:o}=j(),[f,x]=c.useState(!1),[m,u]=c.useState(""),[b,k]=c.useState(null),y=c.useRef(null),g=c.useMemo(()=>t.find(r=>r.id===i)??null,[t,i]),v=c.useMemo(()=>{const r=m.trim().toLowerCase();return r?t.filter(s=>s.name.toLowerCase().includes(r)||s.description.toLowerCase().includes(r)):t},[t,m]);return c.useEffect(()=>{if(!f)return;const r=h=>{h.key==="Escape"&&x(!1)},s=h=>{const w=y.current;w&&!w.contains(h.target)&&x(!1)};return document.addEventListener("keydown",r),document.addEventListener("pointerdown",s),()=>{document.removeEventListener("keydown",r),document.removeEventListener("pointerdown",s)}},[f]),e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx(C,{children:o("addWorker.template")}),e.jsxs("div",{ref:y,className:"relative",children:[e.jsxs("button",{type:"button","aria-haspopup":"listbox","aria-expanded":f,"data-testid":"role-template-picker-trigger",onClick:()=>x(r=>!r),className:"flex w-full items-center justify-between gap-2 rounded border px-3 py-2 text-left text-sm transition-colors hover:bg-3",style:{borderColor:"var(--border)",background:"var(--bg-1)"},children:[e.jsx("span",{className:"min-w-0 flex-1 truncate text-pri",children:g?g.name:o("addWorker.templatePickPlaceholder")}),e.jsx(z,{size:14,className:"shrink-0 text-ter","aria-hidden":!0})]}),f?e.jsxs("div",{role:"listbox","aria-label":o("addWorker.template"),"data-testid":"role-template-picker-menu",className:"elev-2 absolute left-0 right-0 top-full z-30 mt-1 flex max-h-72 flex-col overflow-hidden rounded border",style:{background:"var(--bg-elevated)",borderColor:"var(--border-bright)"},children:[e.jsxs("div",{className:"flex items-center gap-2 border-b px-2 py-1.5",style:{borderColor:"var(--border)"},children:[e.jsx(he,{size:14,className:"text-ter","aria-hidden":!0}),e.jsx("input",{value:m,onChange:r=>u(r.currentTarget.value),placeholder:o("addWorker.templateSearchPlaceholder"),"data-testid":"role-template-search-input",className:"w-full bg-transparent text-sm text-pri outline-none placeholder:text-ter",spellCheck:!1})]}),e.jsx("div",{className:"flex-1 overflow-y-auto py-1",children:t.length===0?e.jsx("div",{"data-testid":"role-template-empty-state",className:"px-3 py-3 text-center text-sm text-ter",children:o("addWorker.templateEmpty")}):v.length===0?e.jsx("div",{className:"px-3 py-3 text-center text-sm text-ter",children:o("addWorker.templateNoMatch")}):v.map(r=>{const s=r.id===i;return e.jsxs("div",{className:"relative",children:[e.jsxs("button",{type:"button",role:"option","aria-selected":s,"data-testid":`role-template-option-${r.id}`,onClick:()=>{n(r.id),x(!1),u("")},className:"flex w-full items-center gap-2 px-3 py-1.5 pr-9 text-left text-sm text-pri hover:bg-3",style:s?{background:"var(--bg-3)"}:void 0,children:[e.jsx("span",{className:"min-w-0 flex-1 truncate",children:r.name}),s?e.jsx(M,{size:14,className:"shrink-0 text-accent","aria-hidden":!0}):null]}),e.jsx("button",{type:"button","aria-label":o("addWorker.templateDeleteAria",{name:r.name}),"data-testid":`role-template-delete-${r.id}`,disabled:!!a,title:a??void 0,onClick:h=>{h.preventDefault(),h.stopPropagation(),!a&&k(r)},className:"absolute right-1 top-1/2 flex h-6 w-6 -translate-y-1/2 items-center justify-center rounded text-ter transition-colors hover:bg-3 hover:text-pri",children:e.jsx(X,{size:14,"aria-hidden":!0})})]},r.id)})}),i!==null?e.jsx("button",{type:"button","data-testid":"role-template-clear",onClick:()=>{n(null),x(!1),u("")},className:"border-t px-3 py-1.5 text-left text-sm text-ter transition-colors hover:bg-3 hover:text-pri",style:{borderColor:"var(--border)"},children:o("addWorker.templateClear")}):null]}):null]}),e.jsx(Y,{open:b!==null,onOpenChange:r=>{r||k(null)},title:o("addWorker.templateDeleteTitle"),description:b?o("addWorker.templateDeleteConfirm",{name:b.name}):"",confirmLabel:o("addWorker.templateDeleteConfirmLabel"),confirmKind:"danger",onConfirm:()=>{if(!b||a)return;const r=b.id;k(null),l(r)}})]})},Ce=({canSaveAsTemplate:t,modified:a,onChange:l,onReset:n,onSaveAsTemplate:i,roleDescription:o,templateBusy:f,workerRole:x,writeDisabledReason:m})=>{const{t:u}=j(),[b,k]=c.useState(!1),[y,g]=c.useState(!1),[v,r]=c.useState("");return c.useEffect(()=>{(x==="custom"||a)&&k(!0)},[a,x]),c.useEffect(()=>{t||(g(!1),r(""))},[t]),e.jsxs("details",{open:b,onToggle:s=>k(s.currentTarget.open),className:"group flex flex-col gap-2",children:[e.jsxs("summary",{className:"flex cursor-pointer select-none items-center justify-between gap-2 list-none",children:[e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsx(z,{size:12,"aria-hidden":!0,className:"-rotate-90 text-ter transition-transform duration-150 group-open:rotate-0"}),e.jsx(C,{children:u("addWorker.roleInstructions")}),a?e.jsxs("span",{className:"text-sm text-ter",children:["· ",u("addWorker.modifiedFrom",{role:u(F(x))})]}):null]}),e.jsxs("div",{className:"flex items-center gap-1",children:[t&&!y?e.jsxs("button",{type:"button","data-testid":"role-template-save",disabled:!!m,title:m??void 0,onClick:s=>{s.preventDefault(),s.stopPropagation(),g(!0)},className:"flex items-center gap-1 rounded px-2 py-0.5 text-xs font-medium transition-colors hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50",style:{color:"var(--accent)",background:"color-mix(in oklab, var(--accent) 14%, transparent)"},children:[e.jsx(ue,{size:12,"aria-hidden":!0}),u("addWorker.saveAsTemplate")]}):null,a?e.jsxs("button",{type:"button",className:"flex items-center gap-1 rounded px-1.5 py-0.5 text-xs text-ter transition-colors hover:bg-3 hover:text-sec",onClick:s=>{s.preventDefault(),s.stopPropagation(),n()},children:[e.jsx(Z,{size:12,"aria-hidden":!0}),u("addWorker.reset")]}):null]})]}),e.jsx("textarea",{"aria-label":"Role instructions",id:"add-worker-role-instructions",value:o,rows:5,onChange:s=>l(s.currentTarget.value),placeholder:x==="custom"?u("addWorker.customPlaceholder"):void 0,title:u("addWorker.roleInstructionsTitle"),className:"input mono resize-y text-sm",style:{minHeight:150},"data-testid":"role-instructions-textarea"}),t&&y?e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("input",{autoFocus:!0,value:v,onChange:s=>r(s.currentTarget.value),placeholder:u("addWorker.templateNamePlaceholder"),"data-testid":"role-template-save-name",className:"input flex-1 text-sm"}),e.jsx("button",{type:"button",disabled:f||!v.trim()||!!m,title:m??void 0,"data-testid":"role-template-save-confirm",onClick:async()=>{if(m)return;const s=v.trim();if(s)try{await i(s),g(!1),r("")}catch{}},className:"icon-btn icon-btn--primary text-xs",children:u("addWorker.templateSaveConfirm")}),e.jsx("button",{type:"button","data-testid":"role-template-save-cancel",onClick:()=>{g(!1),r("")},className:"icon-btn text-xs",children:u("common.cancel")})]}):null]})},D=({active:t,command:a,displayName:l,logoPresetId:n,notFound:i=!1,testId:o,onSelect:f})=>{const{t:x}=j(),m=e.jsx("span",{className:"inline-flex h-5 w-5 shrink-0 items-center justify-center rounded border border-border bg-surface-1 text-ter","data-testid":`${o}-generic-icon`,"aria-hidden":!0,children:e.jsx(be,{size:13})});return e.jsxs("button",{type:"button",onClick:f,"aria-pressed":t,"data-testid":o,className:"selectable-card flex items-center justify-between gap-3 px-3 py-2",children:[e.jsxs("span",{className:"flex min-w-0 items-center gap-3",children:[e.jsx(ee,{commandPresetId:n,fallback:m,size:22}),e.jsxs("span",{className:"flex min-w-0 flex-col items-start gap-0.5",children:[e.jsx("span",{className:"truncate text-base font-medium text-pri",children:l}),e.jsxs("span",{className:"mono truncate text-xs text-ter",children:[a,i?` · ${x("addWorker.agentNotFound")}`:""]})]})]}),t?e.jsx(M,{size:14,className:"shrink-0 text-accent","aria-hidden":!0}):null]})},We=({active:t,preset:a,onSelect:l})=>e.jsx(D,{active:t,command:a.command,displayName:a.displayName,logoPresetId:a.id,notFound:a.available===!1,testId:`agent-radio-${a.id}`,onSelect:l}),we=({commandPresetId:t,commandPresets:a,onPresetChange:l})=>e.jsx(Se,{commandPresetId:t,commandPresets:a,onPresetChange:l}),Se=({commandPresetId:t,commandPresets:a,onPresetChange:l})=>{const{t:n}=j();return e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx(C,{children:n("addWorker.agentCli")}),a.length===0?e.jsx("div",{className:"text-sm text-ter",children:n("addWorker.loadingPresets")}):e.jsxs("div",{className:"grid grid-cols-2 gap-2",children:[a.map(i=>e.jsx(We,{active:t===i.id,preset:i,onSelect:()=>l(i.id)},i.id)),e.jsx(D,{active:t==="",command:n("addWorker.genericCommand"),displayName:n("addWorker.genericAgent"),testId:"agent-radio-generic",onSelect:()=>l("")})]})]})},ze=({onChange:t,value:a})=>{const{t:l}=j(),n=a.trim();return e.jsxs("details",{className:"group flex flex-col gap-2",children:[e.jsx("summary",{className:"flex cursor-pointer select-none items-center justify-between gap-2 list-none",children:e.jsxs("span",{className:"flex min-w-0 items-center gap-1.5",children:[e.jsx(z,{size:12,"aria-hidden":!0,className:"-rotate-90 shrink-0 text-ter transition-transform duration-150 group-open:rotate-0"}),e.jsx(C,{children:l("addWorker.startupCommand")}),n?e.jsxs("span",{className:"truncate text-sm text-ter",children:["· ",l("addWorker.startupOverrides")]}):null]})}),e.jsxs("div",{className:"flex flex-col gap-2 rounded border bg-2 p-3",style:{borderColor:"var(--border)"},children:[e.jsx("input",{"aria-label":"Startup command",value:a,onChange:i=>t(i.currentTarget.value),placeholder:"qwen --model qwen3-coder",className:"input mono text-sm",spellCheck:!1}),e.jsx("p",{className:"text-sm leading-5 text-ter",children:l("addWorker.startupHelp",{example:"claude --resume <session-id>"})})]})]})},Me=c.lazy(()=>de(()=>import("./MarketplaceDrawer-Bk6cpukn.js"),__vite__mapDeps([0,1,2])).then(t=>({default:t.MarketplaceDrawer}))),_e=({commandPresets:t,commandPresetId:a,creating:l=!1,customTemplates:n,onApplyMarketplaceImport:i,onClose:o,onDeleteTemplate:f,onNameChange:x,onPresetChange:m,onRandomName:u,onRoleDescriptionChange:b,onRoleDescriptionReset:k,onRoleChange:y,onSaveAsTemplate:g,onStartupCommandChange:v,onSubmit:r,onTemplateChange:s,roleDescription:h,roleDescriptionDefault:w,selectedTemplateId:_,startupCommand:P,templateBusy:I,workerName:A,workerRole:S,writeDisabledReason:N})=>{const{t:d}=j(),L=te(),[B,O]=c.useState(!1),[q,H]=c.useState(!1),K=c.useMemo(()=>new Set(n.map(p=>p.name)),[n]),V=p=>{i(p),L.show({kind:"success",message:d("marketplace.imported",{name:p.name})})},G=p=>{p||o()},Q=h!==w,E=t.find(p=>p.id===a),$=P.trim(),J=()=>N||(A.trim()?!a&&!$?d("addWorker.pickCliOrStartup"):E?.available===!1&&!$?d("addWorker.unavailable",{name:E.displayName}):h.trim()?null:d("addWorker.emptyInstructions"):d("addWorker.enterName")),U=p=>{const T=J();if(T){p.preventDefault(),L.show({kind:"warning",message:T});return}r(p)};return e.jsxs(ae,{open:!0,onOpenChange:G,children:[e.jsxs(re,{children:[e.jsx(se,{"data-testid":"add-worker-overlay",className:"app-overlay fixed inset-0 z-40"}),e.jsx("div",{className:"pointer-events-none fixed inset-0 z-50 grid place-items-center p-4",children:e.jsx(ne,{"data-testid":"add-worker-content",className:"dialog-scale-pop elev-2 pointer-events-auto flex max-h-[calc(100vh-32px)] w-[560px] max-w-full flex-col rounded-lg border",style:{background:"var(--bg-elevated)",borderColor:"var(--border-bright)"},children:e.jsxs("form",{onSubmit:U,"aria-label":d("addWorker.title"),className:"flex flex-col",children:[e.jsxs("div",{className:"flex shrink-0 flex-col gap-0.5 border-b px-5 py-4",style:{borderColor:"var(--border)"},children:[e.jsx(le,{className:"text-lg font-semibold text-pri",children:d("addWorker.title")}),e.jsx(oe,{className:"text-sm text-ter",children:d("addWorker.description",{command:"team send"})})]}),e.jsxs("div",{className:"flex flex-col gap-4 overflow-y-auto px-5 py-4",children:[e.jsxs("label",{className:"flex flex-col gap-2",children:[e.jsxs("div",{className:"flex items-baseline justify-between gap-2",children:[e.jsx(C,{children:d("addWorker.name")}),e.jsx(ie,{label:d("addWorker.randomTooltip"),children:e.jsxs("button",{type:"button","aria-label":d("addWorker.randomAria"),className:"flex cursor-pointer items-center gap-1 rounded px-1.5 py-0.5 text-xs text-ter transition-colors hover:bg-3 hover:text-sec",onClick:u,"data-testid":"random-worker-name",children:[e.jsx(me,{size:12,"aria-hidden":!0}),d("addWorker.random")]})})]}),e.jsx("input",{autoFocus:!0,value:A,onChange:p=>x(p.target.value),placeholder:d("addWorker.namePlaceholder"),className:"input"})]}),e.jsx(ye,{workerRole:S,onRoleChange:y}),e.jsxs("button",{type:"button",onClick:()=>{H(!0),O(!0)},"data-testid":"open-marketplace",className:"marketplace-browse-btn flex cursor-pointer items-center gap-2 self-start rounded-md border px-3 py-1.5 text-xs text-sec outline-none transition-colors focus-visible:ring-2",style:{background:"var(--bg-0)",borderColor:"var(--border-bright)","--tw-ring-color":"color-mix(in oklab, var(--accent) 45%, transparent)"},children:[e.jsx(ke,{size:14,"aria-hidden":!0}),d("marketplace.openFromAddWorker")]}),S==="custom"?e.jsx(Ne,{customTemplates:n,disabledReason:N,onDeleteTemplate:f,onSelect:s,selectedTemplateId:_}):null,e.jsx(Ce,{canSaveAsTemplate:S==="custom"&&!_&&h.trim().length>0,modified:Q,onChange:b,onReset:k,onSaveAsTemplate:g,roleDescription:h,templateBusy:I,workerRole:S,writeDisabledReason:N}),e.jsx(we,{commandPresetId:a,commandPresets:t,onPresetChange:m}),e.jsx(ze,{value:P,onChange:v})]}),e.jsxs("div",{className:"flex shrink-0 items-center justify-end gap-2 border-t px-5 py-3",style:{borderColor:"var(--border)",background:"var(--bg-2)"},children:[e.jsx("button",{type:"button",onClick:o,className:"icon-btn","data-testid":"add-worker-cancel",children:d("addWorker.cancel")}),e.jsx("button",{type:"submit",disabled:l||!!N,title:N??void 0,className:"icon-btn icon-btn--primary","data-testid":"add-worker-submit",children:d(l?"addWorker.creating":"addWorker.create")})]})]})})})]}),q?e.jsx(c.Suspense,{fallback:null,children:e.jsx(Me,{open:B,onClose:()=>O(!1),onImport:V,importedNames:K})}):null]})},Ae=Object.freeze(Object.defineProperty({__proto__:null,AddWorkerDialog:_e},Symbol.toStringTag,{value:"Module"}));export{Ae as A,he as S};