@k2b/cloud 0.24.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/package.json +3 -2
  2. package/src/_internal/capabilities.ts +12 -0
  3. package/src/_internal/registry.ts +1 -0
  4. package/src/access/GroupCoverage.tsx +175 -0
  5. package/src/access/PermissionEditor.tsx +134 -93
  6. package/src/access/managers.ts +32 -0
  7. package/src/access/messages.ts +36 -0
  8. package/src/ai/approval-routes.ts +5 -5
  9. package/src/ai/capabilities.ts +34 -12
  10. package/src/ai/chat/blocks.tsx +86 -179
  11. package/src/ai/chat/builtin-tools.tsx +57 -30
  12. package/src/ai/chat/live-turn.browser-harness.tsx +37 -0
  13. package/src/ai/chat/message-actions.tsx +6 -2
  14. package/src/ai/chat/message-utils.ts +17 -14
  15. package/src/ai/chat/messages.ts +324 -2
  16. package/src/ai/chat/presentation.tsx +257 -103
  17. package/src/ai/chat/tool-groups.ts +55 -35
  18. package/src/ai/chat/turn-layout.ts +141 -0
  19. package/src/ai/chat/turn-view.tsx +609 -0
  20. package/src/ai/client/projection.ts +37 -6
  21. package/src/ai/executor.ts +38 -5
  22. package/src/ai/memory-learning.ts +7 -12
  23. package/src/ai/memory-workflow-evidence.ts +10 -2
  24. package/src/ai/migrate.ts +45 -2
  25. package/src/ai/model-access.ts +4 -2
  26. package/src/ai/prefs.ts +19 -5
  27. package/src/ai/projects.ts +5 -2
  28. package/src/ai/protocol.ts +20 -4
  29. package/src/ai/provider-fetch.ts +67 -15
  30. package/src/ai/provider-retry.ts +105 -0
  31. package/src/ai/quota-provider.ts +14 -5
  32. package/src/ai/skills.ts +5 -2
  33. package/src/ai/store.ts +108 -3
  34. package/src/ai/stream.ts +2 -0
  35. package/src/ai/timeline.ts +9 -11
  36. package/src/ai/turn-timing.ts +31 -3
  37. package/src/ai/types.ts +12 -2
  38. package/src/browser/FileChooser.tsx +600 -0
  39. package/src/browser/choose-files.browser-harness.tsx +32 -0
  40. package/src/browser/choose-files.tsx +70 -0
  41. package/src/browser/file-chooser-messages.ts +86 -0
  42. package/src/browser/file-providers.ts +217 -0
  43. package/src/browser/files.tsx +23 -0
  44. package/src/cli/admin/account-administration.ts +2 -2
  45. package/src/contracts/registry.ts +2 -0
  46. package/src/server/index.ts +1 -0
  47. package/src/server/services/access.ts +44 -11
  48. package/src/server/services/index.ts +1 -0
  49. package/src/shared/app-presentation.ts +10 -2
  50. package/src/ssr/platform-messages.ts +2 -2
  51. package/src/styles/effects.css +69 -0
  52. package/src/styles/file-chooser.css +169 -0
  53. package/src/styles/global.css +1 -0
@@ -4,7 +4,7 @@ import { z } from "zod";
4
4
  import { listCapabilities } from "../_internal/registry";
5
5
  import { ErrorResponseSchema } from "../contracts";
6
6
  import type { CapabilityRegistryEntry } from "../contracts/registry";
7
- import { type AuthContext, auth, expectUserBackedActor, jsonResponse, rateLimit, requiresAuth, v } from "../server";
7
+ import { type AuthContext, auth, expectUserBackedActor, getLocale, jsonResponse, rateLimit, requiresAuth, v } from "../server";
8
8
  import { type AiToolApprovalPreference, listAiToolApprovalPreferences, revokeAiToolApprovalPreference } from "./approvals";
9
9
  import { buildAiCapabilityCatalog } from "./capabilities";
10
10
 
