tide-commander 1.64.1 → 1.65.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 (45) hide show
  1. package/dist/assets/{BossLogsModal-B72_7BHt.js → BossLogsModal-U4mcLIP3.js} +1 -1
  2. package/dist/assets/{BossSpawnModal-I2gd6Haa.js → BossSpawnModal-CiFvgaQ1.js} +1 -1
  3. package/dist/assets/{ControlsModal-C6N5J1b9.js → ControlsModal-Bz4EQ0ii.js} +1 -1
  4. package/dist/assets/{DockerLogsModal-Sbka63uD.js → DockerLogsModal-CSMh4uRZ.js} +1 -1
  5. package/dist/assets/{EmbeddedEditor-CWXIFbqO.js → EmbeddedEditor-ecjZKKkD.js} +1 -1
  6. package/dist/assets/{GmailOAuthSetup-BD7iFY_z.js → GmailOAuthSetup-pFwSGVxa.js} +1 -1
  7. package/dist/assets/{GoogleOAuthSetup-BFA7pkjF.js → GoogleOAuthSetup-CPvUA9JE.js} +1 -1
  8. package/dist/assets/{IframeModal-CSNQoKQp.js → IframeModal-DcTPyjLy.js} +1 -1
  9. package/dist/assets/{IntegrationsPanel-CxIlAfwM.js → IntegrationsPanel-DHdp8HO-.js} +2 -2
  10. package/dist/assets/{LogViewerModal-oR_MJdlg.js → LogViewerModal-QWGgGmiP.js} +1 -1
  11. package/dist/assets/{MonitoringModal-DQgasofL.js → MonitoringModal-C8cfJlg5.js} +1 -1
  12. package/dist/assets/{PM2LogsModal-BUuealrs.js → PM2LogsModal-uUu_bTeC.js} +1 -1
  13. package/dist/assets/{RestoreArchivedAreaModal-Cw_fsm0f.js → RestoreArchivedAreaModal-C-Zxn6n6.js} +1 -1
  14. package/dist/assets/{Scene2DCanvas-Ct8tkxrX.js → Scene2DCanvas-0uRu2G4V.js} +1 -1
  15. package/dist/assets/{SceneManager-ksMw6QzO.js → SceneManager-xyitf6BI.js} +1 -1
  16. package/dist/assets/{SkillsPanel-CIKjBsqK.js → SkillsPanel-Bgcx1OI3.js} +1 -1
  17. package/dist/assets/SpawnModal-CS3BMuY9.js +1 -0
  18. package/dist/assets/{SubordinateAssignmentModal-fBH0D-n0.js → SubordinateAssignmentModal-DNWrbv9p.js} +1 -1
  19. package/dist/assets/{TriggerManagerPanel-DQaewpQE.js → TriggerManagerPanel-D-pLDqne.js} +1 -1
  20. package/dist/assets/{WorkflowEditorPanel-BbQPfsAP.js → WorkflowEditorPanel-Cu7zjFdE.js} +1 -1
  21. package/dist/assets/{index-IKCoEdSk.js → index-2BVW4z0_.js} +1 -1
  22. package/dist/assets/{index-B9GgZYql.js → index-CWyxpmuL.js} +1 -1
  23. package/dist/assets/{index-3YGJoOHV.js → index-CZl-6UH7.js} +1 -1
  24. package/dist/assets/{index-ecMsTrrV.js → index-DEfCPZTr.js} +3 -3
  25. package/dist/assets/{index-BVOPPyiD.js → index-DM70jqOd.js} +1 -1
  26. package/dist/assets/{index-XGJvKAEa.js → index-Dd0cfrn9.js} +1 -1
  27. package/dist/assets/{index-CqU-Wd9O.js → index-Yy2LrlI7.js} +2 -2
  28. package/dist/assets/{index-BB5wf97o.js → index-cGfzMto2.js} +1 -1
  29. package/dist/assets/main-4iQEaD98.css +1 -0
  30. package/dist/assets/{main-KYv9Wb4s.js → main-BNhrjJHj.js} +82 -82
  31. package/dist/assets/{web-CTASmenh.js → web-BnX_BsjB.js} +1 -1
  32. package/dist/assets/{web-BlD_xY8D.js → web-CyBgxhK2.js} +1 -1
  33. package/dist/index.html +2 -2
  34. package/dist/src/packages/server/claude/runner.js +9 -6
  35. package/dist/src/packages/server/claude/session-loader.js +15 -0
  36. package/dist/src/packages/server/data/builtin-skills/agent-tracking.js +7 -7
  37. package/dist/src/packages/server/data/builtin-skills/boss-instructions.js +14 -1
  38. package/dist/src/packages/server/data/builtin-skills/task-label.js +10 -10
  39. package/dist/src/packages/server/routes/agents.js +39 -0
  40. package/dist/src/packages/server/services/runtime-command-execution.js +2 -1
  41. package/dist/src/packages/server/services/runtime-watchdog.js +2 -1
  42. package/dist/src/packages/server/websocket/handlers/boss-response-handler.js +48 -4
  43. package/package.json +1 -1
  44. package/dist/assets/SpawnModal-ClEPoXzz.js +0 -1
  45. package/dist/assets/main-DpE84tMP.css +0 -1
