@k2b/cloud 0.27.0 → 0.28.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 +2 -2
- package/src/ai/browser-code-contracts.ts +33 -63
- package/src/ai/browser.ts +1 -0
- package/src/ai/chat/blocks.tsx +16 -7
- package/src/ai/chat/live-turn.browser-harness.tsx +6 -3
- package/src/ai/chat/message-actions.tsx +123 -101
- package/src/ai/chat/message-utils.ts +30 -1
- package/src/ai/chat/messages.ts +198 -0
- package/src/ai/chat/presentation.tsx +66 -20
- package/src/ai/chat/tool-groups.ts +1 -1
- package/src/ai/chat/turn-error.ts +21 -0
- package/src/ai/chat/turn-view.tsx +55 -4
- package/src/ai/chat/user-message.tsx +19 -14
- package/src/ai/chat/visual-tools.tsx +1 -1
- 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/data-analysis-skill.ts +3 -3
- package/src/ai/default-tools.ts +15 -16
- package/src/ai/executor.ts +135 -59
- package/src/ai/index.ts +2 -0
- package/src/ai/open-tool-calls.ts +87 -0
- package/src/ai/routes.ts +6 -0
- package/src/ai/run-timeout.ts +4 -5
- package/src/ai/runtime.ts +8 -0
- package/src/ai/skill-seeds.ts +4 -4
- package/src/ai/store.ts +46 -14
- package/src/ai/system-prompt.ts +4 -21
- package/src/ai/turn-failure.ts +100 -0
- package/src/ai/turn-policy.ts +2 -1
- package/src/ai/types.ts +28 -1
- package/src/api/admin-outgoing-mail.ts +185 -5
- package/src/browser/FileChooser.tsx +11 -0
- package/src/browser/file-chooser-messages.ts +2 -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/outgoing-mail.ts +189 -1
- package/src/services/help/store.ts +2 -1
- package/src/services/index.ts +11 -1
- package/src/services/notifications/batches.ts +139 -79
- package/src/services/notifications/channels.ts +33 -13
- package/src/services/notifications/dispatcher.ts +57 -5
- 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 +1 -1
- package/src/services/notifications/runtime.ts +9 -3
- 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/postgres.ts +15 -0
- package/src/services/settings/core-settings.ts +18 -0
- 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/ssr/GlobalAnnouncements.island.tsx +1 -1
- package/src/ssr/platform-messages.ts +1 -1
- package/src/styles/effects.css +15 -17
- 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
|
@@ -13,11 +13,14 @@ import {
|
|
|
13
13
|
userVisibleTextFromMessage,
|
|
14
14
|
vfsAttachmentsFromMessage,
|
|
15
15
|
} from "./message-utils";
|
|
16
|
+
import { aiChatMessages } from "./messages";
|
|
16
17
|
|
|
17
18
|
const openModifyRetryDialog = (
|
|
18
19
|
entry: AiStoredMessage,
|
|
19
20
|
onRetryMessage: (entry: AiStoredMessage, input?: AiRetryMessageInput) => void | Promise<void>,
|
|
21
|
+
locale: string,
|
|
20
22
|
) => {
|
|
23
|
+
const t = aiChatMessages(locale);
|
|
21
24
|
void dialogCore.open<void>((close) => {
|
|
22
25
|
const [draft, setDraft] = createSignal(userVisibleTextFromMessage(entry.message));
|
|
23
26
|
const content = () => userContentWithEditedVisibleText(entry.message, draft());
|
|
@@ -34,11 +37,11 @@ const openModifyRetryDialog = (
|
|
|
34
37
|
|
|
35
38
|
return (
|
|
36
39
|
<PanelDialog>
|
|
37
|
-
<PanelDialog.Header title=
|
|
40
|
+
<PanelDialog.Header title={t.retryTitle} icon="ti ti-pencil" close={close} />
|
|
38
41
|
<PanelDialog.Body>
|
|
39
42
|
<TextInput
|
|
40
|
-
label=
|
|
41
|
-
description=
|
|
43
|
+
label={t.retryPrompt}
|
|
44
|
+
description={t.retryPromptHint}
|
|
42
45
|
multiline
|
|
43
46
|
lines={8}
|
|
44
47
|
value={draft}
|
|
@@ -47,18 +50,18 @@ const openModifyRetryDialog = (
|
|
|
47
50
|
/>
|
|
48
51
|
<Show when={retryMutation.error()}>
|
|
49
52
|
<p class="text-xs text-red-600 dark:text-red-400" role="alert">
|
|
50
|
-
|
|
53
|
+
{t.retryFailed}
|
|
51
54
|
</p>
|
|
52
55
|
</Show>
|
|
53
56
|
</PanelDialog.Body>
|
|
54
57
|
<PanelDialog.Footer>
|
|
55
58
|
<div class="ml-auto flex items-center gap-2">
|
|
56
59
|
<Button type="button" variant="secondary" size="sm" onClick={() => close()}>
|
|
57
|
-
|
|
60
|
+
{t.cancel}
|
|
58
61
|
</Button>
|
|
59
62
|
<Button type="button" variant="ai" size="sm" disabled={!canRetry()} onClick={retry}>
|
|
60
63
|
<i class={`ti ${retryMutation.loading() ? "ti-loader-2 animate-spin" : "ti-refresh"}`} aria-hidden="true" />
|
|
61
|
-
{retryMutation.loading() ?
|
|
64
|
+
{retryMutation.loading() ? t.retrying : t.actionTryAgain}
|
|
62
65
|
</Button>
|
|
63
66
|
</div>
|
|
64
67
|
</PanelDialog.Footer>
|
|
@@ -93,38 +96,39 @@ export const aiUserMessageAttachments = (entry: AiStoredMessage, actions: AiChat
|
|
|
93
96
|
})),
|
|
94
97
|
];
|
|
95
98
|
|
|
96
|
-
export const createAiUserMessageActions = (entry: AiStoredMessage, actions: AiChatActions): ChatAction[] => {
|
|
99
|
+
export const createAiUserMessageActions = (entry: AiStoredMessage, actions: AiChatActions, locale: string): ChatAction[] => {
|
|
100
|
+
const t = aiChatMessages(locale);
|
|
97
101
|
const result: ChatAction[] = [];
|
|
98
102
|
const copyText = copyTextFromMessage(entry.message);
|
|
99
103
|
|
|
100
104
|
if (copyText) {
|
|
101
|
-
result.push({ id: "copy", label:
|
|
105
|
+
result.push({ id: "copy", label: t.actionCopy, icon: "ti ti-copy", copyText });
|
|
102
106
|
}
|
|
103
107
|
if (!entry.compactedAt && actions.onRetryMessage) {
|
|
104
108
|
result.push(
|
|
105
109
|
{
|
|
106
110
|
id: "retry",
|
|
107
|
-
label:
|
|
111
|
+
label: t.actionTryAgain,
|
|
108
112
|
icon: "ti ti-refresh",
|
|
109
113
|
onSelect: () => actions.onRetryMessage?.(entry, { mode: "retry" }),
|
|
110
114
|
},
|
|
111
115
|
{
|
|
112
116
|
id: "details",
|
|
113
|
-
label:
|
|
117
|
+
label: t.actionMoreDetailed,
|
|
114
118
|
icon: "ti ti-list-details",
|
|
115
119
|
onSelect: () => actions.onRetryMessage?.(entry, { mode: "details" }),
|
|
116
120
|
},
|
|
117
121
|
{
|
|
118
122
|
id: "concise",
|
|
119
|
-
label:
|
|
123
|
+
label: t.actionMoreConcise,
|
|
120
124
|
icon: "ti ti-align-left",
|
|
121
125
|
onSelect: () => actions.onRetryMessage?.(entry, { mode: "concise" }),
|
|
122
126
|
},
|
|
123
127
|
{
|
|
124
128
|
id: "edit",
|
|
125
|
-
label:
|
|
129
|
+
label: t.actionEditPrompt,
|
|
126
130
|
icon: "ti ti-pencil",
|
|
127
|
-
onSelect: () => openModifyRetryDialog(entry, actions.onRetryMessage
|
|
131
|
+
onSelect: () => openModifyRetryDialog(entry, actions.onRetryMessage!, locale),
|
|
128
132
|
},
|
|
129
133
|
);
|
|
130
134
|
}
|
|
@@ -136,12 +140,13 @@ export const aiSteerMessageText = (block: Extract<AiTurnBlock, { kind: "steer_me
|
|
|
136
140
|
export const createAiSteerMessageActions = (
|
|
137
141
|
block: Extract<AiTurnBlock, { kind: "steer_message" }>,
|
|
138
142
|
actions: AiChatActions,
|
|
143
|
+
locale: string,
|
|
139
144
|
): ChatAction[] => {
|
|
140
145
|
if (block.status !== "failed" || !actions.onRetrySteer) return [];
|
|
141
146
|
return [
|
|
142
147
|
{
|
|
143
148
|
id: "retry-steer",
|
|
144
|
-
label:
|
|
149
|
+
label: aiChatMessages(locale).actionRetryGuidance,
|
|
145
150
|
icon: "ti ti-refresh",
|
|
146
151
|
onSelect: () => actions.onRetrySteer?.(block),
|
|
147
152
|
},
|
|
@@ -228,7 +228,7 @@ export function CloudSurveyBlock(props: {
|
|
|
228
228
|
</Show>
|
|
229
229
|
</div>
|
|
230
230
|
</div>
|
|
231
|
-
<footer class="flex min-h-12 items-center gap-3
|
|
231
|
+
<footer class="flex min-h-12 items-center gap-3 px-4 pb-3">
|
|
232
232
|
<Show when={submitted()}>
|
|
233
233
|
<span class="inline-flex items-center gap-1.5 text-xs font-medium text-secondary">
|
|
234
234
|
<i class="ti ti-check text-[var(--k2b-ai-accent)]" aria-hidden="true" />
|
|
@@ -94,13 +94,26 @@ const isCurrentStreamSession = (current: AiStreamSession | null, candidate: AiSt
|
|
|
94
94
|
|
|
95
95
|
const DEFAULT_RUN_ERROR = "Assistant response failed.";
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
/** The failure of the chat's latest turn, and that turn when known. */
|
|
98
|
+
type AiRunError = { message: string; turnId: string | null };
|
|
99
99
|
|
|
100
|
-
const
|
|
100
|
+
const conversationRunError = (conversation: AiConversation | null | undefined): AiRunError | null =>
|
|
101
|
+
conversation?.runStatus === "failed"
|
|
102
|
+
? { message: conversation.runError?.trim() || DEFAULT_RUN_ERROR, turnId: conversation.runTurnId ?? null }
|
|
103
|
+
: null;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Whether the failed turn shows its failure itself: its last message carries the reason, and the timeline words it in
|
|
107
|
+
* a notice at the turn's end. The run error then stays out of `error()`, so it does not appear twice. A turn without a
|
|
108
|
+
* recorded reason, such as a failed compaction or a turn from an earlier release, still reports it there.
|
|
109
|
+
*/
|
|
110
|
+
const turnShowsError = (messages: readonly AiStoredMessage[], turnId: string | null): boolean =>
|
|
111
|
+
turnId !== null && messages.some((message) => message.loopId === turnId && message.meta?.turnError);
|
|
112
|
+
|
|
113
|
+
const runErrorFromEvent = (event: AiStreamEvent, activeTurnId: string | null | undefined): AiRunError | null | undefined => {
|
|
101
114
|
if (event.type === "state") return conversationRunError(event.conversation);
|
|
102
115
|
if (event.type !== "turn_finished" || event.turnId !== activeTurnId) return undefined;
|
|
103
|
-
return event.status === "failed" ? event.error?.trim() || DEFAULT_RUN_ERROR : null;
|
|
116
|
+
return event.status === "failed" ? { message: event.error?.trim() || DEFAULT_RUN_ERROR, turnId: event.turnId } : null;
|
|
104
117
|
};
|
|
105
118
|
|
|
106
119
|
export type CreateAiChatControllerOptions = {
|
|
@@ -168,7 +181,7 @@ const errorText = (error: unknown, fallback: string): string =>
|
|
|
168
181
|
export const createAiChatController = (options: CreateAiChatControllerOptions) => {
|
|
169
182
|
const [activeConversationId, setActiveConversationIdSignal] = createSignal<string | null>(options.initialConversationId ?? null);
|
|
170
183
|
const [globalError, setGlobalError] = createSignal<string | null>(options.initialError ?? null);
|
|
171
|
-
const [runError, setRunError] = createSignal<
|
|
184
|
+
const [runError, setRunError] = createSignal<AiRunError | null>(conversationRunError(options.initialDetail?.conversation));
|
|
172
185
|
const [runStatusRaw, setRunStatusRaw] = createSignal<AiChatRunStatus | null>(null);
|
|
173
186
|
const [streamStatus, setStreamStatus] = createSignal<AiStreamStatus>("idle");
|
|
174
187
|
const initialProjection = options.initialDetail ? detailToProjection(options.initialDetail) : emptyProjection();
|
|
@@ -250,7 +263,10 @@ export const createAiChatController = (options: CreateAiChatControllerOptions) =
|
|
|
250
263
|
const conversationId = activeConversationId();
|
|
251
264
|
if (conversationId) openStream(conversationId);
|
|
252
265
|
};
|
|
253
|
-
const error = () =>
|
|
266
|
+
const error = () => {
|
|
267
|
+
const run = runError();
|
|
268
|
+
return globalError() ?? (run && !turnShowsError(state.messages, run.turnId) ? run.message : null);
|
|
269
|
+
};
|
|
254
270
|
|
|
255
271
|
const activeTurn = () => state.activeTurn;
|
|
256
272
|
const messages = createMemo(() => messagesWithPendingSend(visibleMessages(state), pendingSends()[activeConversationId() ?? ""]));
|
|
@@ -1415,6 +1431,7 @@ export const __aiControllerTest = {
|
|
|
1415
1431
|
completeFrontendToolBlock,
|
|
1416
1432
|
conversationRunError,
|
|
1417
1433
|
failSteerBlock,
|
|
1434
|
+
turnShowsError,
|
|
1418
1435
|
projectionForConversationOpen,
|
|
1419
1436
|
reconcileSteerBlocks,
|
|
1420
1437
|
runErrorFromEvent,
|