@k2b/cloud 0.6.0 → 0.8.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 (349) hide show
  1. package/README.md +2 -2
  2. package/package.json +22 -11
  3. package/scripts/README.md +10 -0
  4. package/scripts/browser-performance.ts +14 -0
  5. package/scripts/build-pdf-renderer.ts +39 -0
  6. package/scripts/build.ts +20 -35
  7. package/scripts/preload.ts +16 -12
  8. package/scripts/sync-recovery-smoke.ts +22 -6
  9. package/src/_internal/app-presentation.ts +14 -2
  10. package/src/_internal/build-metadata.ts +8 -1
  11. package/src/_internal/capabilities.ts +131 -7
  12. package/src/_internal/capability-streams.ts +81 -0
  13. package/src/_internal/define-app.ts +80 -37
  14. package/src/_internal/heartbeat.ts +4 -0
  15. package/src/_internal/help-catalog.ts +27 -172
  16. package/src/_internal/help.ts +13 -76
  17. package/src/_internal/page-responses.ts +2 -0
  18. package/src/_internal/postgres-application-name.ts +30 -0
  19. package/src/_internal/process-identity.ts +15 -0
  20. package/src/_internal/process-sync.ts +1 -1
  21. package/src/_internal/readiness.ts +4 -1
  22. package/src/_internal/registry-validation.ts +29 -25
  23. package/src/_internal/registry.ts +2 -53
  24. package/src/_internal/runtime-context.ts +5 -16
  25. package/src/_internal/server-timing.ts +13 -0
  26. package/src/_internal/status-preserving-ssr.ts +18 -6
  27. package/src/_internal/web-vitals-asset.ts +10 -0
  28. package/src/access/PermissionEditor.tsx +18 -151
  29. package/src/access/PrincipalPicker.tsx +110 -0
  30. package/src/access/ResourceApiKeys.tsx +1 -1
  31. package/src/access/ui.ts +1 -0
  32. package/src/ai/admin.ts +14 -13
  33. package/src/ai/assistant-models.ts +12 -1
  34. package/src/ai/audio-format.ts +34 -0
  35. package/src/ai/audio-tool.ts +90 -0
  36. package/src/ai/browser-code-contracts.ts +201 -0
  37. package/src/ai/browser.ts +4 -0
  38. package/src/ai/capabilities.ts +55 -36
  39. package/src/ai/capability-execution.ts +47 -12
  40. package/src/ai/chat/blocks.tsx +229 -90
  41. package/src/ai/chat/builtin-tools.tsx +2 -4
  42. package/src/ai/chat/capability-result.ts +15 -0
  43. package/src/ai/chat/capability-table.tsx +2 -10
  44. package/src/ai/chat/composer-adapter.ts +83 -3
  45. package/src/ai/chat/message-actions.tsx +8 -7
  46. package/src/ai/chat/message-utils.ts +25 -1
  47. package/src/ai/chat/messages.ts +2 -0
  48. package/src/ai/chat/presentation.tsx +31 -47
  49. package/src/ai/chat/primitives.tsx +43 -1
  50. package/src/ai/chat/tool-disclosure.tsx +21 -2
  51. package/src/ai/chat/tool-groups.ts +58 -0
  52. package/src/ai/chat-quotas.ts +25 -0
  53. package/src/ai/chat-task-contracts.ts +14 -10
  54. package/src/ai/chat-tasks.ts +297 -43
  55. package/src/ai/client/controller.ts +165 -39
  56. package/src/ai/client/file-source.ts +12 -6
  57. package/src/ai/client/live-connection.ts +1 -0
  58. package/src/ai/client/projection.ts +29 -11
  59. package/src/ai/client/transport.ts +6 -0
  60. package/src/ai/code-capability-routes.ts +105 -0
  61. package/src/ai/code-capability-transport.ts +39 -0
  62. package/src/ai/code-execution.ts +53 -0
  63. package/src/ai/code-mode-skill.ts +118 -0
  64. package/src/ai/code-runtime-tools.ts +179 -0
  65. package/src/ai/code-source-contracts.ts +270 -0
  66. package/src/ai/code-source-tools.ts +188 -0
  67. package/src/ai/compaction.ts +27 -6
  68. package/src/ai/data-analysis-skill.ts +10 -0
  69. package/src/ai/default-tools.ts +99 -3
  70. package/src/ai/dictation-runtime.ts +171 -0
  71. package/src/ai/dictations.ts +188 -0
  72. package/src/ai/draft-content.ts +49 -0
  73. package/src/ai/enrich.ts +6 -0
  74. package/src/ai/executor.ts +189 -33
  75. package/src/ai/fetch-file-tool.ts +2 -2
  76. package/src/ai/file-content-version.ts +27 -0
  77. package/src/ai/file-context.ts +2 -1
  78. package/src/ai/file-media-type.ts +12 -0
  79. package/src/ai/file-reference-contracts.ts +15 -0
  80. package/src/ai/file-tools.ts +8 -1
  81. package/src/ai/files-store.ts +212 -71
  82. package/src/ai/fixtures/vision-encrypted.pdf +0 -0
  83. package/src/ai/grids-skill.ts +42 -10
  84. package/src/ai/http.ts +26 -8
  85. package/src/ai/index.ts +26 -3
  86. package/src/ai/inference-calls.ts +197 -0
  87. package/src/ai/live-events.ts +5 -1
  88. package/src/ai/live.ts +1 -1
  89. package/src/ai/markdown-pdf-tool.ts +2 -2
  90. package/src/ai/memories.ts +30 -27
  91. package/src/ai/memory-learning-runs.ts +1 -1
  92. package/src/ai/memory-learning.ts +64 -32
  93. package/src/ai/memory-tool.ts +1 -5
  94. package/src/ai/memory-workflow-evidence.ts +9 -4
  95. package/src/ai/message-queue.ts +178 -0
  96. package/src/ai/migrate.ts +189 -11
  97. package/src/ai/model-pricing.ts +43 -0
  98. package/src/ai/pdf-render-worker.ts +75 -0
  99. package/src/ai/pdf-render.fixture.ts +28 -0
  100. package/src/ai/pdf-render.ts +77 -0
  101. package/src/ai/projects-routes.ts +18 -43
  102. package/src/ai/projects.ts +33 -14
  103. package/src/ai/protocol.ts +3 -0
  104. package/src/ai/provider.ts +1 -8
  105. package/src/ai/quota-provider.ts +192 -0
  106. package/src/ai/quota-report.ts +152 -0
  107. package/src/ai/quotas-migrate.ts +45 -0
  108. package/src/ai/quotas.ts +168 -0
  109. package/src/ai/routes.ts +241 -11
  110. package/src/ai/run-timeout.ts +12 -0
  111. package/src/ai/runtime-tools.ts +7 -5
  112. package/src/ai/runtime.ts +74 -17
  113. package/src/ai/settings.ts +36 -8
  114. package/src/ai/short-id.ts +4 -5
  115. package/src/ai/skill-catalog.ts +0 -10
  116. package/src/ai/skill-search.ts +58 -0
  117. package/src/ai/skill-seeds.ts +114 -20
  118. package/src/ai/skill-tool.ts +21 -5
  119. package/src/ai/skills-routes.ts +49 -4
  120. package/src/ai/skills.ts +358 -163
  121. package/src/ai/store.ts +416 -201
  122. package/src/ai/stream.ts +5 -0
  123. package/src/ai/structured-runs.ts +4 -3
  124. package/src/ai/structured.ts +12 -1
  125. package/src/ai/system-prompt.ts +14 -2
  126. package/src/ai/task-contracts.ts +84 -0
  127. package/src/ai/task-execution.ts +92 -0
  128. package/src/ai/timeline.ts +2 -5
  129. package/src/ai/todo-contracts.ts +32 -0
  130. package/src/ai/todo-tool.ts +19 -0
  131. package/src/ai/tool-audit.ts +30 -40
  132. package/src/ai/tools.ts +6 -0
  133. package/src/ai/transcription.ts +202 -0
  134. package/src/ai/turn-timing.ts +114 -0
  135. package/src/ai/types.ts +58 -10
  136. package/src/ai/ui.tsx +3 -0
  137. package/src/ai/usage.ts +118 -118
  138. package/src/ai/vision-tool.ts +60 -10
  139. package/src/api/admin-ai-projects.ts +2 -2
  140. package/src/api/admin-ai-quotas.ts +80 -0
  141. package/src/api/admin-ai-skills.ts +48 -5
  142. package/src/api/admin-ai-usage.ts +2 -2
  143. package/src/api/admin-core-settings.ts +25 -0
  144. package/src/api/admin-identity.ts +91 -51
  145. package/src/api/admin-linux-identities.ts +10 -4
  146. package/src/api/admin-rail.ts +58 -0
  147. package/src/api/announcements.ts +14 -0
  148. package/src/api/app-approval.ts +2 -2
  149. package/src/api/auth.ts +1 -1
  150. package/src/api/capabilities.ts +324 -43
  151. package/src/api/capability-streams.ts +262 -0
  152. package/src/api/help.ts +8 -44
  153. package/src/api/index.ts +8 -2
  154. package/src/api/mcp.ts +45 -34
  155. package/src/api/me-web-vitals.ts +35 -0
  156. package/src/api/me.ts +2 -0
  157. package/src/api/search/schemas.ts +26 -15
  158. package/src/api/search.bench.ts +1 -1
  159. package/src/api/search.ts +54 -17
  160. package/src/browser/CloudResourceSearch.tsx +640 -702
  161. package/src/browser/app-approval-vault.ts +19 -7
  162. package/src/browser/command-bridge.ts +109 -0
  163. package/src/browser/command-shortcuts.ts +76 -0
  164. package/src/browser/commands.ts +67 -0
  165. package/src/browser/navigation-search.ts +48 -0
  166. package/src/browser/notifications.ts +1 -1
  167. package/src/browser/resource-picker.tsx +5 -18
  168. package/src/browser/resource-search-dialog.ts +9 -0
  169. package/src/browser/resource-search-input.ts +46 -0
  170. package/src/browser/resource-search-messages.ts +76 -40
  171. package/src/browser/resource-search.ts +7 -0
  172. package/src/browser/search-bridge.ts +102 -0
  173. package/src/browser/search-commands.ts +59 -0
  174. package/src/browser/search.ts +13 -0
  175. package/src/browser/spotlight-position.ts +208 -0
  176. package/src/browser/testing.ts +6 -0
  177. package/src/browser/web-vitals.ts +25 -0
  178. package/src/capabilities/claims.ts +179 -0
  179. package/src/capabilities/client.ts +34 -0
  180. package/src/capabilities/command-link.ts +16 -0
  181. package/src/capabilities/executions.ts +298 -0
  182. package/src/capabilities/migrate.ts +81 -0
  183. package/src/capabilities/server.ts +116 -35
  184. package/src/capabilities/stream-body.ts +16 -0
  185. package/src/capabilities/streams.ts +28 -0
  186. package/src/cli/admin/ai-quotas.ts +161 -0
  187. package/src/cli/admin/ai-skills.ts +64 -0
  188. package/src/cli/admin/ai-usage.ts +53 -30
  189. package/src/cli/admin/app-credentials.ts +77 -0
  190. package/src/cli/admin/data.ts +17 -0
  191. package/src/cli/admin/index.ts +10 -1
  192. package/src/cli/admin/instance.ts +2 -2
  193. package/src/cli/admin/linux.ts +4 -2
  194. package/src/cli/admin/sync.ts +1 -1
  195. package/src/cli/capabilities.ts +42 -0
  196. package/src/cli/capability-streams.ts +25 -0
  197. package/src/cli/index.ts +3 -2
  198. package/src/config/define-env.ts +97 -0
  199. package/src/config/env.ts +147 -51
  200. package/src/config/index.ts +3 -1
  201. package/src/contracts/app.ts +23 -3
  202. package/src/contracts/capabilities.ts +75 -5
  203. package/src/contracts/capability-streams.ts +46 -0
  204. package/src/contracts/commands.ts +49 -0
  205. package/src/contracts/index.ts +5 -2
  206. package/src/contracts/rail-admin.ts +38 -0
  207. package/src/contracts/rail-preferences.ts +16 -0
  208. package/src/contracts/registry.ts +5 -27
  209. package/src/contracts/settings-types.ts +1 -1
  210. package/src/contracts/shared.ts +18 -5
  211. package/src/contracts/web-vitals.ts +27 -0
  212. package/src/desktop/index.ts +1 -0
  213. package/src/desktop/solid.tsx +1 -1
  214. package/src/index.ts +8 -6
  215. package/src/server/index.ts +3 -0
  216. package/src/server/middleware/auth.ts +3 -2
  217. package/src/server/middleware/route-template.ts +9 -3
  218. package/src/server/middleware/runtime.ts +2 -1
  219. package/src/server/middleware/settings.ts +17 -11
  220. package/src/server/ratelimit.ts +1 -1
  221. package/src/server/services/access-revision.ts +10 -0
  222. package/src/server/services/access.ts +3 -1
  223. package/src/server/services/index.ts +1 -0
  224. package/src/services/account-category-policy.ts +9 -0
  225. package/src/services/account-lifecycle/audit.ts +1 -1
  226. package/src/services/accounts/app.ts +12 -2
  227. package/src/services/accounts/email-write.ts +23 -0
  228. package/src/services/accounts/groups.ts +15 -4
  229. package/src/services/accounts/identities.ts +249 -0
  230. package/src/services/accounts/identity-reconciliation.ts +87 -0
  231. package/src/services/accounts/ipa-data.ts +2 -2
  232. package/src/services/accounts/local-groups.ts +2 -2
  233. package/src/services/accounts/notification-sender.ts +12 -2
  234. package/src/services/accounts/posix.ts +45 -29
  235. package/src/services/accounts/users.ts +2 -2
  236. package/src/services/announcements/index.ts +69 -8
  237. package/src/services/app-approval.ts +11 -10
  238. package/src/services/audit/index.ts +1 -1
  239. package/src/services/cache-fill.ts +35 -0
  240. package/src/services/help/index.ts +109 -0
  241. package/src/services/help/maintenance.ts +25 -0
  242. package/src/services/help/store.ts +180 -0
  243. package/src/services/help/types.ts +46 -0
  244. package/src/services/identity/invocation-actor.ts +2 -2
  245. package/src/services/identity/key-config.ts +8 -5
  246. package/src/services/identity/key-ring.ts +1 -1
  247. package/src/services/identity/runtime-config.ts +6 -5
  248. package/src/services/index.ts +15 -0
  249. package/src/services/ipa/posix.ts +1 -1
  250. package/src/services/ipa/sync.ts +1 -1
  251. package/src/services/ipa/users.ts +268 -221
  252. package/src/services/legal-consent.ts +1 -1
  253. package/src/services/logging/index.ts +15 -13
  254. package/src/services/logging/trace.ts +2 -1
  255. package/src/services/logging/web-vitals.ts +81 -0
  256. package/src/services/mandates/index.ts +21 -4
  257. package/src/services/mandates/policy.ts +165 -2
  258. package/src/services/notifications/batches.ts +125 -57
  259. package/src/services/notifications/browser.ts +12 -0
  260. package/src/services/notifications/index.ts +0 -2
  261. package/src/services/notifications/platform.ts +0 -4
  262. package/src/services/oauth-tokens.ts +3 -3
  263. package/src/services/pdf/gotenberg.ts +131 -72
  264. package/src/services/pdf/index.ts +11 -8
  265. package/src/services/providers/local/users.ts +25 -16
  266. package/src/services/public-http.ts +101 -0
  267. package/src/services/rail-shortcuts.ts +95 -0
  268. package/src/services/rail-snapshot.ts +53 -0
  269. package/src/services/request-cache-redis.ts +29 -0
  270. package/src/services/session/index.ts +15 -2
  271. package/src/services/session/user.ts +62 -37
  272. package/src/services/settings/app.ts +5 -1
  273. package/src/services/settings/core-settings.ts +26 -35
  274. package/src/services/settings/defaults.ts +5 -0
  275. package/src/services/settings/index.ts +3 -3
  276. package/src/services/settings/store.ts +58 -84
  277. package/src/services/webauthn.ts +1 -1
  278. package/src/shared/ai-costs.ts +42 -0
  279. package/src/shared/ai-platform-prompt.ts +1 -1
  280. package/src/shared/ai-quotas.ts +136 -0
  281. package/src/shared/ai-usage.ts +6 -16
  282. package/src/shared/app-presentation.ts +21 -15
  283. package/src/shared/capability-messages.ts +14 -0
  284. package/src/shared/help.ts +6 -42
  285. package/src/shared/index.ts +3 -0
  286. package/src/shared/markdown/extensions/info-blocks.ts +1 -1
  287. package/src/ssr/AdminLayout.tsx +7 -5
  288. package/src/ssr/AdminSidebar.tsx +30 -16
  289. package/src/ssr/AppLaunchpad.island.tsx +33 -136
  290. package/src/ssr/AppLaunchpadPanel.tsx +181 -0
  291. package/src/ssr/BrowserPushRegistration.island.tsx +12 -0
  292. package/src/ssr/GlobalSearchDialog.tsx +167 -20
  293. package/src/ssr/GlobalSearchTrigger.island.tsx +33 -22
  294. package/src/ssr/Layout.tsx +17 -6
  295. package/src/ssr/LayoutBreadcrumbs.island.tsx +3 -5
  296. package/src/ssr/LayoutFooter.tsx +1 -1
  297. package/src/ssr/LayoutHeader.tsx +46 -14
  298. package/src/ssr/LayoutHelp.tsx +22 -23
  299. package/src/ssr/{HotkeysHelpRail.island.tsx → LayoutHelpTrigger.island.tsx} +18 -12
  300. package/src/ssr/LayoutRail.tsx +17 -9
  301. package/src/ssr/MinimalLayout.tsx +1 -1
  302. package/src/ssr/MobileNavigation.tsx +71 -0
  303. package/src/ssr/MobileProfileActions.tsx +49 -0
  304. package/src/ssr/ProfilePreferences.island.tsx +3 -36
  305. package/src/ssr/RailApps.island.tsx +2 -2
  306. package/src/ssr/RailEditor.tsx +17 -2
  307. package/src/ssr/WorkspaceNavigation.island.tsx +7 -0
  308. package/src/ssr/WorkspaceNavigationProvider.tsx +16 -0
  309. package/src/ssr/admin-navigation.ts +11 -1
  310. package/src/ssr/help.ts +29 -0
  311. package/src/ssr/islands/SearchBar.tsx +67 -0
  312. package/src/ssr/islands/index.ts +3 -1
  313. package/src/ssr/layout-context.ts +2 -2
  314. package/src/ssr/layout-help-search.ts +15 -0
  315. package/src/ssr/mobile-menu-history.ts +70 -0
  316. package/src/ssr/platform-messages.ts +16 -8
  317. package/src/ssr/profile-actions.ts +42 -0
  318. package/src/ssr/rail-context.ts +8 -4
  319. package/src/ssr/rail-messages.ts +2 -0
  320. package/src/ssr/rail-navigation.ts +3 -3
  321. package/src/ssr/workspace-navigation.ts +75 -0
  322. package/src/styles/effects.css +6 -6
  323. package/src/styles/global.css +54 -0
  324. package/src/styles/input.css +43 -41
  325. package/src/styles/resource-search.css +505 -0
  326. package/src/styles/utilities-feedback.css +13 -0
  327. package/src/styles/utilities-layout.css +1 -5
  328. package/src/styles/utilities-navigation.css +3 -39
  329. package/src/types/ambient.d.ts +5 -0
  330. package/src/workflows/ai/index.ts +2 -2
  331. package/src/workflows/ai/runtime.ts +19 -90
  332. package/src/workflows/ai/store.ts +2 -0
  333. package/src/workflows/ai/types.ts +6 -82
  334. package/src/workflows/ai-actions.ts +16 -13
  335. package/src/workflows/definition.ts +10 -5
  336. package/src/workflows/store/actions.ts +44 -20
  337. package/src/workflows/store/index.ts +4 -2
  338. package/src/workflows/store/runs.ts +36 -0
  339. package/src/workflows/store/worker-pool.ts +55 -0
  340. package/src/workflows/store/worker-runtime.ts +90 -0
  341. package/src/ai/kit-skill.ts +0 -22
  342. package/src/ai/usage-migrate.ts +0 -81
  343. package/src/browser/notification-target.ts +0 -27
  344. package/src/contracts/notification-live.ts +0 -66
  345. package/src/server/services/freeipa/test-certificates.ts +0 -44
  346. package/src/services/notifications/live.ts +0 -50
  347. package/src/services/session/test-fixture.ts +0 -18
  348. package/src/ssr/BrowserNotifications.island.tsx +0 -135
  349. package/src/ssr/islands/SearchBar.island.tsx +0 -80
