@k2b/cloud 0.29.0 → 0.30.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 +5 -3
- package/src/_internal/app-roles.ts +14 -0
- package/src/_internal/capabilities.ts +111 -3
- package/src/_internal/capability-sentences.ts +413 -0
- package/src/_internal/define-app.ts +10 -1
- package/src/_internal/registry-validation.ts +5 -0
- package/src/access/PermissionEditor.tsx +43 -24
- package/src/ai/app-skill-store.ts +298 -0
- package/src/ai/app-skills.ts +105 -0
- package/src/ai/browser.ts +10 -0
- package/src/ai/capabilities.ts +68 -9
- package/src/ai/chart-block.ts +380 -0
- package/src/ai/chat/action-sentences.ts +49 -0
- package/src/ai/chat/blocks.tsx +203 -185
- package/src/ai/chat/chart-block.tsx +79 -0
- package/src/ai/chat/live-turn.browser-harness.tsx +5 -1
- package/src/ai/chat/message-utils.ts +3 -0
- package/src/ai/chat/messages.ts +12 -26
- package/src/ai/chat/tool-disclosure.tsx +21 -14
- package/src/ai/chat/tool-groups.ts +64 -37
- package/src/ai/chat/tool-names.ts +2 -0
- package/src/ai/chat/turn-layout.ts +4 -4
- package/src/ai/chat/turn-view.tsx +141 -69
- package/src/ai/code-runtime-tools.ts +59 -44
- package/src/ai/default-tool-contracts.ts +198 -0
- package/src/ai/default-tools.ts +25 -1
- package/src/ai/executor.ts +19 -21
- package/src/ai/index.ts +9 -1
- package/src/ai/migrate.ts +59 -12
- package/src/ai/protocol.ts +9 -4
- package/src/ai/runtime-tools.ts +1 -0
- package/src/ai/skill-content.ts +54 -0
- package/src/ai/skills-routes.ts +22 -13
- package/src/ai/skills.ts +177 -231
- package/src/ai/store.ts +20 -13
- package/src/ai/tool-sentences.ts +38 -0
- package/src/ai/types.ts +10 -2
- package/src/api/admin-ai-skills.ts +35 -16
- package/src/api/admin-outgoing-mail.ts +18 -0
- package/src/api/fanout.ts +128 -0
- package/src/api/help.ts +8 -3
- package/src/api/mcp.ts +4 -1
- package/src/api/search.ts +31 -106
- package/src/api/widgets.ts +259 -100
- package/src/browser/CloudResourceSearch.browser-harness.tsx +1 -1
- package/src/browser/ndjson.ts +34 -0
- package/src/browser/reload.ts +46 -0
- package/src/browser/search-stream.ts +4 -26
- package/src/browser/widgets.ts +28 -0
- package/src/capabilities/server.ts +20 -2
- package/src/cli/access.ts +48 -14
- package/src/cli/admin/ai-skills.ts +59 -39
- package/src/cli/admin/outgoing-mail.ts +30 -2
- package/src/contracts/announcement-cookie.ts +62 -0
- package/src/contracts/announcements.ts +14 -52
- package/src/contracts/capabilities.ts +24 -0
- package/src/contracts/outgoing-mail.ts +56 -11
- package/src/contracts/pwa-paths.ts +7 -2
- package/src/contracts/pwa.ts +2 -7
- package/src/contracts/registry.ts +1 -0
- package/src/contracts/shared.ts +5 -0
- package/src/contracts/widgets.ts +20 -0
- package/src/index.ts +1 -0
- package/src/server/help.ts +8 -1
- package/src/services/accounts/users.ts +15 -0
- package/src/services/help/index.ts +18 -5
- package/src/services/help/types.ts +3 -2
- package/src/services/identity/constants.ts +2 -0
- package/src/services/identity/key-ring.ts +123 -42
- package/src/services/identity/runtime-config.ts +13 -2
- package/src/services/outbox.ts +24 -9
- package/src/services/outgoing-mail/index.ts +33 -5
- package/src/services/outgoing-mail/messages.ts +60 -19
- package/src/services/outgoing-mail/store.ts +63 -4
- package/src/shared/network-address.ts +1 -0
- package/src/ssr/AppLaunchpadPanel.tsx +12 -3
- package/src/ssr/GlobalAnnouncements.island.tsx +2 -2
- package/src/ssr/GlobalSearchDialog.tsx +1 -43
- package/src/ssr/GlobalSearchTrigger.island.tsx +12 -3
- package/src/ssr/LayoutHelpTrigger.island.tsx +1 -1
- package/src/ssr/PwaRuntime.island.tsx +1 -1
- package/src/ssr/app-navigation.ts +5 -7
- package/src/ssr/global-search-host.tsx +55 -0
- package/src/ssr/help.ts +3 -3
- package/src/ssr/rail-context.ts +4 -3
- package/src/ssr/runtime.ts +14 -1
- package/src/styles/effects.css +142 -0
- package/src/styles/utilities-markdown-table.css +5 -1
- package/src/ai/code-mode-skill.ts +0 -114
- package/src/ai/data-analysis-skill.ts +0 -10
- package/src/ai/grids-skill.ts +0 -158
- package/src/ai/skill-seeds.ts +0 -560
package/src/ai/chat/messages.ts
CHANGED
|
@@ -9,6 +9,7 @@ const messages = i18n.define({
|
|
|
9
9
|
tableLinks: "Links",
|
|
10
10
|
tableOpen: "Open",
|
|
11
11
|
tableEmpty: "No matching rows.",
|
|
12
|
+
chartUnavailable: "This chart cannot be shown.",
|
|
12
13
|
tableCapped: "Showing the first 100 returned rows. The full tool result remains available in details.",
|
|
13
14
|
tableMore: "More rows are available. Ask for the next page or open the result in the application.",
|
|
14
15
|
assistantTurns: "Assistant turns",
|
|
@@ -84,6 +85,7 @@ const messages = i18n.define({
|
|
|
84
85
|
stepDeliveringFile: "Delivering a file",
|
|
85
86
|
stepPreparingView: "Preparing a view",
|
|
86
87
|
stepPreparingCard: "Preparing a card",
|
|
88
|
+
stepPreparingChart: "Preparing a chart",
|
|
87
89
|
stepCompacting: "Summarizing the conversation",
|
|
88
90
|
stepTool: "Using a tool",
|
|
89
91
|
stepFailed: "failed",
|
|
@@ -95,33 +97,24 @@ const messages = i18n.define({
|
|
|
95
97
|
checkWarnings: ({ count }: { count: number }) => (count === 1 ? "1 warning" : `${count} warnings`),
|
|
96
98
|
openImage: ({ name }: { name: string }) => `Open image ${name}`,
|
|
97
99
|
steered: "Conversation steered",
|
|
98
|
-
reasoning: "Reasoning",
|
|
99
|
-
groupPlan: "Updated working plan",
|
|
100
|
-
groupCode: "Worked with code",
|
|
101
|
-
groupCloud: "Used Cloud integration",
|
|
102
|
-
groupReadFiles: "Read files",
|
|
103
|
-
groupWriteFiles: "Wrote files",
|
|
104
|
-
groupPdf: "Created PDFs",
|
|
105
100
|
groupLoad: "Loaded tools and guidance",
|
|
106
|
-
groupWeb: "Searched the web",
|
|
107
|
-
groupMemory: "Used memory",
|
|
108
|
-
groupDelivered: "Delivered results",
|
|
109
|
-
groupTools: "Used tools",
|
|
110
101
|
groupFailed: ({ count }: { count: number }) => `${count} failed`,
|
|
111
102
|
groupRejected: ({ count }: { count: number }) => `${count} rejected`,
|
|
112
|
-
receiptDone: ({ title }: { title: string }) =>
|
|
103
|
+
receiptDone: ({ title }: { title: string }) => `Done: ${title}`,
|
|
113
104
|
receiptApproved: ({ title }: { title: string }) => `Approved: ${title}`,
|
|
105
|
+
receiptRunning: ({ title }: { title: string }) => `Running: ${title}`,
|
|
114
106
|
approvalNeeded: ({ title }: { title: string }) => `Approval needed: ${title}`,
|
|
115
107
|
answerReady: "Answer ready",
|
|
116
108
|
receiptRejected: ({ title }: { title: string }) => `Rejected: ${title}`,
|
|
117
109
|
receiptFailed: ({ title }: { title: string }) => `Failed: ${title}`,
|
|
118
110
|
receiptNotRun: ({ title, stopped }: { title: string; stopped: boolean }) => `Not run: ${title}${stopped ? " · stopped" : ""}`,
|
|
111
|
+
receiptNotRunSentence: ({ sentence, stopped }: { sentence: string; stopped: boolean }) => `${sentence}${stopped ? " · stopped" : ""}`,
|
|
119
112
|
workSteps: "Work steps",
|
|
120
113
|
streamLoginRequired: "Your session has ended. Sign in again to continue this chat.",
|
|
121
114
|
streamAccessDenied: "You no longer have access to this chat.",
|
|
122
115
|
streamNotFound: "This chat is no longer available.",
|
|
123
116
|
assistant: "Assistant",
|
|
124
|
-
|
|
117
|
+
approvalRunsAfter: "Runs only after you approve it",
|
|
125
118
|
approvalRequired: ({ title }: { title: string }) => `Approval required: ${title}`,
|
|
126
119
|
reject: "Reject",
|
|
127
120
|
approved: "Approved",
|
|
@@ -300,6 +293,7 @@ const messages = i18n.define({
|
|
|
300
293
|
tableLinks: "Links",
|
|
301
294
|
tableOpen: "Öffnen",
|
|
302
295
|
tableEmpty: "Keine passenden Zeilen.",
|
|
296
|
+
chartUnavailable: "Dieses Diagramm kann nicht angezeigt werden.",
|
|
303
297
|
tableCapped:
|
|
304
298
|
"Die ersten 100 zurückgegebenen Zeilen werden angezeigt. Das vollständige Werkzeugergebnis bleibt in den Details verfügbar.",
|
|
305
299
|
tableMore: "Weitere Zeilen sind verfügbar. Frage nach der nächsten Seite oder öffne das Ergebnis in der Anwendung.",
|
|
@@ -382,6 +376,7 @@ const messages = i18n.define({
|
|
|
382
376
|
stepDeliveringFile: "Stellt eine Datei bereit",
|
|
383
377
|
stepPreparingView: "Bereitet eine Ansicht vor",
|
|
384
378
|
stepPreparingCard: "Bereitet eine Karte vor",
|
|
379
|
+
stepPreparingChart: "Bereitet ein Diagramm vor",
|
|
385
380
|
stepCompacting: "Fasst das Gespräch zusammen",
|
|
386
381
|
stepTool: "Nutzt ein Werkzeug",
|
|
387
382
|
stepFailed: "fehlgeschlagen",
|
|
@@ -393,33 +388,24 @@ const messages = i18n.define({
|
|
|
393
388
|
checkWarnings: ({ count }) => (count === 1 ? "1 Warnung" : `${count} Warnungen`),
|
|
394
389
|
openImage: ({ name }) => `Bild ${name} öffnen`,
|
|
395
390
|
steered: "Richtung geändert",
|
|
396
|
-
reasoning: "Überlegungen",
|
|
397
|
-
groupPlan: "Arbeitsplan aktualisiert",
|
|
398
|
-
groupCode: "Mit Code gearbeitet",
|
|
399
|
-
groupCloud: "Cloud-Integration verwendet",
|
|
400
|
-
groupReadFiles: "Dateien gelesen",
|
|
401
|
-
groupWriteFiles: "Dateien geschrieben",
|
|
402
|
-
groupPdf: "PDFs erstellt",
|
|
403
391
|
groupLoad: "Werkzeuge und Wissen geladen",
|
|
404
|
-
groupWeb: "Im Web recherchiert",
|
|
405
|
-
groupMemory: "Gedächtnis genutzt",
|
|
406
|
-
groupDelivered: "Ergebnisse bereitgestellt",
|
|
407
|
-
groupTools: "Werkzeuge verwendet",
|
|
408
392
|
groupFailed: ({ count }) => `${count} fehlgeschlagen`,
|
|
409
393
|
groupRejected: ({ count }) => `${count} abgelehnt`,
|
|
410
|
-
receiptDone: ({ title }) =>
|
|
394
|
+
receiptDone: ({ title }) => `Erledigt: ${title}`,
|
|
411
395
|
receiptApproved: ({ title }) => `Freigegeben: ${title}`,
|
|
396
|
+
receiptRunning: ({ title }) => `Wird ausgeführt: ${title}`,
|
|
412
397
|
approvalNeeded: ({ title }) => `Freigabe nötig: ${title}`,
|
|
413
398
|
answerReady: "Antwort fertig",
|
|
414
399
|
receiptRejected: ({ title }) => `Abgelehnt: ${title}`,
|
|
415
400
|
receiptFailed: ({ title }) => `Fehlgeschlagen: ${title}`,
|
|
416
401
|
receiptNotRun: ({ title, stopped }) => `Nicht ausgeführt: ${title}${stopped ? " · gestoppt" : ""}`,
|
|
402
|
+
receiptNotRunSentence: ({ sentence, stopped }) => `${sentence}${stopped ? " · gestoppt" : ""}`,
|
|
417
403
|
workSteps: "Arbeitsschritte",
|
|
418
404
|
streamLoginRequired: "Deine Sitzung ist abgelaufen. Melde dich erneut an, um diesen Chat fortzusetzen.",
|
|
419
405
|
streamAccessDenied: "Du hast keinen Zugriff mehr auf diesen Chat.",
|
|
420
406
|
streamNotFound: "Dieser Chat ist nicht mehr verfügbar.",
|
|
421
407
|
assistant: "Assistent",
|
|
422
|
-
|
|
408
|
+
approvalRunsAfter: "Wird erst nach deiner Freigabe ausgeführt",
|
|
423
409
|
approvalRequired: ({ title }) => `Freigabe erforderlich: ${title}`,
|
|
424
410
|
reject: "Ablehnen",
|
|
425
411
|
approved: "Freigegeben",
|
|
@@ -1,28 +1,35 @@
|
|
|
1
1
|
import { Chat, type ChatActivityProps } from "@k2b/ui";
|
|
2
|
-
import { createContext, type JSX, splitProps, useContext } from "solid-js";
|
|
2
|
+
import { createContext, createSignal, type JSX, type Signal, splitProps, useContext } from "solid-js";
|
|
3
3
|
|
|
4
4
|
export type AiToolDisclosureState = {
|
|
5
5
|
get: (blockId: string) => boolean | undefined;
|
|
6
6
|
set: (blockId: string, open: boolean) => void;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Disclosure choices by block id. Reactive, so a disclosure that its reader opens renders its body at once, also when
|
|
11
|
+
* nothing else in the turn changes; choices survive a remount and, in session storage, a reload.
|
|
12
|
+
*/
|
|
9
13
|
export const createAiToolDisclosureState = (): AiToolDisclosureState => {
|
|
10
|
-
const
|
|
14
|
+
const signals = new Map<string, Signal<boolean | undefined>>();
|
|
11
15
|
const storageKey = (blockId: string) => `cloud.ai.tool-disclosure:${blockId}`;
|
|
16
|
+
const signal = (blockId: string): Signal<boolean | undefined> => {
|
|
17
|
+
const existing = signals.get(blockId);
|
|
18
|
+
if (existing) return existing;
|
|
19
|
+
let saved: boolean | undefined;
|
|
20
|
+
try {
|
|
21
|
+
if (window.sessionStorage.getItem(storageKey(blockId)) === "open") saved = true;
|
|
22
|
+
} catch {
|
|
23
|
+
/* SSR and disabled storage keep the in-memory state. */
|
|
24
|
+
}
|
|
25
|
+
const created = createSignal(saved);
|
|
26
|
+
signals.set(blockId, created);
|
|
27
|
+
return created;
|
|
28
|
+
};
|
|
12
29
|
return {
|
|
13
|
-
get: (blockId) =>
|
|
14
|
-
if (!openByBlockId.has(blockId)) {
|
|
15
|
-
try {
|
|
16
|
-
const saved = window.sessionStorage.getItem(storageKey(blockId));
|
|
17
|
-
if (saved === "open") openByBlockId.set(blockId, true);
|
|
18
|
-
} catch {
|
|
19
|
-
/* SSR and disabled storage keep the in-memory state. */
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
return openByBlockId.get(blockId);
|
|
23
|
-
},
|
|
30
|
+
get: (blockId) => signal(blockId)[0](),
|
|
24
31
|
set: (blockId, open) => {
|
|
25
|
-
|
|
32
|
+
signal(blockId)[1](open);
|
|
26
33
|
try {
|
|
27
34
|
if (open) window.sessionStorage.setItem(storageKey(blockId), "open");
|
|
28
35
|
else window.sessionStorage.removeItem(storageKey(blockId));
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
import { aiSkillFilePathFromMount } from "../file-mount";
|
|
1
2
|
import type { AiTurnBlock } from "../protocol";
|
|
2
3
|
import { aiChatMessages } from "./messages";
|
|
3
4
|
|
|
4
5
|
type Tool = Extract<AiTurnBlock, { kind: "tool" }>;
|
|
5
6
|
type Text = Extract<AiTurnBlock, { kind: "text" }>;
|
|
6
7
|
export type AiWorkEntry = Exclude<AiTurnBlock, { kind: "text" } | { kind: "steer_message" }>;
|
|
7
|
-
|
|
8
|
+
/**
|
|
9
|
+
* One row of the expanded work, all at one indent: an intermediate text, a step, or a group that folds housekeeping
|
|
10
|
+
* steps and opens in place.
|
|
11
|
+
*/
|
|
12
|
+
export type AiWorkItem =
|
|
13
|
+
| { kind: "text"; block: Text }
|
|
14
|
+
| { kind: "step"; entry: AiWorkEntry }
|
|
15
|
+
| { kind: "housekeeping"; id: string; entries: AiWorkEntry[] };
|
|
8
16
|
|
|
9
17
|
/** A failed call, including a code run that reported an error. A rejected approval is a decision, not a failure. */
|
|
10
18
|
export function isFailedTool(tool: Tool): boolean {
|
|
@@ -20,46 +28,65 @@ export function isFailedTool(tool: Tool): boolean {
|
|
|
20
28
|
);
|
|
21
29
|
}
|
|
22
30
|
|
|
31
|
+
const HOUSEKEEPING_TOOLS = new Set([
|
|
32
|
+
"load_skill",
|
|
33
|
+
"search_skills",
|
|
34
|
+
"load_tools",
|
|
35
|
+
"search_tools",
|
|
36
|
+
"list_apps",
|
|
37
|
+
"search_help",
|
|
38
|
+
"read_help",
|
|
39
|
+
"search_project",
|
|
40
|
+
"read_project_knowledge",
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
/** Preparation the reader rarely needs: loading tools, skills, and knowledge, and reading a loaded skill's files. */
|
|
44
|
+
export const isHousekeepingTool = (tool: Tool): boolean => {
|
|
45
|
+
if (HOUSEKEEPING_TOOLS.has(tool.name)) return true;
|
|
46
|
+
if (tool.name !== "read_file" && tool.name !== "list_files") return false;
|
|
47
|
+
const path = tool.args !== null && typeof tool.args === "object" && "path" in tool.args ? tool.args.path : undefined;
|
|
48
|
+
return typeof path === "string" && aiSkillFilePathFromMount(path.trim()) !== null;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const isStep = (entry: AiWorkEntry) => entry.kind === "tool" || entry.kind === "compaction";
|
|
52
|
+
|
|
23
53
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
54
|
+
* The rows of the expanded work, in the original order. A run of at least two housekeeping steps, with the reasoning
|
|
55
|
+
* between them, folds into one group, unless it would hold all but one of the work's steps: then the steps show
|
|
56
|
+
* directly, so the work line never opens to a summary of itself. Entries listed in `shown` already show as rows of
|
|
57
|
+
* their own and never fold into a group, so nothing a reader sees folds away under them. A group is keyed by its first
|
|
58
|
+
* tool, whose id is the same live and in history.
|
|
26
59
|
*/
|
|
27
|
-
export function groupWorkBlocks(blocks: readonly AiTurnBlock[]):
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
60
|
+
export function groupWorkBlocks(blocks: readonly AiTurnBlock[], shown: ReadonlySet<string> = new Set()): AiWorkItem[] {
|
|
61
|
+
const entries = blocks.filter((block): block is AiWorkEntry | Text => block.kind !== "steer_message");
|
|
62
|
+
const steps = entries.filter((entry) => entry.kind !== "text" && isStep(entry)).length;
|
|
63
|
+
const items: AiWorkItem[] = [];
|
|
64
|
+
let run: AiWorkEntry[] = [];
|
|
65
|
+
let trailing: AiWorkEntry[] = [];
|
|
66
|
+
const flush = () => {
|
|
67
|
+
if (run.length > 0) {
|
|
68
|
+
const runSteps = run.filter(isStep).length;
|
|
69
|
+
if (runSteps >= 2 && steps - runSteps >= 2) items.push({ kind: "housekeeping", id: run[0]!.id, entries: run });
|
|
70
|
+
else for (const entry of run) items.push({ kind: "step", entry });
|
|
71
|
+
}
|
|
72
|
+
// Reasoning after the last housekeeping step belongs to what comes next.
|
|
73
|
+
for (const entry of trailing) items.push({ kind: "step", entry });
|
|
74
|
+
run = [];
|
|
75
|
+
trailing = [];
|
|
76
|
+
};
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
const foldable = !shown.has(entry.id);
|
|
79
|
+
if (foldable && entry.kind === "tool" && isHousekeepingTool(entry)) {
|
|
80
|
+
run.push(...trailing, entry);
|
|
81
|
+
trailing = [];
|
|
82
|
+
} else if (foldable && entry.kind === "thinking" && run.length > 0) trailing.push(entry);
|
|
83
|
+
else {
|
|
84
|
+
flush();
|
|
85
|
+
items.push(entry.kind === "text" ? { kind: "text", block: entry } : { kind: "step", entry });
|
|
34
86
|
}
|
|
35
|
-
const last = groups.at(-1);
|
|
36
|
-
if (last?.kind === "steps") last.entries.push(block);
|
|
37
|
-
else groups.push({ kind: "steps", id: block.id, entries: [block] });
|
|
38
|
-
}
|
|
39
|
-
for (const group of groups) {
|
|
40
|
-
if (group.kind === "steps") group.id = (group.entries.find((entry) => entry.kind === "tool") ?? group.entries[0]!).id;
|
|
41
87
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
/** What a group of tools did, in categories; failures and rejections are counted separately. */
|
|
46
|
-
export function summarizeToolGroup(tools: readonly Tool[], locale: string): string {
|
|
47
|
-
const t = aiChatMessages(locale);
|
|
48
|
-
const categories = tools.map((tool) => {
|
|
49
|
-
if (tool.name === "todo_write") return t.groupPlan;
|
|
50
|
-
if (tool.name === "present" || tool.name === "code_present" || tool.name === "card" || tool.name === "cloud_card")
|
|
51
|
-
return t.groupDelivered;
|
|
52
|
-
if (tool.name.startsWith("code_")) return t.groupCode;
|
|
53
|
-
if (tool.presentation?.kind === "capability" || tool.name === "read_cloud_resource") return t.groupCloud;
|
|
54
|
-
if (["read_file", "list_files", "view_image"].includes(tool.name)) return t.groupReadFiles;
|
|
55
|
-
if (tool.name === "write_file") return t.groupWriteFiles;
|
|
56
|
-
if (["markdown_to_pdf", "html_to_pdf"].includes(tool.name)) return t.groupPdf;
|
|
57
|
-
if (["load_skill", "load_tools", "search_tools", "list_apps"].includes(tool.name)) return t.groupLoad;
|
|
58
|
-
if (tool.name.startsWith("web_") || tool.name === "fetch_file") return t.groupWeb;
|
|
59
|
-
if (tool.name === "memory") return t.groupMemory;
|
|
60
|
-
return t.groupTools;
|
|
61
|
-
});
|
|
62
|
-
return [...new Set(categories)].join(", ");
|
|
88
|
+
flush();
|
|
89
|
+
return items;
|
|
63
90
|
}
|
|
64
91
|
|
|
65
92
|
/** "9 steps · 1 failed · 1 rejected" for a group of steps. */
|
|
@@ -10,6 +10,7 @@ const toolNames = i18n.define({
|
|
|
10
10
|
en: {
|
|
11
11
|
calculate: "Calculate",
|
|
12
12
|
card: "Card",
|
|
13
|
+
chart: "Chart",
|
|
13
14
|
survey: "Survey",
|
|
14
15
|
text_editor: "Text editor",
|
|
15
16
|
local_bash: "Local Bash",
|
|
@@ -77,6 +78,7 @@ const toolNames = i18n.define({
|
|
|
77
78
|
de: {
|
|
78
79
|
calculate: "Rechnen",
|
|
79
80
|
card: "Karte",
|
|
81
|
+
chart: "Diagramm",
|
|
80
82
|
survey: "Umfrage",
|
|
81
83
|
text_editor: "Texteditor",
|
|
82
84
|
local_bash: "Lokale Bash",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type AiTurnBlock, isRenderableTurnBlock } from "../protocol";
|
|
2
2
|
import type { AiStoredMessage } from "../types";
|
|
3
3
|
import { hasCapabilityTable } from "./capability-result";
|
|
4
|
-
import { isCardToolName, isRecord, isSurveyToolName, isTextEditorToolName } from "./message-utils";
|
|
4
|
+
import { isCardToolName, isChartToolName, isRecord, isSurveyToolName, isTextEditorToolName } from "./message-utils";
|
|
5
5
|
import { isFailedTool } from "./tool-groups";
|
|
6
6
|
|
|
7
7
|
type ToolBlock = Extract<AiTurnBlock, { kind: "tool" }>;
|
|
@@ -69,7 +69,7 @@ const isResult = (block: ToolBlock, phase: AiTurnPhase, codePresentations: boole
|
|
|
69
69
|
// A running delivery reserves its place once its arguments arrived: a new version of an earlier result must find the
|
|
70
70
|
// earlier place before it takes one of its own.
|
|
71
71
|
const delivered = block.status === "completed" || (live(phase) && block.status === "running" && block.args !== undefined);
|
|
72
|
-
if (block.name === "present" || isCardToolName(block.name)) return delivered;
|
|
72
|
+
if (block.name === "present" || isCardToolName(block.name) || isChartToolName(block.name)) return delivered;
|
|
73
73
|
if (block.name === "code_present") return codePresentations && delivered;
|
|
74
74
|
return hasCapabilityTable(block);
|
|
75
75
|
};
|
|
@@ -79,9 +79,9 @@ const actionState = (block: ToolBlock, phase: AiTurnPhase): AiTurnActionState |
|
|
|
79
79
|
if (block.status === "rejected") return "rejected";
|
|
80
80
|
if (isSurveyToolName(block.name) || isTextEditorToolName(block.name)) return isFailedTool(block) ? null : "interaction";
|
|
81
81
|
if (block.name === "code_secret") return block.status === "awaiting_client" ? "interaction" : null;
|
|
82
|
-
// Effects in Cloud and decided
|
|
82
|
+
// Effects in Cloud and approvals, decided or expired, stay visible as receipts, whether or not the model mentions them.
|
|
83
83
|
const capabilityAction = block.presentation?.kind === "capability" && block.presentation.capabilityKind === "action";
|
|
84
|
-
if (!capabilityAction &&
|
|
84
|
+
if (!capabilityAction && block.approved === undefined) return null;
|
|
85
85
|
if (block.status === "running" || block.status === "awaiting_client") return live(phase) ? "running" : "not_run";
|
|
86
86
|
return isFailedTool(block) ? "failed" : "done";
|
|
87
87
|
};
|