@k2b/cloud 0.24.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/package.json +3 -2
  2. package/src/_internal/capabilities.ts +12 -0
  3. package/src/_internal/registry.ts +1 -0
  4. package/src/access/GroupCoverage.tsx +175 -0
  5. package/src/access/PermissionEditor.tsx +134 -93
  6. package/src/access/managers.ts +32 -0
  7. package/src/access/messages.ts +36 -0
  8. package/src/ai/approval-routes.ts +5 -5
  9. package/src/ai/capabilities.ts +34 -12
  10. package/src/ai/chat/blocks.tsx +86 -179
  11. package/src/ai/chat/builtin-tools.tsx +57 -30
  12. package/src/ai/chat/live-turn.browser-harness.tsx +37 -0
  13. package/src/ai/chat/message-actions.tsx +6 -2
  14. package/src/ai/chat/message-utils.ts +17 -14
  15. package/src/ai/chat/messages.ts +324 -2
  16. package/src/ai/chat/presentation.tsx +257 -103
  17. package/src/ai/chat/tool-groups.ts +55 -35
  18. package/src/ai/chat/turn-layout.ts +141 -0
  19. package/src/ai/chat/turn-view.tsx +609 -0
  20. package/src/ai/client/projection.ts +37 -6
  21. package/src/ai/executor.ts +38 -5
  22. package/src/ai/memory-learning.ts +7 -12
  23. package/src/ai/memory-workflow-evidence.ts +10 -2
  24. package/src/ai/migrate.ts +45 -2
  25. package/src/ai/model-access.ts +4 -2
  26. package/src/ai/prefs.ts +19 -5
  27. package/src/ai/projects.ts +5 -2
  28. package/src/ai/protocol.ts +20 -4
  29. package/src/ai/provider-fetch.ts +67 -15
  30. package/src/ai/provider-retry.ts +105 -0
  31. package/src/ai/quota-provider.ts +14 -5
  32. package/src/ai/skills.ts +5 -2
  33. package/src/ai/store.ts +108 -3
  34. package/src/ai/stream.ts +2 -0
  35. package/src/ai/timeline.ts +9 -11
  36. package/src/ai/turn-timing.ts +31 -3
  37. package/src/ai/types.ts +12 -2
  38. package/src/browser/FileChooser.tsx +600 -0
  39. package/src/browser/choose-files.browser-harness.tsx +32 -0
  40. package/src/browser/choose-files.tsx +70 -0
  41. package/src/browser/file-chooser-messages.ts +86 -0
  42. package/src/browser/file-providers.ts +217 -0
  43. package/src/browser/files.tsx +23 -0
  44. package/src/cli/admin/account-administration.ts +2 -2
  45. package/src/contracts/registry.ts +2 -0
  46. package/src/server/index.ts +1 -0
  47. package/src/server/services/access.ts +44 -11
  48. package/src/server/services/index.ts +1 -0
  49. package/src/shared/app-presentation.ts +10 -2
  50. package/src/ssr/platform-messages.ts +2 -2
  51. package/src/styles/effects.css +69 -0
  52. package/src/styles/file-chooser.css +169 -0
  53. package/src/styles/global.css +1 -0
