@capybara-hq/kernel 0.1.0-rc.22 → 0.1.0-rc.24

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 (234) hide show
  1. package/bootstrap.json +25 -25
  2. package/dist/admin/admin.controller.d.ts +3 -3
  3. package/dist/admin/admin.controller.js +6 -2
  4. package/dist/admin/admin.module.d.ts +2 -1
  5. package/dist/admin/admin.module.js +7 -3
  6. package/dist/admin/admin.schemas.d.ts +13 -0
  7. package/dist/admin/outbox-dead.controller.d.ts +23 -0
  8. package/dist/admin/outbox-dead.controller.js +62 -0
  9. package/dist/admin/outbox-dead.schemas.d.ts +8 -0
  10. package/dist/admin/outbox-dead.schemas.js +18 -0
  11. package/dist/admin/outbox-dead.service.d.ts +66 -0
  12. package/dist/admin/outbox-dead.service.js +179 -0
  13. package/dist/admin/overview.service.d.ts +40 -5
  14. package/dist/admin/overview.service.js +41 -7
  15. package/dist/agents/from-template.js +1 -1
  16. package/dist/background/finish.d.ts +5 -2
  17. package/dist/background/finish.js +14 -3
  18. package/dist/background/launch.d.ts +44 -5
  19. package/dist/background/launch.js +92 -9
  20. package/dist/background/session.d.ts +14 -2
  21. package/dist/background/session.js +26 -4
  22. package/dist/background/status.d.ts +1 -1
  23. package/dist/background/status.js +3 -3
  24. package/dist/background/stop.d.ts +0 -2
  25. package/dist/background/stop.js +1 -21
  26. package/dist/background/store.d.ts +8 -0
  27. package/dist/background/store.js +24 -0
  28. package/dist/catalog/admin-catalog.controller.d.ts +21 -2
  29. package/dist/catalog/admin-catalog.controller.js +45 -6
  30. package/dist/catalog/admin-catalog.schemas.d.ts +5 -0
  31. package/dist/catalog/admin-catalog.schemas.js +8 -0
  32. package/dist/catalog/catalog-content.d.ts +5 -0
  33. package/dist/catalog/catalog-content.js +1 -0
  34. package/dist/catalog/catalog.module.d.ts +2 -1
  35. package/dist/catalog/catalog.module.js +5 -1
  36. package/dist/catalog/marketplaces.service.d.ts +1 -1
  37. package/dist/catalog/platform-import-update.d.ts +50 -0
  38. package/dist/catalog/platform-import-update.js +116 -0
  39. package/dist/catalog/plugin-import.d.ts +36 -1
  40. package/dist/catalog/plugin-import.js +23 -1
  41. package/dist/catalog/plugin-upgrade.d.ts +18 -0
  42. package/dist/catalog/plugin-upgrade.js +19 -0
  43. package/dist/catalog/requests.service.js +2 -2
  44. package/dist/channels/channels.module.d.ts +5 -2
  45. package/dist/channels/channels.module.js +9 -2
  46. package/dist/channels/deliver-job.d.ts +25 -0
  47. package/dist/channels/deliver-job.js +25 -0
  48. package/dist/channels/inbound.d.ts +1 -1
  49. package/dist/channels/inbound.js +2 -15
  50. package/dist/channels/jobs.d.ts +28 -18
  51. package/dist/channels/jobs.js +63 -26
  52. package/dist/config/schema.d.ts +3 -6
  53. package/dist/config/schema.js +20 -27
  54. package/dist/conversations/agent-tree.d.ts +16 -0
  55. package/dist/conversations/agent-tree.js +36 -0
  56. package/dist/conversations/conversations.service.d.ts +44 -14
  57. package/dist/conversations/conversations.service.js +52 -38
  58. package/dist/conversations/event-log.d.ts +7 -10
  59. package/dist/conversations/event-log.js +24 -15
  60. package/dist/conversations/events.js +2 -1
  61. package/dist/conversations/queue.service.d.ts +8 -3
  62. package/dist/conversations/queue.service.js +37 -13
  63. package/dist/conversations/titles.d.ts +21 -8
  64. package/dist/conversations/titles.js +42 -15
  65. package/dist/db/kernel-schema.d.ts +26 -8
  66. package/dist/devices/devices.service.d.ts +12 -3
  67. package/dist/devices/devices.service.js +30 -9
  68. package/dist/errors.d.ts +15 -0
  69. package/dist/errors.js +15 -0
  70. package/dist/evals/run-replay.js +14 -8
  71. package/dist/extensions/extensions.module.js +3 -0
  72. package/dist/extensions/lifecycle.d.ts +6 -1
  73. package/dist/extensions/lifecycle.js +9 -2
  74. package/dist/extensions/usage-subscribers.d.ts +43 -0
  75. package/dist/extensions/usage-subscribers.js +108 -0
  76. package/dist/http/api.module.js +2 -0
  77. package/dist/identity/settings.d.ts +53 -3
  78. package/dist/identity/settings.js +92 -9
  79. package/dist/index.d.ts +1 -0
  80. package/dist/index.js +1 -0
  81. package/dist/infra/cli/kernel-cli.module.js +5 -0
  82. package/dist/infra/config/kernel-config.service.d.ts +7 -0
  83. package/dist/infra/config/kernel-config.service.js +20 -0
  84. package/dist/infra/events/kernel-events.d.ts +9 -35
  85. package/dist/infra/outbox/outbox-relay.module.d.ts +6 -0
  86. package/dist/infra/outbox/outbox-relay.module.js +18 -0
  87. package/dist/infra/outbox/outbox.dispatcher.d.ts +103 -0
  88. package/dist/infra/outbox/outbox.dispatcher.js +310 -0
  89. package/dist/infra/outbox/outbox.module.d.ts +24 -0
  90. package/dist/infra/outbox/outbox.module.js +50 -0
  91. package/dist/infra/outbox/outbox.relay.d.ts +31 -0
  92. package/dist/infra/outbox/outbox.relay.js +76 -0
  93. package/dist/infra/outbox/outbox.service.d.ts +51 -0
  94. package/dist/infra/outbox/outbox.service.js +131 -0
  95. package/dist/infra/outbox/registry.d.ts +120 -0
  96. package/dist/infra/outbox/registry.js +108 -0
  97. package/dist/infra/queue/queue-names.d.ts +8 -0
  98. package/dist/infra/queue/queue-names.js +16 -0
  99. package/dist/kernel.module.js +3 -0
  100. package/dist/local-devices/device-policy.service.d.ts +4 -0
  101. package/dist/local-devices/device-policy.service.js +61 -3
  102. package/dist/local-devices/local-device-gateway.d.ts +4 -2
  103. package/dist/local-devices/local-device-gateway.js +39 -14
  104. package/dist/local-devices/machines.d.ts +25 -1
  105. package/dist/local-devices/machines.js +91 -15
  106. package/dist/local-devices/policy-summary.d.ts +44 -1
  107. package/dist/local-devices/policy-summary.js +69 -0
  108. package/dist/models/embeddings.d.ts +9 -9
  109. package/dist/models/embeddings.js +11 -18
  110. package/dist/models/models.module.js +3 -0
  111. package/dist/orgs/orgs.service.js +7 -0
  112. package/dist/orgs/settings.controller.js +2 -2
  113. package/dist/orgs/settings.service.d.ts +11 -5
  114. package/dist/orgs/settings.service.js +28 -14
  115. package/dist/overrides.d.ts +20 -3
  116. package/dist/overrides.js +10 -2
  117. package/dist/platform/exclusions.js +5 -0
  118. package/dist/platform/ops/admin.ops.js +50 -9
  119. package/dist/platform/ops/catalog-admin.ops.d.ts +3 -1
  120. package/dist/platform/ops/catalog-admin.ops.js +32 -4
  121. package/dist/platform/ops/org-settings.ops.js +3 -3
  122. package/dist/platform/ops/orgs.ops.js +1 -0
  123. package/dist/platform/ops/outbox.ops.d.ts +21 -0
  124. package/dist/platform/ops/outbox.ops.js +125 -0
  125. package/dist/platform/ops/platform.ops.js +1 -1
  126. package/dist/platform/ops/schedules.ops.js +40 -1
  127. package/dist/platform/platform-ops.module.d.ts +2 -1
  128. package/dist/platform/platform-ops.module.js +3 -0
  129. package/dist/platform/route-schemas.js +3 -0
  130. package/dist/roles/worker.module.js +9 -4
  131. package/dist/runtime/langgraph-loop.js +13 -5
  132. package/dist/runtime/limits.d.ts +15 -0
  133. package/dist/runtime/limits.js +25 -0
  134. package/dist/runtime/loop.d.ts +8 -3
  135. package/dist/runtime/loop.js +2 -1
  136. package/dist/runtime/maintenance.d.ts +4 -2
  137. package/dist/runtime/maintenance.js +17 -2
  138. package/dist/runtime/model-call.d.ts +14 -16
  139. package/dist/runtime/model-call.js +14 -24
  140. package/dist/runtime/queue-lost-jobs.d.ts +24 -0
  141. package/dist/runtime/queue-lost-jobs.js +51 -0
  142. package/dist/runtime/run-control.d.ts +42 -5
  143. package/dist/runtime/run-control.js +66 -12
  144. package/dist/runtime/run-finish.d.ts +39 -12
  145. package/dist/runtime/run-finish.js +97 -14
  146. package/dist/runtime/run-jobs.d.ts +95 -13
  147. package/dist/runtime/run-jobs.js +199 -28
  148. package/dist/runtime/run-orphan-check.job.d.ts +142 -0
  149. package/dist/runtime/run-orphan-check.job.js +339 -0
  150. package/dist/runtime/run-processors.d.ts +16 -9
  151. package/dist/runtime/run-processors.js +1 -1
  152. package/dist/runtime/run-queue.d.ts +64 -55
  153. package/dist/runtime/run-queue.js +147 -284
  154. package/dist/runtime/run-steps.d.ts +1 -2
  155. package/dist/runtime/run-steps.js +8 -11
  156. package/dist/runtime/run-worker.d.ts +6 -17
  157. package/dist/runtime/run-worker.js +32 -34
  158. package/dist/runtime/runtime.module.d.ts +2 -3
  159. package/dist/runtime/runtime.module.js +5 -6
  160. package/dist/runtime/step-log.d.ts +16 -4
  161. package/dist/runtime/step-log.js +26 -6
  162. package/dist/runtime/stream.controller.d.ts +2 -4
  163. package/dist/runtime/stream.controller.js +5 -11
  164. package/dist/runtime/subagents/child.js +1 -1
  165. package/dist/runtime/subagents/definition.d.ts +2 -1
  166. package/dist/runtime/subagents/definition.js +4 -2
  167. package/dist/runtime/subagents/deliveries.d.ts +6 -3
  168. package/dist/runtime/subagents/deliveries.js +16 -7
  169. package/dist/runtime/subagents/dispatch.d.ts +12 -12
  170. package/dist/runtime/subagents/dispatch.js +13 -38
  171. package/dist/runtime/subagents/messages.d.ts +5 -1
  172. package/dist/runtime/subagents/messages.js +11 -3
  173. package/dist/runtime/subagents/skill-fork.d.ts +3 -3
  174. package/dist/runtime/subagents/skill-fork.js +5 -5
  175. package/dist/runtime/subagents/subagents.service.js +1 -1
  176. package/dist/runtime/subagents/tree-jobs.d.ts +61 -0
  177. package/dist/runtime/subagents/tree-jobs.js +86 -0
  178. package/dist/runtime/usage-report-jobs.d.ts +23 -48
  179. package/dist/runtime/usage-report-jobs.js +67 -119
  180. package/dist/runtime/usage-report.d.ts +28 -77
  181. package/dist/runtime/usage-report.js +35 -213
  182. package/dist/runtime/waits.d.ts +48 -198
  183. package/dist/runtime/waits.js +103 -805
  184. package/dist/sandbox/platform-tiers.d.ts +86 -0
  185. package/dist/sandbox/platform-tiers.js +97 -0
  186. package/dist/sandbox/sandbox.service.d.ts +18 -5
  187. package/dist/sandbox/sandbox.service.js +75 -20
  188. package/dist/sandbox/tiers.d.ts +14 -2
  189. package/dist/sandbox/tiers.js +8 -0
  190. package/dist/schedules/calendar.d.ts +134 -0
  191. package/dist/schedules/calendar.js +410 -0
  192. package/dist/schedules/fire.d.ts +1 -3
  193. package/dist/schedules/fire.js +5 -10
  194. package/dist/schedules/run-tracker.d.ts +0 -5
  195. package/dist/schedules/run-tracker.js +0 -13
  196. package/dist/schedules/scan.job.js +15 -20
  197. package/dist/schedules/schedule-math.d.ts +42 -0
  198. package/dist/schedules/schedule-math.js +52 -0
  199. package/dist/schedules/scheduled-tasks.service.d.ts +16 -0
  200. package/dist/schedules/scheduled-tasks.service.js +53 -0
  201. package/dist/schedules/schedules.controller.d.ts +5 -1
  202. package/dist/schedules/schedules.controller.js +15 -1
  203. package/dist/schedules/schedules.schemas.d.ts +35 -0
  204. package/dist/schedules/schedules.schemas.js +27 -0
  205. package/dist/schedules/store.d.ts +5 -0
  206. package/dist/schedules/store.js +20 -0
  207. package/dist/tools/approvals.service.d.ts +26 -7
  208. package/dist/tools/approvals.service.js +88 -10
  209. package/dist/tools/builtin-names.d.ts +5 -3
  210. package/dist/tools/builtin-names.js +12 -4
  211. package/dist/tools/builtins/builtin-tools.d.ts +1 -2
  212. package/dist/tools/builtins/builtin-tools.js +21 -17
  213. package/dist/tools/builtins/sandbox/bash.js +9 -3
  214. package/dist/tools/builtins/sandbox/common.d.ts +18 -1
  215. package/dist/tools/builtins/sandbox/common.js +14 -1
  216. package/dist/tools/builtins/sandbox/index.d.ts +2 -1
  217. package/dist/tools/builtins/sandbox/index.js +4 -1
  218. package/dist/tools/builtins/sandbox/task-list.d.ts +12 -0
  219. package/dist/tools/builtins/sandbox/task-list.js +130 -0
  220. package/dist/tools/builtins/sandbox/task-output.d.ts +8 -1
  221. package/dist/tools/builtins/sandbox/task-output.js +21 -3
  222. package/dist/tools/builtins/web/extract.d.ts +2 -4
  223. package/dist/tools/builtins/web/extract.js +2 -5
  224. package/dist/tools/pipeline.d.ts +1 -1
  225. package/dist/tools/pipeline.js +1 -1
  226. package/dist/tools/questions.service.d.ts +15 -6
  227. package/dist/tools/questions.service.js +20 -8
  228. package/dist/tools/registry.d.ts +4 -2
  229. package/dist/tools/registry.js +4 -3
  230. package/dist/tools/tools.module.js +4 -3
  231. package/package.json +3 -3
  232. package/src/migrations/0118_device_policy_ops.sql +6 -0
  233. package/src/migrations/0119_outbox.sql +22 -0
  234. package/src/migrations/0120_drop_usage_report_sweep.sql +7 -0
