@capybara-hq/kernel 0.1.0-rc.23 → 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 (206) hide show
  1. package/bootstrap.json +25 -25
  2. package/dist/admin/admin.module.d.ts +2 -1
  3. package/dist/admin/admin.module.js +7 -3
  4. package/dist/admin/outbox-dead.controller.d.ts +23 -0
  5. package/dist/admin/outbox-dead.controller.js +62 -0
  6. package/dist/admin/outbox-dead.schemas.d.ts +8 -0
  7. package/dist/admin/outbox-dead.schemas.js +18 -0
  8. package/dist/admin/outbox-dead.service.d.ts +66 -0
  9. package/dist/admin/outbox-dead.service.js +179 -0
  10. package/dist/admin/overview.service.d.ts +40 -5
  11. package/dist/admin/overview.service.js +41 -7
  12. package/dist/agents/from-template.js +1 -1
  13. package/dist/background/finish.d.ts +5 -2
  14. package/dist/background/finish.js +14 -3
  15. package/dist/background/launch.d.ts +2 -1
  16. package/dist/background/launch.js +5 -2
  17. package/dist/background/store.d.ts +8 -0
  18. package/dist/background/store.js +24 -0
  19. package/dist/catalog/admin-catalog.controller.d.ts +21 -2
  20. package/dist/catalog/admin-catalog.controller.js +45 -6
  21. package/dist/catalog/admin-catalog.schemas.d.ts +5 -0
  22. package/dist/catalog/admin-catalog.schemas.js +8 -0
  23. package/dist/catalog/catalog.module.d.ts +2 -1
  24. package/dist/catalog/catalog.module.js +5 -1
  25. package/dist/catalog/marketplaces.service.d.ts +1 -1
  26. package/dist/catalog/platform-import-update.d.ts +50 -0
  27. package/dist/catalog/platform-import-update.js +116 -0
  28. package/dist/catalog/plugin-import.d.ts +36 -1
  29. package/dist/catalog/plugin-import.js +23 -1
  30. package/dist/catalog/plugin-upgrade.d.ts +18 -0
  31. package/dist/catalog/plugin-upgrade.js +19 -0
  32. package/dist/channels/channels.module.d.ts +5 -2
  33. package/dist/channels/channels.module.js +9 -2
  34. package/dist/channels/deliver-job.d.ts +25 -0
  35. package/dist/channels/deliver-job.js +25 -0
  36. package/dist/channels/inbound.d.ts +1 -1
  37. package/dist/channels/inbound.js +2 -15
  38. package/dist/channels/jobs.d.ts +28 -18
  39. package/dist/channels/jobs.js +63 -26
  40. package/dist/config/schema.d.ts +0 -3
  41. package/dist/config/schema.js +11 -20
  42. package/dist/conversations/agent-tree.d.ts +16 -0
  43. package/dist/conversations/agent-tree.js +36 -0
  44. package/dist/conversations/conversations.service.d.ts +44 -14
  45. package/dist/conversations/conversations.service.js +52 -38
  46. package/dist/conversations/event-log.d.ts +7 -10
  47. package/dist/conversations/event-log.js +24 -15
  48. package/dist/conversations/events.js +2 -1
  49. package/dist/conversations/queue.service.d.ts +8 -3
  50. package/dist/conversations/queue.service.js +37 -13
  51. package/dist/conversations/titles.d.ts +21 -8
  52. package/dist/conversations/titles.js +42 -15
  53. package/dist/db/kernel-schema.d.ts +26 -8
  54. package/dist/devices/devices.service.d.ts +12 -3
  55. package/dist/devices/devices.service.js +18 -3
  56. package/dist/errors.d.ts +11 -0
  57. package/dist/errors.js +11 -0
  58. package/dist/evals/run-replay.js +14 -8
  59. package/dist/extensions/extensions.module.js +3 -0
  60. package/dist/extensions/lifecycle.d.ts +6 -1
  61. package/dist/extensions/lifecycle.js +9 -2
  62. package/dist/extensions/usage-subscribers.d.ts +43 -0
  63. package/dist/extensions/usage-subscribers.js +108 -0
  64. package/dist/http/api.module.js +2 -0
  65. package/dist/index.d.ts +1 -0
  66. package/dist/index.js +1 -0
  67. package/dist/infra/cli/kernel-cli.module.js +5 -0
  68. package/dist/infra/events/kernel-events.d.ts +9 -35
  69. package/dist/infra/outbox/outbox-relay.module.d.ts +6 -0
  70. package/dist/infra/outbox/outbox-relay.module.js +18 -0
  71. package/dist/infra/outbox/outbox.dispatcher.d.ts +103 -0
  72. package/dist/infra/outbox/outbox.dispatcher.js +310 -0
  73. package/dist/infra/outbox/outbox.module.d.ts +24 -0
  74. package/dist/infra/outbox/outbox.module.js +50 -0
  75. package/dist/infra/outbox/outbox.relay.d.ts +31 -0
  76. package/dist/infra/outbox/outbox.relay.js +76 -0
  77. package/dist/infra/outbox/outbox.service.d.ts +51 -0
  78. package/dist/infra/outbox/outbox.service.js +131 -0
  79. package/dist/infra/outbox/registry.d.ts +120 -0
  80. package/dist/infra/outbox/registry.js +108 -0
  81. package/dist/infra/queue/queue-names.d.ts +8 -0
  82. package/dist/infra/queue/queue-names.js +16 -0
  83. package/dist/kernel.module.js +3 -0
  84. package/dist/local-devices/device-policy.service.d.ts +4 -0
  85. package/dist/local-devices/device-policy.service.js +61 -3
  86. package/dist/local-devices/machines.d.ts +13 -5
  87. package/dist/local-devices/machines.js +45 -7
  88. package/dist/local-devices/policy-summary.d.ts +44 -1
  89. package/dist/local-devices/policy-summary.js +69 -0
  90. package/dist/models/embeddings.d.ts +9 -9
  91. package/dist/models/embeddings.js +11 -18
  92. package/dist/models/models.module.js +3 -0
  93. package/dist/orgs/orgs.service.js +7 -0
  94. package/dist/overrides.d.ts +20 -3
  95. package/dist/overrides.js +10 -2
  96. package/dist/platform/exclusions.js +5 -0
  97. package/dist/platform/ops/admin.ops.js +18 -2
  98. package/dist/platform/ops/catalog-admin.ops.d.ts +3 -1
  99. package/dist/platform/ops/catalog-admin.ops.js +32 -4
  100. package/dist/platform/ops/outbox.ops.d.ts +21 -0
  101. package/dist/platform/ops/outbox.ops.js +125 -0
  102. package/dist/platform/ops/schedules.ops.js +40 -1
  103. package/dist/platform/platform-ops.module.d.ts +2 -1
  104. package/dist/platform/platform-ops.module.js +3 -0
  105. package/dist/platform/route-schemas.js +3 -0
  106. package/dist/roles/worker.module.js +9 -4
  107. package/dist/runtime/langgraph-loop.js +13 -5
  108. package/dist/runtime/limits.d.ts +15 -0
  109. package/dist/runtime/limits.js +25 -0
  110. package/dist/runtime/loop.d.ts +8 -3
  111. package/dist/runtime/loop.js +2 -1
  112. package/dist/runtime/maintenance.d.ts +4 -2
  113. package/dist/runtime/maintenance.js +17 -2
  114. package/dist/runtime/model-call.d.ts +14 -16
  115. package/dist/runtime/model-call.js +14 -24
  116. package/dist/runtime/queue-lost-jobs.d.ts +24 -0
  117. package/dist/runtime/queue-lost-jobs.js +51 -0
  118. package/dist/runtime/run-control.d.ts +42 -5
  119. package/dist/runtime/run-control.js +66 -12
  120. package/dist/runtime/run-finish.d.ts +39 -12
  121. package/dist/runtime/run-finish.js +97 -14
  122. package/dist/runtime/run-jobs.d.ts +95 -13
  123. package/dist/runtime/run-jobs.js +199 -28
  124. package/dist/runtime/run-orphan-check.job.d.ts +142 -0
  125. package/dist/runtime/run-orphan-check.job.js +339 -0
  126. package/dist/runtime/run-processors.d.ts +16 -9
  127. package/dist/runtime/run-processors.js +1 -1
  128. package/dist/runtime/run-queue.d.ts +64 -55
  129. package/dist/runtime/run-queue.js +147 -284
  130. package/dist/runtime/run-steps.d.ts +1 -2
  131. package/dist/runtime/run-steps.js +8 -11
  132. package/dist/runtime/run-worker.d.ts +6 -17
  133. package/dist/runtime/run-worker.js +32 -34
  134. package/dist/runtime/runtime.module.d.ts +2 -3
  135. package/dist/runtime/runtime.module.js +5 -6
  136. package/dist/runtime/step-log.d.ts +16 -4
  137. package/dist/runtime/step-log.js +26 -6
  138. package/dist/runtime/stream.controller.d.ts +2 -4
  139. package/dist/runtime/stream.controller.js +5 -11
  140. package/dist/runtime/subagents/child.js +1 -1
  141. package/dist/runtime/subagents/definition.d.ts +2 -1
  142. package/dist/runtime/subagents/definition.js +4 -2
  143. package/dist/runtime/subagents/deliveries.d.ts +6 -3
  144. package/dist/runtime/subagents/deliveries.js +16 -7
  145. package/dist/runtime/subagents/dispatch.d.ts +12 -12
  146. package/dist/runtime/subagents/dispatch.js +13 -38
  147. package/dist/runtime/subagents/messages.d.ts +5 -1
  148. package/dist/runtime/subagents/messages.js +11 -3
  149. package/dist/runtime/subagents/skill-fork.d.ts +3 -3
  150. package/dist/runtime/subagents/skill-fork.js +5 -5
  151. package/dist/runtime/subagents/subagents.service.js +1 -1
  152. package/dist/runtime/subagents/tree-jobs.d.ts +61 -0
  153. package/dist/runtime/subagents/tree-jobs.js +86 -0
  154. package/dist/runtime/usage-report-jobs.d.ts +23 -48
  155. package/dist/runtime/usage-report-jobs.js +67 -119
  156. package/dist/runtime/usage-report.d.ts +28 -77
  157. package/dist/runtime/usage-report.js +35 -213
  158. package/dist/runtime/waits.d.ts +48 -198
  159. package/dist/runtime/waits.js +103 -805
  160. package/dist/sandbox/sandbox.service.d.ts +17 -4
  161. package/dist/sandbox/sandbox.service.js +55 -14
  162. package/dist/schedules/calendar.d.ts +134 -0
  163. package/dist/schedules/calendar.js +410 -0
  164. package/dist/schedules/fire.d.ts +1 -3
  165. package/dist/schedules/fire.js +5 -10
  166. package/dist/schedules/run-tracker.d.ts +0 -5
  167. package/dist/schedules/run-tracker.js +0 -13
  168. package/dist/schedules/scan.job.js +15 -20
  169. package/dist/schedules/schedule-math.d.ts +42 -0
  170. package/dist/schedules/schedule-math.js +52 -0
  171. package/dist/schedules/scheduled-tasks.service.d.ts +16 -0
  172. package/dist/schedules/scheduled-tasks.service.js +53 -0
  173. package/dist/schedules/schedules.controller.d.ts +5 -1
  174. package/dist/schedules/schedules.controller.js +15 -1
  175. package/dist/schedules/schedules.schemas.d.ts +35 -0
  176. package/dist/schedules/schedules.schemas.js +27 -0
  177. package/dist/schedules/store.d.ts +5 -0
  178. package/dist/schedules/store.js +20 -0
  179. package/dist/tools/approvals.service.d.ts +26 -7
  180. package/dist/tools/approvals.service.js +88 -10
  181. package/dist/tools/builtin-names.d.ts +5 -3
  182. package/dist/tools/builtin-names.js +12 -4
  183. package/dist/tools/builtins/builtin-tools.d.ts +1 -2
  184. package/dist/tools/builtins/builtin-tools.js +21 -17
  185. package/dist/tools/builtins/sandbox/bash.js +9 -3
  186. package/dist/tools/builtins/sandbox/common.d.ts +18 -1
  187. package/dist/tools/builtins/sandbox/common.js +14 -1
  188. package/dist/tools/builtins/sandbox/index.d.ts +2 -1
  189. package/dist/tools/builtins/sandbox/index.js +4 -1
  190. package/dist/tools/builtins/sandbox/task-list.d.ts +12 -0
  191. package/dist/tools/builtins/sandbox/task-list.js +130 -0
  192. package/dist/tools/builtins/sandbox/task-output.d.ts +8 -1
  193. package/dist/tools/builtins/sandbox/task-output.js +21 -3
  194. package/dist/tools/builtins/web/extract.d.ts +2 -4
  195. package/dist/tools/builtins/web/extract.js +2 -5
  196. package/dist/tools/pipeline.d.ts +1 -1
  197. package/dist/tools/pipeline.js +1 -1
  198. package/dist/tools/questions.service.d.ts +15 -6
  199. package/dist/tools/questions.service.js +20 -8
  200. package/dist/tools/registry.d.ts +4 -2
  201. package/dist/tools/registry.js +4 -3
  202. package/dist/tools/tools.module.js +4 -3
  203. package/package.json +3 -3
  204. package/src/migrations/0118_device_policy_ops.sql +6 -0
  205. package/src/migrations/0119_outbox.sql +22 -0
  206. package/src/migrations/0120_drop_usage_report_sweep.sql +7 -0