@@ -0,0 +1,141 @@
1
+ import { type AiTurnBlock, isRenderableTurnBlock } from "../protocol";
2
+ import { hasCapabilityTable } from "./capability-result";
3
+ import { isCardToolName, isRecord, isSurveyToolName, isTextEditorToolName } from "./message-utils";
4
+ import { isFailedTool } from "./tool-groups";
5
+
6
+ type ToolBlock = Extract<AiTurnBlock, { kind: "tool" }>;
7
+ type TextBlock = Extract<AiTurnBlock, { kind: "text" }>;
8
+
9
+ /**
10
+ * How far a turn (or one segment of it) has come. A live turn is `running` or `waiting`; history is
11
+ * `completed`, `stopped` (the user stopped it), or `failed`.
12
+ */
13
+ export type AiTurnPhase = "running" | "waiting" | "completed" | "stopped" | "failed";
14
+
15
+ /** What a block in place 4 shows: an open decision, a running action, or a receipt for its outcome. */
16
+ export type AiTurnActionState = "open" | "interaction" | "running" | "done" | "rejected" | "failed" | "not_run";
17
+
18
+ export type AiTurnAction = { id: string; block: ToolBlock; state: AiTurnActionState };
19
+
20
+ export type AiTurnResult = { id: string; block: ToolBlock };
21
+
22
+ /**
23
+ * The four fixed places of one turn. Live and history use the same function on the same blocks, so a
24
+ * finished turn looks exactly like the live turn did, and a reload shows the same thing.
25
+ */
26
+ export type AiTurnLayout = {
27
+ /** Place 1: everything except the visible text, in the original order. Shown only when expanded. */
28
+ work: AiTurnBlock[];
29
+ /** Tool calls and compactions. */
30
+ steps: number;
31
+ /** A turn without tool calls or compactions has no work line, unless it was stopped or failed with folded text. */
32
+ showWork: boolean;
33
+ /** Place 2: delivered results in creation order. A result with the target of an earlier one replaces it at its place. */
34
+ results: AiTurnResult[];
35
+ /** Place 3: the newest text. Live it is a status; finished it is the final message. */
36
+ text: TextBlock[];
37
+ /** Place 4: effects in Cloud and decisions, open as a card or finished as a receipt. */
38
+ actions: AiTurnAction[];
39
+ /** The newest block, which names the current step of a live turn. */
40
+ current: AiTurnBlock | null;
41
+ /** What a waiting turn waits for. */
42
+ waitingFor: "approval" | "answer" | null;
43
+ };
44
+
45
+ const live = (phase: AiTurnPhase) => phase === "running" || phase === "waiting";
46
+
47
+ const isInteraction = (name: string) => isSurveyToolName(name) || isTextEditorToolName(name) || name === "code_secret";
48
+
49
+ /**
50
+ * A call that waits for the person: an approval, or an answer in a survey, an editor, or a secret prompt. Only this time
51
+ * stops the work clock; a tool the browser runs by itself is work.
52
+ */
53
+ export const waitsForUser = (block: AiTurnBlock): boolean =>
54
+ block.kind === "tool" && (block.status === "awaiting_approval" || (block.status === "awaiting_client" && isInteraction(block.name)));
55
+
56
+ /** A streamed status replaces the previous one only after its first sentence, so a reader never loses a sentence. */
57
+ export const hasCompleteSentence = (text: string): boolean => /[.!?…:;](?:\s|$)|\n/.test(text);
58
+
59
+ const resultTarget = (block: ToolBlock): string | null => {
60
+ const args = isRecord(block.args) ? block.args : {};
61
+ if (block.name === "present" && typeof args.path === "string") return `present:${args.path.trim()}`;
62
+ if (block.name === "code_present" && typeof args.title === "string") return `code_present:${args.title.trim().toLowerCase()}`;
63
+ return null;
64
+ };
65
+
66
+ const isResult = (block: ToolBlock, phase: AiTurnPhase, codePresentations: boolean): boolean => {
67
+ if (isFailedTool(block) || block.status === "rejected" || block.status === "awaiting_approval") return false;
68
+ // A running delivery reserves its place once its arguments arrived: a new version of an earlier result must find the
69
+ // earlier place before it takes one of its own.
70
+ const delivered = block.status === "completed" || (live(phase) && block.status === "running" && block.args !== undefined);
71
+ if (block.name === "present" || isCardToolName(block.name)) return delivered;
72
+ if (block.name === "code_present") return codePresentations && delivered;
73
+ return hasCapabilityTable(block);
74
+ };
75
+
76
+ const actionState = (block: ToolBlock, phase: AiTurnPhase): AiTurnActionState | null => {
77
+ if (block.status === "awaiting_approval") return "open";
78
+ if (block.status === "rejected") return "rejected";
79
+ if (isSurveyToolName(block.name) || isTextEditorToolName(block.name)) return isFailedTool(block) ? null : "interaction";
80
+ if (block.name === "code_secret") return block.status === "awaiting_client" ? "interaction" : null;
81
+ // Effects in Cloud and decided approvals stay visible as receipts, whether or not the model mentions them.
82
+ const capabilityAction = block.presentation?.kind === "capability" && block.presentation.capabilityKind === "action";
83
+ if (!capabilityAction && !block.approved) return null;
84
+ if (block.status === "running" || block.status === "awaiting_client") return live(phase) ? "running" : "not_run";
85
+ return isFailedTool(block) ? "failed" : "done";
86
+ };
87
+
88
+ /**
89
+ * Assign the blocks of one turn segment to the four places. Pure: the presentation renders live turns and
90
+ * history through this one function.
91
+ */
92
+ export function layoutAiTurn(input: readonly AiTurnBlock[], options: { phase: AiTurnPhase; codePresentations?: boolean }): AiTurnLayout {
93
+ const blocks = input.filter(isRenderableTurnBlock);
94
+ const tools = blocks.filter((block): block is ToolBlock => block.kind === "tool");
95
+ const steps = tools.length + blocks.filter((block) => block.kind === "compaction").length;
96
+
97
+ const results: AiTurnResult[] = [];
98
+ const resultIndex = new Map<string, number>();
99
+ const actions: AiTurnAction[] = [];
100
+ for (const block of tools) {
101
+ const state = actionState(block, options.phase);
102
+ if (state) actions.push({ id: block.id, block, state });
103
+ // An approved tool or a Cloud action can deliver a result too: it keeps its receipt and shows its result.
104
+ if (!isResult(block, options.phase, options.codePresentations ?? false)) continue;
105
+ const target = resultTarget(block);
106
+ const index = target ? resultIndex.get(target) : undefined;
107
+ if (index !== undefined) results[index] = { id: results[index]!.id, block };
108
+ else {
109
+ if (target) resultIndex.set(target, results.length);
110
+ results.push({ id: block.id, block });
111
+ }
112
+ }
113
+
114
+ const texts = blocks.filter((block): block is TextBlock => block.kind === "text");
115
+ let text: TextBlock[] = [];
116
+ if (options.phase !== "stopped" && options.phase !== "failed") {
117
+ if (steps === 0) text = texts;
118
+ else {
119
+ const newest = texts.at(-1);
120
+ const previous = texts.at(-2);
121
+ const shown = newest && previous && live(options.phase) && !hasCompleteSentence(newest.text) ? previous : newest;
122
+ text = shown ? [shown] : [];
123
+ }
124
+ }
125
+ const visible = new Set(text);
126
+ const work = blocks.filter((block) => !visible.has(block as TextBlock));
127
+
128
+ const waits = tools.filter(waitsForUser);
129
+ const waitingFor = waits.some((block) => block.status === "awaiting_approval") ? "approval" : waits.length > 0 ? "answer" : null;
130
+
131
+ return {
132
+ work,
133
+ steps,
134
+ showWork: steps > 0 || ((options.phase === "stopped" || options.phase === "failed") && work.length > 0),
135
+ results,
136
+ text,
137
+ actions,
138
+ current: blocks.at(-1) ?? null,
139
+ waitingFor,
140
+ };
141
+ }