@@ -12,7 +12,6 @@ import type { AgentLoop } from "./loop.js";
12
12
  import { ProviderRegistry } from "./providers.js";
13
13
  import { StepLog } from "./step-log.js";
14
14
  import { SubagentsService } from "./subagents/subagents.service.js";
15
- import { UsageReporter } from "./usage-report.js";
16
15
  /**
17
16
  * Builds the agent loop a run executes (default {@link LangGraphLoop}: its checkpoints live in the
18
17
  * kernel's {@link KyselyCheckpointSaver}, so another replica resumes a crashed worker's run).
@@ -35,5 +34,5 @@ export declare class AgentLoops {
35
34
  */
36
35
  export declare class RunSteps {
37
36
  readonly deps: LoopDeps;
38
- constructor(kdb: KernelDb, eventLog: EventLog, stepLog: StepLog, registry: ProviderRegistry, usageReport: UsageReporter, tools: ToolRegistry, config: KernelConfigService, log: KernelLogger, toolPipeline: ToolPipeline, host: ExtensionHost, subagents: SubagentsService, attachments: AttachmentsService);
37
+ constructor(kdb: KernelDb, eventLog: EventLog, stepLog: StepLog, registry: ProviderRegistry, tools: ToolRegistry, config: KernelConfigService, log: KernelLogger, toolPipeline: ToolPipeline, host: ExtensionHost, subagents: SubagentsService, attachments: AttachmentsService);
39
38
  }
