@k2b/cloud 0.7.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 (182) hide show
  1. package/package.json +5 -4
  2. package/scripts/README.md +10 -0
  3. package/scripts/build.ts +6 -1
  4. package/scripts/preload.ts +7 -0
  5. package/scripts/sync-recovery-smoke.ts +21 -6
  6. package/src/_internal/build-metadata.ts +8 -1
  7. package/src/_internal/capabilities.ts +35 -12
  8. package/src/_internal/define-app.ts +28 -11
  9. package/src/_internal/page-responses.ts +1 -1
  10. package/src/_internal/readiness.ts +4 -1
  11. package/src/_internal/runtime-context.ts +1 -1
  12. package/src/access/PermissionEditor.tsx +15 -9
  13. package/src/access/PrincipalPicker.tsx +9 -3
  14. package/src/access/ResourceApiKeys.tsx +1 -1
  15. package/src/access/ui.ts +1 -2
  16. package/src/ai/admin.ts +2 -2
  17. package/src/ai/assistant-models.ts +1 -1
  18. package/src/ai/audio-tool.ts +1 -1
  19. package/src/ai/browser-code-contracts.ts +174 -52
  20. package/src/ai/browser.ts +4 -2
  21. package/src/ai/capabilities.ts +3 -3
  22. package/src/ai/chat/blocks.tsx +127 -50
  23. package/src/ai/chat/builtin-tools.tsx +2 -4
  24. package/src/ai/chat/capability-table.tsx +1 -1
  25. package/src/ai/chat/composer-adapter.ts +48 -13
  26. package/src/ai/chat/message-utils.ts +18 -5
  27. package/src/ai/chat/presentation.tsx +24 -11
  28. package/src/ai/chat/tool-disclosure.tsx +6 -2
  29. package/src/ai/chat/tool-groups.ts +8 -2
  30. package/src/ai/chat-quotas.ts +2 -2
  31. package/src/ai/chat-task-contracts.ts +9 -13
  32. package/src/ai/chat-tasks.ts +39 -18
  33. package/src/ai/client/controller.ts +87 -38
  34. package/src/ai/client/projection.ts +13 -6
  35. package/src/ai/client/transport.ts +4 -2
  36. package/src/ai/code-capability-routes.ts +22 -27
  37. package/src/ai/code-capability-transport.ts +2 -1
  38. package/src/ai/code-execution.ts +53 -0
  39. package/src/ai/code-mode-skill.ts +9 -5
  40. package/src/ai/code-runtime-tools.ts +16 -3
  41. package/src/ai/code-source-contracts.ts +100 -32
  42. package/src/ai/code-source-tools.ts +49 -6
  43. package/src/ai/compaction.ts +3 -3
  44. package/src/ai/default-tools.ts +87 -14
  45. package/src/ai/dictation-runtime.ts +1 -1
  46. package/src/ai/draft-content.ts +27 -12
  47. package/src/ai/enrich.ts +1 -1
  48. package/src/ai/executor.ts +8 -12
  49. package/src/ai/fetch-file-tool.ts +2 -2
  50. package/src/ai/file-content-version.ts +6 -1
  51. package/src/ai/file-tools.ts +8 -2
  52. package/src/ai/files-store.ts +55 -17
  53. package/src/ai/grids-skill.ts +2 -0
  54. package/src/ai/http.ts +18 -7
  55. package/src/ai/index.ts +24 -25
  56. package/src/ai/inference-calls.ts +3 -4
  57. package/src/ai/live.ts +1 -1
  58. package/src/ai/markdown-pdf-tool.ts +2 -2
  59. package/src/ai/memories.ts +30 -27
  60. package/src/ai/memory-learning-runs.ts +1 -1
  61. package/src/ai/memory-learning.ts +5 -5
  62. package/src/ai/memory-tool.ts +1 -5
  63. package/src/ai/memory-workflow-evidence.ts +9 -4
  64. package/src/ai/message-queue.ts +23 -12
  65. package/src/ai/migrate.ts +6 -4
  66. package/src/ai/model-pricing.ts +4 -4
  67. package/src/ai/pdf-render-worker.ts +3 -2
  68. package/src/ai/projects-routes.ts +15 -40
  69. package/src/ai/projects.ts +18 -13
  70. package/src/ai/quota-provider.ts +6 -5
  71. package/src/ai/quota-report.ts +5 -5
  72. package/src/ai/quotas.ts +3 -3
  73. package/src/ai/routes.ts +60 -34
  74. package/src/ai/run-timeout.ts +3 -1
  75. package/src/ai/runtime-tools.ts +6 -7
  76. package/src/ai/runtime.ts +38 -20
  77. package/src/ai/settings.ts +1 -1
  78. package/src/ai/short-id.ts +4 -5
  79. package/src/ai/skill-search.ts +21 -10
  80. package/src/ai/skill-seeds.ts +16 -7
  81. package/src/ai/skill-tool.ts +6 -1
  82. package/src/ai/skills-routes.ts +32 -15
  83. package/src/ai/skills.ts +54 -28
  84. package/src/ai/store.ts +80 -47
  85. package/src/ai/structured.ts +6 -2
  86. package/src/ai/system-prompt.ts +7 -5
  87. package/src/ai/task-contracts.ts +84 -0
  88. package/src/ai/task-execution.ts +92 -0
  89. package/src/ai/todo-contracts.ts +24 -12
  90. package/src/ai/todo-tool.ts +16 -13
  91. package/src/ai/tools.ts +1 -1
  92. package/src/ai/transcription.ts +4 -4
  93. package/src/ai/turn-timing.ts +1 -1
  94. package/src/ai/ui.tsx +2 -1
  95. package/src/ai/usage.ts +1 -1
  96. package/src/ai/vision-tool.ts +38 -17
  97. package/src/api/admin-ai-quotas.ts +14 -8
  98. package/src/api/admin-ai-skills.ts +3 -3
  99. package/src/api/admin-linux-identities.ts +10 -4
  100. package/src/api/admin-rail.ts +2 -2
  101. package/src/api/app-approval.ts +2 -2
  102. package/src/api/auth.ts +1 -1
  103. package/src/api/capabilities.ts +45 -13
  104. package/src/api/index.ts +5 -5
  105. package/src/api/mcp.ts +5 -12
  106. package/src/browser/CloudResourceSearch.tsx +1 -1
  107. package/src/browser/command-bridge.ts +1 -1
  108. package/src/browser/command-shortcuts.ts +1 -1
  109. package/src/browser/commands.ts +8 -7
  110. package/src/browser/testing.ts +2 -1
  111. package/src/capabilities/client.ts +1 -1
  112. package/src/capabilities/command-link.ts +1 -1
  113. package/src/capabilities/server.ts +2 -1
  114. package/src/cli/admin/ai-quotas.ts +5 -5
  115. package/src/cli/admin/app-credentials.ts +7 -9
  116. package/src/cli/admin/index.ts +3 -3
  117. package/src/cli/admin/instance.ts +2 -2
  118. package/src/cli/admin/linux.ts +4 -2
  119. package/src/cli/admin/sync.ts +1 -1
  120. package/src/cli/capabilities.ts +42 -29
  121. package/src/config/define-env.ts +97 -0
  122. package/src/config/env.ts +147 -54
  123. package/src/config/index.ts +3 -1
  124. package/src/contracts/capabilities.ts +8 -1
  125. package/src/contracts/index.ts +5 -7
  126. package/src/contracts/registry.ts +2 -0
  127. package/src/contracts/shared.ts +12 -3
  128. package/src/desktop/index.ts +1 -0
  129. package/src/desktop/solid.tsx +1 -1
  130. package/src/index.ts +8 -2
  131. package/src/server/index.ts +2 -2
  132. package/src/server/ratelimit.ts +1 -1
  133. package/src/server/services/access-revision.ts +3 -3
  134. package/src/server/services/access.ts +3 -1
  135. package/src/services/accounts/notification-sender.ts +12 -2
  136. package/src/services/announcements/index.ts +4 -4
  137. package/src/services/app-approval.ts +11 -10
  138. package/src/services/identity/key-config.ts +6 -4
  139. package/src/services/identity/runtime-config.ts +6 -5
  140. package/src/services/ipa/posix.ts +1 -1
  141. package/src/services/ipa/sync.ts +1 -1
  142. package/src/services/logging/index.ts +1 -1
  143. package/src/services/logging/trace.ts +1 -1
  144. package/src/services/mandates/index.ts +5 -2
  145. package/src/services/mandates/policy.ts +106 -6
  146. package/src/services/notifications/batches.ts +1 -0
  147. package/src/services/pdf/gotenberg.ts +50 -14
  148. package/src/services/pdf/index.ts +11 -11
  149. package/src/services/public-http.ts +1 -1
  150. package/src/services/rail-shortcuts.ts +1 -1
  151. package/src/services/rail-snapshot.ts +1 -1
  152. package/src/services/request-cache-redis.ts +2 -1
  153. package/src/services/session/user.ts +1 -1
  154. package/src/services/settings/app.ts +5 -1
  155. package/src/services/settings/core-settings.ts +6 -8
  156. package/src/services/settings/index.ts +3 -3
  157. package/src/services/settings/store.ts +3 -3
  158. package/src/services/webauthn.ts +1 -1
  159. package/src/shared/index.ts +3 -6
  160. package/src/shared/markdown/extensions/info-blocks.ts +1 -1
  161. package/src/ssr/AdminSidebar.tsx +2 -2
  162. package/src/ssr/AppLaunchpad.island.tsx +12 -2
  163. package/src/ssr/AppLaunchpadPanel.tsx +4 -4
  164. package/src/ssr/GlobalSearchDialog.tsx +6 -6
  165. package/src/ssr/GlobalSearchTrigger.island.tsx +2 -2
  166. package/src/ssr/Layout.tsx +2 -2
  167. package/src/ssr/LayoutHeader.tsx +1 -1
  168. package/src/ssr/LayoutHelp.tsx +4 -6
  169. package/src/ssr/LayoutHelpTrigger.island.tsx +2 -2
  170. package/src/ssr/MinimalLayout.tsx +1 -1
  171. package/src/ssr/MobileNavigation.tsx +2 -2
  172. package/src/ssr/ProfilePreferences.island.tsx +1 -1
  173. package/src/ssr/admin-navigation.ts +6 -2
  174. package/src/ssr/islands/index.ts +2 -2
  175. package/src/ssr/mobile-menu-history.ts +1 -1
  176. package/src/workflows/ai/index.ts +2 -2
  177. package/src/workflows/ai/runtime.ts +16 -91
  178. package/src/workflows/ai/store.ts +1 -1
  179. package/src/workflows/ai/types.ts +6 -82
  180. package/src/workflows/store/index.ts +2 -2
  181. package/src/server/services/freeipa/test-certificates.ts +0 -44
  182. package/src/services/session/test-fixture.ts +0 -18
