@capybara-hq/kernel 0.1.0-rc.2 → 0.1.0-rc.4

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 (312) hide show
  1. package/dist/admin/admin.controller.d.ts +9 -4
  2. package/dist/admin/admin.controller.js +13 -4
  3. package/dist/admin/extensions.controller.d.ts +8 -1
  4. package/dist/admin/extensions.controller.js +22 -2
  5. package/dist/agents/agent-lookup.d.ts +3 -0
  6. package/dist/agents/agent-lookup.js +20 -2
  7. package/dist/agents/agent-profile.service.d.ts +29 -0
  8. package/dist/agents/agent-profile.service.js +117 -0
  9. package/dist/agents/agents.controller.d.ts +93 -3
  10. package/dist/agents/agents.controller.js +114 -4
  11. package/dist/agents/agents.module.js +6 -2
  12. package/dist/agents/agents.service.d.ts +13 -5
  13. package/dist/agents/agents.service.js +49 -13
  14. package/dist/agents/definition.d.ts +35 -8
  15. package/dist/agents/definition.js +203 -17
  16. package/dist/agents/instruction-files.d.ts +60 -0
  17. package/dist/agents/instruction-files.js +111 -0
  18. package/dist/agents/models.controller.d.ts +9 -3
  19. package/dist/agents/models.controller.js +19 -4
  20. package/dist/agents/profile.d.ts +96 -0
  21. package/dist/agents/profile.js +51 -0
  22. package/dist/agents/readiness.controller.d.ts +26 -0
  23. package/dist/agents/readiness.controller.js +56 -0
  24. package/dist/agents/templates.js +20 -5
  25. package/dist/background/background.controller.d.ts +38 -0
  26. package/dist/background/background.controller.js +87 -0
  27. package/dist/background/background.module.d.ts +10 -0
  28. package/dist/background/background.module.js +32 -0
  29. package/dist/background/finish.d.ts +43 -0
  30. package/dist/background/finish.js +94 -0
  31. package/dist/background/launch-lock.d.ts +14 -0
  32. package/dist/background/launch-lock.js +39 -0
  33. package/dist/background/launch.d.ts +36 -0
  34. package/dist/background/launch.js +70 -0
  35. package/dist/background/poll.job.d.ts +53 -0
  36. package/dist/background/poll.job.js +182 -0
  37. package/dist/background/render.d.ts +23 -0
  38. package/dist/background/render.js +48 -0
  39. package/dist/background/session.d.ts +37 -0
  40. package/dist/background/session.js +66 -0
  41. package/dist/background/status.d.ts +35 -0
  42. package/dist/background/status.js +57 -0
  43. package/dist/background/stop.d.ts +62 -0
  44. package/dist/background/stop.js +225 -0
  45. package/dist/background/store.d.ts +150 -0
  46. package/dist/background/store.js +351 -0
  47. package/dist/background/store.module.d.ts +6 -0
  48. package/dist/background/store.module.js +22 -0
  49. package/dist/background/tasks.service.d.ts +67 -0
  50. package/dist/background/tasks.service.js +186 -0
  51. package/dist/background/wake-rules.d.ts +57 -0
  52. package/dist/background/wake-rules.js +83 -0
  53. package/dist/background/wake.d.ts +28 -0
  54. package/dist/background/wake.js +57 -0
  55. package/dist/catalog/catalog-content.js +4 -1
  56. package/dist/catalog/claude/agents.js +15 -10
  57. package/dist/catalog/marketplaces.service.d.ts +1 -1
  58. package/dist/catalog/plugin-import.js +1 -1
  59. package/dist/catalog/plugin-upgrade.js +1 -1
  60. package/dist/channels/cards.d.ts +8 -2
  61. package/dist/channels/cards.js +17 -9
  62. package/dist/channels/connections.service.d.ts +16 -1
  63. package/dist/channels/connections.service.js +38 -4
  64. package/dist/channels/threads.d.ts +5 -0
  65. package/dist/channels/threads.js +8 -0
  66. package/dist/config/schema.d.ts +18 -0
  67. package/dist/config/schema.js +104 -2
  68. package/dist/conversations/access.js +2 -0
  69. package/dist/conversations/artifacts.d.ts +1 -1
  70. package/dist/conversations/artifacts.js +1 -1
  71. package/dist/conversations/attachments.service.d.ts +30 -0
  72. package/dist/conversations/attachments.service.js +130 -5
  73. package/dist/conversations/conversations.controller.d.ts +36 -5
  74. package/dist/conversations/conversations.controller.js +62 -12
  75. package/dist/conversations/conversations.module.js +3 -0
  76. package/dist/conversations/conversations.service.d.ts +44 -2
  77. package/dist/conversations/conversations.service.js +140 -12
  78. package/dist/conversations/event-log.d.ts +12 -1
  79. package/dist/conversations/event-log.js +29 -1
  80. package/dist/conversations/events.d.ts +81 -3
  81. package/dist/conversations/events.js +83 -16
  82. package/dist/conversations/queue.d.ts +3 -1
  83. package/dist/conversations/queue.js +13 -2
  84. package/dist/conversations/queue.service.js +3 -1
  85. package/dist/conversations/titles.d.ts +7 -0
  86. package/dist/conversations/titles.js +15 -0
  87. package/dist/db/kernel-schema.d.ts +60 -4
  88. package/dist/db/pool.d.ts +5 -0
  89. package/dist/db/pool.js +3 -0
  90. package/dist/errors.d.ts +8 -0
  91. package/dist/errors.js +8 -0
  92. package/dist/evals/recorded-tools.d.ts +27 -1
  93. package/dist/evals/recorded-tools.js +85 -10
  94. package/dist/evals/run-replay.d.ts +10 -2
  95. package/dist/evals/run-replay.js +49 -12
  96. package/dist/evals/step-replay.js +12 -6
  97. package/dist/evals/trajectory.d.ts +2 -2
  98. package/dist/evals/trajectory.js +1 -1
  99. package/dist/extensions/admin-ops.js +2 -0
  100. package/dist/extensions/admin-view.d.ts +3 -1
  101. package/dist/extensions/admin-view.js +10 -2
  102. package/dist/extensions/blobs.js +1 -0
  103. package/dist/extensions/config-issues.d.ts +13 -1
  104. package/dist/extensions/config-issues.js +22 -5
  105. package/dist/extensions/config-json-schema.d.ts +43 -7
  106. package/dist/extensions/config-json-schema.js +124 -58
  107. package/dist/extensions/deployment-config-store.d.ts +6 -0
  108. package/dist/extensions/deployment-config.d.ts +50 -3
  109. package/dist/extensions/deployment-config.js +125 -28
  110. package/dist/extensions/display-name.d.ts +22 -0
  111. package/dist/extensions/display-name.js +77 -0
  112. package/dist/extensions/extensions.module.js +6 -0
  113. package/dist/extensions/host.d.ts +26 -1
  114. package/dist/extensions/host.js +82 -9
  115. package/dist/extensions/org-config.controller.d.ts +11 -3
  116. package/dist/extensions/org-config.controller.js +14 -6
  117. package/dist/extensions/org-config.d.ts +6 -0
  118. package/dist/extensions/org-config.js +12 -13
  119. package/dist/extensions/package.d.ts +14 -1
  120. package/dist/extensions/package.js +51 -2
  121. package/dist/extensions/permission-view.d.ts +3 -0
  122. package/dist/extensions/point-defaults.controller.d.ts +34 -0
  123. package/dist/extensions/point-defaults.controller.js +71 -0
  124. package/dist/extensions/point-defaults.d.ts +64 -0
  125. package/dist/extensions/point-defaults.js +206 -0
  126. package/dist/extensions/registry.d.ts +1 -1
  127. package/dist/extensions/registry.js +2 -0
  128. package/dist/http/api.module.js +8 -0
  129. package/dist/http/nest-logger.d.ts +6 -5
  130. package/dist/http/nest-logger.js +24 -10
  131. package/dist/identity/auth.controller.js +9 -5
  132. package/dist/identity/cookies.d.ts +20 -5
  133. package/dist/identity/cookies.js +44 -6
  134. package/dist/identity/me.controller.d.ts +5 -1
  135. package/dist/identity/me.controller.js +12 -3
  136. package/dist/identity/sessions.d.ts +12 -0
  137. package/dist/identity/sessions.js +45 -0
  138. package/dist/identity/settings.js +1 -1
  139. package/dist/index.d.ts +1 -1
  140. package/dist/infra/config/kernel-config.service.js +6 -0
  141. package/dist/infra/database/database.module.js +1 -0
  142. package/dist/infra/events/kernel-events.d.ts +8 -0
  143. package/dist/infra/lifecycle/process-guards.js +4 -0
  144. package/dist/infra/logging/logging.module.d.ts +4 -2
  145. package/dist/infra/logging/logging.module.js +24 -6
  146. package/dist/infra/queue/redis-connection.d.ts +6 -0
  147. package/dist/infra/queue/redis-connection.js +7 -0
  148. package/dist/infra/redis/redis.module.js +6 -2
  149. package/dist/infra/tokens.d.ts +6 -1
  150. package/dist/infra/tokens.js +6 -1
  151. package/dist/lifecycle/kernel-lifecycle.js +5 -0
  152. package/dist/log.d.ts +13 -1
  153. package/dist/log.js +93 -17
  154. package/dist/models/purposes.d.ts +23 -0
  155. package/dist/models/purposes.js +31 -10
  156. package/dist/orgs/offboarding.d.ts +3 -1
  157. package/dist/orgs/offboarding.js +26 -16
  158. package/dist/orgs/settings.service.d.ts +7 -2
  159. package/dist/orgs/settings.service.js +15 -4
  160. package/dist/roles/worker.module.js +6 -0
  161. package/dist/runtime/context/build.d.ts +15 -13
  162. package/dist/runtime/context/build.js +45 -27
  163. package/dist/runtime/context/claude-tools.d.ts +3 -1
  164. package/dist/runtime/context/claude-tools.js +12 -12
  165. package/dist/runtime/context/compaction.js +4 -0
  166. package/dist/runtime/context/legacy-text.d.ts +22 -0
  167. package/dist/runtime/context/legacy-text.js +37 -0
  168. package/dist/runtime/context/skills.d.ts +29 -11
  169. package/dist/runtime/context/skills.js +37 -30
  170. package/dist/runtime/langgraph-loop.js +3 -2
  171. package/dist/runtime/loop.d.ts +5 -4
  172. package/dist/runtime/maintenance.d.ts +4 -2
  173. package/dist/runtime/maintenance.js +17 -5
  174. package/dist/runtime/model-call.d.ts +17 -1
  175. package/dist/runtime/model-call.js +4 -3
  176. package/dist/runtime/providers.d.ts +6 -0
  177. package/dist/runtime/providers.js +8 -0
  178. package/dist/runtime/run-finish.d.ts +1 -1
  179. package/dist/runtime/run-queue.d.ts +28 -5
  180. package/dist/runtime/run-queue.js +131 -35
  181. package/dist/runtime/run-worker.d.ts +2 -2
  182. package/dist/runtime/run-worker.js +20 -7
  183. package/dist/runtime/runtime.module.js +8 -3
  184. package/dist/runtime/step-log.d.ts +6 -0
  185. package/dist/runtime/step-log.js +16 -2
  186. package/dist/runtime/subagents/definition.d.ts +1 -6
  187. package/dist/runtime/subagents/definition.js +14 -13
  188. package/dist/runtime/subagents/dispatch.d.ts +2 -2
  189. package/dist/runtime/subagents/dispatch.js +2 -2
  190. package/dist/runtime/subagents/org-agents.js +2 -1
  191. package/dist/runtime/subagents/roster.d.ts +1 -1
  192. package/dist/runtime/subagents/roster.js +1 -1
  193. package/dist/runtime/subagents/skill-fork.d.ts +2 -2
  194. package/dist/runtime/subagents/skill-fork.js +2 -2
  195. package/dist/runtime/subagents/subagents.service.d.ts +3 -3
  196. package/dist/runtime/subagents/subagents.service.js +3 -3
  197. package/dist/runtime/usage-report.d.ts +2 -1
  198. package/dist/runtime/usage-report.js +2 -1
  199. package/dist/runtime/waits.d.ts +13 -7
  200. package/dist/runtime/waits.js +42 -16
  201. package/dist/sandbox/sandbox.module.js +6 -1
  202. package/dist/sandbox/sandbox.service.d.ts +61 -3
  203. package/dist/sandbox/sandbox.service.js +141 -7
  204. package/dist/skills/catalog.d.ts +4 -4
  205. package/dist/skills/catalog.js +4 -4
  206. package/dist/skills/changes.service.d.ts +2 -2
  207. package/dist/skills/changes.service.js +5 -4
  208. package/dist/skills/invocation-keys.d.ts +1 -1
  209. package/dist/skills/invocation-keys.js +1 -1
  210. package/dist/skills/invoke.d.ts +1 -1
  211. package/dist/skills/invoke.js +5 -4
  212. package/dist/tools/approvals.service.d.ts +6 -0
  213. package/dist/tools/approvals.service.js +21 -1
  214. package/dist/tools/builtin-names.d.ts +91 -0
  215. package/dist/tools/builtin-names.js +143 -0
  216. package/dist/tools/builtins/agent.d.ts +7 -7
  217. package/dist/tools/builtins/agent.js +7 -11
  218. package/dist/tools/builtins/ask-user.d.ts +1 -1
  219. package/dist/tools/builtins/ask-user.js +2 -2
  220. package/dist/tools/builtins/builtin-tools.d.ts +17 -3
  221. package/dist/tools/builtins/builtin-tools.js +36 -4
  222. package/dist/tools/builtins/read-tool-output.d.ts +1 -1
  223. package/dist/tools/builtins/read-tool-output.js +2 -2
  224. package/dist/tools/builtins/sandbox/bash.d.ts +4 -4
  225. package/dist/tools/builtins/sandbox/bash.js +259 -29
  226. package/dist/tools/builtins/sandbox/common.d.ts +30 -7
  227. package/dist/tools/builtins/sandbox/common.js +9 -5
  228. package/dist/tools/builtins/sandbox/deliver.d.ts +1 -1
  229. package/dist/tools/builtins/sandbox/deliver.js +12 -12
  230. package/dist/tools/builtins/sandbox/edit.d.ts +1 -1
  231. package/dist/tools/builtins/sandbox/edit.js +41 -30
  232. package/dist/tools/builtins/sandbox/file-state.d.ts +34 -0
  233. package/dist/tools/builtins/sandbox/file-state.js +34 -0
  234. package/dist/tools/builtins/sandbox/glob-match.d.ts +1 -1
  235. package/dist/tools/builtins/sandbox/glob-match.js +1 -1
  236. package/dist/tools/builtins/sandbox/glob.d.ts +1 -1
  237. package/dist/tools/builtins/sandbox/glob.js +4 -4
  238. package/dist/tools/builtins/sandbox/grep.d.ts +7 -4
  239. package/dist/tools/builtins/sandbox/grep.js +147 -30
  240. package/dist/tools/builtins/sandbox/index.d.ts +3 -1
  241. package/dist/tools/builtins/sandbox/index.js +7 -1
  242. package/dist/tools/builtins/sandbox/read.d.ts +2 -2
  243. package/dist/tools/builtins/sandbox/read.js +21 -14
  244. package/dist/tools/builtins/sandbox/task-output.d.ts +63 -0
  245. package/dist/tools/builtins/sandbox/task-output.js +304 -0
  246. package/dist/tools/builtins/sandbox/task-stop.d.ts +10 -0
  247. package/dist/tools/builtins/sandbox/task-stop.js +112 -0
  248. package/dist/tools/builtins/sandbox/write.d.ts +1 -1
  249. package/dist/tools/builtins/sandbox/write.js +37 -33
  250. package/dist/tools/builtins/skill-load.d.ts +3 -3
  251. package/dist/tools/builtins/skill-load.js +32 -16
  252. package/dist/tools/builtins/skill-write.d.ts +4 -4
  253. package/dist/tools/builtins/skill-write.js +10 -10
  254. package/dist/tools/builtins/tool-search.d.ts +13 -9
  255. package/dist/tools/builtins/tool-search.js +25 -14
  256. package/dist/tools/builtins/web/errors.d.ts +8 -0
  257. package/dist/tools/builtins/web/errors.js +53 -0
  258. package/dist/tools/builtins/web/extract.d.ts +62 -0
  259. package/dist/tools/builtins/web/extract.js +267 -0
  260. package/dist/tools/builtins/web/fetch.d.ts +55 -0
  261. package/dist/tools/builtins/web/fetch.js +174 -0
  262. package/dist/tools/builtins/web/index.d.ts +13 -0
  263. package/dist/tools/builtins/web/index.js +12 -0
  264. package/dist/tools/builtins/web/page-cache.d.ts +25 -0
  265. package/dist/tools/builtins/web/page-cache.js +77 -0
  266. package/dist/tools/builtins/web/search.d.ts +25 -0
  267. package/dist/tools/builtins/web/search.js +209 -0
  268. package/dist/tools/builtins/web/url.d.ts +17 -0
  269. package/dist/tools/builtins/web/url.js +43 -0
  270. package/dist/tools/connectors.service.js +4 -4
  271. package/dist/tools/display-names.d.ts +9 -0
  272. package/dist/tools/display-names.js +24 -0
  273. package/dist/tools/drift.js +5 -5
  274. package/dist/tools/elicitation.js +1 -1
  275. package/dist/tools/info.d.ts +20 -4
  276. package/dist/tools/info.js +55 -32
  277. package/dist/tools/legacy-data.d.ts +34 -0
  278. package/dist/tools/legacy-data.js +76 -0
  279. package/dist/tools/legacy-keys.d.ts +51 -0
  280. package/dist/tools/legacy-keys.js +115 -0
  281. package/dist/tools/naming.d.ts +1 -5
  282. package/dist/tools/naming.js +1 -7
  283. package/dist/tools/oauth/flow-paths.d.ts +1 -1
  284. package/dist/tools/oauth/flow-paths.js +1 -1
  285. package/dist/tools/outputs.js +1 -1
  286. package/dist/tools/pipeline.d.ts +13 -7
  287. package/dist/tools/pipeline.js +112 -39
  288. package/dist/tools/policy.d.ts +1 -14
  289. package/dist/tools/policy.js +13 -19
  290. package/dist/tools/questions.controller.d.ts +1 -1
  291. package/dist/tools/questions.controller.js +1 -1
  292. package/dist/tools/questions.service.d.ts +1 -1
  293. package/dist/tools/registry.d.ts +38 -24
  294. package/dist/tools/registry.js +54 -47
  295. package/dist/tools/resources.d.ts +1 -1
  296. package/dist/tools/resources.js +1 -1
  297. package/dist/tools/tools.module.js +13 -1
  298. package/dist/tools/validate.js +1 -1
  299. package/package.json +3 -3
  300. package/src/migrations/0063_tool_keys_claude_code.sql +220 -0
  301. package/src/migrations/0064_account_last_org.sql +7 -0
  302. package/src/migrations/0065_conversation_archive.sql +8 -0
  303. package/src/migrations/0066_extension_display_name.sql +4 -0
  304. package/src/migrations/0067_agent_profiles.sql +20 -0
  305. package/src/migrations/0068_agent_avatar_uploads.sql +7 -0
  306. package/src/migrations/0069_attachments_agent_idx.sql +3 -0
  307. package/src/migrations/0070_background_tasks.sql +37 -0
  308. package/src/migrations/0071_background_tasks_reserve.sql +6 -0
  309. package/src/migrations/0072_background_tasks_stopping.sql +5 -0
  310. package/src/migrations/0073_org_extension_point_defaults.sql +11 -0
  311. package/dist/tools/builtins/sandbox/names.d.ts +0 -7
  312. package/dist/tools/builtins/sandbox/names.js +0 -20
