@k2b/cloud 0.27.0 → 0.29.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 +7 -5
- package/src/_internal/cli-plugins.ts +18 -0
- package/src/_internal/define-app.ts +21 -5
- package/src/_internal/registry-validation.ts +30 -1
- package/src/_internal/runtime-context.ts +1 -0
- package/src/access/PermissionEditor.tsx +11 -4
- package/src/ai/assistant-models.ts +25 -4
- package/src/ai/audio-tool.ts +3 -10
- package/src/ai/browser-code-contracts.ts +33 -63
- package/src/ai/browser.ts +1 -0
- package/src/ai/capabilities.ts +35 -9
- package/src/ai/chat/blocks.tsx +113 -24
- package/src/ai/chat/builtin-tools.tsx +3 -1
- package/src/ai/chat/composer-adapter.ts +8 -0
- package/src/ai/chat/live-turn.browser-harness.tsx +15 -3
- package/src/ai/chat/message-actions.tsx +124 -102
- package/src/ai/chat/message-utils.ts +57 -6
- package/src/ai/chat/messages.ts +214 -0
- package/src/ai/chat/presentation.tsx +67 -28
- package/src/ai/chat/tool-groups.ts +1 -1
- package/src/ai/chat/tool-names.ts +157 -0
- package/src/ai/chat/turn-error.ts +21 -0
- package/src/ai/chat/turn-layout.ts +12 -0
- package/src/ai/chat/turn-view.tsx +69 -10
- package/src/ai/chat/user-message.tsx +19 -14
- package/src/ai/chat/visual-tools.tsx +1 -1
- package/src/ai/chat-tasks.ts +16 -3
- package/src/ai/client/controller.ts +23 -6
- package/src/ai/code-mode-skill.ts +21 -25
- package/src/ai/code-runtime-tools.ts +5 -1
- package/src/ai/code-source-contracts.ts +2 -2
- package/src/ai/compaction.ts +22 -3
- package/src/ai/data-analysis-skill.ts +3 -3
- package/src/ai/default-tools.ts +23 -17
- package/src/ai/executor.ts +262 -99
- package/src/ai/file-media-type.ts +4 -0
- package/src/ai/files-store.ts +17 -10
- package/src/ai/http.ts +11 -10
- package/src/ai/index.ts +3 -0
- package/src/ai/memories.ts +2 -0
- package/src/ai/memory-learning.ts +59 -5
- package/src/ai/memory-workflow-evidence.ts +35 -10
- package/src/ai/open-tool-calls.ts +87 -0
- package/src/ai/projects-routes.ts +5 -1
- package/src/ai/projects.ts +5 -1
- package/src/ai/protocol.ts +34 -1
- package/src/ai/provider.ts +16 -1
- package/src/ai/quota-provider.ts +4 -1
- package/src/ai/routes.ts +26 -10
- package/src/ai/run-timeout.ts +4 -5
- package/src/ai/runtime.ts +8 -0
- package/src/ai/settings.ts +54 -17
- package/src/ai/skill-seeds.ts +14 -6
- package/src/ai/store.ts +102 -34
- package/src/ai/structured.ts +2 -1
- package/src/ai/system-prompt.ts +9 -22
- package/src/ai/transcription.ts +58 -13
- package/src/ai/turn-failure.ts +104 -0
- package/src/ai/turn-guidance.ts +505 -0
- package/src/ai/turn-policy.ts +2 -1
- package/src/ai/types.ts +29 -1
- package/src/ai/ui.tsx +1 -0
- package/src/api/admin-core-settings.ts +16 -1
- package/src/api/admin-outgoing-mail.ts +185 -5
- package/src/api/me.ts +42 -1
- package/src/browser/FileChooser.tsx +82 -295
- package/src/browser/FileSaver.tsx +407 -0
- package/src/browser/app-badges.ts +16 -0
- package/src/browser/choose-files.browser-harness.tsx +22 -6
- package/src/browser/choose-files.tsx +23 -36
- package/src/browser/file-chooser-messages.ts +66 -0
- package/src/browser/file-providers.ts +258 -8
- package/src/browser/files.tsx +26 -3
- package/src/browser/notifications.ts +37 -1
- package/src/browser/provider-folder.tsx +301 -0
- package/src/browser/provider-list.ts +39 -0
- package/src/browser/save-files.browser-harness.tsx +35 -0
- package/src/browser/save-files.tsx +107 -0
- package/src/cli/admin/index.ts +3 -1
- package/src/cli/admin/notifications.ts +6 -0
- package/src/cli/admin/outgoing-mail.ts +104 -7
- package/src/contracts/app.ts +10 -0
- package/src/contracts/commands.ts +3 -1
- package/src/contracts/notification-group.ts +6 -0
- package/src/contracts/notification-types.ts +6 -0
- package/src/contracts/outgoing-mail.ts +189 -1
- package/src/contracts/registry.ts +1 -0
- package/src/contracts/user-notifications.ts +74 -0
- package/src/server/middleware/request-logger.ts +11 -2
- package/src/services/accounts/avatar.ts +5 -2
- package/src/services/help/store.ts +2 -1
- package/src/services/image-metadata.ts +438 -0
- package/src/services/index.ts +12 -1
- package/src/services/logging/redaction.ts +50 -0
- package/src/services/mandates/index.ts +4 -2
- package/src/services/mandates/policy.ts +20 -0
- package/src/services/notifications/batches.ts +139 -79
- package/src/services/notifications/browser.ts +48 -15
- package/src/services/notifications/channels.ts +33 -13
- package/src/services/notifications/dispatcher.ts +88 -6
- package/src/services/notifications/email-frame.fixture.html +51 -0
- package/src/services/notifications/{email.ts → email-frame.ts} +12 -35
- package/src/services/notifications/email-mail.ts +101 -0
- package/src/services/notifications/index.ts +49 -36
- package/src/services/notifications/observability.ts +9 -1
- package/src/services/notifications/platform.ts +33 -5
- package/src/services/notifications/preview.ts +23 -0
- package/src/services/notifications/quiet.ts +153 -0
- package/src/services/notifications/runtime.ts +9 -3
- package/src/services/notifications/user.ts +6 -0
- package/src/services/outgoing-mail/admin.ts +84 -0
- package/src/services/outgoing-mail/attachments.ts +135 -0
- package/src/services/outgoing-mail/bulk.ts +11 -0
- package/src/services/outgoing-mail/dispatcher.ts +231 -0
- package/src/services/outgoing-mail/drain.ts +60 -0
- package/src/services/outgoing-mail/enqueue.ts +180 -0
- package/src/services/outgoing-mail/index.ts +103 -10
- package/src/services/outgoing-mail/message.ts +14 -0
- package/src/services/outgoing-mail/messages.ts +431 -0
- package/src/services/outgoing-mail/retention.ts +48 -0
- package/src/services/outgoing-mail/runtime.ts +72 -0
- package/src/services/outgoing-mail/send.ts +130 -0
- package/src/services/outgoing-mail/store.ts +115 -4
- package/src/services/outgoing-mail/sync.ts +39 -0
- package/src/services/outgoing-mail/transport.ts +5 -1
- package/src/services/pdf/markdown.ts +22 -4
- package/src/services/pdf/template-preview.ts +2 -1
- package/src/services/postgres.ts +15 -0
- package/src/services/settings/app.ts +3 -0
- package/src/services/settings/core-settings.ts +18 -0
- package/src/services/settings/store.ts +4 -1
- package/src/services/weather/geo.ts +6 -2
- package/src/shared/ai-platform-prompt.ts +48 -4
- package/src/shared/markdown/extensions/links.ts +28 -20
- package/src/shared/markdown/index.ts +14 -5
- package/src/shared/markdown/shared.ts +0 -7
- package/src/shared/redirect.ts +2 -1
- package/src/shared/template-rendering.ts +120 -7
- package/src/ssr/AppBadge.tsx +30 -0
- package/src/ssr/AppLaunchpadPanel.tsx +47 -26
- package/src/ssr/GlobalAnnouncements.island.tsx +1 -1
- package/src/ssr/IslandError.tsx +33 -0
- package/src/ssr/Layout.tsx +3 -0
- package/src/ssr/LayoutHelp.tsx +4 -2
- package/src/ssr/RailApps.island.tsx +9 -2
- package/src/ssr/app-badges.ts +93 -0
- package/src/ssr/platform-messages.ts +1 -1
- package/src/ssr/rail-messages.ts +4 -0
- package/src/ssr/rail-navigation.ts +2 -0
- package/src/styles/effects.css +38 -17
- package/src/styles/file-chooser.css +25 -0
- package/src/styles/tokens.css +2 -0
- package/src/styles/utilities-markdown-editor.css +4 -39
- package/src/styles/utilities-markdown-table.css +14 -17
- package/src/styles/utilities-navigation.css +41 -0
package/src/ai/chat/messages.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { i18n } from "@k2b/stdlib";
|
|
2
|
+
import type { AiTurnError } from "../types";
|
|
2
3
|
|
|
3
4
|
const messages = i18n.define({
|
|
4
5
|
baseLocale: "en",
|
|
@@ -21,6 +22,25 @@ const messages = i18n.define({
|
|
|
21
22
|
thinking: "Thinking",
|
|
22
23
|
showReasoning: "Show reasoning",
|
|
23
24
|
reconnecting: "Reconnecting",
|
|
25
|
+
turnErrorTitle: "The answer was interrupted.",
|
|
26
|
+
turnErrorReason: ({ code, limitMinutes }: AiTurnError) => {
|
|
27
|
+
if (code === "model_unavailable") return "The model service did not answer.";
|
|
28
|
+
if (code === "provider_stopped") return "The model provider ended this answer early.";
|
|
29
|
+
if (code === "quota_exhausted") return "Your AI usage limit for this period is reached. You can continue once it resets.";
|
|
30
|
+
if (code === "context_full") return "This chat is too long for the model. Start a new chat to continue; this one stays as it is.";
|
|
31
|
+
if (code === "time_limit")
|
|
32
|
+
return limitMinutes ? `The run time limit of ${limitMinutes} minutes was reached.` : "The run time limit was reached.";
|
|
33
|
+
if (code === "step_limit") return "The model reached its step limit for one answer.";
|
|
34
|
+
if (code === "wait_expired") return "The approval or answer did not arrive in time.";
|
|
35
|
+
if (code === "interrupted") return "The work was interrupted several times and could not be resumed.";
|
|
36
|
+
if (code === "not_allowed")
|
|
37
|
+
return "This chat may no longer use its model, project, or apps. Choose another model or ask your administrator.";
|
|
38
|
+
return "Something went wrong.";
|
|
39
|
+
},
|
|
40
|
+
turnErrorKept: "The results so far are kept.",
|
|
41
|
+
turnErrorContinueHint: "Send a new message to continue.",
|
|
42
|
+
continueTurn: "Continue",
|
|
43
|
+
continueTurnMessage: "Continue where you left off.",
|
|
24
44
|
byteRange: ({ start, end }: { start: string; end: string }) => `Bytes ${start}–${end}`,
|
|
25
45
|
worked: "Worked",
|
|
26
46
|
workedFor: ({ duration }: { duration: string }) => `Worked ${duration}`,
|
|
@@ -52,6 +72,7 @@ const messages = i18n.define({
|
|
|
52
72
|
stepWritingCode: "Writing code",
|
|
53
73
|
stepRunningCode: "Running code",
|
|
54
74
|
stepCheckingApp: "Checking the app",
|
|
75
|
+
stepCheckingRun: "Checking the run",
|
|
55
76
|
stepCode: "Working with code",
|
|
56
77
|
stepPlan: "Updating the working plan",
|
|
57
78
|
stepPdf: "Creating a PDF",
|
|
@@ -68,6 +89,11 @@ const messages = i18n.define({
|
|
|
68
89
|
stepFailed: "failed",
|
|
69
90
|
stepRejected: "rejected",
|
|
70
91
|
stepWaiting: "waiting",
|
|
92
|
+
checkPassed: "passed",
|
|
93
|
+
checkNotPassed: "not passed",
|
|
94
|
+
checkFindings: ({ count }: { count: number }) => (count === 1 ? "1 finding" : `${count} findings`),
|
|
95
|
+
checkWarnings: ({ count }: { count: number }) => (count === 1 ? "1 warning" : `${count} warnings`),
|
|
96
|
+
openImage: ({ name }: { name: string }) => `Open image ${name}`,
|
|
71
97
|
steered: "Conversation steered",
|
|
72
98
|
reasoning: "Reasoning",
|
|
73
99
|
groupPlan: "Updated working plan",
|
|
@@ -187,6 +213,87 @@ const messages = i18n.define({
|
|
|
187
213
|
calculate: "Calculate",
|
|
188
214
|
inspectImage: ({ name }: { name: string }) => `Inspect image: ${name}`,
|
|
189
215
|
readResource: ({ ref }: { ref: string }) => `Read resource: ${ref}`,
|
|
216
|
+
actionHelpful: "Helpful",
|
|
217
|
+
actionHelpfulRemove: "Remove positive feedback",
|
|
218
|
+
actionImprove: "Needs improvement",
|
|
219
|
+
actionImproveRemove: "Remove negative feedback",
|
|
220
|
+
actionMessageInfo: "Response details",
|
|
221
|
+
actionCopy: "Copy",
|
|
222
|
+
actionFork: "Continue in a new chat",
|
|
223
|
+
actionTryAgain: "Try again",
|
|
224
|
+
actionMoreDetailed: "More detailed",
|
|
225
|
+
actionMoreConcise: "More concise",
|
|
226
|
+
actionEditPrompt: "Edit and try again",
|
|
227
|
+
actionRetryGuidance: "Send guidance again",
|
|
228
|
+
cancel: "Cancel",
|
|
229
|
+
forkTitle: "Continue in a new chat",
|
|
230
|
+
forkConfirm: "Create chat",
|
|
231
|
+
forkName: "Chat name",
|
|
232
|
+
forkDefaultName: "Continued chat",
|
|
233
|
+
retryTitle: "Edit and try again",
|
|
234
|
+
retryPrompt: "Message",
|
|
235
|
+
retryPromptHint: "Attachments from the original message stay attached.",
|
|
236
|
+
retryFailed: "Could not try again. Your changes are still here.",
|
|
237
|
+
retrying: "Trying again",
|
|
238
|
+
feedbackTitle: "Help improve this response",
|
|
239
|
+
feedbackIntro: "What should be better? Choose all that apply or leave a short note.",
|
|
240
|
+
feedbackDetails: "Anything else",
|
|
241
|
+
feedbackDetailsHint: "Optional. Please leave out confidential information.",
|
|
242
|
+
feedbackSend: "Send feedback",
|
|
243
|
+
feedbackIncorrect: "Incorrect",
|
|
244
|
+
feedbackIncorrectHint: "Facts or conclusions were wrong.",
|
|
245
|
+
feedbackIgnoredRequest: "Did not do what I asked",
|
|
246
|
+
feedbackIgnoredRequestHint: "Important instructions were missed.",
|
|
247
|
+
feedbackIncomplete: "Incomplete",
|
|
248
|
+
feedbackIncompleteHint: "Important information or work was missing.",
|
|
249
|
+
feedbackWrongApp: "Used the wrong app or tool",
|
|
250
|
+
feedbackWrongAppHint: "Something was missing, unnecessary, or used badly.",
|
|
251
|
+
feedbackSlow: "Too slow",
|
|
252
|
+
feedbackSlowHint: "The answer took too long for the result.",
|
|
253
|
+
feedbackOther: "Something else",
|
|
254
|
+
feedbackOtherHint: "Something else should be better.",
|
|
255
|
+
infoSubtitle: "Technical details of this answer",
|
|
256
|
+
infoDetails: "Details",
|
|
257
|
+
infoProviderModel: "Provider model",
|
|
258
|
+
infoModelProfile: "Model",
|
|
259
|
+
infoLoopId: "Run ID",
|
|
260
|
+
infoFinished: "Ended",
|
|
261
|
+
infoEndCompleted: "Completed",
|
|
262
|
+
infoEndStopped: "Stopped",
|
|
263
|
+
infoEndFailed: "Failed",
|
|
264
|
+
infoEndInterrupted: "Interrupted",
|
|
265
|
+
infoEndStepLimit: "Step limit reached",
|
|
266
|
+
infoEndUsageLimit: "Usage limit reached",
|
|
267
|
+
infoEndContextFull: "Context window full",
|
|
268
|
+
infoEndLengthLimit: "Answer length limit reached",
|
|
269
|
+
infoCreated: "Created",
|
|
270
|
+
infoUnknown: "Unknown",
|
|
271
|
+
infoLegacy: "Older message",
|
|
272
|
+
infoTiming: "Time",
|
|
273
|
+
infoWorked: "Worked",
|
|
274
|
+
infoWorkedHint: "writing and tools",
|
|
275
|
+
infoGeneration: "Writing",
|
|
276
|
+
infoToolTime: "Tools",
|
|
277
|
+
infoWaiting: "Waiting for you",
|
|
278
|
+
infoWaitingHint: "approvals and answers",
|
|
279
|
+
infoWallClock: "Total time",
|
|
280
|
+
infoSpeed: "Writing speed",
|
|
281
|
+
infoTokensPerSecond: ({ value }: { value: string }) => `${value} tokens/s`,
|
|
282
|
+
infoUsage: "Usage",
|
|
283
|
+
infoInputTokens: "Input tokens",
|
|
284
|
+
infoOutputTokens: "Output tokens",
|
|
285
|
+
infoTotalTokens: "Total tokens",
|
|
286
|
+
infoTools: "Tools",
|
|
287
|
+
infoToolsHint: "Tools this answer used.",
|
|
288
|
+
infoIssueErrors: "errors",
|
|
289
|
+
infoIssueStream: "connection problems",
|
|
290
|
+
infoIssueMalformed: "malformed",
|
|
291
|
+
infoIssueCancelled: "cancelled",
|
|
292
|
+
compactedCount: ({ count, date }: { count: number; date: string }) =>
|
|
293
|
+
`${count === 1 ? "1 message" : `${count} messages`} summarized · ${date}`,
|
|
294
|
+
compactedHint: "From here on, the model sees this summary instead of the messages above.",
|
|
295
|
+
compactedEmpty: "No visible content",
|
|
296
|
+
malformedImage: "This image could not be read. Export it again or choose another file.",
|
|
190
297
|
},
|
|
191
298
|
de: {
|
|
192
299
|
backgroundRun: "Hintergrundlauf",
|
|
@@ -207,6 +314,27 @@ const messages = i18n.define({
|
|
|
207
314
|
thinking: "Denkt nach",
|
|
208
315
|
showReasoning: "Denkprozess anzeigen",
|
|
209
316
|
reconnecting: "Verbindung wird wiederhergestellt",
|
|
317
|
+
turnErrorTitle: "Die Antwort wurde abgebrochen.",
|
|
318
|
+
turnErrorReason: ({ code, limitMinutes }) => {
|
|
319
|
+
if (code === "model_unavailable") return "Der KI-Dienst hat nicht geantwortet.";
|
|
320
|
+
if (code === "provider_stopped") return "Der KI-Anbieter hat diese Antwort vorzeitig beendet.";
|
|
321
|
+
if (code === "quota_exhausted")
|
|
322
|
+
return "Dein KI-Kontingent für diesen Zeitraum ist aufgebraucht. Du kannst weiterarbeiten, sobald es sich erneuert.";
|
|
323
|
+
if (code === "context_full")
|
|
324
|
+
return "Dieser Chat ist zu lang für das Modell. Starte einen neuen Chat, um weiterzuarbeiten; dieser bleibt erhalten.";
|
|
325
|
+
if (code === "time_limit")
|
|
326
|
+
return limitMinutes ? `Das Laufzeitlimit von ${limitMinutes} Minuten ist erreicht.` : "Das Laufzeitlimit ist erreicht.";
|
|
327
|
+
if (code === "step_limit") return "Das Modell hat sein Schrittlimit für eine Antwort erreicht.";
|
|
328
|
+
if (code === "wait_expired") return "Die Freigabe oder Antwort kam nicht rechtzeitig.";
|
|
329
|
+
if (code === "interrupted") return "Die Arbeit wurde mehrmals unterbrochen und konnte nicht fortgesetzt werden.";
|
|
330
|
+
if (code === "not_allowed")
|
|
331
|
+
return "Dieser Chat darf sein Modell, sein Projekt oder seine Apps nicht mehr nutzen. Wähle ein anderes Modell oder wende dich an deine Administration.";
|
|
332
|
+
return "Etwas ist schiefgelaufen.";
|
|
333
|
+
},
|
|
334
|
+
turnErrorKept: "Die bisherigen Ergebnisse bleiben erhalten.",
|
|
335
|
+
turnErrorContinueHint: "Mit einer neuen Nachricht geht es weiter.",
|
|
336
|
+
continueTurn: "Weiterarbeiten",
|
|
337
|
+
continueTurnMessage: "Mach an der Stelle weiter, an der du aufgehört hast.",
|
|
210
338
|
byteRange: ({ start, end }) => `Bytes ${start}–${end}`,
|
|
211
339
|
worked: "Gearbeitet",
|
|
212
340
|
workedFor: ({ duration }) => `${duration} gearbeitet`,
|
|
@@ -242,6 +370,7 @@ const messages = i18n.define({
|
|
|
242
370
|
stepWritingCode: "Schreibt Code",
|
|
243
371
|
stepRunningCode: "Führt Code aus",
|
|
244
372
|
stepCheckingApp: "Prüft die App",
|
|
373
|
+
stepCheckingRun: "Prüft den Lauf",
|
|
245
374
|
stepCode: "Arbeitet mit Code",
|
|
246
375
|
stepPlan: "Aktualisiert den Arbeitsplan",
|
|
247
376
|
stepPdf: "Erstellt ein PDF",
|
|
@@ -258,6 +387,11 @@ const messages = i18n.define({
|
|
|
258
387
|
stepFailed: "fehlgeschlagen",
|
|
259
388
|
stepRejected: "abgelehnt",
|
|
260
389
|
stepWaiting: "wartet",
|
|
390
|
+
checkPassed: "bestanden",
|
|
391
|
+
checkNotPassed: "nicht bestanden",
|
|
392
|
+
checkFindings: ({ count }) => (count === 1 ? "1 Befund" : `${count} Befunde`),
|
|
393
|
+
checkWarnings: ({ count }) => (count === 1 ? "1 Warnung" : `${count} Warnungen`),
|
|
394
|
+
openImage: ({ name }) => `Bild ${name} öffnen`,
|
|
261
395
|
steered: "Richtung geändert",
|
|
262
396
|
reasoning: "Überlegungen",
|
|
263
397
|
groupPlan: "Arbeitsplan aktualisiert",
|
|
@@ -377,6 +511,86 @@ const messages = i18n.define({
|
|
|
377
511
|
calculate: "Berechnen",
|
|
378
512
|
inspectImage: ({ name }) => `Bild prüfen: ${name}`,
|
|
379
513
|
readResource: ({ ref }) => `Ressource lesen: ${ref}`,
|
|
514
|
+
actionHelpful: "Hilfreich",
|
|
515
|
+
actionHelpfulRemove: "Positive Bewertung entfernen",
|
|
516
|
+
actionImprove: "Verbesserungswürdig",
|
|
517
|
+
actionImproveRemove: "Negative Bewertung entfernen",
|
|
518
|
+
actionMessageInfo: "Details zur Antwort",
|
|
519
|
+
actionCopy: "Kopieren",
|
|
520
|
+
actionFork: "In neuem Chat fortsetzen",
|
|
521
|
+
actionTryAgain: "Noch einmal versuchen",
|
|
522
|
+
actionMoreDetailed: "Ausführlicher",
|
|
523
|
+
actionMoreConcise: "Kürzer",
|
|
524
|
+
actionEditPrompt: "Bearbeiten und neu senden",
|
|
525
|
+
actionRetryGuidance: "Hinweis erneut senden",
|
|
526
|
+
cancel: "Abbrechen",
|
|
527
|
+
forkTitle: "In neuem Chat fortsetzen",
|
|
528
|
+
forkConfirm: "Chat erstellen",
|
|
529
|
+
forkName: "Name des Chats",
|
|
530
|
+
forkDefaultName: "Fortgesetzter Chat",
|
|
531
|
+
retryTitle: "Bearbeiten und neu senden",
|
|
532
|
+
retryPrompt: "Nachricht",
|
|
533
|
+
retryPromptHint: "Die Anhänge der ursprünglichen Nachricht bleiben erhalten.",
|
|
534
|
+
retryFailed: "Das hat nicht geklappt. Deine Änderungen sind noch da.",
|
|
535
|
+
retrying: "Wird gesendet",
|
|
536
|
+
feedbackTitle: "Hilf, diese Antwort zu verbessern",
|
|
537
|
+
feedbackIntro: "Was sollte besser sein? Wähle alles Passende aus oder schreib eine kurze Notiz.",
|
|
538
|
+
feedbackDetails: "Sonst noch etwas",
|
|
539
|
+
feedbackDetailsHint: "Optional. Bitte keine vertraulichen Daten angeben.",
|
|
540
|
+
feedbackSend: "Feedback senden",
|
|
541
|
+
feedbackIncorrect: "Falsch",
|
|
542
|
+
feedbackIncorrectHint: "Fakten oder Schlussfolgerungen stimmten nicht.",
|
|
543
|
+
feedbackIgnoredRequest: "Hat nicht getan, worum ich gebeten habe",
|
|
544
|
+
feedbackIgnoredRequestHint: "Wichtige Anweisungen wurden übersehen.",
|
|
545
|
+
feedbackIncomplete: "Unvollständig",
|
|
546
|
+
feedbackIncompleteHint: "Wichtige Informationen oder Arbeitsschritte fehlten.",
|
|
547
|
+
feedbackWrongApp: "Falsche App oder falsches Werkzeug",
|
|
548
|
+
feedbackWrongAppHint: "Etwas fehlte, war unnötig oder wurde falsch genutzt.",
|
|
549
|
+
feedbackSlow: "Zu langsam",
|
|
550
|
+
feedbackSlowHint: "Die Antwort hat für das Ergebnis zu lange gedauert.",
|
|
551
|
+
feedbackOther: "Etwas anderes",
|
|
552
|
+
feedbackOtherHint: "Etwas anderes sollte besser werden.",
|
|
553
|
+
infoSubtitle: "Technische Angaben zu dieser Antwort",
|
|
554
|
+
infoDetails: "Details",
|
|
555
|
+
infoProviderModel: "Modell beim Anbieter",
|
|
556
|
+
infoModelProfile: "Modell",
|
|
557
|
+
infoLoopId: "Durchlauf-ID",
|
|
558
|
+
infoFinished: "Beendet",
|
|
559
|
+
infoEndCompleted: "Abgeschlossen",
|
|
560
|
+
infoEndStopped: "Gestoppt",
|
|
561
|
+
infoEndFailed: "Fehlgeschlagen",
|
|
562
|
+
infoEndInterrupted: "Unterbrochen",
|
|
563
|
+
infoEndStepLimit: "Schrittlimit erreicht",
|
|
564
|
+
infoEndUsageLimit: "Nutzungslimit erreicht",
|
|
565
|
+
infoEndContextFull: "Kontextfenster voll",
|
|
566
|
+
infoEndLengthLimit: "Maximale Antwortlänge erreicht",
|
|
567
|
+
infoCreated: "Erstellt",
|
|
568
|
+
infoUnknown: "Unbekannt",
|
|
569
|
+
infoLegacy: "Ältere Nachricht",
|
|
570
|
+
infoTiming: "Zeit",
|
|
571
|
+
infoWorked: "Gearbeitet",
|
|
572
|
+
infoWorkedHint: "Schreiben und Werkzeuge",
|
|
573
|
+
infoGeneration: "Schreiben",
|
|
574
|
+
infoToolTime: "Werkzeuge",
|
|
575
|
+
infoWaiting: "Wartezeit auf dich",
|
|
576
|
+
infoWaitingHint: "Freigaben und Antworten",
|
|
577
|
+
infoWallClock: "Gesamtdauer",
|
|
578
|
+
infoSpeed: "Schreibtempo",
|
|
579
|
+
infoTokensPerSecond: ({ value }) => `${value} Token/s`,
|
|
580
|
+
infoUsage: "Verbrauch",
|
|
581
|
+
infoInputTokens: "Eingabe-Token",
|
|
582
|
+
infoOutputTokens: "Ausgabe-Token",
|
|
583
|
+
infoTotalTokens: "Token gesamt",
|
|
584
|
+
infoTools: "Werkzeuge",
|
|
585
|
+
infoToolsHint: "Werkzeuge, die diese Antwort genutzt hat.",
|
|
586
|
+
infoIssueErrors: "Fehler",
|
|
587
|
+
infoIssueStream: "Verbindungsprobleme",
|
|
588
|
+
infoIssueMalformed: "fehlerhaft",
|
|
589
|
+
infoIssueCancelled: "abgebrochen",
|
|
590
|
+
compactedCount: ({ count, date }) => `${count === 1 ? "1 Nachricht" : `${count} Nachrichten`} zusammengefasst · ${date}`,
|
|
591
|
+
compactedHint: "Ab hier sieht das Modell diese Zusammenfassung statt der Nachrichten darüber.",
|
|
592
|
+
compactedEmpty: "Kein sichtbarer Inhalt",
|
|
593
|
+
malformedImage: "Dieses Bild konnte nicht gelesen werden. Exportiere es erneut oder wähle eine andere Datei.",
|
|
380
594
|
},
|
|
381
595
|
},
|
|
382
596
|
});
|
|
@@ -15,11 +15,12 @@ import {
|
|
|
15
15
|
import type { AiActiveTurn } from "../client/projection";
|
|
16
16
|
import { type AiActiveTurnSegment, type AiTurnBlock, splitActiveTurnBlocks } from "../protocol";
|
|
17
17
|
import { type AiAssistantTimelineItem, buildAiMessageTimeline } from "../timeline";
|
|
18
|
-
import type { AiConversationTimelineEntry, AiStoredMessage } from "../types";
|
|
18
|
+
import type { AiConversationTimelineEntry, AiStoredMessage, AiTurnError } from "../types";
|
|
19
19
|
import { type AiChatActions, AiChatActionsProvider, createAssistantMessageActions, useAiChatActions } from "./message-actions";
|
|
20
20
|
import { isRecord, isSurveyToolName, textFromMessage } from "./message-utils";
|
|
21
|
+
import { aiChatMessages } from "./messages";
|
|
21
22
|
import { type AiToolDisclosureState, createAiToolDisclosureState } from "./tool-disclosure";
|
|
22
|
-
import { type AiTurnLayout, type AiTurnPhase, layoutAiTurn, waitsForUser } from "./turn-layout";
|
|
23
|
+
import { type AiTurnLayout, type AiTurnPhase, layoutAiTurn, storedPhase, waitsForUser } from "./turn-layout";
|
|
23
24
|
import { TurnNavigator } from "./turn-navigator";
|
|
24
25
|
import { activeTimelineSeq } from "./turn-navigator-utils";
|
|
25
26
|
import { type AiTurnDuration, type AiTurnSegment, AiTurnView } from "./turn-view";
|
|
@@ -101,15 +102,9 @@ const leadingSteerOpener = (blocks: readonly AssistantBlock[]): string | null =>
|
|
|
101
102
|
return opener;
|
|
102
103
|
};
|
|
103
104
|
|
|
104
|
-
/**
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
if (!reason || reason === "stop") return "completed";
|
|
108
|
-
return reason === "aborted" ? "stopped" : "failed";
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
/** Responses with a work line or rich places span the message column so their right edges align. */
|
|
112
|
-
const isWideLayout = (layout: AiTurnLayout) => layout.showWork || layout.results.length > 0 || layout.actions.length > 0;
|
|
105
|
+
/** Responses with a work line, rich places, or a failure notice span the message column so their right edges align. */
|
|
106
|
+
const isWideLayout = (layout: AiTurnLayout, error?: AiTurnError) =>
|
|
107
|
+
layout.showWork || layout.results.length > 0 || layout.actions.length > 0 || Boolean(error);
|
|
113
108
|
|
|
114
109
|
const isEmptyLayout = (layout: AiTurnLayout) => !isWideLayout(layout) && layout.text.length === 0;
|
|
115
110
|
|
|
@@ -162,13 +157,49 @@ const storedItems = (
|
|
|
162
157
|
actions: AiChatActions,
|
|
163
158
|
views: TurnViews,
|
|
164
159
|
locale: string,
|
|
160
|
+
idle: boolean,
|
|
165
161
|
): ChatTimelineItem[] => {
|
|
166
162
|
const timeline = buildAiMessageTimeline([...messages]);
|
|
167
163
|
const lastItemOfLoop = new Map<string, number>();
|
|
164
|
+
// A failed turn's notice ends its last item, which is its user message when the model never answered.
|
|
165
|
+
const lastEntryOfLoop = new Map<string, number>();
|
|
168
166
|
timeline.forEach((item, index) => {
|
|
169
|
-
|
|
167
|
+
const loopId = item.type === "summary" ? null : item.type === "user" ? item.entry.loopId : item.loopId;
|
|
168
|
+
if (!loopId) return;
|
|
169
|
+
lastEntryOfLoop.set(loopId, index);
|
|
170
|
+
if (item.type === "assistant") lastItemOfLoop.set(loopId, index);
|
|
170
171
|
});
|
|
172
|
+
const turnErrors = new Map<string, AiTurnError>();
|
|
173
|
+
for (const message of messages) if (message.loopId && message.meta?.turnError) turnErrors.set(message.loopId, message.meta.turnError);
|
|
174
|
+
// Only the chat's newest turn can be continued, and only while no other turn runs.
|
|
175
|
+
const newestLoop = idle ? (messages.at(-1)?.loopId ?? null) : null;
|
|
171
176
|
const segmentId = createSegmentIds();
|
|
177
|
+
// The model never answered: the notice stands where the live turn showed its progress, under the same id.
|
|
178
|
+
const unansweredNotice = (entry: AiStoredMessage, itemIndex: number): ChatTimelineItem[] => {
|
|
179
|
+
const loopId = entry.loopId;
|
|
180
|
+
const error = loopId && lastEntryOfLoop.get(loopId) === itemIndex ? turnErrors.get(loopId) : undefined;
|
|
181
|
+
if (!loopId || !error) return [];
|
|
182
|
+
const id = segmentId(loopId, "start");
|
|
183
|
+
return [
|
|
184
|
+
{
|
|
185
|
+
kind: "message",
|
|
186
|
+
id,
|
|
187
|
+
role: "assistant",
|
|
188
|
+
createdAt: entry.createdAt,
|
|
189
|
+
class: "ai-chat-message-wide",
|
|
190
|
+
content: views.content({
|
|
191
|
+
id,
|
|
192
|
+
turnId: loopId,
|
|
193
|
+
phase: "failed",
|
|
194
|
+
layout: layoutAiTurn([], { phase: "failed" }),
|
|
195
|
+
earlier: false,
|
|
196
|
+
duration: () => null,
|
|
197
|
+
error,
|
|
198
|
+
continuable: newestLoop === loopId,
|
|
199
|
+
}),
|
|
200
|
+
},
|
|
201
|
+
];
|
|
202
|
+
};
|
|
172
203
|
return timeline.flatMap((item, itemIndex): ChatTimelineItem | ChatTimelineItem[] => {
|
|
173
204
|
if (item.type === "user") {
|
|
174
205
|
const text = aiUserMessageText(item.entry);
|
|
@@ -176,7 +207,7 @@ const storedItems = (
|
|
|
176
207
|
if (agentMessage) {
|
|
177
208
|
const separator = text.indexOf("\n\n");
|
|
178
209
|
const forwardedText = separator >= 0 ? text.slice(separator + 2) : text;
|
|
179
|
-
|
|
210
|
+
const systemItem: ChatTimelineItem = {
|
|
180
211
|
kind: "message",
|
|
181
212
|
id: item.id,
|
|
182
213
|
role: "system",
|
|
@@ -208,18 +239,20 @@ const storedItems = (
|
|
|
208
239
|
),
|
|
209
240
|
anchorId: item.entry.seq,
|
|
210
241
|
};
|
|
242
|
+
return [systemItem, ...unansweredNotice(item.entry, itemIndex)];
|
|
211
243
|
}
|
|
212
|
-
|
|
244
|
+
const userItem: ChatTimelineItem = {
|
|
213
245
|
kind: "message",
|
|
214
246
|
id: item.id,
|
|
215
247
|
role: "user",
|
|
216
248
|
createdAt: item.entry.createdAt,
|
|
217
249
|
content: text ? <p class="whitespace-pre-wrap">{text}</p> : undefined,
|
|
218
250
|
attachments: aiUserMessageAttachments(item.entry, actions),
|
|
219
|
-
actions: createAiUserMessageActions(item.entry, actions),
|
|
251
|
+
actions: createAiUserMessageActions(item.entry, actions, locale),
|
|
220
252
|
actionDisplay: "menu",
|
|
221
253
|
anchorId: item.entry.seq,
|
|
222
254
|
};
|
|
255
|
+
return [userItem, ...unansweredNotice(item.entry, itemIndex)];
|
|
223
256
|
}
|
|
224
257
|
|
|
225
258
|
if (item.type === "summary") {
|
|
@@ -228,16 +261,14 @@ const storedItems = (
|
|
|
228
261
|
return {
|
|
229
262
|
kind: "activity",
|
|
230
263
|
id: item.id,
|
|
231
|
-
label:
|
|
232
|
-
description: count ?
|
|
264
|
+
label: aiChatMessages(locale).compacted,
|
|
265
|
+
description: count ? aiChatMessages(locale).compactedCount({ count, date }) : date,
|
|
233
266
|
icon: "ti ti-brain",
|
|
234
267
|
tone: "ai",
|
|
235
268
|
content: (
|
|
236
269
|
<div>
|
|
237
|
-
<p class="mb-1 text-
|
|
238
|
-
|
|
239
|
-
</p>
|
|
240
|
-
<p class="whitespace-pre-wrap">{textFromMessage(item.entry.message) || "No visible content"}</p>
|
|
270
|
+
<p class="mb-1 text-dimmed">{aiChatMessages(locale).compactedHint}</p>
|
|
271
|
+
<p class="whitespace-pre-wrap">{textFromMessage(item.entry.message) || aiChatMessages(locale).compactedEmpty}</p>
|
|
241
272
|
</div>
|
|
242
273
|
),
|
|
243
274
|
};
|
|
@@ -245,9 +276,12 @@ const storedItems = (
|
|
|
245
276
|
|
|
246
277
|
const actionEntry = item.actionEntry?.compactedAt ? null : item.actionEntry;
|
|
247
278
|
const lastOfLoop = !item.loopId || lastItemOfLoop.get(item.loopId) === itemIndex;
|
|
248
|
-
const
|
|
279
|
+
const turnError = item.loopId && lastOfLoop ? turnErrors.get(item.loopId) : undefined;
|
|
280
|
+
const phase = turnError ? "failed" : lastOfLoop ? storedPhase(item.entries) : "completed";
|
|
249
281
|
const segments = splitSurveyResults(item.blocks);
|
|
250
|
-
|
|
282
|
+
// A turn that failed after an accepted answer ends with its notice where the live turn showed its progress, below
|
|
283
|
+
// the answer and under the same id, as activeItems() places it.
|
|
284
|
+
if (segments.length === 0 || (turnError && segments.at(-1)?.type === "survey")) segments.push({ type: "assistant", blocks: [] });
|
|
251
285
|
const lastAssistant = segments.findLastIndex((segment) => segment.type === "assistant");
|
|
252
286
|
const scheduledTask = item.entries.find((entry) => entry.meta?.scheduledTask)?.meta?.scheduledTask ?? null;
|
|
253
287
|
let firstSegment = true;
|
|
@@ -268,7 +302,8 @@ const storedItems = (
|
|
|
268
302
|
: undefined;
|
|
269
303
|
const task = firstSegment ? scheduledTask : null;
|
|
270
304
|
firstSegment = false;
|
|
271
|
-
|
|
305
|
+
const error = last ? turnError : undefined;
|
|
306
|
+
if (isEmptyLayout(layout) && !messageActions && !task && !error) return [];
|
|
272
307
|
const id = item.loopId ? segmentId(item.loopId, segmentOpener) : `${item.id}:${index}`;
|
|
273
308
|
return [
|
|
274
309
|
{
|
|
@@ -276,7 +311,7 @@ const storedItems = (
|
|
|
276
311
|
id,
|
|
277
312
|
role: "assistant",
|
|
278
313
|
createdAt: actionEntry?.createdAt ?? item.entries.at(-1)?.createdAt,
|
|
279
|
-
class: isWideLayout(layout) ? "ai-chat-message-wide" : undefined,
|
|
314
|
+
class: isWideLayout(layout, error) ? "ai-chat-message-wide" : undefined,
|
|
280
315
|
content: views.content({
|
|
281
316
|
id,
|
|
282
317
|
turnId,
|
|
@@ -285,6 +320,8 @@ const storedItems = (
|
|
|
285
320
|
earlier: !last,
|
|
286
321
|
duration: last ? () => ({ workedMs: item.workedMs, waitingMs: null }) : () => null,
|
|
287
322
|
scheduledTask: task,
|
|
323
|
+
error,
|
|
324
|
+
continuable: Boolean(error) && newestLoop === item.loopId,
|
|
288
325
|
}),
|
|
289
326
|
actions: messageActions,
|
|
290
327
|
actionDisplay: "inline",
|
|
@@ -300,6 +337,7 @@ const activeItems = (
|
|
|
300
337
|
views: TurnViews,
|
|
301
338
|
duration: Accessor<AiTurnDuration | null>,
|
|
302
339
|
reconnecting: boolean,
|
|
340
|
+
locale: string,
|
|
303
341
|
): ChatTimelineItem[] => {
|
|
304
342
|
if (!turn) return [];
|
|
305
343
|
const segments = splitActiveTurnBlocks(turn.blocks).flatMap((segment): (AiActiveTurnSegment | SurveySegment)[] =>
|
|
@@ -340,7 +378,7 @@ const activeItems = (
|
|
|
340
378
|
role: "user",
|
|
341
379
|
status: block.status === "pending" ? "pending" : block.status === "failed" ? "error" : "complete",
|
|
342
380
|
content: <p class="whitespace-pre-wrap">{aiSteerMessageText(block)}</p>,
|
|
343
|
-
actions: createAiSteerMessageActions(block, actions),
|
|
381
|
+
actions: createAiSteerMessageActions(block, actions, locale),
|
|
344
382
|
actionDisplay: "menu",
|
|
345
383
|
anchorId: turn.seq,
|
|
346
384
|
};
|
|
@@ -449,8 +487,9 @@ export function createAiChatTimeline(source: AiChatTimelineSource): Accessor<rea
|
|
|
449
487
|
// While the stream reconnects or a model call waits for its retry, the live turn says so and its clock stands.
|
|
450
488
|
const reconnecting = createMemo(() => source.activeTurn()?.providerRetry === true || source.reconnecting?.() === true);
|
|
451
489
|
const clock = createActiveTurnClock(source, reconnecting);
|
|
452
|
-
const
|
|
453
|
-
const
|
|
490
|
+
const idle = createMemo(() => source.activeTurn() === null);
|
|
491
|
+
const stored = createMemo(() => storedItems(source.messages(), actions, views, locale(), idle()));
|
|
492
|
+
const active = createMemo(() => activeItems(source.activeTurn(), actions, views, clock, reconnecting(), locale()));
|
|
454
493
|
return createMemo(() => {
|
|
455
494
|
const items = [...stored(), ...active()];
|
|
456
495
|
views.retain(new Set(items.map((item) => item.id)));
|
|
@@ -12,7 +12,7 @@ export function isFailedTool(tool: Tool): boolean {
|
|
|
12
12
|
if (tool.isError || tool.status === "failed") return true;
|
|
13
13
|
const result = tool.result;
|
|
14
14
|
return (
|
|
15
|
-
["code_run", "code_inspect"
|
|
15
|
+
["code_run", "code_inspect"].includes(tool.name) &&
|
|
16
16
|
result !== null &&
|
|
17
17
|
typeof result === "object" &&
|
|
18
18
|
"status" in result &&
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { i18n } from "@k2b/stdlib";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* People-facing names of Cloud's built-in Assistant tools, keyed by tool name. Step rows, receipts, and approvals show
|
|
5
|
+
* these; a capability tool brings its own translated title instead. A tool test fails when a built-in tool has no entry.
|
|
6
|
+
*/
|
|
7
|
+
const toolNames = i18n.define({
|
|
8
|
+
baseLocale: "en",
|
|
9
|
+
messages: {
|
|
10
|
+
en: {
|
|
11
|
+
calculate: "Calculate",
|
|
12
|
+
card: "Card",
|
|
13
|
+
survey: "Survey",
|
|
14
|
+
text_editor: "Text editor",
|
|
15
|
+
local_bash: "Local Bash",
|
|
16
|
+
todo_write: "Working plan",
|
|
17
|
+
memory: "Memory",
|
|
18
|
+
list_files: "List files",
|
|
19
|
+
read_file: "Read file",
|
|
20
|
+
write_file: "Write file",
|
|
21
|
+
fetch_file: "Download file",
|
|
22
|
+
present: "Deliver file",
|
|
23
|
+
markdown_to_pdf: "Markdown to PDF",
|
|
24
|
+
html_to_pdf: "HTML to PDF",
|
|
25
|
+
view_image: "View image",
|
|
26
|
+
transcribe_audio: "Transcribe audio",
|
|
27
|
+
web_search: "Web search",
|
|
28
|
+
web_extract: "Read web page",
|
|
29
|
+
search_tools: "Search tools",
|
|
30
|
+
load_tools: "Load tools",
|
|
31
|
+
list_apps: "List apps",
|
|
32
|
+
search_help: "Search help",
|
|
33
|
+
read_help: "Read help",
|
|
34
|
+
search_project: "Search project",
|
|
35
|
+
read_project_knowledge: "Read project knowledge",
|
|
36
|
+
read_cloud_resource: "Read Cloud item",
|
|
37
|
+
load_skill: "Load skill",
|
|
38
|
+
search_skills: "Search skills",
|
|
39
|
+
code_run: "Run code",
|
|
40
|
+
code_action: "Run app action",
|
|
41
|
+
code_inspect: "Inspect run",
|
|
42
|
+
code_stop: "Stop run",
|
|
43
|
+
code_open: "Open app",
|
|
44
|
+
code_export: "Save run output",
|
|
45
|
+
code_present: "Show app in chat",
|
|
46
|
+
code_check: "App check",
|
|
47
|
+
code_secret: "Enter secret",
|
|
48
|
+
code_database: "App tables",
|
|
49
|
+
code_files: "List stored files",
|
|
50
|
+
code_file_stat: "File details",
|
|
51
|
+
code_file_copy: "Copy file",
|
|
52
|
+
code_database_export: "Export app database",
|
|
53
|
+
code_manage_read: "Read app details",
|
|
54
|
+
code_delete: "Delete app",
|
|
55
|
+
code_database_read: "Read app database",
|
|
56
|
+
code_database_clear: "Clear app data",
|
|
57
|
+
code_database_reset: "Reset app database",
|
|
58
|
+
code_storage_list: "List app storage",
|
|
59
|
+
code_storage_delete: "Delete from app storage",
|
|
60
|
+
code_access_read: "Read app access",
|
|
61
|
+
code_access_change: "Change app access",
|
|
62
|
+
code_actions: "List app actions",
|
|
63
|
+
code_sql: "Query app database",
|
|
64
|
+
code_versions: "List app versions",
|
|
65
|
+
code_list: "Find apps",
|
|
66
|
+
code_read: "Read app source",
|
|
67
|
+
code_history: "App history",
|
|
68
|
+
code_fork: "Copy app",
|
|
69
|
+
code_publish: "Publish app",
|
|
70
|
+
code_unpublish: "Withdraw app",
|
|
71
|
+
code_restore: "Restore app version",
|
|
72
|
+
code_update: "Update app details",
|
|
73
|
+
code_create: "Create app",
|
|
74
|
+
code_write: "Save app files",
|
|
75
|
+
code_remove: "Remove app file",
|
|
76
|
+
},
|
|
77
|
+
de: {
|
|
78
|
+
calculate: "Rechnen",
|
|
79
|
+
card: "Karte",
|
|
80
|
+
survey: "Umfrage",
|
|
81
|
+
text_editor: "Texteditor",
|
|
82
|
+
local_bash: "Lokale Bash",
|
|
83
|
+
todo_write: "Arbeitsplan",
|
|
84
|
+
memory: "Gedächtnis",
|
|
85
|
+
list_files: "Dateien auflisten",
|
|
86
|
+
read_file: "Datei lesen",
|
|
87
|
+
write_file: "Datei schreiben",
|
|
88
|
+
fetch_file: "Datei herunterladen",
|
|
89
|
+
present: "Datei bereitstellen",
|
|
90
|
+
markdown_to_pdf: "Markdown als PDF",
|
|
91
|
+
html_to_pdf: "HTML als PDF",
|
|
92
|
+
view_image: "Bild ansehen",
|
|
93
|
+
transcribe_audio: "Audio transkribieren",
|
|
94
|
+
web_search: "Websuche",
|
|
95
|
+
web_extract: "Webseite lesen",
|
|
96
|
+
search_tools: "Werkzeuge suchen",
|
|
97
|
+
load_tools: "Werkzeuge laden",
|
|
98
|
+
list_apps: "Apps auflisten",
|
|
99
|
+
search_help: "Hilfe durchsuchen",
|
|
100
|
+
read_help: "Hilfe lesen",
|
|
101
|
+
search_project: "Projekt durchsuchen",
|
|
102
|
+
read_project_knowledge: "Projektwissen lesen",
|
|
103
|
+
read_cloud_resource: "Cloud-Eintrag lesen",
|
|
104
|
+
load_skill: "Skill laden",
|
|
105
|
+
search_skills: "Skills suchen",
|
|
106
|
+
code_run: "Code ausführen",
|
|
107
|
+
code_action: "App-Aktion ausführen",
|
|
108
|
+
code_inspect: "Lauf prüfen",
|
|
109
|
+
code_stop: "Lauf beenden",
|
|
110
|
+
code_open: "App öffnen",
|
|
111
|
+
code_export: "Laufergebnis speichern",
|
|
112
|
+
code_present: "App im Chat zeigen",
|
|
113
|
+
code_check: "App-Prüfung",
|
|
114
|
+
code_secret: "Geheimnis eingeben",
|
|
115
|
+
code_database: "App-Tabellen",
|
|
116
|
+
code_files: "Gespeicherte Dateien auflisten",
|
|
117
|
+
code_file_stat: "Dateidetails",
|
|
118
|
+
code_file_copy: "Datei kopieren",
|
|
119
|
+
code_database_export: "App-Datenbank exportieren",
|
|
120
|
+
code_manage_read: "App-Details lesen",
|
|
121
|
+
code_delete: "App löschen",
|
|
122
|
+
code_database_read: "App-Datenbank lesen",
|
|
123
|
+
code_database_clear: "App-Daten leeren",
|
|
124
|
+
code_database_reset: "App-Datenbank zurücksetzen",
|
|
125
|
+
code_storage_list: "App-Speicher auflisten",
|
|
126
|
+
code_storage_delete: "Aus App-Speicher löschen",
|
|
127
|
+
code_access_read: "App-Zugriff lesen",
|
|
128
|
+
code_access_change: "App-Zugriff ändern",
|
|
129
|
+
code_actions: "App-Aktionen auflisten",
|
|
130
|
+
code_sql: "App-Datenbank abfragen",
|
|
131
|
+
code_versions: "App-Versionen auflisten",
|
|
132
|
+
code_list: "Apps suchen",
|
|
133
|
+
code_read: "App-Quelltext lesen",
|
|
134
|
+
code_history: "App-Verlauf",
|
|
135
|
+
code_fork: "App kopieren",
|
|
136
|
+
code_publish: "App veröffentlichen",
|
|
137
|
+
code_unpublish: "App zurückziehen",
|
|
138
|
+
code_restore: "App-Version wiederherstellen",
|
|
139
|
+
code_update: "App-Details ändern",
|
|
140
|
+
code_create: "App erstellen",
|
|
141
|
+
code_write: "App-Dateien speichern",
|
|
142
|
+
code_remove: "App-Datei entfernen",
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
/** Older chats call the visual tools by their `cloud_` names. */
|
|
148
|
+
const ALIASES: Record<string, string> = { cloud_card: "card", cloud_survey: "survey", cloud_text_editor: "text_editor" };
|
|
149
|
+
|
|
150
|
+
/** The localized name of a built-in tool, or undefined for a tool Cloud does not ship. */
|
|
151
|
+
export const aiBuiltinToolName = (name: string, locale: string): string | undefined => {
|
|
152
|
+
const t: Record<string, string> = toolNames.resolve([locale]).t;
|
|
153
|
+
const key = ALIASES[name] ?? name;
|
|
154
|
+
return Object.hasOwn(t, key) ? t[key] : undefined;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export const checkAiToolNames = () => toolNames.check();
|