@@ -1 +1 @@
1
- import{bH as a}from"./main-KYv9Wb4s.js";import{ImpactStyle as i,NotificationType as r}from"./index-CqU-Wd9O.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class h extends a{constructor(){super(...arguments),this.selectionStarted=!1}async impact(t){const e=this.patternForImpact(t==null?void 0:t.style);this.vibrateWithPattern(e)}async notification(t){const e=this.patternForNotification(t==null?void 0:t.type);this.vibrateWithPattern(e)}async vibrate(t){const e=(t==null?void 0:t.duration)||300;this.vibrateWithPattern([e])}async selectionStart(){this.selectionStarted=!0}async selectionChanged(){this.selectionStarted&&this.vibrateWithPattern([70])}async selectionEnd(){this.selectionStarted=!1}patternForImpact(t=i.Heavy){return t===i.Medium?[43]:t===i.Light?[20]:[61]}patternForNotification(t=r.Success){return t===r.Warning?[30,40,30,50,60]:t===r.Error?[27,45,50]:[35,65,21]}vibrateWithPattern(t){if(navigator.vibrate)navigator.vibrate(t);else throw this.unavailable("Browser does not support the vibrate API")}}export{h as HapticsWeb};
1
+ import{bI as a}from"./main-BNhrjJHj.js";import{ImpactStyle as i,NotificationType as r}from"./index-Yy2LrlI7.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class h extends a{constructor(){super(...arguments),this.selectionStarted=!1}async impact(t){const e=this.patternForImpact(t==null?void 0:t.style);this.vibrateWithPattern(e)}async notification(t){const e=this.patternForNotification(t==null?void 0:t.type);this.vibrateWithPattern(e)}async vibrate(t){const e=(t==null?void 0:t.duration)||300;this.vibrateWithPattern([e])}async selectionStart(){this.selectionStarted=!0}async selectionChanged(){this.selectionStarted&&this.vibrateWithPattern([70])}async selectionEnd(){this.selectionStarted=!1}patternForImpact(t=i.Heavy){return t===i.Medium?[43]:t===i.Light?[20]:[61]}patternForNotification(t=r.Success){return t===r.Warning?[30,40,30,50,60]:t===r.Error?[27,45,50]:[35,65,21]}vibrateWithPattern(t){if(navigator.vibrate)navigator.vibrate(t);else throw this.unavailable("Browser does not support the vibrate API")}}export{h as HapticsWeb};
@@ -1 +1 @@
1
- import{bH as s}from"./main-KYv9Wb4s.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class l extends s{constructor(){super(...arguments),this.pending=[],this.deliveredNotifications=[],this.hasNotificationSupport=()=>{if(!("Notification"in window)||!Notification.requestPermission)return!1;if(Notification.permission!=="granted")try{new Notification("")}catch(i){if(i instanceof Error&&i.name==="TypeError")return!1}return!0}}async getDeliveredNotifications(){const i=[];for(const t of this.deliveredNotifications){const e={title:t.title,id:parseInt(t.tag),body:t.body};i.push(e)}return{notifications:i}}async removeDeliveredNotifications(i){for(const t of i.notifications){const e=this.deliveredNotifications.find(n=>n.tag===String(t.id));e==null||e.close(),this.deliveredNotifications=this.deliveredNotifications.filter(()=>!e)}}async removeAllDeliveredNotifications(){for(const i of this.deliveredNotifications)i.close();this.deliveredNotifications=[]}async createChannel(){throw this.unimplemented("Not implemented on web.")}async deleteChannel(){throw this.unimplemented("Not implemented on web.")}async listChannels(){throw this.unimplemented("Not implemented on web.")}async schedule(i){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");for(const t of i.notifications)this.sendNotification(t);return{notifications:i.notifications.map(t=>({id:t.id}))}}async getPending(){return{notifications:this.pending}}async registerActionTypes(){throw this.unimplemented("Not implemented on web.")}async cancel(i){this.pending=this.pending.filter(t=>!i.notifications.find(e=>e.id===t.id))}async areEnabled(){const{display:i}=await this.checkPermissions();return{value:i==="granted"}}async changeExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async checkExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async requestPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(await Notification.requestPermission())}}async checkPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(Notification.permission)}}transformNotificationPermission(i){switch(i){case"granted":return"granted";case"denied":return"denied";default:return"prompt"}}sendPending(){var i;const t=[],e=new Date().getTime();for(const n of this.pending)!((i=n.schedule)===null||i===void 0)&&i.at&&n.schedule.at.getTime()<=e&&(this.buildNotification(n),t.push(n));this.pending=this.pending.filter(n=>!t.find(o=>o===n))}sendNotification(i){var t;if(!((t=i.schedule)===null||t===void 0)&&t.at){const e=i.schedule.at.getTime()-new Date().getTime();this.pending.push(i),setTimeout(()=>{this.sendPending()},e);return}this.buildNotification(i)}buildNotification(i){const t=new Notification(i.title,{body:i.body,tag:String(i.id)});return t.addEventListener("click",this.onClick.bind(this,i),!1),t.addEventListener("show",this.onShow.bind(this,i),!1),t.addEventListener("close",()=>{this.deliveredNotifications=this.deliveredNotifications.filter(()=>!this)},!1),this.deliveredNotifications.push(t),t}onClick(i){const t={actionId:"tap",notification:i};this.notifyListeners("localNotificationActionPerformed",t)}onShow(i){this.notifyListeners("localNotificationReceived",i)}}export{l as LocalNotificationsWeb};
1
+ import{bI as s}from"./main-BNhrjJHj.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class l extends s{constructor(){super(...arguments),this.pending=[],this.deliveredNotifications=[],this.hasNotificationSupport=()=>{if(!("Notification"in window)||!Notification.requestPermission)return!1;if(Notification.permission!=="granted")try{new Notification("")}catch(i){if(i instanceof Error&&i.name==="TypeError")return!1}return!0}}async getDeliveredNotifications(){const i=[];for(const t of this.deliveredNotifications){const e={title:t.title,id:parseInt(t.tag),body:t.body};i.push(e)}return{notifications:i}}async removeDeliveredNotifications(i){for(const t of i.notifications){const e=this.deliveredNotifications.find(n=>n.tag===String(t.id));e==null||e.close(),this.deliveredNotifications=this.deliveredNotifications.filter(()=>!e)}}async removeAllDeliveredNotifications(){for(const i of this.deliveredNotifications)i.close();this.deliveredNotifications=[]}async createChannel(){throw this.unimplemented("Not implemented on web.")}async deleteChannel(){throw this.unimplemented("Not implemented on web.")}async listChannels(){throw this.unimplemented("Not implemented on web.")}async schedule(i){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");for(const t of i.notifications)this.sendNotification(t);return{notifications:i.notifications.map(t=>({id:t.id}))}}async getPending(){return{notifications:this.pending}}async registerActionTypes(){throw this.unimplemented("Not implemented on web.")}async cancel(i){this.pending=this.pending.filter(t=>!i.notifications.find(e=>e.id===t.id))}async areEnabled(){const{display:i}=await this.checkPermissions();return{value:i==="granted"}}async changeExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async checkExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async requestPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(await Notification.requestPermission())}}async checkPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(Notification.permission)}}transformNotificationPermission(i){switch(i){case"granted":return"granted";case"denied":return"denied";default:return"prompt"}}sendPending(){var i;const t=[],e=new Date().getTime();for(const n of this.pending)!((i=n.schedule)===null||i===void 0)&&i.at&&n.schedule.at.getTime()<=e&&(this.buildNotification(n),t.push(n));this.pending=this.pending.filter(n=>!t.find(o=>o===n))}sendNotification(i){var t;if(!((t=i.schedule)===null||t===void 0)&&t.at){const e=i.schedule.at.getTime()-new Date().getTime();this.pending.push(i),setTimeout(()=>{this.sendPending()},e);return}this.buildNotification(i)}buildNotification(i){const t=new Notification(i.title,{body:i.body,tag:String(i.id)});return t.addEventListener("click",this.onClick.bind(this,i),!1),t.addEventListener("show",this.onShow.bind(this,i),!1),t.addEventListener("close",()=>{this.deliveredNotifications=this.deliveredNotifications.filter(()=>!this)},!1),this.deliveredNotifications.push(t),t}onClick(i){const t={actionId:"tap",notification:i};this.notifyListeners("localNotificationActionPerformed",t)}onShow(i){this.notifyListeners("localNotificationReceived",i)}}export{l as LocalNotificationsWeb};
package/dist/index.html CHANGED
@@ -22,10 +22,10 @@
22
22
  <link rel="icon" type="image/png" sizes="16x16" href="/assets/icons/favicon-16x16.png" />
23
23
  <link rel="apple-touch-icon" sizes="180x180" href="/assets/icons/apple-touch-icon.png" />
24
24
  <title>Tide Commander</title>
25
- <script type="module" crossorigin src="/assets/main-KYv9Wb4s.js"></script>
25
+ <script type="module" crossorigin src="/assets/main-BNhrjJHj.js"></script>
26
26
  <link rel="modulepreload" crossorigin href="/assets/vendor-react--Eh9ivFN.js">
27
27
  <link rel="modulepreload" crossorigin href="/assets/vendor-three-Chj50gSY.js">
28
- <link rel="stylesheet" crossorigin href="/assets/main-DpE84tMP.css">
28
+ <link rel="stylesheet" crossorigin href="/assets/main-4iQEaD98.css">
29
29
  </head>
30
30
  <body>
31
31
  <div id="app"></div>