@@ -21,7 +21,6 @@ import { LangGraphLoop } from "./langgraph-loop.js";
21
21
  import { ProviderRegistry } from "./providers.js";
22
22
  import { StepLog } from "./step-log.js";
23
23
  import { SubagentsService } from "./subagents/subagents.service.js";
24
- import { UsageReporter } from "./usage-report.js";
25
24
  /** Optional provider: the {@link AgentLoopFactory} runs use instead of the default (tests). */
26
25
  export const AGENT_LOOP_FACTORY = Symbol("AGENT_LOOP_FACTORY");
27
26
  /** The agent loops of chat runs and of eval full-run replays (one factory, one checkpoint store). */
@@ -52,7 +51,7 @@ export { AgentLoops };
52
51
  */
53
52
  let RunSteps = class RunSteps {
54
53
  deps;
55
- constructor(kdb, eventLog, stepLog, registry, usageReport, tools, config, log, toolPipeline, host, subagents, attachments) {
54
+ constructor(kdb, eventLog, stepLog, registry, tools, config, log, toolPipeline, host, subagents, attachments) {
56
55
  this.deps = {
57
56
  get db() {
58
57
  return kdb.db;
@@ -60,7 +59,6 @@ let RunSteps = class RunSteps {
60
59
  eventLog,
61
60
  stepLog,
62
61
  registry,
63
- usageReport,
64
62
  tools,
65
63
  config,
66
64
  log,
@@ -84,13 +82,12 @@ RunSteps = __decorate([
84
82
  __param(1, Inject(EventLog)),
85
83
  __param(2, Inject(StepLog)),
86
84
  __param(3, Inject(ProviderRegistry)),
87
- __param(4, Inject(UsageReporter)),
88
- __param(5, Inject(ToolRegistry)),
89
- __param(6, Inject(KernelConfigService)),
90
- __param(7, Inject(KERNEL_LOGGER)),
91
- __param(8, Inject(ToolPipeline)),
92
- __param(9, Inject(ExtensionHost)),
93
- __param(10, Inject(SubagentsService)),
94
- __param(11, Inject(AttachmentsService))
85
+ __param(4, Inject(ToolRegistry)),
86
+ __param(5, Inject(KernelConfigService)),
87
+ __param(6, Inject(KERNEL_LOGGER)),
88
+ __param(7, Inject(ToolPipeline)),
89
+ __param(8, Inject(ExtensionHost)),
90
+ __param(9, Inject(SubagentsService)),
91
+ __param(10, Inject(AttachmentsService))
95
92
  ], RunSteps);
96
93
  export { RunSteps };
@@ -8,7 +8,6 @@ import { KernelDb } from "../infra/database/kernel-db.js";
8
8
  import { TransactionHost } from "../infra/database/transaction-host.js";
9
9
  import { KernelEvents } from "../infra/events/kernel-events.js";
10
10
  import { SkillsCatalogService } from "../skills/catalog.js";
11
- import { ToolPipeline } from "../tools/pipeline.js";
12
11
  import { RunAbort } from "./abort.js";
13
12
  import { KyselyCheckpointSaver } from "./checkpointer.js";
14
13
  import { ConversationLock } from "./conversation-lock.js";
@@ -17,7 +16,7 @@ import { type OrgQuota } from "./quota.js";
17
16
  import { RunContextService } from "./run-context.service.js";
18
17
  import { RunDrafts } from "./run-drafts.js";
19
18
  import { RunFinish } from "./run-finish.js";
20
- import type { RunJobData } from "./run-jobs.js";
19
+ import { type RunJobData } from "./run-jobs.js";
21
20
  import { RunQueue } from "./run-queue.js";
22
21
  import { AgentLoops, RunSteps } from "./run-steps.js";
23
22
  import { Waits } from "./waits.js";
@@ -66,12 +65,6 @@ export declare class RunWorker {
66
65
  private readonly skillsCatalog;
67
66
  /** Run start's run context (run context spec §5): who the agent is, the day, the person. */
68
67
  private readonly runContext;
69
- /**
70
- * In the terminal transaction of every run: writes its open `Agent` call results
71
- * (`stopOpenAgentCalls`; a no-op without dispatches); `fire` runs their observe hooks after the
72
- * commit (sub-agents spec §12, review m1: a failed or stalled parent too).
73
- */
74
- private readonly pipeline;
75
68
  private readonly finisher;
76
69
  readonly txHost: TransactionHost;
77
70
  /** The steps' live drafts this process writes for the runs it executes. */
@@ -100,13 +93,7 @@ export declare class RunWorker {
100
93
  /** Run start's skills catalog snapshot (reads the pinned skills' bodies, phase B). */
101
94
  skillsCatalog: Pick<SkillsCatalogService, "record">,
102
95
  /** Run start's run context (run context spec §5): who the agent is, the day, the person. */
103
- runContext: Pick<RunContextService, "record">,
104
- /**
105
- * In the terminal transaction of every run: writes its open `Agent` call results
106
- * (`stopOpenAgentCalls`; a no-op without dispatches); `fire` runs their observe hooks after the
107
- * commit (sub-agents spec §12, review m1: a failed or stalled parent too).
108
- */
109
- pipeline: Pick<ToolPipeline, "stopOpenAgentCalls">, finisher: Pick<RunFinish, "finish">, txHost: TransactionHost,
96
+ runContext: Pick<RunContextService, "record">, finisher: Pick<RunFinish, "finish">, txHost: TransactionHost,
110
97
  /** The steps' live drafts this process writes for the runs it executes. */
111
98
  drafts: RunDrafts, hooks?: RunHooks | undefined);
112
99
  private get db();
@@ -136,13 +123,15 @@ export declare class RunWorker {
136
123
  /**
137
124
  * After any terminal commit of a run that is not a sub-agent's (sub-agents spec §12): stops its
138
125
  * active fork invocation child on its behalf (no account); background `Agent` children keep
139
- * running (agent map spec §5.4). The wait sweep is the backstop.
126
+ * running (agent map spec §5.4). The terminal transaction owed the same stop (`stop-tree`), the
127
+ * backstop when this call never happens.
140
128
  */
141
129
  private stopChildren;
142
130
  /**
143
131
  * After a sub-agent run's terminal state committed (sub-agents spec §7.3): reports it to the
144
132
  * parent. Called with any run id whose conversation may be a child; runs that were not
145
- * dispatched are ignored. Errors are logged (the wait sweep repairs a missed call).
133
+ * dispatched are ignored. Errors are logged: the terminal transaction owed the same delivery
134
+ * (`deliver-to-parent`) and stop (`stop-tree`), which repair a missed call.
146
135
  */
147
136
  private childFinished;
148
137
  /**
@@ -25,16 +25,17 @@ import { KernelEvents } from "../infra/events/kernel-events.js";
25
25
  import { KERNEL_BUS, KERNEL_LOGGER } from "../infra/tokens.js";
26
26
  import { SkillsCatalogService } from "../skills/catalog.js";
27
27
  import { normalizeStoredDefinitionIn } from "../tools/legacy-data.js";
28
- import { ToolPipeline } from "../tools/pipeline.js";
29
28
  import { RunAbort } from "./abort.js";
30
29
  import { KyselyCheckpointSaver } from "./checkpointer.js";
31
30
  import { ConversationLock } from "./conversation-lock.js";
31
+ import { runDeadline, tightestLimit } from "./limits.js";
32
32
  import { RunError } from "./model-call.js";
33
33
  import { ProviderListError, ProviderRegistry } from "./providers.js";
34
34
  import { ORG_QUOTA_LEASE_MS, orgRunLimit, RUN_QUOTA } from "./quota.js";
35
35
  import { RunContextService } from "./run-context.service.js";
36
36
  import { RunDrafts, streamSink } from "./run-drafts.js";
37
- import { RunFinish, TERMINAL_STATUSES } from "./run-finish.js";
37
+ import { RunFinish } from "./run-finish.js";
38
+ import { TERMINAL_STATUSES } from "./run-jobs.js";
38
39
  import { RunQueue } from "./run-queue.js";
39
40
  import { AgentLoops, RunSteps } from "./run-steps.js";
40
41
  import { dispatchOfChildRun, rootConversationOf } from "./subagents/child.js";
@@ -86,7 +87,6 @@ let RunWorker = class RunWorker {
86
87
  models;
87
88
  skillsCatalog;
88
89
  runContext;
89
- pipeline;
90
90
  finisher;
91
91
  txHost;
92
92
  drafts;
@@ -114,13 +114,7 @@ let RunWorker = class RunWorker {
114
114
  /** Run start's skills catalog snapshot (reads the pinned skills' bodies, phase B). */
115
115
  skillsCatalog,
116
116
  /** Run start's run context (run context spec §5): who the agent is, the day, the person. */
117
- runContext,
118
- /**
119
- * In the terminal transaction of every run: writes its open `Agent` call results
120
- * (`stopOpenAgentCalls`; a no-op without dispatches); `fire` runs their observe hooks after the
121
- * commit (sub-agents spec §12, review m1: a failed or stalled parent too).
122
- */
123
- pipeline, finisher, txHost,
117
+ runContext, finisher, txHost,
124
118
  /** The steps' live drafts this process writes for the runs it executes. */
125
119
  drafts, hooks) {
126
120
  this.kdb = kdb;
@@ -140,7 +134,6 @@ let RunWorker = class RunWorker {
140
134
  this.models = models;
141
135
  this.skillsCatalog = skillsCatalog;
142
136
  this.runContext = runContext;
143
- this.pipeline = pipeline;
144
137
  this.finisher = finisher;
145
138
  this.txHost = txHost;
146
139
  this.drafts = drafts;
@@ -197,7 +190,8 @@ let RunWorker = class RunWorker {
197
190
  /**
198
191
  * After any terminal commit of a run that is not a sub-agent's (sub-agents spec §12): stops its
199
192
  * active fork invocation child on its behalf (no account); background `Agent` children keep
200
- * running (agent map spec §5.4). The wait sweep is the backstop.
193
+ * running (agent map spec §5.4). The terminal transaction owed the same stop (`stop-tree`), the
194
+ * backstop when this call never happens.
201
195
  */
202
196
  async stopChildren(runId) {
203
197
  try {
@@ -210,7 +204,8 @@ let RunWorker = class RunWorker {
210
204
  /**
211
205
  * After a sub-agent run's terminal state committed (sub-agents spec §7.3): reports it to the
212
206
  * parent. Called with any run id whose conversation may be a child; runs that were not
213
- * dispatched are ignored. Errors are logged (the wait sweep repairs a missed call).
207
+ * dispatched are ignored. Errors are logged: the terminal transaction owed the same delivery
208
+ * (`deliver-to-parent`) and stop (`stop-tree`), which repair a missed call.
214
209
  */
215
210
  async childFinished(runId) {
216
211
  try {
@@ -514,7 +509,8 @@ let RunWorker = class RunWorker {
514
509
  // a chat run's saved agent version, under the conversation's model switch (UI refresh B); a
515
510
  // sub-agent has none: it never takes a selection
516
511
  let saved;
517
- let maxSteps = runtime.maxSteps;
512
+ /** The run's step limit; `undefined` = no limit (`runtime.maxSteps` 0). */
513
+ let maxSteps = tightestLimit(runtime.maxSteps);
518
514
  let parent;
519
515
  /** A sub-agent's name in its run context: the roster name, or the forked skill's. */
520
516
  let childName;
@@ -531,7 +527,8 @@ let RunWorker = class RunWorker {
531
527
  agent = childRunDefinition(await normalizeStoredDefinitionIn(this.db, conv.org_id, started.payload.definition), trigger?.type === "user_message" && trigger.seq === Number(run.trigger_seq)
532
528
  ? trigger.payload.dispatch?.narrowed
533
529
  : undefined);
534
- maxSteps = Math.min(started.payload.maxSteps, runtime.maxSteps);
530
+ // the tighter of the logged limit and the deployment's (0 in either = no limit there)
531
+ maxSteps = tightestLimit(started.payload.maxSteps, runtime.maxSteps);
535
532
  childName = started.payload.template.invokedSkill?.name ?? started.payload.agentType;
536
533
  const row = await dispatchOfChildRun(this.db, runId);
537
534
  const otherAgent = started.payload.template.source === "agent";
@@ -629,17 +626,21 @@ let RunWorker = class RunWorker {
629
626
  startedAt = updated.started_at;
630
627
  }
631
628
  // 截止时间以首次开始为准:停机后在其他 worker 续跑也不重新计时;等人处理的时长(waited_ms)不计入。
632
- const deadline = startedAt.getTime() + Number(run.waited_ms) + runtime.maxRunSeconds * 1000;
629
+ // `runtime.maxRunSeconds` 0: no deadline, no timer (the run ends when the model stops or is stopped)
630
+ const deadline = runDeadline(startedAt.getTime() + Number(run.waited_ms), runtime.maxRunSeconds);
633
631
  const ac = new AbortController();
634
632
  let abortedBy;
635
633
  let abortDetail;
636
634
  let deadlineHit = false;
637
- const deadlineTimer = setTimeout(() => {
638
- deadlineHit = true;
639
- ac.abort();
640
- }, Math.max(0, deadline - Date.now()));
635
+ const deadlineTimer = deadline === undefined
636
+ ? undefined
637
+ : setTimeout(() => {
638
+ deadlineHit = true;
639
+ ac.abort();
640
+ }, Math.max(0, deadline - Date.now()));
641
641
  state.stopTimers = () => {
642
- clearTimeout(deadlineTimer);
642
+ if (deadlineTimer !== undefined)
643
+ clearTimeout(deadlineTimer);
643
644
  };
644
645
  let unwatch;
645
646
  try {
@@ -693,7 +694,10 @@ let RunWorker = class RunWorker {
693
694
  shutdown: state.shutdown.signal,
694
695
  sink: this.sinkFor(conversationId),
695
696
  pendingAbsorbable,
696
- limits: { maxSteps, deadline },
697
+ limits: {
698
+ ...(maxSteps !== undefined && { maxSteps }),
699
+ ...(deadline !== undefined && { deadline }),
700
+ },
697
701
  // a sub-agent's conversation fires hooks like a chat, with `parent` set (spec §11)
698
702
  conversationKind: conv.kind === "eval" ? "eval" : conv.kind === "subagent" ? "subagent" : "chat",
699
703
  ...(parent !== undefined && { parent }),
@@ -906,7 +910,7 @@ let RunWorker = class RunWorker {
906
910
  * 提交后发布会话通知,然后调用测试钩子。返回终态事件 seq;运行已是终态 → null。
907
911
  */
908
912
  async finish(conversationId, runId, status, consumed, event, hook = true, floorToTrigger = true) {
909
- let fire = () => { };
913
+ // the terminal transaction also writes the run's open fork report and cancels its waits
910
914
  const toSeq = await this.finisher.finish({
911
915
  conversationId,
912
916
  runId,
@@ -914,12 +918,7 @@ let RunWorker = class RunWorker {
914
918
  consumed,
915
919
  event,
916
920
  floorToTrigger,
917
- beforeTerminal: async () => {
918
- fire = (await this.pipeline.stopOpenAgentCalls(runId)).fire;
919
- },
920
921
  });
921
- if (toSeq !== null)
922
- fire();
923
922
  if (hook)
924
923
  await this.hooks?.beforeRelease?.(runId, conversationId);
925
924
  return toSeq;
@@ -952,11 +951,10 @@ RunWorker = __decorate([
952
951
  __param(16, Inject(ProviderRegistry)),
953
952
  __param(17, Inject(SkillsCatalogService)),
954
953
  __param(18, Inject(RunContextService)),
955
- __param(19, Inject(ToolPipeline)),
956
- __param(20, Inject(RunFinish)),
957
- __param(21, Inject(TransactionHost)),
958
- __param(22, Inject(RunDrafts)),
959
- __param(23, Optional()),
960
- __param(23, Inject(RUN_HOOKS))
954
+ __param(19, Inject(RunFinish)),
955
+ __param(20, Inject(TransactionHost)),
956
+ __param(21, Inject(RunDrafts)),
957
+ __param(22, Optional()),
958
+ __param(22, Inject(RUN_HOOKS))
961
959
  ], RunWorker);
962
960
  export { RunWorker };
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * The run runtime: the conversation run lock, this process's event streams, the run queue and its
3
- * worker (consumed through `RuntimeWorkersModule`'s processors), the waits (their sweep is the
4
- * worker role's `WaitSweepJob`), run
5
- * stops, the checkpoints, the org quotas, the step writes and the agent loops. The model provider
3
+ * worker (consumed through `RuntimeWorkersModule`'s processors), the waits (a run nobody runs is
4
+ * the worker role's `RunOrphanCheck`'s to repair), run stops, the checkpoints, the org quotas, the step writes and the agent loops. The model provider
6
5
  * registry, the sub-agent dispatch, `usage.report` and the titles have modules of their own (the
7
6
  * tools, models and conversations domains import them without the runtime).
8
7
  */
@@ -37,12 +37,11 @@ import { StepLogModule } from "./step-log.js";
37
37
  import { ConversationStreams } from "./stream.js";
38
38
  import { SubagentsModule } from "./subagents/subagents.service.js";
39
39
  import { UsageReportModule } from "./usage-report.js";
40
- import { DecisionResumer, Waits } from "./waits.js";
40
+ import { Waits } from "./waits.js";
41
41
  /**
42
42
  * The run runtime: the conversation run lock, this process's event streams, the run queue and its
43
- * worker (consumed through `RuntimeWorkersModule`'s processors), the waits (their sweep is the
44
- * worker role's `WaitSweepJob`), run
45
- * stops, the checkpoints, the org quotas, the step writes and the agent loops. The model provider
43
+ * worker (consumed through `RuntimeWorkersModule`'s processors), the waits (a run nobody runs is
44
+ * the worker role's `RunOrphanCheck`'s to repair), run stops, the checkpoints, the org quotas, the step writes and the agent loops. The model provider
46
45
  * registry, the sub-agent dispatch, `usage.report` and the titles have modules of their own (the
47
46
  * tools, models and conversations domains import them without the runtime).
48
47
  */
@@ -67,7 +66,8 @@ RuntimeModule = __decorate([
67
66
  BackgroundModule,
68
67
  // a scheduled task's run follows its kernel run's end (`RunFinish`)
69
68
  ScheduledTaskTrackerModule,
70
- // the producer side of `wait-timeout` jobs (`MaintenanceProcessor` consumes them)
69
+ // the maintenance queue's handle: the outbox delivers the `wait-timeout` jobs the waits owe
70
+ // (`MaintenanceProcessor` consumes them)
71
71
  QueueModule.registerQueue(MAINTENANCE_QUEUE_NAME),
72
72
  ],
73
73
  providers: [
@@ -78,7 +78,6 @@ RuntimeModule = __decorate([
78
78
  RunFinish,
79
79
  RunQueue,
80
80
  Waits,
81
- DecisionResumer,
82
81
  RunControl,
83
82
  RunDrafts,
84
83
  AgentLoops,
@@ -4,26 +4,38 @@ import type { ConversationEvent } from "../conversations/events.js";
4
4
  import { audit } from "../identity/audit.js";
5
5
  import { KernelDb } from "../infra/database/kernel-db.js";
6
6
  import { TransactionHost } from "../infra/database/transaction-host.js";
7
+ import { UsageReportJobs } from "./usage-report-jobs.js";
7
8
  /** An audit row written with the events (`identity/audit.ts`). */
8
9
  export type StepAuditEntry = Parameters<typeof audit>[1];
9
10
  /**
10
11
  * The writes of a run step that go to the event log together with another row, each in a
11
12
  * transaction of its own (the caller notifies the log after the commit): a model call with the
12
- * tool definitions it sent, a denial with its audit row, and a sub-agent's usage counted on its
13
- * parent run. The step functions (`model-call.ts`, `hook-calls.ts`) reach it through their deps.
13
+ * tool definitions it sent, a denial with its audit row, usage events with their owed
14
+ * `usage.report` deliveries (transactional outbox spec §8.1), and a sub-agent's usage counted on
15
+ * its parent run. The step functions (`model-call.ts`, `hook-calls.ts`) reach it through their deps.
14
16
  */
15
17
  export declare class StepLog {
16
18
  private readonly kdb;
17
19
  private readonly eventLog;
20
+ private readonly usage;
18
21
  readonly txHost: TransactionHost;
19
- constructor(kdb: KernelDb, eventLog: EventLog, txHost: TransactionHost);
22
+ constructor(kdb: KernelDb, eventLog: EventLog, usage: Pick<UsageReportJobs, "oweEvents">, txHost: TransactionHost);
23
+ /**
24
+ * Appends events and owes the `usage.report` delivery of the `usage` events it newly wrote, in
25
+ * one transaction. Returns what was written (hits included) and the highest new seq (null:
26
+ * nothing new).
27
+ */
28
+ appendReported(conversationId: string, events: AppendEvent[]): Promise<{
29
+ written: ConversationEvent[];
30
+ toSeq: number | null;
31
+ }>;
20
32
  /**
21
33
  * Appends events of a sub-agent's run and adds the tokens of the usage events it newly wrote to
22
34
  * the `subagent_tokens_used` of the tree's budget run (sub-agents spec §8.3, agent map spec §6:
23
35
  * the whole tree counts on one run): `counter.budgetRunId`, the run's own window
24
36
  * (`SubagentParent.budgetRunId`), else the parent run's ({@link budgetRunOf}). A usage event
25
37
  * already in the log (an idempotent hit after a crash) adds nothing, so each call counts exactly
26
- * once. Returns what was written (hits included) and the highest new seq (null: nothing new).
38
+ * once; the new usage events' `usage.report` deliveries are owed in the same transaction. Returns what was written (hits included) and the highest new seq (null: nothing new).
27
39
  */
28
40
  appendCounted(conversationId: string, counter: {
29
41
  parentRunId: string;
@@ -17,28 +17,43 @@ import { TransactionHost } from "../infra/database/transaction-host.js";
17
17
  import { Transactional } from "../infra/database/transactional.decorator.js";
18
18
  import { recordToolSpecs } from "../tools/specs.js";
19
19
  import { budgetRunOf } from "./subagents/limits.js";
20
+ import { UsageReportJobs, UsageReportJobsModule } from "./usage-report-jobs.js";
20
21
  /**
21
22
  * The writes of a run step that go to the event log together with another row, each in a
22
23
  * transaction of its own (the caller notifies the log after the commit): a model call with the
23
- * tool definitions it sent, a denial with its audit row, and a sub-agent's usage counted on its
24
- * parent run. The step functions (`model-call.ts`, `hook-calls.ts`) reach it through their deps.
24
+ * tool definitions it sent, a denial with its audit row, usage events with their owed
25
+ * `usage.report` deliveries (transactional outbox spec §8.1), and a sub-agent's usage counted on
26
+ * its parent run. The step functions (`model-call.ts`, `hook-calls.ts`) reach it through their deps.
25
27
  */
26
28
  let StepLog = class StepLog {
27
29
  kdb;
28
30
  eventLog;
31
+ usage;
29
32
  txHost;
30
- constructor(kdb, eventLog, txHost) {
33
+ constructor(kdb, eventLog, usage, txHost) {
31
34
  this.kdb = kdb;
32
35
  this.eventLog = eventLog;
36
+ this.usage = usage;
33
37
  this.txHost = txHost;
34
38
  }
39
+ /**
40
+ * Appends events and owes the `usage.report` delivery of the `usage` events it newly wrote, in
41
+ * one transaction. Returns what was written (hits included) and the highest new seq (null:
42
+ * nothing new).
43
+ */
44
+ async appendReported(conversationId, events) {
45
+ const { events: written, fresh } = await this.eventLog.appendInReport(conversationId, events);
46
+ const added = written.filter((_, i) => fresh[i]);
47
+ await this.usage.oweEvents(conversationId, added);
48
+ return { written, toSeq: added.length > 0 ? Math.max(...added.map((e) => e.seq)) : null };
49
+ }
35
50
  /**
36
51
  * Appends events of a sub-agent's run and adds the tokens of the usage events it newly wrote to
37
52
  * the `subagent_tokens_used` of the tree's budget run (sub-agents spec §8.3, agent map spec §6:
38
53
  * the whole tree counts on one run): `counter.budgetRunId`, the run's own window
39
54
  * (`SubagentParent.budgetRunId`), else the parent run's ({@link budgetRunOf}). A usage event
40
55
  * already in the log (an idempotent hit after a crash) adds nothing, so each call counts exactly
41
- * once. Returns what was written (hits included) and the highest new seq (null: nothing new).
56
+ * once; the new usage events' `usage.report` deliveries are owed in the same transaction. Returns what was written (hits included) and the highest new seq (null: nothing new).
42
57
  */
43
58
  async appendCounted(conversationId, counter, events) {
44
59
  const t = this.kdb.db;
@@ -51,6 +66,7 @@ let StepLog = class StepLog {
51
66
  .executeTakeFirstOrThrow();
52
67
  const written = await this.eventLog.appendIn(conversationId, events);
53
68
  const fresh = written.filter((e) => e.seq > Number(before.last_seq));
69
+ await this.usage.oweEvents(conversationId, fresh);
54
70
  let tokens = 0;
55
71
  for (const e of fresh)
56
72
  if (e.type === "usage")
@@ -79,6 +95,9 @@ let StepLog = class StepLog {
79
95
  return this.eventLog.appendIn(conversationId, events);
80
96
  }
81
97
  };
98
+ __decorate([
99
+ Transactional({ propagation: "requires_new" })
100
+ ], StepLog.prototype, "appendReported", null);
82
101
  __decorate([
83
102
  Transactional({ propagation: "requires_new" })
84
103
  ], StepLog.prototype, "appendCounted", null);
@@ -92,7 +111,8 @@ StepLog = __decorate([
92
111
  Injectable(),
93
112
  __param(0, Inject(KernelDb)),
94
113
  __param(1, Inject(EventLog)),
95
- __param(2, Inject(TransactionHost))
114
+ __param(2, Inject(UsageReportJobs)),
115
+ __param(3, Inject(TransactionHost))
96
116
  ], StepLog);
97
117
  export { StepLog };
98
118
  /**
@@ -103,7 +123,7 @@ let StepLogModule = class StepLogModule {
103
123
  };
104
124
  StepLogModule = __decorate([
105
125
  Module({
106
- imports: [EventLogModule],
126
+ imports: [EventLogModule, UsageReportJobsModule],
107
127
  providers: [StepLog],
108
128
  exports: [StepLog],
109
129
  })
@@ -2,7 +2,6 @@ import type { Logger } from "@capybara-hq/plugin-sdk";
2
2
  import type { FastifyReply, FastifyRequest } from "fastify";
3
3
  import { z } from "zod";
4
4
  import { ConversationAccess } from "../conversations/access.js";
5
- import { ConversationsService } from "../conversations/conversations.service.js";
6
5
  import { ApiTokenService } from "../identity/api-tokens.js";
7
6
  import { type Principal } from "../identity/principal.js";
8
7
  import { SessionService } from "../identity/sessions.js";
@@ -18,7 +17,7 @@ declare const DraftParams: z.ZodObject<{
18
17
  }, z.core.$strip>;
19
18
  /**
20
19
  * 会话事件流(SSE):`GET /api/v1/orgs/:orgId/conversations/:id/stream?after=<seq>`,`agents.use`,只有会话所有者可用。
21
- * - 打开时不阻塞地调用一次 ensureRun(`ConversationsService.kickRun`),停在「运行中」的孤儿运行由此被清理;
20
+ * - opening it never changes run state (a read; transactional outbox spec §6);
22
21
  * - 先订阅会话频道、再回放 `after`(或优先的 `Last-Event-ID`)之后的事件:`id: <seq>` / `event: event`;
23
22
  * - after that catch-up, one `event: draft` (`data: {"runId","step","text","thinking"}`) per step of
24
23
  * the running run whose `assistant_message` is not logged (live drafts, spec 2026-11-07 §3.2);
@@ -33,11 +32,10 @@ export declare class ConversationStreamController {
33
32
  private readonly streams;
34
33
  private readonly sessions;
35
34
  private readonly tokens;
36
- private readonly conversations;
37
35
  private readonly access;
38
36
  private readonly drafts;
39
37
  private readonly log;
40
- constructor(streams: ConversationStreams, sessions: SessionService, tokens: ApiTokenService, conversations: ConversationsService, access: ConversationAccess, drafts: RunDrafts, log: Pick<Logger, "warn">);
38
+ constructor(streams: ConversationStreams, sessions: SessionService, tokens: ApiTokenService, access: ConversationAccess, drafts: RunDrafts, log: Pick<Logger, "warn">);
41
39
  /**
42
40
  * The live drafts of one run (spec 2026-11-07 §3.2): `{ drafts: { step, text, thinking }[] }`,
43
41
  * the steps still streaming whose `assistant_message` is not logged (none once the run is no
@@ -10,7 +10,6 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
10
10
  import { Controller, Get, Headers, Inject, Param, Req, Res } from "@nestjs/common";
11
11
  import { z } from "zod";
12
12
  import { ConversationAccess } from "../conversations/access.js";
13
- import { ConversationsService } from "../conversations/conversations.service.js";
14
13
  import { ApiError } from "../errors.js";
15
14
  import { ApiTokenService } from "../identity/api-tokens.js";
16
15
  import { credentialCheck } from "../identity/credential-check.js";
@@ -45,7 +44,7 @@ class RunDraftParamsDto extends createZodDto(DraftParams, "RunDraftParams") {
45
44
  }
46
45
  /**
47
46
  * 会话事件流(SSE):`GET /api/v1/orgs/:orgId/conversations/:id/stream?after=<seq>`,`agents.use`,只有会话所有者可用。
48
- * - 打开时不阻塞地调用一次 ensureRun(`ConversationsService.kickRun`),停在「运行中」的孤儿运行由此被清理;
47
+ * - opening it never changes run state (a read; transactional outbox spec §6);
49
48
  * - 先订阅会话频道、再回放 `after`(或优先的 `Last-Event-ID`)之后的事件:`id: <seq>` / `event: event`;
50
49
  * - after that catch-up, one `event: draft` (`data: {"runId","step","text","thinking"}`) per step of
51
50
  * the running run whose `assistant_message` is not logged (live drafts, spec 2026-11-07 §3.2);
@@ -60,15 +59,13 @@ let ConversationStreamController = class ConversationStreamController {
60
59
  streams;
61
60
  sessions;
62
61
  tokens;
63
- conversations;
64
62
  access;
65
63
  drafts;
66
64
  log;
67
- constructor(streams, sessions, tokens, conversations, access, drafts, log) {
65
+ constructor(streams, sessions, tokens, access, drafts, log) {
68
66
  this.streams = streams;
69
67
  this.sessions = sessions;
70
68
  this.tokens = tokens;
71
- this.conversations = conversations;
72
69
  this.access = access;
73
70
  this.drafts = drafts;
74
71
  this.log = log;
@@ -131,8 +128,6 @@ let ConversationStreamController = class ConversationStreamController {
131
128
  },
132
129
  });
133
130
  reply.hijack();
134
- // 可见性已通过:不阻塞地 ensureRun(清理孤儿运行、投递等待中的消息),见 ConversationsService.kickRun。
135
- this.conversations.kickRun(id);
136
131
  res.on("close", () => handle.close());
137
132
  // 订阅期间客户端已断开:close 事件可能已经错过
138
133
  if (res.destroyed || !res.socket || res.socket.destroyed) {
@@ -173,9 +168,8 @@ ConversationStreamController = __decorate([
173
168
  __param(0, Inject(ConversationStreams)),
174
169
  __param(1, Inject(SessionService)),
175
170
  __param(2, Inject(ApiTokenService)),
176
- __param(3, Inject(ConversationsService)),
177
- __param(4, Inject(ConversationAccess)),
178
- __param(5, Inject(RunDrafts)),
179
- __param(6, Inject(KERNEL_LOGGER))
171
+ __param(3, Inject(ConversationAccess)),
172
+ __param(4, Inject(RunDrafts)),
173
+ __param(5, Inject(KERNEL_LOGGER))
180
174
  ], ConversationStreamController);
181
175
  export { ConversationStreamController };
@@ -179,7 +179,7 @@ export async function childResult(eventLog, row, opts = {}) {
179
179
  else {
180
180
  status = "aborted";
181
181
  // §4.4: a person's stop names its account; a stop by nobody (null) is the kernel stopping the
182
- // child on its parent's behalf (stopChildren with no account: the parent's end, the sweep)
182
+ // child on its parent's behalf (stopChildren with no account: the parent's end, its `stop-tree` job)
183
183
  abortedBy =
184
184
  terminal.payload.reason === "user" && terminal.payload.byAccountId === null
185
185
  ? "parent"
@@ -20,6 +20,7 @@ export declare class SubagentTypeGoneError extends Error {
20
20
  export interface EffectiveDefinition {
21
21
  definition: AgentDefinition;
22
22
  template: EventPayload<"subagent_started">["template"];
23
+ /** The child's step limit; 0 = no limit. */
23
24
  maxSteps: number;
24
25
  /**
25
26
  * The child's preamble, frozen into `subagent_started.preamble` (model-visible):
@@ -72,7 +73,7 @@ export interface ChildDepth {
72
73
  * - `general` / `inline` / `plugin`: the parent's definition narrowed by the template (tools by
73
74
  * resolved key, `disallowedTools`, {@link CHILD_REMOVED_TOOLS}; policies stricter-wins; skills
74
75
  * by its `disallowedSkills`), the template's instructions, its `skills` the child has named in
75
- * the preamble to load first (general gets no line), `maxSteps = min(template ?? runtime.maxSteps, runtime.maxSteps)`, and the model tier
76
+ * the preamble to load first (general gets no line), `maxSteps` = the tighter of the template's and `runtime.maxSteps` (0 = no limit), and the model tier
76
77
  * `tier` (the call's) → the template's → `inherit`; an unmapped tier runs on the parent's model
77
78
  * (`tierFallback`); a template `effort` is set when the resolved model declares it;
78
79
  * - `agent`: the org agent's version that passed the gates (`orgAgent`, from {@link checkOrgAgent}),
@@ -6,6 +6,7 @@ import { ApiError } from "../../errors.js";
6
6
  import { invocationOf, resolveSkills } from "../../skills/catalog.js";
7
7
  import { agentSkills, usableSkills } from "../../skills/usable.js";
8
8
  import { AGENT_TOOL, CHILD_REMOVED_TOOLS } from "../../tools/builtin-names.js";
9
+ import { tightestLimit } from "../limits.js";
9
10
  import { SUBAGENT_PREAMBLE } from "./preamble.js";
10
11
  export { CHILD_REMOVED_TOOLS } from "../../tools/builtin-names.js";
11
12
  /**
@@ -222,7 +223,7 @@ function orgAgentDefinition(d, a) {
222
223
  * - `general` / `inline` / `plugin`: the parent's definition narrowed by the template (tools by
223
224
  * resolved key, `disallowedTools`, {@link CHILD_REMOVED_TOOLS}; policies stricter-wins; skills
224
225
  * by its `disallowedSkills`), the template's instructions, its `skills` the child has named in
225
- * the preamble to load first (general gets no line), `maxSteps = min(template ?? runtime.maxSteps, runtime.maxSteps)`, and the model tier
226
+ * the preamble to load first (general gets no line), `maxSteps` = the tighter of the template's and `runtime.maxSteps` (0 = no limit), and the model tier
226
227
  * `tier` (the call's) → the template's → `inherit`; an unmapped tier runs on the parent's model
227
228
  * (`tierFallback`); a template `effort` is set when the resolved model declares it;
228
229
  * - `agent`: the org agent's version that passed the gates (`orgAgent`, from {@link checkOrgAgent}),
@@ -293,7 +294,8 @@ export async function effectiveDefinition(d, a) {
293
294
  return {
294
295
  definition,
295
296
  template: ref,
296
- maxSteps: Math.min(template?.maxSteps ?? runtimeMax, runtimeMax),
297
+ // the tighter of the template's and the deployment's, 0 = no limit (`runtime.maxSteps` 0)
298
+ maxSteps: tightestLimit(template?.maxSteps, runtimeMax) ?? 0,
297
299
  preamble: names.length === 0
298
300
  ? SUBAGENT_PREAMBLE
299
301
  : `${SUBAGENT_PREAMBLE}\n\n${loadSkillsFirstText(names)}`,