@anchrd/intel-ui 0.14.0 → 0.16.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.
- package/package.json +3 -2
- package/src/agent/agent-calendar/agent-calendar.tsx +42 -4
- package/src/agent/agent-costs/agent-costs.ts +95 -0
- package/src/agent/agent-delegation-notice/agent-delegation-notice.ts +48 -0
- package/src/agent/agent-entry-title/agent-entry-title.ts +48 -7
- package/src/agent/agent-log/agent-log.tsx +162 -16
- package/src/agent/agent-models/agent-models.ts +31 -67
- package/src/agent/agent-models/agent-models.types.ts +11 -5
- package/src/agent/agent-profile/agent-profile.tsx +237 -51
- package/src/agent/agent-state/agent-state.ts +17 -0
- package/src/agent/agent-status-dot/agent-status-dot.ts +73 -0
- package/src/agent/agent.tsx +159 -50
- package/src/app/app-tree/app-tree.tsx +9 -3
- package/src/app/settings-dialog/settings-dialog.tsx +13 -4
- package/src/data/agent-runtime/agent-runtime.ts +38 -0
- package/src/data/intel-data-provider/intel-data-provider.ts +20 -0
- package/src/data/intel-data-provider/intel-data-provider.types.ts +19 -0
- package/src/entry-picker/entry-picker.tsx +16 -5
- package/src/hooks/use-model-catalog.ts +26 -0
- package/src/i18n/de.json +30 -10
- package/src/i18n/en.json +30 -10
- package/src/i18n/es.json +30 -10
- package/src/section-hint/section-hint.tsx +40 -0
- package/src/timezone/timezone-context.tsx +25 -5
- package/src/timezone/timezone.ts +20 -5
- package/src/title-row/title-row.tsx +9 -4
- package/src/user-name/user-name.ts +41 -0
- package/tsconfig.json +7 -1
package/src/i18n/de.json
CHANGED
|
@@ -282,12 +282,13 @@
|
|
|
282
282
|
"agent.tab.chat": "Chat",
|
|
283
283
|
"agent.tab.calendar": "Kalender",
|
|
284
284
|
"agent.tab.log": "Protokoll",
|
|
285
|
-
"agent.
|
|
286
|
-
"agent.
|
|
287
|
-
"agent.
|
|
288
|
-
"agent.
|
|
285
|
+
"agent.status.running": "Läuft",
|
|
286
|
+
"agent.status.waiting": "Wartet auf den nächsten Lauf",
|
|
287
|
+
"agent.status.failed": "Letzter Lauf fehlgeschlagen",
|
|
288
|
+
"agent.status.paused": "Pausiert",
|
|
289
|
+
"agent.status.unknown": "Laufzeit nicht erreichbar",
|
|
289
290
|
"agent.nextRun": "nächster Lauf {when} ({zone})",
|
|
290
|
-
"agent.
|
|
291
|
+
"agent.lastRun": "letzter Lauf {when}",
|
|
291
292
|
"agent.pausedNoRuns": "kein Zeitplan feuert, keine Aufgabe und keine Mail wird angenommen; der Chat antwortet weiter",
|
|
292
293
|
"agent.pause": "Pausieren",
|
|
293
294
|
"agent.resume": "Fortsetzen",
|
|
@@ -296,6 +297,7 @@
|
|
|
296
297
|
"agent.runNowPaused": "Setze den Agenten fort, bevor du ihn ausführst.",
|
|
297
298
|
"agent.actionFailed": "Das hat nicht funktioniert. Versuche es erneut.",
|
|
298
299
|
"agent.keyMissing": "Dieser Agent hat keinen Application-Key, deshalb kann kein Lauf starten. Ersetze seinen Key unter „Identität“ im Profil.",
|
|
300
|
+
"agent.createdBy": "von {name}",
|
|
299
301
|
"agent.notPermitted": "Du darfst diesen Agenten lesen, aber nicht steuern. Dafür braucht es die Berechtigung „Agenten ausführen“.",
|
|
300
302
|
"agent.noRuntime": "Diese Installation betreibt keine Agenten-Laufzeit, deshalb kann dieser Agent hier weder chatten noch laufen. Seine Definition bleibt erhalten und funktioniert wieder auf einer Installation, die eine hat.",
|
|
301
303
|
"agent.loadFailed": "Dieser Agent konnte nicht geladen werden. Prüfe deinen Zugriff und versuche es erneut.",
|
|
@@ -320,10 +322,10 @@
|
|
|
320
322
|
"agent.contactAdd": "Erreichbarkeit hinzufügen",
|
|
321
323
|
"agent.contactAddReason": "Eine Mail-Adresse gehört der Installation, nicht der Definition des Agenten — sie wird dort konfiguriert, wo die Laufzeit betrieben wird.",
|
|
322
324
|
"agent.knows": "Was er weiß",
|
|
323
|
-
"agent.knowsHint": "
|
|
324
|
-
"agent.addReference": "
|
|
325
|
+
"agent.knowsHint": "Was dieser Agent liest, und wofür jeder Eintrag zählt. Eine Systemnachricht weist ihn an — ein Ordner, ein Dokument oder eine Tabelle. Semantischer Kontext wird durchsucht, und in das Gedächtnis wird zurückgeschrieben und es wird ebenso durchsucht, deshalb sind beide Ordner. Der Name verspricht weniger als die Rolle: Was der Agent sich im Gedächtnis notiert, kommt ihm bei jeder späteren Suche wieder entgegen.",
|
|
326
|
+
"agent.addReference": "Etwas zum Lesen hinzufügen",
|
|
325
327
|
"agent.removeReferenceOf": "{title} nicht mehr lesen",
|
|
326
|
-
"agent.
|
|
328
|
+
"agent.pickEntry": "Was gelesen wird",
|
|
327
329
|
"agent.roleFor": "Wofür {title} zählt",
|
|
328
330
|
"agent.role.system-message": "Systemnachricht",
|
|
329
331
|
"agent.role.semantic-context": "Semantischer Kontext",
|
|
@@ -335,11 +337,12 @@
|
|
|
335
337
|
"agent.removeToolServerOf": "{name} diesem Agenten wegnehmen",
|
|
336
338
|
"agent.toolServerUnavailable": "Du erreichst diesen Server nicht mehr",
|
|
337
339
|
"agent.toolsDelegationNotice": "Wer diesen Agenten ausführen darf, handelt auf deiner Verbindung.",
|
|
340
|
+
"agent.toolsDelegationNoticeBy": "Wer diesen Agenten ausführen darf, handelt auf der Verbindung von {name}.",
|
|
341
|
+
"agent.toolsDelegationNoticeSomebody": "Wer diesen Agenten ausführen darf, handelt auf der Verbindung dessen, der ihm diese Werkzeuge gegeben hat — nicht auf deiner.",
|
|
338
342
|
"agent.toolsNotConnected": "Du bist noch nicht am Firmenportal angemeldet, es gibt also nichts abzugeben. Öffne „Werkzeuge“, um dich zu verbinden.",
|
|
339
343
|
"agent.toolsNoneLeft": "Jeder Server, den du erreichst, ist diesem Agenten bereits gegeben.",
|
|
340
344
|
"agent.schedule": "Zeitplan",
|
|
341
345
|
"agent.scheduleHint": "Wann dieser Agent von sich aus handelt und was er ausführt. Jeder Zeitplan behält die Zeitzone, die er bekommen hat — er meint damit für alle Lesenden dieselbe Stunde.",
|
|
342
|
-
"agent.scheduleEmpty": "Dieser Agent läuft nur, wenn er gefragt wird.",
|
|
343
346
|
"agent.addSchedule": "Zeitplan hinzufügen",
|
|
344
347
|
"agent.removeSchedule": "Diesen Zeitplan entfernen",
|
|
345
348
|
"agent.scheduleTarget": "Was ausgeführt wird",
|
|
@@ -350,8 +353,23 @@
|
|
|
350
353
|
"agent.model": "Modell",
|
|
351
354
|
"agent.modelHint": "Welches Modell denkt. Eine Änderung übernimmt die Laufzeit innerhalb einer Minute.",
|
|
352
355
|
"agent.modelFigures": "{context} Kontext · {input} $ rein / {output} $ raus je 1 Mio. Token",
|
|
356
|
+
"agent.usage": "{input} rein · {output} raus · {cacheRead} Cache gelesen · {cacheWrite} Cache geschrieben",
|
|
357
|
+
"agent.usageUnknown": "Für diesen Lauf sind keine Verbrauchszahlen erfasst",
|
|
358
|
+
"agent.usageNotReported": "Dieser Anbieter meldet keinen Verbrauch",
|
|
359
|
+
"agent.costMissing": "Kosten stehen nicht im Gateway-Log",
|
|
360
|
+
"agent.costNotConfigured": "Keine Kostenangabe: Diese Installation hat kein Lese-Token für das AI Gateway.",
|
|
361
|
+
"agent.costUnreadable": "Keine Kostenangabe: Das AI-Gateway-Log war nicht lesbar.",
|
|
362
|
+
"agent.costPartial": "Das Gateway-Log wurde bis zur Seitengrenze gelesen — diese Summen sind eine Untergrenze.",
|
|
363
|
+
"agent.costWindow": "Letzte {days} Tage",
|
|
364
|
+
"agent.costWindowCalls": "{count} Modellaufrufe",
|
|
365
|
+
"agent.costs": "Kosten",
|
|
366
|
+
"agent.costsSpent": "{amount} in den letzten {days} Tagen",
|
|
367
|
+
"agent.costsSpentPast": "Bereits ausgegeben, für vergangene Läufe — keine Vorhersage für das oben gewählte Modell.",
|
|
368
|
+
"agent.costsSpentWith": "mit {models}",
|
|
369
|
+
"agent.modelFiguresStale": "Diese Angaben stammen aus einer eingebauten Tabelle und können veraltet sein.",
|
|
353
370
|
"agent.calendar": "Kommende Läufe",
|
|
354
|
-
"agent.calendarZones": "Jede Zeit steht in der Zeitzone ihres eigenen Zeitplans, denn dann läuft der Agent.",
|
|
371
|
+
"agent.calendarZones": "Jede Zeit steht in der Zeitzone ihres eigenen Zeitplans, denn dann läuft der Agent. Weicht deine eigene Zeitzone ab, steht sie daneben.",
|
|
372
|
+
"agent.calendarYourTime": "{when} bei dir",
|
|
355
373
|
"agent.calendarUnreadable": "Keiner der Zeitpläne dieses Agenten lässt sich als Cron-Ausdruck lesen, es ist also nichts geplant.",
|
|
356
374
|
"agent.log": "Läufe",
|
|
357
375
|
"agent.logEmpty": "Dieser Agent ist noch nicht gelaufen.",
|
|
@@ -359,6 +377,8 @@
|
|
|
359
377
|
"agent.runFailed": "Dieser Lauf konnte nicht gelesen werden.",
|
|
360
378
|
"agent.runLog": "Was dieser Lauf getan hat",
|
|
361
379
|
"agent.runLogEmpty": "Dieser Lauf hat nichts aufgezeichnet.",
|
|
380
|
+
"agent.runAnswer": "Was dieser Lauf geantwortet hat",
|
|
381
|
+
"agent.runAnswerEmpty": "Dieser Lauf hat keine Antwort aufgezeichnet.",
|
|
362
382
|
"agent.steps": "{count} Schritte",
|
|
363
383
|
"agent.trigger.chat": "aus dem Chat",
|
|
364
384
|
"agent.trigger.schedule": "nach Zeitplan",
|
package/src/i18n/en.json
CHANGED
|
@@ -282,12 +282,13 @@
|
|
|
282
282
|
"agent.tab.chat": "Chat",
|
|
283
283
|
"agent.tab.calendar": "Calendar",
|
|
284
284
|
"agent.tab.log": "Log",
|
|
285
|
-
"agent.
|
|
286
|
-
"agent.
|
|
287
|
-
"agent.
|
|
288
|
-
"agent.
|
|
285
|
+
"agent.status.running": "Running",
|
|
286
|
+
"agent.status.waiting": "Waiting for its next run",
|
|
287
|
+
"agent.status.failed": "Last run failed",
|
|
288
|
+
"agent.status.paused": "Paused",
|
|
289
|
+
"agent.status.unknown": "Runtime not reachable",
|
|
289
290
|
"agent.nextRun": "next run {when} ({zone})",
|
|
290
|
-
"agent.
|
|
291
|
+
"agent.lastRun": "last run {when}",
|
|
291
292
|
"agent.pausedNoRuns": "no schedule fires, no task and no mail is taken; the chat still answers",
|
|
292
293
|
"agent.pause": "Pause",
|
|
293
294
|
"agent.resume": "Resume",
|
|
@@ -296,6 +297,7 @@
|
|
|
296
297
|
"agent.runNowPaused": "Resume the agent before running it.",
|
|
297
298
|
"agent.actionFailed": "That did not work. Try again.",
|
|
298
299
|
"agent.keyMissing": "This agent has no application key, so no run can start. Replace its key under Identity in the profile.",
|
|
300
|
+
"agent.createdBy": "by {name}",
|
|
299
301
|
"agent.notPermitted": "You may read this agent, but not drive it. Driving an agent needs the “run agents” permission.",
|
|
300
302
|
"agent.noRuntime": "This installation runs no agent runtime, so this agent cannot chat or run here. Its definition is kept and works again on an installation that has one.",
|
|
301
303
|
"agent.loadFailed": "This agent could not be loaded. Check your access and try again.",
|
|
@@ -320,10 +322,10 @@
|
|
|
320
322
|
"agent.contactAdd": "Add contact",
|
|
321
323
|
"agent.contactAddReason": "A mail address belongs to the installation, not to the agent's definition, so it is configured where the runtime is deployed.",
|
|
322
324
|
"agent.knows": "What it knows",
|
|
323
|
-
"agent.knowsHint": "
|
|
324
|
-
"agent.addReference": "Add
|
|
325
|
+
"agent.knowsHint": "What this agent reads, and what each entry counts as. A system message instructs it — a folder, a document or a table. Semantic context is searched, and memory is written back to and searched as well, so both of those are folders. The name promises less than the role does: what the agent notes down in memory comes back to it in every later search.",
|
|
326
|
+
"agent.addReference": "Add something to read",
|
|
325
327
|
"agent.removeReferenceOf": "Stop reading {title}",
|
|
326
|
-
"agent.
|
|
328
|
+
"agent.pickEntry": "What to read",
|
|
327
329
|
"agent.roleFor": "What {title} counts as",
|
|
328
330
|
"agent.role.system-message": "System message",
|
|
329
331
|
"agent.role.semantic-context": "Semantic context",
|
|
@@ -335,11 +337,12 @@
|
|
|
335
337
|
"agent.removeToolServerOf": "Take {name} away from this agent",
|
|
336
338
|
"agent.toolServerUnavailable": "You no longer reach this server",
|
|
337
339
|
"agent.toolsDelegationNotice": "Anyone who may run this agent acts on your connection.",
|
|
340
|
+
"agent.toolsDelegationNoticeBy": "Anyone who may run this agent acts on the connection of {name}.",
|
|
341
|
+
"agent.toolsDelegationNoticeSomebody": "Anyone who may run this agent acts on the connection of whoever gave it these tools — not on yours.",
|
|
338
342
|
"agent.toolsNotConnected": "You are not signed in to the company portal yet, so there is nothing to give away. Open Tools to connect.",
|
|
339
343
|
"agent.toolsNoneLeft": "Every server you reach is already given to this agent.",
|
|
340
344
|
"agent.schedule": "Schedule",
|
|
341
345
|
"agent.scheduleHint": "When this agent acts on its own, and what it runs. Each schedule keeps the timezone it was given, so it means the same hour to everybody reading it.",
|
|
342
|
-
"agent.scheduleEmpty": "This agent runs only when it is asked.",
|
|
343
346
|
"agent.addSchedule": "Add schedule",
|
|
344
347
|
"agent.removeSchedule": "Remove this schedule",
|
|
345
348
|
"agent.scheduleTarget": "What to run",
|
|
@@ -350,8 +353,23 @@
|
|
|
350
353
|
"agent.model": "Model",
|
|
351
354
|
"agent.modelHint": "Which model does the thinking. A change is picked up by the runtime within a minute.",
|
|
352
355
|
"agent.modelFigures": "{context} context · ${input} in / ${output} out per 1M tokens",
|
|
356
|
+
"agent.usage": "{input} in · {output} out · {cacheRead} cache read · {cacheWrite} cache written",
|
|
357
|
+
"agent.usageUnknown": "No usage figures for this run",
|
|
358
|
+
"agent.usageNotReported": "This provider reports no usage",
|
|
359
|
+
"agent.costMissing": "Cost not in the gateway log",
|
|
360
|
+
"agent.costNotConfigured": "No cost figures: this installation has no AI Gateway read token.",
|
|
361
|
+
"agent.costUnreadable": "No cost figures: the AI Gateway log could not be read.",
|
|
362
|
+
"agent.costPartial": "The gateway log was read up to its page limit, so these totals are a lower bound.",
|
|
363
|
+
"agent.costWindow": "Last {days} days",
|
|
364
|
+
"agent.costWindowCalls": "{count} model calls",
|
|
365
|
+
"agent.costs": "Cost",
|
|
366
|
+
"agent.costsSpent": "{amount} in the last {days} days",
|
|
367
|
+
"agent.costsSpentPast": "Already spent, on past runs — not a forecast for the model chosen above.",
|
|
368
|
+
"agent.costsSpentWith": "with {models}",
|
|
369
|
+
"agent.modelFiguresStale": "These figures come from a built-in table and can be out of date.",
|
|
353
370
|
"agent.calendar": "Upcoming runs",
|
|
354
|
-
"agent.calendarZones": "Each time is shown in the timezone of its own schedule, because that is when the agent runs.",
|
|
371
|
+
"agent.calendarZones": "Each time is shown in the timezone of its own schedule, because that is when the agent runs. Where your own timezone differs, it is added beside it.",
|
|
372
|
+
"agent.calendarYourTime": "{when} your time",
|
|
355
373
|
"agent.calendarUnreadable": "None of this agent's schedules can be read as a cron expression, so nothing is planned.",
|
|
356
374
|
"agent.log": "Runs",
|
|
357
375
|
"agent.logEmpty": "This agent has not run yet.",
|
|
@@ -359,6 +377,8 @@
|
|
|
359
377
|
"agent.runFailed": "This run could not be read.",
|
|
360
378
|
"agent.runLog": "What this run did",
|
|
361
379
|
"agent.runLogEmpty": "This run recorded nothing.",
|
|
380
|
+
"agent.runAnswer": "What this run answered",
|
|
381
|
+
"agent.runAnswerEmpty": "This run recorded no answer.",
|
|
362
382
|
"agent.steps": "{count} steps",
|
|
363
383
|
"agent.trigger.chat": "from the chat",
|
|
364
384
|
"agent.trigger.schedule": "on schedule",
|
package/src/i18n/es.json
CHANGED
|
@@ -282,12 +282,13 @@
|
|
|
282
282
|
"agent.tab.chat": "Chat",
|
|
283
283
|
"agent.tab.calendar": "Calendario",
|
|
284
284
|
"agent.tab.log": "Registro",
|
|
285
|
-
"agent.
|
|
286
|
-
"agent.
|
|
287
|
-
"agent.
|
|
288
|
-
"agent.
|
|
285
|
+
"agent.status.running": "En marcha",
|
|
286
|
+
"agent.status.waiting": "Esperando su próxima ejecución",
|
|
287
|
+
"agent.status.failed": "La última ejecución falló",
|
|
288
|
+
"agent.status.paused": "En pausa",
|
|
289
|
+
"agent.status.unknown": "Entorno de ejecución no accesible",
|
|
289
290
|
"agent.nextRun": "próxima ejecución {when} ({zone})",
|
|
290
|
-
"agent.
|
|
291
|
+
"agent.lastRun": "última ejecución {when}",
|
|
291
292
|
"agent.pausedNoRuns": "no se dispara ningún horario, no se acepta ninguna tarea ni ningún correo; el chat sigue respondiendo",
|
|
292
293
|
"agent.pause": "Pausar",
|
|
293
294
|
"agent.resume": "Reanudar",
|
|
@@ -296,6 +297,7 @@
|
|
|
296
297
|
"agent.runNowPaused": "Reanuda el agente antes de ejecutarlo.",
|
|
297
298
|
"agent.actionFailed": "Eso no ha funcionado. Inténtalo de nuevo.",
|
|
298
299
|
"agent.keyMissing": "Este agente no tiene clave de aplicación, así que no puede empezar ninguna ejecución. Sustituye su clave en «Identidad», dentro del perfil.",
|
|
300
|
+
"agent.createdBy": "por {name}",
|
|
299
301
|
"agent.notPermitted": "Puedes leer este agente, pero no manejarlo. Manejar un agente necesita el permiso «ejecutar agentes».",
|
|
300
302
|
"agent.noRuntime": "Esta instalación no tiene entorno de ejecución de agentes, así que este agente no puede chatear ni ejecutarse aquí. Su definición se conserva y vuelve a funcionar en una instalación que sí lo tenga.",
|
|
301
303
|
"agent.loadFailed": "Este agente no se ha podido cargar. Comprueba tu acceso e inténtalo de nuevo.",
|
|
@@ -320,10 +322,10 @@
|
|
|
320
322
|
"agent.contactAdd": "Añadir un contacto",
|
|
321
323
|
"agent.contactAddReason": "Una dirección de correo pertenece a la instalación, no a la definición del agente, así que se configura donde está desplegado el entorno de ejecución.",
|
|
322
324
|
"agent.knows": "Lo que sabe",
|
|
323
|
-
"agent.knowsHint": "
|
|
324
|
-
"agent.addReference": "Añadir
|
|
325
|
+
"agent.knowsHint": "Lo que lee este agente y para qué cuenta cada entrada. Un mensaje de sistema lo instruye — una carpeta, un documento o una tabla. El contexto semántico se busca, y en la memoria se escribe de vuelta y también se busca, por eso ambas son carpetas. El nombre promete menos que el papel: lo que el agente anota en la memoria le vuelve en cada búsqueda posterior.",
|
|
326
|
+
"agent.addReference": "Añadir algo para leer",
|
|
325
327
|
"agent.removeReferenceOf": "Dejar de leer {title}",
|
|
326
|
-
"agent.
|
|
328
|
+
"agent.pickEntry": "Qué se lee",
|
|
327
329
|
"agent.roleFor": "Para qué cuenta {title}",
|
|
328
330
|
"agent.role.system-message": "Mensaje de sistema",
|
|
329
331
|
"agent.role.semantic-context": "Contexto semántico",
|
|
@@ -335,11 +337,12 @@
|
|
|
335
337
|
"agent.removeToolServerOf": "Quitarle {name} a este agente",
|
|
336
338
|
"agent.toolServerUnavailable": "Ya no alcanzas este servidor",
|
|
337
339
|
"agent.toolsDelegationNotice": "Quien pueda ejecutar este agente actúa sobre tu conexión.",
|
|
340
|
+
"agent.toolsDelegationNoticeBy": "Quien pueda ejecutar este agente actúa sobre la conexión de {name}.",
|
|
341
|
+
"agent.toolsDelegationNoticeSomebody": "Quien pueda ejecutar este agente actúa sobre la conexión de quien le dio estas herramientas, no sobre la tuya.",
|
|
338
342
|
"agent.toolsNotConnected": "Todavía no has iniciado sesión en el portal de la empresa, así que no hay nada que ceder. Abre «Herramientas» para conectarte.",
|
|
339
343
|
"agent.toolsNoneLeft": "Todos los servidores que alcanzas ya están dados a este agente.",
|
|
340
344
|
"agent.schedule": "Horario",
|
|
341
345
|
"agent.scheduleHint": "Cuándo actúa este agente por su cuenta y qué ejecuta. Cada horario conserva la zona horaria con la que se creó, así que significa la misma hora para todo el que lo lea.",
|
|
342
|
-
"agent.scheduleEmpty": "Este agente solo se ejecuta cuando se le pide.",
|
|
343
346
|
"agent.addSchedule": "Añadir un horario",
|
|
344
347
|
"agent.removeSchedule": "Quitar este horario",
|
|
345
348
|
"agent.scheduleTarget": "Qué ejecutar",
|
|
@@ -350,8 +353,23 @@
|
|
|
350
353
|
"agent.model": "Modelo",
|
|
351
354
|
"agent.modelHint": "Qué modelo piensa. El entorno de ejecución recoge un cambio en menos de un minuto.",
|
|
352
355
|
"agent.modelFigures": "{context} de contexto · {input} $ de entrada / {output} $ de salida por cada millón de tokens",
|
|
356
|
+
"agent.usage": "{input} de entrada · {output} de salida · {cacheRead} leídos de caché · {cacheWrite} escritos en caché",
|
|
357
|
+
"agent.usageUnknown": "No hay cifras de consumo para esta ejecución",
|
|
358
|
+
"agent.usageNotReported": "Este proveedor no informa del consumo",
|
|
359
|
+
"agent.costMissing": "El coste no figura en el registro de la pasarela",
|
|
360
|
+
"agent.costNotConfigured": "Sin cifras de coste: esta instalación no tiene un token de lectura de AI Gateway.",
|
|
361
|
+
"agent.costUnreadable": "Sin cifras de coste: no se pudo leer el registro de AI Gateway.",
|
|
362
|
+
"agent.costPartial": "El registro de la pasarela se leyó hasta su límite de páginas, así que estos totales son un mínimo.",
|
|
363
|
+
"agent.costWindow": "Últimos {days} días",
|
|
364
|
+
"agent.costWindowCalls": "{count} llamadas al modelo",
|
|
365
|
+
"agent.costs": "Coste",
|
|
366
|
+
"agent.costsSpent": "{amount} en los últimos {days} días",
|
|
367
|
+
"agent.costsSpentPast": "Ya gastado, en ejecuciones pasadas: no es una previsión para el modelo elegido arriba.",
|
|
368
|
+
"agent.costsSpentWith": "con {models}",
|
|
369
|
+
"agent.modelFiguresStale": "Estas cifras proceden de una tabla incorporada y pueden estar desactualizadas.",
|
|
353
370
|
"agent.calendar": "Próximas ejecuciones",
|
|
354
|
-
"agent.calendarZones": "Cada hora se muestra en la zona horaria de su propio horario, porque es cuando se ejecuta el agente.",
|
|
371
|
+
"agent.calendarZones": "Cada hora se muestra en la zona horaria de su propio horario, porque es cuando se ejecuta el agente. Si tu propia zona horaria difiere, se añade al lado.",
|
|
372
|
+
"agent.calendarYourTime": "{when} en tu hora",
|
|
355
373
|
"agent.calendarUnreadable": "Ninguno de los horarios de este agente se puede leer como expresión cron, así que no hay nada planificado.",
|
|
356
374
|
"agent.log": "Ejecuciones",
|
|
357
375
|
"agent.logEmpty": "Este agente todavía no se ha ejecutado.",
|
|
@@ -359,6 +377,8 @@
|
|
|
359
377
|
"agent.runFailed": "Esta ejecución no se ha podido leer.",
|
|
360
378
|
"agent.runLog": "Lo que ha hecho esta ejecución",
|
|
361
379
|
"agent.runLogEmpty": "Esta ejecución no ha registrado nada.",
|
|
380
|
+
"agent.runAnswer": "Lo que ha respondido esta ejecución",
|
|
381
|
+
"agent.runAnswerEmpty": "Esta ejecución no ha registrado ninguna respuesta.",
|
|
362
382
|
"agent.steps": "{count} pasos",
|
|
363
383
|
"agent.trigger.chat": "desde el chat",
|
|
364
384
|
"agent.trigger.schedule": "por horario",
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Info } from "lucide-react";
|
|
2
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* An explanation behind an ⓘ, beside the thing it explains (#204, shared since #249).
|
|
6
|
+
*
|
|
7
|
+
* It exists for the sentence that answers "what IS this" — read once, then furniture. Not for the
|
|
8
|
+
* sentence you need WHILE you type: `agent.cronHint` ("five fields: minute, hour, …") deliberately
|
|
9
|
+
* stays a visible paragraph, because a reference you read while writing must not be behind a hover.
|
|
10
|
+
* That is the dividing line: *what is this* may hide, *how do I write this* may not.
|
|
11
|
+
*
|
|
12
|
+
* ⚠️ The hint is the trigger's accessible NAME, not only the tooltip's content: Radix describes the
|
|
13
|
+
* trigger by the content only while it is open, and a listener who tabs past a nameless icon button
|
|
14
|
+
* would never learn there is anything behind it.
|
|
15
|
+
*
|
|
16
|
+
* ⚠️ A real `<button>`, not a bare icon or a `<span>` with a hover style. A tooltip that only opens
|
|
17
|
+
* on hover cannot be opened at all on a touch device, and an element that is not focusable is one
|
|
18
|
+
* the keyboard walks straight past.
|
|
19
|
+
*
|
|
20
|
+
* ⚠️ It does NOT replace a field's description. Where this sits beside a form control, the control
|
|
21
|
+
* keeps its own `aria-describedby` pointing at an `sr-only` copy of the same words — the tooltip's
|
|
22
|
+
* content is in the accessibility tree only while it is open, so a field that gave up its
|
|
23
|
+
* description for one would quietly have lost it (#249).
|
|
24
|
+
*/
|
|
25
|
+
export function SectionHint({ hint }: { hint: string }) {
|
|
26
|
+
return (
|
|
27
|
+
<TooltipProvider delayDuration={300}>
|
|
28
|
+
<Tooltip>
|
|
29
|
+
<TooltipTrigger
|
|
30
|
+
type="button"
|
|
31
|
+
aria-label={hint}
|
|
32
|
+
className="rounded-full text-muted-foreground outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring"
|
|
33
|
+
>
|
|
34
|
+
<Info aria-hidden="true" className="size-3.5" />
|
|
35
|
+
</TooltipTrigger>
|
|
36
|
+
<TooltipContent className="max-w-xs">{hint}</TooltipContent>
|
|
37
|
+
</Tooltip>
|
|
38
|
+
</TooltipProvider>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { createContext, type ReactNode, useContext, useMemo, useState } from "react";
|
|
2
|
-
import { browserTimezone,
|
|
2
|
+
import { browserTimezone, readChosenTimezone, rememberTimezone } from "./timezone.ts";
|
|
3
3
|
|
|
4
4
|
// The React seam of the timezone preference, the same shape as i18n-context and theme-context.
|
|
5
5
|
|
|
6
6
|
export interface TimezoneSelection {
|
|
7
|
+
/** The zone to read times in: the choice, or the browser's own where there is none. */
|
|
7
8
|
current: string;
|
|
9
|
+
/**
|
|
10
|
+
* ⚠️ The zone somebody actually CHOSE — `null` while they never did (#256). `current` cannot
|
|
11
|
+
* answer this: it falls back to the browser, and a screen that treated that as a preference would
|
|
12
|
+
* annotate every schedule with a guess nobody made.
|
|
13
|
+
*/
|
|
14
|
+
chosen: string | null;
|
|
8
15
|
select(zone: string): void;
|
|
9
16
|
}
|
|
10
17
|
|
|
@@ -16,17 +23,18 @@ interface TimezoneProviderProps {
|
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
export function TimezoneProvider({ store, children }: TimezoneProviderProps) {
|
|
19
|
-
const [
|
|
26
|
+
const [chosen, setChosen] = useState(() => readChosenTimezone(store));
|
|
20
27
|
|
|
21
28
|
const selection = useMemo<TimezoneSelection>(
|
|
22
29
|
() => ({
|
|
23
|
-
current,
|
|
30
|
+
current: chosen ?? browserTimezone(),
|
|
31
|
+
chosen,
|
|
24
32
|
select: (zone) => {
|
|
25
33
|
rememberTimezone(zone, store);
|
|
26
|
-
|
|
34
|
+
setChosen(zone);
|
|
27
35
|
},
|
|
28
36
|
}),
|
|
29
|
-
[
|
|
37
|
+
[chosen, store],
|
|
30
38
|
);
|
|
31
39
|
|
|
32
40
|
return <TimezoneContext value={selection}>{children}</TimezoneContext>;
|
|
@@ -43,3 +51,15 @@ export function useTimezone(): string {
|
|
|
43
51
|
const selection = useContext(TimezoneContext);
|
|
44
52
|
return selection?.current ?? browserTimezone();
|
|
45
53
|
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The zone the reader CHOSE, or `null` (#256).
|
|
57
|
+
*
|
|
58
|
+
* ⚠️ Not `useTimezone`. This is for screens that add the reader's own reading BESIDE a time that is
|
|
59
|
+
* already correct — the agent's calendar. Nothing may be added for somebody who never set a zone:
|
|
60
|
+
* the browser's guess presented as "your time" is a setting the reader did not make, and it would
|
|
61
|
+
* put a second time on every row of a screen that was fine as it was.
|
|
62
|
+
*/
|
|
63
|
+
export function useChosenTimezone(): string | null {
|
|
64
|
+
return useContext(TimezoneContext)?.chosen ?? null;
|
|
65
|
+
}
|
package/src/timezone/timezone.ts
CHANGED
|
@@ -39,16 +39,31 @@ export function isKnownTimezone(zone: string): boolean {
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
/**
|
|
43
|
+
* The zone the reader actually CHOSE, or `null` when they never did.
|
|
44
|
+
*
|
|
45
|
+
* ⚠️ The difference from `readTimezone` is the whole reason this exists (#256). A screen that wants
|
|
46
|
+
* to add "…and this is what it says on your clock" may only do so for a zone somebody set: falling
|
|
47
|
+
* back to the browser would put a guess on screen dressed as a preference, and the agent's calendar
|
|
48
|
+
* would start annotating every schedule for a reader who never asked for it.
|
|
49
|
+
*
|
|
50
|
+
* A stored zone the engine no longer knows counts as "never chose": IANA renames zones, and a name
|
|
51
|
+
* that stopped resolving would otherwise throw on every date the calendar draws.
|
|
52
|
+
*/
|
|
53
|
+
export function readChosenTimezone(store?: Pick<Storage, "getItem">): string | null {
|
|
45
54
|
let stored: string | null = null;
|
|
46
55
|
try {
|
|
47
56
|
stored = store?.getItem(STORE_KEY) ?? null;
|
|
48
57
|
} catch {
|
|
49
|
-
return
|
|
58
|
+
return null;
|
|
50
59
|
}
|
|
51
|
-
return stored !== null && isKnownTimezone(stored) ? stored :
|
|
60
|
+
return stored !== null && isKnownTimezone(stored) ? stored : null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// The zone to read times in — the choice, and the browser's own where there is none. Never null: a
|
|
64
|
+
// component that has to format a date needs an answer, and the browser's is the honest one.
|
|
65
|
+
export function readTimezone(store?: Pick<Storage, "getItem">): string {
|
|
66
|
+
return readChosenTimezone(store) ?? browserTimezone();
|
|
52
67
|
}
|
|
53
68
|
|
|
54
69
|
export function rememberTimezone(zone: string, store?: Pick<Storage, "setItem">): void {
|
|
@@ -11,10 +11,11 @@ import { ResourceMenu, type ResourceTarget } from "@/resource-menu/resource-menu
|
|
|
11
11
|
* the rule could be forgotten. That is the whole point: the menu holds rename, move, share and
|
|
12
12
|
* archive, and a position one has to look for is a position one stops using.
|
|
13
13
|
*
|
|
14
|
-
* The
|
|
15
|
-
* is. `title-meta` is a quiet word beside the name (a table's row count); `title-
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* The three slots are for what belongs to this thing but lives further down the tree, where its
|
|
15
|
+
* state is. `title-meta` is a quiet word beside the name (a table's row count); `title-byline` is a
|
|
16
|
+
* quiet line under it (who made an agent, #258); `title-actions` is a button in the group (saving a
|
|
17
|
+
* document, exporting a table). All are filled through `ActionSlot`, and the two in the group sit
|
|
18
|
+
* before the menu for the same reason `children` do.
|
|
18
19
|
*/
|
|
19
20
|
export function TitleRow({
|
|
20
21
|
title,
|
|
@@ -47,6 +48,10 @@ export function TitleRow({
|
|
|
47
48
|
<h2 className="truncate text-lg font-semibold">{title}</h2>
|
|
48
49
|
<span data-slot="title-meta" className="text-sm text-muted-foreground" />
|
|
49
50
|
</div>
|
|
51
|
+
{/* ⚠️ `empty:hidden` is the whole rule for "no line rather than an empty one": nothing is
|
|
52
|
+
portalled in here for most kinds, and a slot that kept its margin would make every
|
|
53
|
+
head a few pixels taller for a line nobody wrote (#258). */}
|
|
54
|
+
<span data-slot="title-byline" className="mt-0.5 block min-w-0 empty:hidden" />
|
|
50
55
|
{description ? <p className="mt-1 text-sm text-muted-foreground">{description}</p> : null}
|
|
51
56
|
</div>
|
|
52
57
|
</div>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { SessionUser } from "@anchrd/intel-contract";
|
|
2
|
+
import { useQuery } from "@tanstack/react-query";
|
|
3
|
+
import { useIntelRouterContext } from "@/router/router-context.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The signed-in person, from the one query the shell already makes.
|
|
7
|
+
*
|
|
8
|
+
* ⚠️ The same query key the user footer uses, so asking here costs no second request — and the two
|
|
9
|
+
* can never disagree about who is signed in.
|
|
10
|
+
*/
|
|
11
|
+
export function useSessionUser(): SessionUser | null {
|
|
12
|
+
const { data } = useIntelRouterContext();
|
|
13
|
+
const session = useQuery({ queryKey: ["session"], queryFn: () => data.getSession() });
|
|
14
|
+
return session.data ?? null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** What a person is CALLED, by the same rule the user footer follows: the name, or the address. */
|
|
18
|
+
export function displayNameOf(user: SessionUser): string {
|
|
19
|
+
return user.name ?? user.email;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The display name behind a user id — or `null` where this browser cannot say.
|
|
24
|
+
*
|
|
25
|
+
* ⚠️ Today it can say for exactly one id: the signed-in person's. Intel has no user directory —
|
|
26
|
+
* `/session` answers from the token's own subject and deliberately carries nothing else, and the
|
|
27
|
+
* Gate SDK Intel holds (`authorize`, `interfaces`) has no lookup either. So a foreign id is not
|
|
28
|
+
* "probably a person whose name we could guess": it is unresolvable, and every caller here has to
|
|
29
|
+
* treat it as such.
|
|
30
|
+
*
|
|
31
|
+
* ⚠️ Never fall back to the id. An id under a name is not an answer to "who made this", it is the
|
|
32
|
+
* question again in smaller type — and it would leak an identifier onto a screen that has no reason
|
|
33
|
+
* to carry one. Callers show nothing instead (#258).
|
|
34
|
+
*
|
|
35
|
+
* This is the ONE place that changes when Intel gains a way to resolve a name (#261).
|
|
36
|
+
*/
|
|
37
|
+
export function useUserName(userId: string | null): string | null {
|
|
38
|
+
const me = useSessionUser();
|
|
39
|
+
if (userId === null || me === null || me.id !== userId) return null;
|
|
40
|
+
return displayNameOf(me);
|
|
41
|
+
}
|
package/tsconfig.json
CHANGED