@@ -1,7 +1,7 @@
1
- import { AiFileLocation, AiFileReference } from "./file-reference-contracts";
2
1
  import { z } from "zod";
3
- import { CodeResourceId } from "./browser-code-contracts";
4
2
  import { PrincipalSchema } from "../contracts/shared";
3
+ import { CodeResourceId } from "./browser-code-contracts";
4
+ import { AiFileLocation, AiFileReference } from "./file-reference-contracts";
5
5
 
6
6
  // Flat Assistant tool inputs. The owning service also validates its domain contracts.
7
7
  const Id = z.object({ id: CodeResourceId }).strict();
@@ -42,76 +42,142 @@ const DatabaseSql = z.object({
42
42
  const CodeWriteInput = Id.extend({
43
43
  expectedRevision: z.number().int().positive(),
44
44
  entry: ArtifactPath.optional(),
45
- files: z.array(z.union([ArtifactFile, z.object({ path: ArtifactPath, fromFile: AiFileReference }).strict()])).min(1).max(64),
45
+ files: z
46
+ .array(z.union([ArtifactFile, z.object({ path: ArtifactPath, fromFile: AiFileReference }).strict()]))
47
+ .min(1)
48
+ .max(64),
46
49
  });
47
50
 
48
51
  export const CODE_SOURCE_TOOLS = {
49
52
  code_files: {
50
- description: "List authorized files in one explicit chat, Project or App shared store. Returns locations, metadata and nextAfter. Use code_file_stat to obtain the opaque version before copying; does not load bytes into model context.",
51
- input: AiFileLocation.omit({ path: true }).extend({ after: z.string().max(500).default(""), limit: z.number().int().min(1).max(1000).default(100) }),
53
+ description:
54
+ "List authorized files in one explicit chat, Project or App shared store. Returns locations, metadata and nextAfter. Use code_file_stat to obtain the opaque version before copying; does not load bytes into model context.",
55
+ input: AiFileLocation.omit({ path: true }).extend({
56
+ after: z.string().max(500).default(""),
57
+ limit: z.number().int().min(1).max(1000).default(100),
58
+ }),
52
59
  },
53
60
  code_file_stat: {
54
- description: "Inspect one authorized file location. Returns exists plus its exact versioned reference, size and mediaType, never its bytes. App files require Use, Project files Read, chats ownership.",
61
+ description:
62
+ "Inspect one authorized file location. Returns exists plus its exact versioned reference, size and mediaType, never its bytes. App files require Use, Project files Read, chats ownership.",
55
63
  input: z.object({ file: AiFileLocation }).strict(),
56
64
  },
57
65
  code_file_copy: {
58
- description: "Copy one exact file version between chat, Project and App shared stores with fresh review. Source bytes never enter tool output. App destination requires Use, Project destination Write, chat destination ownership. Set expectedVersion null only for a new destination; otherwise use its version from code_file_stat. Source/version, target rights, overwrite and byte limits are rechecked. This grants no access to other files.",
66
+ description:
67
+ "Copy one exact file version between chat, Project and App shared stores with fresh review. Source bytes never enter tool output. App destination requires Use, Project destination Write, chat destination ownership. Set expectedVersion null only for a new destination; otherwise use its version from code_file_stat. Source/version, target rights, overwrite and byte limits are rechecked. This grants no access to other files.",
59
68
  review: true,
60
- input: z.object({ source: AiFileReference, destination: AiFileLocation, expectedVersion: z.string().min(1).max(128).nullable() }).strict(),
69
+ input: z
70
+ .object({ source: AiFileReference, destination: AiFileLocation, expectedVersion: z.string().min(1).max(128).nullable() })
71
+ .strict(),
61
72
  },
62
73
  code_database_export: {
63
- description: "Export an App database backup into the current chat as a file. Manage required. Returns the actual path/version; no backup bytes enter tool output. Never overwrites an existing file. The chat file-size/storage limits apply. This is a Studio backup operation, not access to vendor APIs.",
64
- input: Id.extend({ path: z.string().startsWith("/").max(240).default("/database.sqlite").describe("New chat filename; an existing path is rejected. Choose another name after a conflict.") }),
74
+ description:
75
+ "Export an App database backup into the current chat as a file. Manage required. Returns the actual path/version; no backup bytes enter tool output. Never overwrites an existing file. The chat file-size/storage limits apply. This is a Studio backup operation, not access to vendor APIs.",
76
+ input: Id.extend({
77
+ path: z
78
+ .string()
79
+ .startsWith("/")
80
+ .max(240)
81
+ .default("/database.sqlite")
82
+ .describe("New chat filename; an existing path is rejected. Choose another name after a conflict."),
83
+ }),
65
84
  },
66
85
  code_manage_read: {
67
- description: "Read an App management snapshot before deletion: title, source revision, publication, file/JSON counts, database connection and managementRevision. Manage required. Does not expose another user's credentials or database server internals.", input: Id,
86
+ description:
87
+ "Read an App management snapshot before deletion: title, source revision, publication, file/JSON counts, database connection and managementRevision. Manage required. Does not expose another user's credentials or database server internals.",
88
+ input: Id,
68
89
  },
69
90
  code_delete: {
70
- description: "Permanently delete an App, all source history, publications, grants and server data with fresh review. Read code_manage_read first. Database physical cleanup is queued, not claimed finished. Cannot delete an App that changed since review.", review: true,
91
+ description:
92
+ "Permanently delete an App, all source history, publications, grants and server data with fresh review. Read code_manage_read first. Database physical cleanup is queued, not claimed finished. Cannot delete an App that changed since review.",
93
+ review: true,
71
94
  input: Id.extend({ expectedManagementRevision: z.string().regex(/^[a-f0-9]{64}$/) }),
72
95
  },
73
96
  code_database_read: {
74
- description: "Read App database connection status, generation, dataRevision and aggregate overview. Manage required; does not create a database. These are Studio contracts, not access to the backing service API.",
97
+ description:
98
+ "Read App database connection status, generation, dataRevision and aggregate overview. Manage required; does not create a database. These are Studio contracts, not access to the backing service API.",
75
99
  input: Id,
76
100
  },
77
101
  code_database_clear: {
78
- description: "Permanently clear all table rows with fresh review while preserving schema, source, files and JSON storage. Manage required. Read code_database_read first. Returns completed and clearedTables; partial failure is explicit and cannot be blindly retried.",
102
+ description:
103
+ "Permanently clear all table rows with fresh review while preserving schema, source, files and JSON storage. Manage required. Read code_database_read first. Returns completed and clearedTables; partial failure is explicit and cannot be blindly retried.",
79
104
  review: true,
80
105
  input: Id.extend({ expectedGeneration: z.string().regex(/^[a-f0-9]{64}$/), expectedDataRevision: z.uuid() }),
81
106
  },
82
107
  code_database_reset: {
83
- description: "Disconnect and permanently discard an App database, including schema, with fresh review. Preserves source, publications and files/JSON storage. Next explicit connection creates an empty database; physical deletion is queued. Read code_database_read first. Never use as a routine query fix.",
108
+ description:
109
+ "Disconnect and permanently discard an App database, including schema, with fresh review. Preserves source, publications and files/JSON storage. Next explicit connection creates an empty database; physical deletion is queued. Read code_database_read first. Never use as a routine query fix.",
84
110
  review: true,
85
- input: Id.extend({ expectedGeneration: z.string().regex(/^[a-f0-9]{64}$/).nullable(), expectedDataRevision: z.uuid().nullable() }),
111
+ input: Id.extend({
112
+ expectedGeneration: z
113
+ .string()
114
+ .regex(/^[a-f0-9]{64}$/)
115
+ .nullable(),
116
+ expectedDataRevision: z.uuid().nullable(),
117
+ }),
86
118
  },
87
119
  code_storage_list: {
88
- description: "Inspect an App's shared files or JSON keys for administration. Manage required. Returns storageRevision, counts and a page of key/bytes/mediaType/version metadata; no file contents. Follow nextAfter.",
89
- input: Id.extend({ area: z.enum(["files", "kv"]), after: z.string().max(240).default(""), limit: z.number().int().min(1).max(1000).default(100) }),
120
+ description:
121
+ "Inspect an App's shared files or JSON keys for administration. Manage required. Returns storageRevision, counts and a page of key/bytes/mediaType/version metadata; no file contents. Follow nextAfter.",
122
+ input: Id.extend({
123
+ area: z.enum(["files", "kv"]),
124
+ after: z.string().max(240).default(""),
125
+ limit: z.number().int().min(1).max(1000).default(100),
126
+ }),
90
127
  },
91
128
  code_storage_delete: {
92
- description: "Delete one shared file/JSON key, or clear an explicit storage area, with fresh user review. Manage required. Source, publications and database are preserved. Read code_storage_list first.",
129
+ description:
130
+ "Delete one shared file/JSON key, or clear an explicit storage area, with fresh user review. Manage required. Source, publications and database are preserved. Read code_storage_list first.",
93
131
  review: true,
94
- input: Id.extend({ area: z.enum(["files", "kv", "all"]), key: z.string().min(1).max(240).optional().describe("Delete this exact key; omit to clear the entire area. Not valid with area all."), expectedStorageRevision: z.number().int().positive().describe("storageRevision from code_storage_list; concurrent writes invalidate the review.") }).refine(input => input.area !== "all" || input.key === undefined, "all clears both areas and cannot select a key"),
132
+ input: Id.extend({
133
+ area: z.enum(["files", "kv", "all"]),
134
+ key: z.string().min(1).max(240).optional().describe("Delete this exact key; omit to clear the entire area. Not valid with area all."),
135
+ expectedStorageRevision: z
136
+ .number()
137
+ .int()
138
+ .positive()
139
+ .describe("storageRevision from code_storage_list; concurrent writes invalidate the review."),
140
+ }).refine((input) => input.area !== "all" || input.key === undefined, "all clears both areas and cannot select a key"),
95
141
  },
96
142
  code_access_read: {
97
- description: "Read an App's current grants and accessRevision before changing access. Manage required. App Use is read; Manage is admin. Skill access is separate.",
143
+ description:
144
+ "Read an App's current grants and accessRevision before changing access. Manage required. App Use is read; Manage is admin. Skill access is separate.",
98
145
  input: Id,
99
146
  },
100
147
  code_access_change: {
101
- description: "Grant, change or revoke one App permission with fresh user review. Read code_access_read first. Sharing a referenced Skill is a separate operation.",
148
+ description:
149
+ "Grant, change or revoke one App permission with fresh user review. Read code_access_read first. Sharing a referenced Skill is a separate operation.",
102
150
  review: true,
103
151
  input: Id.extend({
104
- expectedAccessRevision: z.string().regex(/^[a-f0-9]{64}$/).describe("Exact accessRevision returned by code_access_read."),
105
- principal: PrincipalSchema.refine(principal => principal.type !== "service_account", "Studio Apps support user, group, authenticated or public grants.").optional().describe("New user or group from core.entities.search, authenticated, or public (read/run only, no server data). Service accounts are unsupported. Supply principal OR accessId."),
152
+ expectedAccessRevision: z
153
+ .string()
154
+ .regex(/^[a-f0-9]{64}$/)
155
+ .describe("Exact accessRevision returned by code_access_read."),
156
+ principal: PrincipalSchema.refine(
157
+ (principal) => principal.type !== "service_account",
158
+ "Studio Apps support user, group, authenticated or public grants.",
159
+ )
160
+ .optional()
161
+ .describe(
162
+ "New user or group from core.entities.search, authenticated, or public (read/run only, no server data). Service accounts are unsupported. Supply principal OR accessId.",
163
+ ),
106
164
  accessId: z.uuid().optional().describe("Existing grant ID from code_access_read; supply accessId OR principal."),
107
165
  permission: z.enum(["read", "admin"]).nullable().describe("read means Use, admin means Manage; null removes an existing accessId."),
108
- }).refine(input => Number(input.principal !== undefined) + Number(input.accessId !== undefined) === 1
109
- && (input.principal === undefined || input.permission !== null), "Supply principal with permission OR accessId with permission (null removes)")
110
- .refine(input => input.principal?.type !== "public" || input.permission === "read", "Public Apps only support read/run access"),
166
+ })
167
+ .refine(
168
+ (input) =>
169
+ Number(input.principal !== undefined) + Number(input.accessId !== undefined) === 1 &&
170
+ (input.principal === undefined || input.permission !== null),
171
+ "Supply principal with permission OR accessId with permission (null removes)",
172
+ )
173
+ .refine((input) => input.principal?.type !== "public" || input.permission === "read", "Public Apps only support read/run access"),
111
174
  },
112
175
  code_actions: {
113
- description: "Discover a visible App's currently published actions and their complete input/output JSON Schemas without running code. Returns publishedVersion for code_action. Requires Use; no draft source or management tools are loaded.",
114
- input: Id.extend({ draft: z.boolean().default(false).describe("Inspect current draft actions instead of the publication; Manage required.") }),
176
+ description:
177
+ "Discover a visible App's currently published actions and their complete input/output JSON Schemas without running code. Returns publishedVersion for code_action. Requires Use; no draft source or management tools are loaded.",
178
+ input: Id.extend({
179
+ draft: z.boolean().default(false).describe("Inspect current draft actions instead of the publication; Manage required."),
180
+ }),
115
181
  },
116
182
  code_sql: {
117
183
  description:
@@ -163,7 +229,8 @@ export const CODE_SOURCE_TOOLS = {
163
229
  }),
164
230
  },