@@ -1,13 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import type { Tool, ToolContext, ToolResolver } from "@k2b/nessi";
3
3
  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";
4
+ import { HELP_READ_MAX_CHARS, HELP_SEARCH_MAX_LIMIT, readHelpArticle } from "../_internal/help-catalog";
11
5
  import {
12
6
  type CapabilityActionManifest,
13
7
  type CapabilityActionReview,
@@ -16,8 +10,12 @@ import {
16
10
  cloudResourceRefAppId,
17
11
  resolveCapabilityResourceReader,
18
12
  } from "../contracts/capabilities";
19
- import type { CapabilityRegistryEntry, HelpRegistryEntry } from "../contracts/registry";
13
+ import type { CapabilityRegistryEntry } from "../contracts/registry";
20
14
  import type { RequestActor } from "../server";
15
+ import { createHelpReader, type HelpReaderFactory } from "../services/help";
16
+ import { recordRejectedAiCapability } from "./capability-execution";
17
+ import { CODE_SOURCE_TOOLS } from "./code-source-contracts";
18
+ import { createCodeSourceTool } from "./code-source-tools";
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 the live Help snapshot without loading one tool per article. */
308
- export const createAiHelpTools = (registry: readonly HelpRegistryEntry[], locale = "en"): AiRuntimeTool[] => {
309
- const documents = createHelpCatalog(registry, locale);
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 1-3 concise English product terms and scope appId when known. Returns compact document ids for read_help; skip this tool for straightforward live-data requests.",
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: searchHelpCatalog(documents, { query, appId, limit: boundedLimit(limit, DEFAULT_SEARCH_LIMIT, HELP_SEARCH_MAX_LIMIT) }),
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: readHelpCatalog(documents, { appId, documentId, query }),
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
- if (entry.kind === "action") {
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))) throw new Error("Capability Action was rejected by the user.");
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
- listHelpRegistry?: () => Promise<HelpRegistryEntry[]>;
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, helpRegistry] = await Promise.all([
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.listHelpRegistry ? createAiHelpTools(helpRegistry, input.locale) : [];
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
- if (!review && action?.idempotency === "required" && input.context.callId) {
102
- const key = idempotencyKey(input.conversationId, input.context.callId);
103
- if (input.turnId) {
104
- await aiToolAudit.noteCapabilityDispatch({
105
- conversationId: input.conversationId,
106
- turnId: input.turnId,
107
- callId: input.context.callId,
108
- toolName: input.entry.name,
109
- idempotencyKey: key,
110
- });
111
- }
112
- headers.set("idempotency-key", key);
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
+ };