@danieltmn/tamnora 0.25.1 → 0.26.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 (87) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +13 -25
  3. package/SECURITY.md +3 -4
  4. package/harness/packages/cli/src/cli.js +0 -3
  5. package/harness/packages/cli/src/commands/bridge.js +0 -1
  6. package/harness/packages/cli/src/commands/repl.js +3 -3
  7. package/harness/packages/cli/src/commands/run.js +1 -1
  8. package/harness/packages/cli/src/commands/server.js +1 -1
  9. package/harness/packages/cli/src/commands/tui.js +1 -1
  10. package/harness/packages/cli/src/lib/util.js +2 -2
  11. package/harness/packages/cli/src/tui/app.js +2 -2
  12. package/harness/packages/cli/test/tunnel.test.js +5 -32
  13. package/harness/packages/core/src/agents/index.js +13 -41
  14. package/harness/packages/core/src/config/app.js +0 -4
  15. package/harness/packages/core/src/config/home.js +1 -20
  16. package/harness/packages/core/src/config/index.js +5 -8
  17. package/harness/packages/core/src/extensions/load.js +22 -0
  18. package/harness/packages/core/src/frontmatter.js +32 -0
  19. package/harness/packages/core/src/hooks/index.js +2 -16
  20. package/harness/packages/core/src/index.js +2 -14
  21. package/harness/packages/core/src/plugins/index.js +2 -16
  22. package/harness/packages/core/src/procs/project.js +68 -0
  23. package/harness/packages/core/src/procs/static.js +17 -0
  24. package/harness/packages/core/src/rules/index.js +8 -27
  25. package/harness/packages/core/src/skills/index.js +8 -19
  26. package/harness/packages/core/src/storage/sqlite.js +595 -690
  27. package/harness/packages/core/src/tunnel.js +9 -147
  28. package/harness/packages/core/test/home.test.js +2 -35
  29. package/harness/packages/core/test/procs.test.js +3 -64
  30. package/harness/packages/core/test/storage.test.js +1 -12
  31. package/harness/packages/server/public/app.js +2 -19
  32. package/harness/packages/server/public/appearance.js +2 -56
  33. package/harness/packages/server/public/boot.js +2 -2
  34. package/harness/packages/server/public/cards.js +2 -21
  35. package/harness/packages/server/public/chat.js +0 -16
  36. package/harness/packages/server/public/chatFormat.js +0 -11
  37. package/harness/packages/server/public/chrome.js +2 -5
  38. package/harness/packages/server/public/commands.js +1 -2
  39. package/harness/packages/server/public/explorer.js +7 -10
  40. package/harness/packages/server/public/index.html +1228 -1326
  41. package/harness/packages/server/public/landing/boot.js +2 -3
  42. package/harness/packages/server/public/landing/index.html +3 -367
  43. package/harness/packages/server/public/settings.js +1255 -1322
  44. package/harness/packages/server/public/settingsNav.js +0 -1
  45. package/harness/packages/server/public/styles.css +2341 -2867
  46. package/harness/packages/server/public/system.js +356 -495
  47. package/harness/packages/server/public/voice.js +6 -22
  48. package/harness/packages/server/src/auth.js +4 -15
  49. package/harness/packages/server/src/cli.js +1 -1
  50. package/harness/packages/server/src/lib/agents.js +13 -27
  51. package/harness/packages/server/src/lib/appearance.js +3 -22
  52. package/harness/packages/server/src/lib/channels/telegram/commands.js +1 -1
  53. package/harness/packages/server/src/lib/definitions.js +40 -0
  54. package/harness/packages/server/src/lib/skills.js +14 -21
  55. package/harness/packages/server/src/lib/ttsSettings.js +2 -2
  56. package/harness/packages/server/src/lib/workspace.js +9 -11
  57. package/harness/packages/server/src/routes/chat.js +0 -43
  58. package/harness/packages/server/src/routes/preview.js +5 -22
  59. package/harness/packages/server/src/routes/procs.js +2 -4
  60. package/harness/packages/server/src/routes/tts.js +1 -1
  61. package/harness/packages/server/src/server.js +1 -5
  62. package/harness/packages/server/test/appearance-route.test.js +3 -3
  63. package/harness/packages/server/test/appearance.test.js +12 -38
  64. package/harness/packages/server/test/chatFormat.test.js +0 -8
  65. package/harness/packages/server/test/consoleTabs.test.js +4 -4
  66. package/harness/packages/server/test/ops.test.js +8 -56
  67. package/harness/packages/server/test/preview.test.js +2 -42
  68. package/harness/packages/server/test/security.test.js +194 -370
  69. package/harness/packages/server/test/settingsNav.test.js +2 -2
  70. package/harness/packages/server/test/tts.test.js +4 -4
  71. package/harness/tamnora.example.json +1 -1
  72. package/package.json +2 -2
  73. package/harness/packages/cli/src/commands/eval.js +0 -186
  74. package/harness/packages/core/src/eval/dataset.js +0 -47
  75. package/harness/packages/core/src/eval/judge.js +0 -39
  76. package/harness/packages/core/src/eval/report.js +0 -48
  77. package/harness/packages/core/src/eval/runTask.js +0 -62
  78. package/harness/packages/core/src/eval/runner.js +0 -153
  79. package/harness/packages/core/src/eval/scorers.js +0 -217
  80. package/harness/packages/core/src/eval/task.js +0 -59
  81. package/harness/packages/core/src/procs/detect.js +0 -149
  82. package/harness/packages/core/test/eval.test.js +0 -144
  83. package/harness/packages/server/src/lib/advisor.js +0 -198
  84. package/harness/packages/server/src/routes/advisor.js +0 -79
  85. package/harness/packages/server/src/routes/eval.js +0 -100
  86. package/harness/packages/server/test/advisor.test.js +0 -249
  87. package/harness/scripts/example-dataset.jsonl +0 -4