165
231
  code_unpublish: {
166
- description: "Withdraw the current App publication with fresh review. Use code_manage_read first. Preserves source, publication history and data; ordinary Use-level callers can no longer start this App or its actions.",
232
+ description:
233
+ "Withdraw the current App publication with fresh review. Use code_manage_read first. Preserves source, publication history and data; ordinary Use-level callers can no longer start this App or its actions.",
167
234
  review: true,
168
235
  input: Id.extend({ expectedPublishedVersion: z.number().int().positive() }),
169
236
  },
@@ -189,8 +256,9 @@ export const CODE_SOURCE_TOOLS = {
189
256
  input: ArtifactCreate.pick({ title: true, description: true, icon: true }),
190
257
  },
191
258
  code_write: {
192
- description: "Atomically save complete source files against expectedRevision from code_read/create. Other files stay unchanged. Returns revision and compiler diagnostics; never runs. fromFile imports one exact chat/Project/App file reference as UTF-8 source without printing bytes. File imports receive fresh review because source may be shared or published. Use code_file_stat first; source file and bundle byte limits apply.",
193
- review: (input: unknown) => CodeWriteInput.parse(input).files.some(file => "fromFile" in file),
259
+ description:
260
+ "Atomically save complete source files against expectedRevision from code_read/create. Other files stay unchanged. Returns revision and compiler diagnostics; never runs. fromFile imports one exact chat/Project/App file reference as UTF-8 source without printing bytes. File imports receive fresh review because source may be shared or published. Use code_file_stat first; source file and bundle byte limits apply.",
261
+ review: (input: unknown) => CodeWriteInput.parse(input).files.some((file) => "fromFile" in file),
194
262
  input: CodeWriteInput,
195
263
  },
