@k2b/cloud 0.44.0 → 0.45.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 (73) hide show
  1. package/package.json +6 -2
  2. package/src/access/GroupCoverage.tsx +20 -5
  3. package/src/access/PermissionEditor.tsx +11 -2
  4. package/src/access/PrincipalAvatar.tsx +30 -21
  5. package/src/access/messages.ts +4 -4
  6. package/src/account/EntitySearch.tsx +12 -0
  7. package/src/ai/dictation-runtime.ts +6 -2
  8. package/src/ai/executor.ts +9 -3
  9. package/src/ai/index.ts +1 -0
  10. package/src/ai/project-tool.ts +4 -4
  11. package/src/ai/projects-authorization.ts +53 -0
  12. package/src/ai/projects-routes.ts +56 -46
  13. package/src/ai/projects.ts +100 -79
  14. package/src/ai/recent-resources.ts +3 -3
  15. package/src/ai/routes.ts +30 -30
  16. package/src/ai/skill-tool.ts +4 -4
  17. package/src/ai/skills-routes.ts +27 -26
  18. package/src/ai/skills.ts +120 -81
  19. package/src/api/accounts-entities.ts +6 -3
  20. package/src/api/admin-ai-skills.ts +4 -1
  21. package/src/api/auth.ts +9 -2
  22. package/src/browser/session.ts +39 -0
  23. package/src/browser/shared-text-history-validation.ts +151 -0
  24. package/src/browser/shared-text-history.ts +464 -0
  25. package/src/browser/shared-text-offline-authorized.ts +29 -0
  26. package/src/browser/shared-text-offline.ts +351 -0
  27. package/src/browser/shared-text-provider.ts +714 -0
  28. package/src/browser/shared-text-table-contract.ts +150 -0
  29. package/src/browser/shared-text.ts +19 -0
  30. package/src/contracts/shared.ts +2 -1
  31. package/src/server/api/respond.ts +10 -2
  32. package/src/server/index.ts +1 -0
  33. package/src/server/services/access.ts +23 -10
  34. package/src/server/services/index.ts +1 -0
  35. package/src/services/accounts/app.ts +30 -13
  36. package/src/services/accounts/entities.ts +29 -16
  37. package/src/services/accounts/group-sql.ts +23 -0
  38. package/src/services/accounts/groups.ts +16 -13
  39. package/src/services/accounts/identities.ts +8 -1
  40. package/src/services/accounts/identity-reconciliation.ts +3 -1
  41. package/src/services/accounts/local-groups.ts +69 -4
  42. package/src/services/accounts/users.ts +38 -8
  43. package/src/services/mandates/index.ts +2 -2
  44. package/src/services/mandates/policy.ts +1 -0
  45. package/src/services/session/index.ts +12 -3
  46. package/src/shared/email-html.ts +1 -0
  47. package/src/shared/markdown/extensions/katex.ts +8 -1
  48. package/src/shared/markdown/extensions/tables.ts +14 -1
  49. package/src/shared/markdown/index.ts +22 -11
  50. package/src/shared-text/anchors.ts +29 -0
  51. package/src/shared-text/awareness.ts +87 -0
  52. package/src/shared-text/changes.ts +27 -0
  53. package/src/shared-text/documents.ts +428 -77
  54. package/src/shared-text/events.ts +12 -2
  55. package/src/shared-text/index.ts +16 -1
  56. package/src/shared-text/log.ts +24 -5
  57. package/src/shared-text/snapshots.ts +69 -23
  58. package/src/shared-text/structural-history.ts +377 -0
  59. package/src/shared-text/table-actions.ts +432 -0
  60. package/src/shared-text/types.ts +24 -5
  61. package/src/ssr/AppLaunchpadPanel.tsx +1 -1
  62. package/src/ssr/CloudShell.island.tsx +19 -3
  63. package/src/ssr/Layout.tsx +1 -1
  64. package/src/ssr/MobileProfileActions.tsx +2 -2
  65. package/src/ssr/ProfilePreferences.tsx +2 -1
  66. package/src/ssr/__snapshots__/Layout.unchanged.render.test.tsx.snap +3 -3
  67. package/src/ssr/admin-navigation.ts +1 -11
  68. package/src/ssr/app-launchpad.tsx +6 -1
  69. package/src/ssr/platform-messages.ts +2 -0
  70. package/src/ssr/profile-actions.ts +3 -6
  71. package/src/ssr/shell-data.tsx +1 -1
  72. package/src/workflows/language/expressions.ts +3 -2
  73. package/src/workflows/runtime/executor.ts +2 -1
