@k2b/cloud 0.30.0 → 0.32.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 -4
- package/src/_internal/app-presentation.ts +22 -1
- package/src/_internal/capabilities.ts +2 -2
- package/src/_internal/define-app.ts +17 -5
- package/src/_internal/registry.ts +50 -6
- package/src/_internal/widget-declarations.ts +53 -0
- package/src/ai/app-skills.ts +1 -7
- package/src/ai/approval-routes.ts +40 -4
- package/src/ai/approvals.ts +68 -10
- package/src/ai/audio-tool.ts +2 -1
- package/src/ai/browser.ts +109 -0
- package/src/ai/capabilities.ts +6 -2
- package/src/ai/chart-block.ts +431 -81
- package/src/ai/chat/action-sentences.ts +13 -7
- package/src/ai/chat/blocks.tsx +64 -39
- package/src/ai/chat/builtin-tools.tsx +9 -7
- package/src/ai/chat/chart-block.tsx +218 -44
- package/src/ai/chat/explorer-block.tsx +276 -0
- package/src/ai/chat/file-tools.tsx +40 -1
- package/src/ai/chat/live-turn.browser-harness.tsx +4 -1
- package/src/ai/chat/message-actions.tsx +9 -1
- package/src/ai/chat/message-utils.ts +7 -0
- package/src/ai/chat/messages.ts +34 -0
- package/src/ai/chat/presentation.tsx +13 -3
- package/src/ai/chat/reference-text.tsx +34 -0
- package/src/ai/chat/tool-groups.ts +67 -12
- package/src/ai/chat/tool-names.ts +18 -2
- package/src/ai/chat/turn-layout.ts +8 -0
- package/src/ai/chat/turn-view.tsx +111 -32
- package/src/ai/chat/view-block.browser-harness.tsx +35 -0
- package/src/ai/chat/view-block.tsx +417 -0
- package/src/ai/chat/visual-tools.tsx +32 -53
- package/src/ai/chat/web-tools.tsx +95 -1
- package/src/ai/client/controller.ts +6 -3
- package/src/ai/client-tool-results.ts +9 -0
- package/src/ai/code-runtime-tools.ts +50 -17
- package/src/ai/code-source-contracts.ts +25 -21
- package/src/ai/code-source-tools.ts +3 -1
- package/src/ai/default-tool-contracts.ts +334 -91
- package/src/ai/default-tools.ts +87 -11
- package/src/ai/executor.ts +343 -45
- package/src/ai/fetch-file-tool.ts +41 -4
- package/src/ai/file-error.ts +2 -0
- package/src/ai/file-tools.ts +27 -7
- package/src/ai/files-store.ts +60 -0
- package/src/ai/firecrawl-tools.ts +87 -4
- package/src/ai/html-pdf-tool.ts +3 -2
- package/src/ai/http.ts +37 -12
- package/src/ai/index.ts +98 -0
- package/src/ai/local-tool-contracts.ts +244 -0
- package/src/ai/malformed-tool-call-repair.ts +144 -0
- package/src/ai/markdown-pdf-tool.ts +2 -1
- package/src/ai/migrate.ts +120 -2
- package/src/ai/openai-api-messages.ts +53 -0
- package/src/ai/openai-api-slots.ts +40 -0
- package/src/ai/openai-api.ts +383 -0
- package/src/ai/protocol.ts +28 -3
- package/src/ai/provider.ts +16 -4
- package/src/ai/request-context.ts +33 -0
- package/src/ai/routes.ts +99 -49
- package/src/ai/runtime.ts +90 -15
- package/src/ai/settings.ts +5 -0
- package/src/ai/skill-catalog.ts +20 -5
- package/src/ai/skill-format.ts +17 -7
- package/src/ai/skill-tool.ts +15 -5
- package/src/ai/skills-routes.ts +20 -6
- package/src/ai/skills.ts +26 -7
- package/src/ai/store.ts +91 -1
- package/src/ai/system-prompt.ts +69 -10
- package/src/ai/timeline.ts +3 -1
- package/src/ai/tool-audit.ts +11 -1
- package/src/ai/tools.ts +94 -3
- package/src/ai/types.ts +48 -0
- package/src/ai/view-block.ts +941 -0
- package/src/ai/vision-tool.ts +2 -1
- package/src/ai/web-provenance.ts +103 -0
- package/src/api/admin-identity.ts +6 -1
- package/src/api/app-approval-actions.ts +36 -0
- package/src/api/app-approval.ts +10 -2
- package/src/api/approvals-loader.ts +15 -0
- package/src/api/approvals.ts +120 -0
- package/src/api/capabilities.ts +3 -2
- package/src/api/index.ts +3 -0
- package/src/api/me.ts +12 -3
- package/src/api/widgets.ts +36 -5
- package/src/approvals/Approvals.browser-harness.tsx +16 -0
- package/src/approvals/Approvals.tsx +621 -0
- package/src/approvals/ApprovalsNavStatus.tsx +20 -0
- package/src/approvals/messages.ts +95 -0
- package/src/approvals/ui.ts +8 -0
- package/src/browser/CloudResourceSearch.tsx +1 -1
- package/src/browser/app-approval.ts +32 -0
- package/src/browser/choose-files.tsx +26 -13
- package/src/browser/command-shortcuts.ts +21 -6
- package/src/browser/commands.ts +13 -4
- package/src/browser/file-provider-error.ts +11 -0
- package/src/browser/file-providers.ts +3 -12
- package/src/browser/files.tsx +1 -2
- package/src/browser/live-connection.ts +2 -3
- package/src/browser/provider-list.ts +2 -1
- package/src/browser/save-files.tsx +13 -2
- package/src/browser/spotlight-position.ts +4 -1
- package/src/browser/widgets.ts +7 -5
- package/src/capabilities/executions.ts +3 -0
- package/src/cli/account.ts +21 -2
- package/src/cli/commands.ts +24 -2
- package/src/cli/index.ts +16 -0
- package/src/cli/plugin.ts +30 -0
- package/src/contracts/app-approval.ts +120 -0
- package/src/contracts/app.ts +34 -2
- package/src/contracts/approval-paths.ts +4 -0
- package/src/contracts/approvals.ts +81 -0
- package/src/contracts/command-path.ts +17 -0
- package/src/contracts/commands.ts +5 -15
- package/src/contracts/registry.ts +9 -1
- package/src/contracts/shared.ts +5 -0
- package/src/contracts/widgets.ts +47 -2
- package/src/desktop/solid.tsx +6 -7
- package/src/events/live-protocol.ts +2 -1
- package/src/events/live-server-message.ts +40 -0
- package/src/events/testing.ts +88 -0
- package/src/server/index.ts +3 -0
- package/src/server/middleware/auth.ts +11 -1
- package/src/server/request-timeout.ts +22 -0
- package/src/server/services/access.ts +4 -3
- package/src/server/widget-request.ts +27 -0
- package/src/services/app-approval.ts +103 -15
- package/src/services/approvals/cursor.ts +15 -0
- package/src/services/approvals/device.ts +112 -0
- package/src/services/approvals/index.ts +241 -0
- package/src/services/approvals/list.ts +120 -0
- package/src/services/approvals/migrate.ts +41 -0
- package/src/services/identity/models-only-auth.ts +25 -0
- package/src/services/notifications/email-frame.ts +16 -3
- package/src/services/service-account-credentials.ts +22 -10
- package/src/services/settings/core-settings.ts +27 -0
- package/src/shared/ai-platform-prompt.ts +26 -1
- package/src/shared/app-presentation.ts +11 -0
- package/src/shared/email-html.ts +46 -7
- package/src/shared/index.ts +6 -1
- package/src/shared/markdown/extensions/code.ts +14 -0
- package/src/shared/markdown/index.ts +4 -4
- package/src/ssr/AdminLayout.tsx +4 -7
- package/src/ssr/AppBadge.tsx +4 -4
- package/src/ssr/AppLaunchpad.island.tsx +1 -1
- package/src/ssr/AppLaunchpadPanel.tsx +4 -4
- package/src/ssr/Layout.tsx +38 -14
- package/src/ssr/LayoutFooter.tsx +1 -1
- package/src/ssr/LayoutHeader.tsx +17 -20
- package/src/ssr/LayoutHelp.tsx +1 -1
- package/src/ssr/LayoutRail.tsx +1 -2
- package/src/ssr/PageTrail.tsx +23 -0
- package/src/ssr/PageTrailRow.island.tsx +20 -0
- package/src/ssr/PageTrailRuntime.island.tsx +26 -0
- package/src/ssr/ProfilePreferences.island.tsx +7 -3
- package/src/ssr/RailApps.island.tsx +2 -2
- package/src/ssr/app-badges.ts +50 -6
- package/src/ssr/islands/index.ts +1 -0
- package/src/ssr/layout-runtime.ts +60 -0
- package/src/ssr/mobile-menu-history.ts +1 -1
- package/src/ssr/page-trail-runtime.ts +313 -0
- package/src/ssr/page-trail.browser-harness.tsx +265 -0
- package/src/ssr/page-trail.ts +59 -0
- package/src/ssr/platform-messages.ts +4 -0
- package/src/ssr/rail-navigation.ts +5 -3
- package/src/ssr/runtime.ts +6 -1
- package/src/styles/approvals.css +390 -0
- package/src/styles/effects.css +21 -4
- package/src/styles/global.css +39 -3
- package/src/styles/utilities-layout.css +24 -1
- package/src/styles/utilities-navigation.css +6 -18
package/src/ai/timeline.ts
CHANGED
|
@@ -125,7 +125,9 @@ export const buildAiMessageTimeline = (messages: AiStoredMessage[]): AiMessageTi
|
|
|
125
125
|
|
|
126
126
|
const blocks = [
|
|
127
127
|
...steerBlocks,
|
|
128
|
-
...buildBlocksFromMessages(
|
|
128
|
+
...buildBlocksFromMessages(
|
|
129
|
+
entries.map((stored) => ({ seq: stored.seq, message: stored.message, meta: stored.meta, createdAt: stored.createdAt })),
|
|
130
|
+
),
|
|
129
131
|
];
|
|
130
132
|
items.push({
|
|
131
133
|
type: "assistant",
|
package/src/ai/tool-audit.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { sql } from "bun";
|
|
2
|
+
import type { ApprovalDecisionChannel } from "../services/approvals";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* AI-specific tool-call state only: approval, turn linkage, and the request id
|
|
@@ -8,7 +9,14 @@ import { sql } from "bun";
|
|
|
8
9
|
*/
|
|
9
10
|
export type AiToolCallLocation = "server" | "client" | "client_view" | "client_interaction";
|
|
10
11
|
|
|
11
|
-
export type AiToolApprovalState =
|
|
12
|
+
export type AiToolApprovalState =
|
|
13
|
+
| "not_required"
|
|
14
|
+
| "waiting"
|
|
15
|
+
| "approved_once"
|
|
16
|
+
| "approved_for_chat"
|
|
17
|
+
| "approved_always"
|
|
18
|
+
| "approved_by_preference"
|
|
19
|
+
| "rejected";
|
|
12
20
|
|
|
13
21
|
export const aiToolAudit = {
|
|
14
22
|
/** Resolve the current server-recorded invocation, never a model-supplied chat id. */
|
|
@@ -140,12 +148,14 @@ export const aiToolAudit = {
|
|
|
140
148
|
turnId: string;
|
|
141
149
|
callId: string;
|
|
142
150
|
approvalState: Exclude<AiToolApprovalState, "not_required" | "waiting">;
|
|
151
|
+
decidedVia?: ApprovalDecisionChannel;
|
|
143
152
|
}): Promise<void> => {
|
|
144
153
|
const approved = input.approvalState !== "rejected";
|
|
145
154
|
await sql`
|
|
146
155
|
UPDATE ai.tool_calls
|
|
147
156
|
SET
|
|
148
157
|
approval_state = ${input.approvalState},
|
|
158
|
+
approval_decided_via = ${input.decidedVia ?? null},
|
|
149
159
|
status = CASE WHEN ${approved} THEN status ELSE 'rejected' END,
|
|
150
160
|
approved_at = CASE WHEN ${approved} THEN now() ELSE approved_at END,
|
|
151
161
|
rejected_at = CASE WHEN ${approved} THEN rejected_at ELSE now() END
|
package/src/ai/tools.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { z } from "zod";
|
|
|
4
4
|
import type { RequestActor } from "../server";
|
|
5
5
|
import { aiToolNeedsApproval } from "./approvals";
|
|
6
6
|
import type {
|
|
7
|
+
AiApprovalTarget,
|
|
7
8
|
AiDataBoundary,
|
|
8
9
|
AiFrontendToolMode,
|
|
9
10
|
AiProjectFileToolSource,
|
|
@@ -13,6 +14,7 @@ import type {
|
|
|
13
14
|
AiToolApprovalPolicy,
|
|
14
15
|
AiToolDefinition,
|
|
15
16
|
AiToolRuntime,
|
|
17
|
+
AiWebsiteReceipt,
|
|
16
18
|
} from "./types";
|
|
17
19
|
|
|
18
20
|
export const defineAiTool = <TInput extends z.ZodType, TOutput extends z.ZodType>(config: {
|
|
@@ -52,6 +54,7 @@ export const defineAiTool = <TInput extends z.ZodType, TOutput extends z.ZodType
|
|
|
52
54
|
conversationId?: string;
|
|
53
55
|
turnId?: string;
|
|
54
56
|
attachedFilePaths?: ReadonlySet<string>;
|
|
57
|
+
localToolsEnabled?: boolean;
|
|
55
58
|
allowedDataBoundaries?: AiDataBoundary[];
|
|
56
59
|
projectFiles?: AiProjectFileToolSource;
|
|
57
60
|
skillFiles?: AiSkillFileToolSource;
|
|
@@ -60,6 +63,12 @@ export const defineAiTool = <TInput extends z.ZodType, TOutput extends z.ZodType
|
|
|
60
63
|
timeZone?: string;
|
|
61
64
|
/** Localized short status; never include payloads or secrets. */
|
|
62
65
|
reportProgress?: (message: string) => Promise<void>;
|
|
66
|
+
/** Asks like `requestApproval`, and lets the person remember the approval of `target`. */
|
|
67
|
+
requestApprovalFor?: (message: string, target: AiApprovalTarget) => Promise<boolean>;
|
|
68
|
+
/** Relays a decision for an effect another platform component consumes and executes. */
|
|
69
|
+
requestRelayedApproval?: (message: string, options: { approvalId?: string; target?: AiApprovalTarget }) => Promise<boolean>;
|
|
70
|
+
/** Shows requests a website approval let through as they go out, so the chat keeps them whatever the outcome. */
|
|
71
|
+
reportWebsiteReceipts?: (receipts: AiWebsiteReceipt[]) => Promise<void>;
|
|
63
72
|
},
|
|
64
73
|
) => Promise<z.infer<TOutput>>,
|
|
65
74
|
): AiToolRuntime<TInput, TOutput> {
|
|
@@ -99,6 +108,7 @@ export type AiToolPreparationContext = {
|
|
|
99
108
|
conversationId?: string;
|
|
100
109
|
turnId?: string;
|
|
101
110
|
attachedFilePaths?: ReadonlySet<string>;
|
|
111
|
+
localToolsEnabled?: boolean;
|
|
102
112
|
allowedDataBoundaries?: AiDataBoundary[];
|
|
103
113
|
projectFiles?: AiProjectFileToolSource;
|
|
104
114
|
skillFiles?: AiSkillFileToolSource;
|
|
@@ -106,15 +116,93 @@ export type AiToolPreparationContext = {
|
|
|
106
116
|
locale?: string;
|
|
107
117
|
timeZone?: string;
|
|
108
118
|
reportToolProgress?: (callId: string, message: string) => Promise<void>;
|
|
119
|
+
/** Uses the approved snapshot immediately before the tool executes its effect. */
|
|
120
|
+
consumeApproval?: (approvalCallId: string, input: unknown) => Promise<AiApprovalUse>;
|
|
121
|
+
/** Links a relayed decision to the record the effect owner will consume. */
|
|
122
|
+
linkApproval?: (approvalCallId: string, approvalId: string) => void;
|
|
123
|
+
/** Hears what a nested approval asks for before the approval is requested. */
|
|
124
|
+
describeApproval?: (approvalCallId: string, target: AiApprovalTarget) => void;
|
|
125
|
+
reportWebsiteReceipts?: (callId: string, receipts: AiWebsiteReceipt[]) => Promise<void>;
|
|
109
126
|
};
|
|
110
127
|
|
|
128
|
+
/**
|
|
129
|
+
* What using an approval found: `approved` lets the effect run, `changed` means the approved snapshot no longer
|
|
130
|
+
* matches and asks again, and `used` means an earlier run of the call already used it, so the call must not run again.
|
|
131
|
+
*/
|
|
132
|
+
export type AiApprovalUse = "approved" | "changed" | "used";
|
|
133
|
+
|
|
134
|
+
const approvalCallIdPattern = /^(.*)-approval-(\d+)$/;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Whether `askedCallIds` holds a question of the same tool call that Nessi asked after `approvalCallId`, the call's
|
|
138
|
+
* own approval or one of its numbered questions. A turn that continues after such a question runs the tool from its
|
|
139
|
+
* start again, so the approvals before it are used again by the same call.
|
|
140
|
+
*/
|
|
141
|
+
export const aiApprovalAskedLater = (approvalCallId: string, askedCallIds: Iterable<string>): boolean => {
|
|
142
|
+
const own = approvalCallIdPattern.exec(approvalCallId);
|
|
143
|
+
const callId = own ? own[1] : approvalCallId,
|
|
144
|
+
index = own ? Number(own[2]) : -1;
|
|
145
|
+
for (const asked of askedCallIds) {
|
|
146
|
+
const later = approvalCallIdPattern.exec(asked);
|
|
147
|
+
if (later && later[1] === callId && Number(later[2]) > index) return true;
|
|
148
|
+
}
|
|
149
|
+
return false;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const approvalUsedError = () =>
|
|
153
|
+
new Error("This approval was already used, so Cloud does not run the call again. Check its outcome before you call again.");
|
|
154
|
+
|
|
111
155
|
export const prepareAiTools = (input: AiToolPreparationContext & { tools?: AiRuntimeTool[] }): PreparedAiTools => {
|
|
112
156
|
const canonicalNames = new Map<string, string>();
|
|
113
157
|
const approvalPolicies = new Map<string, AiToolApprovalPolicy>();
|
|
114
158
|
const frontendModes = new Map<string, AiFrontendToolMode>();
|
|
115
159
|
|
|
160
|
+
const approvalContext = async (toolInput: unknown, ctx: ToolContext, needsApproval: boolean) => {
|
|
161
|
+
const callId = ctx.callId;
|
|
162
|
+
const use = (approvalCallId: string): Promise<AiApprovalUse> =>
|
|
163
|
+
input.consumeApproval ? input.consumeApproval(approvalCallId, toolInput) : Promise.resolve("approved");
|
|
164
|
+
if (needsApproval && input.consumeApproval) {
|
|
165
|
+
const used = callId ? await use(callId) : "changed";
|
|
166
|
+
if (used === "used") throw approvalUsedError();
|
|
167
|
+
if (used === "changed") throw new Error("This approval no longer matches the call. Ask again.");
|
|
168
|
+
}
|
|
169
|
+
// Nessi numbers the approvals of one execution in request order; count them to name each one's call ID.
|
|
170
|
+
let approvals = 0;
|
|
171
|
+
const requestApproval = async (message: string, target?: AiApprovalTarget): Promise<boolean> => {
|
|
172
|
+
while (true) {
|
|
173
|
+
const approvalCallId = `${callId}-approval-${approvals++}`;
|
|
174
|
+
if (target) input.describeApproval?.(approvalCallId, target);
|
|
175
|
+
if (!(await ctx.requestApproval(message))) return false;
|
|
176
|
+
const used = await use(approvalCallId);
|
|
177
|
+
if (used === "approved") return true;
|
|
178
|
+
if (used === "used") throw approvalUsedError();
|
|
179
|
+
// A changed snapshot requires a new human decision under the next Nessi action ID.
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
return {
|
|
183
|
+
...ctx,
|
|
184
|
+
requestApproval,
|
|
185
|
+
requestApprovalFor: (message: string, target: AiApprovalTarget) => requestApproval(message, target),
|
|
186
|
+
requestRelayedApproval: async (message: string, options: { approvalId?: string; target?: AiApprovalTarget }) => {
|
|
187
|
+
const approvalCallId = `${callId}-approval-${approvals++}`;
|
|
188
|
+
if (options.approvalId) input.linkApproval?.(approvalCallId, options.approvalId);
|
|
189
|
+
if (options.target) input.describeApproval?.(approvalCallId, options.target);
|
|
190
|
+
if (!(await ctx.requestApproval(message))) return false;
|
|
191
|
+
// The effect's own record is used where it runs. Without one, as for a website request from code, the relay
|
|
192
|
+
// that hands over the decision uses the chat's record; a decision it cannot use is not handed over as approval.
|
|
193
|
+
return options.approvalId !== undefined || (await use(approvalCallId)) === "approved";
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
};
|
|
197
|
+
|
|
116
198
|
const tools = (input.tools ?? []).map((tool): Tool => {
|
|
117
|
-
if (!isCloudAiTool(tool))
|
|
199
|
+
if (!isCloudAiTool(tool)) {
|
|
200
|
+
if (tool.kind !== "server") return tool;
|
|
201
|
+
return {
|
|
202
|
+
...tool,
|
|
203
|
+
execute: async (toolInput, ctx) => tool.execute(toolInput, await approvalContext(toolInput, ctx, tool.def.needsApproval === true)),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
118
206
|
|
|
119
207
|
canonicalNames.set(tool.def.name, tool.def.canonicalName ?? tool.def.name);
|
|
120
208
|
approvalPolicies.set(tool.def.name, tool.def.approval);
|
|
@@ -130,16 +218,17 @@ export const prepareAiTools = (input: AiToolPreparationContext & { tools?: AiRun
|
|
|
130
218
|
});
|
|
131
219
|
|
|
132
220
|
if (tool.location === "server") {
|
|
133
|
-
return nessiTool.server((toolInput, ctx) => {
|
|
221
|
+
return nessiTool.server(async (toolInput, ctx) => {
|
|
134
222
|
if (!input.actor) throw new Error(`AI server tool "${tool.def.name}" requires a request actor.`);
|
|
135
223
|
const report = input.reportToolProgress;
|
|
136
224
|
const callId = ctx.callId;
|
|
137
225
|
return tool.run(toolInput, {
|
|
138
|
-
...ctx,
|
|
226
|
+
...(await approvalContext(toolInput, ctx, aiToolNeedsApproval(tool.def.approval))),
|
|
139
227
|
actor: input.actor,
|
|
140
228
|
conversationId: input.conversationId,
|
|
141
229
|
turnId: input.turnId,
|
|
142
230
|
attachedFilePaths: input.attachedFilePaths,
|
|
231
|
+
localToolsEnabled: input.localToolsEnabled,
|
|
143
232
|
allowedDataBoundaries: input.allowedDataBoundaries,
|
|
144
233
|
projectFiles: input.projectFiles,
|
|
145
234
|
skillFiles: input.skillFiles,
|
|
@@ -147,6 +236,8 @@ export const prepareAiTools = (input: AiToolPreparationContext & { tools?: AiRun
|
|
|
147
236
|
locale: input.locale,
|
|
148
237
|
timeZone: input.timeZone,
|
|
149
238
|
reportProgress: callId && report ? (message) => report(callId, message) : undefined,
|
|
239
|
+
reportWebsiteReceipts:
|
|
240
|
+
callId && input.reportWebsiteReceipts ? (receipts) => input.reportWebsiteReceipts!(callId, receipts) : undefined,
|
|
150
241
|
});
|
|
151
242
|
});
|
|
152
243
|
}
|
package/src/ai/types.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type { z } from "zod";
|
|
|
15
15
|
import type { CapabilitySentenceField } from "../_internal/capability-sentences";
|
|
16
16
|
import type { CapabilityActionReview, CapabilityActionSentences, CloudResourceRef } from "../contracts/capabilities";
|
|
17
17
|
import type { RequestActor } from "../server";
|
|
18
|
+
import type { AiLocalClient, AiLocalContext, AiLocalToolName } from "./local-tool-contracts";
|
|
18
19
|
import type { AiTurnBlock } from "./protocol";
|
|
19
20
|
|
|
20
21
|
export const AI_MODEL_CAPABILITIES = ["streaming", "tools", "vision", "transcription"] as const;
|
|
@@ -84,6 +85,7 @@ export type AiSettingsState =
|
|
|
84
85
|
defaultModelId: string;
|
|
85
86
|
visionModelId?: string;
|
|
86
87
|
globalInstructions: string;
|
|
88
|
+
localClientInstructions: string;
|
|
87
89
|
compactionInstructions: string;
|
|
88
90
|
maxToolResultChars: number;
|
|
89
91
|
firecrawlConfigured: boolean;
|
|
@@ -95,6 +97,7 @@ export type AiSettingsState =
|
|
|
95
97
|
defaultModelId: string;
|
|
96
98
|
visionModelId?: string;
|
|
97
99
|
globalInstructions: string;
|
|
100
|
+
localClientInstructions: string;
|
|
98
101
|
compactionInstructions: string;
|
|
99
102
|
maxToolResultChars: number;
|
|
100
103
|
firecrawlConfigured: boolean;
|
|
@@ -303,6 +306,8 @@ export type AiStoredMessage = {
|
|
|
303
306
|
toolPresentations?: Record<string, AiToolPresentation>;
|
|
304
307
|
/** How the person decided an approval; `expired` when the turn ended while it still waited. */
|
|
305
308
|
toolOutcomes?: Record<string, "rejected" | "approved" | "expired">;
|
|
309
|
+
/** Requests a website approval let through, by tool call and then by `AiWebsiteReceipt.index`. */
|
|
310
|
+
websiteReceipts?: Record<string, Record<string, { method: string; url: string }>>;
|
|
306
311
|
/** Why the turn failed, on the last message of its loop. The chat words it in the reader's language. */
|
|
307
312
|
turnError?: AiTurnError;
|
|
308
313
|
} | null;
|
|
@@ -420,6 +425,10 @@ export type AiPendingTurnAction =
|
|
|
420
425
|
message?: string;
|
|
421
426
|
review?: CapabilityActionReview;
|
|
422
427
|
allowAlways: boolean;
|
|
428
|
+
/** Whether the approval can be remembered for this chat. */
|
|
429
|
+
allowChat: boolean;
|
|
430
|
+
/** The website origin a chat approval would allow, for an HTTP request a code run makes. */
|
|
431
|
+
website?: string;
|
|
423
432
|
}
|
|
424
433
|
| {
|
|
425
434
|
type: "frontend_tool";
|
|
@@ -441,6 +450,7 @@ export type AiTurnAbortRequest =
|
|
|
441
450
|
};
|
|
442
451
|
|
|
443
452
|
export type AiPendingTurnActionRecord = {
|
|
453
|
+
approvalId?: string;
|
|
444
454
|
turnId: string;
|
|
445
455
|
conversationId: string;
|
|
446
456
|
callId: string;
|
|
@@ -452,6 +462,10 @@ export type AiPendingTurnActionRecord = {
|
|
|
452
462
|
review?: CapabilityActionReview;
|
|
453
463
|
approvalScope: string;
|
|
454
464
|
allowAlways: boolean;
|
|
465
|
+
/** Whether the approval can be remembered for this chat; defaults to `allowAlways`. */
|
|
466
|
+
allowChat?: boolean;
|
|
467
|
+
/** Remembered-approval name when it differs from `name`: the website or Action a code run asks for. */
|
|
468
|
+
rememberToolName?: string;
|
|
455
469
|
frontendMode?: AiFrontendToolMode;
|
|
456
470
|
resolvedEvent: InboundEvent | null;
|
|
457
471
|
};
|
|
@@ -460,6 +474,7 @@ export type AiTurnToolSource = { kind: "none" } | { kind: "default"; appTools?:
|
|
|
460
474
|
|
|
461
475
|
export type AiClientToolId =
|
|
462
476
|
| "local_bash"
|
|
477
|
+
| AiLocalToolName
|
|
463
478
|
| "code_run"
|
|
464
479
|
| "code_action"
|
|
465
480
|
| "code_inspect"
|
|
@@ -607,6 +622,11 @@ export type AiChatTurnRunConfig = {
|
|
|
607
622
|
selectedSkillIds?: string[];
|
|
608
623
|
/** Server-owned marker: model grants apply only to interactive Assistant chat. */
|
|
609
624
|
assistantChat?: true;
|
|
625
|
+
/**
|
|
626
|
+
* Server-owned marker: a person started this turn in a signed-in browser session, not through `cld`, an API key,
|
|
627
|
+
* a delegated account, or a schedule. Only such a turn uses website approvals.
|
|
628
|
+
*/
|
|
629
|
+
signedInSession?: true;
|
|
610
630
|
kind?: "chat";
|
|
611
631
|
input: Input;
|
|
612
632
|
/** Stable public ID exposed as runtime context, not instructions. */
|
|
@@ -626,6 +646,8 @@ export type AiChatTurnRunConfig = {
|
|
|
626
646
|
/** Whether this turn's materialized tools can inspect referenced images. */
|
|
627
647
|
canInspectAttachedImages?: boolean;
|
|
628
648
|
clientToolIds?: AiClientToolId[];
|
|
649
|
+
client?: AiLocalClient;
|
|
650
|
+
localContext?: AiLocalContext;
|
|
629
651
|
toolSource?: AiTurnToolSource;
|
|
630
652
|
toolApprovalContext?: {
|
|
631
653
|
actorUserId: string;
|
|
@@ -971,6 +993,7 @@ export type AiConversationService = {
|
|
|
971
993
|
savePendingTurnAction(input: AiPendingTurnActionRecord): Promise<void>;
|
|
972
994
|
listPendingTurnActions(input: { conversationId: string; turnId: string }): Promise<AiPendingTurnAction[]>;
|
|
973
995
|
getPendingTurnAction(input: { conversationId: string; turnId: string; callId: string }): Promise<AiPendingTurnActionRecord | null>;
|
|
996
|
+
findPendingTurnActionByApproval(input: { approvalId: string; ownerUserId: string }): Promise<AiPendingTurnActionRecord | null>;
|
|
974
997
|
listPendingActionRecords(input: { conversationId: string; turnId: string }): Promise<AiPendingTurnActionRecord[]>;
|
|
975
998
|
listResolvedPendingActions(input: { conversationId: string; turnId: string }): Promise<AiPendingTurnActionRecord[]>;
|
|
976
999
|
resolvePendingTurnAction(input: {
|
|
@@ -979,6 +1002,7 @@ export type AiConversationService = {
|
|
|
979
1002
|
callId: string;
|
|
980
1003
|
event: InboundEvent;
|
|
981
1004
|
}): Promise<AiPendingTurnActionRecord | null>;
|
|
1005
|
+
markPendingTurnActionConsumedInline(input: { conversationId: string; turnId: string; callId: string }): Promise<void>;
|
|
982
1006
|
clearPendingTurnActions(input: { conversationId: string; turnId: string }): Promise<void>;
|
|
983
1007
|
enqueueTurnSteer(input: {
|
|
984
1008
|
conversationId: string;
|
|
@@ -1016,6 +1040,23 @@ export type AiAccessResult<TAccess = unknown> = {
|
|
|
1016
1040
|
|
|
1017
1041
|
export type AiToolApprovalPolicy = "never" | "once" | "always" | { kind: "user-configurable"; default: "once" | "always"; scope?: string };
|
|
1018
1042
|
|
|
1043
|
+
/**
|
|
1044
|
+
* What a server tool asks a person to approve when the approval can be remembered: a website or an Action, not the
|
|
1045
|
+
* tool itself. A target can always be remembered for its chat.
|
|
1046
|
+
*/
|
|
1047
|
+
export type AiApprovalTarget = {
|
|
1048
|
+
toolName: string;
|
|
1049
|
+
approvalScope: string;
|
|
1050
|
+
/** Whether the person may also remember it everywhere. */
|
|
1051
|
+
always: boolean;
|
|
1052
|
+
};
|
|
1053
|
+
|
|
1054
|
+
/**
|
|
1055
|
+
* A request a remembered website approval let through without asking. The chat shows it with its full URL and a revoke
|
|
1056
|
+
* action; `index` orders the receipts of one tool call, and a receipt reported again with the same index replaces it.
|
|
1057
|
+
*/
|
|
1058
|
+
export type AiWebsiteReceipt = { index: number; method: string; url: string };
|
|
1059
|
+
|
|
1019
1060
|
export type AiFrontendToolMode = "client" | "client_view" | "client_interaction";
|
|
1020
1061
|
|
|
1021
1062
|
export type AiCapabilityToolPresentation = {
|
|
@@ -1063,6 +1104,7 @@ export type AiToolRuntime<TInput extends z.ZodType = z.ZodType, TOutput extends
|
|
|
1063
1104
|
conversationId?: string;
|
|
1064
1105
|
turnId?: string;
|
|
1065
1106
|
attachedFilePaths?: ReadonlySet<string>;
|
|
1107
|
+
localToolsEnabled?: boolean;
|
|
1066
1108
|
allowedDataBoundaries?: AiDataBoundary[];
|
|
1067
1109
|
projectFiles?: AiProjectFileToolSource;
|
|
1068
1110
|
skillFiles?: AiSkillFileToolSource;
|
|
@@ -1071,6 +1113,12 @@ export type AiToolRuntime<TInput extends z.ZodType = z.ZodType, TOutput extends
|
|
|
1071
1113
|
timeZone?: string;
|
|
1072
1114
|
/** Localized short status; never include payloads or secrets. */
|
|
1073
1115
|
reportProgress?: (message: string) => Promise<void>;
|
|
1116
|
+
/** Asks like `requestApproval`, and lets the person remember the approval of `target`. */
|
|
1117
|
+
requestApprovalFor?: (message: string, target: AiApprovalTarget) => Promise<boolean>;
|
|
1118
|
+
/** Relays a decision for an effect another platform component consumes and executes. */
|
|
1119
|
+
requestRelayedApproval?: (message: string, options: { approvalId?: string; target?: AiApprovalTarget }) => Promise<boolean>;
|
|
1120
|
+
/** Shows requests a website approval let through as they go out, so the chat keeps them whatever the outcome. */
|
|
1121
|
+
reportWebsiteReceipts?: (receipts: AiWebsiteReceipt[]) => Promise<void>;
|
|
1074
1122
|
},
|
|
1075
1123
|
): Promise<z.infer<TOutput>>;
|
|
1076
1124
|
}
|