196
264
  code_remove: {
@@ -1,4 +1,6 @@
1
1
  import { z } from "zod";
2
+ import { readBoundedJson } from "../_internal/bounded-json";
3
+ import { getApp } from "../_internal/registry";
2
4
  import {
3
5
  capabilityIdempotencyKeyHash,
4
6
  capabilityRequestHash,
@@ -6,17 +8,29 @@ import {
6
8
  completeCapabilityClaim,
7
9
  markCapabilityClaimUncertain,
8
10
  } from "../capabilities/claims";
9
- import { getApp } from "../_internal/registry";
10
- import { readBoundedJson } from "../_internal/bounded-json";
11
- import { withActiveIdentitySigner } from "../services/identity/key-ring";
12
11
  import { signInvocationToken } from "../services/identity/invocation-token";
12
+ import { withActiveIdentitySigner } from "../services/identity/key-ring";
13
13
  import { LOCALE_HEADER } from "../shared/locale";
14
- import { CODE_SOURCE_TOOLS, type CodeSourceToolName } from "./code-source-contracts";
15
14
  import { resolveAiCapabilityActor } from "./capability-execution";
15
+ import { aiChatTasks } from "./chat-tasks";
16
+ import { authorizeCodeExecution } from "./code-execution";
17
+ import { CODE_SOURCE_TOOLS, type CodeSourceToolName } from "./code-source-contracts";
16
18
  import { aiConversations } from "./store";
17
19
  import { defineAiTool } from "./tools";
18
20
 
19
- const WRITE_TOOLS = new Set(["code_create", "code_write", "code_remove", "code_update", "code_fork", "code_publish", "code_restore", "code_database_export", ...Object.entries(CODE_SOURCE_TOOLS).filter(([, definition]) => "review" in definition && definition.review).map(([name]) => name)]);
21
+ const WRITE_TOOLS = new Set([
22
+ "code_create",
23
+ "code_write",
24
+ "code_remove",
25
+ "code_update",
26
+ "code_fork",
27
+ "code_publish",
28
+ "code_restore",
29
+ "code_database_export",
30
+ ...Object.entries(CODE_SOURCE_TOOLS)
31
+ .filter(([, definition]) => "review" in definition && definition.review)
32
+ .map(([name]) => name),
33
+ ]);
20
34
 
21
35
  const Reply = z.discriminatedUnion("ok", [
22
36
  z.object({ ok: z.literal(true), data: z.unknown() }),
@@ -41,10 +55,38 @@ export function createCodeSourceTool(name: CodeSourceToolName) {
41
55
  persistedActor: context.actor,
42
56
  store: aiConversations,
43
57
  });
58
+ const databaseOperations: Partial<Record<CodeSourceToolName, string>> = {
59
+ code_sql: "query",
60
+ code_database_read: "status",
61
+ code_database_export: "export",
62
+ code_database_clear: "clear",
63
+ code_database_reset: "reset",
64
+ };
65
+ const databaseOperation = databaseOperations[name];
66
+ let authorizeDatabase: (() => Promise<void>) | undefined;
67
+ if (context.turnId && databaseOperation) {
68
+ const config = await aiConversations.getTurnRunConfig({ conversationId: context.conversationId, turnId: context.turnId });
69
+ if (config?.kind !== "compact" && config?.background) {
70
+ const { id } = z.object({ id: z.string() }).parse(input);
71
+ const conversationId = context.conversationId,
72
+ turnId = context.turnId;
73
+ authorizeDatabase = async () => {
74
+ const { config } = await authorizeCodeExecution(conversationId, turnId, actor.user.id);
75
+ if (!config.mandate) throw new Error("Background database access requires task grants");
76
+ await aiChatTasks.authorizeRuntime({
77
+ mandate: config.mandate,
78
+ kind: "database",
79
+ input: { resourceId: id, operation: databaseOperation },
80
+ });
81
+ };
82
+ }
83
+ }
84
+ await authorizeDatabase?.();
44
85
  const app = await getApp("assistant");
45
86
  if (!app) throw new Error("Assistant is unavailable.");
46
87
  const signal = context.signal;
47
88
  const send = async (review = false) => {
89
+ await authorizeDatabase?.();
48
90
  const signed = await withActiveIdentitySigner(
49
91
  "invocation",
50
92
  (signer) =>
@@ -85,7 +127,8 @@ export function createCodeSourceTool(name: CodeSourceToolName) {
85
127
  };
86
128
  if ("review" in definition && (typeof definition.review === "function" ? definition.review(input) : definition.review)) {
87
129
  const preview = z.object({ data: z.object({ message: z.string().min(1) }) }).parse((await send(true)).data).data;
88
- if (!await context.requestApproval(preview.message)) throw new Error("The user declined this change. No mutation was sent.");
130
+ if (!authorizeDatabase && !(await context.requestApproval(preview.message)))
131
+ throw new Error("The user declined this change. No mutation was sent.");
89
132
  }
90
133
  // Reuse the platform replay guard; these tools are not registered capabilities.
91
134
  const claim = WRITE_TOOLS.has(name)
@@ -91,7 +91,7 @@ const findLoopSplitIndex = (entries: StoreEntry[], keepLoops: number): number =>
91
91
 
92
92
  /** Preserve recent complete rounds, retaining one when the first loop has only two. */
93
93
  const findRoundSplitIndex = (entries: StoreEntry[]): number => {
94
- const rounds = entries.flatMap((entry, index) => entry.message.role === "assistant" && entry.kind === "message" ? [index] : []);
94
+ const rounds = entries.flatMap((entry, index) => (entry.message.role === "assistant" && entry.kind === "message" ? [index] : []));
95
95
  if (!rounds.length) return -1;
96
96
  const candidate = rounds.length === 1 ? entries.length : rounds[rounds.length - (rounds.length > 2 ? 2 : 1)]!;
97
97
  const pending = new Set<string>();
@@ -132,8 +132,8 @@ export const createCloudCompactFn = (input: {
132
132
  return (ctx) => {
133
133
  const window = ctx.provider.contextWindow;
134
134
  const reserve = input.maxOutputTokens ?? 0;
135
- const fillRatio = typeof ctx.fillRatio === "number" && window && window > reserve
136
- ? ctx.fillRatio * window / (window - reserve) : ctx.fillRatio;
135
+ const fillRatio =
136
+ typeof ctx.fillRatio === "number" && window && window > reserve ? (ctx.fillRatio * window) / (window - reserve) : ctx.fillRatio;
137
137
  if (!ctx.force && (typeof fillRatio !== "number" || fillRatio < COMPACTION_FILL_RATIO)) return null;
138
138
 
139
139
  const totalLoops = countConversationLoops(ctx.entries);
@@ -1,9 +1,20 @@
1
- import { createAiTodoTool } from "./todo-tool";
1
+ import { z } from "zod";
2
+ import { createCloudAiTranscribeAudioTool } from "./audio-tool";
3
+ import {
4
+ CODE_RUNTIME_TOOL_NAMES,
5
+ CodeActionInput,
6
+ CodeExportInput,
7
+ CodeInspectInput,
8
+ CodeInteractInput,
9
+ CodeOpenInput,
10
+ CodePresentInput,
11
+ CodeRunInput,
12
+ CodeSecretInput,
13
+ CodeStopInput,
14
+ } from "./browser-code-contracts";
2
15
  import { runManagedCodeTool } from "./code-runtime-tools";
3
16
  import { CODE_SOURCE_TOOLS } from "./code-source-contracts";
4
17
  import { createCodeSourceTools } from "./code-source-tools";
5
- import { CODE_RUNTIME_TOOL_NAMES, CodeActionInput, CodeRunInput, CodeInspectInput, CodeInteractInput, CodeStopInput, CodeOpenInput, CodeExportInput, CodePresentInput, CodeSecretInput } from "./browser-code-contracts";
6
- import { z } from "zod";
7
18
  import {
8
19
  CloudAiCardInputSchema,
9
20
  CloudAiCardOutputSchema,
@@ -14,6 +25,7 @@ import {
14
25
  CloudAiTextEditorInputSchema,
15
26
  CloudAiTextEditorOutputSchema,
16
27
  } from "./default-tool-contracts";
28
+ import { createCloudAiFetchFileTool } from "./fetch-file-tool";
17
29
  import {
18
30
  createCloudAiCalculateTool,
19
31
  createCloudAiListFilesTool,
@@ -21,12 +33,11 @@ import {
21
33
  createCloudAiReadFileTool,
22
34
  createCloudAiWriteFileTool,
23
35
  } from "./file-tools";
24
- import { createCloudAiFetchFileTool } from "./fetch-file-tool";
25
36
  import { createCloudAiWebExtractTool, createCloudAiWebSearchTool, isCloudAiFirecrawlConfigured } from "./firecrawl-tools";
26
37
  import { createCloudAiMarkdownToPdfTool } from "./markdown-pdf-tool";
38
+ import { createAiTodoTool } from "./todo-tool";
27
39
  import { defineAiTool } from "./tools";
28
40
  import type { AiDataBoundary, AiRuntimeTool } from "./types";
29
- import { createCloudAiTranscribeAudioTool } from "./audio-tool";
30
41
  import { createCloudAiViewImageTool } from "./vision-tool";
31
42
 
32
43
  export {
@@ -95,15 +106,77 @@ export const createCloudAiLocalBashTool = () =>
95
106
  }).client();
96
107
 
97
108
  export const createCloudAiCodeTools = () => [
98
- defineAiTool({name:"code_secret",description:"Open a trusted Secret input dialog. The user enters the value directly into encrypted Assistant storage; only configured/name returns. Never ask for a credential in chat, survey, app controls or code_interact. Personal secrets are scoped to this chat or resource and bound to the exact HTTPS origin, header and prefix. Use secret(name,{prefix}) in http.fetch headers; load assistant-code-mode for HTTP details. Web UI required for secret entry; stored secrets also work from CLI.",inputSchema:CodeSecretInput,outputSchema:z.object({configured:z.boolean(),name:z.string()}),approval:"never"}).client(),
99
- defineAiTool({ name: "code_action", description: "Call one published Studio App action using its exact discovered name, publication and input schema. Discover with code_actions first. Use permission is sufficient; draft and management access are not granted. Runtime effects and approvals are real. Never repeat an uncertain mutation blindly.", inputSchema: CodeActionInput, outputSchema: z.json(), approval: "never" }).server(runManagedCodeTool("code_action")),
100
- defineAiTool({ name: "code_run", promptHint: "For file analysis, data transformations or combining Cloud data, run a short one-off script with code_run. Load assistant-code-mode for its runtime APIs; no saved app is required.", description: "Run a saved resource id OR one-off code in the isolated worker. Optional resourceId binds one-off code to existing app data (Manage required), without editing its source. Includes capabilities.run(name,input) to chain Cloud capabilities in JavaScript; load assistant-code-mode for its runtime APIs. Scripts accept current chat inputPaths; app test runs use them only as explicit picker fixtures. Returns output, logs and UI state for agent inspection only; use code_present to show a one-off visualization to the user. Local test storage is temporary; shared data, database writes and capability effects are real and keep normal permissions and approvals.", inputSchema: CodeRunInput, outputSchema: z.json(), approval: "never" }).server(runManagedCodeTool("code_run")),
101
- defineAiTool({ name: "code_inspect", description: "Inspect a test run: errors, logs, output, controls and pending modal. Use nodeId to inspect table rows or list items; follow pagination only as needed.", inputSchema: CodeInspectInput, outputSchema: z.json(), approval: "never" }).server(runManagedCodeTool("code_inspect")),
102
- defineAiTool({ name: "code_interact", description: "Operate a control or answer a pending modal using its exact snapshot ID. Buttons need only id; controls use event:{type:change,value:...} or event:{type:select,key:...}. Use answer for modal replies, or answer:null to cancel. Copy an interactions example from code_inspect; never send JSON as a string. Use steps:[{id,event?},...] for up to three known sequential interactions; stops on errors, modals or background work. Returns one resulting state with completedSteps and nextStep.", inputSchema: CodeInteractInput, outputSchema: z.json(), approval: "never" }).server(runManagedCodeTool("code_interact")),
103
- defineAiTool({ name: "code_stop", description: "Stop and release an isolated test run.", inputSchema: CodeStopInput, outputSchema: z.json(), approval: "never" }).server(runManagedCodeTool("code_stop")),
104
- defineAiTool({ name: "code_open", description: "Show the app beside the chat without starting it. For one-off calculations, return the result instead of opening an app.", inputSchema: CodeOpenInput, outputSchema: z.json(), approval: "never" }).client(),
105
- defineAiTool({ name: "code_present", description: "Present a successful one-off run as a persistent interactive visualization in this chat. Requires code_run with code, without resourceId or saved app id. Saves source, selected inputs and UI preview. Users can activate controls and download the current view. Test runs are not visible until this succeeds. For reusable apps use code_open instead.", inputSchema: CodePresentInput, outputSchema: z.json(), approval: "never" }).server(runManagedCodeTool("code_present")),
106
- defineAiTool({ name: "code_export", description: "Copy a captured output file from a test run into the chat. Returns a path for normal file tools and presentation.", inputSchema: CodeExportInput, outputSchema: z.json(), approval: "never" }).server(runManagedCodeTool("code_export")),
109
+ defineAiTool({
110
+ name: "code_secret",
111
+ description:
112
+ "Open a trusted Secret input dialog. The user enters the value directly into encrypted Assistant storage; only configured/name returns. Never ask for a credential in chat, survey, app controls or code_interact. Personal secrets are scoped to this chat or resource and bound to the exact HTTPS origin, header and prefix. Use secret(name,{prefix}) in http.fetch headers; load assistant-code-mode for HTTP details. Web UI required for secret entry; stored secrets also work from CLI.",
113
+ inputSchema: CodeSecretInput,
114
+ outputSchema: z.object({ configured: z.boolean(), name: z.string() }),
115
+ approval: "never",
116
+ }).client(),
117
+ defineAiTool({
118
+ name: "code_action",
119
+ description:
120
+ "Call one published Studio App action using its exact discovered name, publication and input schema. Discover with code_actions first. Use permission is sufficient; draft and management access are not granted. Runtime effects and approvals are real. Never repeat an uncertain mutation blindly.",
121
+ inputSchema: CodeActionInput,
122
+ outputSchema: z.json(),
123
+ approval: "never",
124
+ }).server(runManagedCodeTool("code_action")),
125
+ defineAiTool({
126
+ name: "code_run",
127
+ promptHint:
128
+ "For file analysis, data transformations or combining Cloud data, run a short one-off script with code_run. Load assistant-code-mode for its runtime APIs; no saved app is required.",
129
+ description:
130
+ "Run a saved resource id OR one-off code in the isolated worker. Optional resourceId binds one-off code to existing app data (Manage required), without editing its source. Includes capabilities.run(name,input) to chain Cloud capabilities in JavaScript; load assistant-code-mode for its runtime APIs. Scripts accept current chat inputPaths; app test runs use them only as explicit picker fixtures. Returns output, logs and UI state for agent inspection only; use code_present to show a one-off visualization to the user. Local test storage is temporary; shared data, database writes and capability effects are real and keep normal permissions and approvals.",
131
+ inputSchema: CodeRunInput,
132
+ outputSchema: z.json(),
133
+ approval: "never",
134
+ }).server(runManagedCodeTool("code_run")),
135
+ defineAiTool({
136
+ name: "code_inspect",
137
+ description:
138
+ "Inspect a test run: errors, logs, output, controls and pending modal. Use nodeId to inspect table rows or list items; follow pagination only as needed.",
139
+ inputSchema: CodeInspectInput,
140
+ outputSchema: z.json(),
141
+ approval: "never",
142
+ }).server(runManagedCodeTool("code_inspect")),
143
+ defineAiTool({
144
+ name: "code_interact",
145
+ description:
146
+ "Operate a control or answer a pending modal using its exact snapshot ID. Buttons need only id; controls use event:{type:change,value:...} or event:{type:select,key:...}. Use answer for modal replies, or answer:null to cancel. Copy an interactions example from code_inspect; never send JSON as a string. Use steps:[{id,event?},...] for up to three known sequential interactions; stops on errors, modals or background work. Returns one resulting state with completedSteps and nextStep.",
147
+ inputSchema: CodeInteractInput,
148
+ outputSchema: z.json(),
149
+ approval: "never",
150
+ }).server(runManagedCodeTool("code_interact")),
151
+ defineAiTool({
152
+ name: "code_stop",
153
+ description: "Stop and release an isolated test run.",
154
+ inputSchema: CodeStopInput,
155
+ outputSchema: z.json(),
156
+ approval: "never",
157
+ }).server(runManagedCodeTool("code_stop")),
158
+ defineAiTool({
159
+ name: "code_open",
160
+ description: "Show the app beside the chat without starting it. For one-off calculations, return the result instead of opening an app.",
161
+ inputSchema: CodeOpenInput,
162
+ outputSchema: z.json(),
163
+ approval: "never",
164
+ }).client(),
165
+ defineAiTool({
166
+ name: "code_present",
167
+ description:
168
+ "Present a successful one-off run as a persistent interactive visualization in this chat. Requires code_run with code, without resourceId or saved app id. Saves source, selected inputs and UI preview. Users can activate controls and download the current view. Test runs are not visible until this succeeds. For reusable apps use code_open instead.",
169
+ inputSchema: CodePresentInput,
170
+ outputSchema: z.json(),
171
+ approval: "never",
172
+ }).server(runManagedCodeTool("code_present")),
173
+ defineAiTool({
174
+ name: "code_export",
175
+ description: "Copy a captured output file from a test run into the chat. Returns a path for normal file tools and presentation.",
176
+ inputSchema: CodeExportInput,
177
+ outputSchema: z.json(),
178
+ approval: "never",
179
+ }).server(runManagedCodeTool("code_export")),
107
180
  ];
108
181
 
109
182
  export const createDefaultCloudAiTools = () => [createAiTodoTool(), createCloudAiSurveyTool(), createCloudAiTextEditorTool()];
@@ -4,7 +4,7 @@ import { logger, trace } from "../services/logging";
4
4
  import { superviseRuntimeTask } from "../services/runtime-lifecycle";
5
5
  import { aiModelAccess } from "./model-access";
6
6
  import { aiProjects } from "./projects";
7
- import { AiTranscriptionError, describeTranscriptionFailure, runAiTranscription, type RunAiTranscriptionInput } from "./transcription";
7
+ import { AiTranscriptionError, describeTranscriptionFailure, type RunAiTranscriptionInput, runAiTranscription } from "./transcription";
8
8
 
9
9
  const log = logger("ai:dictations");
10
10
  const queue = lazySync((sync) =>
@@ -1,23 +1,37 @@
1
1
  import type { AiDraftContentPart } from "./types";
2
2
 
3
- export const aiDraftPartLabel = (part: AiDraftContentPart): string => part.type === "text" ? part.text
4
- : part.type === "resource" ? part.title ?? part.ref.id
5
- : part.type === "project-file" ? part.path : part.path.split("/").at(-1) || part.path;
3
+ export const aiDraftPartLabel = (part: AiDraftContentPart): string =>
4
+ part.type === "text"
5
+ ? part.text
6
+ : part.type === "resource"
7
+ ? (part.title ?? part.ref.id)
8
+ : part.type === "project-file"
9
+ ? part.path
10
+ : part.path.split("/").at(-1) || part.path;
6
11
 
7
- export const aiDraftText = (content: readonly AiDraftContentPart[]) => content.map(part =>
8
- part.type === "text" || part.inline ? aiDraftPartLabel(part) : "").join("");
12
+ export const aiDraftText = (content: readonly AiDraftContentPart[]) =>
13
+ content.map((part) => (part.type === "text" || part.inline ? aiDraftPartLabel(part) : "")).join("");
9
14
 
10
15
  /** Queue text edits preserve untouched inline identities and ordinary attachments. */
11
16
  export function editAiDraftText(content: readonly AiDraftContentPart[], text: string): AiDraftContentPart[] {
12
17
  const before = aiDraftText(content);
13
- let start = 0, oldEnd = before.length, newEnd = text.length;
18
+ let start = 0,
19
+ oldEnd = before.length,
20
+ newEnd = text.length;
14
21
  while (start < oldEnd && start < newEnd && before[start] === text[start]) start++;
15
- while (oldEnd > start && newEnd > start && before[oldEnd - 1] === text[newEnd - 1]) { oldEnd--; newEnd--; }
22
+ while (oldEnd > start && newEnd > start && before[oldEnd - 1] === text[newEnd - 1]) {
23
+ oldEnd--;
24
+ newEnd--;
25
+ }
16
26
  let position = 0;
17
- const retained = content.flatMap(part => {
18
- if (part.type === "text") { position += part.text.length; return []; }
27
+ const retained = content.flatMap((part) => {
28
+ if (part.type === "text") {
29
+ position += part.text.length;
30
+ return [];
31
+ }
19
32
  if (!part.inline) return [];
20
- const from = position; position += aiDraftPartLabel(part).length;
33
+ const from = position;
34
+ position += aiDraftPartLabel(part).length;
21
35
  if (position <= start) return [{ from, end: position, part }];
22
36
  if (from >= oldEnd) return [{ from: from + newEnd - oldEnd, end: position + newEnd - oldEnd, part }];
23
37
  return [];
@@ -26,9 +40,10 @@ export function editAiDraftText(content: readonly AiDraftContentPart[], text: st
26
40
  let offset = 0;
27
41
  for (const item of retained) {
28
42
  if (item.from > offset) result.push({ type: "text", text: text.slice(offset, item.from) });
29
- result.push(item.part); offset = item.end;
43
+ result.push(item.part);
44
+ offset = item.end;
30
45
  }
31
46
  if (offset < text.length) result.push({ type: "text", text: text.slice(offset) });
32
- result.push(...content.filter(part => part.type !== "text" && !part.inline));
47
+ result.push(...content.filter((part) => part.type !== "text" && !part.inline));
33
48
  return result;
34
49
  }
package/src/ai/enrich.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { AiBackgroundAdmissionError, AiBackgroundCostError } from "./inference-calls";
2
1
  import { z } from "zod";
3
2
  import { coreSettings } from "../services";
4
3
  import type { TraceContext } from "../services/logging";
5
4
  import { logger, trace } from "../services/logging";
5
+ import { AiBackgroundAdmissionError, AiBackgroundCostError } from "./inference-calls";
6
6
  import { aiConversations } from "./store";
7
7
  import type { RunAiStructuredInput, RunAiStructuredResult } from "./structured";
8
8
  import { resolveAiBackgroundModel, runAiStructured } from "./structured";
@@ -1,16 +1,9 @@
1
- import { createRunToolStore } from "./capabilities";
2
- import { CODE_RUNTIME_TOOL_NAMES } from "./browser-code-contracts";
3
- import { AiCapabilityExecutionError } from "./capability-execution";
4
- import { aiChatTasks } from "./chat-tasks";
5
- import { assistantQuotaProvider, inferenceProvider } from "./quota-provider";
6
- import { AiRunTimeout } from "./run-timeout";
7
- import { createTurnTimingRecorder, withDurableTurnTiming } from "./turn-timing";
8
1
  import type { CompactEvent, NessiLoop, OutboundEvent, Provider, Tool, ToolResolver } from "@k2b/nessi";
9
2
  import { compact, nessi } from "@k2b/nessi";
10
- import { createHelpReader } from "../services/help";
11
3
  import { listCapabilities } from "../_internal/registry";
12
4
  import type { CapabilityActionReview } from "../contracts/capabilities";
13
5
  import type { AccessSubject, RequestActor } from "../server";
6
+ import { createHelpReader } from "../services/help";
14
7
  import { logger } from "../services/logging";
15
8
  import { coreSettings } from "../services/settings/api";
16
9
  import { normalizeLocale } from "../shared/locale";
@@ -22,8 +15,9 @@ import {
22
15
  hasRememberedAiToolApproval,
23
16
  } from "./approvals";
24
17
  import { isAssistantChatTurn } from "./assistant-models";
25
- import { createAiToolResolver } from "./capabilities";
26
- import { executeAiCapability, resolveAiCapabilityActor, reviewAiCapability } from "./capability-execution";
18
+ import { createAiToolResolver, createRunToolStore } from "./capabilities";
19
+ import { AiCapabilityExecutionError, executeAiCapability, resolveAiCapabilityActor, reviewAiCapability } from "./capability-execution";
20
+ import { aiChatTasks } from "./chat-tasks";
27
21
  import { createCloudCompactFn } from "./compaction";
28
22
  import { createCloudAiCodeTools, createCloudAiLocalBashTool, createConfiguredDefaultCloudAiTools } from "./default-tools";
29
23
  import { aiFileStore } from "./files-store";
@@ -46,7 +40,9 @@ import {
46
40
  streamBlockId,
47
41
  toolBlockId,
48
42
  } from "./protocol";
43
+ import { assistantQuotaProvider, inferenceProvider } from "./quota-provider";
49
44
  import { collectConversationResourceObservations } from "./resource-refs";
45
+ import { AiRunTimeout } from "./run-timeout";
50
46
  import { isAiVisionModelConfigured, type resolveAiModel } from "./settings";
51
47
  import { selectAiSkillCatalog } from "./skill-catalog";
52
48
  import { createCloudAiLoadSkillTool, createCloudAiSearchSkillsTool, loadSelectedAiSkills } from "./skill-tool";
@@ -57,6 +53,7 @@ import { composeAiSystemPrompt } from "./system-prompt";
57
53
  import { aiToolAudit } from "./tool-audit";
58
54
  import { resolveAiToolResultMaxChars } from "./tool-result-budget";
59
55
  import { aiToolPromptHints, type PreparedAiTools, prepareAiTools } from "./tools";
56
+ import { createTurnTimingRecorder, withDurableTurnTiming } from "./turn-timing";
60
57
  import type {
61
58
  AiChatTurnRunConfig,
62
59
  AiFrontendToolMode,
@@ -791,8 +788,7 @@ export class AiTurnExecutor {
791
788
  const allowed = allowedTools === null ? null : new Set(allowedTools);
792
789
  const activeTools = toolsSupported
793
790
  ? runtimeTools.filter(
794
- (tool) =>
795
- (!allowed || allowed.has(tool.def.name)) && !(config.mandate && CODE_RUNTIME_TOOL_NAMES.some((name) => name === tool.def.name)),
791
+ (tool) => (!allowed || allowed.has(tool.def.name)) && !(config.mandate && ["code_open", "code_secret"].includes(tool.def.name)),
796
792
  )
797
793
  : [];
798
794
  const memoryToolEnabled = activeTools.some((tool) => tool.def.name === "memory");
@@ -1,9 +1,9 @@
1
1
  import type { ClientRequest, IncomingHttpHeaders, IncomingMessage } from "node:http";
2
2
  import { type RequestOptions as HttpsRequestOptions, request as httpsRequest } from "node:https";
3
- import { resolvePublicNetworkAddresses, type PublicNetworkAddress } from "../services/network-security";
3
+ import { z } from "zod";
4
+ import { type PublicNetworkAddress, resolvePublicNetworkAddresses } from "../services/network-security";
4
5
  import { AI_FILES_MAX_FILE_BYTES_DEFAULT, aiFileStore, guessAiMediaType, normalizeAiFilePath } from "./files-store";
5
6
  import { defineAiTool } from "./tools";
6
- import { z } from "zod";
7
7
 
8
8
  const FETCH_FILE_MAX_REDIRECTS = 5;
9
9
  const FETCH_FILE_MAX_ADDRESS_ATTEMPTS = 4;
@@ -18,5 +18,10 @@ export class AiFileVersionConflict extends Error {
18
18
 
19
19
  /** A known file write rejection; no destination bytes were written. */
20
20
  export class AiFileWriteError extends Error {
21
- constructor(readonly code: "CONFLICT" | "STORAGE_FULL", message: string) { super(message); }
21
+ constructor(
22
+ readonly code: "CONFLICT" | "STORAGE_FULL",
23
+ message: string,
24
+ ) {
25
+ super(message);
26
+ }
22
27
  }