@@ -1,4 +1,5 @@
1
- import { Hono, type MiddlewareHandler } from "hono";
1
+ import { type Context, Hono, type MiddlewareHandler } from "hono";
2
+ import { HTTPException } from "hono/http-exception";
2
3
  import { z } from "zod";
3
4
  import { getCapability } from "../_internal/registry";
4
5
  import { CloudResourceRefSchema, cloudResourceRefAppId, resolveCapabilityResourceReader } from "../contracts/capabilities";
@@ -22,6 +23,7 @@ import {
22
23
  type AiProjectReference,
23
24
  aiProjects,
24
25
  } from "./projects";
26
+ import { AiResourceCredentialError } from "./projects-authorization";
25
27
 
26
28
  const ProjectFieldsSchema = z.object({
27
29
  name: z.string().trim().min(1).max(AI_PROJECT_NAME_MAX_CHARS),
@@ -31,7 +33,13 @@ const ProjectFieldsSchema = z.object({
31
33
  defaultModel: AiModelRequestSchema.nullable().optional(),
32
34
  });
33
35
 
34
- const UpdateProjectSchema = ProjectFieldsSchema.partial().refine((input) => Object.keys(input).length > 0, "No changes supplied.");
36
+ const UpdateProjectSchema = ProjectFieldsSchema.extend({
37
+ description: ProjectFieldsSchema.shape.description.removeDefault(),
38
+ icon: ProjectFieldsSchema.shape.icon.removeDefault(),
39
+ instructions: ProjectFieldsSchema.shape.instructions.removeDefault(),
40
+ })
41
+ .partial()
42
+ .refine((input) => Object.keys(input).length > 0, "No changes supplied.");
35
43
  const ProjectAccessSchema = z.object({ principal: AuthenticatedPrincipalSchema, permission: z.enum(["read", "write", "admin"]) });
36
44
  const ProjectAccessUpdateSchema = z.object({ permission: z.enum(["read", "write", "admin"]) });
37
45
  const KnowledgeSchema = z.object({
@@ -65,6 +73,11 @@ const publicReference = (reference: AiProjectReference, projectId: string): AiPr
65
73
  projectId,
66
74
  });
67
75
 
76
+ const resourceSubject = (c: Context<AuthContext>) => ({
77
+ actor: c.get("actor"),
78
+ accessSubject: c.get("accessSubject"),
79
+ });
80
+
68
81
  type AiProjectsRouteDependencies = {
69
82
  limit?: MiddlewareHandler<AuthContext>;
70
83
  authenticate?: MiddlewareHandler<AuthContext>;
@@ -73,56 +86,54 @@ type AiProjectsRouteDependencies = {
73
86
 
74
87
  const buildAiProjectsRoutes = (dependencies: AiProjectsRouteDependencies = {}) =>
75
88
  new Hono<AuthContext>()
89
+ .onError((error, c) => {
90
+ if (error instanceof AiResourceCredentialError) return respond(c, fail(err.forbidden(error.message)));
91
+ if (error instanceof HTTPException) return error.getResponse();
92
+ throw error;
93
+ })
76
94
  // The list combines projects from separate grants; the writer of one can rename it next to another project's instructions.
77
95
  .use("*", noTransform())
78
96
  .use(dependencies.limit ?? rateLimit())
79
97
  .use("*", dependencies.authenticate ?? auth.requireRole("authenticated"))
80
- .get("/", async (c) => respond(c, ok({ projects: (await aiProjects.list(c.get("accessSubject") ?? null)).map(publicProject) })))
98
+ .get("/", async (c) => respond(c, ok({ projects: (await aiProjects.list(resourceSubject(c))).map(publicProject) })))
81
99
  .post("/", dependencies.authenticate ?? auth.requireRole("authenticated"), v("json", ProjectFieldsSchema), async (c) =>
82
100
  respond(
83
101
  c,
84
102
  ok({
85
- project: publicProject(await aiProjects.create({ subject: c.get("accessSubject"), ...c.req.valid("json") })),
103
+ project: publicProject(await aiProjects.create({ subject: resourceSubject(c), ...c.req.valid("json") })),
86
104
  }),
87
105
  201,
88
106
  ),
89
107
  )
90
108
  .get("/:projectId", async (c) => {
91
- const project = await aiProjects.getByShortId(c.req.param("projectId")!, c.get("accessSubject") ?? null);
109
+ const project = await aiProjects.getByShortId(c.req.param("projectId")!, resourceSubject(c));
92
110
  return project ? respond(c, ok({ project: publicProject(project) })) : notFound(c);
93
111
  })
94
112
  .patch("/:projectId", v("json", UpdateProjectSchema), async (c) => {
95
- const project = await aiProjects.getByShortId(c.req.param("projectId")!, c.get("accessSubject") ?? null, "write");
113
+ const project = await aiProjects.getByShortId(c.req.param("projectId")!, resourceSubject(c), "write");
96
114
  if (!project) return notFound(c);
97
- const updated = await aiProjects.update(project.id, c.get("accessSubject") ?? null, c.req.valid("json"));
115
+ const updated = await aiProjects.update(project.id, resourceSubject(c), c.req.valid("json"));
98
116
  return updated ? respond(c, ok({ project: publicProject(updated) })) : notFound(c);
99
117
  })
100
118
  .delete("/:projectId", async (c) => {
101
- const project = await aiProjects.getByShortId(c.req.param("projectId")!, c.get("accessSubject") ?? null, "admin");
102
- return project && (await aiProjects.delete(project.id, c.get("accessSubject") ?? null))
103
- ? respond(c, ok({ deleted: true }))
104
- : notFound(c);
119
+ const project = await aiProjects.getByShortId(c.req.param("projectId")!, resourceSubject(c), "admin");
120
+ return project && (await aiProjects.delete(project.id, resourceSubject(c))) ? respond(c, ok({ deleted: true })) : notFound(c);
105
121
  })
106
122
  .get("/:projectId/access", async (c) => {
107
- const project = await aiProjects.getByShortId(c.req.param("projectId")!, c.get("accessSubject") ?? null, "admin");
108
- const access = project ? await aiProjects.listAccess(project.id, c.get("accessSubject") ?? null) : null;
123
+ const project = await aiProjects.getByShortId(c.req.param("projectId")!, resourceSubject(c), "admin");
124
+ const access = project ? await aiProjects.listAccess(project.id, resourceSubject(c)) : null;
109
125
  return access ? respond(c, ok({ access })) : notFound(c);
110
126
  })
111
127
  .post("/:projectId/access", v("json", ProjectAccessSchema), async (c) => {
112
- const project = await aiProjects.getByShortId(c.req.param("projectId")!, c.get("accessSubject") ?? null, "admin");
113
- const access = project ? await aiProjects.grantAccess(project.id, c.get("accessSubject") ?? null, c.req.valid("json")) : null;
128
+ const project = await aiProjects.getByShortId(c.req.param("projectId")!, resourceSubject(c), "admin");
129
+ const access = project ? await aiProjects.grantAccess(project.id, resourceSubject(c), c.req.valid("json")) : null;
114
130
  return access ? respond(c, ok({ access }), 201) : notFound(c);
115
131
  })
116
132
  .patch("/:projectId/access/:accessId", v("json", ProjectAccessUpdateSchema), async (c) => {
117
- const project = await aiProjects.getByShortId(c.req.param("projectId")!, c.get("accessSubject") ?? null, "admin");
133
+ const project = await aiProjects.getByShortId(c.req.param("projectId")!, resourceSubject(c), "admin");
118
134
  try {
119
135
  return project &&
120
- (await aiProjects.updateAccess(
121
- project.id,
122
- c.req.param("accessId")!,
123
- c.get("accessSubject") ?? null,
124
- c.req.valid("json").permission,
125
- ))
136
+ (await aiProjects.updateAccess(project.id, c.req.param("accessId")!, resourceSubject(c), c.req.valid("json").permission))
126
137
  ? respond(c, ok({ updated: true }))
127
138
  : notFound(c, "Access entry");
128
139
  } catch (error) {
@@ -131,9 +142,9 @@ const buildAiProjectsRoutes = (dependencies: AiProjectsRouteDependencies = {}) =
131
142
  }
132
143
  })
133
144
  .delete("/:projectId/access/:accessId", async (c) => {
134
- const project = await aiProjects.getByShortId(c.req.param("projectId")!, c.get("accessSubject") ?? null, "admin");
145
+ const project = await aiProjects.getByShortId(c.req.param("projectId")!, resourceSubject(c), "admin");
135
146
  try {
136
- return project && (await aiProjects.revokeAccess(project.id, c.req.param("accessId")!, c.get("accessSubject") ?? null))
147
+ return project && (await aiProjects.revokeAccess(project.id, c.req.param("accessId")!, resourceSubject(c)))
137
148
  ? respond(c, ok({ deleted: true }))
138
149
  : notFound(c, "Access entry");
139
150
  } catch (error) {
@@ -142,51 +153,50 @@ const buildAiProjectsRoutes = (dependencies: AiProjectsRouteDependencies = {}) =
142
153
  }
143
154
  })
144
155
  .get("/:projectId/knowledge", v("query", KnowledgeQuerySchema), async (c) => {
145
- const project = await aiProjects.getByShortId(c.req.param("projectId")!, c.get("accessSubject") ?? null);
156
+ const project = await aiProjects.getByShortId(c.req.param("projectId")!, resourceSubject(c));
146
157
  if (!project) return notFound(c);
147
158
  return respond(
148
159
  c,
149
160
  ok({
150
- knowledge: (await aiProjects.listKnowledge(project.id, c.get("accessSubject") ?? null, c.req.valid("query").q)).map((item) =>
161
+ knowledge: (await aiProjects.listKnowledge(project.id, resourceSubject(c), c.req.valid("query").q)).map((item) =>
151
162
  publicKnowledge(item, project.shortId),
152
163
  ),
153
164
  }),
154
165
  );
155
166
  })
156
167
  .post("/:projectId/knowledge", v("json", KnowledgeSchema), async (c) => {
157
- const project = await aiProjects.getByShortId(c.req.param("projectId")!, c.get("accessSubject") ?? null, "write");
158
- const knowledge = project ? await aiProjects.createKnowledge(project.id, c.get("accessSubject") ?? null, c.req.valid("json")) : null;
168
+ const project = await aiProjects.getByShortId(c.req.param("projectId")!, resourceSubject(c), "write");
169
+ const knowledge = project ? await aiProjects.createKnowledge(project.id, resourceSubject(c), c.req.valid("json")) : null;
159
170
  return knowledge ? respond(c, ok({ knowledge: publicKnowledge(knowledge, project!.shortId) }), 201) : notFound(c);
160
171
  })
161
172
  .patch("/:projectId/knowledge/:knowledgeId", v("json", UpdateKnowledgeSchema), async (c) => {
162
- const project = await aiProjects.getByShortId(c.req.param("projectId")!, c.get("accessSubject") ?? null, "write");
173
+ const project = await aiProjects.getByShortId(c.req.param("projectId")!, resourceSubject(c), "write");
163
174
  const knowledge =
164
- project &&
165
- (await aiProjects.updateKnowledge(project.id, c.req.param("knowledgeId")!, c.get("accessSubject") ?? null, c.req.valid("json")));
175
+ project && (await aiProjects.updateKnowledge(project.id, c.req.param("knowledgeId")!, resourceSubject(c), c.req.valid("json")));
166
176
  return knowledge ? respond(c, ok({ knowledge: publicKnowledge(knowledge, project!.shortId) })) : notFound(c, "Knowledge entry");
167
177
  })
168
178
  .delete("/:projectId/knowledge/:knowledgeId", async (c) => {
169
- const project = await aiProjects.getByShortId(c.req.param("projectId")!, c.get("accessSubject") ?? null, "write");
170
- return project && (await aiProjects.deleteKnowledge(project.id, c.req.param("knowledgeId")!, c.get("accessSubject") ?? null))
179
+ const project = await aiProjects.getByShortId(c.req.param("projectId")!, resourceSubject(c), "write");
180
+ return project && (await aiProjects.deleteKnowledge(project.id, c.req.param("knowledgeId")!, resourceSubject(c)))
171
181
  ? respond(c, ok({ deleted: true }))
172
182
  : notFound(c, "Knowledge entry");
173
183
  })
174
184
  .get("/:projectId/files", async (c) => {
175
- const project = await aiProjects.getByShortId(c.req.param("projectId")!, c.get("accessSubject") ?? null);
185
+ const project = await aiProjects.getByShortId(c.req.param("projectId")!, resourceSubject(c));
176
186
  if (!project) return notFound(c);
177
187
  return respond(
178
188
  c,
179
189
  ok({
180
- files: (await aiProjects.listFiles(project.id, c.get("accessSubject") ?? null)).map((file) => publicFile(file, project.shortId)),
190
+ files: (await aiProjects.listFiles(project.id, resourceSubject(c))).map((file) => publicFile(file, project.shortId)),
181
191
  }),
182
192
  );
183
193
  })
184
194
  .post("/:projectId/files", v("json", ProjectFileSchema), async (c) => {
185
195
  const body = c.req.valid("json");
186
196
  try {
187
- const project = await aiProjects.getByShortId(c.req.param("projectId")!, c.get("accessSubject") ?? null, "write");
197
+ const project = await aiProjects.getByShortId(c.req.param("projectId")!, resourceSubject(c), "write");
188
198
  const file = project
189
- ? await aiProjects.writeFile(project.id, c.get("accessSubject") ?? null, {
199
+ ? await aiProjects.writeFile(project.id, resourceSubject(c), {
190
200
  path: body.path,
191
201
  mediaType: body.mediaType,
192
202
  bytes: decodeAiFileContent(body.content, body.encoding),
@@ -200,8 +210,8 @@ const buildAiProjectsRoutes = (dependencies: AiProjectsRouteDependencies = {}) =
200
210
  }
201
211
  })
202
212
  .get("/:projectId/files/:fileId", async (c) => {
203
- const project = await aiProjects.getByShortId(c.req.param("projectId")!, c.get("accessSubject") ?? null);
204
- const file = project ? await aiProjects.readFile(project.id, c.req.param("fileId")!, c.get("accessSubject") ?? null) : null;
213
+ const project = await aiProjects.getByShortId(c.req.param("projectId")!, resourceSubject(c));
214
+ const file = project ? await aiProjects.readFile(project.id, c.req.param("fileId")!, resourceSubject(c)) : null;
205
215
  if (!file) return notFound(c, "Project file");
206
216
  return respond(
207
217
  c,
@@ -213,18 +223,18 @@ const buildAiProjectsRoutes = (dependencies: AiProjectsRouteDependencies = {}) =
213
223
  );
214
224
  })
215
225
  .delete("/:projectId/files/:fileId", async (c) => {
216
- const project = await aiProjects.getByShortId(c.req.param("projectId")!, c.get("accessSubject") ?? null, "write");
217
- return project && (await aiProjects.deleteFile(project.id, c.req.param("fileId")!, c.get("accessSubject") ?? null))
226
+ const project = await aiProjects.getByShortId(c.req.param("projectId")!, resourceSubject(c), "write");
227
+ return project && (await aiProjects.deleteFile(project.id, c.req.param("fileId")!, resourceSubject(c)))
218
228
  ? respond(c, ok({ deleted: true }))
219
229
  : notFound(c, "Project file");
220
230
  })
221
231
  .get("/:projectId/references", async (c) => {
222
- const project = await aiProjects.getByShortId(c.req.param("projectId")!, c.get("accessSubject") ?? null);
232
+ const project = await aiProjects.getByShortId(c.req.param("projectId")!, resourceSubject(c));
223
233
  if (!project) return notFound(c);
224
234
  return respond(
225
235
  c,
226
236
  ok({
227
- references: (await aiProjects.listReferences(project.id, c.get("accessSubject") ?? null)).map((reference) =>
237
+ references: (await aiProjects.listReferences(project.id, resourceSubject(c))).map((reference) =>
228
238
  publicReference(reference, project.shortId),
229
239
  ),
230
240
  }),
@@ -232,7 +242,7 @@ const buildAiProjectsRoutes = (dependencies: AiProjectsRouteDependencies = {}) =
232
242
  })
233
243
  .post("/:projectId/references", v("json", ReferenceSchema), async (c) => {
234
244
  const body = c.req.valid("json");
235
- const subject = c.get("accessSubject") ?? null;
245
+ const subject = resourceSubject(c);
236
246
  const project = await aiProjects.getByShortId(c.req.param("projectId")!, subject, "write");
237
247
  if (!project) return notFound(c);
238
248
  let entry: Awaited<ReturnType<typeof getCapability>>;
@@ -248,8 +258,8 @@ const buildAiProjectsRoutes = (dependencies: AiProjectsRouteDependencies = {}) =
248
258
  return reference ? respond(c, ok({ reference: publicReference(reference, project.shortId) }), 201) : notFound(c);
249
259
  })
250
260
  .delete("/:projectId/references/:referenceId", async (c) => {
251
- const project = await aiProjects.getByShortId(c.req.param("projectId")!, c.get("accessSubject") ?? null, "write");
252
- return project && (await aiProjects.deleteReference(project.id, c.req.param("referenceId")!, c.get("accessSubject") ?? null))
261
+ const project = await aiProjects.getByShortId(c.req.param("projectId")!, resourceSubject(c), "write");
262
+ return project && (await aiProjects.deleteReference(project.id, c.req.param("referenceId")!, resourceSubject(c)))
253
263
  ? respond(c, ok({ deleted: true }))
254
264
  : notFound(c, "Project reference");
255
265
  });