@opengeni/worker-bundle 1.0.0-canary.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/dist/activities/agent-run-admission.d.ts +3 -1
  2. package/dist/activities/agent-turn/admission.d.ts +24 -3
  3. package/dist/activities/agent-turn/agent-build.d.ts +4 -0
  4. package/dist/activities/agent-turn/claude-usage-observer.d.ts +58 -0
  5. package/dist/activities/agent-turn/code-search.d.ts +60 -0
  6. package/dist/activities/agent-turn/codex-capacity.d.ts +13 -0
  7. package/dist/activities/agent-turn/codex-plan-entitlement.d.ts +105 -0
  8. package/dist/activities/agent-turn/codex.d.ts +3 -2
  9. package/dist/activities/agent-turn/compaction-prep.d.ts +21 -5
  10. package/dist/activities/agent-turn/credential-leases.d.ts +19 -33
  11. package/dist/activities/agent-turn/credential-restriction.d.ts +3 -0
  12. package/dist/activities/agent-turn/errors.d.ts +82 -11
  13. package/dist/activities/agent-turn/failure-settlement.d.ts +6 -3
  14. package/dist/activities/agent-turn/final-reply.d.ts +18 -0
  15. package/dist/activities/agent-turn/finalization-monitor.d.ts +17 -0
  16. package/dist/activities/agent-turn/finalization.d.ts +4 -2
  17. package/dist/activities/agent-turn/github-repository-bindings.d.ts +2 -0
  18. package/dist/activities/agent-turn/governance-model.d.ts +9 -2
  19. package/dist/activities/agent-turn/history-sink.d.ts +2 -0
  20. package/dist/activities/agent-turn/history.d.ts +2 -0
  21. package/dist/activities/agent-turn/input-wait-reply.d.ts +41 -0
  22. package/dist/activities/agent-turn/media-artifacts.d.ts +5 -3
  23. package/dist/activities/agent-turn/model-call-admission.d.ts +15 -0
  24. package/dist/activities/agent-turn/model-usage.d.ts +6 -0
  25. package/dist/activities/agent-turn/model-wire-dispatch.d.ts +15 -0
  26. package/dist/activities/agent-turn/optional-repositories.d.ts +59 -0
  27. package/dist/activities/agent-turn/provider-dispatch-barrier.d.ts +12 -1
  28. package/dist/activities/agent-turn/quiescence.d.ts +7 -7
  29. package/dist/activities/agent-turn/recovery-warning.d.ts +9 -0
  30. package/dist/activities/agent-turn/refresh-credentials.d.ts +6 -0
  31. package/dist/activities/agent-turn/run-credentials.d.ts +8 -3
  32. package/dist/activities/agent-turn/sandbox-establish.d.ts +2 -1
  33. package/dist/activities/agent-turn/sandbox-provision.d.ts +3 -1
  34. package/dist/activities/agent-turn/sandbox-route.d.ts +3 -20
  35. package/dist/activities/agent-turn/sandbox-runtime.d.ts +2 -2
  36. package/dist/activities/agent-turn/session-title.d.ts +34 -2
  37. package/dist/activities/agent-turn/skill-checkout.d.ts +25 -6
  38. package/dist/activities/agent-turn/skill-read.d.ts +70 -2
  39. package/dist/activities/agent-turn/skill-remove.d.ts +14 -0
  40. package/dist/activities/agent-turn/skill-search.d.ts +3 -1
  41. package/dist/activities/agent-turn/skill-tools.d.ts +15 -2
  42. package/dist/activities/agent-turn/skill-transfer.d.ts +18 -6
  43. package/dist/activities/agent-turn/stream-attempt.d.ts +7 -3
  44. package/dist/activities/agent-turn/subscription-lease.d.ts +38 -0
  45. package/dist/activities/agent-turn/tool-environment.d.ts +56 -6
  46. package/dist/activities/agent-turn/tool-policy.d.ts +43 -0
  47. package/dist/activities/agent-turn/turn-context.d.ts +29 -0
  48. package/dist/activities/agent-turn/turn-sandbox-access.d.ts +3 -0
  49. package/dist/activities/agent-turn/xai-capacity.d.ts +2 -1
  50. package/dist/activities/automations.d.ts +3 -1
  51. package/dist/activities/browser-deadline-checkpoint.d.ts +29 -0
  52. package/dist/activities/codemode-dispatcher.d.ts +9 -2
  53. package/dist/activities/codex-capacity.d.ts +18 -2
  54. package/dist/activities/codex-rotation.d.ts +7 -1
  55. package/dist/activities/context-compaction.d.ts +16 -2
  56. package/dist/activities/gmail-files.d.ts +32 -0
  57. package/dist/activities/goals.d.ts +8 -0
  58. package/dist/activities/knowledge-indexing.d.ts +19 -0
  59. package/dist/activities/mcp-account-routes.d.ts +3 -0
  60. package/dist/activities/mcp-credentials.d.ts +12 -13
  61. package/dist/activities/mcp-operation-authority.d.ts +0 -5
  62. package/dist/activities/model-call-fact-reconciler.d.ts +34 -0
  63. package/dist/activities/open-suffix-resume.d.ts +13 -0
  64. package/dist/activities/parent-wake.d.ts +6 -12
  65. package/dist/activities/retained-screenshots.d.ts +7 -2
  66. package/dist/activities/rig-verification.d.ts +2 -3
  67. package/dist/activities/run-credential-renewal.d.ts +1 -0
  68. package/dist/activities/run-credentials.d.ts +15 -3
  69. package/dist/activities/sandbox-images.d.ts +1 -1
  70. package/dist/activities/sandbox-lease.d.ts +47 -9
  71. package/dist/activities/scheduled-tasks.d.ts +4 -35
  72. package/dist/activities/session-state.d.ts +70 -3
  73. package/dist/activities/types.d.ts +55 -6
  74. package/dist/activities/workspace-capture.d.ts +2 -0
  75. package/dist/activities/workspace-credential-provider.d.ts +46 -0
  76. package/dist/{activities-control-HMFGSKC5.js → activities-control-BNPTRTOE.js} +1896 -418
  77. package/dist/activities-control-BNPTRTOE.js.map +1 -0
  78. package/dist/activities-control.d.ts +182 -4
  79. package/dist/{activities-turn-AP67JADS.js → activities-turn-5LGZOSVU.js} +4476 -1327
  80. package/dist/activities-turn-5LGZOSVU.js.map +1 -0
  81. package/dist/activities.d.ts +182 -4
  82. package/dist/artifact-materializer-entry.js +5 -2
  83. package/dist/artifact-materializer-entry.js.map +1 -1
  84. package/dist/artifact-outbox-entry.js +5 -2
  85. package/dist/artifact-outbox-entry.js.map +1 -1
  86. package/dist/browser-deadline-checkpoint-contract.d.ts +2 -0
  87. package/dist/{chunk-A4XQWVZK.js → chunk-3ZTRWDMN.js} +307 -4
  88. package/dist/chunk-3ZTRWDMN.js.map +1 -0
  89. package/dist/{chunk-RAMNYSAW.js → chunk-4ER3YXOT.js} +22 -4
  90. package/dist/chunk-4ER3YXOT.js.map +1 -0
  91. package/dist/{chunk-CYITU5UH.js → chunk-LJX7Z2KP.js} +1445 -343
  92. package/dist/chunk-LJX7Z2KP.js.map +1 -0
  93. package/dist/editable-artifact-materializer-service.d.ts +2 -2
  94. package/dist/editable-artifact-outbox-service.d.ts +2 -2
  95. package/dist/host-export-pump.d.ts +3 -1
  96. package/dist/host-export.d.ts +1 -1
  97. package/dist/host-export.js +1 -1
  98. package/dist/http.d.ts +1 -1
  99. package/dist/index.d.ts +95 -3
  100. package/dist/index.js +137 -43
  101. package/dist/index.js.map +1 -1
  102. package/dist/observability-metrics.d.ts +118 -8
  103. package/dist/retained-process-diagnostics.d.ts +4 -0
  104. package/dist/retained-process-retry.d.ts +7 -0
  105. package/dist/sandbox-resume.d.ts +55 -13
  106. package/dist/sandbox-routing.d.ts +5 -3
  107. package/dist/sandbox-snapshot-diagnostics.d.ts +20 -0
  108. package/dist/session-event-fanout.d.ts +6 -0
  109. package/dist/warm-capture-holder.d.ts +3 -0
  110. package/dist/worker-service-lifecycle.d.ts +12 -0
  111. package/dist/workflow-bundle.js +273 -41
  112. package/dist/workflows/activities.d.ts +1 -1
  113. package/dist/workflows/browser-deadline-checkpoint.d.ts +9 -0
  114. package/dist/workflows/scheduled-tasks.d.ts +4 -0
  115. package/dist/workflows/session.d.ts +36 -0
  116. package/dist/workflows.d.ts +1 -0
  117. package/package.json +21 -19
  118. package/src/activities/agent-run-admission.ts +17 -1
  119. package/src/activities/agent-turn/admission.ts +66 -3
  120. package/src/activities/agent-turn/agent-build.ts +84 -10
  121. package/src/activities/agent-turn/claim.ts +186 -43
  122. package/src/activities/agent-turn/claude-usage-observer.ts +221 -0
  123. package/src/activities/agent-turn/code-search.ts +275 -0
  124. package/src/activities/agent-turn/codex-capacity.ts +172 -8
  125. package/src/activities/agent-turn/codex-plan-entitlement.ts +254 -0
  126. package/src/activities/agent-turn/codex.ts +13 -5
  127. package/src/activities/agent-turn/compaction-prep.ts +170 -102
  128. package/src/activities/agent-turn/credential-leases.ts +122 -216
  129. package/src/activities/agent-turn/credential-restriction.ts +14 -0
  130. package/src/activities/agent-turn/errors.ts +647 -48
  131. package/src/activities/agent-turn/failure-settlement.ts +682 -130
  132. package/src/activities/agent-turn/final-reply.ts +46 -0
  133. package/src/activities/agent-turn/finalization-monitor.ts +120 -0
  134. package/src/activities/agent-turn/finalization.ts +146 -43
  135. package/src/activities/agent-turn/github-repository-bindings.ts +3 -1
  136. package/src/activities/agent-turn/governance-model.ts +69 -28
  137. package/src/activities/agent-turn/history-sink.ts +16 -2
  138. package/src/activities/agent-turn/history.ts +38 -11
  139. package/src/activities/agent-turn/input-wait-reply.ts +103 -0
  140. package/src/activities/agent-turn/knowledge-source-tools.ts +2 -0
  141. package/src/activities/agent-turn/media-artifacts.ts +2 -1
  142. package/src/activities/agent-turn/model-call-admission.ts +94 -0
  143. package/src/activities/agent-turn/model-usage.ts +64 -3
  144. package/src/activities/agent-turn/model-wire-dispatch.ts +27 -0
  145. package/src/activities/agent-turn/optional-repositories.ts +224 -0
  146. package/src/activities/agent-turn/provider-dispatch-barrier.ts +22 -0
  147. package/src/activities/agent-turn/quiescence.ts +10 -7
  148. package/src/activities/agent-turn/recovery-warning.ts +16 -0
  149. package/src/activities/agent-turn/refresh-credentials.ts +44 -0
  150. package/src/activities/agent-turn/run-credentials.ts +116 -55
  151. package/src/activities/agent-turn/run.ts +320 -93
  152. package/src/activities/agent-turn/sandbox-establish.ts +84 -8
  153. package/src/activities/agent-turn/sandbox-provision.ts +3 -1
  154. package/src/activities/agent-turn/sandbox-route.ts +4 -21
  155. package/src/activities/agent-turn/sandbox-runtime.ts +343 -18
  156. package/src/activities/agent-turn/session-title.ts +81 -5
  157. package/src/activities/agent-turn/skill-checkout.ts +111 -24
  158. package/src/activities/agent-turn/skill-install.ts +1 -1
  159. package/src/activities/agent-turn/skill-read.ts +374 -28
  160. package/src/activities/agent-turn/skill-remove.ts +46 -0
  161. package/src/activities/agent-turn/skill-save.ts +1 -1
  162. package/src/activities/agent-turn/skill-search.ts +10 -1
  163. package/src/activities/agent-turn/skill-selection.ts +4 -0
  164. package/src/activities/agent-turn/skill-tools.ts +112 -18
  165. package/src/activities/agent-turn/skill-transfer.ts +68 -23
  166. package/src/activities/agent-turn/stream-attempt.ts +313 -77
  167. package/src/activities/agent-turn/subscription-lease.ts +115 -0
  168. package/src/activities/agent-turn/tool-environment.ts +316 -167
  169. package/src/activities/agent-turn/tool-policy.ts +80 -1
  170. package/src/activities/agent-turn/turn-context.ts +35 -0
  171. package/src/activities/agent-turn/turn-sandbox-access.ts +7 -1
  172. package/src/activities/agent-turn/xai-capacity.ts +94 -51
  173. package/src/activities/automations.ts +76 -4
  174. package/src/activities/browser-deadline-checkpoint.ts +279 -0
  175. package/src/activities/capabilities.ts +108 -11
  176. package/src/activities/codemode-dispatcher.ts +36 -8
  177. package/src/activities/codex-capacity.ts +60 -25
  178. package/src/activities/codex-rotation.ts +39 -17
  179. package/src/activities/context-compaction.ts +75 -18
  180. package/src/activities/gmail-files.ts +88 -0
  181. package/src/activities/goals.ts +116 -121
  182. package/src/activities/google-drive-publication.ts +1 -3
  183. package/src/activities/image-generation-operation.ts +1 -1
  184. package/src/activities/knowledge-indexing.ts +269 -15
  185. package/src/activities/knowledge-source-sync.ts +10 -0
  186. package/src/activities/mcp-account-routes.ts +17 -0
  187. package/src/activities/mcp-credentials.ts +83 -194
  188. package/src/activities/mcp-operation-authority.ts +16 -41
  189. package/src/activities/model-call-fact-reconciler.ts +112 -0
  190. package/src/activities/open-suffix-resume.ts +15 -2
  191. package/src/activities/parent-wake.ts +46 -21
  192. package/src/activities/retained-screenshots.ts +82 -11
  193. package/src/activities/rig-verification.ts +38 -37
  194. package/src/activities/run-credential-renewal.ts +13 -1
  195. package/src/activities/run-credentials.ts +59 -17
  196. package/src/activities/run-input.ts +25 -16
  197. package/src/activities/sandbox-images.ts +0 -1
  198. package/src/activities/sandbox-lease.ts +798 -82
  199. package/src/activities/scheduled-tasks.ts +550 -178
  200. package/src/activities/session-state.ts +245 -6
  201. package/src/activities/site-auth-maintenance.ts +3 -0
  202. package/src/activities/types.ts +72 -5
  203. package/src/activities/workflow-wake.ts +2 -17
  204. package/src/activities/workspace-capture.ts +13 -2
  205. package/src/activities/workspace-credential-provider.ts +283 -0
  206. package/src/activities-control.ts +4 -0
  207. package/src/activities-turn.ts +5 -0
  208. package/src/activities.ts +13 -0
  209. package/src/activity-services.ts +16 -4
  210. package/src/browser-deadline-checkpoint-contract.ts +5 -0
  211. package/src/editable-artifact-hint-broker.ts +3 -0
  212. package/src/editable-artifact-materializer-service.ts +7 -3
  213. package/src/editable-artifact-outbox-service.ts +4 -4
  214. package/src/github-rest-mcp.ts +1 -2
  215. package/src/host-export-pump.ts +26 -4
  216. package/src/host-export.ts +3 -0
  217. package/src/http.ts +4 -2
  218. package/src/index.ts +99 -37
  219. package/src/observability-metrics.ts +510 -5
  220. package/src/personal-github-git-credentials.ts +2 -1
  221. package/src/pr-review-credentials.ts +1 -3
  222. package/src/retained-process-diagnostics.ts +50 -0
  223. package/src/retained-process-retry.ts +36 -0
  224. package/src/sandbox-resume.ts +388 -101
  225. package/src/sandbox-routing.ts +150 -25
  226. package/src/sandbox-snapshot-diagnostics.ts +112 -0
  227. package/src/session-event-fanout.ts +23 -0
  228. package/src/warm-capture-holder.ts +15 -0
  229. package/src/worker-service-lifecycle.ts +53 -0
  230. package/src/workflows/activities.ts +16 -1
  231. package/src/workflows/browser-deadline-checkpoint.ts +50 -0
  232. package/src/workflows/file-upload-reaper.ts +2 -0
  233. package/src/workflows/scheduled-tasks.ts +26 -13
  234. package/src/workflows/session.ts +242 -22
  235. package/src/workflows.ts +4 -0
  236. package/dist/activities/agent-turn/knowledge-sources.d.ts +0 -20
  237. package/dist/activities/packs.d.ts +0 -55
  238. package/dist/activities-control-HMFGSKC5.js.map +0 -1
  239. package/dist/activities-turn-AP67JADS.js.map +0 -1
  240. package/dist/chunk-A4XQWVZK.js.map +0 -1
  241. package/dist/chunk-CYITU5UH.js.map +0 -1
  242. package/dist/chunk-RAMNYSAW.js.map +0 -1
  243. package/src/activities/agent-turn/knowledge-sources.ts +0 -86
  244. package/src/activities/packs.ts +0 -166