@@ -70,7 +70,7 @@ export const createAiApprovalPreferenceRoutes = (dependencies: AiApprovalRouteDe
70
70
  describeRoute({
71
71
  tags: ["AI"],
72
72
  summary: "List remembered AI approvals",
73
- description: "Lists AI tool approvals remembered for the current user.",
73
+ description: "Lists AI tool approvals remembered for the current user, with titles in the request locale.",
74
74
  ...requiresAuth,
75
75
  responses: {
76
76
  200: jsonResponse(AiApprovalPreferenceListSchema, "Remembered approvals"),
@@ -84,16 +84,16 @@ export const createAiApprovalPreferenceRoutes = (dependencies: AiApprovalRouteDe
84
84
  listPreferences(user.id),
85
85
  registry().catch(() => [] as CapabilityRegistryEntry[]),
86
86
  ]);
87
- const catalogByName = new Map(buildAiCapabilityCatalog(capabilities).map((entry) => [entry.name, entry]));
87
+ const catalogByName = new Map(buildAiCapabilityCatalog(capabilities, getLocale(c)).map((entry) => [entry.name, entry]));
88
88
  const approvals: AiApprovalPreferenceView[] = preferences.map((preference) => {
89
89
  const capability = catalogByName.get(preference.toolName);
90
90
  return {
91
91
  ...preference,
92
- title: capability?.title ?? fallbackTitle(preference.toolName),
92
+ title: capability?.display.title ?? fallbackTitle(preference.toolName),
93
93
  app: capability
94
94
  ? {
95
95
  id: capability.appId,
96
- name: capability.appName,
96
+ name: capability.display.appName,
97
97
  icon: capability.app.appIcon,
98
98
  ...(capability.app.appAccent ? { accent: capability.app.appAccent } : {}),
99
99
  }
@@ -1,6 +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 { resolveCapabilityOperationTitle } from "../_internal/capabilities";
4
5
  import { HELP_READ_MAX_CHARS, HELP_SEARCH_MAX_LIMIT, readHelpArticle } from "../_internal/help-catalog";
5
6
  import {
6
7
  type CapabilityActionManifest,
@@ -13,6 +14,7 @@ import {
13
14
  import type { CapabilityRegistryEntry } from "../contracts/registry";
14
15
  import type { RequestActor } from "../server";
15
16
  import { createHelpReader, type HelpReaderFactory } from "../services/help";
17
+ import { resolveAppIdentityPresentation } from "../shared/app-presentation";
16
18
  import { recordRejectedAiCapability } from "./capability-execution";
17
19
  import { CODE_SOURCE_TOOLS } from "./code-source-contracts";
18
20
  import { createCodeSourceTool } from "./code-source-tools";
@@ -44,6 +46,12 @@ export type AiCapabilityAppCatalogItem = {
44
46
  export type AiCapabilityCatalogEntry = AiCapabilityCatalogItem & {
45
47
  app: CapabilityRegistryEntry;
46
48
  operation: CapabilityQueryManifest | CapabilityActionManifest;
49
+ /**
50
+ * What people see in tool rows and approvals, in the locale the catalog was built for. The model keeps
51
+ * the base `title`, `description`, and schemas: they are prompt text, and stable prompts do not change
52
+ * with the reader's language.
53
+ */
54
+ display: { appName: string; title: string };
47
55
  };
48
56
 
49
57
  export type AiToolKind = "builtin" | AiCapabilityKind;
@@ -109,8 +117,11 @@ export const buildAiCapabilityAppCatalog = (apps: readonly CapabilityRegistryEnt
109
117
  });
110
118
  };
111
119
 
112
- /** Build one deterministic, immutable view of the current live registry. */
113
- export const buildAiCapabilityCatalog = (apps: CapabilityRegistryEntry[]): AiCapabilityCatalogEntry[] => {
120
+ /**
121
+ * Build one deterministic, immutable view of the current live registry. `locale` selects the human
122
+ * presentation in `display`; model-facing fields always stay in the app's base presentation.
123
+ */
124
+ export const buildAiCapabilityCatalog = (apps: CapabilityRegistryEntry[], locale?: string): AiCapabilityCatalogEntry[] => {
114
125
  const entries = [...apps]
115
126
  .sort(
116
127
  (left, right) =>
@@ -119,12 +130,18 @@ export const buildAiCapabilityCatalog = (apps: CapabilityRegistryEntry[]): AiCap
119
130
  left.appName.localeCompare(right.appName) ||
120
131
  left.endpoint.localeCompare(right.endpoint),
121
132
  )
122
- .flatMap((app) => [
123
- ...app.manifest.actions.map((operation) => ({ app, operation, kind: "action" as const })),
124
- ...app.manifest.queries.map((operation) => ({ app, operation, kind: "query" as const })),
125
- ])
133
+ .flatMap((app) => {
134
+ const appName = locale
135
+ ? resolveAppIdentityPresentation({ name: app.appName, description: app.appDescription, presentation: app.appPresentation }, locale)
136
+ .name
137
+ : app.appName;
138
+ return [
139
+ ...app.manifest.actions.map((operation) => ({ app, appName, operation, kind: "action" as const })),
140
+ ...app.manifest.queries.map((operation) => ({ app, appName, operation, kind: "query" as const })),
141
+ ];
142
+ })
126
143
  .map(
127
- ({ app, operation, kind }): AiCapabilityCatalogEntry => ({
144
+ ({ app, appName, operation, kind }): AiCapabilityCatalogEntry => ({
128
145
  name: aiCapabilityId(app.appId, operation.localId),
129
146
  providerName: aiCapabilityToolName(app.appId, kind, operation.localId),
130
147
  appId: app.appId,
@@ -135,6 +152,12 @@ export const buildAiCapabilityCatalog = (apps: CapabilityRegistryEntry[]): AiCap
135
152
  description: operation.description,
136
153
  app,
137
154
  operation,
155
+ display: {
156
+ appName,
157
+ title: locale
158
+ ? resolveCapabilityOperationTitle(operation, kind === "action" ? "actions" : "queries", app.presentation, locale)
159
+ : operation.title,
160
+ },
138
161
  }),
139
162
  )
140
163
  .sort((left, right) => left.name.localeCompare(right.name));
@@ -566,8 +589,7 @@ export const createLoadedAiCapabilityTools = (input: {
566
589
  if (!input.authorizeBackground && entry.kind === "action" && (entry.operation as CapabilityActionManifest).approval !== "none") {
567
590
  const review = (await input.review?.(entry, args, context)) ?? null;
568
591
  if (review && context.callId) input.onReview?.(context.callId, review);
569
- const message =
570
- review?.message ?? `${entry.appName}: ${entry.title}\nReview the validated arguments below before running this Action.`;
592
+ const message = review?.message ?? `${entry.display.appName}: ${entry.display.title}`;
571
593
  if (!(await context.requestApproval(message))) {
572
594
  if (input.actor.kind === "user") {
573
595
  await recordRejectedAiCapability({ entry, actor: input.actor, args }).catch(() => undefined);
@@ -667,7 +689,7 @@ export const createAiToolResolver =
667
689
  });
668
690
  }
669
691
  const allowed = input.allowedTools == null ? null : new Set(input.allowedTools);
670
- const capabilityCatalog = buildAiCapabilityCatalog(registry).filter((entry) => !allowed || allowed.has(entry.name));
692
+ const capabilityCatalog = buildAiCapabilityCatalog(registry, input.locale).filter((entry) => !allowed || allowed.has(entry.name));
671
693
  const helpTools = input.help ? createAiHelpTools(input.help, input.locale) : [];
672
694
  const resourceTool =
673
695
  input.execute && (capabilityCatalog.length > 0 || input.staticTools.some((tool) => tool.def.name === "code_read"))
@@ -719,10 +741,10 @@ export const createAiToolResolver =
719
741
  presentations.set(entry.providerName, {
720
742
  kind: "capability",
721
743
  appId: entry.appId,
722
- appName: entry.appName,
744
+ appName: entry.display.appName,
723
745
  appIcon: entry.app.appIcon,
724
746
  appAccent: entry.app.appAccent,
725
- title: entry.title,
747
+ title: entry.display.title,
726
748
  capabilityKind: entry.kind,
727
749
  });
728
750
  // Rememberable scopes are resolved by the owning app for each concrete