@@ -1,1326 +1,1228 @@
1
- <!doctype html>
2
- <html lang="es">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>Tamnora Console</title>
7
- <link rel="icon" href="/favicon.ico" sizes="any" />
8
- <link rel="apple-touch-icon" href="/icons/icon-192x192.png" />
9
- <link rel="manifest" href="/manifest.webmanifest" />
10
- <meta name="theme-color" content="#231a17" />
11
- <script src="/boot.js"></script>
12
- <link rel="preconnect" href="https://fonts.googleapis.com" />
13
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
14
- <link
15
- id="tamnora-fonts"
16
- rel="stylesheet"
17
- href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Libre+Baskerville:wght@400;700&family=Manrope:wght@400;500;600;700&display=swap"
18
- />
19
- <link rel="stylesheet" href="styles.css" />
20
- </head>
21
- <body>
22
- <header class="topbar">
23
- <div class="brand"><span class="logo"><img src="/icons/logo.png" alt="Tamnora" /></span> <button id="back-chat" type="button" class="back-chat" title="volver al chat" aria-label="volver al chat" hidden><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6" /></svg><span>chat</span></button> <span id="topbar-section" class="topbar-section">chat</span> <span class="ver">v0.25.1</span></div>
24
- <div class="settings">
25
- <button id="new-session-top" type="button" class="btn topbar-new" title="nueva sesión" aria-label="nueva sesión">
26
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true">
27
- <path d="M12 5v14M5 12h14" />
28
- </svg>
29
- <span class="topbar-new-label">nueva sesión</span>
30
- </button>
31
- <button id="search-toggle" type="button" class="btn icon" title="buscar (Ctrl/Cmd+K)" aria-label="buscar">
32
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
33
- <circle cx="11" cy="11" r="8" />
34
- <path d="m21 21-4.3-4.3" />
35
- </svg>
36
- </button>
37
- <div class="menu">
38
- <button id="more-tab" type="button" class="btn icon" title="más opciones" aria-haspopup="menu" aria-controls="more-menu" aria-expanded="false" aria-label="más opciones">
39
- <svg viewBox="0 0 24 24" fill="currentColor" stroke="none" aria-hidden="true">
40
- <circle cx="12" cy="5" r="1.7" />
41
- <circle cx="12" cy="12" r="1.7" />
42
- <circle cx="12" cy="19" r="1.7" />
43
- </svg>
44
- </button>
45
- <div id="more-menu" class="menu-pop" role="menu" hidden>
46
- <button type="button" class="active" data-tab="chat" role="menuitem">chat</button>
47
- <button type="button" data-tab="providers" role="menuitem">proveedores</button>
48
- <button type="button" data-tab="ajustes" role="menuitem">ajustes</button>
49
- <div class="menu-divider" role="separator"></div>
50
- <button type="button" data-tab="eval" role="menuitem">eval</button>
51
- <button type="button" data-tab="runs" role="menuitem">runs</button>
52
- </div>
53
- </div>
54
- <button id="settings-toggle" type="button" class="btn icon" title="modelo, modo y auto (ventana rápida)" aria-label="modelo y modo del agente">
55
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
56
- <path d="M21 4h-7M10 4H3M21 12h-9M8 12H3M21 20h-5M12 20H3" />
57
- <path d="M14 2v4M8 10v4M16 18v4" />
58
- </svg>
59
- </button>
60
- <button id="theme-toggle" type="button" class="btn icon" title="cambiar tema" aria-label="cambiar tema">
61
- <svg class="ico-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
62
- <circle cx="12" cy="12" r="4" />
63
- <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41" />
64
- </svg>
65
- <svg class="ico-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
66
- <path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" />
67
- </svg>
68
- </button>
69
- </div>
70
- </header>
71
-
72
- <div id="update-banner" class="update-banner" hidden>
73
- <span class="update-banner-icon" aria-hidden="true">⬆</span>
74
- <span id="update-banner-text" class="update-banner-text"></span>
75
- <div class="update-banner-actions">
76
- <button id="update-run" type="button" class="btn primary">actualizar</button>
77
- <button id="update-later" type="button" class="btn">más tarde</button>
78
- </div>
79
- </div>
80
-
81
- <main>
82
- <section id="view-chat" class="view active">
83
- <aside id="sidebar" class="sidebar">
84
- <div class="panel-head">
85
- <span class="panel-head-title">sesiones</span>
86
- <button id="sidebar-collapse" type="button" class="btn icon" title="ocultar panel de sesiones" aria-label="ocultar panel de sesiones">
87
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
88
- <path d="m15 18-6-6 6-6" />
89
- </svg>
90
- </button>
91
- </div>
92
- <button id="new-session" class="btn block">+ nueva sesión</button>
93
- <ul id="sessions" class="sessions"></ul>
94
- <div class="rail-footer">
95
- <span>workspace</span>
96
- <b id="rail-cwd">…</b>
97
- <a href="/landing" id="logout" class="rail-exit" title="cerrar la sesión visual">salir →</a>
98
- </div>
99
- </aside>
100
- <div id="resizer-sidebar" class="resizer" title="ajustar" role="separator" aria-orientation="vertical"></div>
101
- <div id="sidebar-backdrop" class="drawer-backdrop" hidden></div>
102
- <div class="conversation">
103
- <div id="tabbar" class="tabbar" hidden></div>
104
- <div id="panes" class="panes">
105
- <div id="messages" class="messages">
106
- <div class="empty">
107
- <div class="empty-brand"><span>Tamnora</span><span class="accent">Harness</span></div>
108
- <p class="empty-sub">Elegí un proveedor y un modelo, y escribí tu primer mensaje.</p>
109
- <div class="empty-hints">
110
- <span class="hint"><kbd>/help</kbd> ver los comandos</span>
111
- <span class="hint"><kbd>/new</kbd> abrir una sesión en otra carpeta</span>
112
- <span class="hint"><kbd>/init</kbd> analizar el proyecto y crear AGENTS.md</span>
113
- </div>
114
- </div>
115
- </div>
116
- <div id="scroll-nav" class="scroll-nav" hidden>
117
- <button id="scroll-top" type="button" class="scroll-btn" title="ir al inicio" aria-label="ir al inicio de la conversación">
118
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
119
- <path d="m18 15-6-6-6 6" />
120
- </svg>
121
- </button>
122
- <button id="scroll-down" type="button" class="scroll-btn" title="ir al final" aria-label="ir al final de la conversación">
123
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
124
- <path d="m6 9 6 6 6-6" />
125
- </svg>
126
- </button>
127
- </div>
128
- </div>
129
- <form id="chat-form" class="composer">
130
- <div id="slash-menu" class="slash-menu" hidden></div>
131
- <div id="toasts" class="toasts" aria-live="polite"></div>
132
- <div id="attach-preview" class="attach-preview" hidden></div>
133
- <div id="chat-queue" class="chat-queue" hidden></div>
134
- <div id="audio-player" class="audio-player" hidden></div>
135
- <div class="composer-top">
136
- <button type="button" id="cwd-pill" class="ctrl-pill" title="carpeta de trabajo de la sesión">
137
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
138
- <path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
139
- </svg>
140
- <span id="cwd-pill-label">sin carpeta</span>
141
- <svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
142
- </button>
143
- <div class="ctrl-wrap">
144
- <button type="button" id="mode-pill" class="ctrl-pill" aria-haspopup="menu" aria-expanded="false" title="permisos del agente">
145
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /></svg>
146
- <span id="mode-pill-label">Workspace Write</span>
147
- <svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
148
- </button>
149
- <div id="mode-menu" class="ctrl-menu" role="menu" hidden>
150
- <button type="button" class="ctrl-menu-item" data-kind="read" role="menuitemradio" aria-checked="false">
151
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z" /><circle cx="12" cy="12" r="3" /></svg>
152
- <span>Read Only</span>
153
- <svg class="ctrl-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg>
154
- </button>
155
- <button type="button" class="ctrl-menu-item" data-kind="write" role="menuitemradio" aria-checked="false">
156
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" /><path d="m15 5 4 4" /></svg>
157
- <span>Workspace Write</span>
158
- <svg class="ctrl-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg>
159
- </button>
160
- <button type="button" class="ctrl-menu-item" data-kind="full" role="menuitemradio" aria-checked="false">
161
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /><path d="m9 12 2 2 4-4" /></svg>
162
- <span>Full access</span>
163
- <svg class="ctrl-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg>
164
- </button>
165
- <button type="button" class="ctrl-menu-item" data-kind="voice" role="menuitemradio" aria-checked="false" title="conversación por voz: solo notas .md y .json">
166
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19v3" /><path d="M19 10v2a7 7 0 0 1-14 0v-2" /><path d="M12 15a3 3 0 0 0 3-3V6a3 3 0 0 0-6 0v6a3 3 0 0 0 3 3Z" /></svg>
167
- <span>Voz (.md / .json)</span>
168
- <svg class="ctrl-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg>
169
- </button>
170
- </div>
171
- </div>
172
- </div>
173
-
174
- <div class="composer-row">
175
- <button type="button" id="attach-image" class="add-btn" title="Adjuntar imagen (o pegala / arrastrala)" aria-label="adjuntar imagen">
176
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14" /><path d="M12 5v14" /></svg>
177
- </button>
178
- <button type="button" id="mic-btn" class="add-btn" title="Dictar por voz (tocá para grabar y parar)" aria-label="dictar por voz">
179
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
180
- <path d="M12 19v3" />
181
- <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
182
- <path d="M12 15a3 3 0 0 0 3-3V6a3 3 0 0 0-6 0v6a3 3 0 0 0 3 3Z" />
183
- </svg>
184
- <span class="mic-count" hidden></span>
185
- </button>
186
- <button type="button" id="hands-free-btn" class="add-btn" title="manos libres: conversación por voz" aria-label="manos libres" aria-pressed="false">
187
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
188
- <path d="M4.9 19.1a10 10 0 0 1 0-14.2" />
189
- <path d="M7.8 16.2a6 6 0 0 1 0-8.4" />
190
- <circle cx="12" cy="12" r="2" />
191
- <path d="M16.2 7.8a6 6 0 0 1 0 8.4" />
192
- <path d="M19.1 4.9a10 10 0 0 1 0 14.2" />
193
- </svg>
194
- </button>
195
- <input id="image-input" type="file" accept="image/*" multiple hidden />
196
- <textarea id="chat-input" rows="1" placeholder="escribí un mensaje…" title="Enter envía · Shift+Enter salto" aria-label="mensaje para el asistente" spellcheck="false"></textarea>
197
- <div class="ctrl-wrap">
198
- <button type="button" id="effort-pill" class="ctrl-pill" aria-haspopup="menu" aria-expanded="false" title="esfuerzo de razonamiento">
199
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z" /></svg>
200
- <span id="effort-pill-label">auto</span>
201
- <svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
202
- </button>
203
- <div id="effort-menu" class="ctrl-menu" role="menu" hidden></div>
204
- </div>
205
- <button type="button" id="model-pill" class="ctrl-pill model-pill" title="proveedor y modelo activos">
206
- <span id="model-pill-label">—</span>
207
- <svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
208
- </button>
209
- <div class="composer-actions">
210
- <button class="send-btn" type="submit" aria-label="enviar" title="enviar">
211
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m5 12 7-7 7 7" /><path d="M12 19V5" /></svg>
212
- </button>
213
- <button class="add-btn interrupt-btn" type="button" id="interrupt-btn" title="Interrumpir al agente y enviar este mensaje (Ctrl+Enter)" aria-label="interrumpir y enviar" hidden>
214
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z" /></svg>
215
- </button>
216
- <button class="add-btn" type="button" id="stop-btn" title="Detener la respuesta" aria-label="detener" hidden>
217
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="6" y="6" width="12" height="12" rx="2" /></svg>
218
- </button>
219
- </div>
220
- </div>
221
-
222
- <div id="statusbar" class="composer-status" title="estado del run" role="button" tabindex="0">
223
- <span id="sb-activity" class="sb-activity" role="status" hidden></span>
224
- <span id="sb-sync" class="sb-channel retrying" hidden title="sin sincronía con el server"></span>
225
- <span id="sb-channel" class="sb-channel" hidden title="canal de eventos entre dispositivos"></span>
226
- <span id="sb-rounds">listo</span>
227
- <span class="spacer"></span>
228
- <span id="sb-context" class="ctx muted" title="uso del contexto del modelo">
229
- <span id="sb-tokens">tokens —</span>
230
- <span class="ctx-bar" aria-hidden="true"><span id="sb-context-fill" class="ctx-fill"></span></span>
231
- </span>
232
- </div>
233
- </form>
234
- </div>
235
-
236
- <div id="resizer-explorer" class="resizer" title="ajustar" role="separator" aria-orientation="vertical"></div>
237
-
238
- <aside id="explorer" class="explorer" hidden>
239
- <div class="explorer-head">
240
- <div class="xtabs">
241
- <button type="button" class="xtab is-active" data-xp="files">estructura</button>
242
- <button type="button" class="xtab" data-xp="preview">preview</button>
243
- <button type="button" class="xtab" data-xp="procs">procs</button>
244
- <button type="button" class="xtab" data-xp="git">git</button>
245
- </div>
246
- <span class="spacer"></span>
247
- <button id="explorer-refresh" type="button" class="btn icon" title="refrescar" aria-label="refrescar">
248
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
249
- <path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" />
250
- <path d="M21 3v5h-5" />
251
- <path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" />
252
- <path d="M8 16H3v5" />
253
- </svg>
254
- </button>
255
- <button id="explorer-close" type="button" class="btn icon" title="cerrar" aria-label="cerrar el panel de archivos">
256
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
257
- <path d="M18 6 6 18M6 6l12 12" />
258
- </svg>
259
- </button>
260
- </div>
261
-
262
- <div id="xp-files" class="xp-pane">
263
- <div id="explorer-root" class="explorer-cwd" title=""></div>
264
- <div class="explorer-filter">
265
- <input id="explorer-filter" placeholder="filtrar por nombre…" aria-label="filtrar archivos por nombre" spellcheck="false" />
266
- </div>
267
- <ul id="explorer-tree" class="tree"></ul>
268
- <div id="explorer-status" class="explorer-status"></div>
269
- </div>
270
-
271
- <div id="xp-preview" class="xp-pane" hidden>
272
- <div class="xp-bar">
273
- <input id="preview-url" placeholder="http://127.0.0.1:5173" aria-label="URL de la vista previa" spellcheck="false" />
274
- <button id="preview-open" class="btn" type="button">abrir</button>
275
- <select id="preview-provider" title="cómo exponer el puerto para acceso remoto" aria-label="proveedor de acceso remoto">
276
- <option value="tailscale">tailscale</option>
277
- <option value="tunnelmole">público</option>
278
- </select>
279
- <button id="preview-tunnel" class="btn" type="button" title="Exponer el puerto para acceso remoto (tailscale privado o túnel público)">túnel</button>
280
- <button id="preview-close" class="btn danger" type="button" hidden title="Cerrar el túnel/exposición remota">cerrar</button>
281
- </div>
282
- <div class="preview-tools">
283
- <select id="preview-device" title="tamaño de pantalla" aria-label="tamaño de pantalla">
284
- <option value="auto">adaptable</option>
285
- <option value="390">móvil</option>
286
- <option value="768">tablet</option>
287
- <option value="1280">laptop</option>
288
- <option value="1600">pc</option>
289
- </select>
290
- <span class="spacer"></span>
291
- <button id="preview-max" class="btn icon" type="button" title="maximizar" aria-pressed="false" aria-label="maximizar la vista previa">
292
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
293
- <path d="M8 3H5a2 2 0 0 0-2 2v3M21 8V5a2 2 0 0 0-2-2h-3M3 16v3a2 2 0 0 0 2 2h3M16 21h3a2 2 0 0 0 2-2v-3" />
294
- </svg>
295
- </button>
296
- </div>
297
- <div id="preview-status" class="explorer-status"></div>
298
- <div id="preview-stage" class="preview-stage" data-device="auto">
299
- <iframe id="preview-frame" class="preview-frame" title="vista previa" referrerpolicy="no-referrer"></iframe>
300
- </div>
301
- </div>
302
-
303
- <div id="xp-procs" class="xp-pane" hidden>
304
- <div class="xp-bar">
305
- <button id="procs-detect" class="btn" type="button">detectar</button>
306
- <span id="procs-cwd" class="muted grow"></span>
307
- </div>
308
- <div id="procs-chips" class="chips"></div>
309
- <ul id="procs-list" class="providers"></ul>
310
- <pre id="procs-log" class="procs-log"></pre>
311
- </div>
312
-
313
- <div id="xp-git" class="xp-pane" hidden>
314
- <div class="xp-bar">
315
- <span id="git-branch" class="git-branch">…</span>
316
- <span id="git-sync" class="muted grow"></span>
317
- </div>
318
- <div class="explorer-filter">
319
- <input id="git-filter" aria-label="filtrar archivos por nombre" placeholder="filtrar por nombre…" spellcheck="false" />
320
- </div>
321
- <ul id="git-list" class="git-list"></ul>
322
- <div id="git-status" class="explorer-status"></div>
323
- </div>
324
- </aside>
325
-
326
- <button id="sidebar-handle" type="button" class="panel-handle panel-handle-left" title="mostrar sesiones" aria-label="mostrar panel de sesiones" aria-pressed="false">
327
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
328
- <path d="m9 18 6-6-6-6" />
329
- </svg>
330
- </button>
331
- <button id="explorer-handle" type="button" class="panel-handle panel-handle-right" title="mostrar archivos" aria-label="mostrar panel de archivos" aria-pressed="false">
332
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
333
- <path d="m15 18-6-6 6-6" />
334
- </svg>
335
- </button>
336
-
337
- <dialog id="file-modal" class="modal file-modal">
338
- <div class="modal-card">
339
- <div class="row between">
340
- <h3 id="file-title">archivo</h3>
341
- <div class="file-actions">
342
- <span id="file-meta" class="muted"></span>
343
- <select id="file-device" class="file-device" title="tamaño de pantalla" aria-label="tamaño de pantalla" hidden>
344
- <option value="auto">adaptable</option>
345
- <option value="390">móvil</option>
346
- <option value="768">tablet</option>
347
- <option value="1280">laptop</option>
348
- <option value="1600">pc</option>
349
- </select>
350
- <button id="file-max" type="button" class="btn icon" title="maximizar" aria-pressed="false" aria-label="maximizar la vista previa" hidden>
351
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
352
- <path d="M8 3H5a2 2 0 0 0-2 2v3M21 8V5a2 2 0 0 0-2-2h-3M3 16v3a2 2 0 0 0 2 2h3M16 21h3a2 2 0 0 0 2-2v-3" />
353
- </svg>
354
- </button>
355
- <button id="file-preview-toggle" type="button" class="btn" hidden>vista previa</button>
356
- <button id="file-close" type="button" class="btn icon" title="cerrar" aria-label="cerrar">
357
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
358
- <path d="M18 6 6 18M6 6l12 12" />
359
- </svg>
360
- </button>
361
- </div>
362
- </div>
363
- <div id="file-code" class="code-view">
364
- <pre id="file-gutter" class="code-gutter" aria-hidden="true"></pre>
365
- <pre id="file-content" class="file-preview"></pre>
366
- </div>
367
- <div id="file-render" class="file-render text" hidden></div>
368
- <div id="file-stage" class="preview-stage" data-device="auto" hidden>
369
- <iframe id="file-frame" class="file-frame" title="vista previa del archivo"></iframe>
370
- </div>
371
- </div>
372
- </dialog>
373
-
374
- <dialog id="git-modal" class="modal file-modal">
375
- <div class="modal-card">
376
- <div class="row between">
377
- <h3 id="git-title">cambios</h3>
378
- <div class="file-actions">
379
- <span id="git-meta" class="muted"></span>
380
- <button id="git-close" type="button" class="btn icon" title="cerrar" aria-label="cerrar">
381
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
382
- <path d="M18 6 6 18M6 6l12 12" />
383
- </svg>
384
- </button>
385
- </div>
386
- </div>
387
- <pre id="git-diff" class="file-preview git-diff"></pre>
388
- </div>
389
- </dialog>
390
- </section>
391
-
392
- <section id="view-providers" class="view">
393
- <div class="panel">
394
- <div class="row between">
395
- <h2>Proveedores</h2>
396
- <button id="provider-new" class="btn">+ nuevo proveedor</button>
397
- </div>
398
- <ul id="provider-list" class="providers"></ul>
399
- </div>
400
-
401
- <dialog id="provider-modal" class="modal">
402
- <form id="provider-form" class="modal-card">
403
- <div class="row between">
404
- <h3 id="provider-title">nuevo proveedor</h3>
405
- <button type="button" id="pf-close" class="btn icon" title="cerrar" aria-label="cerrar">
406
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
407
- <path d="M18 6 6 18M6 6l12 12" />
408
- </svg>
409
- </button>
410
- </div>
411
- <div class="row">
412
- <label class="field"><span>id</span><input id="pf-id" spellcheck="false" placeholder="deepseek" /></label>
413
- <label class="field"><span>nombre</span><input id="pf-label" spellcheck="false" placeholder="DeepSeek" /></label>
414
- <label class="field grow"><span>base URL</span><input id="pf-baseurl" spellcheck="false" placeholder="https://api.deepseek.com/v1" /></label>
415
- </div>
416
- <div class="row">
417
- <label class="field grow"><span>API key</span><input id="pf-key" type="password" spellcheck="false" placeholder="dejar vacío para no cambiar" /></label>
418
- <label class="field grow"><span>modelos (coma, opcional)</span><input id="pf-models" spellcheck="false" placeholder="deepseek-chat, deepseek-reasoner" /></label>
419
- </div>
420
- <div class="row">
421
- <button type="button" id="pf-test" class="btn">probar conexión</button>
422
- <span class="spacer"></span>
423
- <button type="button" id="pf-cancel" class="btn">cancelar</button>
424
- <button type="submit" class="btn primary">guardar</button>
425
- </div>
426
- <div id="pf-status" class="summary"></div>
427
- </form>
428
- </dialog>
429
- </section>
430
-
431
- <section id="view-eval" class="view">
432
- <div class="panel">
433
- <h2>Evaluación</h2>
434
- <label class="field">
435
- <span>dataset — JSON array o JSONL (una tarea por línea)</span>
436
- <textarea id="eval-tasks" rows="8" spellcheck="false" placeholder='{"id":"t1","input":"¿2+2?","expect":{"equals":"4"}}'></textarea>
437
- </label>
438
- <div class="row">
439
- <input id="eval-models" aria-label="modelos para evaluar (separados por coma)" placeholder="modelos separados por coma" spellcheck="false" />
440
- <input id="eval-trials" aria-label="trials por tarea" type="number" min="1" value="1" title="trials" />
441
- <input id="eval-concurrency" aria-label="concurrencia" type="number" min="1" value="2" title="concurrencia" />
442
- <input id="eval-judge" aria-label="judge-model (opcional)" placeholder="judge-model (opcional)" spellcheck="false" />
443
- <button id="eval-run" class="btn primary">correr</button>
444
- </div>
445
- <div id="eval-summary" class="summary"></div>
446
- <table class="results">
447
- <thead>
448
- <tr><th>ok</th><th>tarea</th><th>modelo</th><th>score</th><th>ms</th><th>detalle</th></tr>
449
- </thead>
450
- <tbody id="eval-results"></tbody>
451
- </table>
452
- </div>
453
- </section>
454
-
455
- <section id="view-runs" class="view">
456
- <div class="panel">
457
- <div class="row between">
458
- <h2>Runs</h2>
459
- <button id="runs-refresh" class="btn">refrescar</button>
460
- </div>
461
- <ul id="runs-list" class="runs"></ul>
462
- <div id="run-detail"></div>
463
- </div>
464
- </section>
465
-
466
- <section id="view-ajustes" class="view">
467
- <div class="ajustes-wrap" data-level="list">
468
- <aside class="ajustes-side">
469
- <input id="ajustes-search" class="ajustes-search" type="search" placeholder="buscar en ajustes…" aria-label="buscar en ajustes" autocomplete="off" spellcheck="false" />
470
- <nav class="ajustes-tabs" role="tablist" aria-orientation="vertical" aria-label="secciones de ajustes">
471
- <div class="ajustes-group" data-group="trabajo" role="presentation">
472
- <span class="ajustes-group-title" aria-hidden="true">Trabajo</span>
473
- <button type="button" class="ajustes-tab active" id="ajustes-tab-agente" data-panel="agente" role="tab" aria-selected="true" aria-controls="ajustes-panel-agente">Agente</button>
474
- <button type="button" class="ajustes-tab" id="ajustes-tab-sesiones" data-panel="sesiones" role="tab" aria-selected="false" aria-controls="ajustes-panel-sesiones">Sesiones</button>
475
- <button type="button" class="ajustes-tab" id="ajustes-tab-tareas" data-panel="tareas" role="tab" aria-selected="false" aria-controls="ajustes-panel-tareas">Tareas</button>
476
- <button type="button" class="ajustes-tab" id="ajustes-tab-limites" data-panel="limites" role="tab" aria-selected="false" aria-controls="ajustes-panel-limites">Límites</button>
477
- </div>
478
- <div class="ajustes-group" data-group="contexto" role="presentation">
479
- <span class="ajustes-group-title" aria-hidden="true">Contexto</span>
480
- <button type="button" class="ajustes-tab" id="ajustes-tab-perfil" data-panel="perfil" role="tab" aria-selected="false" aria-controls="ajustes-panel-perfil">Perfil</button>
481
- <button type="button" class="ajustes-tab" id="ajustes-tab-memoria" data-panel="memoria" role="tab" aria-selected="false" aria-controls="ajustes-panel-memoria">Memoria</button>
482
- <button type="button" class="ajustes-tab" id="ajustes-tab-skills" data-panel="skills" role="tab" aria-selected="false" aria-controls="ajustes-panel-skills">Skills</button>
483
- <button type="button" class="ajustes-tab" id="ajustes-tab-agentes" data-panel="agentes" role="tab" aria-selected="false" aria-controls="ajustes-panel-agentes">Subagentes</button>
484
- <button type="button" class="ajustes-tab" id="ajustes-tab-mcp" data-panel="mcp" role="tab" aria-selected="false" aria-controls="ajustes-panel-mcp">MCP</button>
485
- </div>
486
- <div class="ajustes-group" data-group="canales" role="presentation">
487
- <span class="ajustes-group-title" aria-hidden="true">Voz y canales</span>
488
- <button type="button" class="ajustes-tab" id="ajustes-tab-voz" data-panel="voz" role="tab" aria-selected="false" aria-controls="ajustes-panel-voz">Voz</button>
489
- <button type="button" class="ajustes-tab" id="ajustes-tab-transcripcion" data-panel="transcripcion" role="tab" aria-selected="false" aria-controls="ajustes-panel-transcripcion">Transcripción</button>
490
- <button type="button" class="ajustes-tab" id="ajustes-tab-asesor" data-panel="asesor" role="tab" aria-selected="false" aria-controls="ajustes-panel-asesor">Asesor</button>
491
- <button type="button" class="ajustes-tab" id="ajustes-tab-telegram" data-panel="telegram" role="tab" aria-selected="false" aria-controls="ajustes-panel-telegram">Telegram</button>
492
- </div>
493
- <div class="ajustes-group" data-group="interfaz" role="presentation">
494
- <span class="ajustes-group-title" aria-hidden="true">Interfaz</span>
495
- <button type="button" class="ajustes-tab" id="ajustes-tab-apariencia" data-panel="apariencia" role="tab" aria-selected="false" aria-controls="ajustes-panel-apariencia">Apariencia</button>
496
- </div>
497
- </nav>
498
- <p id="ajustes-empty" class="ajustes-empty muted" hidden>sin resultados</p>
499
- </aside>
500
- <div class="ajustes-main">
501
- <button type="button" id="ajustes-back" class="ajustes-back" aria-label="volver a la lista de ajustes">
502
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6" /></svg>
503
- <span>ajustes</span>
504
- </button>
505
-
506
- <div class="panel ajustes-panel active" id="ajustes-panel-agente" data-panel="agente" role="tabpanel" aria-labelledby="ajustes-tab-agente">
507
- <div class="row between">
508
- <h2>Agente</h2>
509
- </div>
510
- <p class="muted verify-line">
511
- Modo, proveedor, modelo y aprobación automática del agente.
512
- </p>
513
- <div id="agent-controls-slot">
514
- <div id="agent-settings-controls">
515
- <label for="mode">Modo</label>
516
- <select id="mode" title="modo de agente"></select>
517
- <label for="provider">Proveedor</label>
518
- <select id="provider" title="proveedor"></select>
519
- <label for="model">Modelo</label>
520
- <div class="model-row">
521
- <select id="model" title="modelo"></select>
522
- <button type="button" id="model-pick" class="btn" title="elegir qué modelos mostrar">elegir</button>
523
- </div>
524
- <label for="effort">Esfuerzo de razonamiento</label>
525
- <select id="effort" title="cuánto piensa el modelo antes de responder">
526
- <option value="">auto (default del modelo)</option>
527
- <option value="off">off · sin razonamiento</option>
528
- <option value="low">low · bajo</option>
529
- <option value="medium">medium · medio</option>
530
- <option value="high">high · alto</option>
531
- <option value="max">max · máximo</option>
532
- </select>
533
- <label class="check" title="aprobar automáticamente tools con permiso ask">
534
- <input id="yes" type="checkbox" /> Aprobar automáticamente las tools (auto)
535
- </label>
536
- <label class="check" title="el agente hace exactamente lo pedido, sin mejoras ni cambios fuera del pedido">
537
- <input id="strict-scope" type="checkbox" /> Hacer solo lo pedido (estricto)
538
- </label>
539
- </div>
540
- </div>
541
- <div class="row">
542
- <button type="button" id="agent-settings-open" class="btn">abrir en ventana</button>
543
- <button type="button" id="agent-providers" class="btn">gestionar proveedores</button>
544
- </div>
545
- </div>
546
-
547
- <div class="panel ajustes-panel" id="ajustes-panel-perfil" data-panel="perfil" role="tabpanel" aria-labelledby="ajustes-tab-perfil">
548
- <div class="row between">
549
- <h2>Perfil</h2>
550
- <span id="perfil-status" class="summary">cargando…</span>
551
- </div>
552
- <p class="muted verify-line">
553
- Datos que el agente conoce de vos. Viven en <code>app.json</code> (la casa) y se
554
- inyectan en el prompt de todas las corridas.
555
- </p>
556
- <label class="field"><span>cómo querés que te llame</span>
557
- <input id="perfil-address" spellcheck="false" placeholder="Señor · Amo · Amigo · Compañero · Daniel…" />
558
- </label>
559
- <label class="field"><span>nombre</span>
560
- <input id="perfil-name" spellcheck="false" placeholder="Daniel" />
561
- </label>
562
- <label class="field"><span>idioma preferido</span>
563
- <input id="perfil-language" spellcheck="false" placeholder="es" />
564
- </label>
565
- <label class="field"><span>notas</span>
566
- <textarea id="perfil-notes" rows="3" placeholder="Preferencias, contexto, cómo te gusta trabajar…"></textarea>
567
- </label>
568
- <div class="row between">
569
- <span class="muted">datos extra</span>
570
- <button type="button" id="perfil-add-field" class="btn">+ agregar</button>
571
- </div>
572
- <div id="perfil-fields" class="perfil-fields"></div>
573
- <div class="row">
574
- <button id="perfil-save" class="btn primary" type="button">guardar</button>
575
- <span id="perfil-msg" class="summary"></span>
576
- </div>
577
- </div>
578
-
579
- <div class="panel ajustes-panel" id="ajustes-panel-sesiones" data-panel="sesiones" role="tabpanel" aria-labelledby="ajustes-tab-sesiones">
580
- <div class="row between">
581
- <h2>Sesiones</h2>
582
- </div>
583
- <p class="muted verify-line">
584
- Carpeta donde se abre el selector de carpetas y <code>/new</code> cuando no hay sesión.
585
- </p>
586
- <label for="default-cwd">Carpeta predeterminada para nuevas sesiones</label>
587
- <div class="row default-cwd-row">
588
- <input id="default-cwd" type="text" readonly placeholder="(la casa del usuario)" title="carpeta donde se abre el selector y /new sin sesión" />
589
- <button type="button" id="default-cwd-pick" class="btn">elegir</button>
590
- <button type="button" id="default-cwd-clear" class="btn icon" title="quitar la carpeta predeterminada" aria-label="quitar la carpeta predeterminada">
591
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
592
- <path d="M18 6 6 18M6 6l12 12" />
593
- </svg>
594
- </button>
595
- </div>
596
- </div>
597
-
598
- <div class="panel ajustes-panel" id="ajustes-panel-mcp" data-panel="mcp" role="tabpanel" aria-labelledby="ajustes-tab-mcp">
599
- <div class="row between">
600
- <h2>Servidores MCP</h2>
601
- <div class="row">
602
- <button id="mcp-reload" class="btn">recargar</button>
603
- <button id="mcp-new" class="btn">+ nuevo</button>
604
- </div>
605
- </div>
606
- <p class="muted verify-line">
607
- Se combinan con <code>mcp[]</code> de <code>tamnora.json</code>; los guardados acá viven en la base.
608
- </p>
609
- <div class="import-bar">
610
- <label class="field grow"><span>carga de herramientas MCP</span>
611
- <select id="mcp-mode">
612
- <option value="auto">auto (diferir si hay muchas)</option>
613
- <option value="lazy">diferido (índice + mcp_call)</option>
614
- <option value="eager">completas (todas en cada turno)</option>
615
- </select>
616
- </label>
617
- <span id="mcp-count" class="muted"></span>
618
- </div>
619
- <div class="import-bar">
620
- <input id="mcp-pinned" aria-label="tools/servidores MCP fijados" placeholder="tools/servidores fijos (coma), ej. mcp__postgres__query" spellcheck="false" />
621
- <button id="mcp-pinned-save" class="btn" type="button">fijar</button>
622
- </div>
623
- <div class="import-bar">
624
- <input id="mcp-import" aria-label="importar servidor MCP" placeholder="nombre del registro oficial, URL o JSON (bloque mcpServers)" spellcheck="false" />
625
- <button id="mcp-import-btn" class="btn" type="button">importar</button>
626
- </div>
627
- <div class="import-bar">
628
- <input id="mcp-search" aria-label="buscar en el registro de MCP" placeholder="buscar en el registro oficial (ej. github, postgres)" spellcheck="false" />
629
- <button id="mcp-search-btn" class="btn" type="button">buscar</button>
630
- </div>
631
- <div id="mcp-import-status" class="summary"></div>
632
- <div id="mcp-search-status" class="summary"></div>
633
- <ul id="mcp-search-results" class="providers"></ul>
634
- <ul id="mcp-list" class="providers"></ul>
635
- <details class="catalog">
636
- <summary>sugeridos</summary>
637
- <ul id="mcp-catalog" class="providers"></ul>
638
- </details>
639
- </div>
640
-
641
- <div class="panel ajustes-panel" id="ajustes-panel-skills" data-panel="skills" role="tabpanel" aria-labelledby="ajustes-tab-skills">
642
- <div class="row between">
643
- <h2>Skills</h2>
644
- <button id="skill-new" class="btn">+ nueva</button>
645
- </div>
646
- <p class="muted verify-line">
647
- Instrucciones especializadas en la casa de Tamnora (<code>skills/</code>). Las habilitadas se inyectan al system prompt.
648
- </p>
649
- <div class="import-bar">
650
- <input id="skill-import" aria-label="importar skill" placeholder="https://skills.sh/owner/repo/skill" spellcheck="false" />
651
- <button id="skill-import-btn" class="btn" type="button">importar</button>
652
- </div>
653
- <div class="import-bar">
654
- <input id="skill-search" aria-label="buscar skills en GitHub" placeholder="buscar skills en GitHub (ej. testing, react)" spellcheck="false" />
655
- <button id="skill-search-btn" class="btn" type="button">buscar</button>
656
- </div>
657
- <div id="skill-import-status" class="summary"></div>
658
- <div id="skill-search-status" class="summary"></div>
659
- <ul id="skill-search-results" class="providers"></ul>
660
- <ul id="skill-list" class="providers"></ul>
661
- <details class="catalog">
662
- <summary>sugeridas</summary>
663
- <ul id="skill-catalog" class="providers"></ul>
664
- </details>
665
- </div>
666
-
667
- <div class="panel ajustes-panel" id="ajustes-panel-agentes" data-panel="agentes" role="tabpanel" aria-labelledby="ajustes-tab-agentes">
668
- <div class="row between">
669
- <h2>Subagentes</h2>
670
- <button id="agent-new" class="btn">+ nuevo</button>
671
- </div>
672
- <p class="muted verify-line">
673
- Subagentes definidos en <code>agents/</code> (la casa) o <code>.tamnora/agents/</code> (el proyecto).
674
- El agente principal los ve como catálogo y delega con <code>task</code>; cada uno aporta su
675
- modelo, modo, tools, skills y esfuerzo.
676
- </p>
677
- <div id="agent-status" class="summary"></div>
678
- <ul id="agent-list" class="providers"></ul>
679
- </div>
680
-
681
- <div class="panel ajustes-panel" id="ajustes-panel-memoria" data-panel="memoria" role="tabpanel" aria-labelledby="ajustes-tab-memoria">
682
- <div class="row between">
683
- <h2>Memoria</h2>
684
- <button id="memory-new" class="btn">+ tema</button>
685
- </div>
686
- <p class="muted verify-line">
687
- Lo que el proyecto recuerda entre sesiones (<code>.tamnora/memory/</code>). El agente la mantiene con la
688
- herramienta <code>memory</code>; acá la podés revisar y editar.
689
- </p>
690
- <label class="field"><span>índice (MEMORY.md)</span>
691
- <textarea id="memory-index" rows="6" spellcheck="false" placeholder="# Memoria del proyecto"></textarea>
692
- </label>
693
- <div class="row between settings-actions">
694
- <span id="memory-status" class="summary"></span>
695
- <button id="memory-save-index" class="btn" type="button">guardar índice</button>
696
- </div>
697
- <ul id="memory-list" class="providers"></ul>
698
- </div>
699
-
700
- <div class="panel ajustes-panel" id="ajustes-panel-tareas" data-panel="tareas" role="tabpanel" aria-labelledby="ajustes-tab-tareas">
701
- <div class="row between">
702
- <h2>Tareas</h2>
703
- <button id="schedule-new" class="btn">+ tarea</button>
704
- </div>
705
- <p class="muted verify-line">
706
- Trabajos recurrentes que corren solos mientras el server está vivo. <code>agent</code> corre un turno
707
- (con las tools y permisos configurados); <code>shell</code> ejecuta un comando permitido.
708
- </p>
709
- <div id="schedule-status" class="summary"></div>
710
- <ul id="schedule-list" class="providers"></ul>
711
- </div>
712
-
713
- <div class="panel ajustes-panel" id="ajustes-panel-telegram" data-panel="telegram" role="tabpanel" aria-labelledby="ajustes-tab-telegram">
714
- <div class="row between">
715
- <h2>Telegram</h2>
716
- <span id="tg-status" class="summary">cargando…</span>
717
- </div>
718
- <p class="muted verify-line">
719
- Bot por long polling (no necesita URL pública). El token vive en <code>app.json</code>
720
- (la casa) y nunca se muestra completo.
721
- </p>
722
- <label class="field"><span>token del bot</span>
723
- <input id="tg-token" type="password" autocomplete="off" spellcheck="false"
724
- placeholder="123456:ABC… (dejar vacío para no cambiar)" />
725
- </label>
726
- <label class="field"><span>chats autorizados (ids separados por coma)</span>
727
- <input id="tg-chats" spellcheck="false" placeholder="123456789, -1001234567890" />
728
- </label>
729
- <label class="field"><span>proveedor de transcripción (voz, opcional)</span>
730
- <input id="tg-transcribe-provider" spellcheck="false"
731
- placeholder="openai · groq — vacío = proveedor activo" />
732
- </label>
733
- <label class="field"><span>modelo de transcripción (voz)</span>
734
- <input id="tg-transcribe" spellcheck="false" placeholder="whisper-1" />
735
- </label>
736
- <label class="check" title="avisar y pedir permisos por Telegram de los proyectos abiertos en la consola">
737
- <input id="tg-notify" type="checkbox" /> Avisar por Telegram (proyectos abiertos en la consola)
738
- </label>
739
- <div class="voz-announce">
740
- <span class="muted">qué avisa (siempre con el nombre del proyecto):</span>
741
- <label class="check" title="resumen al terminar un turno">
742
- <input id="tg-notify-done" type="checkbox" /> el fin de turno
743
- </label>
744
- <label class="check" title="cuando un run falla">
745
- <input id="tg-notify-error" type="checkbox" /> los errores
746
- </label>
747
- <label class="check" title="cuando el agente pide una aprobación">
748
- <input id="tg-notify-permission" type="checkbox" /> los permisos
749
- </label>
750
- <label class="check" title="resultado de subagentes (tool task)">
751
- <input id="tg-notify-agent" type="checkbox" /> los subagentes
752
- </label>
753
- </div>
754
- <p class="muted verify-line">
755
- Solo avisa de los proyectos que tengas <b>abiertos como pestañas</b> en la consola.
756
- </p>
757
- <details class="tg-help">
758
- <summary>¿Cómo habilito mi chat?</summary>
759
- <ol>
760
- <li>Creá el bot con <b>@BotFather</b> (<code>/newbot</code>) y pegá el token acá.</li>
761
- <li>Guardá y mandale cualquier mensaje al bot: si tu chat no está autorizado, te
762
- responde tu <b>chat id</b>.</li>
763
- <li>Agregá ese id a <strong>chats autorizados</strong> y volvé a guardar.</li>
764
- </ol>
765
- </details>
766
- <div class="row">
767
- <button id="tg-save" class="btn primary" type="button">guardar</button>
768
- <button id="tg-test" class="btn" type="button">probar token</button>
769
- <span id="tg-msg" class="summary"></span>
770
- </div>
771
- </div>
772
-
773
- <div class="panel ajustes-panel" id="ajustes-panel-voz" data-panel="voz" role="tabpanel" aria-labelledby="ajustes-tab-voz">
774
- <div class="row between">
775
- <h2>Voz</h2>
776
- <span id="voz-status" class="summary">cargando…</span>
777
- </div>
778
- <p class="muted verify-line">
779
- El agente cuenta lo que hace con frases cortas (voces neuronales de Edge, sin API
780
- key): arranque, herramientas y cierre. No lee la respuesta entera. En Telegram llega
781
- como nota de voz al terminar.
782
- </p>
783
- <label class="check" title="hablar mientras trabaja">
784
- <input id="voz-enabled" type="checkbox" /> Hablar mientras trabaja
785
- </label>
786
- <label class="field"><span>voz</span>
787
- <select id="voz-voice" title="voz del catálogo de Edge"></select>
788
- </label>
789
- <label class="field"><span>velocidad</span>
790
- <div class="voz-range">
791
- <input id="voz-rate" type="range" min="-100" max="100" step="5" value="0" title="velocidad del habla (%)" />
792
- <output id="voz-rate-val" for="voz-rate">normal</output>
793
- </div>
794
- </label>
795
- <label class="field"><span>tono</span>
796
- <div class="voz-range">
797
- <input id="voz-pitch" type="range" min="-50" max="50" step="5" value="0" title="tono de la voz (Hz)" />
798
- <output id="voz-pitch-val" for="voz-pitch">normal</output>
799
- </div>
800
- </label>
801
- <div class="voz-announce">
802
- <span class="muted">qué cuenta:</span>
803
- <label class="check" title="avisar al arrancar el turno">
804
- <input id="voz-ann-start" type="checkbox" /> el arranque
805
- </label>
806
- <label class="check" title="avisar las herramientas (como mucho una cada 20 s)">
807
- <input id="voz-ann-tools" type="checkbox" /> las herramientas
808
- </label>
809
- <label class="check" title="avisar al terminar">
810
- <input id="voz-ann-end" type="checkbox" /> el cierre
811
- </label>
812
- <label class="check" title="leer los consejos y el resumen del asesor">
813
- <input id="voz-ann-advice" type="checkbox" /> los consejos/resumen del asesor
814
- </label>
815
- <label class="check" title="leer un resumen breve de lo que hizo el agente">
816
- <input id="voz-ann-summary" type="checkbox" /> Explicar resumidamente qué hizo
817
- </label>
818
- <label class="check" title="leer la respuesta final del agente, sin bloques de código ni tablas">
819
- <input id="voz-ann-answer" type="checkbox" /> la respuesta final (sin código)
820
- </label>
821
- </div>
822
- <p id="voz-ffmpeg" class="muted" hidden>
823
- Sin <code>ffmpeg</code> en el PATH, en Telegram llega como audio MP3 en vez de nota
824
- de voz.
825
- </p>
826
- <div class="row">
827
- <button id="voz-save" class="btn primary" type="button">guardar</button>
828
- <button id="voz-test" class="btn" type="button">probar voz</button>
829
- <span id="voz-msg" class="summary"></span>
830
- </div>
831
- </div>
832
-
833
- <div class="panel ajustes-panel" id="ajustes-panel-asesor" data-panel="asesor" role="tabpanel" aria-labelledby="ajustes-tab-asesor">
834
- <div class="row between">
835
- <h2>Asesor</h2>
836
- <span id="asesor-status" class="summary">cargando…</span>
837
- </div>
838
- <p class="muted verify-line">
839
- Un asesor que observa lo que el agente hace, lo interpreta y solo habla cuando tiene
840
- algo que aportar: te alerta de un problema en el código, te explica un proceso o te da
841
- un consejo. No lee la respuesta del agente. Usa el proveedor y modelo activos, o unos propios.
842
- </p>
843
- <div class="row">
844
- <label class="field"><span>proveedor (opcional)</span>
845
- <input id="asesor-provider" spellcheck="false" placeholder="por defecto: el activo" />
846
- </label>
847
- <label class="field grow"><span>modelo (opcional)</span>
848
- <input id="asesor-model" spellcheck="false" placeholder="por defecto: el activo" />
849
- </label>
850
- </div>
851
- <label class="check" title="tener un asesor que interprete y comente el turno">
852
- <input id="asesor-enabled" type="checkbox" /> Habilitar el asesor
853
- </label>
854
- <div class="voz-announce">
855
- <span class="muted">qué aporta:</span>
856
- <label class="check" title="alertar de problemas reales en el código">
857
- <input id="asesor-code" type="checkbox" /> problemas en el código
858
- </label>
859
- <label class="check" title="explicar procesos o decisiones">
860
- <input id="asesor-explain" type="checkbox" /> explicar procesos
861
- </label>
862
- <label class="check" title="dar consejos concretos">
863
- <input id="asesor-advice" type="checkbox" /> consejos
864
- </label>
865
- <label class="check" title="resumir en una o dos frases qué pidió y qué se hizo">
866
- <input id="asesor-summary" type="checkbox" /> resumir qué hizo
867
- </label>
868
- </div>
869
- <label class="field"><span>largo de cada consejo (palabras)</span>
870
- <input id="asesor-words" type="number" min="10" max="120" step="5" value="40" />
871
- </label>
872
- <label class="field"><span>instrucciones para el asesor (opcional)</span>
873
- <textarea id="asesor-notes" rows="3" placeholder="Ej: avisame siempre si toco secretos o config de producción."></textarea>
874
- </label>
875
- <div class="row">
876
- <button id="asesor-save" class="btn primary" type="button">guardar</button>
877
- <button id="asesor-test" class="btn" type="button">probar asesor</button>
878
- <span id="asesor-msg" class="summary"></span>
879
- </div>
880
- </div>
881
-
882
- <div class="panel ajustes-panel" id="ajustes-panel-transcripcion" data-panel="transcripcion" role="tabpanel" aria-labelledby="ajustes-tab-transcripcion">
883
- <div class="row between">
884
- <h2>Transcripción</h2>
885
- <span id="tr-status" class="summary">cargando…</span>
886
- </div>
887
- <p class="muted verify-line">
888
- Audio → texto para el <b>dictado del chat</b> (micrófono del input) y las notas de voz
889
- de Telegram.
890
- </p>
891
- <label class="field"><span>motor</span>
892
- <select id="tr-mode" title="cómo se transcribe el dictado del chat">
893
- <option value="server">Servidor · proveedor de transcripción</option>
894
- <option value="google">Servidor · Google (sin API key)</option>
895
- <option value="browser">Navegador · nativo (Web Speech API)</option>
896
- </select>
897
- </label>
898
- <label class="field"><span>idioma del dictado (se le indica al motor para no confundirse)</span>
899
- <input id="tr-language" spellcheck="false" placeholder="es-AR" />
900
- </label>
901
- <div id="tr-server-fields">
902
- <label class="field"><span>proveedor de transcripción (vacío = proveedor activo)</span>
903
- <input id="tr-provider" spellcheck="false" placeholder="groq · openai" />
904
- </label>
905
- <label class="field"><span>modelo</span>
906
- <input id="tr-model" spellcheck="false" placeholder="whisper-large-v3-turbo" />
907
- </label>
908
- <p class="muted verify-line">
909
- El proveedor activo no siempre sabe transcribir (p. ej. DeepSeek), así que conviene
910
- apuntar a uno con <code>/audio/transcriptions</code> (Groq, OpenAI…). Si lo dejás
911
- vacío se usa lo configurado en <b>Telegram</b> y, si tampoco hay, el proveedor activo
912
- con <code>whisper-1</code>.
913
- </p>
914
- </div>
915
- <p class="muted verify-line" id="tr-google-note" hidden>
916
- Usa el servicio de voz de Chromium/Google <b>desde el servidor</b>, sin API key ni
917
- depender del navegador (servicio no oficial, como el TTS de Edge). Configurá el idioma.
918
- </p>
919
- <p class="muted verify-line" id="tr-browser-note" hidden>
920
- El navegador transcribe en vivo (Chrome/Edge) sin usar el servidor. Necesita el
921
- servicio de voz de Google; en Brave o sin internet suele fallar (error «network»).
922
- </p>
923
- <div class="row">
924
- <button id="tr-save" class="btn primary" type="button">guardar</button>
925
- <button id="tr-test" class="btn icon" type="button" title="grabar y ver qué devuelve la transcripción" aria-label="probar transcripción">
926
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
927
- <path d="M12 19v3" />
928
- <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
929
- <path d="M12 15a3 3 0 0 0 3-3V6a3 3 0 0 0-6 0v6a3 3 0 0 0 3 3Z" />
930
- </svg>
931
- <span class="mic-count" hidden></span>
932
- </button>
933
- <span id="tr-msg" class="summary"></span>
934
- </div>
935
- <p id="tr-test-result" class="summary"></p>
936
- </div>
937
-
938
- <div class="panel ajustes-panel" id="ajustes-panel-limites" data-panel="limites" role="tabpanel" aria-labelledby="ajustes-tab-limites">
939
- <div class="row between">
940
- <h2>Límites del agente</h2>
941
- </div>
942
- <p class="muted verify-line">
943
- El agente trabaja hasta terminar solo (<code>end_turn</code>). Estos topes son
944
- opcionales: <code>0</code> = sin límite. Al cortarse, cierra con un resumen.
945
- </p>
946
- <label class="field"><span>máximo de rondas de herramientas (0 = sin límite)</span>
947
- <input id="limits-rounds" type="number" min="0" step="1" placeholder="0" />
948
- </label>
949
- <label class="field"><span>presupuesto de tokens (0 = sin límite)</span>
950
- <input id="limits-tokens" type="number" min="0" step="1000" placeholder="0" />
951
- </label>
952
- <label class="field"><span>presupuesto de tiempo en segundos (0 = sin límite)</span>
953
- <input id="limits-duration" type="number" min="0" step="1" placeholder="0" />
954
- </label>
955
- <label class="field"><span>máximo de tokens de salida por respuesta (0 = default del proveedor)</span>
956
- <input id="limits-output" type="number" min="0" step="256" placeholder="0" />
957
- </label>
958
- <label class="check" title="reusa el prompt procesado entre rondas: ahorra costo y latencia en conversaciones largas (según el proveedor)">
959
- <input id="limits-caching" type="checkbox" /> prompt caching
960
- </label>
961
- <div class="row">
962
- <button id="limits-save" class="btn primary" type="button">guardar</button>
963
- <span id="limits-msg" class="summary"></span>
964
- </div>
965
- </div>
966
-
967
- <div class="panel ajustes-panel" id="ajustes-panel-apariencia" data-panel="apariencia" role="tabpanel" aria-labelledby="ajustes-tab-apariencia">
968
- <div class="row between">
969
- <h2>Apariencia</h2>
970
- </div>
971
- <p class="muted verify-line">
972
- Tema, tipografía y tamaño de la consola. Se guardan en tu casa y te siguen entre
973
- dispositivos.
974
- </p>
975
- <label class="field"><span>tema</span></label>
976
- <div id="apariencia-themes" class="theme-grid"></div>
977
- <label class="field"><span>tipografía</span>
978
- <select id="apariencia-font"></select>
979
- </label>
980
- <div id="apariencia-preview" class="apariencia-preview">
981
- <strong>Tamnora Harness</strong>
982
- <span>El veloz murciélago hindú comía feliz cardillo y kiwi.</span>
983
- <code>const saludo = 'hola';</code>
984
- </div>
985
- <div class="voz-range">
986
- <span class="muted">tamaño del chat</span>
987
- <input id="apariencia-chat-font" aria-label="tamano del texto del chat" type="range" min="12" max="20" step="0.5" />
988
- <output id="apariencia-chat-font-out">14.5px</output>
989
- </div>
990
- <div class="voz-range">
991
- <span class="muted">escala de la interfaz</span>
992
- <input id="apariencia-scale" aria-label="escala general de la interfaz" type="range" min="80" max="130" step="5" />
993
- <output id="apariencia-scale-out">100%</output>
994
- </div>
995
- <div class="row">
996
- <button id="apariencia-reset" class="btn" type="button">restablecer</button>
997
- <span id="apariencia-msg" class="summary"></span>
998
- </div>
999
- </div>
1000
- </div>
1001
- </div>
1002
- </section>
1003
-
1004
- <dialog id="folder-modal" class="modal folder-modal">
1005
- <div class="modal-card">
1006
- <div class="row between">
1007
- <h3>carpeta de trabajo de la sesión</h3>
1008
- <button type="button" id="folder-close" class="btn icon" title="cerrar" aria-label="cerrar">
1009
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1010
- <path d="M18 6 6 18M6 6l12 12" />
1011
- </svg>
1012
- </button>
1013
- </div>
1014
- <div class="folder-toolbar">
1015
- <button type="button" id="folder-up" class="btn icon" title="subir un nivel" aria-label="subir un nivel">
1016
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1017
- <path d="m5 12 7-7 7 7M12 19V5" />
1018
- </svg>
1019
- </button>
1020
- <button type="button" id="folder-home" class="btn icon" title="ir al inicio" aria-label="ir al inicio">
1021
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1022
- <path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
1023
- <path d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
1024
- </svg>
1025
- </button>
1026
- <button type="button" id="folder-roots" class="btn icon" title="ir a las unidades" aria-label="ir a las unidades">
1027
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1028
- <path d="M2 12h20" />
1029
- <path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" />
1030
- <path d="M6 16h.01M10 16h.01" />
1031
- </svg>
1032
- </button>
1033
- <button type="button" id="folder-new" class="btn icon" title="nueva carpeta acá" aria-label="nueva carpeta acá">
1034
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1035
- <path d="M12 10v6M9 13h6" />
1036
- <path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
1037
- </svg>
1038
- </button>
1039
- <nav id="folder-crumbs" class="crumbs" aria-label="ruta actual"></nav>
1040
- </div>
1041
- <div class="folder-search">
1042
- <input id="folder-goto" aria-label="ir a una ruta o filtrar carpetas" spellcheck="false" placeholder="filtrar carpetas, o pegá una ruta y Enter" />
1043
- <button type="button" id="folder-go" class="btn">ir</button>
1044
- </div>
1045
- <div id="folder-create" class="folder-create" hidden>
1046
- <input id="folder-new-name" aria-label="nombre de la nueva carpeta" spellcheck="false" placeholder="nombre de la nueva carpeta" />
1047
- <button type="button" id="folder-new-create" class="btn primary">crear</button>
1048
- <button type="button" id="folder-new-cancel" class="btn">cancelar</button>
1049
- </div>
1050
- <ul id="folder-list" class="folders"></ul>
1051
- <div class="folder-footer">
1052
- <span id="folder-status" class="summary"></span>
1053
- <span class="spacer"></span>
1054
- <button type="button" id="folder-cancel" class="btn">cancelar</button>
1055
- <button type="button" id="folder-use" class="btn primary">usar esta carpeta</button>
1056
- </div>
1057
- </div>
1058
- </dialog>
1059
- </main>
1060
-
1061
- <dialog id="settings-modal" class="modal settings-modal">
1062
- <div class="modal-card">
1063
- <div class="row between">
1064
- <h3>modelo y modo del agente</h3>
1065
- <button type="button" id="settings-close" class="btn icon" title="cerrar" aria-label="cerrar">
1066
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1067
- <path d="M18 6 6 18M6 6l12 12" />
1068
- </svg>
1069
- </button>
1070
- </div>
1071
- <div id="settings-controls-slot"></div>
1072
- <div class="row between settings-actions">
1073
- <button type="button" id="settings-providers" class="btn">gestionar proveedores</button>
1074
- <button type="button" id="settings-done" class="btn primary">listo</button>
1075
- </div>
1076
- </div>
1077
- </dialog>
1078
-
1079
- <dialog id="mcp-modal" class="modal">
1080
- <form id="mcp-form" class="modal-card">
1081
- <div class="row between">
1082
- <h3 id="mcp-title">nuevo servidor MCP</h3>
1083
- <button type="button" id="mcp-close" class="btn icon" title="cerrar" aria-label="cerrar">
1084
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1085
- <path d="M18 6 6 18M6 6l12 12" />
1086
- </svg>
1087
- </button>
1088
- </div>
1089
- <div class="row">
1090
- <label class="field"><span>id</span><input id="mf-id" spellcheck="false" placeholder="echo" /></label>
1091
- <label class="field grow"><span>nombre</span><input id="mf-name" spellcheck="false" placeholder="Echo" /></label>
1092
- </div>
1093
- <div class="row">
1094
- <label class="field"><span>transporte</span>
1095
- <select id="mf-transport">
1096
- <option value="stdio">stdio</option>
1097
- <option value="http">http</option>
1098
- </select>
1099
- </label>
1100
- <label class="field grow" id="mf-command-field"><span>comando</span><input id="mf-command" spellcheck="false" placeholder="node" /></label>
1101
- <label class="field grow" id="mf-url-field" hidden><span>url</span><input id="mf-url" spellcheck="false" placeholder="https://…/mcp" /></label>
1102
- </div>
1103
- <div class="row">
1104
- <label class="field grow" id="mf-args-field"><span>args (separados por espacio)</span><input id="mf-args" spellcheck="false" placeholder="harness/scripts/mcp-echo-server.mjs" /></label>
1105
- </div>
1106
- <label class="field" id="mf-env-field"><span>env (KEY=valor por línea · usá <code>env:VAR</code> para no hardcodear secretos)</span>
1107
- <textarea id="mf-env" rows="3" spellcheck="false" placeholder="API_KEY=env:MY_API_KEY"></textarea>
1108
- </label>
1109
- <label class="check" id="mf-inherit-field"><input id="mf-inherit-env" type="checkbox" /> heredar todo el entorno (<code>inheritEnv</code>, solo MCPs de confianza)</label>
1110
- <label class="check"><input id="mf-enabled" type="checkbox" checked /> habilitado</label>
1111
- <div class="row between settings-actions">
1112
- <button type="button" id="mf-cancel" class="btn">cancelar</button>
1113
- <button type="submit" class="btn primary">guardar</button>
1114
- </div>
1115
- <div id="mf-status" class="summary"></div>
1116
- </form>
1117
- </dialog>
1118
-
1119
- <dialog id="skill-modal" class="modal">
1120
- <form id="skill-form" class="modal-card">
1121
- <div class="row between">
1122
- <h3 id="skill-title">nueva skill</h3>
1123
- <button type="button" id="skill-close" class="btn icon" title="cerrar" aria-label="cerrar">
1124
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1125
- <path d="M18 6 6 18M6 6l12 12" />
1126
- </svg>
1127
- </button>
1128
- </div>
1129
- <div class="row">
1130
- <label class="field"><span>id</span><input id="sf-id" spellcheck="false" placeholder="revisar-pr" /></label>
1131
- <label class="field grow"><span>nombre</span><input id="sf-name" spellcheck="false" placeholder="Revisar PR" /></label>
1132
- </div>
1133
- <label class="field"><span>descripción</span><input id="sf-description" spellcheck="false" placeholder="Cuándo conviene usarla" /></label>
1134
- <label class="field"><span>cuándo usarla (opcional)</span><input id="sf-when" spellcheck="false" placeholder="al revisar un pull request" /></label>
1135
- <label class="field"><span>cuerpo (markdown)</span>
1136
- <textarea id="sf-body" rows="10" spellcheck="false" placeholder="Pasos e instrucciones de la skill…"></textarea>
1137
- </label>
1138
- <div class="row between settings-actions">
1139
- <button type="button" id="sf-cancel" class="btn">cancelar</button>
1140
- <button type="submit" class="btn primary">guardar</button>
1141
- </div>
1142
- <div id="sf-status" class="summary"></div>
1143
- </form>
1144
- </dialog>
1145
-
1146
- <dialog id="agent-modal" class="modal">
1147
- <form id="agent-form" class="modal-card">
1148
- <div class="row between">
1149
- <h3 id="agent-title">nuevo subagente</h3>
1150
- <button type="button" id="agent-close" class="btn icon" title="cerrar" aria-label="cerrar">
1151
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1152
- <path d="M18 6 6 18M6 6l12 12" />
1153
- </svg>
1154
- </button>
1155
- </div>
1156
- <div class="row">
1157
- <label class="field"><span>id</span><input id="af-id" spellcheck="false" placeholder="revisor" /></label>
1158
- <label class="field grow"><span>nombre</span><input id="af-name" spellcheck="false" placeholder="Revisor" /></label>
1159
- </div>
1160
- <label class="field"><span>descripción (cuándo delegarle)</span><input id="af-description" spellcheck="false" placeholder="Revisa cambios y busca bugs" /></label>
1161
- <div class="row">
1162
- <label class="field"><span>proveedor (opcional)</span>
1163
- <select id="af-provider"><option value="">heredar</option></select>
1164
- </label>
1165
- <label class="field grow"><span>modelo (opcional)</span><input id="af-model" spellcheck="false" placeholder="heredar" /></label>
1166
- </div>
1167
- <div class="row">
1168
- <label class="field"><span>modo</span>
1169
- <select id="af-mode">
1170
- <option value="">heredar (plan)</option>
1171
- <option value="plan">plan (solo lectura)</option>
1172
- <option value="agent">agent (escritura)</option>
1173
- <option value="voice">voice (.md / .json)</option>
1174
- </select>
1175
- </label>
1176
- <label class="field"><span>esfuerzo</span>
1177
- <select id="af-effort">
1178
- <option value="">heredar</option>
1179
- <option value="off">off</option>
1180
- <option value="low">low</option>
1181
- <option value="medium">medium</option>
1182
- <option value="high">high</option>
1183
- <option value="max">max</option>
1184
- </select>
1185
- </label>
1186
- <label class="field"><span>max rondas</span><input id="af-rounds" type="number" min="0" placeholder="6" /></label>
1187
- </div>
1188
- <label class="field"><span>tools (coma · vacío = las del modo)</span><input id="af-tools" spellcheck="false" placeholder="read, grep, glob" /></label>
1189
- <label class="field"><span>skills (Ctrl/Cmd para elegir varias · vacío = todas)</span>
1190
- <select id="af-skills" multiple size="5"></select>
1191
- </label>
1192
- <label class="field"><span>instrucciones del subagente (markdown)</span>
1193
- <textarea id="af-body" rows="8" spellcheck="false" placeholder="Cómo encarar las tareas que le deleguen…"></textarea>
1194
- </label>
1195
- <div class="row between settings-actions">
1196
- <button type="button" id="af-cancel" class="btn">cancelar</button>
1197
- <button type="submit" class="btn primary">guardar</button>
1198
- </div>
1199
- <div id="af-status" class="summary"></div>
1200
- </form>
1201
- </dialog>
1202
-
1203
- <dialog id="memory-modal" class="modal">
1204
- <form id="memory-form" class="modal-card">
1205
- <div class="row between">
1206
- <h3 id="memory-title">nuevo tema</h3>
1207
- <button type="button" id="memory-close" class="btn icon" title="cerrar" aria-label="cerrar">
1208
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1209
- <path d="M18 6 6 18M6 6l12 12" />
1210
- </svg>
1211
- </button>
1212
- </div>
1213
- <div class="row">
1214
- <label class="field"><span>tema (slug)</span><input id="mem-name" spellcheck="false" placeholder="arquitectura" /></label>
1215
- <label class="field grow"><span>título</span><input id="mem-title" spellcheck="false" placeholder="Arquitectura" /></label>
1216
- </div>
1217
- <label class="field"><span>contenido (markdown)</span>
1218
- <textarea id="mem-body" rows="10" spellcheck="false" placeholder="Qué recordar del proyecto…"></textarea>
1219
- </label>
1220
- <div class="row between settings-actions">
1221
- <button type="button" id="mem-cancel" class="btn">cancelar</button>
1222
- <button type="submit" class="btn primary">guardar</button>
1223
- </div>
1224
- <div id="mem-status" class="summary"></div>
1225
- </form>
1226
- </dialog>
1227
-
1228
- <dialog id="schedule-modal" class="modal">
1229
- <form id="schedule-form" class="modal-card">
1230
- <div class="row between">
1231
- <h3 id="schedule-title">nueva tarea</h3>
1232
- <button type="button" id="schedule-close" class="btn icon" title="cerrar" aria-label="cerrar">
1233
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1234
- <path d="M18 6 6 18M6 6l12 12" />
1235
- </svg>
1236
- </button>
1237
- </div>
1238
- <div class="row">
1239
- <label class="field"><span>id</span><input id="sc-id" spellcheck="false" placeholder="resumen-diario" /></label>
1240
- <label class="field grow"><span>nombre</span><input id="sc-name" spellcheck="false" placeholder="Resumen diario" /></label>
1241
- </div>
1242
- <div class="row">
1243
- <label class="field"><span>tipo</span>
1244
- <select id="sc-type">
1245
- <option value="agent">agent</option>
1246
- <option value="shell">shell</option>
1247
- </select>
1248
- </label>
1249
- <label class="field grow"><span>cuándo</span>
1250
- <input id="sc-spec" spellcheck="false" placeholder="every 30 m · daily 09:00 · 0 6 * * 1" />
1251
- </label>
1252
- </div>
1253
- <label class="field" id="sc-agent-prompt-row"><span>prompt (agent)</span>
1254
- <textarea id="sc-prompt" rows="4" spellcheck="false" placeholder="Qué tiene que hacer el agente…"></textarea>
1255
- </label>
1256
- <label class="field" id="sc-agent-mode-row"><span>modo (agent)</span>
1257
- <select id="sc-mode">
1258
- <option value="agent">agent</option>
1259
- <option value="plan">plan</option>
1260
- </select>
1261
- </label>
1262
- <label class="field" id="sc-shell-cmd-row"><span>comando (shell)</span>
1263
- <input id="sc-command" spellcheck="false" placeholder="node · npm · pnpm" />
1264
- </label>
1265
- <label class="field" id="sc-shell-args-row"><span>argumentos (shell)</span>
1266
- <input id="sc-args" spellcheck="false" placeholder="run build" />
1267
- </label>
1268
- <label class="field"><span>carpeta (opcional)</span>
1269
- <input id="sc-cwd" spellcheck="false" placeholder="por defecto: la carpeta del server" />
1270
- </label>
1271
- <label class="check"><input id="sc-enabled" type="checkbox" checked /> activa</label>
1272
- <div class="row between settings-actions">
1273
- <button type="button" id="sc-cancel" class="btn">cancelar</button>
1274
- <button type="submit" class="btn primary">guardar</button>
1275
- </div>
1276
- <div id="sc-status" class="summary"></div>
1277
- </form>
1278
- </dialog>
1279
-
1280
- <dialog id="palette-modal" class="modal palette-modal">
1281
- <div class="palette">
1282
- <div class="palette-row">
1283
- <svg class="palette-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1284
- <circle cx="11" cy="11" r="8" />
1285
- <path d="m21 21-4.3-4.3" />
1286
- </svg>
1287
- <input id="palette-input" aria-label="buscar sesiones, mensajes y comandos" type="text" placeholder="buscar sesiones y mensajes, o escribí un comando…" spellcheck="false" autocomplete="off" />
1288
- <span class="palette-hint">esc</span>
1289
- </div>
1290
- <ul id="palette-list" class="palette-list"></ul>
1291
- </div>
1292
- </dialog>
1293
-
1294
- <dialog id="model-modal" class="modal model-modal">
1295
- <div class="modal-card">
1296
- <div class="row between">
1297
- <h3>elegir modelos</h3>
1298
- <button type="button" id="model-close" class="btn icon" title="cerrar" aria-label="cerrar">
1299
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1300
- <path d="M18 6 6 18M6 6l12 12" />
1301
- </svg>
1302
- </button>
1303
- </div>
1304
- <div class="model-search">
1305
- <input id="model-query" aria-label="buscar modelos" type="text" placeholder="buscar por nombre o id…" spellcheck="false" autocomplete="off" />
1306
- </div>
1307
- <div class="model-actions">
1308
- <button type="button" id="model-all" class="btn">todos</button>
1309
- <button type="button" id="model-none" class="btn">ninguno</button>
1310
- <button type="button" id="model-free" class="btn">solo gratis</button>
1311
- <span class="spacer"></span>
1312
- <span id="model-count" class="summary"></span>
1313
- </div>
1314
- <ul id="model-list" class="model-list"></ul>
1315
- <div class="row between settings-actions">
1316
- <span id="model-status" class="summary"></span>
1317
- <span class="spacer"></span>
1318
- <button type="button" id="model-cancel" class="btn">cancelar</button>
1319
- <button type="button" id="model-save" class="btn primary">guardar</button>
1320
- </div>
1321
- </div>
1322
- </dialog>
1323
-
1324
- <script type="module" src="app.js"></script>
1325
- </body>
1326
- </html>
1
+ <!doctype html>
2
+ <html lang="es">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Tamnora Console</title>
7
+ <link rel="icon" href="/favicon.ico" sizes="any" />
8
+ <link rel="apple-touch-icon" href="/icons/icon-192x192.png" />
9
+ <link rel="manifest" href="/manifest.webmanifest" />
10
+ <meta name="theme-color" content="#231a17" />
11
+ <script src="/boot.js"></script>
12
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
13
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
14
+ <link
15
+ id="tamnora-fonts"
16
+ rel="stylesheet"
17
+ href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Libre+Baskerville:wght@400;700&family=Manrope:wght@400;500;600;700&display=swap"
18
+ />
19
+ <link rel="stylesheet" href="styles.css" />
20
+ </head>
21
+ <body>
22
+ <header class="topbar">
23
+ <div class="brand"><span class="logo"><img src="/icons/logo.png" alt="Tamnora" /></span> <button id="back-chat" type="button" class="back-chat" title="volver al chat" aria-label="volver al chat" hidden><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6" /></svg><span>chat</span></button> <span id="topbar-section" class="topbar-section">chat</span> <span class="ver">v0.26.0</span></div>
24
+ <div class="settings">
25
+ <button id="new-session-top" type="button" class="btn topbar-new" title="nueva sesión" aria-label="nueva sesión">
26
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true">
27
+ <path d="M12 5v14M5 12h14" />
28
+ </svg>
29
+ <span class="topbar-new-label">nueva sesión</span>
30
+ </button>
31
+ <button id="search-toggle" type="button" class="btn icon" title="buscar (Ctrl/Cmd+K)" aria-label="buscar">
32
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
33
+ <circle cx="11" cy="11" r="8" />
34
+ <path d="m21 21-4.3-4.3" />
35
+ </svg>
36
+ </button>
37
+ <div class="menu">
38
+ <button id="more-tab" type="button" class="btn icon" title="más opciones" aria-haspopup="menu" aria-controls="more-menu" aria-expanded="false" aria-label="más opciones">
39
+ <svg viewBox="0 0 24 24" fill="currentColor" stroke="none" aria-hidden="true">
40
+ <circle cx="12" cy="5" r="1.7" />
41
+ <circle cx="12" cy="12" r="1.7" />
42
+ <circle cx="12" cy="19" r="1.7" />
43
+ </svg>
44
+ </button>
45
+ <div id="more-menu" class="menu-pop" role="menu" hidden>
46
+ <button type="button" class="active" data-tab="chat" role="menuitem">chat</button>
47
+ <button type="button" data-tab="providers" role="menuitem">proveedores</button>
48
+ <button type="button" data-tab="ajustes" role="menuitem">ajustes</button>
49
+ </div>
50
+ </div>
51
+ <button id="settings-toggle" type="button" class="btn icon" title="modelo, modo y auto (ventana rápida)" aria-label="modelo y modo del agente">
52
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
53
+ <path d="M21 4h-7M10 4H3M21 12h-9M8 12H3M21 20h-5M12 20H3" />
54
+ <path d="M14 2v4M8 10v4M16 18v4" />
55
+ </svg>
56
+ </button>
57
+ <button id="theme-toggle" type="button" class="btn icon" title="cambiar tema" aria-label="cambiar tema">
58
+ <svg class="ico-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
59
+ <circle cx="12" cy="12" r="4" />
60
+ <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41" />
61
+ </svg>
62
+ <svg class="ico-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
63
+ <path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" />
64
+ </svg>
65
+ </button>
66
+ </div>
67
+ </header>
68
+
69
+ <div id="update-banner" class="update-banner" hidden>
70
+ <span class="update-banner-icon" aria-hidden="true">⬆</span>
71
+ <span id="update-banner-text" class="update-banner-text"></span>
72
+ <div class="update-banner-actions">
73
+ <button id="update-run" type="button" class="btn primary">actualizar</button>
74
+ <button id="update-later" type="button" class="btn">más tarde</button>
75
+ </div>
76
+ </div>
77
+
78
+ <main>
79
+ <section id="view-chat" class="view active">
80
+ <aside id="sidebar" class="sidebar">
81
+ <div class="panel-head">
82
+ <span class="panel-head-title">sesiones</span>
83
+ <button id="sidebar-collapse" type="button" class="btn icon" title="ocultar panel de sesiones" aria-label="ocultar panel de sesiones">
84
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
85
+ <path d="m15 18-6-6 6-6" />
86
+ </svg>
87
+ </button>
88
+ </div>
89
+ <button id="new-session" class="btn block">+ nueva sesión</button>
90
+ <ul id="sessions" class="sessions"></ul>
91
+ <div class="rail-footer">
92
+ <span>workspace</span>
93
+ <b id="rail-cwd">…</b>
94
+ <a href="/landing" id="logout" class="rail-exit" title="cerrar la sesión visual">salir →</a>
95
+ </div>
96
+ </aside>
97
+ <div id="resizer-sidebar" class="resizer" title="ajustar" role="separator" aria-orientation="vertical"></div>
98
+ <div id="sidebar-backdrop" class="drawer-backdrop" hidden></div>
99
+ <div class="conversation">
100
+ <div id="tabbar" class="tabbar" hidden></div>
101
+ <div id="panes" class="panes">
102
+ <div id="messages" class="messages">
103
+ <div class="empty">
104
+ <div class="empty-brand"><span>Tamnora</span><span class="accent">Harness</span></div>
105
+ <p class="empty-sub">Elegí un proveedor y un modelo, y escribí tu primer mensaje.</p>
106
+ <div class="empty-hints">
107
+ <span class="hint"><kbd>/help</kbd> ver los comandos</span>
108
+ <span class="hint"><kbd>/new</kbd> abrir una sesión en otra carpeta</span>
109
+ <span class="hint"><kbd>/init</kbd> analizar el proyecto y crear AGENTS.md</span>
110
+ </div>
111
+ </div>
112
+ </div>
113
+ <div id="scroll-nav" class="scroll-nav" hidden>
114
+ <button id="scroll-top" type="button" class="scroll-btn" title="ir al inicio" aria-label="ir al inicio de la conversación">
115
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
116
+ <path d="m18 15-6-6-6 6" />
117
+ </svg>
118
+ </button>
119
+ <button id="scroll-down" type="button" class="scroll-btn" title="ir al final" aria-label="ir al final de la conversación">
120
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
121
+ <path d="m6 9 6 6 6-6" />
122
+ </svg>
123
+ </button>
124
+ </div>
125
+ </div>
126
+ <form id="chat-form" class="composer">
127
+ <div id="slash-menu" class="slash-menu" hidden></div>
128
+ <div id="toasts" class="toasts" aria-live="polite"></div>
129
+ <div id="attach-preview" class="attach-preview" hidden></div>
130
+ <div id="chat-queue" class="chat-queue" hidden></div>
131
+ <div id="audio-player" class="audio-player" hidden></div>
132
+ <div class="composer-top">
133
+ <button type="button" id="cwd-pill" class="ctrl-pill" title="carpeta de trabajo de la sesión">
134
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
135
+ <path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
136
+ </svg>
137
+ <span id="cwd-pill-label">sin carpeta</span>
138
+ <svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
139
+ </button>
140
+ <div class="ctrl-wrap">
141
+ <button type="button" id="mode-pill" class="ctrl-pill" aria-haspopup="menu" aria-expanded="false" title="permisos del agente">
142
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /></svg>
143
+ <span id="mode-pill-label">Workspace Write</span>
144
+ <svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
145
+ </button>
146
+ <div id="mode-menu" class="ctrl-menu" role="menu" hidden>
147
+ <button type="button" class="ctrl-menu-item" data-kind="read" role="menuitemradio" aria-checked="false">
148
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z" /><circle cx="12" cy="12" r="3" /></svg>
149
+ <span>Read Only</span>
150
+ <svg class="ctrl-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg>
151
+ </button>
152
+ <button type="button" class="ctrl-menu-item" data-kind="write" role="menuitemradio" aria-checked="false">
153
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" /><path d="m15 5 4 4" /></svg>
154
+ <span>Workspace Write</span>
155
+ <svg class="ctrl-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg>
156
+ </button>
157
+ <button type="button" class="ctrl-menu-item" data-kind="full" role="menuitemradio" aria-checked="false">
158
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /><path d="m9 12 2 2 4-4" /></svg>
159
+ <span>Full access</span>
160
+ <svg class="ctrl-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg>
161
+ </button>
162
+ <button type="button" class="ctrl-menu-item" data-kind="voice" role="menuitemradio" aria-checked="false" title="conversación por voz: solo notas .md y .json">
163
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19v3" /><path d="M19 10v2a7 7 0 0 1-14 0v-2" /><path d="M12 15a3 3 0 0 0 3-3V6a3 3 0 0 0-6 0v6a3 3 0 0 0 3 3Z" /></svg>
164
+ <span>Voz (.md / .json)</span>
165
+ <svg class="ctrl-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg>
166
+ </button>
167
+ </div>
168
+ </div>
169
+ </div>
170
+
171
+ <div class="composer-row">
172
+ <button type="button" id="attach-image" class="add-btn" title="Adjuntar imagen (o pegala / arrastrala)" aria-label="adjuntar imagen">
173
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14" /><path d="M12 5v14" /></svg>
174
+ </button>
175
+ <button type="button" id="mic-btn" class="add-btn" title="Dictar por voz (tocá para grabar y parar)" aria-label="dictar por voz">
176
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
177
+ <path d="M12 19v3" />
178
+ <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
179
+ <path d="M12 15a3 3 0 0 0 3-3V6a3 3 0 0 0-6 0v6a3 3 0 0 0 3 3Z" />
180
+ </svg>
181
+ <span class="mic-count" hidden></span>
182
+ </button>
183
+ <button type="button" id="hands-free-btn" class="add-btn" title="manos libres: conversación por voz" aria-label="manos libres" aria-pressed="false">
184
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
185
+ <path d="M4.9 19.1a10 10 0 0 1 0-14.2" />
186
+ <path d="M7.8 16.2a6 6 0 0 1 0-8.4" />
187
+ <circle cx="12" cy="12" r="2" />
188
+ <path d="M16.2 7.8a6 6 0 0 1 0 8.4" />
189
+ <path d="M19.1 4.9a10 10 0 0 1 0 14.2" />
190
+ </svg>
191
+ </button>
192
+ <input id="image-input" type="file" accept="image/*" multiple hidden />
193
+ <textarea id="chat-input" rows="1" placeholder="escribí un mensaje…" title="Enter envía · Shift+Enter salto" aria-label="mensaje para el asistente" spellcheck="false"></textarea>
194
+ <div class="ctrl-wrap">
195
+ <button type="button" id="effort-pill" class="ctrl-pill" aria-haspopup="menu" aria-expanded="false" title="esfuerzo de razonamiento">
196
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z" /></svg>
197
+ <span id="effort-pill-label">auto</span>
198
+ <svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
199
+ </button>
200
+ <div id="effort-menu" class="ctrl-menu" role="menu" hidden></div>
201
+ </div>
202
+ <button type="button" id="model-pill" class="ctrl-pill model-pill" title="proveedor y modelo activos">
203
+ <span id="model-pill-label">—</span>
204
+ <svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
205
+ </button>
206
+ <div class="composer-actions">
207
+ <button class="send-btn" type="submit" aria-label="enviar" title="enviar">
208
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m5 12 7-7 7 7" /><path d="M12 19V5" /></svg>
209
+ </button>
210
+ <button class="add-btn interrupt-btn" type="button" id="interrupt-btn" title="Interrumpir al agente y enviar este mensaje (Ctrl+Enter)" aria-label="interrumpir y enviar" hidden>
211
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z" /></svg>
212
+ </button>
213
+ <button class="add-btn" type="button" id="stop-btn" title="Detener la respuesta" aria-label="detener" hidden>
214
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="6" y="6" width="12" height="12" rx="2" /></svg>
215
+ </button>
216
+ </div>
217
+ </div>
218
+
219
+ <div id="statusbar" class="composer-status" title="estado del run" role="button" tabindex="0">
220
+ <span id="sb-activity" class="sb-activity" role="status" hidden></span>
221
+ <span id="sb-sync" class="sb-channel retrying" hidden title="sin sincronía con el server"></span>
222
+ <span id="sb-channel" class="sb-channel" hidden title="canal de eventos entre dispositivos"></span>
223
+ <span id="sb-rounds">listo</span>
224
+ <span class="spacer"></span>
225
+ <span id="sb-context" class="ctx muted" title="uso del contexto del modelo">
226
+ <span id="sb-tokens">tokens —</span>
227
+ <span class="ctx-bar" aria-hidden="true"><span id="sb-context-fill" class="ctx-fill"></span></span>
228
+ </span>
229
+ </div>
230
+ </form>
231
+ </div>
232
+
233
+ <div id="resizer-explorer" class="resizer" title="ajustar" role="separator" aria-orientation="vertical"></div>
234
+
235
+ <aside id="explorer" class="explorer" hidden>
236
+ <div class="explorer-head">
237
+ <div class="xtabs">
238
+ <button type="button" class="xtab is-active" data-xp="files">estructura</button>
239
+ <button type="button" class="xtab" data-xp="preview">preview</button>
240
+ <button type="button" class="xtab" data-xp="procs">procs</button>
241
+ <button type="button" class="xtab" data-xp="git">git</button>
242
+ </div>
243
+ <span class="spacer"></span>
244
+ <button id="explorer-refresh" type="button" class="btn icon" title="refrescar" aria-label="refrescar">
245
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
246
+ <path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" />
247
+ <path d="M21 3v5h-5" />
248
+ <path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" />
249
+ <path d="M8 16H3v5" />
250
+ </svg>
251
+ </button>
252
+ <button id="explorer-close" type="button" class="btn icon" title="cerrar" aria-label="cerrar el panel de archivos">
253
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
254
+ <path d="M18 6 6 18M6 6l12 12" />
255
+ </svg>
256
+ </button>
257
+ </div>
258
+
259
+ <div id="xp-files" class="xp-pane">
260
+ <div id="explorer-root" class="explorer-cwd" title=""></div>
261
+ <div class="explorer-filter">
262
+ <input id="explorer-filter" placeholder="filtrar por nombre…" aria-label="filtrar archivos por nombre" spellcheck="false" />
263
+ </div>
264
+ <ul id="explorer-tree" class="tree"></ul>
265
+ <div id="explorer-status" class="explorer-status"></div>
266
+ </div>
267
+
268
+ <div id="xp-preview" class="xp-pane" hidden>
269
+ <div class="xp-bar">
270
+ <input id="preview-url" placeholder="http://127.0.0.1:5173" aria-label="URL de la vista previa" spellcheck="false" />
271
+ <button id="preview-open" class="btn" type="button">abrir</button>
272
+ <button id="preview-tunnel" class="btn" type="button" title="Exponer el puerto en el tailnet (privado)">túnel</button>
273
+ <button id="preview-close" class="btn danger" type="button" hidden title="Cerrar el túnel/exposición remota">cerrar</button>
274
+ </div>
275
+ <div class="preview-tools">
276
+ <select id="preview-device" title="tamaño de pantalla" aria-label="tamaño de pantalla">
277
+ <option value="auto">adaptable</option>
278
+ <option value="390">móvil</option>
279
+ <option value="768">tablet</option>
280
+ <option value="1280">laptop</option>
281
+ <option value="1600">pc</option>
282
+ </select>
283
+ <span class="spacer"></span>
284
+ <button id="preview-max" class="btn icon" type="button" title="maximizar" aria-pressed="false" aria-label="maximizar la vista previa">
285
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
286
+ <path d="M8 3H5a2 2 0 0 0-2 2v3M21 8V5a2 2 0 0 0-2-2h-3M3 16v3a2 2 0 0 0 2 2h3M16 21h3a2 2 0 0 0 2-2v-3" />
287
+ </svg>
288
+ </button>
289
+ </div>
290
+ <div id="preview-status" class="explorer-status"></div>
291
+ <div id="preview-stage" class="preview-stage" data-device="auto">
292
+ <iframe id="preview-frame" class="preview-frame" title="vista previa" referrerpolicy="no-referrer"></iframe>
293
+ </div>
294
+ </div>
295
+
296
+ <div id="xp-procs" class="xp-pane" hidden>
297
+ <div class="xp-bar">
298
+ <button id="procs-detect" class="btn" type="button">recargar</button>
299
+ <span id="procs-cwd" class="muted grow"></span>
300
+ </div>
301
+ <div id="procs-chips" class="chips"></div>
302
+ <ul id="procs-list" class="providers"></ul>
303
+ <pre id="procs-log" class="procs-log"></pre>
304
+ </div>
305
+
306
+ <div id="xp-git" class="xp-pane" hidden>
307
+ <div class="xp-bar">
308
+ <span id="git-branch" class="git-branch">…</span>
309
+ <span id="git-sync" class="muted grow"></span>
310
+ </div>
311
+ <div class="explorer-filter">
312
+ <input id="git-filter" aria-label="filtrar archivos por nombre" placeholder="filtrar por nombre…" spellcheck="false" />
313
+ </div>
314
+ <ul id="git-list" class="git-list"></ul>
315
+ <div id="git-status" class="explorer-status"></div>
316
+ </div>
317
+ </aside>
318
+
319
+ <button id="sidebar-handle" type="button" class="panel-handle panel-handle-left" title="mostrar sesiones" aria-label="mostrar panel de sesiones" aria-pressed="false">
320
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
321
+ <path d="m9 18 6-6-6-6" />
322
+ </svg>
323
+ </button>
324
+ <button id="explorer-handle" type="button" class="panel-handle panel-handle-right" title="mostrar archivos" aria-label="mostrar panel de archivos" aria-pressed="false">
325
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
326
+ <path d="m15 18-6-6 6-6" />
327
+ </svg>
328
+ </button>
329
+
330
+ <dialog id="file-modal" class="modal file-modal">
331
+ <div class="modal-card">
332
+ <div class="row between">
333
+ <h3 id="file-title">archivo</h3>
334
+ <div class="file-actions">
335
+ <span id="file-meta" class="muted"></span>
336
+ <select id="file-device" class="file-device" title="tamaño de pantalla" aria-label="tamaño de pantalla" hidden>
337
+ <option value="auto">adaptable</option>
338
+ <option value="390">móvil</option>
339
+ <option value="768">tablet</option>
340
+ <option value="1280">laptop</option>
341
+ <option value="1600">pc</option>
342
+ </select>
343
+ <button id="file-max" type="button" class="btn icon" title="maximizar" aria-pressed="false" aria-label="maximizar la vista previa" hidden>
344
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
345
+ <path d="M8 3H5a2 2 0 0 0-2 2v3M21 8V5a2 2 0 0 0-2-2h-3M3 16v3a2 2 0 0 0 2 2h3M16 21h3a2 2 0 0 0 2-2v-3" />
346
+ </svg>
347
+ </button>
348
+ <button id="file-preview-toggle" type="button" class="btn" hidden>vista previa</button>
349
+ <button id="file-close" type="button" class="btn icon" title="cerrar" aria-label="cerrar">
350
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
351
+ <path d="M18 6 6 18M6 6l12 12" />
352
+ </svg>
353
+ </button>
354
+ </div>
355
+ </div>
356
+ <div id="file-code" class="code-view">
357
+ <pre id="file-gutter" class="code-gutter" aria-hidden="true"></pre>
358
+ <pre id="file-content" class="file-preview"></pre>
359
+ </div>
360
+ <div id="file-render" class="file-render text" hidden></div>
361
+ <div id="file-stage" class="preview-stage" data-device="auto" hidden>
362
+ <iframe id="file-frame" class="file-frame" title="vista previa del archivo"></iframe>
363
+ </div>
364
+ </div>
365
+ </dialog>
366
+
367
+ <dialog id="git-modal" class="modal file-modal">
368
+ <div class="modal-card">
369
+ <div class="row between">
370
+ <h3 id="git-title">cambios</h3>
371
+ <div class="file-actions">
372
+ <span id="git-meta" class="muted"></span>
373
+ <button id="git-close" type="button" class="btn icon" title="cerrar" aria-label="cerrar">
374
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
375
+ <path d="M18 6 6 18M6 6l12 12" />
376
+ </svg>
377
+ </button>
378
+ </div>
379
+ </div>
380
+ <pre id="git-diff" class="file-preview git-diff"></pre>
381
+ </div>
382
+ </dialog>
383
+ </section>
384
+
385
+ <section id="view-providers" class="view">
386
+ <div class="panel">
387
+ <div class="row between">
388
+ <h2>Proveedores</h2>
389
+ <button id="provider-new" class="btn">+ nuevo proveedor</button>
390
+ </div>
391
+ <ul id="provider-list" class="providers"></ul>
392
+ </div>
393
+
394
+ <dialog id="provider-modal" class="modal">
395
+ <form id="provider-form" class="modal-card">
396
+ <div class="row between">
397
+ <h3 id="provider-title">nuevo proveedor</h3>
398
+ <button type="button" id="pf-close" class="btn icon" title="cerrar" aria-label="cerrar">
399
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
400
+ <path d="M18 6 6 18M6 6l12 12" />
401
+ </svg>
402
+ </button>
403
+ </div>
404
+ <div class="row">
405
+ <label class="field"><span>id</span><input id="pf-id" spellcheck="false" placeholder="deepseek" /></label>
406
+ <label class="field"><span>nombre</span><input id="pf-label" spellcheck="false" placeholder="DeepSeek" /></label>
407
+ <label class="field grow"><span>base URL</span><input id="pf-baseurl" spellcheck="false" placeholder="https://api.deepseek.com/v1" /></label>
408
+ </div>
409
+ <div class="row">
410
+ <label class="field grow"><span>API key</span><input id="pf-key" type="password" spellcheck="false" placeholder="dejar vacío para no cambiar" /></label>
411
+ <label class="field grow"><span>modelos (coma, opcional)</span><input id="pf-models" spellcheck="false" placeholder="deepseek-chat, deepseek-reasoner" /></label>
412
+ </div>
413
+ <div class="row">
414
+ <button type="button" id="pf-test" class="btn">probar conexión</button>
415
+ <span class="spacer"></span>
416
+ <button type="button" id="pf-cancel" class="btn">cancelar</button>
417
+ <button type="submit" class="btn primary">guardar</button>
418
+ </div>
419
+ <div id="pf-status" class="summary"></div>
420
+ </form>
421
+ </dialog>
422
+ </section>
423
+
424
+ <section id="view-ajustes" class="view">
425
+ <div class="ajustes-wrap" data-level="list">
426
+ <aside class="ajustes-side">
427
+ <input id="ajustes-search" class="ajustes-search" type="search" placeholder="buscar en ajustes…" aria-label="buscar en ajustes" autocomplete="off" spellcheck="false" />
428
+ <nav class="ajustes-tabs" role="tablist" aria-orientation="vertical" aria-label="secciones de ajustes">
429
+ <div class="ajustes-group" data-group="trabajo" role="presentation">
430
+ <span class="ajustes-group-title" aria-hidden="true">Trabajo</span>
431
+ <button type="button" class="ajustes-tab active" id="ajustes-tab-agente" data-panel="agente" role="tab" aria-selected="true" aria-controls="ajustes-panel-agente">Agente</button>
432
+ <button type="button" class="ajustes-tab" id="ajustes-tab-sesiones" data-panel="sesiones" role="tab" aria-selected="false" aria-controls="ajustes-panel-sesiones">Sesiones</button>
433
+ <button type="button" class="ajustes-tab" id="ajustes-tab-tareas" data-panel="tareas" role="tab" aria-selected="false" aria-controls="ajustes-panel-tareas">Tareas</button>
434
+ <button type="button" class="ajustes-tab" id="ajustes-tab-limites" data-panel="limites" role="tab" aria-selected="false" aria-controls="ajustes-panel-limites">Límites</button>
435
+ </div>
436
+ <div class="ajustes-group" data-group="contexto" role="presentation">
437
+ <span class="ajustes-group-title" aria-hidden="true">Contexto</span>
438
+ <button type="button" class="ajustes-tab" id="ajustes-tab-perfil" data-panel="perfil" role="tab" aria-selected="false" aria-controls="ajustes-panel-perfil">Perfil</button>
439
+ <button type="button" class="ajustes-tab" id="ajustes-tab-memoria" data-panel="memoria" role="tab" aria-selected="false" aria-controls="ajustes-panel-memoria">Memoria</button>
440
+ <button type="button" class="ajustes-tab" id="ajustes-tab-skills" data-panel="skills" role="tab" aria-selected="false" aria-controls="ajustes-panel-skills">Skills</button>
441
+ <button type="button" class="ajustes-tab" id="ajustes-tab-agentes" data-panel="agentes" role="tab" aria-selected="false" aria-controls="ajustes-panel-agentes">Subagentes</button>
442
+ <button type="button" class="ajustes-tab" id="ajustes-tab-mcp" data-panel="mcp" role="tab" aria-selected="false" aria-controls="ajustes-panel-mcp">MCP</button>
443
+ </div>
444
+ <div class="ajustes-group" data-group="canales" role="presentation">
445
+ <span class="ajustes-group-title" aria-hidden="true">Voz y canales</span>
446
+ <button type="button" class="ajustes-tab" id="ajustes-tab-voz" data-panel="voz" role="tab" aria-selected="false" aria-controls="ajustes-panel-voz">Voz</button>
447
+ <button type="button" class="ajustes-tab" id="ajustes-tab-transcripcion" data-panel="transcripcion" role="tab" aria-selected="false" aria-controls="ajustes-panel-transcripcion">Transcripción</button>
448
+ <button type="button" class="ajustes-tab" id="ajustes-tab-telegram" data-panel="telegram" role="tab" aria-selected="false" aria-controls="ajustes-panel-telegram">Telegram</button>
449
+ </div>
450
+ <div class="ajustes-group" data-group="interfaz" role="presentation">
451
+ <span class="ajustes-group-title" aria-hidden="true">Interfaz</span>
452
+ <button type="button" class="ajustes-tab" id="ajustes-tab-apariencia" data-panel="apariencia" role="tab" aria-selected="false" aria-controls="ajustes-panel-apariencia">Apariencia</button>
453
+ </div>
454
+ </nav>
455
+ <p id="ajustes-empty" class="ajustes-empty muted" hidden>sin resultados</p>
456
+ </aside>
457
+ <div class="ajustes-main">
458
+ <button type="button" id="ajustes-back" class="ajustes-back" aria-label="volver a la lista de ajustes">
459
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6" /></svg>
460
+ <span>ajustes</span>
461
+ </button>
462
+
463
+ <div class="panel ajustes-panel active" id="ajustes-panel-agente" data-panel="agente" role="tabpanel" aria-labelledby="ajustes-tab-agente">
464
+ <div class="row between">
465
+ <h2>Agente</h2>
466
+ </div>
467
+ <p class="muted verify-line">
468
+ Modo, proveedor, modelo y aprobación automática del agente.
469
+ </p>
470
+ <div id="agent-controls-slot">
471
+ <div id="agent-settings-controls">
472
+ <label for="mode">Modo</label>
473
+ <select id="mode" title="modo de agente"></select>
474
+ <label for="provider">Proveedor</label>
475
+ <select id="provider" title="proveedor"></select>
476
+ <label for="model">Modelo</label>
477
+ <div class="model-row">
478
+ <select id="model" title="modelo"></select>
479
+ <button type="button" id="model-pick" class="btn" title="elegir qué modelos mostrar">elegir</button>
480
+ </div>
481
+ <label for="effort">Esfuerzo de razonamiento</label>
482
+ <select id="effort" title="cuánto piensa el modelo antes de responder">
483
+ <option value="">auto (default del modelo)</option>
484
+ <option value="off">off · sin razonamiento</option>
485
+ <option value="low">low · bajo</option>
486
+ <option value="medium">medium · medio</option>
487
+ <option value="high">high · alto</option>
488
+ <option value="max">max · máximo</option>
489
+ </select>
490
+ <label class="check" title="aprobar automáticamente tools con permiso ask">
491
+ <input id="yes" type="checkbox" /> Aprobar automáticamente las tools (auto)
492
+ </label>
493
+ <label class="check" title="el agente hace exactamente lo pedido, sin mejoras ni cambios fuera del pedido">
494
+ <input id="strict-scope" type="checkbox" /> Hacer solo lo pedido (estricto)
495
+ </label>
496
+ </div>
497
+ </div>
498
+ <div class="row">
499
+ <button type="button" id="agent-settings-open" class="btn">abrir en ventana</button>
500
+ <button type="button" id="agent-providers" class="btn">gestionar proveedores</button>
501
+ </div>
502
+ </div>
503
+
504
+ <div class="panel ajustes-panel" id="ajustes-panel-perfil" data-panel="perfil" role="tabpanel" aria-labelledby="ajustes-tab-perfil">
505
+ <div class="row between">
506
+ <h2>Perfil</h2>
507
+ <span id="perfil-status" class="summary">cargando…</span>
508
+ </div>
509
+ <p class="muted verify-line">
510
+ Datos que el agente conoce de vos. Viven en <code>app.json</code> (la casa) y se
511
+ inyectan en el prompt de todas las corridas.
512
+ </p>
513
+ <label class="field"><span>cómo querés que te llame</span>
514
+ <input id="perfil-address" spellcheck="false" placeholder="Señor · Amo · Amigo · Compañero · Daniel…" />
515
+ </label>
516
+ <label class="field"><span>nombre</span>
517
+ <input id="perfil-name" spellcheck="false" placeholder="Daniel" />
518
+ </label>
519
+ <label class="field"><span>idioma preferido</span>
520
+ <input id="perfil-language" spellcheck="false" placeholder="es" />
521
+ </label>
522
+ <label class="field"><span>notas</span>
523
+ <textarea id="perfil-notes" rows="3" placeholder="Preferencias, contexto, cómo te gusta trabajar…"></textarea>
524
+ </label>
525
+ <div class="row between">
526
+ <span class="muted">datos extra</span>
527
+ <button type="button" id="perfil-add-field" class="btn">+ agregar</button>
528
+ </div>
529
+ <div id="perfil-fields" class="perfil-fields"></div>
530
+ <div class="row">
531
+ <button id="perfil-save" class="btn primary" type="button">guardar</button>
532
+ <span id="perfil-msg" class="summary"></span>
533
+ </div>
534
+ </div>
535
+
536
+ <div class="panel ajustes-panel" id="ajustes-panel-sesiones" data-panel="sesiones" role="tabpanel" aria-labelledby="ajustes-tab-sesiones">
537
+ <div class="row between">
538
+ <h2>Sesiones</h2>
539
+ </div>
540
+ <p class="muted verify-line">
541
+ Carpeta donde se abre el selector de carpetas y <code>/new</code> cuando no hay sesión.
542
+ </p>
543
+ <label for="default-cwd">Carpeta predeterminada para nuevas sesiones</label>
544
+ <div class="row default-cwd-row">
545
+ <input id="default-cwd" type="text" readonly placeholder="(la casa del usuario)" title="carpeta donde se abre el selector y /new sin sesión" />
546
+ <button type="button" id="default-cwd-pick" class="btn">elegir</button>
547
+ <button type="button" id="default-cwd-clear" class="btn icon" title="quitar la carpeta predeterminada" aria-label="quitar la carpeta predeterminada">
548
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
549
+ <path d="M18 6 6 18M6 6l12 12" />
550
+ </svg>
551
+ </button>
552
+ </div>
553
+ </div>
554
+
555
+ <div class="panel ajustes-panel" id="ajustes-panel-mcp" data-panel="mcp" role="tabpanel" aria-labelledby="ajustes-tab-mcp">
556
+ <div class="row between">
557
+ <h2>Servidores MCP</h2>
558
+ <div class="row">
559
+ <button id="mcp-reload" class="btn">recargar</button>
560
+ <button id="mcp-new" class="btn">+ nuevo</button>
561
+ </div>
562
+ </div>
563
+ <p class="muted verify-line">
564
+ Se combinan con <code>mcp[]</code> de <code>tamnora.json</code>; los guardados acá viven en la base.
565
+ </p>
566
+ <div class="import-bar">
567
+ <label class="field grow"><span>carga de herramientas MCP</span>
568
+ <select id="mcp-mode">
569
+ <option value="auto">auto (diferir si hay muchas)</option>
570
+ <option value="lazy">diferido (índice + mcp_call)</option>
571
+ <option value="eager">completas (todas en cada turno)</option>
572
+ </select>
573
+ </label>
574
+ <span id="mcp-count" class="muted"></span>
575
+ </div>
576
+ <div class="import-bar">
577
+ <input id="mcp-pinned" aria-label="tools/servidores MCP fijados" placeholder="tools/servidores fijos (coma), ej. mcp__postgres__query" spellcheck="false" />
578
+ <button id="mcp-pinned-save" class="btn" type="button">fijar</button>
579
+ </div>
580
+ <div class="import-bar">
581
+ <input id="mcp-import" aria-label="importar servidor MCP" placeholder="nombre del registro oficial, URL o JSON (bloque mcpServers)" spellcheck="false" />
582
+ <button id="mcp-import-btn" class="btn" type="button">importar</button>
583
+ </div>
584
+ <div class="import-bar">
585
+ <input id="mcp-search" aria-label="buscar en el registro de MCP" placeholder="buscar en el registro oficial (ej. github, postgres)" spellcheck="false" />
586
+ <button id="mcp-search-btn" class="btn" type="button">buscar</button>
587
+ </div>
588
+ <div id="mcp-import-status" class="summary"></div>
589
+ <div id="mcp-search-status" class="summary"></div>
590
+ <ul id="mcp-search-results" class="providers"></ul>
591
+ <ul id="mcp-list" class="providers"></ul>
592
+ <details class="catalog">
593
+ <summary>sugeridos</summary>
594
+ <ul id="mcp-catalog" class="providers"></ul>
595
+ </details>
596
+ </div>
597
+
598
+ <div class="panel ajustes-panel" id="ajustes-panel-skills" data-panel="skills" role="tabpanel" aria-labelledby="ajustes-tab-skills">
599
+ <div class="row between">
600
+ <h2>Skills</h2>
601
+ <button id="skill-new" class="btn">+ nueva</button>
602
+ </div>
603
+ <p class="muted verify-line">
604
+ Instrucciones especializadas en la casa de Tamnora (<code>skills/</code>). Las habilitadas se inyectan al system prompt.
605
+ </p>
606
+ <div class="import-bar">
607
+ <input id="skill-import" aria-label="importar skill" placeholder="https://skills.sh/owner/repo/skill" spellcheck="false" />
608
+ <button id="skill-import-btn" class="btn" type="button">importar</button>
609
+ </div>
610
+ <div class="import-bar">
611
+ <input id="skill-search" aria-label="buscar skills en GitHub" placeholder="buscar skills en GitHub (ej. testing, react)" spellcheck="false" />
612
+ <button id="skill-search-btn" class="btn" type="button">buscar</button>
613
+ </div>
614
+ <div id="skill-import-status" class="summary"></div>
615
+ <div id="skill-search-status" class="summary"></div>
616
+ <ul id="skill-search-results" class="providers"></ul>
617
+ <ul id="skill-list" class="providers"></ul>
618
+ <details class="catalog">
619
+ <summary>sugeridas</summary>
620
+ <ul id="skill-catalog" class="providers"></ul>
621
+ </details>
622
+ </div>
623
+
624
+ <div class="panel ajustes-panel" id="ajustes-panel-agentes" data-panel="agentes" role="tabpanel" aria-labelledby="ajustes-tab-agentes">
625
+ <div class="row between">
626
+ <h2>Subagentes</h2>
627
+ <button id="agent-new" class="btn">+ nuevo</button>
628
+ </div>
629
+ <p class="muted verify-line">
630
+ Subagentes definidos en <code>agents/</code> (la casa) o <code>.tamnora/agents/</code> (el proyecto).
631
+ El agente principal los ve como catálogo y delega con <code>task</code>; cada uno aporta su
632
+ modelo, modo, tools, skills y esfuerzo.
633
+ </p>
634
+ <div id="agent-status" class="summary"></div>
635
+ <ul id="agent-list" class="providers"></ul>
636
+ </div>
637
+
638
+ <div class="panel ajustes-panel" id="ajustes-panel-memoria" data-panel="memoria" role="tabpanel" aria-labelledby="ajustes-tab-memoria">
639
+ <div class="row between">
640
+ <h2>Memoria</h2>
641
+ <button id="memory-new" class="btn">+ tema</button>
642
+ </div>
643
+ <p class="muted verify-line">
644
+ Lo que el proyecto recuerda entre sesiones (<code>.tamnora/memory/</code>). El agente la mantiene con la
645
+ herramienta <code>memory</code>; acá la podés revisar y editar.
646
+ </p>
647
+ <label class="field"><span>índice (MEMORY.md)</span>
648
+ <textarea id="memory-index" rows="6" spellcheck="false" placeholder="# Memoria del proyecto"></textarea>
649
+ </label>
650
+ <div class="row between settings-actions">
651
+ <span id="memory-status" class="summary"></span>
652
+ <button id="memory-save-index" class="btn" type="button">guardar índice</button>
653
+ </div>
654
+ <ul id="memory-list" class="providers"></ul>
655
+ </div>
656
+
657
+ <div class="panel ajustes-panel" id="ajustes-panel-tareas" data-panel="tareas" role="tabpanel" aria-labelledby="ajustes-tab-tareas">
658
+ <div class="row between">
659
+ <h2>Tareas</h2>
660
+ <button id="schedule-new" class="btn">+ tarea</button>
661
+ </div>
662
+ <p class="muted verify-line">
663
+ Trabajos recurrentes que corren solos mientras el server está vivo. <code>agent</code> corre un turno
664
+ (con las tools y permisos configurados); <code>shell</code> ejecuta un comando permitido.
665
+ </p>
666
+ <div id="schedule-status" class="summary"></div>
667
+ <ul id="schedule-list" class="providers"></ul>
668
+ </div>
669
+
670
+ <div class="panel ajustes-panel" id="ajustes-panel-telegram" data-panel="telegram" role="tabpanel" aria-labelledby="ajustes-tab-telegram">
671
+ <div class="row between">
672
+ <h2>Telegram</h2>
673
+ <span id="tg-status" class="summary">cargando…</span>
674
+ </div>
675
+ <p class="muted verify-line">
676
+ Bot por long polling (no necesita URL pública). El token vive en <code>app.json</code>
677
+ (la casa) y nunca se muestra completo.
678
+ </p>
679
+ <label class="field"><span>token del bot</span>
680
+ <input id="tg-token" type="password" autocomplete="off" spellcheck="false"
681
+ placeholder="123456:ABC… (dejar vacío para no cambiar)" />
682
+ </label>
683
+ <label class="field"><span>chats autorizados (ids separados por coma)</span>
684
+ <input id="tg-chats" spellcheck="false" placeholder="123456789, -1001234567890" />
685
+ </label>
686
+ <label class="field"><span>proveedor de transcripción (voz, opcional)</span>
687
+ <input id="tg-transcribe-provider" spellcheck="false"
688
+ placeholder="openai · groq — vacío = proveedor activo" />
689
+ </label>
690
+ <label class="field"><span>modelo de transcripción (voz)</span>
691
+ <input id="tg-transcribe" spellcheck="false" placeholder="whisper-1" />
692
+ </label>
693
+ <label class="check" title="avisar y pedir permisos por Telegram de los proyectos abiertos en la consola">
694
+ <input id="tg-notify" type="checkbox" /> Avisar por Telegram (proyectos abiertos en la consola)
695
+ </label>
696
+ <div class="voz-announce">
697
+ <span class="muted">qué avisa (siempre con el nombre del proyecto):</span>
698
+ <label class="check" title="resumen al terminar un turno">
699
+ <input id="tg-notify-done" type="checkbox" /> el fin de turno
700
+ </label>
701
+ <label class="check" title="cuando un run falla">
702
+ <input id="tg-notify-error" type="checkbox" /> los errores
703
+ </label>
704
+ <label class="check" title="cuando el agente pide una aprobación">
705
+ <input id="tg-notify-permission" type="checkbox" /> los permisos
706
+ </label>
707
+ <label class="check" title="resultado de subagentes (tool task)">
708
+ <input id="tg-notify-agent" type="checkbox" /> los subagentes
709
+ </label>
710
+ </div>
711
+ <p class="muted verify-line">
712
+ Solo avisa de los proyectos que tengas <b>abiertos como pestañas</b> en la consola.
713
+ </p>
714
+ <details class="tg-help">
715
+ <summary>¿Cómo habilito mi chat?</summary>
716
+ <ol>
717
+ <li>Creá el bot con <b>@BotFather</b> (<code>/newbot</code>) y pegá el token acá.</li>
718
+ <li>Guardá y mandale cualquier mensaje al bot: si tu chat no está autorizado, te
719
+ responde tu <b>chat id</b>.</li>
720
+ <li>Agregá ese id a <strong>chats autorizados</strong> y volvé a guardar.</li>
721
+ </ol>
722
+ </details>
723
+ <div class="row">
724
+ <button id="tg-save" class="btn primary" type="button">guardar</button>
725
+ <button id="tg-test" class="btn" type="button">probar token</button>
726
+ <span id="tg-msg" class="summary"></span>
727
+ </div>
728
+ </div>
729
+
730
+ <div class="panel ajustes-panel" id="ajustes-panel-voz" data-panel="voz" role="tabpanel" aria-labelledby="ajustes-tab-voz">
731
+ <div class="row between">
732
+ <h2>Voz</h2>
733
+ <span id="voz-status" class="summary">cargando…</span>
734
+ </div>
735
+ <p class="muted verify-line">
736
+ El agente cuenta lo que hace con frases cortas (voces neuronales de Edge, sin API
737
+ key): arranque, herramientas y cierre. No lee la respuesta entera. En Telegram llega
738
+ como nota de voz al terminar.
739
+ </p>
740
+ <label class="check" title="hablar mientras trabaja">
741
+ <input id="voz-enabled" type="checkbox" /> Hablar mientras trabaja
742
+ </label>
743
+ <label class="field"><span>voz</span>
744
+ <select id="voz-voice" title="voz del catálogo de Edge"></select>
745
+ </label>
746
+ <label class="field"><span>velocidad</span>
747
+ <div class="voz-range">
748
+ <input id="voz-rate" type="range" min="-100" max="100" step="5" value="0" title="velocidad del habla (%)" />
749
+ <output id="voz-rate-val" for="voz-rate">normal</output>
750
+ </div>
751
+ </label>
752
+ <label class="field"><span>tono</span>
753
+ <div class="voz-range">
754
+ <input id="voz-pitch" type="range" min="-50" max="50" step="5" value="0" title="tono de la voz (Hz)" />
755
+ <output id="voz-pitch-val" for="voz-pitch">normal</output>
756
+ </div>
757
+ </label>
758
+ <div class="voz-announce">
759
+ <span class="muted">qué cuenta:</span>
760
+ <label class="check" title="avisar al arrancar el turno">
761
+ <input id="voz-ann-start" type="checkbox" /> el arranque
762
+ </label>
763
+ <label class="check" title="avisar las herramientas (como mucho una cada 20 s)">
764
+ <input id="voz-ann-tools" type="checkbox" /> las herramientas
765
+ </label>
766
+ <label class="check" title="avisar al terminar">
767
+ <input id="voz-ann-end" type="checkbox" /> el cierre
768
+ </label>
769
+ <label class="check" title="leer la respuesta final del agente, sin bloques de código ni tablas">
770
+ <input id="voz-ann-answer" type="checkbox" /> la respuesta final (sin código)
771
+ </label>
772
+ </div>
773
+ <p id="voz-ffmpeg" class="muted" hidden>
774
+ Sin <code>ffmpeg</code> en el PATH, en Telegram llega como audio MP3 en vez de nota
775
+ de voz.
776
+ </p>
777
+ <div class="row">
778
+ <button id="voz-save" class="btn primary" type="button">guardar</button>
779
+ <button id="voz-test" class="btn" type="button">probar voz</button>
780
+ <span id="voz-msg" class="summary"></span>
781
+ </div>
782
+ </div>
783
+
784
+ <div class="panel ajustes-panel" id="ajustes-panel-transcripcion" data-panel="transcripcion" role="tabpanel" aria-labelledby="ajustes-tab-transcripcion">
785
+ <div class="row between">
786
+ <h2>Transcripción</h2>
787
+ <span id="tr-status" class="summary">cargando…</span>
788
+ </div>
789
+ <p class="muted verify-line">
790
+ Audio → texto para el <b>dictado del chat</b> (micrófono del input) y las notas de voz
791
+ de Telegram.
792
+ </p>
793
+ <label class="field"><span>motor</span>
794
+ <select id="tr-mode" title="cómo se transcribe el dictado del chat">
795
+ <option value="server">Servidor · proveedor de transcripción</option>
796
+ <option value="google">Servidor · Google (sin API key)</option>
797
+ <option value="browser">Navegador · nativo (Web Speech API)</option>
798
+ </select>
799
+ </label>
800
+ <label class="field"><span>idioma del dictado (se le indica al motor para no confundirse)</span>
801
+ <input id="tr-language" spellcheck="false" placeholder="es-AR" />
802
+ </label>
803
+ <div id="tr-server-fields">
804
+ <label class="field"><span>proveedor de transcripción (vacío = proveedor activo)</span>
805
+ <input id="tr-provider" spellcheck="false" placeholder="groq · openai" />
806
+ </label>
807
+ <label class="field"><span>modelo</span>
808
+ <input id="tr-model" spellcheck="false" placeholder="whisper-large-v3-turbo" />
809
+ </label>
810
+ <p class="muted verify-line">
811
+ El proveedor activo no siempre sabe transcribir (p. ej. DeepSeek), así que conviene
812
+ apuntar a uno con <code>/audio/transcriptions</code> (Groq, OpenAI…). Si lo dejás
813
+ vacío se usa lo configurado en <b>Telegram</b> y, si tampoco hay, el proveedor activo
814
+ con <code>whisper-1</code>.
815
+ </p>
816
+ </div>
817
+ <p class="muted verify-line" id="tr-google-note" hidden>
818
+ Usa el servicio de voz de Chromium/Google <b>desde el servidor</b>, sin API key ni
819
+ depender del navegador (servicio no oficial, como el TTS de Edge). Configurá el idioma.
820
+ </p>
821
+ <p class="muted verify-line" id="tr-browser-note" hidden>
822
+ El navegador transcribe en vivo (Chrome/Edge) sin usar el servidor. Necesita el
823
+ servicio de voz de Google; en Brave o sin internet suele fallar (error «network»).
824
+ </p>
825
+ <div class="row">
826
+ <button id="tr-save" class="btn primary" type="button">guardar</button>
827
+ <button id="tr-test" class="btn icon" type="button" title="grabar y ver qué devuelve la transcripción" aria-label="probar transcripción">
828
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
829
+ <path d="M12 19v3" />
830
+ <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
831
+ <path d="M12 15a3 3 0 0 0 3-3V6a3 3 0 0 0-6 0v6a3 3 0 0 0 3 3Z" />
832
+ </svg>
833
+ <span class="mic-count" hidden></span>
834
+ </button>
835
+ <span id="tr-msg" class="summary"></span>
836
+ </div>
837
+ <p id="tr-test-result" class="summary"></p>
838
+ </div>
839
+
840
+ <div class="panel ajustes-panel" id="ajustes-panel-limites" data-panel="limites" role="tabpanel" aria-labelledby="ajustes-tab-limites">
841
+ <div class="row between">
842
+ <h2>Límites del agente</h2>
843
+ </div>
844
+ <p class="muted verify-line">
845
+ El agente trabaja hasta terminar solo (<code>end_turn</code>). Estos topes son
846
+ opcionales: <code>0</code> = sin límite. Al cortarse, cierra con un resumen.
847
+ </p>
848
+ <label class="field"><span>máximo de rondas de herramientas (0 = sin límite)</span>
849
+ <input id="limits-rounds" type="number" min="0" step="1" placeholder="0" />
850
+ </label>
851
+ <label class="field"><span>presupuesto de tokens (0 = sin límite)</span>
852
+ <input id="limits-tokens" type="number" min="0" step="1000" placeholder="0" />
853
+ </label>
854
+ <label class="field"><span>presupuesto de tiempo en segundos (0 = sin límite)</span>
855
+ <input id="limits-duration" type="number" min="0" step="1" placeholder="0" />
856
+ </label>
857
+ <label class="field"><span>máximo de tokens de salida por respuesta (0 = default del proveedor)</span>
858
+ <input id="limits-output" type="number" min="0" step="256" placeholder="0" />
859
+ </label>
860
+ <label class="check" title="reusa el prompt procesado entre rondas: ahorra costo y latencia en conversaciones largas (según el proveedor)">
861
+ <input id="limits-caching" type="checkbox" /> prompt caching
862
+ </label>
863
+ <div class="row">
864
+ <button id="limits-save" class="btn primary" type="button">guardar</button>
865
+ <span id="limits-msg" class="summary"></span>
866
+ </div>
867
+ </div>
868
+
869
+ <div class="panel ajustes-panel" id="ajustes-panel-apariencia" data-panel="apariencia" role="tabpanel" aria-labelledby="ajustes-tab-apariencia">
870
+ <div class="row between">
871
+ <h2>Apariencia</h2>
872
+ </div>
873
+ <p class="muted verify-line">
874
+ Tema, tipografía y tamaño de la consola. Se guardan en tu casa y te siguen entre
875
+ dispositivos.
876
+ </p>
877
+ <label class="field"><span>tema</span></label>
878
+ <div id="apariencia-themes" class="theme-grid"></div>
879
+ <label class="field"><span>tipografía</span>
880
+ <select id="apariencia-font"></select>
881
+ </label>
882
+ <div id="apariencia-preview" class="apariencia-preview">
883
+ <strong>Tamnora Harness</strong>
884
+ <span>El veloz murciélago hindú comía feliz cardillo y kiwi.</span>
885
+ <code>const saludo = 'hola';</code>
886
+ </div>
887
+ <div class="voz-range">
888
+ <span class="muted">tamaño del chat</span>
889
+ <input id="apariencia-chat-font" aria-label="tamano del texto del chat" type="range" min="12" max="20" step="0.5" />
890
+ <output id="apariencia-chat-font-out">14.5px</output>
891
+ </div>
892
+ <div class="voz-range">
893
+ <span class="muted">escala de la interfaz</span>
894
+ <input id="apariencia-scale" aria-label="escala general de la interfaz" type="range" min="80" max="130" step="5" />
895
+ <output id="apariencia-scale-out">100%</output>
896
+ </div>
897
+ <div class="row">
898
+ <button id="apariencia-reset" class="btn" type="button">restablecer</button>
899
+ <span id="apariencia-msg" class="summary"></span>
900
+ </div>
901
+ </div>
902
+ </div>
903
+ </div>
904
+ </section>
905
+
906
+ <dialog id="folder-modal" class="modal folder-modal">
907
+ <div class="modal-card">
908
+ <div class="row between">
909
+ <h3>carpeta de trabajo de la sesión</h3>
910
+ <button type="button" id="folder-close" class="btn icon" title="cerrar" aria-label="cerrar">
911
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
912
+ <path d="M18 6 6 18M6 6l12 12" />
913
+ </svg>
914
+ </button>
915
+ </div>
916
+ <div class="folder-toolbar">
917
+ <button type="button" id="folder-up" class="btn icon" title="subir un nivel" aria-label="subir un nivel">
918
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
919
+ <path d="m5 12 7-7 7 7M12 19V5" />
920
+ </svg>
921
+ </button>
922
+ <button type="button" id="folder-home" class="btn icon" title="ir al inicio" aria-label="ir al inicio">
923
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
924
+ <path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
925
+ <path d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
926
+ </svg>
927
+ </button>
928
+ <button type="button" id="folder-roots" class="btn icon" title="ir a las unidades" aria-label="ir a las unidades">
929
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
930
+ <path d="M2 12h20" />
931
+ <path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" />
932
+ <path d="M6 16h.01M10 16h.01" />
933
+ </svg>
934
+ </button>
935
+ <button type="button" id="folder-new" class="btn icon" title="nueva carpeta acá" aria-label="nueva carpeta acá">
936
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
937
+ <path d="M12 10v6M9 13h6" />
938
+ <path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
939
+ </svg>
940
+ </button>
941
+ <nav id="folder-crumbs" class="crumbs" aria-label="ruta actual"></nav>
942
+ </div>
943
+ <div class="folder-search">
944
+ <input id="folder-goto" aria-label="ir a una ruta o filtrar carpetas" spellcheck="false" placeholder="filtrar carpetas, o pegá una ruta y Enter" />
945
+ <button type="button" id="folder-go" class="btn">ir</button>
946
+ </div>
947
+ <div id="folder-create" class="folder-create" hidden>
948
+ <input id="folder-new-name" aria-label="nombre de la nueva carpeta" spellcheck="false" placeholder="nombre de la nueva carpeta" />
949
+ <button type="button" id="folder-new-create" class="btn primary">crear</button>
950
+ <button type="button" id="folder-new-cancel" class="btn">cancelar</button>
951
+ </div>
952
+ <ul id="folder-list" class="folders"></ul>
953
+ <div class="folder-footer">
954
+ <span id="folder-status" class="summary"></span>
955
+ <span class="spacer"></span>
956
+ <button type="button" id="folder-cancel" class="btn">cancelar</button>
957
+ <button type="button" id="folder-use" class="btn primary">usar esta carpeta</button>
958
+ </div>
959
+ </div>
960
+ </dialog>
961
+ </main>
962
+
963
+ <dialog id="settings-modal" class="modal settings-modal">
964
+ <div class="modal-card">
965
+ <div class="row between">
966
+ <h3>modelo y modo del agente</h3>
967
+ <button type="button" id="settings-close" class="btn icon" title="cerrar" aria-label="cerrar">
968
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
969
+ <path d="M18 6 6 18M6 6l12 12" />
970
+ </svg>
971
+ </button>
972
+ </div>
973
+ <div id="settings-controls-slot"></div>
974
+ <div class="row between settings-actions">
975
+ <button type="button" id="settings-providers" class="btn">gestionar proveedores</button>
976
+ <button type="button" id="settings-done" class="btn primary">listo</button>
977
+ </div>
978
+ </div>
979
+ </dialog>
980
+
981
+ <dialog id="mcp-modal" class="modal">
982
+ <form id="mcp-form" class="modal-card">
983
+ <div class="row between">
984
+ <h3 id="mcp-title">nuevo servidor MCP</h3>
985
+ <button type="button" id="mcp-close" class="btn icon" title="cerrar" aria-label="cerrar">
986
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
987
+ <path d="M18 6 6 18M6 6l12 12" />
988
+ </svg>
989
+ </button>
990
+ </div>
991
+ <div class="row">
992
+ <label class="field"><span>id</span><input id="mf-id" spellcheck="false" placeholder="echo" /></label>
993
+ <label class="field grow"><span>nombre</span><input id="mf-name" spellcheck="false" placeholder="Echo" /></label>
994
+ </div>
995
+ <div class="row">
996
+ <label class="field"><span>transporte</span>
997
+ <select id="mf-transport">
998
+ <option value="stdio">stdio</option>
999
+ <option value="http">http</option>
1000
+ </select>
1001
+ </label>
1002
+ <label class="field grow" id="mf-command-field"><span>comando</span><input id="mf-command" spellcheck="false" placeholder="node" /></label>
1003
+ <label class="field grow" id="mf-url-field" hidden><span>url</span><input id="mf-url" spellcheck="false" placeholder="https://…/mcp" /></label>
1004
+ </div>
1005
+ <div class="row">
1006
+ <label class="field grow" id="mf-args-field"><span>args (separados por espacio)</span><input id="mf-args" spellcheck="false" placeholder="harness/scripts/mcp-echo-server.mjs" /></label>
1007
+ </div>
1008
+ <label class="field" id="mf-env-field"><span>env (KEY=valor por línea · usá <code>env:VAR</code> para no hardcodear secretos)</span>
1009
+ <textarea id="mf-env" rows="3" spellcheck="false" placeholder="API_KEY=env:MY_API_KEY"></textarea>
1010
+ </label>
1011
+ <label class="check" id="mf-inherit-field"><input id="mf-inherit-env" type="checkbox" /> heredar todo el entorno (<code>inheritEnv</code>, solo MCPs de confianza)</label>
1012
+ <label class="check"><input id="mf-enabled" type="checkbox" checked /> habilitado</label>
1013
+ <div class="row between settings-actions">
1014
+ <button type="button" id="mf-cancel" class="btn">cancelar</button>
1015
+ <button type="submit" class="btn primary">guardar</button>
1016
+ </div>
1017
+ <div id="mf-status" class="summary"></div>
1018
+ </form>
1019
+ </dialog>
1020
+
1021
+ <dialog id="skill-modal" class="modal">
1022
+ <form id="skill-form" class="modal-card">
1023
+ <div class="row between">
1024
+ <h3 id="skill-title">nueva skill</h3>
1025
+ <button type="button" id="skill-close" class="btn icon" title="cerrar" aria-label="cerrar">
1026
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1027
+ <path d="M18 6 6 18M6 6l12 12" />
1028
+ </svg>
1029
+ </button>
1030
+ </div>
1031
+ <div class="row">
1032
+ <label class="field"><span>id</span><input id="sf-id" spellcheck="false" placeholder="revisar-pr" /></label>
1033
+ <label class="field grow"><span>nombre</span><input id="sf-name" spellcheck="false" placeholder="Revisar PR" /></label>
1034
+ </div>
1035
+ <label class="field"><span>descripción</span><input id="sf-description" spellcheck="false" placeholder="Cuándo conviene usarla" /></label>
1036
+ <label class="field"><span>cuándo usarla (opcional)</span><input id="sf-when" spellcheck="false" placeholder="al revisar un pull request" /></label>
1037
+ <label class="field"><span>cuerpo (markdown)</span>
1038
+ <textarea id="sf-body" rows="10" spellcheck="false" placeholder="Pasos e instrucciones de la skill…"></textarea>
1039
+ </label>
1040
+ <div class="row between settings-actions">
1041
+ <button type="button" id="sf-cancel" class="btn">cancelar</button>
1042
+ <button type="submit" class="btn primary">guardar</button>
1043
+ </div>
1044
+ <div id="sf-status" class="summary"></div>
1045
+ </form>
1046
+ </dialog>
1047
+
1048
+ <dialog id="agent-modal" class="modal">
1049
+ <form id="agent-form" class="modal-card">
1050
+ <div class="row between">
1051
+ <h3 id="agent-title">nuevo subagente</h3>
1052
+ <button type="button" id="agent-close" class="btn icon" title="cerrar" aria-label="cerrar">
1053
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1054
+ <path d="M18 6 6 18M6 6l12 12" />
1055
+ </svg>
1056
+ </button>
1057
+ </div>
1058
+ <div class="row">
1059
+ <label class="field"><span>id</span><input id="af-id" spellcheck="false" placeholder="revisor" /></label>
1060
+ <label class="field grow"><span>nombre</span><input id="af-name" spellcheck="false" placeholder="Revisor" /></label>
1061
+ </div>
1062
+ <label class="field"><span>descripción (cuándo delegarle)</span><input id="af-description" spellcheck="false" placeholder="Revisa cambios y busca bugs" /></label>
1063
+ <div class="row">
1064
+ <label class="field"><span>proveedor (opcional)</span>
1065
+ <select id="af-provider"><option value="">heredar</option></select>
1066
+ </label>
1067
+ <label class="field grow"><span>modelo (opcional)</span><input id="af-model" spellcheck="false" placeholder="heredar" /></label>
1068
+ </div>
1069
+ <div class="row">
1070
+ <label class="field"><span>modo</span>
1071
+ <select id="af-mode">
1072
+ <option value="">heredar (plan)</option>
1073
+ <option value="plan">plan (solo lectura)</option>
1074
+ <option value="agent">agent (escritura)</option>
1075
+ <option value="voice">voice (.md / .json)</option>
1076
+ </select>
1077
+ </label>
1078
+ <label class="field"><span>esfuerzo</span>
1079
+ <select id="af-effort">
1080
+ <option value="">heredar</option>
1081
+ <option value="off">off</option>
1082
+ <option value="low">low</option>
1083
+ <option value="medium">medium</option>
1084
+ <option value="high">high</option>
1085
+ <option value="max">max</option>
1086
+ </select>
1087
+ </label>
1088
+ <label class="field"><span>max rondas</span><input id="af-rounds" type="number" min="0" placeholder="6" /></label>
1089
+ </div>
1090
+ <label class="field"><span>tools (coma · vacío = las del modo)</span><input id="af-tools" spellcheck="false" placeholder="read, grep, glob" /></label>
1091
+ <label class="field"><span>skills (Ctrl/Cmd para elegir varias · vacío = todas)</span>
1092
+ <select id="af-skills" multiple size="5"></select>
1093
+ </label>
1094
+ <label class="field"><span>instrucciones del subagente (markdown)</span>
1095
+ <textarea id="af-body" rows="8" spellcheck="false" placeholder="Cómo encarar las tareas que le deleguen…"></textarea>
1096
+ </label>
1097
+ <div class="row between settings-actions">
1098
+ <button type="button" id="af-cancel" class="btn">cancelar</button>
1099
+ <button type="submit" class="btn primary">guardar</button>
1100
+ </div>
1101
+ <div id="af-status" class="summary"></div>
1102
+ </form>
1103
+ </dialog>
1104
+
1105
+ <dialog id="memory-modal" class="modal">
1106
+ <form id="memory-form" class="modal-card">
1107
+ <div class="row between">
1108
+ <h3 id="memory-title">nuevo tema</h3>
1109
+ <button type="button" id="memory-close" class="btn icon" title="cerrar" aria-label="cerrar">
1110
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1111
+ <path d="M18 6 6 18M6 6l12 12" />
1112
+ </svg>
1113
+ </button>
1114
+ </div>
1115
+ <div class="row">
1116
+ <label class="field"><span>tema (slug)</span><input id="mem-name" spellcheck="false" placeholder="arquitectura" /></label>
1117
+ <label class="field grow"><span>título</span><input id="mem-title" spellcheck="false" placeholder="Arquitectura" /></label>
1118
+ </div>
1119
+ <label class="field"><span>contenido (markdown)</span>
1120
+ <textarea id="mem-body" rows="10" spellcheck="false" placeholder="Qué recordar del proyecto…"></textarea>
1121
+ </label>
1122
+ <div class="row between settings-actions">
1123
+ <button type="button" id="mem-cancel" class="btn">cancelar</button>
1124
+ <button type="submit" class="btn primary">guardar</button>
1125
+ </div>
1126
+ <div id="mem-status" class="summary"></div>
1127
+ </form>
1128
+ </dialog>
1129
+
1130
+ <dialog id="schedule-modal" class="modal">
1131
+ <form id="schedule-form" class="modal-card">
1132
+ <div class="row between">
1133
+ <h3 id="schedule-title">nueva tarea</h3>
1134
+ <button type="button" id="schedule-close" class="btn icon" title="cerrar" aria-label="cerrar">
1135
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1136
+ <path d="M18 6 6 18M6 6l12 12" />
1137
+ </svg>
1138
+ </button>
1139
+ </div>
1140
+ <div class="row">
1141
+ <label class="field"><span>id</span><input id="sc-id" spellcheck="false" placeholder="resumen-diario" /></label>
1142
+ <label class="field grow"><span>nombre</span><input id="sc-name" spellcheck="false" placeholder="Resumen diario" /></label>
1143
+ </div>
1144
+ <div class="row">
1145
+ <label class="field"><span>tipo</span>
1146
+ <select id="sc-type">
1147
+ <option value="agent">agent</option>
1148
+ <option value="shell">shell</option>
1149
+ </select>
1150
+ </label>
1151
+ <label class="field grow"><span>cuándo</span>
1152
+ <input id="sc-spec" spellcheck="false" placeholder="every 30 m · daily 09:00 · 0 6 * * 1" />
1153
+ </label>
1154
+ </div>
1155
+ <label class="field" id="sc-agent-prompt-row"><span>prompt (agent)</span>
1156
+ <textarea id="sc-prompt" rows="4" spellcheck="false" placeholder="Qué tiene que hacer el agente…"></textarea>
1157
+ </label>
1158
+ <label class="field" id="sc-agent-mode-row"><span>modo (agent)</span>
1159
+ <select id="sc-mode">
1160
+ <option value="agent">agent</option>
1161
+ <option value="plan">plan</option>
1162
+ </select>
1163
+ </label>
1164
+ <label class="field" id="sc-shell-cmd-row"><span>comando (shell)</span>
1165
+ <input id="sc-command" spellcheck="false" placeholder="node · npm · pnpm" />
1166
+ </label>
1167
+ <label class="field" id="sc-shell-args-row"><span>argumentos (shell)</span>
1168
+ <input id="sc-args" spellcheck="false" placeholder="run build" />
1169
+ </label>
1170
+ <label class="field"><span>carpeta (opcional)</span>
1171
+ <input id="sc-cwd" spellcheck="false" placeholder="por defecto: la carpeta del server" />
1172
+ </label>
1173
+ <label class="check"><input id="sc-enabled" type="checkbox" checked /> activa</label>
1174
+ <div class="row between settings-actions">
1175
+ <button type="button" id="sc-cancel" class="btn">cancelar</button>
1176
+ <button type="submit" class="btn primary">guardar</button>
1177
+ </div>
1178
+ <div id="sc-status" class="summary"></div>
1179
+ </form>
1180
+ </dialog>
1181
+
1182
+ <dialog id="palette-modal" class="modal palette-modal">
1183
+ <div class="palette">
1184
+ <div class="palette-row">
1185
+ <svg class="palette-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1186
+ <circle cx="11" cy="11" r="8" />
1187
+ <path d="m21 21-4.3-4.3" />
1188
+ </svg>
1189
+ <input id="palette-input" aria-label="buscar sesiones, mensajes y comandos" type="text" placeholder="buscar sesiones y mensajes, o escribí un comando…" spellcheck="false" autocomplete="off" />
1190
+ <span class="palette-hint">esc</span>
1191
+ </div>
1192
+ <ul id="palette-list" class="palette-list"></ul>
1193
+ </div>
1194
+ </dialog>
1195
+
1196
+ <dialog id="model-modal" class="modal model-modal">
1197
+ <div class="modal-card">
1198
+ <div class="row between">
1199
+ <h3>elegir modelos</h3>
1200
+ <button type="button" id="model-close" class="btn icon" title="cerrar" aria-label="cerrar">
1201
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1202
+ <path d="M18 6 6 18M6 6l12 12" />
1203
+ </svg>
1204
+ </button>
1205
+ </div>
1206
+ <div class="model-search">
1207
+ <input id="model-query" aria-label="buscar modelos" type="text" placeholder="buscar por nombre o id…" spellcheck="false" autocomplete="off" />
1208
+ </div>
1209
+ <div class="model-actions">
1210
+ <button type="button" id="model-all" class="btn">todos</button>
1211
+ <button type="button" id="model-none" class="btn">ninguno</button>
1212
+ <button type="button" id="model-free" class="btn">solo gratis</button>
1213
+ <span class="spacer"></span>
1214
+ <span id="model-count" class="summary"></span>
1215
+ </div>
1216
+ <ul id="model-list" class="model-list"></ul>
1217
+ <div class="row between settings-actions">
1218
+ <span id="model-status" class="summary"></span>
1219
+ <span class="spacer"></span>
1220
+ <button type="button" id="model-cancel" class="btn">cancelar</button>
1221
+ <button type="button" id="model-save" class="btn primary">guardar</button>
1222
+ </div>
1223
+ </div>
1224
+ </dialog>
1225
+
1226
+ <script type="module" src="app.js"></script>
1227
+ </body>
1228
+ </html>