@@ -404,10 +404,11 @@ export class ClaudeRunner {
404
404
  getActiveProcessCount() {
405
405
  return this.activeProcesses.size;
406
406
  }
407
- async stop(agentId) {
408
- // Clear any queued messages an explicit stop means the user wants the agent to halt,
409
- // so delivering buffered messages to a new process would be unexpected.
410
- if (this.messageQueue.has(agentId)) {
407
+ async stop(agentId, clearQueue = true) {
408
+ // Only clear queued messages on explicit user-initiated stops.
409
+ // Respawns (watchdog, auto-restart) should preserve the queue so
410
+ // messages sent while the agent was processing are not lost.
411
+ if (clearQueue && this.messageQueue.has(agentId)) {
411
412
  const count = this.messageQueue.get(agentId).length;
412
413
  this.messageQueue.delete(agentId);
413
414
  if (count > 0) {
@@ -416,7 +417,7 @@ export class ClaudeRunner {
416
417
  }
417
418
  await this.lifecycle.stop(agentId);
418
419
  }
419
- async stopAll(killProcesses = true) {
420
+ async stopAll(killProcesses = true, clearQueue = true) {
420
421
  if (this.persistTimer) {
421
422
  clearInterval(this.persistTimer);
422
423
  this.persistTimer = null;
@@ -425,7 +426,9 @@ export class ClaudeRunner {
425
426
  clearInterval(this.watchdogTimer);
426
427
  this.watchdogTimer = null;
427
428
  }
428
- this.messageQueue.clear();
429
+ if (clearQueue) {
430
+ this.messageQueue.clear();
431
+ }
429
432
  await this.lifecycle.stopAll(killProcesses);
430
433
  }
431
434
  isRunning(agentId) {
@@ -443,6 +443,21 @@ async function waitForFileStable(filePath, maxWaitMs = 500) {
443
443
  }
444
444
  }
445
445
  function parseClaudeEntryMessages(entry, messages, toolUseIdToName) {
446
+ // Messages typed while a turn is in flight are persisted as
447
+ // attachment.queued_command rather than a top-level user entry, so emit
448
+ // them as user messages to keep history chronologically intact.
449
+ if (entry.type === 'attachment' && entry.attachment?.type === 'queued_command') {
450
+ const prompt = entry.attachment.prompt;
451
+ if (typeof prompt === 'string' && prompt.trim()) {
452
+ messages.push({
453
+ type: 'user',
454
+ content: prompt,
455
+ timestamp: entry.timestamp,
456
+ uuid: entry.uuid ?? `${entry.timestamp}-queued-user`,
457
+ });
458
+ }
459
+ return;
460
+ }
446
461
  if (entry.type === 'user' && entry.message?.content) {
447
462
  if (Array.isArray(entry.message.content)) {
448
463
  for (const block of entry.message.content) {
@@ -6,7 +6,7 @@ export const agentTracking = {
6
6
  assignedAgentClasses: ['*'],
7
7
  content: `# Agent Tracking Status (MANDATORY)
8
8
 
9
- **Every turn MUST end with a tracking-status PATCH curl as your final tool call. No exceptions — not for tiny replies, not for questions, not for refusals. Skipping it leaves the user's board stuck on stale \`writing\` or \`working\`.**
9
+ **Every turn MUST end with a tracking-status PATCH curl as your final tool call. No exceptions — not for tiny replies, not for questions, not for refusals. Skipping it leaves the user's board stuck on stale \`thinking\` or \`working\`.**
10
10
 
11
11
  ## When to Call
12
12
  - After finishing ANY reply (one-word answers included)
@@ -23,7 +23,7 @@ export const agentTracking = {
23
23
  \`\`\`
24
24
 
25
25
  ## Statuses
26
- - \`writing\` — the "thinking" state, set AUTOMATICALLY at the start of each turn by the Task Label skill. You normally do NOT set this yourself; the combined opening PATCH (taskLabel + trackingStatus:'writing') covers it. The UI shows an animated typing-dots indicator while in this state.
26
+ - \`thinking\` — the "thinking" state, set AUTOMATICALLY at the start of each turn by the Task Label skill. You normally do NOT set this yourself; the combined opening PATCH (taskLabel + trackingStatus:'thinking') covers it. The UI shows an animated typing-dots indicator while in this state.
27
27
  - \`working\` — longer-running work in progress; set this mid-turn if you want to clear the typing-dots indicator before you're fully done (e.g. a long build, a tight loop of edits). Optional.
28
28
  - \`need-review\` — finished work awaiting user review (describe what)
29
29
  - \`blocked\` — cannot proceed (say WHO/WHAT blocks you)
@@ -31,18 +31,18 @@ export const agentTracking = {
31
31
  - \`waiting-subordinates\` — boss agent waiting on delegated work
32
32
 
33
33
  ## Status Lifecycle Within a Turn
34
- 1. **Start of turn:** the Task Label skill sets \`writing\` (typing dots visible).
35
- 2. **During the turn:** the \`writing\` state persists until you replace it. You MAY optionally PATCH to \`working\` once you start doing real work (reads/edits/bash) if the turn is long — this swaps typing dots for the cyan working indicator.
36
- 3. **End of turn:** send the final tracking PATCH with the outcome status (\`need-review\`, \`blocked\`, \`can-clear-context\`, or \`waiting-subordinates\`). This replaces \`writing\`/\`working\` with the final state.
34
+ 1. **Start of turn:** the Task Label skill sets \`thinking\` (typing dots visible).
35
+ 2. **During the turn:** the \`thinking\` state persists until you replace it. You MAY optionally PATCH to \`working\` once you start doing real work (reads/edits/bash) if the turn is long — this swaps typing dots for the cyan working indicator.
36
+ 3. **End of turn:** send the final tracking PATCH with the outcome status (\`need-review\`, \`blocked\`, \`can-clear-context\`, or \`waiting-subordinates\`). This replaces \`thinking\`/\`working\` with the final state.
37
37
 
38
38
  ## Rules
39
39
  - Detail ≤ 80 chars
40
40
  - Tracking curl is the VERY LAST tool call — all user-facing text comes BEFORE it, nothing after
41
41
  - Don't pick \`can-clear-context\` if anything still needs user confirmation — use \`need-review\`
42
- - Do NOT manually set \`writing\` outside the opening PATCH from the Task Label skill — it's only for turn-start.
42
+ - Do NOT manually set \`thinking\` outside the opening PATCH from the Task Label skill — it's only for turn-start.
43
43
 
44
44
  ## Final Check Before Ending a Turn
45
45
  1. Have I sent the PATCH this turn? If not — send now.
46
46
  2. Is it my last tool call with no output after? If not — fix order.
47
- 3. Have I replaced the opening \`writing\` status with a meaningful final status? If not — do it now.`,
47
+ 3. Have I replaced the opening \`thinking\` status with a meaningful final status? If not — do it now.`,
48
48
  };
@@ -411,8 +411,21 @@ You can ONLY spawn new agents when the user EXPLICITLY requests it.
411
411
 
412
412
  ### Spawn Block Format (ONLY when user explicitly requests):
413
413
  \\\`\\\`\\\`spawn
414
- [{"name": "<Agent Name>", "class": "<agent class>", "cwd": "<optional working directory>"}]
414
+ [{
415
+ "name": "<Agent Name>",
416
+ "class": "<agent class slug>",
417
+ "cwd": "<optional working directory>",
418
+ "model": "<optional: claude-opus-4-7 | claude-sonnet-4-6 | claude-haiku-4-5 | etc.>",
419
+ "effort": "<optional: low | medium | high | xHigh | max>",
420
+ "initialSkillIds": ["<optional skill-id-1>", "<skill-id-2>"],
421
+ "provider": "<optional: claude | codex | opencode>",
422
+ "customInstructions": "<optional extra system prompt for this agent>",
423
+ "codexModel": "<optional: only when provider='codex'>",
424
+ "opencodeModel": "<optional: only when provider='opencode'>"
425
+ }]
415
426
  \\\`\\\`\\\`
416
427
 
428
+ **ALL fields except \`name\` and \`class\` are OPTIONAL.** Omit any field to use defaults (provider=claude, default model for the provider, agent class default skills, no custom instructions, cwd=boss's cwd).
429
+
417
430
  Valid classes: Any registered agent class in the system, including built-in classes (scout, builder, debugger, architect, warrior, support) and custom classes. Use the class slug (e.g. "growey", "espeon", "charming"). If the user requests a specific class, use that class name exactly as they specify it.`,
418
431
  };
@@ -1,23 +1,23 @@
1
1
  export const taskLabel = {
2
2
  slug: 'task-label',
3
3
  name: 'Task Label',
4
- description: 'Generate a brief task label and mark writing status at the start of every turn',
4
+ description: 'Generate a brief task label and mark thinking status at the start of every turn',
5
5
  allowedTools: ['Bash(curl:*)'],
6
6
  assignedAgentClasses: ['*'],
7
- content: `# Task Label + Writing Status (MANDATORY - Execute FIRST)
7
+ content: `# Task Label + Thinking Status (MANDATORY - Execute FIRST)
8
8
 
9
- **IMPORTANT: Every new turn begins with a single PATCH that (a) sets a brief task label and (b) flips your tracking status to \`writing\`. This makes the UI show a typing indicator while you plan/think before any real work happens.**
9
+ **IMPORTANT: Every new turn begins with a single PATCH that (a) sets a brief task label and (b) flips your tracking status to \`thinking\`. This makes the UI show a typing indicator while you plan/think before any real work happens.**
10
10
 
11
11
  ## Steps (do this FIRST before anything else):
12
12
  1. Read the user's request
13
13
  2. Generate a 1-5 word summary of the task (e.g., "Fix auth bug", "Add dark mode", "Refactor API calls", "Update tests")
14
- 3. Call the API to set BOTH your task label AND writing status in one PATCH:
14
+ 3. Call the API to set BOTH your task label AND thinking status in one PATCH:
15
15
 
16
16
  \`PATCH /api/agents/YOUR_AGENT_ID\`
17
17
 
18
18
  **Body (combined):**
19
19
  \`\`\`json
20
- {"taskLabel":"YOUR 1-5 WORD LABEL","trackingStatus":"writing","trackingStatusDetail":"Short sentence describing what you're about to do"}
20
+ {"taskLabel":"YOUR 1-5 WORD LABEL","trackingStatus":"thinking","trackingStatusDetail":"Short sentence describing what you're about to do"}
21
21
  \`\`\`
22
22
 
23
23
  4. Then proceed with the actual task
@@ -28,15 +28,15 @@ export const taskLabel = {
28
28
  - Be specific but concise (e.g., "Fix login redirect" not "Work on stuff")
29
29
  - \`trackingStatusDetail\` ≤ 80 chars — a tiny preview of the plan, shown under the typing dots
30
30
 
31
- ## Why \`writing\`?
32
- \`writing\` is the "thinking" status. It renders a typing-dots indicator in the tracking board so the user knows you've received their request and are forming a plan. It will be replaced automatically by your normal end-of-turn tracking PATCH (see the Agent Tracking skill), which sets the final status (\`need-review\`, \`blocked\`, \`can-clear-context\`, etc.).
31
+ ## Why \`thinking\`?
32
+ \`thinking\` is the "thinking" status. It renders a typing-dots indicator in the tracking board so the user knows you've received their request and are forming a plan. It will be replaced automatically by your normal end-of-turn tracking PATCH (see the Agent Tracking skill), which sets the final status (\`need-review\`, \`blocked\`, \`can-clear-context\`, etc.).
33
33
 
34
34
  ## CRITICAL: Execution Order
35
35
 
36
36
  YOUR VERY FIRST TOOL CALL, before ANY other tool — no Read, no Grep, no Glob, no Bash, no Agent, no TodoWrite, no WebSearch, NOTHING — MUST be the combined PATCH above. This is non-negotiable and has zero exceptions.
37
37
 
38
38
  ### Pre-flight checklist (run mentally before your first response):
39
- - (a) Have I sent the combined taskLabel + writing PATCH yet for this task?
39
+ - (a) Have I sent the combined taskLabel + thinking PATCH yet for this task?
40
40
  - (b) If NO — the ONLY acceptable first action is that PATCH curl. Stop. Do that first.
41
41
  - (c) If YES — proceed with normal work.
42
42
 
@@ -51,7 +51,7 @@ Grep("somePattern") ← VIOLATION
51
51
  # WRONG: Planning before labeling
52
52
  TodoWrite([...]) ← VIOLATION
53
53
 
54
- # WRONG: Two separate PATCHes (one for label, one for writing)
54
+ # WRONG: Two separate PATCHes (one for label, one for thinking)
55
55
  PATCH taskLabel only → PATCH trackingStatus only ← VIOLATION (combine them)
56
56
 
57
57
  # WRONG: Batching the label PATCH with other tool calls
@@ -63,7 +63,7 @@ PATCH taskLabel + Grep + Read ← VIOLATION
63
63
  # First turn: ONLY the combined PATCH, nothing else
64
64
  curl -s -X PATCH http://localhost:5174/api/agents/YOUR_AGENT_ID \\
65
65
  -H "Content-Type: application/json" -H "X-Auth-Token: abcd" \\
66
- -d '{"taskLabel":"Fix login redirect","trackingStatus":"writing","trackingStatusDetail":"Tracing the bad redirect on mobile Safari"}'
66
+ -d '{"taskLabel":"Fix login redirect","trackingStatus":"thinking","trackingStatusDetail":"Tracing the bad redirect on mobile Safari"}'
67
67
 
68
68
  # Second turn onward: proceed with all actual work (Read, Grep, etc.)
69
69
  \`\`\`
@@ -15,6 +15,7 @@ import { getAllCustomClasses } from '../services/custom-class-service.js';
15
15
  import { createLogger } from '../utils/logger.js';
16
16
  import { buildCustomAgentConfig } from '../websocket/handlers/command-handler.js';
17
17
  import { clearDelegation, getBossForSubordinate } from '../websocket/handlers/boss-response-handler.js';
18
+ import { OpencodeBackend } from '../opencode/backend.js';
18
19
  import { getSystemPrompt, setSystemPrompt, clearSystemPrompt, isEchoPromptEnabled, setEchoPromptEnabled, getCodexBinaryPath, setCodexBinaryPath, isTmuxModeEnabled, setTmuxModeEnabled } from '../services/system-prompt-service.js';
19
20
  const log = createLogger('Routes');
20
21
  const router = Router();
@@ -66,6 +67,44 @@ function runCommandWithTimeout(command, args, timeoutMs, cwd) {
66
67
  });
67
68
  });
68
69
  }
70
+ let opencodeModelsCache = null;
71
+ const OPENCODE_MODELS_TTL_MS = 60 * 60 * 1000; // 1 hour
72
+ router.get('/opencode/models', async (req, res) => {
73
+ const refresh = req.query.refresh === 'true' || req.query.refresh === '1';
74
+ const now = Date.now();
75
+ if (!refresh && opencodeModelsCache && now - opencodeModelsCache.fetchedAt < OPENCODE_MODELS_TTL_MS) {
76
+ res.json({
77
+ models: opencodeModelsCache.models,
78
+ source: opencodeModelsCache.source,
79
+ cached: true,
80
+ fetchedAt: opencodeModelsCache.fetchedAt,
81
+ });
82
+ return;
83
+ }
84
+ try {
85
+ const opencodeExe = new OpencodeBackend().getExecutablePath();
86
+ const args = refresh ? ['models', '--refresh'] : ['models'];
87
+ const result = await runCommandWithTimeout(opencodeExe, args, 15000);
88
+ const models = result.output
89
+ .split('\n')
90
+ .map((line) => line.trim())
91
+ .filter((line) => line.length > 0 && line.includes('/'));
92
+ if (models.length === 0) {
93
+ res.status(502).json({
94
+ error: 'opencode CLI returned no models',
95
+ stderr: result.errorOutput || undefined,
96
+ exitCode: result.exitCode,
97
+ });
98
+ return;
99
+ }
100
+ opencodeModelsCache = { models, fetchedAt: now, source: 'cli' };
101
+ res.json({ models, source: 'cli', cached: false, fetchedAt: now });
102
+ }
103
+ catch (err) {
104
+ log.error(' opencode models fetch failed:', err);
105
+ res.status(500).json({ error: err?.message || 'Failed to run opencode CLI' });
106
+ }
107
+ });
69
108
  // GET /api/agents/claude-sessions - List all Claude Code sessions
70
109
  // NOTE: This must be defined BEFORE /:id routes to prevent being interpreted as an ID
71
110
  router.get('/claude-sessions', async (req, res) => {
@@ -111,7 +111,8 @@ export function createRuntimeCommandExecution(deps) {
111
111
  }
112
112
  else {
113
113
  log.log(`[sendCommand] Agent ${agentId} (${agent.provider}): backend does not support stdin, stopping current process to respawn with resume`);
114
- await runner.stop(agentId);
114
+ // Preserve queued messages — they will be drained after the new process completes its turn
115
+ await runner.stop(agentId, false);
115
116
  }
116
117
  }
117
118
  else if (!processRunning) {
@@ -41,7 +41,8 @@ export function startStdinWatchdog(options) {
41
41
  }
42
42
  if (runner && !runner.hasRecentActivity(agentId, STDIN_ACTIVITY_TIMEOUT_MS)) {
43
43
  log.warn(`[STDIN-WATCHDOG] Agent ${agentId}: No activity after stdin message, respawning process...`);
44
- await runner.stop(agentId);
44
+ // Preserve queued messages — they will be drained after the respawned process completes its turn
45
+ await runner.stop(agentId, false);
45
46
  try {
46
47
  await onRespawn(agentId, command, systemPrompt, customAgent);
47
48
  log.log(`[STDIN-WATCHDOG] Agent ${agentId}: Successfully respawned process`);
@@ -3,8 +3,8 @@
3
3
  * Parses delegation, spawn, work-plan, and analysis-request blocks from boss agent responses
4
4
  */
5
5
  import { BUILT_IN_AGENT_CLASSES } from '../../../shared/agent-types.js';
6
- import { agentService, runtimeService, bossService, workPlanService } from '../../services/index.js';
7
- import { getAllCustomClasses } from '../../services/custom-class-service.js';
6
+ import { agentService, runtimeService, bossService, workPlanService, skillService } from '../../services/index.js';
7
+ import { getAllCustomClasses, getClassDefaultSkillIds } from '../../services/custom-class-service.js';
8
8
  import { logger, getCommanderBaseUrl } from '../../utils/index.js';
9
9
  import { getLastBossCommand, buildCustomAgentConfig } from './command-handler.js';
10
10
  const log = logger.ws;
@@ -308,7 +308,7 @@ export async function parseBossSpawn(bossId, bossName, resultText, broadcast, se
308
308
  const customClassIds = getAllCustomClasses().map(c => c.id);
309
309
  const validClasses = [...builtInClassIds, ...customClassIds];
310
310
  for (const spawnRequest of spawns) {
311
- const { name, class: agentClass, cwd } = spawnRequest;
311
+ const { name, class: agentClass, cwd, model, codexModel, opencodeModel, effort, initialSkillIds, provider, customInstructions, codexConfig, permissionMode, } = spawnRequest;
312
312
  if (!name || !agentClass) {
313
313
  log.error(` Spawn request missing required fields (name, class):`, spawnRequest);
314
314
  continue;
@@ -317,10 +317,54 @@ export async function parseBossSpawn(bossId, bossName, resultText, broadcast, se
317
317
  log.error(` Invalid agent class "${agentClass}". Must be one of: ${validClasses.join(', ')}`);
318
318
  continue;
319
319
  }
320
+ let safeProvider;
321
+ if (provider !== undefined) {
322
+ if (provider === 'claude' || provider === 'codex' || provider === 'opencode') {
323
+ safeProvider = provider;
324
+ }
325
+ else {
326
+ log.warn(` Spawn: invalid provider "${provider}" — falling back to default. Allowed: claude, codex, opencode.`);
327
+ }
328
+ }
329
+ let safeEffort;
330
+ if (effort !== undefined) {
331
+ if (effort === 'low' || effort === 'medium' || effort === 'high' || effort === 'xHigh' || effort === 'max') {
332
+ safeEffort = effort;
333
+ }
334
+ else {
335
+ log.warn(` Spawn: invalid effort "${effort}" — ignoring. Allowed: low, medium, high, xHigh, max.`);
336
+ }
337
+ }
338
+ let safeSkillIds;
339
+ if (initialSkillIds !== undefined) {
340
+ if (Array.isArray(initialSkillIds) && initialSkillIds.every(s => typeof s === 'string')) {
341
+ safeSkillIds = initialSkillIds;
342
+ }
343
+ else {
344
+ log.warn(` Spawn: initialSkillIds must be an array of strings — ignoring.`);
345
+ }
346
+ }
320
347
  const agentCwd = cwd || bossCwd;
321
348
  log.log(` Boss ${bossName} spawning new ${agentClass} agent: "${name}" in ${agentCwd}`);
322
349
  try {
323
- const newAgent = await agentService.createAgent(name, agentClass, agentCwd);
350
+ const newAgent = await agentService.createAgent(name, agentClass, agentCwd, undefined, // position
351
+ undefined, // sessionId
352
+ undefined, // useChrome
353
+ permissionMode ?? 'bypass', safeSkillIds, undefined, // isBoss
354
+ model, codexModel, typeof customInstructions === 'string' ? customInstructions : undefined, safeProvider, codexConfig, safeEffort, opencodeModel);
355
+ // Assign initial skills (mirrors agent-handler behavior). Combine
356
+ // explicitly requested skills with the class's default skills.
357
+ const requestedSkills = safeSkillIds ?? [];
358
+ const classDefaultSkills = getClassDefaultSkillIds(newAgent.class);
359
+ const allSkillIds = [...new Set([...requestedSkills, ...classDefaultSkills])];
360
+ for (const skillId of allSkillIds) {
361
+ try {
362
+ skillService.assignSkillToAgent(skillId, newAgent.id);
363
+ }
364
+ catch (skillErr) {
365
+ log.warn(` Failed to assign skill "${skillId}" to ${newAgent.name}: ${skillErr}`);
366
+ }
367
+ }
324
368
  const currentSubordinates = bossService.getSubordinates(bossId).map(a => a.id);
325
369
  const newSubordinates = [...currentSubordinates, newAgent.id];
326
370
  bossService.assignSubordinates(bossId, newSubordinates);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tide-commander",
3
- "version": "1.64.1",
3
+ "version": "1.65.0",
4
4
  "description": "Visual multi-agent orchestrator and manager for Claude Code with 3D/2D interface",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1 +0,0 @@
1
- import{u as Ve,$ as Ze,a0 as es,a1 as ss,a2 as as,r as t,a3 as ye,S as ne,a4 as te,A as Ae,l as le,a5 as ns,s as S,h as ts,j as s,a6 as ls,G as De,a7 as x,a8 as os,a9 as oe,I as ie,aa as T,ab as K,ac as ce,ad as is,ae as cs}from"./main-KYv9Wb4s.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";function Ee(r,g){const b=g.filter(C=>!r.has(C));return b.length===0?`${g[Math.floor(Math.random()*g.length)]}-${Date.now()%1e3}`:b[Math.floor(Math.random()*b.length)]}function ms({isOpen:r,onClose:g,onSpawnStart:b,onSpawnEnd:C,spawnPosition:Ie,spawnAreaId:N}){const{t:a}=Ve(["terminal","common"]),P=Ze(),p=es(),re=ss(),H=re.length>0?re:as,[m,v]=t.useState(""),[M,F]=t.useState(()=>ye(ne.LAST_CWD)),[i,k]=t.useState("scout"),[de,E]=t.useState(!1),[Le,R]=t.useState(!1),[pe,J]=t.useState(!1),[me,ue]=t.useState(""),[y,Y]=t.useState([]),[A,U]=t.useState(null),[_e,he]=t.useState(!1),[I,we]=t.useState(""),[h,$e]=t.useState(""),[z,Te]=t.useState(!1),[q,Pe]=t.useState("bypass"),[o,X]=t.useState("claude"),[j,L]=t.useState({fullAuto:!0,sandbox:"workspace-write",approvalMode:"on-request",search:!1}),[xe,Q]=t.useState(new Set),[V,fe]=t.useState("opus[1m]"),[Z,ge]=t.useState("xHigh"),[ee,Fe]=t.useState("gpt-5.3-codex"),[se,Re]=t.useState("minimax/MiniMax-M1-80k"),[je,Ue]=t.useState(""),[D,ze]=t.useState(""),O=t.useRef(null),Ne=t.useRef(!1),_=t.useCallback(()=>{window.__spawnModalAreaContext=null},[]),w=t.useMemo(()=>P.filter(e=>e.enabled),[P]),qe=["full-notifications","streaming-exec","task-label","report-task-to-boss","agent-tracking","send-message-to-agent"];t.useEffect(()=>{if(r&&!Ne.current){if(w.length>0){const l=w.filter(c=>qe.includes(c.slug)).map(c=>c.id);l.length>0&&Q(new Set(l))}const n=ye(ne.DEFAULT_AGENT_CLASS);if(n==="random"){const c=[...Object.keys(is),...p.map(u=>u.id)];k(c[Math.floor(Math.random()*c.length)])}else k(n||"scout")}Ne.current=r},[r,w,p]);const ve=t.useMemo(()=>{if(!D.trim())return w;const e=D.toLowerCase();return w.filter(n=>n.name.toLowerCase().includes(e)||n.description.toLowerCase().includes(e)||n.slug.toLowerCase().includes(e))},[w,D]),Oe=t.useCallback(e=>{Q(n=>{const l=new Set(n);return l.has(e)?l.delete(e):l.add(e),l})},[]);t.useMemo(()=>w.filter(e=>e.assignedAgentClasses.includes(i)),[w,i]);const We=t.useMemo(()=>{var n;const e=p.find(l=>l.id===i);return(n=e==null?void 0:e.defaultSkillIds)!=null&&n.length?P.filter(l=>e.defaultSkillIds.includes(l.id)):[]},[p,i,P]),Se=t.useMemo(()=>{if(!I.trim())return y;const e=I.toLowerCase();return y.filter(n=>n.sessionId.toLowerCase().includes(e)||n.projectPath.toLowerCase().includes(e)||n.firstMessage&&n.firstMessage.toLowerCase().includes(e))},[y,I]),W=t.useMemo(()=>{if(!h.trim())return p;const e=h.toLowerCase();return p.filter(n=>n.name.toLowerCase().includes(e)||n.description.toLowerCase().includes(e)||n.id.toLowerCase().includes(e))},[p,h]),G=t.useMemo(()=>{if(!h.trim())return te;const e=h.toLowerCase();return te.filter(n=>{const l=Ae[n.id];return n.name.toLowerCase().includes(e)||n.id.toLowerCase().includes(e)||l.description.toLowerCase().includes(e)})},[h]);t.useEffect(()=>{if(!r||!h.trim())return;const e=[...W.map(n=>n.id),...G.map(n=>n.id)];e.length===1&&e[0]!==i&&k(e[0])},[r,h,W,G,i]);const d=t.useMemo(()=>p.find(e=>e.id===i),[p,i]),Ge=t.useMemo(()=>{if(d!=null&&d.model)return d.model},[d]),Be=t.useMemo(()=>{if(d!=null&&d.customModelPath)return le(`/api/custom-models/${d.id}`)},[d]),Ke=d==null?void 0:d.modelScale,He=t.useMemo(()=>d?"scout":i,[i,d]),B=t.useCallback(async e=>{he(!0);try{const n=e?le(`/api/agents/claude-sessions?cwd=${encodeURIComponent(e)}`):le("/api/agents/claude-sessions"),c=await(await ns(n)).json();Y(c.sessions||[])}catch(n){console.error("Failed to fetch sessions:",n),Y([])}finally{he(!1)}},[]);t.useEffect(()=>{r?B(M||void 0):(Y([]),U(null),we(""))},[r,B]),t.useEffect(()=>{if(!r)return;const e=setTimeout(()=>{B(M||void 0),U(null)},300);return()=>clearTimeout(e)},[M,r,B]),t.useEffect(()=>{if(!r||!N)return;const e=S.getState().areas.get(N);if(e!=null&&e.directories&&e.directories.length>0){F(e.directories[0]);return}const n=Array.from(S.getState().agents.values()).filter(f=>{var $;return(($=S.getAreaForAgent(f.id))==null?void 0:$.id)===N&&f.cwd});if(n.length===0)return;const l=new Map;for(const f of n)l.set(f.cwd,(l.get(f.cwd)||0)+1);let c="",u=0;for(const[f,$]of l)$>u&&(c=f,u=$);c&&F(c)},[r,N]),t.useEffect(()=>{if(r){const e=new Set(Array.from(S.getState().agents.values()).map(u=>u.name)),n=Ee(e,H),l=p.find(u=>u.id===i),c=l?`${l.name} ${n}`:n;v(c),O.current&&(O.current.focus(),O.current.select())}},[r,H]),t.useEffect(()=>{if(!r)return;const e=p.find(n=>n.id===i);if(e){const n=p.find(l=>m.startsWith(l.name+" "));if(n){const l=m.substring(n.name.length+1);v(`${e.name} ${l}`)}else v(`${e.name} ${m}`)}else{const n=p.find(l=>m.startsWith(l.name+" "));if(n){const l=m.substring(n.name.length+1);v(l)}}},[i]),t.useEffect(()=>{i==="boss"&&fe("claude-opus-4-7")},[i]);const Je=()=>{var c;console.log("[SpawnModal] handleSpawn called"),R(!1);const e=A&&((c=y.find(u=>u.sessionId===A))==null?void 0:c.projectPath)||M;if(console.log("[SpawnModal] Effective CWD:",e),console.log("[SpawnModal] Agent name:",m),console.log("[SpawnModal] Agent class:",i),console.log("[SpawnModal] Permission mode:",q),console.log("[SpawnModal] Provider:",o),console.log("[SpawnModal] Use Chrome:",z),console.log("[SpawnModal] Session ID:",A||"none"),!e.trim()){console.error("[SpawnModal] Empty CWD, showing error"),R(!0);return}if(!m.trim()){console.log("[SpawnModal] Empty name, regenerating");const u=new Set(Array.from(S.getState().agents.values()).map(f=>f.name));v(Ee(u,H));return}cs(ne.LAST_CWD,e),E(!0),b();const n=Array.from(xe),l=je.trim()||void 0;console.log("[SpawnModal] Calling store.spawnAgent with:",{name:m.trim(),class:i,cwd:e.trim(),sessionId:A||void 0,useChrome:o==="claude"?z:!1,permissionMode:q,provider:o,codexConfig:o==="codex"?j:void 0,codexModel:o==="codex"?ee:void 0,opencodeModel:o==="opencode"?se:void 0,initialSkillIds:n,model:o==="claude"?V:void 0,customInstructions:l?`${l.length} chars`:void 0,spawnAreaId:N||void 0}),window.__spawnModalAreaContext=N?{areaId:N}:null,S.spawnAgent(m.trim(),i,e.trim(),Ie||void 0,A||void 0,o==="claude"?z:!1,q,n,o,o==="codex"?j:void 0,o==="codex"?ee:void 0,o==="claude"?V:void 0,l,o==="claude"?Z:void 0,o==="opencode"?se:void 0)},be=()=>{console.log("[SpawnModal] Agent creation successful"),E(!1),v(""),Q(new Set),_(),C(),g()},Ce=()=>{console.error("[SpawnModal] Agent creation failed"),E(!1),R(!0),_(),C()},Me=e=>{console.log("[SpawnModal] Directory not found:",e),E(!1),ue(e),J(!0),C()},ke=()=>{J(!1),E(!0),b(),S.createDirectoryAndSpawn(me,m.trim(),i)},ae=()=>{J(!1),ue(""),_()};t.useEffect(()=>(window.__spawnModalSuccess=be,window.__spawnModalError=Ce,window.__spawnModalDirNotFound=Me,()=>{_(),delete window.__spawnModalSuccess,delete window.__spawnModalError,delete window.__spawnModalDirNotFound}),[m,i,be,Ce,Me,_]);const{handleMouseDown:Ye,handleClick:Xe}=ts(g),Qe=e=>{e.key==="Escape"&&g()};return!r&&!pe?null:pe?s.jsx("div",{className:"modal-overlay visible",onClick:ae,onKeyDown:e=>{e.key==="Escape"&&ae(),e.key==="Enter"&&ke()},children:s.jsxs("div",{className:"modal confirm-modal",onClick:e=>e.stopPropagation(),children:[s.jsx("div",{className:"modal-header",children:a("terminal:spawn.directoryNotFound")}),s.jsxs("div",{className:"modal-body confirm-modal-body",children:[s.jsx("p",{children:a("terminal:spawn.directoryNotExist")}),s.jsx("code",{className:"confirm-modal-path",children:me}),s.jsx("p",{children:a("terminal:spawn.wouldYouCreate")})]}),s.jsxs("div",{className:"modal-footer",children:[s.jsx("button",{className:"btn btn-secondary",onClick:ae,children:a("common:buttons.cancel")}),s.jsx("button",{className:"btn btn-primary",onClick:ke,autoFocus:!0,children:a("terminal:spawn.createDirectory")})]})]})}):s.jsx("div",{className:`modal-overlay ${r?"visible":""}`,onMouseDown:Ye,onClick:Xe,onKeyDown:Qe,children:s.jsxs("div",{className:"modal spawn-modal",children:[s.jsx("div",{className:"modal-header",children:a("terminal:spawn.deployTitle")}),s.jsxs("div",{className:"modal-body spawn-modal-body",children:[s.jsxs("div",{className:"spawn-top-section",children:[s.jsx("div",{className:"spawn-preview-compact",children:s.jsx(ls,{agentClass:He,modelFile:Ge,customModelUrl:Be,modelScale:Ke,width:100,height:120})}),s.jsxs("div",{className:"spawn-class-section",children:[s.jsx("div",{className:"spawn-class-label",children:a("terminal:spawn.agentClass")}),p.length+te.length>6&&s.jsx("input",{type:"text",className:"spawn-input class-search-input",placeholder:a("terminal:spawn.filterClasses"),value:h,onChange:e=>$e(e.target.value)}),s.jsxs("div",{className:"class-selector-inline",children:[W.map(e=>s.jsxs("button",{className:`class-chip ${i===e.id?"selected":""}`,onClick:()=>k(e.id),title:e.description,children:[s.jsx(De,{classId:e.id,size:18,className:"class-chip-icon"}),s.jsx("span",{className:"class-chip-name",children:e.name})]},e.id)),G.map(e=>{const n=Ae[e.id];return s.jsxs("button",{className:`class-chip ${i===e.id?"selected":""}`,onClick:()=>k(e.id),title:n.description,children:[s.jsx(De,{classId:e.id,size:18,className:"class-chip-icon"}),s.jsx("span",{className:"class-chip-name",children:e.name})]},e.id)}),h&&W.length===0&&G.length===0&&s.jsx("div",{className:"class-search-empty",children:a("terminal:spawn.noClassesMatch",{query:h})})]})]})]}),s.jsxs("div",{className:"spawn-form-section",children:[s.jsxs("div",{className:"spawn-form-row",children:[s.jsxs("div",{className:"spawn-field",children:[s.jsx("label",{className:"spawn-label",children:a("common:labels.name")}),s.jsx("input",{ref:O,type:"text",className:"spawn-input",placeholder:a("terminal:spawn.agentNamePlaceholder"),value:m,onChange:e=>v(e.target.value)})]}),s.jsxs("div",{className:"spawn-field spawn-field-wide",children:[s.jsxs("label",{className:"spawn-label",children:[a("terminal:spawn.workingDir"),s.jsx(x,{text:a("terminal:spawn.helpWorkingDir"),title:a("terminal:spawn.workingDir"),position:"top",size:"sm"})]}),s.jsx(os,{value:M,onChange:e=>{F(e),R(!1)},placeholder:a("terminal:spawn.workingDirPlaceholder"),className:`spawn-input ${Le?"error":""}`,directoriesOnly:!0})]})]}),s.jsxs("div",{className:"spawn-form-row",children:[s.jsxs("div",{className:"spawn-field",children:[s.jsxs("label",{className:"spawn-label",children:[a("terminal:spawn.selectRuntime"),s.jsx(x,{text:a("terminal:spawn.helpRuntime"),title:a("terminal:spawn.runtimeTitle"),position:"top",size:"sm"})]}),s.jsxs("div",{className:"spawn-select-row",children:[s.jsxs("button",{className:`spawn-select-btn ${o==="claude"?"selected":""}`,onClick:()=>X("claude"),title:a("terminal:spawn.useClaudeCli"),children:[s.jsx("img",{src:"/assets/claude.ico",alt:"Claude",className:"spawn-provider-icon"}),s.jsx("span",{children:"Claude"})]}),s.jsxs("button",{className:`spawn-select-btn ${o==="codex"?"selected":""}`,onClick:()=>X("codex"),title:a("terminal:spawn.useCodexCli"),children:[s.jsx("img",{src:"/assets/codex.ico",alt:"Codex",className:"spawn-provider-icon"}),s.jsx("span",{children:"Codex"})]}),s.jsxs("button",{className:`spawn-select-btn spawn-select-btn--opencode ${o==="opencode"?"selected":""}`,onClick:()=>X("opencode"),title:"Use OpenCode CLI (multi-provider)",children:[s.jsx("img",{src:"/assets/opencode.svg",alt:"OpenCode",className:"spawn-provider-icon"}),s.jsx("span",{children:"OpenCode"})]})]})]}),s.jsxs("div",{className:"spawn-field",children:[s.jsxs("label",{className:"spawn-label",children:[a("common:labels.permissions"),s.jsx(x,{text:a("terminal:spawn.helpPermission"),title:a("terminal:spawn.permissionMode"),position:"top",size:"sm"})]}),s.jsx("div",{className:"spawn-select-row",children:Object.keys(oe).map(e=>s.jsxs("button",{className:`spawn-select-btn ${q===e?"selected":""}`,onClick:()=>Pe(e),title:oe[e].description,children:[s.jsx("span",{children:s.jsx(ie,{name:e==="bypass"?"bolt":"lock",size:12})}),s.jsx("span",{children:oe[e].label})]},e))})]})]}),s.jsx("div",{className:"spawn-form-row",children:s.jsxs("div",{className:"spawn-field",children:[s.jsxs("label",{className:"spawn-label",children:[a("common:labels.model"),s.jsx(x,{text:a("terminal:spawn.helpModel"),title:a("terminal:spawn.modelTitle"),position:"top",size:"sm"})]}),o==="claude"?s.jsx("div",{className:"spawn-select-row spawn-select-row--wrap",children:Object.keys(T).filter(e=>!T[e].deprecated).map(e=>s.jsxs("button",{className:`spawn-select-btn ${V===e?"selected":""}`,onClick:()=>fe(e),title:T[e].description,children:[s.jsx("span",{children:T[e].icon}),s.jsx("span",{children:T[e].label})]},e))}):o==="codex"?s.jsx("div",{className:"spawn-select-row spawn-select-row--codex-models",children:Object.keys(K).map(e=>s.jsxs("button",{className:`spawn-select-btn ${ee===e?"selected":""}`,onClick:()=>Fe(e),title:K[e].description,children:[s.jsx("span",{children:K[e].icon}),s.jsx("span",{children:K[e].label})]},e))}):o==="opencode"?s.jsx("input",{type:"text",className:"spawn-input",value:se,onChange:e=>Re(e.target.value),placeholder:"provider/model (e.g., minimax/MiniMax-M1-80k)"}):s.jsx("div",{className:"spawn-inline-hint",children:a("terminal:spawn.chooseCodexModel")})]})}),s.jsxs("div",{className:"spawn-form-row",children:[o==="claude"&&s.jsxs("div",{className:"spawn-field",children:[s.jsx("label",{className:"spawn-label",children:"Effort"}),s.jsxs("div",{className:"spawn-select-row spawn-select-row--effort",children:[s.jsx("button",{className:`spawn-select-btn spawn-select-btn--compact ${Z===void 0?"selected":""}`,onClick:()=>ge(void 0),title:"Use default effort level",children:s.jsx("span",{children:"Default"})}),Object.keys(ce).map(e=>s.jsx("button",{className:`spawn-select-btn spawn-select-btn--compact ${Z===e?"selected":""}`,onClick:()=>ge(e),title:ce[e].description,children:s.jsx("span",{children:ce[e].label})},e))]})]}),s.jsxs("div",{className:"spawn-field",children:[s.jsx("label",{className:"spawn-label",children:a("terminal:spawn.browser")}),s.jsxs("label",{className:"spawn-checkbox",children:[s.jsx("input",{type:"checkbox",checked:z,onChange:e=>Te(e.target.checked),disabled:o!=="claude"}),s.jsxs("span",{children:[s.jsx(ie,{name:"globe",size:12})," ",a("terminal:spawn.chromeBrowser")]}),s.jsx(x,{text:a(o==="claude"?"terminal:spawn.helpChrome":"terminal:spawn.helpChromeDisabled"),title:a("terminal:spawn.chromeBrowser"),position:"top",size:"sm"})]})]})]}),o==="codex"&&s.jsxs("div",{className:"codex-config-section",children:[s.jsx("div",{className:"codex-config-title",children:a("terminal:spawn.codex.configuration")}),s.jsxs("div",{className:"codex-config-options",children:[s.jsxs("div",{className:"codex-option-group",children:[s.jsxs("label",{className:"spawn-checkbox",children:[s.jsx("input",{type:"checkbox",checked:j.fullAuto!==!1,onChange:e=>L(n=>({...n,fullAuto:e.target.checked}))}),s.jsx("span",{children:a("terminal:spawn.codex.fullAuto")}),s.jsx(x,{text:a("terminal:spawn.helpFullAuto"),title:a("terminal:spawn.fullAutoTitle"),position:"top",size:"sm"})]}),s.jsxs("label",{className:"spawn-checkbox",children:[s.jsx("input",{type:"checkbox",checked:!!j.search,onChange:e=>L(n=>({...n,search:e.target.checked}))}),s.jsx("span",{children:a("terminal:spawn.codex.search")}),s.jsx(x,{text:a("terminal:spawn.helpSearch"),title:a("terminal:spawn.searchTitle"),position:"top",size:"sm"})]})]}),j.fullAuto===!1&&s.jsxs("div",{className:"codex-option-group",children:[s.jsx("div",{className:"codex-option-header",children:a("terminal:spawn.codex.restrictions")}),s.jsxs("select",{className:"spawn-input codex-select",value:j.sandbox||"workspace-write",onChange:e=>L(n=>({...n,sandbox:e.target.value})),children:[s.jsxs("option",{value:"read-only",children:["📖 ",a("terminal:spawn.codex.sandboxReadOnly")]}),s.jsxs("option",{value:"workspace-write",children:["✏️ ",a("terminal:spawn.codex.sandboxWorkspaceWrite")]}),s.jsxs("option",{value:"danger-full-access",children:["⚡ ",a("terminal:spawn.codex.sandboxDangerFullAccess")]})]}),s.jsxs("select",{className:"spawn-input codex-select",value:j.approvalMode||"on-request",onChange:e=>L(n=>({...n,approvalMode:e.target.value})),children:[s.jsxs("option",{value:"untrusted",children:["🔒 ",a("terminal:spawn.codex.approvalsUntrusted")]}),s.jsxs("option",{value:"on-failure",children:["⚠️ ",a("terminal:spawn.codex.approvalsOnFailure")]}),s.jsxs("option",{value:"on-request",children:["🤔 ",a("terminal:spawn.codex.approvalsOnRequest")]}),s.jsxs("option",{value:"never",children:["✅ ",a("terminal:spawn.codex.approvalsNever")]})]})]}),s.jsxs("div",{className:"codex-option-group",children:[s.jsx("div",{className:"codex-option-header",children:a("terminal:spawn.codex.profile")}),s.jsx("input",{type:"text",className:"spawn-input codex-profile-input",placeholder:a("terminal:spawn.codex.profilePlaceholder"),value:j.profile||"",onChange:e=>L(n=>({...n,profile:e.target.value||void 0}))})]})]})]}),w.length>0&&s.jsxs("div",{className:"spawn-skills-section",children:[s.jsxs("label",{className:"spawn-label",children:[a("terminal:spawn.skills")," ",s.jsxs("span",{className:"spawn-label-hint",children:["(",a("common:labels.optional"),")"]}),s.jsx(x,{text:a("terminal:spawn.helpSkills"),title:a("terminal:spawn.skillsTitle"),position:"top",size:"sm"})]}),w.length>6&&s.jsx("input",{type:"text",className:"spawn-input skill-search-input",placeholder:a("terminal:spawn.filterSkills"),value:D,onChange:e=>ze(e.target.value)}),s.jsxs("div",{className:"spawn-skills-inline",children:[ve.map(e=>{const n=xe.has(e.id);return We.some(c=>c.id===e.id)?null:s.jsxs("button",{className:`spawn-skill-chip ${n?"selected":""}`,onClick:()=>Oe(e.id),title:e.description,children:[n&&s.jsx("span",{className:"spawn-skill-check",children:s.jsx(ie,{name:"check",size:10})}),s.jsx("span",{children:e.name}),e.builtin&&s.jsx("span",{className:"spawn-skill-builtin",children:"TC"})]},e.id)}),D&&ve.length===0&&s.jsx("div",{className:"skill-search-empty",children:a("terminal:spawn.noSkillsMatch",{query:D})})]})]}),s.jsxs("div",{className:"spawn-custom-instructions-section",children:[s.jsxs("label",{className:"spawn-label",children:[a("terminal:spawn.customInstructions")," ",s.jsxs("span",{className:"spawn-label-hint",children:["(",a("common:labels.optional"),")"]}),s.jsx(x,{text:a("terminal:spawn.helpCustomInstructions"),title:a("terminal:spawn.customInstructions"),position:"top",size:"sm"})]}),s.jsx("textarea",{className:"spawn-input spawn-textarea",placeholder:a("terminal:spawn.customInstructionsPlaceholder"),value:je,onChange:e=>Ue(e.target.value),rows:3})]}),s.jsxs("div",{className:"spawn-sessions-section",children:[s.jsxs("label",{className:"spawn-label",children:[a("terminal:spawn.linkSession")," ",s.jsxs("span",{className:"spawn-label-hint",children:["(",a("common:labels.optional"),")"]}),s.jsx(x,{text:a("terminal:spawn.helpLinkSession"),title:a("terminal:spawn.linkSessionTitle"),position:"top",size:"sm"})]}),y.length>0&&s.jsx("input",{type:"text",className:"spawn-input session-search-input",placeholder:a("terminal:spawn.searchSessions"),value:I,onChange:e=>we(e.target.value)}),s.jsx("div",{className:"sessions-list",children:_e?s.jsx("div",{className:"sessions-loading",children:a("terminal:spawn.loadingSessions")}):y.length===0?s.jsx("div",{className:"sessions-empty",children:a("terminal:spawn.noSessions")}):Se.length===0?s.jsx("div",{className:"sessions-empty",children:a("terminal:spawn.noSessionsMatch",{query:I})}):Se.map(e=>{const n=A===e.sessionId,l=Date.now()-new Date(e.lastModified).getTime(),c=l<6e4?a("common:time.justNow"):l<36e5?a("common:time.minutesAgo",{count:Math.floor(l/6e4)}):l<864e5?a("common:time.hoursAgo",{count:Math.floor(l/36e5)}):a("common:time.daysAgo",{count:Math.floor(l/864e5)});return s.jsxs("div",{className:`session-item ${n?"selected":""}`,onClick:()=>{n?U(null):(U(e.sessionId),F(e.projectPath))},children:[s.jsxs("div",{className:"session-item-header",children:[s.jsx("span",{className:"session-item-path",children:e.projectPath}),s.jsx("span",{className:"session-item-age",children:c})]}),s.jsx("div",{className:"session-item-preview",children:e.firstMessage||a("terminal:spawn.messagesCount",{count:e.messageCount})})]},e.sessionId)})})]})]})]}),s.jsxs("div",{className:"modal-footer",children:[s.jsx("button",{className:"btn btn-secondary",onClick:g,children:a("common:buttons.cancel")}),s.jsx("button",{className:"btn btn-primary",onClick:Je,disabled:de,children:a(de?"common:buttons.deploying":"common:buttons2.deploy")})]})]})})}export{ms as SpawnModal};