@k2b/cloud 0.6.0-rc.0 → 0.7.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/README.md +2 -2
- package/package.json +30 -17
- package/scripts/browser-performance.ts +14 -0
- package/scripts/build-pdf-renderer.ts +39 -0
- package/scripts/build.ts +15 -35
- package/scripts/preload.ts +9 -12
- package/scripts/sync-recovery-smoke.ts +2 -1
- package/src/_internal/app-presentation.ts +14 -2
- package/src/_internal/capabilities.ts +108 -7
- package/src/_internal/capability-streams.ts +81 -0
- package/src/_internal/define-app.ts +62 -36
- package/src/_internal/heartbeat.ts +4 -0
- package/src/_internal/help-catalog.ts +27 -172
- package/src/_internal/help.ts +13 -76
- package/src/_internal/page-responses.ts +2 -0
- package/src/_internal/postgres-application-name.ts +30 -0
- package/src/_internal/process-identity.ts +15 -0
- package/src/_internal/process-sync.ts +1 -1
- package/src/_internal/registry-validation.ts +29 -25
- package/src/_internal/registry.ts +2 -53
- package/src/_internal/runtime-context.ts +5 -16
- package/src/_internal/server-timing.ts +13 -0
- package/src/_internal/status-preserving-ssr.ts +18 -6
- package/src/_internal/web-vitals-asset.ts +10 -0
- package/src/access/PermissionEditor.tsx +14 -153
- package/src/access/PrincipalPicker.tsx +104 -0
- package/src/access/ui.ts +2 -0
- package/src/ai/admin.ts +14 -13
- package/src/ai/assistant-models.ts +12 -1
- package/src/ai/audio-format.ts +34 -0
- package/src/ai/audio-tool.ts +90 -0
- package/src/ai/browser-code-contracts.ts +79 -0
- package/src/ai/browser.ts +2 -0
- package/src/ai/capabilities.ts +55 -36
- package/src/ai/capability-execution.ts +47 -12
- package/src/ai/chat/blocks.tsx +157 -95
- package/src/ai/chat/capability-result.ts +15 -0
- package/src/ai/chat/capability-table.tsx +2 -10
- package/src/ai/chat/composer-adapter.ts +48 -3
- package/src/ai/chat/message-actions.tsx +8 -7
- package/src/ai/chat/message-utils.ts +12 -1
- package/src/ai/chat/messages.ts +2 -0
- package/src/ai/chat/presentation.tsx +19 -48
- package/src/ai/chat/primitives.tsx +43 -1
- package/src/ai/chat/tool-disclosure.tsx +17 -2
- package/src/ai/chat/tool-groups.ts +52 -0
- package/src/ai/chat-quotas.ts +25 -0
- package/src/ai/chat-task-contracts.ts +9 -1
- package/src/ai/chat-tasks.ts +276 -43
- package/src/ai/client/controller.ts +110 -33
- package/src/ai/client/file-source.ts +12 -6
- package/src/ai/client/live-connection.ts +1 -0
- package/src/ai/client/projection.ts +22 -11
- package/src/ai/client/transport.ts +4 -0
- package/src/ai/code-capability-routes.ts +110 -0
- package/src/ai/code-capability-transport.ts +38 -0
- package/src/ai/code-mode-skill.ts +114 -0
- package/src/ai/code-runtime-tools.ts +166 -0
- package/src/ai/code-source-contracts.ts +202 -0
- package/src/ai/code-source-tools.ts +145 -0
- package/src/ai/compaction.ts +27 -6
- package/src/ai/data-analysis-skill.ts +10 -0
- package/src/ai/default-tools.ts +25 -2
- package/src/ai/dictation-runtime.ts +171 -0
- package/src/ai/dictations.ts +188 -0
- package/src/ai/draft-content.ts +34 -0
- package/src/ai/enrich.ts +6 -0
- package/src/ai/executor.ts +191 -31
- package/src/ai/file-content-version.ts +22 -0
- package/src/ai/file-context.ts +2 -1
- package/src/ai/file-media-type.ts +12 -0
- package/src/ai/file-reference-contracts.ts +15 -0
- package/src/ai/file-tools.ts +2 -1
- package/src/ai/files-store.ts +174 -71
- package/src/ai/fixtures/vision-encrypted.pdf +0 -0
- package/src/ai/grids-skill.ts +40 -10
- package/src/ai/http.ts +15 -8
- package/src/ai/index.ts +26 -2
- package/src/ai/inference-calls.ts +198 -0
- package/src/ai/live-events.ts +5 -1
- package/src/ai/memory-learning.ts +60 -28
- package/src/ai/message-queue.ts +167 -0
- package/src/ai/migrate.ts +187 -11
- package/src/ai/model-pricing.ts +43 -0
- package/src/ai/pdf-render-worker.ts +74 -0
- package/src/ai/pdf-render.fixture.ts +28 -0
- package/src/ai/pdf-render.ts +77 -0
- package/src/ai/projects-routes.ts +3 -3
- package/src/ai/projects.ts +20 -6
- package/src/ai/protocol.ts +3 -0
- package/src/ai/provider.ts +1 -8
- package/src/ai/quota-provider.ts +191 -0
- package/src/ai/quota-report.ts +152 -0
- package/src/ai/quotas-migrate.ts +45 -0
- package/src/ai/quotas.ts +168 -0
- package/src/ai/routes.ts +215 -11
- package/src/ai/run-timeout.ts +10 -0
- package/src/ai/runtime-tools.ts +3 -0
- package/src/ai/runtime.ts +55 -16
- package/src/ai/settings.ts +36 -8
- package/src/ai/short-id.ts +2 -2
- package/src/ai/skill-catalog.ts +0 -10
- package/src/ai/skill-search.ts +47 -0
- package/src/ai/skill-seeds.ts +105 -20
- package/src/ai/skill-tool.ts +16 -5
- package/src/ai/skills-routes.ts +32 -4
- package/src/ai/skills.ts +324 -155
- package/src/ai/store.ts +373 -191
- package/src/ai/stream.ts +5 -0
- package/src/ai/structured-runs.ts +4 -3
- package/src/ai/structured.ts +8 -1
- package/src/ai/system-prompt.ts +12 -2
- package/src/ai/timeline.ts +2 -5
- package/src/ai/todo-contracts.ts +20 -0
- package/src/ai/todo-tool.ts +16 -0
- package/src/ai/tool-audit.ts +30 -40
- package/src/ai/tools.ts +6 -0
- package/src/ai/transcription.ts +202 -0
- package/src/ai/turn-timing.ts +114 -0
- package/src/ai/types.ts +58 -10
- package/src/ai/ui.tsx +2 -0
- package/src/ai/usage.ts +118 -118
- package/src/ai/vision-tool.ts +39 -10
- package/src/api/admin-ai-projects.ts +2 -2
- package/src/api/admin-ai-quotas.ts +74 -0
- package/src/api/admin-ai-skills.ts +48 -5
- package/src/api/admin-ai-usage.ts +2 -2
- package/src/api/admin-core-settings.ts +25 -0
- package/src/api/admin-identity.ts +91 -51
- package/src/api/admin-rail.ts +58 -0
- package/src/api/announcements.ts +14 -0
- package/src/api/capabilities.ts +292 -43
- package/src/api/capability-streams.ts +262 -0
- package/src/api/help.ts +8 -44
- package/src/api/index.ts +6 -0
- package/src/api/mcp.ts +44 -26
- package/src/api/me-web-vitals.ts +35 -0
- package/src/api/me.ts +2 -0
- package/src/api/search/schemas.ts +26 -15
- package/src/api/search.bench.ts +1 -1
- package/src/api/search.ts +54 -17
- package/src/browser/CloudResourceSearch.tsx +640 -702
- package/src/browser/app-approval-vault.ts +19 -7
- package/src/browser/command-bridge.ts +109 -0
- package/src/browser/command-shortcuts.ts +76 -0
- package/src/browser/commands.ts +66 -0
- package/src/browser/navigation-search.ts +48 -0
- package/src/browser/notifications.ts +1 -1
- package/src/browser/resource-picker.tsx +5 -18
- package/src/browser/resource-search-dialog.ts +9 -0
- package/src/browser/resource-search-input.ts +46 -0
- package/src/browser/resource-search-messages.ts +76 -40
- package/src/browser/resource-search.ts +7 -0
- package/src/browser/search-bridge.ts +102 -0
- package/src/browser/search-commands.ts +59 -0
- package/src/browser/search.ts +13 -0
- package/src/browser/spotlight-position.ts +208 -0
- package/src/browser/testing.ts +5 -0
- package/src/browser/web-vitals.ts +25 -0
- package/src/capabilities/claims.ts +179 -0
- package/src/capabilities/client.ts +34 -0
- package/src/capabilities/command-link.ts +16 -0
- package/src/capabilities/executions.ts +298 -0
- package/src/capabilities/migrate.ts +81 -0
- package/src/capabilities/server.ts +114 -34
- package/src/capabilities/stream-body.ts +16 -0
- package/src/capabilities/streams.ts +28 -0
- package/src/cli/admin/ai-quotas.ts +161 -0
- package/src/cli/admin/ai-skills.ts +64 -0
- package/src/cli/admin/ai-usage.ts +53 -30
- package/src/cli/admin/app-credentials.ts +79 -0
- package/src/cli/admin/data.ts +17 -0
- package/src/cli/admin/index.ts +9 -0
- package/src/cli/capabilities.ts +29 -0
- package/src/cli/capability-streams.ts +25 -0
- package/src/cli/index.ts +3 -2
- package/src/config/env.ts +11 -8
- package/src/contracts/app.ts +23 -3
- package/src/contracts/capabilities.ts +68 -5
- package/src/contracts/capability-streams.ts +46 -0
- package/src/contracts/commands.ts +49 -0
- package/src/contracts/index.ts +6 -1
- package/src/contracts/rail-admin.ts +38 -0
- package/src/contracts/rail-preferences.ts +16 -0
- package/src/contracts/registry.ts +3 -27
- package/src/contracts/settings-types.ts +1 -1
- package/src/contracts/shared.ts +9 -5
- package/src/contracts/web-vitals.ts +27 -0
- package/src/desktop/solid.tsx +3 -3
- package/src/index.ts +0 -4
- package/src/server/index.ts +3 -0
- package/src/server/middleware/auth.ts +3 -2
- package/src/server/middleware/route-template.ts +9 -3
- package/src/server/middleware/runtime.ts +2 -1
- package/src/server/middleware/settings.ts +17 -11
- package/src/server/services/access-revision.ts +10 -0
- package/src/server/services/access.ts +1 -1
- package/src/server/services/index.ts +1 -0
- package/src/services/account-category-policy.ts +9 -0
- package/src/services/account-lifecycle/audit.ts +1 -1
- package/src/services/accounts/app.ts +12 -2
- package/src/services/accounts/email-write.ts +23 -0
- package/src/services/accounts/groups.ts +15 -4
- package/src/services/accounts/identities.ts +249 -0
- package/src/services/accounts/identity-reconciliation.ts +87 -0
- package/src/services/accounts/ipa-data.ts +2 -2
- package/src/services/accounts/local-groups.ts +2 -2
- package/src/services/accounts/posix.ts +45 -29
- package/src/services/accounts/users.ts +2 -2
- package/src/services/announcements/index.ts +69 -8
- package/src/services/audit/index.ts +1 -1
- package/src/services/cache-fill.ts +35 -0
- package/src/services/help/index.ts +109 -0
- package/src/services/help/maintenance.ts +25 -0
- package/src/services/help/store.ts +180 -0
- package/src/services/help/types.ts +46 -0
- package/src/services/identity/invocation-actor.ts +2 -2
- package/src/services/identity/key-config.ts +2 -1
- package/src/services/identity/key-ring.ts +1 -1
- package/src/services/index.ts +15 -0
- package/src/services/ipa/users.ts +268 -221
- package/src/services/legal-consent.ts +1 -1
- package/src/services/logging/index.ts +15 -13
- package/src/services/logging/trace.ts +2 -1
- package/src/services/logging/web-vitals.ts +81 -0
- package/src/services/mandates/index.ts +18 -4
- package/src/services/mandates/policy.ts +65 -2
- package/src/services/notifications/batches.ts +124 -57
- package/src/services/notifications/browser.ts +12 -0
- package/src/services/notifications/index.ts +0 -2
- package/src/services/notifications/platform.ts +0 -4
- package/src/services/oauth-tokens.ts +3 -3
- package/src/services/pdf/gotenberg.ts +107 -84
- package/src/services/pdf/index.ts +3 -0
- package/src/services/providers/local/users.ts +25 -16
- package/src/services/public-http.ts +101 -0
- package/src/services/rail-shortcuts.ts +95 -0
- package/src/services/rail-snapshot.ts +53 -0
- package/src/services/request-cache-redis.ts +28 -0
- package/src/services/session/index.ts +15 -2
- package/src/services/session/user.ts +62 -37
- package/src/services/settings/core-settings.ts +21 -28
- package/src/services/settings/defaults.ts +5 -0
- package/src/services/settings/store.ts +58 -84
- package/src/shared/ai-costs.ts +42 -0
- package/src/shared/ai-platform-prompt.ts +1 -1
- package/src/shared/ai-quotas.ts +136 -0
- package/src/shared/ai-usage.ts +6 -16
- package/src/shared/app-presentation.ts +21 -15
- package/src/shared/capability-messages.ts +14 -0
- package/src/shared/help.ts +6 -42
- package/src/shared/index.ts +6 -0
- package/src/ssr/AdminLayout.tsx +7 -5
- package/src/ssr/AdminSidebar.tsx +29 -15
- package/src/ssr/AppLaunchpad.island.tsx +24 -137
- package/src/ssr/AppLaunchpadPanel.tsx +181 -0
- package/src/ssr/BrowserPushRegistration.island.tsx +12 -0
- package/src/ssr/GlobalSearchDialog.tsx +167 -20
- package/src/ssr/GlobalSearchTrigger.island.tsx +33 -22
- package/src/ssr/Layout.tsx +17 -6
- package/src/ssr/LayoutBreadcrumbs.island.tsx +3 -5
- package/src/ssr/LayoutFooter.tsx +1 -1
- package/src/ssr/LayoutHeader.tsx +46 -14
- package/src/ssr/LayoutHelp.tsx +23 -22
- package/src/ssr/{HotkeysHelpRail.island.tsx → LayoutHelpTrigger.island.tsx} +18 -12
- package/src/ssr/LayoutRail.tsx +17 -9
- package/src/ssr/MobileNavigation.tsx +71 -0
- package/src/ssr/MobileProfileActions.tsx +49 -0
- package/src/ssr/ProfilePreferences.island.tsx +3 -36
- package/src/ssr/RailApps.island.tsx +2 -2
- package/src/ssr/RailEditor.tsx +17 -2
- package/src/ssr/WorkspaceNavigation.island.tsx +7 -0
- package/src/ssr/WorkspaceNavigationProvider.tsx +16 -0
- package/src/ssr/admin-navigation.ts +7 -1
- package/src/ssr/help.ts +29 -0
- package/src/ssr/islands/SearchBar.tsx +67 -0
- package/src/ssr/islands/index.ts +3 -1
- package/src/ssr/layout-context.ts +2 -2
- package/src/ssr/layout-help-search.ts +15 -0
- package/src/ssr/mobile-menu-history.ts +70 -0
- package/src/ssr/platform-messages.ts +16 -8
- package/src/ssr/profile-actions.ts +42 -0
- package/src/ssr/rail-context.ts +8 -4
- package/src/ssr/rail-messages.ts +2 -0
- package/src/ssr/rail-navigation.ts +3 -3
- package/src/ssr/workspace-navigation.ts +75 -0
- package/src/styles/effects.css +6 -6
- package/src/styles/global.css +54 -0
- package/src/styles/input.css +43 -41
- package/src/styles/resource-search.css +505 -0
- package/src/styles/utilities-feedback.css +13 -0
- package/src/styles/utilities-layout.css +1 -5
- package/src/styles/utilities-navigation.css +3 -39
- package/src/types/ambient.d.ts +5 -0
- package/src/workflows/ai/runtime.ts +5 -1
- package/src/workflows/ai/store.ts +2 -0
- package/src/workflows/ai-actions.ts +16 -13
- package/src/workflows/definition.ts +10 -5
- package/src/workflows/store/actions.ts +44 -20
- package/src/workflows/store/index.ts +2 -0
- package/src/workflows/store/runs.ts +36 -0
- package/src/workflows/store/worker-pool.ts +55 -0
- package/src/workflows/store/worker-runtime.ts +90 -0
- package/scripts/app-favicon.test.ts +0 -49
- package/src/ai/chat/blocks.render.test.tsx +0 -1108
- package/src/ai/kit-skill.ts +0 -22
- package/src/ai/usage-migrate.ts +0 -81
- package/src/browser/CloudResourceSearch.behavior.test.tsx +0 -74
- package/src/browser/notification-target.ts +0 -27
- package/src/contracts/notification-live.ts +0 -66
- package/src/services/notifications/live.ts +0 -50
- package/src/ssr/BrowserNotifications.island.tsx +0 -135
- package/src/ssr/GlobalSearchDialog.behavior.test.tsx +0 -96
- package/src/ssr/Layout.render.test.tsx +0 -148
- package/src/ssr/islands/SearchBar.island.tsx +0 -80
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const CodeResourceId = z.string().regex(/^[23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz]{6}$/).describe("Public resource short ID returned by code_create or code_list.");
|
|
4
|
+
const id = CodeResourceId;
|
|
5
|
+
const runId = z.string().min(1).max(180).describe("Run ID returned by code_run in this conversation execution host.");
|
|
6
|
+
export const CODE_RUNTIME_TOOL_NAMES = ["code_run", "code_action", "code_inspect", "code_interact", "code_stop", "code_open", "code_export", "code_present", "code_secret"] as const;
|
|
7
|
+
export const CodeRunInput = z.object({
|
|
8
|
+
id: id.optional(),
|
|
9
|
+
resourceId: id.optional().describe("Optional data context for one-off code. Requires Manage; uses this resource database and shared files/KV without changing its source. Local storage stays temporary."),
|
|
10
|
+
version: z.number().int().positive().optional().describe("Published version to run; resource admins only. Omit for the current accessible source."),
|
|
11
|
+
code: z.string().min(1).max(1024 * 1024).optional().describe("One-off JavaScript/TypeScript entry exporting one function. Use code OR a saved resource id; no title or icon needed."),
|
|
12
|
+
inputPaths: z.array(z.string().min(1).max(500)).max(64).default([]).describe("Explicit current-chat input files. Scripts can read them; app test runs expose them only through the simulated picker. User apps never receive chat files."),
|
|
13
|
+
}).strict().refine(input => Number(input.id !== undefined) + Number(input.code !== undefined) === 1, "Provide exactly one of id or code").refine(input => input.version === undefined || input.id !== undefined,"A published version requires a saved resource id").refine(input => input.resourceId === undefined || input.code !== undefined, "resourceId requires one-off code");
|
|
14
|
+
export const CodeActionInput = z.object({
|
|
15
|
+
id,
|
|
16
|
+
action: z.string().regex(/^[a-z][a-zA-Z0-9_]*$/).max(80).describe("Exact name returned by code_actions."),
|
|
17
|
+
publishedVersion: z.number().int().positive().optional().describe("Exact publication returned by code_actions; a changed publication requires fresh discovery. Supply this OR draft revision."),
|
|
18
|
+
revision: z.number().int().positive().optional().describe("Exact draft revision returned by code_actions({draft:true}); Manage required. Supply this OR publishedVersion."),
|
|
19
|
+
input: z.json().describe("Action input matching the discovered inputSchema."),
|
|
20
|
+
}).strict().refine(input => Number(input.publishedVersion !== undefined) + Number(input.revision !== undefined) === 1, "Supply publishedVersion OR draft revision");
|
|
21
|
+
export const CodeInspectInput = z.object({ runId, waitMs: z.number().int().min(0).max(30000).default(0).describe("Wait up to this duration for background work to finish before returning its real state; never restarts work."), nodeId: z.string().min(1).max(80).optional(), offset: z.number().int().min(0).default(0), limit: z.number().int().min(1).max(100).default(20) }).strict();
|
|
22
|
+
const CodeInteraction = z.object({
|
|
23
|
+
id: z.string().min(1).max(80).describe("Control ID or pending modal ID returned by the snapshot."),
|
|
24
|
+
event: z.discriminatedUnion("type", [
|
|
25
|
+
z.object({ type: z.literal("change"), value: z.union([z.string(), z.number().finite(), z.boolean(), z.null(), z.array(z.string()), z.object({ start: z.iso.date().nullable(), end: z.iso.date().nullable() }).strict()]) }).strict(),
|
|
26
|
+
z.object({ type: z.literal("select"), key: z.string().nullable() }).strict(),
|
|
27
|
+
z.object({ type: z.literal("view"), value: z.enum(["chart", "table"]) }).strict(),
|
|
28
|
+
z.object({ type: z.literal("refresh") }).strict(),
|
|
29
|
+
z.object({ type: z.literal("request"), request: z.record(z.string(), z.json()) }).strict(),
|
|
30
|
+
]).optional().describe("An event object, never a JSON-encoded string. Omit to activate a button. Example: {type:'change',value:['Sued']} or {type:'view',value:'table'}."),
|
|
31
|
+
answer: z.json().optional().describe("Only for the pending modal ID: the modal answer, or null to cancel. Do not use for controls."),
|
|
32
|
+
}).strict();
|
|
33
|
+
const validInteraction = (input: z.infer<typeof CodeInteraction>) => input.event === undefined || input.answer === undefined;
|
|
34
|
+
export const CodeInteractInput = CodeInteraction.partial({id:true}).extend({
|
|
35
|
+
runId,
|
|
36
|
+
// Three normal 15-second callback budgets share the existing 45-second call budget.
|
|
37
|
+
steps:z.array(CodeInteraction.refine(validInteraction,"Supply event OR answer")).min(1).max(3).optional()
|
|
38
|
+
.describe("Up to three sequential interactions in one call. Use steps OR the top-level id/event/answer. Stops on an error, pending modal or background work; returns completedSteps and nextStep."),
|
|
39
|
+
}).strict().refine(input=>input.steps ? input.id===undefined && input.event===undefined && input.answer===undefined : input.id!==undefined && (input.event===undefined || input.answer===undefined),"Supply steps OR one id with event/answer.");
|
|
40
|
+
export const CodeStopInput = z.object({ runId }).strict();
|
|
41
|
+
export const CodeOpenInput = z.object({ id }).strict();
|
|
42
|
+
export const CodePresentInput = z.object({ runId, title: z.string().trim().min(1).max(120) }).strict();
|
|
43
|
+
export const CodeExportInput = z.object({ runId, name: z.string().min(1).max(180).describe("Captured output filename returned by the snapshot.") }).strict();
|
|
44
|
+
|
|
45
|
+
export const CodeSecretInput = z.object({
|
|
46
|
+
resourceId:id.optional().describe("Scope to this app/script; omit to configure the current chat only."),
|
|
47
|
+
name:z.string().regex(/^[a-zA-Z][a-zA-Z0-9_-]{0,79}$/),
|
|
48
|
+
origin:z.string().url().max(2000).describe("Exact HTTPS origin allowed to receive the secret."),
|
|
49
|
+
header:z.string().min(1).max(80).default("authorization"),
|
|
50
|
+
prefix:z.string().max(160).default("Bearer "),
|
|
51
|
+
}).strict();
|
|
52
|
+
|
|
53
|
+
// Internal bridge envelope. Each model-facing tool receives only its own flat schema.
|
|
54
|
+
export const CodeRuntimeInput = z.discriminatedUnion("operation", [
|
|
55
|
+
CodeRunInput.safeExtend({ operation: z.literal("run") }),
|
|
56
|
+
CodeActionInput.safeExtend({ operation: z.literal("action") }),
|
|
57
|
+
CodeInspectInput.extend({ operation: z.literal("inspect") }),
|
|
58
|
+
CodeInteractInput.safeExtend({ operation: z.literal("interact") }),
|
|
59
|
+
CodeStopInput.extend({ operation: z.literal("stop") }),
|
|
60
|
+
CodeOpenInput.extend({ operation: z.literal("open") }),
|
|
61
|
+
CodeSecretInput.extend({ operation: z.literal("secret") }),
|
|
62
|
+
CodePresentInput.extend({ operation: z.literal("present") }),
|
|
63
|
+
CodeExportInput.extend({ operation: z.literal("export") }),
|
|
64
|
+
]);
|
|
65
|
+
export type CodeRuntimeInput = z.infer<typeof CodeRuntimeInput>;
|
|
66
|
+
export function parseCodeToolInput(name: string, args: unknown): CodeRuntimeInput {
|
|
67
|
+
switch (name) {
|
|
68
|
+
case "code_secret": return { operation: "secret", ...CodeSecretInput.parse(args) };
|
|
69
|
+
case "code_action": return { operation: "action", ...CodeActionInput.parse(args) };
|
|
70
|
+
case "code_run": return { operation: "run", ...CodeRunInput.parse(args) };
|
|
71
|
+
case "code_inspect": return { operation: "inspect", ...CodeInspectInput.parse(args) };
|
|
72
|
+
case "code_interact": return { operation: "interact", ...CodeInteractInput.parse(args) };
|
|
73
|
+
case "code_stop": return { operation: "stop", ...CodeStopInput.parse(args) };
|
|
74
|
+
case "code_open": return { operation: "open", ...CodeOpenInput.parse(args) };
|
|
75
|
+
case "code_present": return { operation: "present", ...CodePresentInput.parse(args) };
|
|
76
|
+
case "code_export": return { operation: "export", ...CodeExportInput.parse(args) };
|
|
77
|
+
default: throw new Error("Unknown code tool");
|
|
78
|
+
}
|
|
79
|
+
}
|
package/src/ai/browser.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { parseAiTodoPlan, type AiTodoPlan } from "./todo-contracts";
|
|
2
|
+
export { CodeResourceId, CodeActionInput, CODE_RUNTIME_TOOL_NAMES, CodeRuntimeInput, parseCodeToolInput } from "./browser-code-contracts";
|
|
1
3
|
import { AI_TURN_ATTACHMENT_MAX_ITEMS } from "./limits";
|
|
2
4
|
import type { AiConversation, AiDraftContentPart } from "./types";
|
|
3
5
|
|
package/src/ai/capabilities.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
+
import { createHelpReader, type HelpReaderFactory } from "../services/help";
|
|
2
|
+
import { createCodeSourceTool } from "./code-source-tools";
|
|
3
|
+
import { CODE_SOURCE_TOOLS } from "./code-source-contracts";
|
|
1
4
|
import { createHash } from "node:crypto";
|
|
2
5
|
import type { Tool, ToolContext, ToolResolver } from "@k2b/nessi";
|
|
3
6
|
import { z } from "zod";
|
|
4
|
-
import {
|
|
5
|
-
createHelpCatalog,
|
|
6
|
-
HELP_READ_MAX_CHARS,
|
|
7
|
-
HELP_SEARCH_MAX_LIMIT,
|
|
8
|
-
readHelpCatalog,
|
|
9
|
-
searchHelpCatalog,
|
|
10
|
-
} from "../_internal/help-catalog";
|
|
7
|
+
import { HELP_READ_MAX_CHARS, HELP_SEARCH_MAX_LIMIT, readHelpArticle } from "../_internal/help-catalog";
|
|
11
8
|
import {
|
|
12
9
|
type CapabilityActionManifest,
|
|
13
10
|
type CapabilityActionReview,
|
|
@@ -16,8 +13,9 @@ import {
|
|
|
16
13
|
cloudResourceRefAppId,
|
|
17
14
|
resolveCapabilityResourceReader,
|
|
18
15
|
} from "../contracts/capabilities";
|
|
19
|
-
import type { CapabilityRegistryEntry
|
|
16
|
+
import type { CapabilityRegistryEntry } from "../contracts/registry";
|
|
20
17
|
import type { RequestActor } from "../server";
|
|
18
|
+
import { recordRejectedAiCapability } from "./capability-execution";
|
|
21
19
|
import { CLOUD_AI_DEFERRED_BUILTIN_TOOL_NAMES } from "./default-tools";
|
|
22
20
|
import { type AiToolPreparationContext, defineAiTool, type PreparedAiTools, prepareAiTools } from "./tools";
|
|
23
21
|
import type { AiConversationService, AiRuntimeTool, AiToolPresentation } from "./types";
|
|
@@ -304,14 +302,14 @@ const AiHelpDocumentSchema = AiHelpCatalogItemSchema.extend({
|
|
|
304
302
|
truncated: z.boolean(),
|
|
305
303
|
}).strict();
|
|
306
304
|
|
|
307
|
-
/** Search and read
|
|
308
|
-
export const createAiHelpTools = (
|
|
309
|
-
const
|
|
305
|
+
/** Search and read published Help without loading one tool per article. */
|
|
306
|
+
export const createAiHelpTools = (help: HelpReaderFactory = createHelpReader, locale = "en"): AiRuntimeTool[] => {
|
|
307
|
+
const reader = help(locale);
|
|
310
308
|
|
|
311
309
|
const search = defineAiTool({
|
|
312
310
|
name: "search_help",
|
|
313
311
|
description:
|
|
314
|
-
"Search installed Cloud app Help when product behavior, settings, workflows, permissions, or app errors are unclear. Use
|
|
312
|
+
"Search installed Cloud app Help when product behavior, settings, workflows, permissions, or app errors are unclear. Use concise product terms in the request language and scope appId when known. Returns compact document ids for read_help; skip this tool for straightforward live-data requests.",
|
|
315
313
|
inputSchema: z
|
|
316
314
|
.object({
|
|
317
315
|
query: z.string().trim().min(1).max(200).describe("Product task or concept to find."),
|
|
@@ -322,7 +320,7 @@ export const createAiHelpTools = (registry: readonly HelpRegistryEntry[], locale
|
|
|
322
320
|
outputSchema: z.object({ documents: z.array(AiHelpCatalogItemSchema).max(HELP_SEARCH_MAX_LIMIT) }).strict(),
|
|
323
321
|
approval: "never",
|
|
324
322
|
}).server(async ({ query, appId, limit }) => ({
|
|
325
|
-
documents:
|
|
323
|
+
documents: await reader.search({ query, appId, limit: boundedLimit(limit, DEFAULT_SEARCH_LIMIT, HELP_SEARCH_MAX_LIMIT) }),
|
|
326
324
|
}));
|
|
327
325
|
|
|
328
326
|
const read = defineAiTool({
|
|
@@ -338,25 +336,14 @@ export const createAiHelpTools = (registry: readonly HelpRegistryEntry[], locale
|
|
|
338
336
|
.strict(),
|
|
339
337
|
outputSchema: z.object({ document: AiHelpDocumentSchema.nullable() }).strict(),
|
|
340
338
|
approval: "never",
|
|
341
|
-
}).server(async ({ appId, documentId, query }) =>
|
|
342
|
-
document
|
|
343
|
-
|
|
339
|
+
}).server(async ({ appId, documentId, query }) => {
|
|
340
|
+
const document = await reader.read({ appId, documentId });
|
|
341
|
+
return { document: document ? readHelpArticle(document, query) : null };
|
|
342
|
+
});
|
|
344
343
|
|
|
345
344
|
return [search, read];
|
|
346
345
|
};
|
|
347
346
|
|
|
348
|
-
const resolveHelpRegistry = async (
|
|
349
|
-
listRegistry: () => Promise<HelpRegistryEntry[]>,
|
|
350
|
-
onError?: (error: unknown) => void,
|
|
351
|
-
): Promise<HelpRegistryEntry[]> => {
|
|
352
|
-
try {
|
|
353
|
-
return await listRegistry();
|
|
354
|
-
} catch (error) {
|
|
355
|
-
onError?.(error);
|
|
356
|
-
return [];
|
|
357
|
-
}
|
|
358
|
-
};
|
|
359
|
-
|
|
360
347
|
const resolveCapabilityRegistry = async (
|
|
361
348
|
listRegistry: () => Promise<CapabilityRegistryEntry[]>,
|
|
362
349
|
onError?: (error: unknown) => void,
|
|
@@ -554,7 +541,9 @@ export const createAiToolMetaTools = (input: {
|
|
|
554
541
|
export const createLoadedAiCapabilityTools = (input: {
|
|
555
542
|
catalog: readonly AiCapabilityCatalogEntry[];
|
|
556
543
|
loadedNames: readonly string[];
|
|
544
|
+
actor: RequestActor;
|
|
557
545
|
review?: (entry: AiCapabilityCatalogEntry, args: unknown, context: ToolContext) => Promise<CapabilityActionReview | null>;
|
|
546
|
+
authorizeBackground?: (entry: AiCapabilityCatalogEntry, args: unknown) => Promise<void>;
|
|
558
547
|
onReview?: (callId: string, review: CapabilityActionReview) => void;
|
|
559
548
|
execute: (entry: AiCapabilityCatalogEntry, args: unknown, context: ToolContext) => Promise<unknown>;
|
|
560
549
|
}): AiRuntimeTool[] => {
|
|
@@ -573,12 +562,18 @@ export const createLoadedAiCapabilityTools = (input: {
|
|
|
573
562
|
// live review has resolved. The review may supply an app-owned scope.
|
|
574
563
|
approval: "never",
|
|
575
564
|
}).server(async (args, context) => {
|
|
576
|
-
|
|
565
|
+
await input.authorizeBackground?.(entry, args);
|
|
566
|
+
if (!input.authorizeBackground && entry.kind === "action" && (entry.operation as CapabilityActionManifest).approval !== "none") {
|
|
577
567
|
const review = (await input.review?.(entry, args, context)) ?? null;
|
|
578
568
|
if (review && context.callId) input.onReview?.(context.callId, review);
|
|
579
569
|
const message =
|
|
580
570
|
review?.message ?? `${entry.appName}: ${entry.title}\nReview the validated arguments below before running this Action.`;
|
|
581
|
-
if (!(await context.requestApproval(message)))
|
|
571
|
+
if (!(await context.requestApproval(message))) {
|
|
572
|
+
if (input.actor.kind === "user") {
|
|
573
|
+
await recordRejectedAiCapability({ entry, actor: input.actor, args }).catch(() => undefined);
|
|
574
|
+
}
|
|
575
|
+
throw new Error("Capability Action was rejected by the user.");
|
|
576
|
+
}
|
|
582
577
|
}
|
|
583
578
|
return input.execute(entry, args, context);
|
|
584
579
|
}),
|
|
@@ -599,6 +594,11 @@ export const createAiResourceReaderTool = (input: {
|
|
|
599
594
|
outputSchema: z.unknown(),
|
|
600
595
|
approval: "never",
|
|
601
596
|
}).server(async (ref, context) => {
|
|
597
|
+
if (ref.type === "assistant.artifact") {
|
|
598
|
+
const tool = createCodeSourceTool("code_read");
|
|
599
|
+
if (tool.location !== "server") throw new Error("Code reader must execute on the server.");
|
|
600
|
+
return tool.run(CODE_SOURCE_TOOLS.code_read.input.parse({ id: ref.id }), context);
|
|
601
|
+
}
|
|
602
602
|
const appId = cloudResourceRefAppId(ref);
|
|
603
603
|
const app = input.apps.find((candidate) => candidate.appId === appId);
|
|
604
604
|
const reader = app ? resolveCapabilityResourceReader(app.manifest, ref) : null;
|
|
@@ -611,6 +611,24 @@ export const createAiResourceReaderTool = (input: {
|
|
|
611
611
|
});
|
|
612
612
|
|
|
613
613
|
/** Nessi resolver: one registry/load-state snapshot drives discovery, loading, schemas, and execution per model turn. */
|
|
614
|
+
/** Background discovery belongs to one run, never the interactive conversation. */
|
|
615
|
+
export const createRunToolStore = (initialNames: readonly string[]): Pick<AiConversationService, "getLoadedTools" | "loadTools"> => {
|
|
616
|
+
let current = [...new Set(initialNames)];
|
|
617
|
+
return {
|
|
618
|
+
getLoadedTools: async () => [...current],
|
|
619
|
+
loadTools: async ({ names, maxLoadedTools }) => {
|
|
620
|
+
const requested = [...new Set(names.map((name) => name.trim()).filter(Boolean))];
|
|
621
|
+
const alreadyLoaded = requested.filter((name) => current.includes(name));
|
|
622
|
+
const added = requested.filter((name) => !current.includes(name));
|
|
623
|
+
const combined = [...current, ...added];
|
|
624
|
+
const limit = Math.floor(maxLoadedTools ?? 0);
|
|
625
|
+
const evicted = limit > 0 ? combined.slice(0, Math.max(0, combined.length - limit)) : [];
|
|
626
|
+
current = combined.slice(evicted.length);
|
|
627
|
+
return { loaded: added.filter((name) => current.includes(name)), alreadyLoaded, evicted };
|
|
628
|
+
},
|
|
629
|
+
};
|
|
630
|
+
};
|
|
631
|
+
|
|
614
632
|
export const createAiToolResolver =
|
|
615
633
|
(input: {
|
|
616
634
|
conversationId: string;
|
|
@@ -621,12 +639,12 @@ export const createAiToolResolver =
|
|
|
621
639
|
store: Pick<AiConversationService, "getLoadedTools" | "loadTools">;
|
|
622
640
|
listRegistry?: () => Promise<CapabilityRegistryEntry[]>;
|
|
623
641
|
onCapabilityRegistryError?: (error: unknown) => void;
|
|
624
|
-
|
|
625
|
-
onHelpRegistryError?: (error: unknown) => void;
|
|
642
|
+
help?: HelpReaderFactory;
|
|
626
643
|
locale?: string;
|
|
627
644
|
maxLoadedTools?: number;
|
|
628
645
|
execute?: (entry: AiCapabilityCatalogEntry, args: unknown, context: ToolContext) => Promise<unknown>;
|
|
629
646
|
review?: (entry: AiCapabilityCatalogEntry, args: unknown, context: ToolContext) => Promise<CapabilityActionReview | null>;
|
|
647
|
+
authorizeBackground?: (entry: AiCapabilityCatalogEntry, args: unknown) => Promise<void>;
|
|
630
648
|
onReview?: (callId: string, review: CapabilityActionReview) => void;
|
|
631
649
|
onPrepared?: (snapshot: {
|
|
632
650
|
prepared: PreparedAiTools;
|
|
@@ -635,10 +653,9 @@ export const createAiToolResolver =
|
|
|
635
653
|
}) => void;
|
|
636
654
|
}): ToolResolver =>
|
|
637
655
|
async (): Promise<Tool[]> => {
|
|
638
|
-
const [registry, persistedLoadedNames
|
|
656
|
+
const [registry, persistedLoadedNames] = await Promise.all([
|
|
639
657
|
input.listRegistry ? resolveCapabilityRegistry(input.listRegistry, input.onCapabilityRegistryError) : [],
|
|
640
658
|
input.store.getLoadedTools({ conversationId: input.conversationId }),
|
|
641
|
-
input.listHelpRegistry ? resolveHelpRegistry(input.listHelpRegistry, input.onHelpRegistryError) : [],
|
|
642
659
|
]);
|
|
643
660
|
const configuredLimit = Math.floor(input.maxLoadedTools ?? 0);
|
|
644
661
|
const loadedNames = configuredLimit > 0 ? persistedLoadedNames.slice(-configuredLimit) : persistedLoadedNames;
|
|
@@ -651,9 +668,9 @@ export const createAiToolResolver =
|
|
|
651
668
|
}
|
|
652
669
|
const allowed = input.allowedTools == null ? null : new Set(input.allowedTools);
|
|
653
670
|
const capabilityCatalog = buildAiCapabilityCatalog(registry).filter((entry) => !allowed || allowed.has(entry.name));
|
|
654
|
-
const helpTools = input.
|
|
671
|
+
const helpTools = input.help ? createAiHelpTools(input.help, input.locale) : [];
|
|
655
672
|
const resourceTool =
|
|
656
|
-
input.execute && capabilityCatalog.length > 0
|
|
673
|
+
input.execute && (capabilityCatalog.length > 0 || input.staticTools.some((tool) => tool.def.name === "code_read"))
|
|
657
674
|
? createAiResourceReaderTool({ apps: registry, catalog: capabilityCatalog, execute: input.execute })
|
|
658
675
|
: null;
|
|
659
676
|
const builtIns = [...input.staticTools, ...helpTools, ...(resourceTool ? [resourceTool] : [])].filter(
|
|
@@ -679,8 +696,10 @@ export const createAiToolResolver =
|
|
|
679
696
|
? createLoadedAiCapabilityTools({
|
|
680
697
|
catalog: capabilityCatalog,
|
|
681
698
|
loadedNames,
|
|
699
|
+
actor: input.actor,
|
|
682
700
|
review: input.review,
|
|
683
701
|
onReview: input.onReview,
|
|
702
|
+
authorizeBackground: input.authorizeBackground,
|
|
684
703
|
execute: input.execute,
|
|
685
704
|
})
|
|
686
705
|
: []),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import type { ToolContext } from "@k2b/nessi";
|
|
3
3
|
import { type CapabilityDispatchDependencies, dispatchCapability } from "../api/capabilities";
|
|
4
|
+
import { capabilityValueMeta, recordCapabilityExecution } from "../capabilities/executions";
|
|
4
5
|
import { type CapabilityActionManifest, type CapabilityActionReview, CapabilityActionReviewSchema } from "../contracts/capabilities";
|
|
5
6
|
import type { RequestActor } from "../server";
|
|
6
7
|
import type { AccessSubject } from "../server/services/access";
|
|
@@ -98,18 +99,24 @@ const dispatchAiCapability = async (input: AiCapabilityCall, review: boolean): P
|
|
|
98
99
|
const headers = new Headers();
|
|
99
100
|
if (input.locale) headers.set(LOCALE_HEADER, input.locale);
|
|
100
101
|
const action = input.entry.kind === "action" ? (input.entry.operation as CapabilityActionManifest) : null;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
102
|
+
// One request id per dispatch links the tool call, the platform execution
|
|
103
|
+
// record, and the target app's own audit rows.
|
|
104
|
+
const requestId = crypto.randomUUID();
|
|
105
|
+
headers.set("x-request-id", requestId);
|
|
106
|
+
const key =
|
|
107
|
+
!review && action?.idempotency === "required" && input.context.callId
|
|
108
|
+
? idempotencyKey(input.conversationId, input.context.callId)
|
|
109
|
+
: null;
|
|
110
|
+
if (key) headers.set("idempotency-key", key);
|
|
111
|
+
if (!review && input.turnId && input.context.callId) {
|
|
112
|
+
await aiToolAudit.noteCapabilityDispatch({
|
|
113
|
+
conversationId: input.conversationId,
|
|
114
|
+
turnId: input.turnId,
|
|
115
|
+
callId: input.context.callId,
|
|
116
|
+
toolName: input.entry.name,
|
|
117
|
+
requestId,
|
|
118
|
+
idempotencyKey: key,
|
|
119
|
+
});
|
|
113
120
|
}
|
|
114
121
|
const request = new Request("http://cloud.internal/api/ai/capability", {
|
|
115
122
|
method: "POST",
|
|
@@ -120,6 +127,7 @@ const dispatchAiCapability = async (input: AiCapabilityCall, review: boolean): P
|
|
|
120
127
|
await dispatch({
|
|
121
128
|
request,
|
|
122
129
|
kind: input.entry.kind === "query" ? "queries" : "actions",
|
|
130
|
+
origin: "assistant",
|
|
123
131
|
review,
|
|
124
132
|
appId: input.entry.appId,
|
|
125
133
|
capabilityId: input.entry.operation.localId,
|
|
@@ -149,3 +157,30 @@ export const reviewAiCapability = async (input: AiCapabilityCall): Promise<Capab
|
|
|
149
157
|
};
|
|
150
158
|
|
|
151
159
|
export const executeAiCapability = (input: AiCapabilityCall): Promise<unknown> => dispatchAiCapability(input, false);
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* A rejected approval never reaches the dispatcher, so the assistant records
|
|
163
|
+
* the same execution row the dispatcher would have written for the attempt.
|
|
164
|
+
*/
|
|
165
|
+
export const recordRejectedAiCapability = async (input: {
|
|
166
|
+
entry: Pick<AiCapabilityCatalogEntry, "appId" | "kind" | "name" | "operation">;
|
|
167
|
+
actor: CapabilityActor;
|
|
168
|
+
args: unknown;
|
|
169
|
+
}): Promise<void> => {
|
|
170
|
+
const at = new Date();
|
|
171
|
+
await recordCapabilityExecution({
|
|
172
|
+
requestId: crypto.randomUUID(),
|
|
173
|
+
origin: "assistant",
|
|
174
|
+
appId: input.entry.appId,
|
|
175
|
+
capability: input.entry.name,
|
|
176
|
+
kind: input.entry.kind,
|
|
177
|
+
destructive: "destructive" in input.entry.operation ? input.entry.operation.destructive : false,
|
|
178
|
+
actorKind: "user",
|
|
179
|
+
actorId: input.actor.user.id,
|
|
180
|
+
userId: input.actor.user.id,
|
|
181
|
+
status: "rejected",
|
|
182
|
+
inputMeta: capabilityValueMeta(input.args),
|
|
183
|
+
startedAt: at,
|
|
184
|
+
completedAt: at,
|
|
185
|
+
});
|
|
186
|
+
};
|