@@ -68,7 +68,7 @@ export const DEPLOYMENT_SETTINGS = z.object({
68
68
  /** 组织能否自建连接器(不经企业目录)。 */
69
69
  customConnectors: CustomPolicy.default("allow"),
70
70
  /**
71
- * 组织能否自建技能(不经企业目录):deny 时组织管理员不能上传技能包。agent 自写技能(`skill_write`,私有 agent 的
71
+ * 组织能否自建技能(不经企业目录):deny 时组织管理员不能上传技能包。agent 自写技能(`SkillWrite`,私有 agent 的
72
72
  * agent 级技能与共享 agent 经负责人审批的变更)不受此开关约束——那是 agent 定义的一部分,由 R3 / R4 把关。
73
73
  */
74
74
  customSkills: CustomPolicy.default("allow"),
package/dist/index.d.ts CHANGED
@@ -31,7 +31,7 @@ export { type ApprovalDecision, ExtensionApprovals, type PendingExtension, type
31
31
  export { blobKey, ExtensionBlobIntegrityError, type ExtensionBlobSource, ExtensionBlobs, } from "./extensions/blobs.js";
32
32
  export { BOOTSTRAP_MANIFEST_FILE, type BootstrapEntryResult, BootstrapFailedError, type BootstrapFailure, type BootstrapResolveOptions, type BootstrapResult, BootstrapService, bootstrapManifestSha256, readBootstrapManifest, resolveBootstrapPackages, } from "./extensions/bootstrap.js";
33
33
  export { type ExtensionCli, openExtensionCli } from "./extensions/cli.js";
34
- export { DEPLOYMENT_SECRETS_ORG, type DeploymentConfigField, type DeploymentConfigView, ExtensionDeploymentConfigService, mergeDeploymentConfig, PLUGIN_DEPLOYMENT_CONFIG_TOPIC, readStoredDeploymentConfig, } from "./extensions/deployment-config.js";
34
+ export { DEPLOYMENT_SECRETS_ORG, type DeploymentConfigField, type DeploymentConfigView, ExtensionDeploymentConfigService, mergeDeploymentConfig, PLUGIN_DEPLOYMENT_CONFIG_TOPIC, readStoredDeploymentConfig, type SecretEntry, } from "./extensions/deployment-config.js";
35
35
  export { assertSecretsReachable, ExtensionHost, type ExtensionLoadPlan, type InspectedExtension, type InstalledExtensionLoader, type InstallFailureReason, } from "./extensions/host.js";
36
36
  export { ExtensionUnloadedError, type InflightEntry, InflightTracker, PLUGIN_UNLOADED_TOOL_ERROR, type TrackOptions, trackCalls, } from "./extensions/inflight.js";
37
37
  export { type DesiredExtension, type ExtensionInstallRow, ExtensionInstalls, type IngestSource, type PackageReview, } from "./extensions/installs.js";
@@ -34,6 +34,12 @@ const HOT_APPLIERS = {
34
34
  "subagents.maxConcurrentPerRun": () => { },
35
35
  "subagents.maxDispatchesPerRun": () => { },
36
36
  "subagents.maxTokensPerRun": () => { },
37
+ // The web tools read these limits through the config service on every call.
38
+ "web.search.maxResults": () => { },
39
+ "web.fetch.maxPageChars": () => { },
40
+ "web.fetch.maxAnswerTokens": () => { },
41
+ "web.fetch.cacheSeconds": () => { },
42
+ "web.fetch.timeoutMs": () => { },
37
43
  };
38
44
  const HOT_PATHS = configFields(KernelConfigSchema)
39
45
  .filter((f) => f.meta?.hot)
@@ -66,6 +66,7 @@ let DatabaseModule = class DatabaseModule {
66
66
  const { db } = config.get();
67
67
  return createDb(db.url, db.poolMax, (err) => log.error("pg idle client error", { err }), {
68
68
  connectTimeoutMs: db.connectTimeoutMs,
69
+ idleTimeoutMs: db.idleTimeoutMs,
69
70
  onClientError: (err) => log.warn("pg client error", { err }),
70
71
  });
71
72
  },
@@ -78,6 +78,14 @@ export interface KernelEventMap {
78
78
  "run.orphaned": {
79
79
  runId: string;
80
80
  };
81
+ /**
82
+ * A background task of the conversation finished (after the commit of its
83
+ * `background_task_finished` event). `BackgroundWake` asks the run queue for a wake run; a lost
84
+ * event is re-derived by the next `ensureRun` (a message, the end of a run, a page opening).
85
+ */
86
+ "background_task.finished": {
87
+ conversationId: string;
88
+ };
81
89
  }
82
90
  type Handler<K extends keyof KernelEventMap> = (payload: KernelEventMap[K]) => Promise<void> | void;
83
91
  /** Typed in-process event emitter; handlers run in order, a failing one is logged and skipped. */
@@ -39,6 +39,10 @@ export function createFatalRecorder(deps) {
39
39
  let recent = [];
40
40
  let open = false;
41
41
  return (kind, err) => {
42
+ // Exiting already: log nothing more. A failing log write (EPIPE on a dead stdout) raises the
43
+ // next event itself; logging it again would keep the process busy forever and starve the exit.
44
+ if (open)
45
+ return;
42
46
  try {
43
47
  const t = now();
44
48
  recent = recent.filter((x) => t - x < GUARD_CIRCUIT_WINDOW_MS);
@@ -6,8 +6,10 @@ export interface LoggingModuleOptions {
6
6
  logger?: KernelLogger;
7
7
  }
8
8
  /**
9
- * Provides the process logger as `KERNEL_LOGGER`. The logger is created before the Nest context
10
- * (it must exist for startup errors), so it is passed in or derived from the startup config here.
9
+ * Provides the process logger as `KERNEL_ROOT_LOGGER` and, as `KERNEL_LOGGER`, one child per injecting
10
+ * class with `context` = the class name (the `[context=…]` of the pretty output; octok's
11
+ * per-class Nest logger context). The logger is created before the Nest context (it must exist for
12
+ * startup errors), so it is passed in or derived from the startup config here.
11
13
  */
12
14
  export declare class LoggingModule {
13
15
  static forRoot(config: KernelConfig, opts?: LoggingModuleOptions): DynamicModule;
@@ -5,12 +5,15 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  var LoggingModule_1;
8
- import { Global, Module } from "@nestjs/common";
8
+ import { Global, Module, Scope } from "@nestjs/common";
9
+ import { INQUIRER } from "@nestjs/core";
9
10
  import { createConfiguredLogger } from "../../log-config.js";
10
- import { KERNEL_LOGGER } from "../tokens.js";
11
+ import { KERNEL_LOGGER, KERNEL_ROOT_LOGGER } from "../tokens.js";
11
12
  /**
12
- * Provides the process logger as `KERNEL_LOGGER`. The logger is created before the Nest context
13
- * (it must exist for startup errors), so it is passed in or derived from the startup config here.
13
+ * Provides the process logger as `KERNEL_ROOT_LOGGER` and, as `KERNEL_LOGGER`, one child per injecting
14
+ * class with `context` = the class name (the `[context=…]` of the pretty output; octok's
15
+ * per-class Nest logger context). The logger is created before the Nest context (it must exist for
16
+ * startup errors), so it is passed in or derived from the startup config here.
14
17
  */
15
18
  let LoggingModule = LoggingModule_1 = class LoggingModule {
16
19
  static forRoot(config, opts = {}) {
@@ -21,8 +24,16 @@ let LoggingModule = LoggingModule_1 = class LoggingModule {
21
24
  return {
22
25
  module: LoggingModule_1,
23
26
  global: true,
24
- providers: [{ provide: KERNEL_LOGGER, useValue: logger }],
25
- exports: [KERNEL_LOGGER],
27
+ providers: [
28
+ { provide: KERNEL_ROOT_LOGGER, useValue: logger },
29
+ {
30
+ provide: KERNEL_LOGGER,
31
+ scope: Scope.TRANSIENT,
32
+ inject: [KERNEL_ROOT_LOGGER, INQUIRER],
33
+ useFactory: (root, inquirer) => contextLogger(root, inquirer),
34
+ },
35
+ ],
36
+ exports: [KERNEL_ROOT_LOGGER, KERNEL_LOGGER],
26
37
  };
27
38
  }
28
39
  };
@@ -32,3 +43,10 @@ LoggingModule = LoggingModule_1 = __decorate([
32
43
  // biome-ignore lint/complexity/noStaticOnlyClass: a Nest dynamic module
33
44
  ], LoggingModule);
34
45
  export { LoggingModule };
46
+ /** The child of `root` for the class instance `inquirer`; `root` itself for a factory provider. */
47
+ function contextLogger(root, inquirer) {
48
+ const name = inquirer?.constructor?.name;
49
+ return typeof name === "string" && name !== "" && name !== "Object"
50
+ ? root.child({ context: name })
51
+ : root;
52
+ }
@@ -1,4 +1,10 @@
1
1
  import type { RedisOptions } from "bullmq";
2
+ /**
3
+ * TCP keepalive of every long-lived kernel Redis connection: probes start after 10 s idle (the OS
4
+ * default is 2 hours), so a connection a dropped network left half-open is found and reconnected
5
+ * before a command waits on it, not at the next write (EPIPE).
6
+ */
7
+ export declare const REDIS_KEEPALIVE_MS = 10000;
2
8
  /**
3
9
  * BullMQ connection options from the kernel's Redis URL, the same connection the hand-made
4
10
  * `new Redis(url, { maxRetriesPerRequest: null })` workers use: BullMQ requires
@@ -1,3 +1,9 @@
1
+ /**
2
+ * TCP keepalive of every long-lived kernel Redis connection: probes start after 10 s idle (the OS
3
+ * default is 2 hours), so a connection a dropped network left half-open is found and reconnected
4
+ * before a command waits on it, not at the next write (EPIPE).
5
+ */
6
+ export const REDIS_KEEPALIVE_MS = 10_000;
1
7
  /**
2
8
  * BullMQ connection options from the kernel's Redis URL, the same connection the hand-made
3
9
  * `new Redis(url, { maxRetriesPerRequest: null })` workers use: BullMQ requires
@@ -14,5 +20,6 @@ export function bullConnectionOptions(url) {
14
20
  ...(db !== undefined && Number.isInteger(db) && { db }),
15
21
  ...(u.protocol === "rediss:" && { tls: {} }),
16
22
  maxRetriesPerRequest: null,
23
+ keepAlive: REDIS_KEEPALIVE_MS,
17
24
  };
18
25
  }
@@ -14,6 +14,7 @@ import { closeWithin } from "../close-within.js";
14
14
  import { KernelConfigService } from "../config/kernel-config.service.js";
15
15
  import { GracefulShutdownModule } from "../lifecycle/graceful-shutdown.module.js";
16
16
  import { GracefulShutdown } from "../lifecycle/graceful-shutdown.service.js";
17
+ import { REDIS_KEEPALIVE_MS } from "../queue/redis-connection.js";
17
18
  import { KERNEL_BUS, KERNEL_LOGGER, REDIS, REDIS_SUBSCRIBER } from "../tokens.js";
18
19
  /**
19
20
  * Closes the bus subscriber, then the main connection, at the end of the shutdown
@@ -64,7 +65,10 @@ RedisModule = __decorate([
64
65
  provide: REDIS,
65
66
  inject: [KernelConfigService, KERNEL_LOGGER],
66
67
  useFactory: (config, log) => {
67
- const redis = new Redis(config.get().redis.url, { lazyConnect: true });
68
+ const redis = new Redis(config.get().redis.url, {
69
+ lazyConnect: true,
70
+ keepAlive: REDIS_KEEPALIVE_MS,
71
+ });
68
72
  redis.on("error", (err) => log.error("redis connection error", { err }));
69
73
  return redis;
70
74
  },
@@ -72,7 +76,7 @@ RedisModule = __decorate([
72
76
  {
73
77
  provide: REDIS_SUBSCRIBER,
74
78
  inject: [KernelConfigService, KERNEL_LOGGER],
75
- useFactory: (config, log) => createBusSubscriber(() => new Redis(config.get().redis.url), log),
79
+ useFactory: (config, log) => createBusSubscriber(() => new Redis(config.get().redis.url, { keepAlive: REDIS_KEEPALIVE_MS }), log),
76
80
  },
77
81
  {
78
82
  provide: KERNEL_BUS,
@@ -1,6 +1,11 @@
1
1
  /** Injection tokens of the global infra modules. */
2
- /** The process logger (`KernelLogger`). */
2
+ /**
3
+ * A class's logger (`KernelLogger`): the process logger with `context` set to the injecting class's
4
+ * name. Transient, so `app.get` cannot resolve it; use `KERNEL_ROOT_LOGGER` outside an injection.
5
+ */
3
6
  export declare const KERNEL_LOGGER: unique symbol;
7
+ /** The process logger itself (`KernelLogger`), without a context. */
8
+ export declare const KERNEL_ROOT_LOGGER: unique symbol;
4
9
  /** The startup input of `KernelConfigService`: `{ config, env }`. */
5
10
  export declare const KERNEL_CONFIG_INPUT: unique symbol;
6
11
  /** The Kysely pool, untyped (`Kysely<any>`); infra only, services inject `KernelDb`. */
@@ -1,6 +1,11 @@
1
1
  /** Injection tokens of the global infra modules. */
2
- /** The process logger (`KernelLogger`). */
2
+ /**
3
+ * A class's logger (`KernelLogger`): the process logger with `context` set to the injecting class's
4
+ * name. Transient, so `app.get` cannot resolve it; use `KERNEL_ROOT_LOGGER` outside an injection.
5
+ */
3
6
  export const KERNEL_LOGGER = Symbol("KERNEL_LOGGER");
7
+ /** The process logger itself (`KernelLogger`), without a context. */
8
+ export const KERNEL_ROOT_LOGGER = Symbol("KERNEL_ROOT_LOGGER");
4
9
  /** The startup input of `KernelConfigService`: `{ config, env }`. */
5
10
  export const KERNEL_CONFIG_INPUT = Symbol("KERNEL_CONFIG_INPUT");
6
11
  /** The Kysely pool, untyped (`Kysely<any>`); infra only, services inject `KernelDb`. */
@@ -36,6 +36,7 @@ import { GatewayRole } from "../roles/gateway-role.js";
36
36
  import { WorkerRole } from "../roles/worker-role.js";
37
37
  import { UsageReporter } from "../runtime/usage-report.js";
38
38
  import { sandboxConfigWarnings } from "../sandbox/sandbox.service.js";
39
+ import { warnLegacyToolKeys } from "../tools/legacy-data.js";
39
40
  import { KERNEL_APP_OPTIONS } from "./tokens.js";
40
41
  /**
41
42
  * The stop phase budgets. Each phase waits `min(its budget, what is left of the total)`, where the
@@ -193,6 +194,10 @@ let KernelLifecycle = class KernelLifecycle {
193
194
  // In flight for the stop: aborted when the shutdown begins, which then waits for its session to end
194
195
  await this.shutdown.trackInFlight(migrate(config.db.url, { schema: KERNEL_SCHEMA, dir: KERNEL_MIGRATIONS_DIR }, { connectTimeoutMs: config.db.connectTimeoutMs, signal: this.shutdown.signal }));
195
196
  this.checkpoint();
197
+ // Rows still holding a release-candidate tool key that migration 0063 could not map: one
198
+ // read-only count, a WARN when there are any (tool naming spec §8.2); never blocks the start
199
+ await warnLegacyToolKeys(this.kdb.db, this.log).catch((e) => this.log.warn("tools.legacy_key_count_failed", { error: errMsg(e) }));
200
+ this.checkpoint();
196
201
  // official 清单来源按配置同步(幂等;每个副本启动时都做)
197
202
  await this.marketplaces.syncOfficialFromConfig();
198
203
  // The bootstrap plugins (spec §7), once per manifest across replicas (the Redis lock
package/dist/log.d.ts CHANGED
@@ -30,6 +30,11 @@ export interface KernelLogger extends Logger {
30
30
  redact(text: string): string;
31
31
  /** Waits until buffered records reached every output (Loki); never rejects. Call before exit. */
32
32
  flush(): Promise<void>;
33
+ /**
34
+ * A logger that adds `fields` to every record (e.g. `{ context: "RunWorker" }`); call fields cannot
35
+ * overwrite them. It shares everything else with this logger: level, secrets, outputs.
36
+ */
37
+ child(fields: Record<string, string>): KernelLogger;
33
38
  }
34
39
  /**
35
40
  * 用与 logger 相同的匹配器打码任意文本(工具结果等不经过日志的内容用):展开规则同 `addOrgSecrets`
@@ -37,13 +42,15 @@ export interface KernelLogger extends Logger {
37
42
  * 不得小于匹配器的分桶前缀长度 4)。没有可用的值时原样返回。
38
43
  */
39
44
  export declare function createRedactor(values: readonly unknown[], minLength?: number): (s: string) => string;
40
- /** Console format: `pretty` (pino-pretty, one line per record), `json`, or `auto` (pretty on a TTY). */
45
+ /** Console format: `pretty` (pino-pretty), `json`, or `auto` (pretty on a TTY). */
41
46
  export declare const LOG_FORMATS: readonly ["auto", "pretty", "json"];
42
47
  /** Console format. */
43
48
  export type LogFormat = (typeof LOG_FORMATS)[number];
44
49
  /** A minimal writable the console output goes to (`process.stdout` by default). */
45
50
  export interface LogOutput {
46
51
  write(chunk: string): unknown;
52
+ /** A stream's `error` event (EPIPE when its reader is gone). */
53
+ on?(event: "error", listener: (err: unknown) => void): unknown;
47
54
  }
48
55
  /** Options of {@link createLogger}. */
49
56
  export interface LoggerOptions {
@@ -58,6 +65,11 @@ export interface LoggerOptions {
58
65
  out?: LogOutput;
59
66
  /** Whether the console is a terminal (picks `auto` and colors); default `process.stdout.isTTY`. */
60
67
  isTTY?: boolean;
68
+ /**
69
+ * Pretty output in colors; default: `FORCE_COLOR` (set and not `0`) turns them on, `NO_COLOR` off,
70
+ * else a TTY. `pnpm dev:kernel` forces them, since its output goes through `tee`.
71
+ */
72
+ colors?: boolean;
61
73
  /** Also push every record to Loki. */
62
74
  loki?: LokiOptions;
63
75
  /** Fields on every record (e.g. `role`); log calls cannot overwrite them. */
package/dist/log.js CHANGED
@@ -105,15 +105,16 @@ function expandSecret(value) {
105
105
  }
106
106
  return out;
107
107
  }
108
- /** Console format: `pretty` (pino-pretty, one line per record), `json`, or `auto` (pretty on a TTY). */
108
+ /** Console format: `pretty` (pino-pretty), `json`, or `auto` (pretty on a TTY). */
109
109
  export const LOG_FORMATS = ["auto", "pretty", "json"];
110
- /** Fields shown in the pretty prefix instead of the trailing object, in this order. */
111
- const PREFIX_FIELDS = ["reqId", "traceId", "jobId", "runId", "plugin"];
112
- /** pino-pretty's message format: `[role] [reqId=…] [plugin=…] msg`, each part only when present. */
110
+ /** Fields shown in the pretty prefix instead of the lines below, in this order. */
111
+ const PREFIX_FIELDS = ["traceId", "reqId", "queue", "jobId", "runId", "context", "plugin"];
112
+ /**
113
+ * pino-pretty's message format, the octok layout:
114
+ * `[pid=…] - [traceId=…] [reqId=…] [context=…] msg`, each bracket only when present.
115
+ */
113
116
  function prettyMessage(log, messageKey) {
114
- const parts = [];
115
- if (typeof log.role === "string")
116
- parts.push(`[${log.role}]`);
117
+ const parts = [`[pid=${process.pid}] -`];
117
118
  for (const k of PREFIX_FIELDS) {
118
119
  const v = log[k];
119
120
  if (typeof v === "string" || typeof v === "number")
@@ -122,6 +123,24 @@ function prettyMessage(log, messageKey) {
122
123
  parts.push(String(log[messageKey] ?? ""));
123
124
  return parts.join(" ");
124
125
  }
126
+ /** The HTTP access record (`http/app.ts`) as one sentence: method, path, status and duration. */
127
+ function foldAccessLine(log) {
128
+ if (log.msg !== "request" || typeof log.method !== "string" || typeof log.url !== "string") {
129
+ return log;
130
+ }
131
+ const { method, url, status, ms, ...rest } = log;
132
+ return { ...rest, msg: `${method} ${url} ${status} ${ms}ms` };
133
+ }
134
+ function colorsOn(opts, isTTY) {
135
+ if (opts.colors !== undefined)
136
+ return opts.colors;
137
+ const force = process.env.FORCE_COLOR;
138
+ if (force !== undefined && force !== "0")
139
+ return true;
140
+ if (process.env.NO_COLOR)
141
+ return false;
142
+ return isTTY;
143
+ }
125
144
  function consoleStream(opts) {
126
145
  if (opts.write) {
127
146
  const write = opts.write;
@@ -132,23 +151,50 @@ function consoleStream(opts) {
132
151
  if (!LOG_FORMATS.includes(format)) {
133
152
  throw new Error(`invalid log.format "${format}" (expected ${LOG_FORMATS.join(", ")})`);
134
153
  }
135
- const out = opts.out ?? process.stdout;
154
+ const target = opts.out ?? process.stdout;
155
+ // A console whose reader is gone (the `tee` of `pnpm dev:kernel` stopped by Ctrl-C, a closed
156
+ // pipe) fails every write with EPIPE. Unhandled, each failure is an uncaught exception the
157
+ // process guard logs, which writes again: an endless chain that starves the event loop. So the
158
+ // first stream error ends console output for good (Loki, when configured, still gets records).
159
+ let broken = false;
160
+ target.on?.("error", () => {
161
+ broken = true;
162
+ });
163
+ const out = {
164
+ write: (s) => {
165
+ if (!broken)
166
+ target.write(s);
167
+ },
168
+ };
136
169
  const isTTY = opts.isTTY ?? process.stdout.isTTY === true;
137
170
  const pretty = format === "pretty" || (format === "auto" && isTTY);
138
171
  if (!pretty)
139
172
  return { write: (s) => out.write(s) };
140
- // In process (no worker thread): pino-pretty formats each line synchronously.
173
+ const colors = colorsOn(opts, isTTY);
174
+ // In process (no worker thread): pino-pretty formats each record synchronously. The layout follows
175
+ // octok's buildPinoPrettyOptions: full local time, colored level, the bracket prefix, and
176
+ // the remaining fields as indented `key: value` lines below the message (errors with their stack).
141
177
  const format_ = prettyFactory({
142
- colorize: isTTY,
143
- colorizeObjects: isTTY,
144
- singleLine: true,
145
- // errors stay inside the one-line object instead of a multi-line stack block
146
- errorLikeObjectKeys: [],
147
- translateTime: "SYS:HH:MM:ss.l",
178
+ colorize: colors,
179
+ colorizeObjects: colors,
180
+ singleLine: false,
181
+ translateTime: "SYS:standard",
148
182
  ignore: ["pid", "hostname", "role", "spanId", ...PREFIX_FIELDS].join(","),
149
183
  messageFormat: (log, messageKey) => prettyMessage(log, messageKey),
150
184
  });
151
- return { write: (s) => out.write(format_(s)) };
185
+ return {
186
+ write: (s) => {
187
+ let record;
188
+ try {
189
+ record = JSON.parse(s);
190
+ }
191
+ catch {
192
+ out.write(s);
193
+ return;
194
+ }
195
+ out.write(format_(foldAccessLine(record)));
196
+ },
197
+ };
152
198
  }
153
199
  /**
154
200
  * Creates the kernel logger on pino. Each record is `{ time, level, msg, ...base, ...fields }` with
@@ -254,7 +300,7 @@ export function createLogger(level = "info", opts = {}) {
254
300
  }
255
301
  sink[lvl](obj, redactMsg(msg));
256
302
  };
257
- return {
303
+ const root = {
258
304
  debug: (msg, fields) => emit("debug", msg, fields),
259
305
  info: (msg, fields) => emit("info", msg, fields),
260
306
  warn: (msg, fields) => emit("warn", msg, fields),
@@ -299,5 +345,35 @@ export function createLogger(level = "info", opts = {}) {
299
345
  }
300
346
  }
301
347
  },
348
+ // a method, so a wrapper of this logger (a test proxy) is the parent its children forward to
349
+ child(fields) {
350
+ return childLogger(this, fields);
351
+ },
352
+ };
353
+ return root;
354
+ }
355
+ /**
356
+ * A logger that adds `bound` to every record and forwards it to `parent`'s public methods (so a spy
357
+ * on the parent sees its children's records); call fields cannot overwrite the bound ones.
358
+ */
359
+ function childLogger(parent, bound) {
360
+ const merged = (fields) => ({ ...fields, ...bound });
361
+ const child = {
362
+ debug: (msg, fields) => parent.debug(msg, merged(fields)),
363
+ info: (msg, fields) => parent.info(msg, merged(fields)),
364
+ warn: (msg, fields) => parent.warn(msg, merged(fields)),
365
+ error: (msg, fields) => parent.error(msg, merged(fields)),
366
+ get level() {
367
+ return parent.level;
368
+ },
369
+ setLevel: (level) => parent.setLevel(level),
370
+ addSecrets: (values) => parent.addSecrets(values),
371
+ addOrgSecrets: (values) => parent.addOrgSecrets(values),
372
+ redact: (text) => parent.redact(text),
373
+ flush: () => parent.flush(),
374
+ child(fields) {
375
+ return childLogger(this, fields);
376
+ },
302
377
  };
378
+ return child;
303
379
  }
@@ -35,6 +35,13 @@ export declare const KERNEL_PURPOSES: readonly [{
35
35
  readonly zh: "会话标题";
36
36
  readonly en: "Conversation titles";
37
37
  };
38
+ }, {
39
+ readonly purpose: "web_fetch";
40
+ readonly kind: "chat";
41
+ readonly label: {
42
+ readonly zh: "网页提取";
43
+ readonly en: "Web page extraction";
44
+ };
38
45
  }, {
39
46
  readonly purpose: "subagent_fast";
40
47
  readonly kind: "chat";
@@ -140,6 +147,19 @@ export declare class ModelPurposesService {
140
147
  setOrg(orgId: string, purpose: string, target: PurposeTarget | null, actor: string, actorTokenId?: string): Promise<PurposeView>;
141
148
  /** 生效的映射:组织覆盖,否则部署默认;都没有 → `model.unconfigured`;用途未声明 → `model.purpose_unknown`。 */
142
149
  resolve(orgId: string, purpose: string): Promise<PurposeTarget>;
150
+ /** The target a {@link chat} call on `purpose` uses: the mapping, else `fallback` when unmapped. */
151
+ private chatTarget;
152
+ /**
153
+ * The limits of the model a {@link chat} call on `purpose` would use now (the same mapping and
154
+ * `fallback` rule): its context window and output cap, so a caller can size its request (e.g.
155
+ * the `WebFetch` page) before the call. Fails as `chat` does before sending.
156
+ */
157
+ chatLimits(orgId: string, purpose: string, opts?: {
158
+ fallback?: PurposeTarget;
159
+ }): Promise<{
160
+ contextWindow: number;
161
+ maxOutputTokens: number;
162
+ }>;
143
163
  /**
144
164
  * 按用途发一次对话请求:解析映射、按目标模型的声明宽松处理参数(补默认值,与运行时一致),用 `consumeModelStream`
145
165
  * 收完(只有流空闲看门狗)。输出上限取 `min(req.maxOutputTokens, 目标模型的 maxOutputTokens)`。
@@ -147,10 +167,13 @@ export declare class ModelPurposesService {
147
167
  * `opts.fallback`: the target used when the purpose is not mapped (`model.unconfigured`).
148
168
  * `opts.beforeSend`: called with the resolved target and the final request just before sending;
149
169
  * what it throws is thrown as is (a pre-send failure, e.g. a denied `model.request` hook).
170
+ * `opts.paramDefaults`: values for parameters the target does not set, kept only where the
171
+ * target model declares the key and the value fits it (e.g. `thinking: "off"` for a short call).
150
172
  */
151
173
  chat(orgId: string, purpose: string, req: Omit<ModelRequest, "model" | "params">, signal: AbortSignal, opts?: {
152
174
  fallback?: PurposeTarget;
153
175
  beforeSend?: (target: ModelRef, request: ModelRequest) => Promise<void>;
176
+ paramDefaults?: Record<string, ParamValue>;
154
177
  }): Promise<{
155
178
  content: ContentBlock[];
156
179
  stopReason: StopReason;
@@ -41,6 +41,11 @@ export const KERNEL_PURPOSES = [
41
41
  { purpose: "eval", kind: "chat", label: { zh: "评测评委", en: "Eval judge" } },
42
42
  /** Conversation titles (UI refresh B); unmapped → the conversation's current chat model. */
43
43
  { purpose: "title", kind: "chat", label: { zh: "会话标题", en: "Conversation titles" } },
44
+ /**
45
+ * The `WebFetch` extraction call (web tools spec §5); unmapped → the calling conversation's
46
+ * current chat model (a sub-agent's call: the sub-agent's).
47
+ */
48
+ { purpose: "web_fetch", kind: "chat", label: { zh: "网页提取", en: "Web page extraction" } },
44
49
  {
45
50
  purpose: "subagent_fast",
46
51
  kind: "chat",
@@ -254,6 +259,27 @@ let ModelPurposesService = class ModelPurposesService {
254
259
  }
255
260
  return view.effective;
256
261
  }
262
+ /** The target a {@link chat} call on `purpose` uses: the mapping, else `fallback` when unmapped. */
263
+ async chatTarget(orgId, purpose, fallback) {
264
+ try {
265
+ return await this.resolve(orgId, purpose);
266
+ }
267
+ catch (e) {
268
+ if (!(e instanceof ApiError && e.code === "model.unconfigured") || !fallback)
269
+ throw e;
270
+ return fallback;
271
+ }
272
+ }
273
+ /**
274
+ * The limits of the model a {@link chat} call on `purpose` would use now (the same mapping and
275
+ * `fallback` rule): its context window and output cap, so a caller can size its request (e.g.
276
+ * the `WebFetch` page) before the call. Fails as `chat` does before sending.
277
+ */
278
+ async chatLimits(orgId, purpose, opts = {}) {
279
+ const target = await this.chatTarget(orgId, purpose, opts.fallback);
280
+ const { info } = await this.providers.resolve(target.model);
281
+ return { contextWindow: info.contextWindow, maxOutputTokens: info.maxOutputTokens };
282
+ }
257
283
  /**
258
284
  * 按用途发一次对话请求:解析映射、按目标模型的声明宽松处理参数(补默认值,与运行时一致),用 `consumeModelStream`
259
285
  * 收完(只有流空闲看门狗)。输出上限取 `min(req.maxOutputTokens, 目标模型的 maxOutputTokens)`。
@@ -261,22 +287,17 @@ let ModelPurposesService = class ModelPurposesService {
261
287
  * `opts.fallback`: the target used when the purpose is not mapped (`model.unconfigured`).
262
288
  * `opts.beforeSend`: called with the resolved target and the final request just before sending;
263
289
  * what it throws is thrown as is (a pre-send failure, e.g. a denied `model.request` hook).
290
+ * `opts.paramDefaults`: values for parameters the target does not set, kept only where the
291
+ * target model declares the key and the value fits it (e.g. `thinking: "off"` for a short call).
264
292
  */
265
293
  async chat(orgId, purpose, req, signal, opts = {}) {
266
- let target;
267
- try {
268
- target = await this.resolve(orgId, purpose);
269
- }
270
- catch (e) {
271
- if (!(e instanceof ApiError && e.code === "model.unconfigured") || !opts.fallback)
272
- throw e;
273
- target = opts.fallback;
274
- }
294
+ const target = await this.chatTarget(orgId, purpose, opts.fallback);
275
295
  const { provider, info } = await this.providers.resolve(target.model);
276
296
  const request = {
277
297
  ...req,
278
298
  model: info.id,
279
- params: sanitizeParams(info.parameters, target.params).value,
299
+ // undeclared or unfitting defaults are dropped like any other parameter
300
+ params: sanitizeParams(info.parameters, { ...opts.paramDefaults, ...target.params }).value,
280
301
  // 输出上限不超过目标模型自己的上限(与运行时 assembleModelRequest 一致)
281
302
  maxOutputTokens: Math.min(req.maxOutputTokens, info.maxOutputTokens),
282
303
  };
@@ -83,7 +83,9 @@ export declare class Offboarding {
83
83
  * In the caller's transaction (`mandatory`: the caller holds the org row lock, order org row →
84
84
  * membership, and did the "last owner" checks):
85
85
  * 1. 删除成员关系(非成员 → `member.not_found`;删后组织没有 owner → `org.last_owner`),清空指向该组织的会话当前组织;
86
- * 2. 吊销该账号**全部**会话与个人令牌(令牌与会话是账号级的,无法只吊销「该组织下」的,按保守处理);
86
+ * 2. Removal and directory offboarding revoke **every** session and personal token of the
87
+ * account (both are account-wide, so conservatively all); a person who leaves (`left`) keeps
88
+ * them, and the session's current org is cleared (the membership check ends the access);
87
89
  * 3. 删除其在该组织所有连接器上的个人凭据;
88
90
  * 4. 取消其作为处理人的 pending 审批与提问(写 cancelled 事件),记下此人在该组织进行中的运行(提交后中止);
89
91
  * 5. 其负责的该组织 agent 转给组织所有者(最早加入的 owner)并置 `needs_reassignment = true`;
@@ -83,7 +83,9 @@ let Offboarding = class Offboarding {
83
83
  * In the caller's transaction (`mandatory`: the caller holds the org row lock, order org row →
84
84
  * membership, and did the "last owner" checks):
85
85
  * 1. 删除成员关系(非成员 → `member.not_found`;删后组织没有 owner → `org.last_owner`),清空指向该组织的会话当前组织;
86
- * 2. 吊销该账号**全部**会话与个人令牌(令牌与会话是账号级的,无法只吊销「该组织下」的,按保守处理);
86
+ * 2. Removal and directory offboarding revoke **every** session and personal token of the
87
+ * account (both are account-wide, so conservatively all); a person who leaves (`left`) keeps
88
+ * them, and the session's current org is cleared (the membership check ends the access);
87
89
  * 3. 删除其在该组织所有连接器上的个人凭据;
88
90
  * 4. 取消其作为处理人的 pending 审批与提问(写 cancelled 事件),记下此人在该组织进行中的运行(提交后中止);
89
91
  * 5. 其负责的该组织 agent 转给组织所有者(最早加入的 owner)并置 `needs_reassignment = true`;
@@ -150,21 +152,29 @@ let Offboarding = class Offboarding {
150
152
  .where("account_id", "=", accountId)
151
153
  .where("current_org_id", "=", orgId)
152
154
  .execute();
153
- const sessions = await this.db
154
- .updateTable("sessions")
155
- .set({ revoked_at: sql `now()` })
156
- .where("account_id", "=", accountId)
157
- .where("revoked_at", "is", null)
158
- .returning("id")
159
- .execute();
160
- const tokens = await this.db
161
- .updateTable("api_tokens")
162
- .set({ revoked_at: sql `now()` })
163
- .where("kind", "=", "personal")
164
- .where("account_id", "=", accountId)
165
- .where("revoked_at", "is", null)
166
- .returning("id")
167
- .execute();
155
+ // A person who leaves keeps their sign-in and personal tokens: the org guard reads the
156
+ // membership on every request, so access to this org ends with the row above, and their other
157
+ // orgs keep working. Removal and directory offboarding revoke both (account-wide, conservative).
158
+ const revoke = reason !== "left";
159
+ const sessions = revoke
160
+ ? await this.db
161
+ .updateTable("sessions")
162
+ .set({ revoked_at: sql `now()` })
163
+ .where("account_id", "=", accountId)
164
+ .where("revoked_at", "is", null)
165
+ .returning("id")
166
+ .execute()
167
+ : [];
168
+ const tokens = revoke
169
+ ? await this.db
170
+ .updateTable("api_tokens")
171
+ .set({ revoked_at: sql `now()` })
172
+ .where("kind", "=", "personal")
173
+ .where("account_id", "=", accountId)
174
+ .where("revoked_at", "is", null)
175
+ .returning("id")
176
+ .execute()
177
+ : [];
168
178
  // in this transaction (the approvals and questions services join it: `mandatory`)
169
179
  const credentials = await this.credentials.clearAccount(orgId, accountId);
170
180
  const approvals = await this.approvals.cancelPendingFor(orgId, accountId);