@@ -1,3 +1,8 @@
1
+ import { getScheduledTaskClaudeProviderAccountAuthoritySnapshot } from "@opengeni/db";
2
+ import {
3
+ ATLASSIAN_NATIVE_RETIRED_REASON,
4
+ isRetiredNativeAtlassianTask,
5
+ } from "@opengeni/contracts/atlassian-native-retirement";
1
6
  import {
2
7
  AUTOMATIC_SESSION_TITLE_FALLBACK,
3
8
  DEFAULT_FIRST_PARTY_MCP_PERMISSIONS,
@@ -10,28 +15,46 @@ import {
10
15
  SessionAgentAccess,
11
16
  SessionMemoryScope,
12
17
  normalizeAutomaticSessionTitle,
18
+ metadataWithTurnExecutionPolicyV1,
19
+ mergeResourceRefs,
13
20
  scheduledOccurrencePayloadUtf8Bytes,
14
21
  stableJson,
15
22
  TurnExecutionPolicyV1,
16
23
  type ScheduledTask,
17
24
  type ScheduledTaskRun,
25
+ type ResolvedAgentConfig,
18
26
  } from "@opengeni/contracts";
19
27
  import {
20
- defaultSessionMcpServerIds,
21
28
  openGeniSlackBotMetadata,
22
29
  requireOpenGeniSlackBotConnection,
30
+ withScheduledSlackBotPostingTools,
23
31
  resolveWorkspaceCatalogSettings,
32
+ resolveScheduledTaskDefaultModel,
24
33
  resolveSessionToolPolicy,
25
34
  workspaceCustomModelReference,
26
35
  lockActiveCustomModelForAdmission,
27
36
  scheduledSlackBotConnectionId,
37
+ freezeConnectionAccounts,
38
+ ConnectionAccountSelectionError,
39
+ scheduledConnectionSurfaceEligibility,
40
+ scheduledConnectionTools,
41
+ settingsWithEnabledCapabilityMcpServers,
42
+ settingsWithSessionMcpServerMetadata,
28
43
  swapActiveSandbox,
44
+ applySessionAgentConfigWriteThrough,
45
+ resolveSessionAgentConfigForCreate,
46
+ recordSessionCreated,
47
+ recordUserMessageAccepted,
48
+ type ProductUsageMetricsSink,
29
49
  } from "@opengeni/core";
30
50
  import {
31
51
  appendSessionEvents,
32
52
  addSessionSystemUpdateWithSourceMutation,
33
53
  bindScheduledTaskRunSessionInTransaction,
34
54
  createScheduledTaskRun,
55
+ recordScheduledTaskAdmissionFailure,
56
+ recordScheduledTaskAdmissionRefusal,
57
+ type ScheduledTaskAdmissionRefusalReason,
35
58
  createSession,
36
59
  createSessionWithIdempotencyKeyResult,
37
60
  enqueueSessionWorkflowWakeIfRunnable,
@@ -41,7 +64,6 @@ import {
41
64
  getScheduledTaskRunAcceptedExecution,
42
65
  getScheduledTaskRunByProducerKey,
43
66
  getScheduledTargetSessionExecution,
44
- getScheduledTaskPersonalConnectionDelegations,
45
67
  getScheduledTaskRunPersonalResourceAuthority,
46
68
  getScheduledTaskPersonalResourceAuthoritySubject,
47
69
  getScheduledTaskRevisionAuthority,
@@ -53,11 +75,11 @@ import {
53
75
  getNestedAgentDepthDeploymentPolicy,
54
76
  getSessionByCreateIdempotencyKey,
55
77
  getSessionCreationExecutionPolicy,
78
+ metadataWithAgentConfigCreateIdentity,
56
79
  getVariableSet,
57
80
  isCodexBilledModel,
58
81
  initializeSessionStartAtomically,
59
82
  materializeScheduledTaskReusableSessionFromRun,
60
- listEnabledMcpCapabilityServerIds,
61
83
  listInstalledApiIntegrationServerIdsForDelegations,
62
84
  markScheduledTaskRunFailedIfQueued,
63
85
  markScheduledTaskRunSkippedIfQueued,
@@ -112,6 +134,30 @@ type ScheduledTemporalActivityIdentity = {
112
134
  activityId: string;
113
135
  };
114
136
 
137
+ export function scheduledTaskRunExecutionPolicy(
138
+ policy: TurnExecutionPolicyV1,
139
+ input: DispatchScheduledTaskRunInput,
140
+ creatorRestriction?: "developer_setup",
141
+ ): TurnExecutionPolicyV1 {
142
+ return creatorRestriction === "developer_setup" ||
143
+ (input.triggerType !== "scheduled" && input.credentialRestriction === "developer_setup")
144
+ ? { ...policy, credentialRestriction: "developer_setup" }
145
+ : policy;
146
+ }
147
+
148
+ /** Only a creator ceiling is standing; a manual caller ceiling is per-run. */
149
+ export function scheduledSessionExecutionPolicyMetadata(
150
+ policy: unknown,
151
+ creatorRestriction?: "developer_setup",
152
+ ): Record<string, unknown> {
153
+ if (policy === undefined || policy === null) return {};
154
+ const acceptedPolicy = TurnExecutionPolicyV1.parse(policy);
155
+ return creatorRestriction === "developer_setup" &&
156
+ acceptedPolicy.credentialRestriction === "developer_setup"
157
+ ? metadataWithTurnExecutionPolicyV1({}, acceptedPolicy)
158
+ : {};
159
+ }
160
+
115
161
  export function scheduledTaskRunProducerKey(
116
162
  input: DispatchScheduledTaskRunInput,
117
163
  suppliedActivityIdentity?: ScheduledTemporalActivityIdentity,
@@ -181,6 +227,15 @@ export function scheduledTaskGeneratedSessionCreateIdempotencyKey(producerKey: s
181
227
  * alone, with no Intl call and so no host ICU or tzdata build to make the two
182
228
  * texts differ.
183
229
  */
230
+ /** A scheduler-generated run session: a service-created root session. */
231
+ function recordScheduledSessionCreated(observability: ProductUsageMetricsSink | undefined): void {
232
+ recordSessionCreated(observability, {
233
+ surface: "scheduled",
234
+ createdByKind: "service",
235
+ parentSessionId: null,
236
+ });
237
+ }
238
+
184
239
  export function scheduledTaskSessionTitle(taskName: string): string {
185
240
  return normalizeAutomaticSessionTitle(taskName) ?? "Scheduled run";
186
241
  }
@@ -280,6 +335,9 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
280
335
  producerKey: stableProducerKey,
281
336
  });
282
337
  if (priorRun?.actionKind === "agent_turn") {
338
+ if (priorRun.admissionDiagnostic || priorRun.admissionRefusal) {
339
+ return scheduledAdmissionRefusalResult(priorRun);
340
+ }
283
341
  const acceptedExecution = await getScheduledTaskRunAcceptedExecution(db, {
284
342
  workspaceId: input.workspaceId,
285
343
  runId: priorRun.id,
@@ -294,6 +352,15 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
294
352
  }
295
353
  throw new Error("scheduled agent run is missing accepted execution truth");
296
354
  }
355
+ if (priorRun.status === "queued" && isRetiredNativeAtlassianTask(acceptedExecution.task)) {
356
+ await markScheduledTaskRunFailedIfQueued(
357
+ db,
358
+ priorRun.workspaceId,
359
+ priorRun.id,
360
+ ATLASSIAN_NATIVE_RETIRED_REASON,
361
+ );
362
+ return scheduledRunTerminalResult(ATLASSIAN_NATIVE_RETIRED_REASON);
363
+ }
297
364
  await recordScheduledTaskFiredUsage(db, acceptedExecution, priorRun);
298
365
  if (priorRun.status === "dispatched") {
299
366
  return await replayScheduledTaskDispatch({
@@ -317,6 +384,7 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
317
384
  try {
318
385
  return await recoverBoundScheduledTaskDispatch({
319
386
  db,
387
+ observability: baseService.observability,
320
388
  bus,
321
389
  settings,
322
390
  wakeSessionWorkflow,
@@ -361,6 +429,8 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
361
429
  if (!task) {
362
430
  return { action: "deleted" };
363
431
  }
432
+ if (isRetiredNativeAtlassianTask(task))
433
+ return { action: "blocked", reason: ATLASSIAN_NATIVE_RETIRED_REASON };
364
434
  if (task.action.kind !== "agent_turn") {
365
435
  return { action: "blocked", reason: "legacy_source_schedule_requires_migration" };
366
436
  }
@@ -391,20 +461,6 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
391
461
  if (incidentDeclaration.action === "blocked") {
392
462
  return incidentDeclaration;
393
463
  }
394
- const taskPersonalConnectionDelegations = await getScheduledTaskPersonalConnectionDelegations(
395
- db,
396
- task.workspaceId,
397
- task.id,
398
- );
399
- const taskConnectionAuthoritySubjects = [
400
- ...new Set(
401
- taskPersonalConnectionDelegations.map((delegation) => delegation.ownerSubjectId),
402
- ),
403
- ];
404
- if (taskConnectionAuthoritySubjects.length > 1) {
405
- throw new Error("scheduled connection authority has multiple causal humans");
406
- }
407
- const taskConnectionAuthoritySubjectId = taskConnectionAuthoritySubjects[0] ?? null;
408
464
  const taskPersonalResourceAuthoritySubjectId =
409
465
  await getScheduledTaskPersonalResourceAuthoritySubject(db, {
410
466
  accountId: task.accountId,
@@ -418,17 +474,52 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
418
474
  taskId: task.id,
419
475
  taskAuthorityRevision: task.authorityRevision,
420
476
  });
477
+ const taskAuthoritySubjectId = task.ownerSubjectId;
478
+ // Every pre-admission refusal below is recorded as a visible run receipt
479
+ // under this producer identity instead of throwing (a thrown activity is
480
+ // retried to exhaustion and leaves no run) or returning a bare blocked
481
+ // result (the occurrence silently disappears). Terminal refusals are
482
+ // deterministic for this task revision and digest; transient ones
483
+ // (`retryable`) are skipped and a later occurrence is admitted normally.
484
+ // Redelivery replays the same receipt, so no occurrence gets two runs.
485
+ const refuseAdmission = async (
486
+ reason: ScheduledTaskAdmissionRefusalReason,
487
+ retryable: boolean,
488
+ detail: string,
489
+ ): Promise<DispatchScheduledTaskRunResult> => {
490
+ console.warn(
491
+ `[scheduled-task] refused occurrence of ${task.id} (revision ${task.authorityRevision}): ${reason}: ${detail}`,
492
+ );
493
+ const receipt = await recordScheduledTaskAdmissionRefusal(db, {
494
+ workspaceId: task.workspaceId,
495
+ taskId: task.id,
496
+ taskAuthorityRevision: task.authorityRevision,
497
+ taskExecutionDigest: task.executionDigest,
498
+ triggerType: input.triggerType,
499
+ producerKey: stableProducerKey,
500
+ reason,
501
+ retryable,
502
+ });
503
+ if (!receipt.admissionRefusal) {
504
+ // A concurrent delivery accepted this producer first. Follow the
505
+ // existing exact-receipt recovery path, never replace its outcome.
506
+ return createScheduledTaskActivities(services).dispatchScheduledTaskRun(input);
507
+ }
508
+ return scheduledAdmissionRefusalResult(receipt);
509
+ };
421
510
  if (
422
- taskConnectionAuthoritySubjectId &&
423
- taskPersonalResourceAuthoritySubjectId &&
424
- taskConnectionAuthoritySubjectId !== taskPersonalResourceAuthoritySubjectId
511
+ task.action.kind === "agent_turn" &&
512
+ ((taskRevisionAuthority?.subjectId ?? null) !== taskAuthoritySubjectId ||
513
+ [taskPersonalResourceAuthoritySubjectId].some(
514
+ (subject) => subject !== null && subject !== taskAuthoritySubjectId,
515
+ ))
425
516
  ) {
426
- throw new Error("scheduled authority classes have different causal humans");
517
+ return await refuseAdmission(
518
+ "scheduled_authority_unavailable",
519
+ false,
520
+ "scheduled authority differs from its immutable execution owner",
521
+ );
427
522
  }
428
- const taskAuthoritySubjectId =
429
- taskRevisionAuthority?.subjectId ??
430
- taskPersonalResourceAuthoritySubjectId ??
431
- taskConnectionAuthoritySubjectId;
432
523
  const acceptedTargetSessionId =
433
524
  task.runMode === "existing_session"
434
525
  ? task.targetSessionId
@@ -450,29 +541,157 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
450
541
  )
451
542
  : null;
452
543
  const settings = await settingsForTask(task, targetSessionExecutionBase?.model);
453
- const model = task.agentConfig.model ?? settings.openaiModel;
454
- const reasoningEffort = task.agentConfig.reasoningEffort ?? settings.openaiReasoningEffort;
544
+ // A task that names no model resolves the new-chat default at each fresh
545
+ // occurrence (saved workspace default, then a usable connected
546
+ // subscription under the task's frozen SuperGrok authority, then the
547
+ // credits default while the organization holds a credit balance, then the
548
+ // deployment default). A frozen SuperGrok pool that is gone only means
549
+ // SuperGrok is not ready. The accepted execution freezes the result, so
550
+ // retries and recovery never resolve again. Existing-session runs keep
551
+ // that session's model.
552
+ const resolvedDefault =
553
+ task.agentConfig.model || targetSessionExecutionBase
554
+ ? null
555
+ : await resolveScheduledTaskDefaultModel(db, settings, task);
556
+ const model = task.agentConfig.model ?? resolvedDefault?.model ?? settings.openaiModel;
557
+ const reasoningEffort =
558
+ task.agentConfig.reasoningEffort ??
559
+ resolvedDefault?.reasoningEffort ??
560
+ settings.openaiReasoningEffort;
455
561
  let sandboxBackend = task.agentConfig.sandboxBackend ?? settings.sandboxBackend;
456
562
  let sandboxOs: "linux" | "macos" | "windows" = "linux";
457
- const taskTools = withFirstPartyTools(settings, task.agentConfig.tools);
563
+ let taskTools = withFirstPartyTools(settings, task.agentConfig.tools);
458
564
  // A task created by a live agent attempt froze its creator's effective
459
565
  // first-party selection, permission set, and session access policy;
460
566
  // its generated sessions inherit that boundary under today's
461
567
  // deployment ceiling. A human/API-created task (null policy) keeps the
462
568
  // deployment default exactly as before.
463
569
  const creatorPolicy = await getScheduledTaskCreatorPolicy(db, task.workspaceId, task.id);
464
- const firstPartyMcpTools = creatorPolicy?.firstPartyMcpTools
465
- ? allowedFirstPartyMcpToolsForSession(settings, creatorPolicy.firstPartyMcpTools)
466
- : resolveFirstPartyMcpToolPolicy(settings).default;
570
+ // A person-chosen Slack channel adds only the two bot posting tools;
571
+ // they post nowhere else, whatever the creator's own selection was.
572
+ let firstPartyMcpTools = withScheduledSlackBotPostingTools(
573
+ creatorPolicy?.firstPartyMcpTools
574
+ ? allowedFirstPartyMcpToolsForSession(settings, creatorPolicy.firstPartyMcpTools)
575
+ : resolveFirstPartyMcpToolPolicy(settings).default,
576
+ task.agentConfig,
577
+ resolveFirstPartyMcpToolPolicy(settings).allowed,
578
+ );
579
+ // Agent configuration for generated sessions (null = legacy). The
580
+ // scheduled baseline above (creator policy or deployment default) is
581
+ // what "all" reproduces; capabilities only narrow it. Frozen into the
582
+ // accepted execution so retries and recovery never resolve again.
583
+ let resolvedAgentConfig: ResolvedAgentConfig | null = null;
584
+ let resolvedAgentInstructions: string | undefined;
585
+ const generatedTarget =
586
+ task.runMode === "new_session_per_run" ||
587
+ (task.runMode === "reusable_session" && task.reusableSessionId === null);
588
+ if (generatedTarget) {
589
+ try {
590
+ const agentResolution = resolveSessionAgentConfigForCreate({
591
+ settings,
592
+ creator: "scheduled",
593
+ request: task.agentConfig.agent,
594
+ instructions: undefined,
595
+ workspaceSettings: (await requireWorkspace(db, task.workspaceId)).settings,
596
+ parent: null,
597
+ goal: task.agentConfig.goal !== undefined,
598
+ });
599
+ resolvedAgentConfig = agentResolution.config;
600
+ resolvedAgentInstructions = agentResolution.instructions;
601
+ const written = applySessionAgentConfigWriteThrough({
602
+ config: resolvedAgentConfig,
603
+ firstPartyMcpTools,
604
+ tools: taskTools,
605
+ toolPolicy: { mode: "explicit", inheritedFromSessionId: null },
606
+ productServerIds: task.agentConfig.tools.map((tool) => tool.id),
607
+ });
608
+ firstPartyMcpTools = written.firstPartyMcpTools;
609
+ taskTools = written.tools;
610
+ } catch (error) {
611
+ const status =
612
+ error instanceof Error ? (error as Error & { status?: unknown }).status : undefined;
613
+ if (!(error instanceof Error) || status !== 422) throw error;
614
+ return await refuseAdmission(
615
+ "scheduled_authority_unavailable",
616
+ false,
617
+ `scheduled agent configuration is not admissible: ${error.message}`,
618
+ );
619
+ }
620
+ }
467
621
  const firstPartyMcpPermissions = creatorPolicy?.firstPartyMcpPermissions
468
622
  ? [...creatorPolicy.firstPartyMcpPermissions]
469
623
  : [...DEFAULT_FIRST_PARTY_MCP_PERMISSIONS];
624
+ // A fresh occurrence resolves the immutable owner's current accounts.
625
+ // Retry/recovery above uses the already accepted occurrence snapshot.
626
+ const connectionSettings = await settingsWithEnabledCapabilityMcpServers(
627
+ db,
628
+ task.workspaceId,
629
+ settings,
630
+ taskAuthoritySubjectId ? { subjectId: taskAuthoritySubjectId } : {},
631
+ );
632
+ const connectionTarget =
633
+ acceptedTargetSessionId && targetSessionExecutionBase
634
+ ? await requireSession(db, task.workspaceId, acceptedTargetSessionId)
635
+ : null;
636
+ const connectionTools = await scheduledConnectionTools(
637
+ db,
638
+ task.workspaceId,
639
+ connectionSettings,
640
+ connectionTarget,
641
+ taskTools,
642
+ taskAuthoritySubjectId ?? undefined,
643
+ );
644
+ const taskConnections = await freezeConnectionAccounts({
645
+ db,
646
+ accountId: task.accountId,
647
+ workspaceId: task.workspaceId,
648
+ settings: connectionTarget
649
+ ? settingsWithSessionMcpServerMetadata(connectionSettings, connectionTarget.mcpServers)
650
+ : connectionSettings,
651
+ tools: connectionTools,
652
+ resources: mergeResourceRefs(connectionTarget?.resources ?? [], task.agentConfig.resources),
653
+ source: taskAuthoritySubjectId
654
+ ? { kind: "subject", subjectId: taskAuthoritySubjectId, accountId: task.accountId }
655
+ : { kind: "none" },
656
+ authoritySelections: task.agentConfig.connectionAccounts ?? [],
657
+ authoritySelectionsFrozen: task.agentConfig.connectionAccountsFrozen === true,
658
+ ...scheduledConnectionSurfaceEligibility(
659
+ settings,
660
+ connectionTarget ?? {
661
+ firstPartyMcpTools,
662
+ firstPartyMcpPermissions,
663
+ },
664
+ ),
665
+ }).catch((error: unknown) => {
666
+ if (error instanceof ConnectionAccountSelectionError) return error;
667
+ throw error;
668
+ });
669
+ if (taskConnections instanceof ConnectionAccountSelectionError) {
670
+ const receipt = await recordScheduledTaskAdmissionFailure(db, {
671
+ workspaceId: task.workspaceId,
672
+ taskId: task.id,
673
+ taskAuthorityRevision: task.authorityRevision,
674
+ taskExecutionDigest: task.executionDigest,
675
+ triggerType: input.triggerType,
676
+ producerKey: stableProducerKey,
677
+ diagnostic: taskConnections.diagnostic,
678
+ });
679
+ if (!receipt.admissionDiagnostic) {
680
+ // A concurrent delivery accepted this producer first. Follow the
681
+ // existing exact-receipt recovery path, never replace its outcome.
682
+ return createScheduledTaskActivities(services).dispatchScheduledTaskRun(input);
683
+ }
684
+ return scheduledAdmissionRefusalResult(receipt);
685
+ }
686
+ const {
687
+ personalConnectionDelegations: taskPersonalConnectionDelegations,
688
+ mcpAccountBindings: taskMcpAccountBindings,
689
+ } = taskConnections;
690
+ const taskConnectionAuthoritySubjectId =
691
+ taskPersonalConnectionDelegations.length > 0 ? taskAuthoritySubjectId : null;
470
692
  const creatorSessionPolicy = scheduledCreatorSessionPolicyInput(
471
693
  creatorPolicy?.sessionPolicy ?? null,
472
694
  );
473
- const generatedTarget =
474
- task.runMode === "new_session_per_run" ||
475
- (task.runMode === "reusable_session" && task.reusableSessionId === null);
476
695
  if (generatedTarget && task.agentConfig.machineTarget) {
477
696
  const access = {
478
697
  accountId: task.accountId,
@@ -490,16 +709,37 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
490
709
  !machine.enrollmentId ||
491
710
  machine.scope === "user"
492
711
  ) {
493
- throw new Error("scheduled Connected Machine target is unavailable");
712
+ return await refuseAdmission(
713
+ "machine_target_unavailable",
714
+ false,
715
+ "scheduled Connected Machine target is unavailable",
716
+ );
494
717
  }
495
718
  const enrollment = await getEnrollment(db, access, machine.enrollmentId);
496
- if (!enrollment || enrollment.status !== "active") {
497
- throw new Error("scheduled Connected Machine enrollment is unavailable");
719
+ if (!enrollment) {
720
+ return await refuseAdmission(
721
+ "machine_target_unavailable",
722
+ false,
723
+ "scheduled Connected Machine enrollment is gone",
724
+ );
725
+ }
726
+ if (enrollment.status !== "active") {
727
+ // A paused/suspended enrollment can be re-activated; skip this
728
+ // occurrence rather than failing the schedule.
729
+ return await refuseAdmission(
730
+ "machine_enrollment_inactive",
731
+ true,
732
+ `scheduled Connected Machine enrollment is ${enrollment.status}`,
733
+ );
498
734
  }
499
735
  sandboxBackend = "selfhosted";
500
736
  sandboxOs = enrollment.os;
501
737
  } else if (generatedTarget && sandboxBackend === "selfhosted") {
502
- throw new Error("self-hosted scheduled task has no Connected Machine target");
738
+ return await refuseAdmission(
739
+ "machine_target_unavailable",
740
+ false,
741
+ "self-hosted scheduled task has no Connected Machine target",
742
+ );
503
743
  }
504
744
  const generatedSessionDepthPolicy = generatedTarget
505
745
  ? await (async () => {
@@ -537,20 +777,16 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
537
777
  : null;
538
778
  const targetSessionExecution = targetSessionExecutionBase
539
779
  ? await (async () => {
540
- const [capabilityServerIds, apiIntegrationServerIds] = await Promise.all([
541
- listEnabledMcpCapabilityServerIds(db, task.workspaceId),
542
- listInstalledApiIntegrationServerIdsForDelegations(
780
+ const apiIntegrationServerIds =
781
+ await listInstalledApiIntegrationServerIdsForDelegations(
543
782
  db,
544
783
  task.workspaceId,
545
784
  taskPersonalConnectionDelegations,
546
- ),
785
+ );
786
+ const availableMcpServerIds = new Set([
787
+ ...connectionSettings.mcpServers.map((server) => server.id),
788
+ ...apiIntegrationServerIds,
547
789
  ]);
548
- const workspaceDefaultMcpServerIds = new Set(
549
- settings.mcpServers.map((server) => server.id),
550
- );
551
- for (const id of capabilityServerIds) workspaceDefaultMcpServerIds.add(id);
552
- for (const id of apiIntegrationServerIds) workspaceDefaultMcpServerIds.add(id);
553
- const availableMcpServerIds = new Set(workspaceDefaultMcpServerIds);
554
790
  for (const id of targetSessionExecutionBase.mcpServerIds) {
555
791
  availableMcpServerIds.add(id);
556
792
  }
@@ -558,9 +794,10 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
558
794
  toolPolicy: targetSessionExecutionBase.toolPolicy,
559
795
  sessionTools: targetSessionExecutionBase.tools,
560
796
  availableMcpServerIds,
561
- defaultMcpServerIds: defaultSessionMcpServerIds(
562
- [...workspaceDefaultMcpServerIds].map((id) => ({ id })),
563
- ),
797
+ defaultMcpServerIds: [
798
+ ...connectionTools.map((tool) => tool.id),
799
+ ...apiIntegrationServerIds,
800
+ ],
564
801
  });
565
802
  return {
566
803
  ...targetSessionExecutionBase,
@@ -589,7 +826,11 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
589
826
  )
590
827
  : null;
591
828
  if (generatedTarget && task.variableSetId && !acceptedVariableSet) {
592
- throw new Error(`variable set not found: ${task.variableSetId}`);
829
+ return await refuseAdmission(
830
+ "variable_set_unavailable",
831
+ false,
832
+ `variable set not found: ${task.variableSetId}`,
833
+ );
593
834
  }
594
835
  const acceptedRig =
595
836
  generatedTarget && task.rigId
@@ -604,12 +845,17 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
604
845
  )
605
846
  : null;
606
847
  if (generatedTarget && task.rigId && (!acceptedRig || !acceptedRig.activeVersion)) {
607
- throw new Error(`rig has no active version to bind: ${task.rigId}`);
848
+ return await refuseAdmission(
849
+ "rig_version_unavailable",
850
+ false,
851
+ `sandbox environment has no active version to bind: ${task.rigId}`,
852
+ );
608
853
  }
609
- const acceptedRigDefaultVariableSets = acceptedRig?.activeVersion
854
+ const acceptedRigDefaultVariableSetRows = acceptedRig?.activeVersion
610
855
  ? await Promise.all(
611
- acceptedRig.activeVersion.defaultVariableSetIds.map(async (variableSetId) => {
612
- const variableSet = await getVariableSet(
856
+ acceptedRig.activeVersion.defaultVariableSetIds.map(async (variableSetId) => ({
857
+ variableSetId,
858
+ variableSet: await getVariableSet(
613
859
  db,
614
860
  {
615
861
  accountId: task.accountId,
@@ -617,14 +863,23 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
617
863
  subjectId: taskAuthoritySubjectId ?? task.createdBy.subjectId,
618
864
  },
619
865
  variableSetId,
620
- );
621
- if (!variableSet) {
622
- throw new Error(`rig default Variable Set not found: ${variableSetId}`);
623
- }
624
- return { id: variableSet.id, generation: variableSet.generation };
625
- }),
866
+ ),
867
+ })),
626
868
  )
627
869
  : [];
870
+ const missingRigDefaultVariableSet = acceptedRigDefaultVariableSetRows.find(
871
+ (row) => !row.variableSet,
872
+ );
873
+ if (missingRigDefaultVariableSet) {
874
+ return await refuseAdmission(
875
+ "variable_set_unavailable",
876
+ false,
877
+ `sandbox environment default Variable Set not found: ${missingRigDefaultVariableSet.variableSetId}`,
878
+ );
879
+ }
880
+ const acceptedRigDefaultVariableSets = acceptedRigDefaultVariableSetRows.map(
881
+ ({ variableSet }) => ({ id: variableSet!.id, generation: variableSet!.generation }),
882
+ );
628
883
  let incidentPreflightRequired = incidentDeclaration.action === "required";
629
884
  if (incidentPreflightRequired) {
630
885
  const existingSessionId =
@@ -688,6 +943,8 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
688
943
  }
689
944
  const taskXaiProviderAccountAuthoritySnapshot =
690
945
  await getScheduledTaskXaiProviderAccountAuthoritySnapshot(db, task.workspaceId, task.id);
946
+ const taskClaudeProviderAccountAuthoritySnapshot =
947
+ await getScheduledTaskClaudeProviderAccountAuthoritySnapshot(db, task.workspaceId, task.id);
691
948
  // A scheduled bot selection was authorized when the task was written,
692
949
  // but connection status and tenant/role binding are mutable. Revalidate
693
950
  // before any session/model cost and never fall back to a personal Slack
@@ -703,7 +960,7 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
703
960
  ? openGeniSlackBotMetadata(slackBotConnection.metadata)
704
961
  : null;
705
962
  if (slackBotConnection && !slackBotMetadata) {
706
- throw new Error("OpenGeni Slack bot connection metadata is invalid");
963
+ throw new Error("Opengeni Slack bot connection metadata is invalid");
707
964
  }
708
965
  const xaiAuthoritySubjectId =
709
966
  taskXaiProviderAccountAuthoritySnapshot.scope === "user" &&
@@ -711,21 +968,67 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
711
968
  ? task.createdBy.subjectId
712
969
  : null;
713
970
  if (taskXaiProviderAccountAuthoritySnapshot.scope === "user" && !xaiAuthoritySubjectId) {
714
- throw new Error("scheduled user-scoped xAI authority has no causal human");
971
+ return await refuseAdmission(
972
+ "scheduled_authority_unavailable",
973
+ false,
974
+ "scheduled user-scoped xAI authority has no causal human",
975
+ );
715
976
  }
716
977
  if (
717
978
  xaiAuthoritySubjectId &&
718
979
  taskAuthoritySubjectId &&
719
980
  xaiAuthoritySubjectId !== taskAuthoritySubjectId
720
981
  ) {
721
- throw new Error("scheduled authority classes have different causal humans");
982
+ return await refuseAdmission(
983
+ "scheduled_authority_unavailable",
984
+ false,
985
+ "scheduled authority classes have different causal humans",
986
+ );
722
987
  }
723
- const causalHumanSubjectId = taskAuthoritySubjectId ?? xaiAuthoritySubjectId;
988
+ const claudeAuthoritySubjectId =
989
+ taskClaudeProviderAccountAuthoritySnapshot.scope === "user" && task.ownerSubjectId
990
+ ? task.ownerSubjectId
991
+ : null;
992
+ if (
993
+ taskClaudeProviderAccountAuthoritySnapshot.scope === "user" &&
994
+ !claudeAuthoritySubjectId
995
+ ) {
996
+ return await refuseAdmission(
997
+ "scheduled_authority_unavailable",
998
+ false,
999
+ "scheduled user-scoped Claude authority has no causal human",
1000
+ );
1001
+ }
1002
+ if (
1003
+ claudeAuthoritySubjectId &&
1004
+ taskAuthoritySubjectId &&
1005
+ claudeAuthoritySubjectId !== taskAuthoritySubjectId
1006
+ ) {
1007
+ return await refuseAdmission(
1008
+ "scheduled_authority_unavailable",
1009
+ false,
1010
+ "scheduled authority classes have different causal humans",
1011
+ );
1012
+ }
1013
+ const causalHumanSubjectId =
1014
+ taskAuthoritySubjectId ?? xaiAuthoritySubjectId ?? claudeAuthoritySubjectId;
1015
+ if (
1016
+ xaiAuthoritySubjectId &&
1017
+ claudeAuthoritySubjectId &&
1018
+ xaiAuthoritySubjectId !== claudeAuthoritySubjectId
1019
+ )
1020
+ return await refuseAdmission(
1021
+ "scheduled_authority_unavailable",
1022
+ false,
1023
+ "scheduled subscription pools have different causal humans",
1024
+ );
724
1025
  if (
725
1026
  task.agentConfig.knowledgeSource &&
726
1027
  causalHumanSubjectId !== task.agentConfig.knowledgeSource.initiatingSubjectId
727
1028
  ) {
728
- throw new Error(
1029
+ return await refuseAdmission(
1030
+ "scheduled_authority_unavailable",
1031
+ false,
729
1032
  "Source schedule requires its connection owner's current human authorization; update the source schedule to authorize it",
730
1033
  );
731
1034
  }
@@ -737,7 +1040,11 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
737
1040
  (acceptedVariableSet?.scope === "user" || acceptedRig?.scope === "user") &&
738
1041
  !causalHumanSubjectId
739
1042
  ) {
740
- throw new Error("scheduled personal-resource execution has no causal human");
1043
+ return await refuseAdmission(
1044
+ "scheduled_authority_unavailable",
1045
+ false,
1046
+ "scheduled personal-resource execution has no causal human",
1047
+ );
741
1048
  }
742
1049
  const admissionDenial = await agentRunAdmissionDenial(
743
1050
  { ...baseService, settings },
@@ -746,10 +1053,13 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
746
1053
  workspaceId: task.workspaceId,
747
1054
  model: targetSessionExecution?.model ?? model,
748
1055
  requestedAgentRuns: input.agentRunUsageIdempotencyKey ? 0 : 1,
1056
+ initiatingHumanSubjectId: causalHumanSubjectId,
749
1057
  },
750
1058
  );
751
1059
  if (admissionDenial) {
752
- return { action: "blocked", reason: admissionDenial };
1060
+ // Credits and monthly limits clear on their own (top-up, new period):
1061
+ // skip this occurrence visibly; a later one is admitted normally.
1062
+ return await refuseAdmission(admissionDenial, true, "agent run admission denied");
753
1063
  }
754
1064
  const acceptedModel = targetSessionExecution?.model ?? model;
755
1065
  const acceptedReasoningEffort = targetSessionExecution?.reasoningEffort ?? reasoningEffort;
@@ -772,23 +1082,28 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
772
1082
  }
773
1083
  }
774
1084
  : undefined;
775
- const turnExecutionPolicy: TurnExecutionPolicyV1 = resolveTurnExecutionPolicyV1(settings, {
776
- modelId: acceptedModel,
777
- requestedModelId: generatedTarget && task.agentConfig.model ? task.agentConfig.model : null,
778
- modelSource: generatedTarget
779
- ? task.agentConfig.model
780
- ? "explicit"
781
- : "deployment"
782
- : "session",
783
- reasoningEffort: acceptedReasoningEffort,
784
- reasoningSource: generatedTarget
785
- ? task.agentConfig.reasoningEffort
786
- ? "explicit"
787
- : "deployment"
788
- : "session",
789
- latencyMode: acceptedLatencyMode,
790
- latencyModeSource: generatedTarget ? "deployment" : "session",
791
- });
1085
+ const turnExecutionPolicy = scheduledTaskRunExecutionPolicy(
1086
+ resolveTurnExecutionPolicyV1(settings, {
1087
+ modelId: acceptedModel,
1088
+ requestedModelId:
1089
+ generatedTarget && task.agentConfig.model ? task.agentConfig.model : null,
1090
+ modelSource: generatedTarget
1091
+ ? task.agentConfig.model
1092
+ ? "explicit"
1093
+ : "deployment"
1094
+ : "session",
1095
+ reasoningEffort: acceptedReasoningEffort,
1096
+ reasoningSource: generatedTarget
1097
+ ? task.agentConfig.reasoningEffort
1098
+ ? "explicit"
1099
+ : "deployment"
1100
+ : "session",
1101
+ latencyMode: acceptedLatencyMode,
1102
+ latencyModeSource: generatedTarget ? "deployment" : "session",
1103
+ }),
1104
+ input,
1105
+ creatorPolicy?.credentialRestriction,
1106
+ );
792
1107
  const deferredEvents: Array<{
793
1108
  sessionId: string;
794
1109
  events: Awaited<ReturnType<typeof appendSessionEvents>>;
@@ -821,6 +1136,12 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
821
1136
  resolvedTools: taskTools,
822
1137
  resolvedFirstPartyMcpTools: firstPartyMcpTools,
823
1138
  resolvedFirstPartyMcpPermissions: firstPartyMcpPermissions,
1139
+ ...(resolvedAgentConfig
1140
+ ? {
1141
+ resolvedAgentConfig,
1142
+ ...(resolvedAgentInstructions !== undefined ? { resolvedAgentInstructions } : {}),
1143
+ }
1144
+ : {}),
824
1145
  resolvedVariableSet: acceptedVariableSet
825
1146
  ? { id: acceptedVariableSet.id, generation: acceptedVariableSet.generation }
826
1147
  : null,
@@ -843,11 +1164,14 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
843
1164
  targetSessionExecution,
844
1165
  generatedSessionBinding,
845
1166
  personalConnectionDelegations: taskPersonalConnectionDelegations,
1167
+ mcpAccountBindings: taskMcpAccountBindings,
846
1168
  personalResourceAuthoritySubjectId: taskPersonalResourceAuthoritySubjectId,
847
1169
  causalHumanSubjectId,
848
1170
  causalHumanAuthority: taskRevisionAuthority,
849
1171
  xaiProviderAccountAuthoritySnapshot: taskXaiProviderAccountAuthoritySnapshot,
1172
+ claudeProviderAccountAuthoritySnapshot: taskClaudeProviderAccountAuthoritySnapshot,
850
1173
  xaiAuthoritySubjectId,
1174
+ claudeAuthoritySubjectId,
851
1175
  connectionAuthoritySubjectId: taskConnectionAuthoritySubjectId,
852
1176
  triggerInitiator: input.initiator ?? { kind: "service", subjectId: "scheduler" },
853
1177
  agentRunUsageIdempotencyKey: input.agentRunUsageIdempotencyKey ?? null,
@@ -966,16 +1290,7 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
966
1290
  taskId: task.id,
967
1291
  },
968
1292
  );
969
- if (
970
- stableJson({
971
- task: exactAuthority.task,
972
- personalConnectionDelegations: exactAuthority.personalConnectionDelegations,
973
- }) !==
974
- stableJson({
975
- task,
976
- personalConnectionDelegations: taskPersonalConnectionDelegations,
977
- })
978
- ) {
1293
+ if (stableJson(exactAuthority) !== stableJson(task)) {
979
1294
  throw new IncidentTelemetryPreflightBlockedError(
980
1295
  "incident_preflight_metadata_missing",
981
1296
  );
@@ -1009,7 +1324,7 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
1009
1324
  if (task.rigId) {
1010
1325
  const rig = acceptedRig;
1011
1326
  if (!rig || !rig.activeVersion) {
1012
- throw new Error(`rig has no active version to bind: ${task.rigId}`);
1327
+ throw new Error(`sandbox environment has no active version to bind: ${task.rigId}`);
1013
1328
  }
1014
1329
  frozenRigId = rig.id;
1015
1330
  frozenRigVersionId =
@@ -1017,7 +1332,9 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
1017
1332
  (resource) => resource.resourceKind === "rig" && resource.resourceId === rig.id,
1018
1333
  )?.resourceVersionId ?? rig.activeVersion.id;
1019
1334
  if (frozenRigVersionId !== rig.activeVersion.id) {
1020
- throw new Error("scheduled run rig version changed during admission");
1335
+ throw new Error(
1336
+ "scheduled run sandbox environment version changed during admission",
1337
+ );
1021
1338
  }
1022
1339
  }
1023
1340
  let session: Awaited<ReturnType<typeof createSession>>;
@@ -1034,6 +1351,10 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
1034
1351
  tools: taskTools,
1035
1352
  firstPartyMcpTools,
1036
1353
  firstPartyMcpPermissions,
1354
+ ...(resolvedAgentConfig ? { agentConfig: resolvedAgentConfig } : {}),
1355
+ ...(resolvedAgentInstructions !== undefined
1356
+ ? { instructions: resolvedAgentInstructions }
1357
+ : {}),
1037
1358
  ...creatorSessionPolicy,
1038
1359
  ...(task.agentConfig.knowledgeSource?.destination.kind === "personal"
1039
1360
  ? {
@@ -1043,6 +1364,10 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
1043
1364
  : {}),
1044
1365
  metadata: {
1045
1366
  ...taskMetadata,
1367
+ ...scheduledSessionExecutionPolicyMetadata(
1368
+ turnExecutionPolicy,
1369
+ creatorPolicy?.credentialRestriction,
1370
+ ),
1046
1371
  model,
1047
1372
  reasoningEffort,
1048
1373
  scheduledTaskId: task.id,
@@ -1074,6 +1399,8 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
1074
1399
  rigVersionId: frozenRigVersionId,
1075
1400
  personalConnectionDelegations: [],
1076
1401
  initialXaiProviderAccountAuthoritySnapshot: taskXaiProviderAccountAuthoritySnapshot,
1402
+ initialClaudeProviderAccountAuthoritySnapshot:
1403
+ taskClaudeProviderAccountAuthoritySnapshot,
1077
1404
  maxNestedAgentDepthOverride: task.agentConfig.maxNestedAgentDepth ?? null,
1078
1405
  frozenNestedAgentDepthPolicy: {
1079
1406
  effectiveMaxNestedAgentDepth:
@@ -1138,6 +1465,7 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
1138
1465
  }
1139
1466
  session = created.session;
1140
1467
  sessionCreated = created.created;
1468
+ if (sessionCreated) recordScheduledSessionCreated(baseService.observability);
1141
1469
  if (!sessionCreated) {
1142
1470
  await bindScheduledTaskRunSessionInTransaction(dispatchDb, {
1143
1471
  accountId: task.accountId,
@@ -1171,7 +1499,7 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
1171
1499
  (task.agentConfig.slackBotConnectionId ?? null)
1172
1500
  ) {
1173
1501
  throw new Error(
1174
- "scheduled alert occurrence OpenGeni Slack bot binding does not match its canonical session",
1502
+ "scheduled alert occurrence Opengeni Slack bot binding does not match its canonical session",
1175
1503
  );
1176
1504
  }
1177
1505
  }
@@ -1366,9 +1694,12 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
1366
1694
  task.createdBy.kind === "subject"
1367
1695
  ? { xaiAuthoritySubjectId: task.createdBy.subjectId }
1368
1696
  : {}),
1697
+ ...(claudeAuthoritySubjectId ? { claudeAuthoritySubjectId } : {}),
1369
1698
  },
1370
1699
  personalConnectionDelegations: taskPersonalConnectionDelegations,
1700
+ mcpAccountBindings: taskMcpAccountBindings,
1371
1701
  xaiProviderAccountAuthoritySnapshot: taskXaiProviderAccountAuthoritySnapshot,
1702
+ claudeProviderAccountAuthoritySnapshot: taskClaudeProviderAccountAuthoritySnapshot,
1372
1703
  scheduledTaskRunId: run.id,
1373
1704
  },
1374
1705
  async (tx, wakeEventId, _updateId, rejectionReason) => {
@@ -1421,6 +1752,9 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
1421
1752
  run: { ...run, status: "skipped" as const, error },
1422
1753
  };
1423
1754
  }
1755
+ if (scheduledUpdate.added) {
1756
+ recordUserMessageAccepted(baseService.observability, { surface: "scheduled" });
1757
+ }
1424
1758
  if (scheduledUpdate.added && scheduledUpdate.events.length > 0) {
1425
1759
  if (deferPublications) {
1426
1760
  deferredEvents.push({
@@ -1460,9 +1794,8 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
1460
1794
  // the locked admission check below remains authoritative. Mirrors
1461
1795
  // apps/api/src/domain/sessions.ts.
1462
1796
  assertReusableSessionRevivable(session.status);
1463
- // Defensive backstop for the API-level 409: a reusable session keeps
1464
- // its creation-time attachment, so a diverged task attachment must
1465
- // fail the run instead of silently running with the wrong secrets.
1797
+ // Slack routing remains bound to the reusable chat; execution
1798
+ // attachments come from its accepted target snapshot.
1466
1799
  assertReusableSessionBindingMatches(session, task);
1467
1800
  const bundled = await addSessionSystemUpdateWithSourceMutation(
1468
1801
  dispatchDb,
@@ -1493,9 +1826,12 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
1493
1826
  task.createdBy.kind === "subject"
1494
1827
  ? { xaiAuthoritySubjectId: task.createdBy.subjectId }
1495
1828
  : {}),
1829
+ ...(claudeAuthoritySubjectId ? { claudeAuthoritySubjectId } : {}),
1496
1830
  },
1497
1831
  personalConnectionDelegations: taskPersonalConnectionDelegations,
1832
+ mcpAccountBindings: taskMcpAccountBindings,
1498
1833
  xaiProviderAccountAuthoritySnapshot: taskXaiProviderAccountAuthoritySnapshot,
1834
+ claudeProviderAccountAuthoritySnapshot: taskClaudeProviderAccountAuthoritySnapshot,
1499
1835
  scheduledTaskRunId: run.id,
1500
1836
  },
1501
1837
  async (tx, wakeEventId, _updateId, rejectionReason) => {
@@ -1563,6 +1899,9 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
1563
1899
  run: { ...run, status: "skipped" as const, error },
1564
1900
  };
1565
1901
  }
1902
+ if (bundled.added) {
1903
+ recordUserMessageAccepted(baseService.observability, { surface: "scheduled" });
1904
+ }
1566
1905
  if (bundled.added && bundled.events.length > 0) {
1567
1906
  if (deferPublications) {
1568
1907
  deferredEvents.push({
@@ -1687,28 +2026,24 @@ class IncidentTelemetryPreflightBlockedError extends Error {
1687
2026
  }
1688
2027
 
1689
2028
  /**
1690
- * Defensive backstop for the API-level 409: a reusable/existing target session
1691
- * keeps its creation-time attachment, so a diverged task attachment or Slack
1692
- * bot binding must settle the run terminally instead of silently running with
1693
- * the wrong secrets - on the first attempt and on every recovery attempt.
2029
+ * Defensive backstop for the API-level Slack binding guard. Variable Sets
2030
+ * belong to the live target and are fenced by its accepted execution snapshot,
2031
+ * not by the task's retained creation defaults.
1694
2032
  */
1695
2033
  function assertReusableSessionBindingMatches(
1696
- session: { variableSetId: string | null; metadata: Record<string, unknown> },
1697
- task: Pick<ScheduledTask, "variableSetId" | "agentConfig">,
2034
+ session: { metadata: Record<string, unknown> },
2035
+ task: Pick<ScheduledTask, "runMode" | "agentConfig">,
1698
2036
  ): void {
1699
- if ((session.variableSetId ?? null) !== (task.variableSetId ?? null)) {
1700
- throw new ScheduledRunTerminalAuthorityError(
1701
- "scheduled_reusable_binding_changed",
1702
- "scheduled task variableSet attachment does not match its reusable session",
1703
- );
1704
- }
2037
+ // Existing-chat messages use the target policy frozen at admission. Task
2038
+ // creation defaults are not an additional authority or binding constraint.
2039
+ if (task.runMode === "existing_session") return;
1705
2040
  if (
1706
2041
  scheduledSlackBotConnectionId(session.metadata) !==
1707
2042
  (task.agentConfig.slackBotConnectionId ?? null)
1708
2043
  ) {
1709
2044
  throw new ScheduledRunTerminalAuthorityError(
1710
2045
  "scheduled_reusable_binding_changed",
1711
- "scheduled task OpenGeni Slack bot binding does not match its reusable session",
2046
+ "scheduled task Opengeni Slack bot binding does not match its reusable session",
1712
2047
  );
1713
2048
  }
1714
2049
  }
@@ -1766,9 +2101,31 @@ function scheduledRunRecoveryTerminalError(
1766
2101
  return null;
1767
2102
  }
1768
2103
 
2104
+ /** The blocked result of a diagnostic (refused) run receipt, fresh or replayed. */
2105
+ function scheduledAdmissionRefusalResult(
2106
+ run: Pick<ScheduledTaskRun, "id" | "admissionDiagnostic" | "admissionRefusal">,
2107
+ ): Extract<DispatchScheduledTaskRunResult, { action: "blocked" }> {
2108
+ if (run.admissionRefusal) {
2109
+ return {
2110
+ action: "blocked",
2111
+ reason: run.admissionRefusal.reason as ScheduledTaskAdmissionRefusalReason,
2112
+ runId: run.id,
2113
+ refusal: run.admissionRefusal,
2114
+ };
2115
+ }
2116
+ return {
2117
+ action: "blocked",
2118
+ reason: "connection_account_unavailable",
2119
+ runId: run.id,
2120
+ ...(run.admissionDiagnostic ? { diagnostic: run.admissionDiagnostic } : {}),
2121
+ };
2122
+ }
2123
+
1769
2124
  function scheduledRunTerminalResult(
1770
2125
  error: string | null,
1771
2126
  ): Extract<DispatchScheduledTaskRunResult, { action: "blocked" }> {
2127
+ if (error === ATLASSIAN_NATIVE_RETIRED_REASON)
2128
+ return { action: "blocked", reason: ATLASSIAN_NATIVE_RETIRED_REASON };
1772
2129
  if (error === "scheduled_authority_exhausted") {
1773
2130
  return { action: "blocked", reason: "scheduled_authority_exhausted" };
1774
2131
  }
@@ -1799,18 +2156,10 @@ async function prepareIncidentTelemetrySource(input: {
1799
2156
  workspaceId: string;
1800
2157
  sessionId: string;
1801
2158
  alertOccurrenceLabels: Readonly<Record<string, string>> | null;
1802
- acceptedExecution?: ScheduledTaskRunAcceptedExecution;
2159
+ acceptedExecution: ScheduledTaskRunAcceptedExecution;
1803
2160
  }) {
1804
2161
  const session = await requireSession(input.tx, input.workspaceId, input.sessionId);
1805
- const { task, personalConnectionDelegations } = input.acceptedExecution
1806
- ? {
1807
- task: input.acceptedExecution.task,
1808
- personalConnectionDelegations: input.acceptedExecution.personalConnectionDelegations,
1809
- }
1810
- : await requireScheduledTaskIncidentAuthorityInTransaction(input.tx, {
1811
- workspaceId: input.workspaceId,
1812
- taskId: input.taskId,
1813
- });
2162
+ const { task, personalConnectionDelegations } = input.acceptedExecution;
1814
2163
  const responder = await resolveIncidentTelemetryResponderMetadata({
1815
2164
  db: input.tx,
1816
2165
  settings: input.settings,
@@ -1818,39 +2167,34 @@ async function prepareIncidentTelemetrySource(input: {
1818
2167
  session,
1819
2168
  personalConnectionDelegations,
1820
2169
  personalResourceAuthoritySubjectId:
1821
- input.acceptedExecution?.personalResourceAuthoritySubjectId ?? null,
1822
- ...(input.acceptedExecution
2170
+ input.acceptedExecution.personalResourceAuthoritySubjectId ?? null,
2171
+ executionPolicy: input.acceptedExecution.targetSessionExecution
1823
2172
  ? {
1824
- executionPolicy: input.acceptedExecution.targetSessionExecution
1825
- ? {
1826
- tools: input.acceptedExecution.targetSessionExecution.tools,
1827
- firstPartyMcpTools:
1828
- input.acceptedExecution.targetSessionExecution.firstPartyMcpTools,
1829
- firstPartyMcpPermissions:
1830
- input.acceptedExecution.targetSessionExecution.firstPartyMcpPermissions,
1831
- variableSetIds: input.acceptedExecution.targetSessionExecution.variableSets.map(
1832
- (variableSet) => variableSet.id,
1833
- ),
1834
- variableSetId: input.acceptedExecution.targetSessionExecution.variableSetId,
1835
- rigId: input.acceptedExecution.targetSessionExecution.rigId,
1836
- rigVersionId: input.acceptedExecution.targetSessionExecution.rigVersionId,
1837
- toolPolicy: input.acceptedExecution.targetSessionExecution.toolPolicy,
1838
- mcpServerIds: input.acceptedExecution.targetSessionExecution.mcpServerIds,
1839
- toolPolicyVersion: input.acceptedExecution.targetSessionExecution.toolPolicyVersion,
1840
- }
1841
- : {
1842
- tools: input.acceptedExecution.resolvedTools,
1843
- firstPartyMcpTools: input.acceptedExecution.resolvedFirstPartyMcpTools,
1844
- firstPartyMcpPermissions: input.acceptedExecution.resolvedFirstPartyMcpPermissions,
1845
- variableSetId: input.acceptedExecution.resolvedVariableSet?.id ?? null,
1846
- rigId: input.acceptedExecution.resolvedRig?.id ?? null,
1847
- rigVersionId: input.acceptedExecution.resolvedRig?.versionId ?? null,
1848
- toolPolicy: session.toolPolicy,
1849
- mcpServerIds: [],
1850
- toolPolicyVersion: session.toolPolicyVersion,
1851
- },
2173
+ tools: input.acceptedExecution.targetSessionExecution.tools,
2174
+ firstPartyMcpTools: input.acceptedExecution.targetSessionExecution.firstPartyMcpTools,
2175
+ firstPartyMcpPermissions:
2176
+ input.acceptedExecution.targetSessionExecution.firstPartyMcpPermissions,
2177
+ variableSetIds: input.acceptedExecution.targetSessionExecution.variableSets.map(
2178
+ (variableSet) => variableSet.id,
2179
+ ),
2180
+ variableSetId: input.acceptedExecution.targetSessionExecution.variableSetId,
2181
+ rigId: input.acceptedExecution.targetSessionExecution.rigId,
2182
+ rigVersionId: input.acceptedExecution.targetSessionExecution.rigVersionId,
2183
+ toolPolicy: input.acceptedExecution.targetSessionExecution.toolPolicy,
2184
+ mcpServerIds: input.acceptedExecution.targetSessionExecution.mcpServerIds,
2185
+ toolPolicyVersion: input.acceptedExecution.targetSessionExecution.toolPolicyVersion,
1852
2186
  }
1853
- : {}),
2187
+ : {
2188
+ tools: input.acceptedExecution.resolvedTools,
2189
+ firstPartyMcpTools: input.acceptedExecution.resolvedFirstPartyMcpTools,
2190
+ firstPartyMcpPermissions: input.acceptedExecution.resolvedFirstPartyMcpPermissions,
2191
+ variableSetId: input.acceptedExecution.resolvedVariableSet?.id ?? null,
2192
+ rigId: input.acceptedExecution.resolvedRig?.id ?? null,
2193
+ rigVersionId: input.acceptedExecution.resolvedRig?.versionId ?? null,
2194
+ toolPolicy: session.toolPolicy,
2195
+ mcpServerIds: [],
2196
+ toolPolicyVersion: session.toolPolicyVersion,
2197
+ },
1854
2198
  });
1855
2199
  const preflight = evaluateIncidentTelemetryPreflight({
1856
2200
  agentConfig: task.agentConfig,
@@ -1967,6 +2311,7 @@ async function seedScheduledGeneratedSessionRoute(input: {
1967
2311
 
1968
2312
  async function recoverBoundScheduledTaskDispatch(input: {
1969
2313
  db: Database;
2314
+ observability?: ProductUsageMetricsSink | undefined;
1970
2315
  bus: ControlActivityServices["bus"];
1971
2316
  settings: ControlActivityServices["settings"];
1972
2317
  wakeSessionWorkflow: WakeSessionWorkflowSignal | null;
@@ -1981,6 +2326,11 @@ async function recoverBoundScheduledTaskDispatch(input: {
1981
2326
  const generatedSession =
1982
2327
  task.runMode === "new_session_per_run" ||
1983
2328
  (task.runMode === "reusable_session" && task.reusableSessionId === null);
2329
+ // The immutable creator policy determines standing session authority, not
2330
+ // a manual caller's per-run ceiling. Read it for creation and recovery checks.
2331
+ const recoveredCreatorPolicy = generatedSession
2332
+ ? await getScheduledTaskCreatorPolicy(input.db, task.workspaceId, task.id)
2333
+ : null;
1984
2334
  const frozenSlack = input.acceptedExecution.resolvedSlackBotConnection;
1985
2335
  if (frozenSlack) {
1986
2336
  const currentSlack = await requireOpenGeniSlackBotConnection(
@@ -2003,9 +2353,8 @@ async function recoverBoundScheduledTaskDispatch(input: {
2003
2353
  let session: Awaited<ReturnType<typeof createSession>>;
2004
2354
  if (input.run.sessionId) {
2005
2355
  session = await requireSession(input.db, task.workspaceId, input.run.sessionId);
2006
- // A bound existing/reusable target must still carry the exact Variable Set
2007
- // and Slack bot binding frozen with this occurrence; recovery must fail the
2008
- // run terminally rather than deliver into a diverged session.
2356
+ // Preserve the reusable chat's Slack routing; the accepted target snapshot
2357
+ // independently fences its current Variable Set attachments during recovery.
2009
2358
  if (!generatedSession) assertReusableSessionBindingMatches(session, task);
2010
2359
  } else if (generatedSession) {
2011
2360
  const variableSet = input.acceptedExecution.resolvedVariableSet;
@@ -2049,14 +2398,6 @@ async function recoverBoundScheduledTaskDispatch(input: {
2049
2398
  }
2050
2399
  const taskMetadata = { ...task.agentConfig.metadata };
2051
2400
  delete taskMetadata[OPENGENI_SLACK_BOT_SESSION_METADATA_KEY];
2052
- // Tools and permissions were frozen into the accepted execution. The
2053
- // creator session policy is immutable on the task row (a tombstoned task
2054
- // still answers), so re-reading it here is deterministic for the same run.
2055
- const recoveredCreatorPolicy = await getScheduledTaskCreatorPolicy(
2056
- input.db,
2057
- task.workspaceId,
2058
- task.id,
2059
- );
2060
2401
  const created = await createSessionWithIdempotencyKeyResult(input.db, {
2061
2402
  accountId: task.accountId,
2062
2403
  workspaceId: task.workspaceId,
@@ -2066,6 +2407,12 @@ async function recoverBoundScheduledTaskDispatch(input: {
2066
2407
  tools: input.acceptedExecution.resolvedTools,
2067
2408
  firstPartyMcpTools: input.acceptedExecution.resolvedFirstPartyMcpTools,
2068
2409
  firstPartyMcpPermissions: input.acceptedExecution.resolvedFirstPartyMcpPermissions,
2410
+ ...(input.acceptedExecution.resolvedAgentConfig
2411
+ ? { agentConfig: input.acceptedExecution.resolvedAgentConfig }
2412
+ : {}),
2413
+ ...(input.acceptedExecution.resolvedAgentInstructions !== undefined
2414
+ ? { instructions: input.acceptedExecution.resolvedAgentInstructions }
2415
+ : {}),
2069
2416
  ...scheduledCreatorSessionPolicyInput(recoveredCreatorPolicy?.sessionPolicy ?? null),
2070
2417
  ...(task.agentConfig.knowledgeSource?.destination.kind === "personal"
2071
2418
  ? {
@@ -2075,6 +2422,10 @@ async function recoverBoundScheduledTaskDispatch(input: {
2075
2422
  : {}),
2076
2423
  metadata: {
2077
2424
  ...taskMetadata,
2425
+ ...scheduledSessionExecutionPolicyMetadata(
2426
+ input.acceptedExecution.turnExecutionPolicy,
2427
+ recoveredCreatorPolicy?.credentialRestriction,
2428
+ ),
2078
2429
  model: input.acceptedExecution.resolvedModel,
2079
2430
  reasoningEffort: input.acceptedExecution.resolvedReasoningEffort,
2080
2431
  scheduledTaskId: task.id,
@@ -2103,6 +2454,8 @@ async function recoverBoundScheduledTaskDispatch(input: {
2103
2454
  personalConnectionDelegations: [],
2104
2455
  initialXaiProviderAccountAuthoritySnapshot:
2105
2456
  input.acceptedExecution.xaiProviderAccountAuthoritySnapshot,
2457
+ initialClaudeProviderAccountAuthoritySnapshot:
2458
+ input.acceptedExecution.claudeProviderAccountAuthoritySnapshot,
2106
2459
  maxNestedAgentDepthOverride: task.agentConfig.maxNestedAgentDepth ?? null,
2107
2460
  frozenNestedAgentDepthPolicy: {
2108
2461
  effectiveMaxNestedAgentDepth:
@@ -2133,6 +2486,7 @@ async function recoverBoundScheduledTaskDispatch(input: {
2133
2486
  });
2134
2487
  if (created.denied) throw new SessionSpawnDeniedDbError(created.denial);
2135
2488
  session = created.session;
2489
+ if (created.created) recordScheduledSessionCreated(input.observability);
2136
2490
  if (!created.created) {
2137
2491
  await bindScheduledTaskRunSessionInTransaction(input.db, {
2138
2492
  accountId: task.accountId,
@@ -2203,20 +2557,27 @@ async function recoverBoundScheduledTaskDispatch(input: {
2203
2557
  );
2204
2558
  const expectedTaskMetadata = { ...task.agentConfig.metadata };
2205
2559
  delete expectedTaskMetadata[OPENGENI_SLACK_BOT_SESSION_METADATA_KEY];
2206
- const expectedMetadata = {
2207
- ...expectedTaskMetadata,
2208
- model: input.acceptedExecution.resolvedModel,
2209
- reasoningEffort: input.acceptedExecution.resolvedReasoningEffort,
2210
- scheduledTaskId: task.id,
2211
- scheduledTaskRunMode: task.runMode,
2212
- ...(task.agentConfig.goal ? { scheduledTaskGoal: task.agentConfig.goal } : {}),
2213
- scheduledTaskRunId: canonicalGeneratedRunId,
2214
- ...(frozenSlack ? { [OPENGENI_SLACK_BOT_SESSION_METADATA_KEY]: frozenSlack.id } : {}),
2215
- };
2560
+ const expectedMetadata = metadataWithAgentConfigCreateIdentity(
2561
+ {
2562
+ ...expectedTaskMetadata,
2563
+ ...scheduledSessionExecutionPolicyMetadata(
2564
+ input.acceptedExecution.turnExecutionPolicy,
2565
+ recoveredCreatorPolicy?.credentialRestriction,
2566
+ ),
2567
+ model: input.acceptedExecution.resolvedModel,
2568
+ reasoningEffort: input.acceptedExecution.resolvedReasoningEffort,
2569
+ scheduledTaskId: task.id,
2570
+ scheduledTaskRunMode: task.runMode,
2571
+ ...(task.agentConfig.goal ? { scheduledTaskGoal: task.agentConfig.goal } : {}),
2572
+ scheduledTaskRunId: canonicalGeneratedRunId,
2573
+ ...(frozenSlack ? { [OPENGENI_SLACK_BOT_SESSION_METADATA_KEY]: frozenSlack.id } : {}),
2574
+ },
2575
+ input.acceptedExecution.resolvedAgentConfig,
2576
+ );
2216
2577
  if (
2217
2578
  session.createIdempotencyKey !== expectedCreateKey ||
2218
2579
  session.initialMessage !== task.agentConfig.prompt ||
2219
- session.instructions !== null ||
2580
+ session.instructions !== (input.acceptedExecution.resolvedAgentInstructions ?? null) ||
2220
2581
  session.policyRole !== null ||
2221
2582
  stableJson(session.skills) !== "[]" ||
2222
2583
  stableJson(session.toolPolicy) !==
@@ -2258,6 +2619,8 @@ async function recoverBoundScheduledTaskDispatch(input: {
2258
2619
  stableJson(input.acceptedExecution.resolvedFirstPartyMcpTools) ||
2259
2620
  stableJson(session.firstPartyMcpPermissions) !==
2260
2621
  stableJson(input.acceptedExecution.resolvedFirstPartyMcpPermissions) ||
2622
+ stableJson(session.agent) !==
2623
+ stableJson(input.acceptedExecution.resolvedAgentConfig ?? null) ||
2261
2624
  session.maxNestedAgentDepthOverride !== (task.agentConfig.maxNestedAgentDepth ?? null) ||
2262
2625
  (session.variableSetId ?? null) !==
2263
2626
  (input.acceptedExecution.resolvedVariableSet?.id ?? null) ||
@@ -2354,10 +2717,16 @@ async function recoverBoundScheduledTaskDispatch(input: {
2354
2717
  ...(input.acceptedExecution.xaiAuthoritySubjectId
2355
2718
  ? { xaiAuthoritySubjectId: input.acceptedExecution.xaiAuthoritySubjectId }
2356
2719
  : {}),
2720
+ ...(input.acceptedExecution.claudeAuthoritySubjectId
2721
+ ? { claudeAuthoritySubjectId: input.acceptedExecution.claudeAuthoritySubjectId }
2722
+ : {}),
2357
2723
  },
2358
2724
  personalConnectionDelegations: input.acceptedExecution.personalConnectionDelegations,
2725
+ mcpAccountBindings: input.acceptedExecution.mcpAccountBindings ?? null,
2359
2726
  xaiProviderAccountAuthoritySnapshot:
2360
2727
  input.acceptedExecution.xaiProviderAccountAuthoritySnapshot,
2728
+ claudeProviderAccountAuthoritySnapshot:
2729
+ input.acceptedExecution.claudeProviderAccountAuthoritySnapshot,
2361
2730
  scheduledTaskRunId: input.run.id,
2362
2731
  },
2363
2732
  async (tx, wakeEventId, _updateId, rejectionReason) => {
@@ -2422,6 +2791,9 @@ async function recoverBoundScheduledTaskDispatch(input: {
2422
2791
  ) {
2423
2792
  return { action: "blocked", reason: "scheduled_run_terminal" };
2424
2793
  }
2794
+ if (scheduledUpdate.added) {
2795
+ recordUserMessageAccepted(input.observability, { surface: "scheduled" });
2796
+ }
2425
2797
  if (scheduledUpdate.added && scheduledUpdate.events.length > 0) {
2426
2798
  await publishDurableSessionEvents(
2427
2799
  input.bus,