@@ -18,6 +18,8 @@ import { PlatformInboxService } from "../inbox/platform-inbox.service.js";
18
18
  import { KERNEL_LOGGER } from "../infra/tokens.js";
19
19
  import { OrgsService } from "../orgs/orgs.service.js";
20
20
  import { ProviderRegistry } from "../runtime/providers.js";
21
+ import { QueueLostJobs } from "../runtime/queue-lost-jobs.js";
22
+ import { OutboxDeadService } from "./outbox-dead.service.js";
21
23
  /** The most rows each attention list returns; the rest are counted in `more`. */
22
24
  export const ATTENTION_LIST_MAX = 20;
23
25
  /** The reason an extension is listed, or null when it needs nothing. */
@@ -53,8 +55,9 @@ export function providerProblem(p) {
53
55
  /**
54
56
  * The admin overview (spec §4.1, §5.2): its numbers and the 待处理 lists, read live from the
55
57
  * sources the admin pages read (`ExtensionAdmin`, the catalog, the provider registry's cached
56
- * lists). Each source is read on its own: a failing one is reported in `unavailable`. Reads are not
57
- * audited (decision D5) and not cached further (every source is PG or already Redis-cached).
58
+ * lists, the dead outbox rows, the run queue's lost-jobs canary). Each source is read on its own: a failing one is reported in
59
+ * `unavailable`. Reads are not audited (decision D5) and not cached further (every source is PG
60
+ * or already Redis-cached).
58
61
  */
59
62
  let AdminOverviewService = class AdminOverviewService {
60
63
  extensions;
@@ -66,8 +69,10 @@ let AdminOverviewService = class AdminOverviewService {
66
69
  orgs;
67
70
  accounts;
68
71
  marketplaces;
72
+ outboxDead;
73
+ lostJobs;
69
74
  log;
70
- constructor(extensions, catalog, registry, displayNames, icons, inbox, orgs, accounts, marketplaces, log) {
75
+ constructor(extensions, catalog, registry, displayNames, icons, inbox, orgs, accounts, marketplaces, outboxDead, lostJobs, log) {
71
76
  this.extensions = extensions;
72
77
  this.catalog = catalog;
73
78
  this.registry = registry;
@@ -77,6 +82,8 @@ let AdminOverviewService = class AdminOverviewService {
77
82
  this.orgs = orgs;
78
83
  this.accounts = accounts;
79
84
  this.marketplaces = marketplaces;
85
+ this.outboxDead = outboxDead;
86
+ this.lostJobs = lostJobs;
80
87
  this.log = log;
81
88
  }
82
89
  async stats() {
@@ -87,10 +94,17 @@ let AdminOverviewService = class AdminOverviewService {
87
94
  this.attention(),
88
95
  ]);
89
96
  const listed = (s) => attention[s].length + attention.more[s];
97
+ // each dead row waits for a super admin, not the one item that counts them
98
+ const dead = attention.outbox.reduce((n, o) => n + o.count, 0);
90
99
  return {
91
100
  orgs: orgs.length,
92
101
  accounts,
93
- attentionTotal: inbox + listed("extensions") + listed("updates") + listed("providers"),
102
+ attentionTotal: inbox +
103
+ listed("extensions") +
104
+ listed("updates") +
105
+ listed("providers") +
106
+ dead +
107
+ attention.queue.length,
94
108
  partial: attention.unavailable.length > 0,
95
109
  };
96
110
  }
@@ -98,16 +112,20 @@ let AdminOverviewService = class AdminOverviewService {
98
112
  // the catalog is read once: the update rows, and the plugins the inbox already lists
99
113
  const items = this.catalogItems();
100
114
  items.catch(() => { }); // awaited (and reported) by updateRows
101
- const [extensions, updates, providers] = await Promise.allSettled([
115
+ const [extensions, updates, providers, outbox, queue] = await Promise.allSettled([
102
116
  this.extensionRows(items),
103
117
  this.updateRows(items),
104
118
  this.providerRows(),
119
+ this.outboxRows(),
120
+ this.queueRows(),
105
121
  ]);
106
122
  const out = {
107
123
  extensions: [],
108
124
  updates: [],
109
125
  providers: [],
110
- more: { extensions: 0, updates: 0, providers: 0 },
126
+ outbox: [],
127
+ queue: [],
128
+ more: { extensions: 0, updates: 0, providers: 0, outbox: 0, queue: 0 },
111
129
  unavailable: [],
112
130
  };
113
131
  /** The rows of a source that answered (capped), or none and its name in `unavailable`. */
@@ -127,8 +145,22 @@ let AdminOverviewService = class AdminOverviewService {
127
145
  out.extensions = take("extensions", extensions);
128
146
  out.updates = take("updates", updates);
129
147
  out.providers = take("providers", providers);
148
+ out.outbox = take("outbox", outbox);
149
+ out.queue = take("queue", queue);
130
150
  return out;
131
151
  }
152
+ /** The `queue_lost_jobs` item when the run queue lost jobs this week, else none. */
153
+ async queueRows() {
154
+ const lost = await this.lostJobs.summary();
155
+ return lost === null ? [] : [{ kind: "queue_lost_jobs", ...lost }];
156
+ }
157
+ /** The `outbox_dead` item when some outbox rows are dead, else none. */
158
+ async outboxRows() {
159
+ const { count, oldestAt } = await this.outboxDead.summary();
160
+ if (count === 0 || oldestAt === null)
161
+ return [];
162
+ return [{ kind: "outbox_dead", count, oldestAt: oldestAt.toISOString() }];
163
+ }
132
164
  /**
133
165
  * The extensions needing a look, but not one pending approval whose plugin waits in the platform
134
166
  * inbox (a permission confirmation or an org upload: the inbox already lists and counts it). A
@@ -218,6 +250,8 @@ AdminOverviewService = __decorate([
218
250
  __param(6, Inject(OrgsService)),
219
251
  __param(7, Inject(AccountsService)),
220
252
  __param(8, Inject(MarketplacesService)),
221
- __param(9, Inject(KERNEL_LOGGER))
253
+ __param(9, Inject(OutboxDeadService)),
254
+ __param(10, Inject(QueueLostJobs)),
255
+ __param(11, Inject(KERNEL_LOGGER))
222
256
  ], AdminOverviewService);
223
257
  export { AdminOverviewService };
@@ -329,7 +329,7 @@ export async function planFromTemplate(db, orgId, row, opts) {
329
329
  }
330
330
  if (describesSubagents)
331
331
  listed.add(AGENT_TOOL);
332
- // `TaskOutput` / `TaskStop` follow `Bash`
332
+ // `TaskOutput` / `TaskStop` / `TaskList` follow `Bash`
333
333
  for (const name of AGENT_BUILTIN_NAMES)
334
334
  if (!listed.has(name))
335
335
  offBuiltin(name);
@@ -1,6 +1,7 @@
1
1
  import { EventLog } from "../conversations/event-log.js";
2
2
  import { TransactionHost } from "../infra/database/transaction-host.js";
3
3
  import { KernelEvents } from "../infra/events/kernel-events.js";
4
+ import { OutboxService } from "../infra/outbox/outbox.service.js";
4
5
  import { type BackgroundTaskRow, BackgroundTaskStore } from "./store.js";
5
6
  /** Where a task ends (as {@link BackgroundTaskStore.finish} takes it). */
6
7
  export type BackgroundFinish = Parameters<BackgroundTaskStore["finish"]>[1];
@@ -19,7 +20,8 @@ export declare const finishedEventKey: (taskId: string) => string;
19
20
  * task (two pollers, a poll and a stop) exactly one writes it. After the commit: the log's notify,
20
21
  * the `changed: "background"` frame (to the owner and, for a sub-agent's task, the conversation
21
22
  * that owns the sandbox, whose panel lists it too) and the `background_task.finished` domain event
22
- * (`BackgroundWake` wakes an idle conversation; it lives in the runtime, above this module).
23
+ * (`BackgroundWake` wakes an idle conversation; it lives in the runtime, above this module). A
24
+ * finish that may wake the conversation also owes the ensure-run backstop in the transaction.
23
25
  *
24
26
  * Holds no lock and runs no sandbox command: the caller reads `lastLines` before.
25
27
  */
@@ -28,7 +30,8 @@ export declare class BackgroundTaskFinisher {
28
30
  private readonly eventLog;
29
31
  private readonly events;
30
32
  readonly txHost: TransactionHost;
31
- constructor(store: Pick<BackgroundTaskStore, "finish">, eventLog: EventLog, events: KernelEvents, txHost: TransactionHost);
33
+ private readonly outbox;
34
+ constructor(store: Pick<BackgroundTaskStore, "finish">, eventLog: EventLog, events: KernelEvents, txHost: TransactionHost, outbox: Pick<OutboxService, "add">);
32
35
  /**
33
36
  * Finishes `task` as `to` with its last output lines (cut to their last 4 000 characters);
34
37
  * `reported` when a tool call already told the model in its run. False (nothing written) when the
@@ -13,6 +13,8 @@ import { afterCommit } from "../infra/database/after-commit.js";
13
13
  import { TransactionHost } from "../infra/database/transaction-host.js";
14
14
  import { Transactional } from "../infra/database/transactional.decorator.js";
15
15
  import { KernelEvents } from "../infra/events/kernel-events.js";
16
+ import { OutboxService } from "../infra/outbox/outbox.service.js";
17
+ import { oweEnsureRun } from "../runtime/run-jobs.js";
16
18
  import { taskOutputFile } from "./launch.js";
17
19
  import { LAST_LINES_MAX_CHARS } from "./session.js";
18
20
  import { BackgroundTaskStore } from "./store.js";
@@ -25,7 +27,8 @@ export const finishedEventKey = (taskId) => `background_task_finished:${taskId}`
25
27
  * task (two pollers, a poll and a stop) exactly one writes it. After the commit: the log's notify,
26
28
  * the `changed: "background"` frame (to the owner and, for a sub-agent's task, the conversation
27
29
  * that owns the sandbox, whose panel lists it too) and the `background_task.finished` domain event
28
- * (`BackgroundWake` wakes an idle conversation; it lives in the runtime, above this module).
30
+ * (`BackgroundWake` wakes an idle conversation; it lives in the runtime, above this module). A
31
+ * finish that may wake the conversation also owes the ensure-run backstop in the transaction.
29
32
  *
30
33
  * Holds no lock and runs no sandbox command: the caller reads `lastLines` before.
31
34
  */
@@ -34,11 +37,13 @@ let BackgroundTaskFinisher = class BackgroundTaskFinisher {
34
37
  eventLog;
35
38
  events;
36
39
  txHost;
37
- constructor(store, eventLog, events, txHost) {
40
+ outbox;
41
+ constructor(store, eventLog, events, txHost, outbox) {
38
42
  this.store = store;
39
43
  this.eventLog = eventLog;
40
44
  this.events = events;
41
45
  this.txHost = txHost;
46
+ this.outbox = outbox;
42
47
  }
43
48
  /**
44
49
  * Finishes `task` as `to` with its last output lines (cut to their last 4 000 characters);
@@ -71,6 +76,11 @@ let BackgroundTaskFinisher = class BackgroundTaskFinisher {
71
76
  },
72
77
  },
73
78
  ]);
79
+ // a finish that may wake the conversation owes the ensure-run backstop with its event: the
80
+ // wake after the commit (`BackgroundWake`) may never happen (transactional outbox spec §6)
81
+ if (event && !reported && opts.stoppedBy === undefined) {
82
+ await oweEnsureRun(this.outbox, task.conversationId, event.seq, task.orgId);
83
+ }
74
84
  const conversations = [...new Set([task.conversationId, task.sandboxConversationId])];
75
85
  afterCommit(async () => {
76
86
  if (event)
@@ -90,6 +100,7 @@ BackgroundTaskFinisher = __decorate([
90
100
  __param(0, Inject(BackgroundTaskStore)),
91
101
  __param(1, Inject(EventLog)),
92
102
  __param(2, Inject(KernelEvents)),
93
- __param(3, Inject(TransactionHost))
103
+ __param(3, Inject(TransactionHost)),
104
+ __param(4, Inject(OutboxService))
94
105
  ], BackgroundTaskFinisher);
95
106
  export { BackgroundTaskFinisher };
@@ -70,7 +70,8 @@ export declare const GROUP_LAUNCHER: string;
70
70
  * `setsid nohup sh -c 'timeout -s KILL <seconds> …'` and the sid is `$!`, the pid `setsid` started;
71
71
  * elsewhere (macOS: no `setsid`, no `timeout`, no `pgrep -s`) {@link GROUP_LAUNCHER} runs it in a
72
72
  * process group of its own, watched by the launcher, and writes the sid (the group id) itself,
73
- * which the launch waits for (up to 10 s). An
73
+ * which the launch waits for (up to 10 s). The script starts with `BACKGROUND_LAUNCH_LINE`, so a
74
+ * local device, which kills what any other exec leaves running, leaves the task alone. An
74
75
  * environment without one of the needed programs fails with a readable error on stderr and exit
75
76
  * code 127, before anything starts. `id` must be a task id and `seconds` a positive integer (both
76
77
  * go into the script unquoted).
@@ -1,5 +1,5 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import { DEFAULT_SANDBOX_ROOTS } from "@capybara-hq/plugin-sdk";
2
+ import { BACKGROUND_LAUNCH_LINE, DEFAULT_SANDBOX_ROOTS } from "@capybara-hq/plugin-sdk";
3
3
  /**
4
4
  * Launching a background Bash task (spec 2026-11-05-background-bash-design §4.2): the task id, the
5
5
  * one short exec that starts the command detached from the exec, and the model-visible result.
@@ -112,7 +112,8 @@ export const GROUP_LAUNCHER = [
112
112
  * `setsid nohup sh -c 'timeout -s KILL <seconds> …'` and the sid is `$!`, the pid `setsid` started;
113
113
  * elsewhere (macOS: no `setsid`, no `timeout`, no `pgrep -s`) {@link GROUP_LAUNCHER} runs it in a
114
114
  * process group of its own, watched by the launcher, and writes the sid (the group id) itself,
115
- * which the launch waits for (up to 10 s). An
115
+ * which the launch waits for (up to 10 s). The script starts with `BACKGROUND_LAUNCH_LINE`, so a
116
+ * local device, which kills what any other exec leaves running, leaves the task alone. An
116
117
  * environment without one of the needed programs fails with a readable error on stderr and exit
117
118
  * code 127, before anything starts. `id` must be a task id and `seconds` a positive integer (both
118
119
  * go into the script unquoted).
@@ -127,6 +128,8 @@ export function launchScript(id, command, seconds) {
127
128
  const t = `${TASKS_DIR}/${id}`;
128
129
  const need = (list) => `for c in ${list.join(" ")}; do command -v "$c" >/dev/null 2>&1 || { echo "Background tasks need ${list.join(", ")} in this environment; $c is missing." >&2; exit 127; }; done`;
129
130
  return [
131
+ // first: a local device kills what an exec leaves running, except a launch's detached task
132
+ BACKGROUND_LAUNCH_LINE,
130
133
  "set -e",
131
134
  SESSION_MODE_SCRIPT,
132
135
  `if [ "$pg" = -s ]; then ${need(REQUIRED_SESSION)}; else ${need(REQUIRED_GROUP)}; fi`,
@@ -137,6 +137,14 @@ export declare class BackgroundTaskStore {
137
137
  runningStarted(): Promise<BackgroundTaskRow[]>;
138
138
  /** The running tasks one conversation owns (not those of its sub-agents), oldest first. */
139
139
  runningOwnedBy(conversationId: string): Promise<BackgroundTaskRow[]>;
140
+ /**
141
+ * The finished tasks one conversation owns (not those of its sub-agents), the most recently
142
+ * finished first, at most `limit` of them, with how many it owns in all (`TaskList`).
143
+ */
144
+ finishedOwnedBy(conversationId: string, limit: number): Promise<{
145
+ rows: BackgroundTaskRow[];
146
+ total: number;
147
+ }>;
140
148
  /** The running tasks of one root conversation's tree (what `maxTasks` limits). */
141
149
  countRunning(sandboxConversationId: string): Promise<number>;
142
150
  /** The tasks owned by any of the conversations, newest first. */
@@ -258,6 +258,30 @@ let BackgroundTaskStore = class BackgroundTaskStore {
258
258
  .execute();
259
259
  return rows.map(toRow);
260
260
  }
261
+ /**
262
+ * The finished tasks one conversation owns (not those of its sub-agents), the most recently
263
+ * finished first, at most `limit` of them, with how many it owns in all (`TaskList`).
264
+ */
265
+ async finishedOwnedBy(conversationId, limit) {
266
+ const [rows, count] = await Promise.all([
267
+ this.db
268
+ .selectFrom("background_tasks")
269
+ .selectAll()
270
+ .where("conversation_id", "=", conversationId)
271
+ .where("status", "<>", "running")
272
+ .orderBy("finished_at", "desc")
273
+ .orderBy("id", "desc")
274
+ .limit(limit)
275
+ .execute(),
276
+ this.db
277
+ .selectFrom("background_tasks")
278
+ .select((eb) => eb.fn.countAll().as("n"))
279
+ .where("conversation_id", "=", conversationId)
280
+ .where("status", "<>", "running")
281
+ .executeTakeFirstOrThrow(),
282
+ ]);
283
+ return { rows: rows.map(toRow), total: Number(count.n) };
284
+ }
261
285
  /** The running tasks of one root conversation's tree (what `maxTasks` limits). */
262
286
  async countRunning(sandboxConversationId) {
263
287
  const r = await this.db
@@ -5,12 +5,13 @@ import { type ClientBuildSummary, ClientBuilds } from "../client/client-builds.s
5
5
  import { PluginGuides, type RenderedSetupGuide } from "../extensions/plugin-guides.js";
6
6
  import { PluginIcons } from "../extensions/plugin-icons.js";
7
7
  import { type Principal } from "../identity/principal.js";
8
- import { AdminCatalogGuideQuery, CatalogItemsQuery, CatalogUpdateBody, CatalogUpdatePreviewBody, CatalogDisabledBody as DisabledBody, CatalogGitImportBody as GitBody, CatalogNpmImportBody as NpmBody, CatalogScopeBody as ScopeBody, CatalogScopePreviewBody as ScopePreviewBody, CatalogShownBody as ShownBody } from "./admin-catalog.schemas.js";
8
+ import { AdminCatalogGuideQuery, CatalogImportUpdateBody, CatalogItemsQuery, CatalogUpdateBody, CatalogUpdatePreviewBody, CatalogDisabledBody as DisabledBody, CatalogGitImportBody as GitBody, CatalogNpmImportBody as NpmBody, CatalogScopeBody as ScopeBody, CatalogScopePreviewBody as ScopePreviewBody, CatalogShownBody as ShownBody } from "./admin-catalog.schemas.js";
9
9
  import { AppearanceApplyBody } from "./appearance.schemas.js";
10
10
  import { type AppearanceResult, AppearanceService } from "./appearance.service.js";
11
11
  import { type CatalogItemDetail, type CatalogItemView, CatalogService, type PermissionsMismatch } from "./catalog.service.js";
12
12
  import { CatalogScopeService, type ScopePreview } from "./catalog-scope.service.js";
13
13
  import { OrgCatalogService } from "./org-catalog.service.js";
14
+ import { PlatformImportUpdate, type PlatformImportUpdateResult } from "./platform-import-update.js";
14
15
  import { type PluginConfirmResult, PluginImport, type PluginReview } from "./plugin-import.js";
15
16
  import { PlatformPluginUpdater, type PlatformUpdatePreview, type PlatformUpdateResult } from "./plugin-upgrade.js";
16
17
  declare const ConfirmBody: z.ZodObject<{
@@ -41,11 +42,12 @@ export declare class AdminCatalogController {
41
42
  private readonly orgCatalog;
42
43
  private readonly scope;
43
44
  private readonly updater;
45
+ private readonly importUpdate;
44
46
  private readonly icons;
45
47
  private readonly appearance;
46
48
  private readonly clientBuilds;
47
49
  private readonly guides;
48
- constructor(catalog: CatalogService, pluginImport: PluginImport, orgCatalog: OrgCatalogService, scope: CatalogScopeService, updater: PlatformPluginUpdater, icons: PluginIcons, appearance: AppearanceService, clientBuilds: ClientBuilds, guides: PluginGuides);
50
+ constructor(catalog: CatalogService, pluginImport: PluginImport, orgCatalog: OrgCatalogService, scope: CatalogScopeService, updater: PlatformPluginUpdater, importUpdate: PlatformImportUpdate, icons: PluginIcons, appearance: AppearanceService, clientBuilds: ClientBuilds, guides: PluginGuides);
49
51
  /**
50
52
  * Prepares the import of an uploaded plugin (an npm-pack tgz as the raw body,
51
53
  * `application/octet-stream`, at most `packages.maxBytes`): reads and checks the whole plugin
@@ -108,6 +110,23 @@ export declare class AdminCatalogController {
108
110
  * is an unknown field (400 `validation_failed`).
109
111
  */
110
112
  confirm(principal: Principal, stagingId: string, body: z.infer<typeof ConfirmBody>): Promise<PluginConfirmResult>;
113
+ /**
114
+ * What confirming a staged new version of an installed platform plugin would do as the platform
115
+ * update (`PlatformImportUpdate`, the one 更新到 wizard): the update preview of the staged
116
+ * version, nothing written (cancelling the wizard leaves only the staging row). Not a platform
117
+ * plugin yet -> 409 `plugin.upgrade_target_invalid` (reason `not_installed`); staging missing ->
118
+ * 404 `plugin.staging_not_found`; otherwise as `items/:id/update/preview`.
119
+ */
120
+ importUpdatePreview(stagingId: string): Promise<PlatformUpdatePreview>;
121
+ /**
122
+ * Imports the staged version and updates the platform plugin to it, confirmed with the import
123
+ * review's `reviewHash` and the update preview's `diffHash` (201 `{ item, update }`). Stale review
124
+ * -> 409 `plugin.review_stale`, stale preview -> 409 `plugin.upgrade_stale`, another update
125
+ * running -> 409 `plugin.install_in_progress`: nothing written. The update failing after the
126
+ * import -> 409 `plugin.imported_not_updated` (the version stays in the catalog, the platform on
127
+ * its version, the extension's approval put back; details name the cause).
128
+ */
129
+ importAndUpdate(principal: Principal, stagingId: string, body: z.infer<typeof CatalogImportUpdateBody>): Promise<PlatformImportUpdateResult>;
111
130
  /**
112
131
  * The item list (`includeDeleted=true` includes deleted items). Paging: `limit` (default 200, at
113
132
  * most 1000) and `offset`; `nextOffset` is the next page's offset, null when there is none.
@@ -16,13 +16,14 @@ import { PluginIcons } from "../extensions/plugin-icons.js";
16
16
  import { CurrentPrincipal, patTokenId, requireUser, SuperAdmin, } from "../identity/principal.js";
17
17
  import { createZodDto } from "../infra/validation/create-zod-dto.js";
18
18
  import { ZBody, ZQuery } from "../infra/validation/zod-params.js";
19
- import { AdminCatalogGuideQuery, CatalogItemsQuery, CatalogUpdateBody, CatalogUpdatePreviewBody, CatalogDisabledBody as DisabledBody, CatalogGitImportBody as GitBody, CatalogHash as Hash, CatalogNpmImportBody as NpmBody, CatalogScopeOrgs as Orgs, CatalogScope as Scope, CatalogScopeBody as ScopeBody, CatalogScopePreviewBody as ScopePreviewBody, CatalogShownBody as ShownBody, } from "./admin-catalog.schemas.js";
19
+ import { AdminCatalogGuideQuery, CatalogImportUpdateBody, CatalogItemsQuery, CatalogUpdateBody, CatalogUpdatePreviewBody, CatalogDisabledBody as DisabledBody, CatalogGitImportBody as GitBody, CatalogHash as Hash, CatalogNpmImportBody as NpmBody, CatalogScopeOrgs as Orgs, CatalogScope as Scope, CatalogScopeBody as ScopeBody, CatalogScopePreviewBody as ScopePreviewBody, CatalogShownBody as ShownBody, } from "./admin-catalog.schemas.js";
20
20
  import { AppearanceApplyBody } from "./appearance.schemas.js";
21
21
  import { AppearanceService, suggestedAppearanceOf, } from "./appearance.service.js";
22
22
  import { CatalogService, } from "./catalog.service.js";
23
23
  import { CatalogScopeService } from "./catalog-scope.service.js";
24
24
  import { sendCatalogIcon } from "./org-catalog.controller.js";
25
25
  import { OrgCatalogService } from "./org-catalog.service.js";
26
+ import { PlatformImportUpdate } from "./platform-import-update.js";
26
27
  import { PluginImport } from "./plugin-import.js";
27
28
  import { PlatformPluginUpdater, } from "./plugin-upgrade.js";
28
29
  const ConfirmBody = z
@@ -66,6 +67,8 @@ class CatalogUpdatePreviewBodyDto extends createZodDto(CatalogUpdatePreviewBody,
66
67
  }
67
68
  class CatalogUpdateBodyDto extends createZodDto(CatalogUpdateBody, "CatalogUpdateBody") {
68
69
  }
70
+ class CatalogImportUpdateBodyDto extends createZodDto(CatalogImportUpdateBody, "CatalogImportUpdateBody") {
71
+ }
69
72
  class AppearanceApplyBodyDto extends createZodDto(AppearanceApplyBody, "AppearanceApplyBody") {
70
73
  }
71
74
  /**
@@ -80,16 +83,18 @@ let AdminCatalogController = class AdminCatalogController {
80
83
  orgCatalog;
81
84
  scope;
82
85
  updater;
86
+ importUpdate;
83
87
  icons;
84
88
  appearance;
85
89
  clientBuilds;
86
90
  guides;
87
- constructor(catalog, pluginImport, orgCatalog, scope, updater, icons, appearance, clientBuilds, guides) {
91
+ constructor(catalog, pluginImport, orgCatalog, scope, updater, importUpdate, icons, appearance, clientBuilds, guides) {
88
92
  this.catalog = catalog;
89
93
  this.pluginImport = pluginImport;
90
94
  this.orgCatalog = orgCatalog;
91
95
  this.scope = scope;
92
96
  this.updater = updater;
97
+ this.importUpdate = importUpdate;
93
98
  this.icons = icons;
94
99
  this.appearance = appearance;
95
100
  this.clientBuilds = clientBuilds;
@@ -169,6 +174,28 @@ let AdminCatalogController = class AdminCatalogController {
169
174
  ...(body.orgs !== undefined && { orgs: body.orgs }),
170
175
  }, actorOpts(actor));
171
176
  }
177
+ /**
178
+ * What confirming a staged new version of an installed platform plugin would do as the platform
179
+ * update (`PlatformImportUpdate`, the one 更新到 wizard): the update preview of the staged
180
+ * version, nothing written (cancelling the wizard leaves only the staging row). Not a platform
181
+ * plugin yet -> 409 `plugin.upgrade_target_invalid` (reason `not_installed`); staging missing ->
182
+ * 404 `plugin.staging_not_found`; otherwise as `items/:id/update/preview`.
183
+ */
184
+ importUpdatePreview(stagingId) {
185
+ return this.importUpdate.preview(stagingId);
186
+ }
187
+ /**
188
+ * Imports the staged version and updates the platform plugin to it, confirmed with the import
189
+ * review's `reviewHash` and the update preview's `diffHash` (201 `{ item, update }`). Stale review
190
+ * -> 409 `plugin.review_stale`, stale preview -> 409 `plugin.upgrade_stale`, another update
191
+ * running -> 409 `plugin.install_in_progress`: nothing written. The update failing after the
192
+ * import -> 409 `plugin.imported_not_updated` (the version stays in the catalog, the platform on
193
+ * its version, the extension's approval put back; details name the cause).
194
+ */
195
+ importAndUpdate(principal, stagingId, body) {
196
+ const actor = requireUser(principal);
197
+ return this.importUpdate.confirm(actor.account.id, stagingId, body, actorOpts(actor));
198
+ }
172
199
  /**
173
200
  * The item list (`includeDeleted=true` includes deleted items). Paging: `limit` (default 200, at
174
201
  * most 1000) and `offset`; `nextOffset` is the next page's offset, null when there is none.
@@ -367,6 +394,17 @@ __decorate([
367
394
  __param(1, Param("stagingId")),
368
395
  __param(2, ZBody(CatalogImportConfirmBody))
369
396
  ], AdminCatalogController.prototype, "confirm", null);
397
+ __decorate([
398
+ Get("imports/:stagingId/update-preview"),
399
+ __param(0, Param("stagingId"))
400
+ ], AdminCatalogController.prototype, "importUpdatePreview", null);
401
+ __decorate([
402
+ Post("imports/:stagingId/update"),
403
+ HttpCode(201),
404
+ __param(0, CurrentPrincipal()),
405
+ __param(1, Param("stagingId")),
406
+ __param(2, ZBody(CatalogImportUpdateBodyDto))
407
+ ], AdminCatalogController.prototype, "importAndUpdate", null);
370
408
  __decorate([
371
409
  Get("items"),
372
410
  __param(0, ZQuery(CatalogItemsQueryDto))
@@ -457,9 +495,10 @@ AdminCatalogController = __decorate([
457
495
  __param(2, Inject(OrgCatalogService)),
458
496
  __param(3, Inject(CatalogScopeService)),
459
497
  __param(4, Inject(PlatformPluginUpdater)),
460
- __param(5, Inject(PluginIcons)),
461
- __param(6, Inject(AppearanceService)),
462
- __param(7, Inject(ClientBuilds)),
463
- __param(8, Inject(PluginGuides))
498
+ __param(5, Inject(PlatformImportUpdate)),
499
+ __param(6, Inject(PluginIcons)),
500
+ __param(7, Inject(AppearanceService)),
501
+ __param(8, Inject(ClientBuilds)),
502
+ __param(9, Inject(PluginGuides))
464
503
  ], AdminCatalogController);
465
504
  export { AdminCatalogController };
@@ -20,6 +20,11 @@ export declare const CatalogUpdateBody: z.ZodObject<{
20
20
  version: z.ZodString;
21
21
  diffHash: z.ZodString;
22
22
  }, z.core.$strict>;
23
+ /** `POST imports/:stagingId/update`: the import review's hash and the update preview's. */
24
+ export declare const CatalogImportUpdateBody: z.ZodObject<{
25
+ reviewHash: z.ZodString;
26
+ diffHash: z.ZodString;
27
+ }, z.core.$strict>;
23
28
  /** `:id` as the routes take it (`@Param`). */
24
29
  export declare const CatalogIdPathParams: z.ZodObject<{
25
30
  id: z.ZodString;
@@ -13,6 +13,10 @@ export const CatalogUpdatePreviewBody = z.object({ version: CatalogVersion }).st
13
13
  export const CatalogUpdateBody = z
14
14
  .object({ version: CatalogVersion, diffHash: CatalogHash })
15
15
  .strict();
16
+ /** `POST imports/:stagingId/update`: the import review's hash and the update preview's. */
17
+ export const CatalogImportUpdateBody = z
18
+ .object({ reviewHash: CatalogHash, diffHash: CatalogHash })
19
+ .strict();
16
20
  /** `:id` as the routes take it (`@Param`). */
17
21
  export const CatalogIdPathParams = z.object({ id: z.string().min(1) });
18
22
  /** `:stagingId` of a staged import as the route takes it (`@Param`). */
@@ -75,6 +79,10 @@ export const ADMIN_CATALOG_ROUTES = [
75
79
  { route: "POST /api/v1/admin/catalog/import/npm", body: CatalogNpmImportBody },
76
80
  { route: "POST /api/v1/admin/catalog/import/git", body: CatalogGitImportBody },
77
81
  { route: "GET /api/v1/admin/catalog/imports/:stagingId", params: CatalogStagingPathParams },
82
+ {
83
+ route: "GET /api/v1/admin/catalog/imports/:stagingId/update-preview",
84
+ params: CatalogStagingPathParams,
85
+ },
78
86
  {
79
87
  route: "POST /api/v1/admin/catalog/items/:id/scope/preview",
80
88
  params: CatalogIdPathParams,
@@ -2,7 +2,8 @@ import { CatalogImportModule } from "./catalog-import.module.js";
2
2
  export { CatalogImportModule };
3
3
  /**
4
4
  * The plugin catalog (spec §4, §10): {@link CatalogImportModule}, catalog requests, org installs
5
- * with their dependencies, the platform plugin update (`PlatformPluginUpdater`), the admin's scope
5
+ * with their dependencies, the platform plugin update (`PlatformPluginUpdater`) and the update to a
6
+ * staged version (`PlatformImportUpdate`), the admin's scope
6
7
  * and disable
7
8
  * (`CatalogScopeService`), org admins' plugin uploads (`OrgPluginUploads`) and their own plugins (`OwnedItems`), and the producer of the plugin scope sync
8
9
  * (`PluginScopeSync`; its consumer is the worker role's `PluginScopeWorkersModule`). The catalog's periodic jobs (`CatalogJobs`) are
@@ -21,6 +21,7 @@ import { OrgCatalogService } from "./org-catalog.service.js";
21
21
  import { OrgExtensionSwitch } from "./org-extension-switch.js";
22
22
  import { OrgPluginUploads } from "./org-plugin-uploads.js";
23
23
  import { OwnedItems } from "./owned-items.js";
24
+ import { PlatformImportUpdate } from "./platform-import-update.js";
24
25
  import { PluginInstaller } from "./plugin-install.js";
25
26
  import { PlatformPluginUpdater } from "./plugin-upgrade.js";
26
27
  import { CatalogRequestsService } from "./requests.service.js";
@@ -32,7 +33,8 @@ const PLUGIN_SCOPE_QUEUE_MODULE = QueueModule.registerQueue(PLUGIN_SCOPE_QUEUE);
32
33
  const MAINTENANCE_QUEUE_MODULE = QueueModule.registerQueue(MAINTENANCE_QUEUE_NAME);
33
34
  /**
34
35
  * The plugin catalog (spec §4, §10): {@link CatalogImportModule}, catalog requests, org installs
35
- * with their dependencies, the platform plugin update (`PlatformPluginUpdater`), the admin's scope
36
+ * with their dependencies, the platform plugin update (`PlatformPluginUpdater`) and the update to a
37
+ * staged version (`PlatformImportUpdate`), the admin's scope
36
38
  * and disable
37
39
  * (`CatalogScopeService`), org admins' plugin uploads (`OrgPluginUploads`) and their own plugins (`OwnedItems`), and the producer of the plugin scope sync
38
40
  * (`PluginScopeSync`; its consumer is the worker role's `PluginScopeWorkersModule`). The catalog's periodic jobs (`CatalogJobs`) are
@@ -69,6 +71,7 @@ CatalogModule = __decorate([
69
71
  ComponentScopeService,
70
72
  OrgPluginUploads,
71
73
  OwnedItems,
74
+ PlatformImportUpdate,
72
75
  ],
73
76
  exports: [
74
77
  PLUGIN_SCOPE_QUEUE_MODULE,
@@ -84,6 +87,7 @@ CatalogModule = __decorate([
84
87
  ComponentScopeService,
85
88
  OrgPluginUploads,
86
89
  OwnedItems,
90
+ PlatformImportUpdate,
87
91
  ],
88
92
  })
89
93
  ], CatalogModule);
@@ -188,7 +188,7 @@ export declare function headsToResolve(entries: readonly MarketplaceEntry[], imp
188
188
  /** The host versions (an entry's `minKernel` is judged against them). */
189
189
  export declare const HOST_VERSIONS: {
190
190
  readonly kernel: string;
191
- readonly sdk: "0.1.0-rc.17";
191
+ readonly sdk: "0.1.0-rc.18";
192
192
  };
193
193
  /** Whether this deployment satisfies the entry's `minKernel`. */
194
194
  export declare const entryCompatible: (e: Pick<MarketplaceEntry, "minKernel">) => boolean;
@@ -0,0 +1,50 @@
1
+ import type { KernelLogger } from "../log.js";
2
+ import type { ActorOpts, CatalogItemView } from "./catalog.service.js";
3
+ import { PluginImport } from "./plugin-import.js";
4
+ import { PlatformPluginUpdater, type PlatformUpdatePreview, type PlatformUpdateResult } from "./plugin-upgrade.js";
5
+ /** `POST /api/v1/admin/catalog/imports/:stagingId/update`: the import and the update it applied. */
6
+ export interface PlatformImportUpdateResult {
7
+ item: CatalogItemView;
8
+ update: PlatformUpdateResult;
9
+ }
10
+ /**
11
+ * The platform's update of an installed plugin to a version staged for import (the one wizard of
12
+ * the admin plugin pages, maintainer decision 2026-11-13): the import review (prepared as any
13
+ * import, `PluginImport.prepare`) and the platform update preview of the staged version
14
+ * ({@link preview}: `PlatformPluginUpdater.previewStaged`, the target read from the staging) are
15
+ * confirmed together ({@link confirm} with the review's `reviewHash` and the preview's `diffHash`).
16
+ * Nothing is written before that confirm: cancelling the wizard leaves only the staging row, which
17
+ * expires like any staged import.
18
+ *
19
+ * The confirm holds the item's update lock (`PlatformPluginUpdater.withItemLock`, one per item
20
+ * across replicas: another update -> 409 `plugin.install_in_progress`), recomputes the preview
21
+ * (another `diffHash` -> 409 `plugin.upgrade_stale`, nothing written), commits the import without
22
+ * deploying its extension (the permission snapshot is written as the review approved it), then
23
+ * runs the platform update, which moves the extension with its own rollback and writes the
24
+ * components in one transaction. When that update fails, the platform stays on its version, the
25
+ * extension where it was and its previous approval is put back; the imported version stays in the
26
+ * catalog (its 版本 tab, and 更新到 retries the update with the plain update preview): 409
27
+ * `plugin.imported_not_updated` says so, with the cause in the details. No in-process state.
28
+ */
29
+ export declare class PlatformImportUpdate {
30
+ private readonly pluginImport;
31
+ private readonly updater;
32
+ private readonly log;
33
+ constructor(pluginImport: PluginImport, updater: PlatformPluginUpdater, log: KernelLogger);
34
+ /**
35
+ * The platform update the staged version would apply (writes nothing). The staging must be a
36
+ * platform staging (else 404 `plugin.staging_not_found`) of a new version of a live
37
+ * platform-owned plugin (else 409 `plugin.upgrade_target_invalid`, reason `not_installed`); the
38
+ * updater's refusals apply as for any update preview. `needsApproval` is false: the confirm
39
+ * approves the permissions the review shows.
40
+ */
41
+ preview(stagingId: string): Promise<PlatformUpdatePreview>;
42
+ /** Confirms the import and the update together (see the class comment). */
43
+ confirm(actor: string, stagingId: string, input: {
44
+ reviewHash: string;
45
+ diffHash: string;
46
+ }, opts?: ActorOpts): Promise<PlatformImportUpdateResult>;
47
+ /** The staged target, which must be a new version of a live platform-owned plugin. */
48
+ private target;
49
+ private previewOf;
50
+ }