@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
@@ -0,0 +1,88 @@
1
+ import { createHash } from "node:crypto";
2
+ import type { ObjectStorage } from "@opengeni/storage";
3
+ import type { GmailFileMaterializationRequest } from "@opengeni/runtime/gmail-rest-mcp";
4
+ import type { ConnectorAttachmentMaterializer } from "@opengeni/runtime";
5
+ import type { SandboxChannelAService } from "@opengeni/runtime/sandbox";
6
+
7
+ /** Use the selected filesystem namespace, retaining descriptor confinement and
8
+ * one extra byte to distinguish a complete exact-limit input from truncation. */
9
+ export async function readGmailFileFromChannel(
10
+ channel: Pick<SandboxChannelAService, "fsRead">,
11
+ request: { path: string; maxBytes: number },
12
+ ): Promise<Uint8Array> {
13
+ const file = await channel.fsRead({
14
+ path: request.path,
15
+ maxBytes: request.maxBytes + 1,
16
+ encoding: "base64",
17
+ workspaceOnly: true,
18
+ });
19
+ const bytes = Buffer.from(file.content, "base64");
20
+ if (file.truncated || bytes.byteLength > request.maxBytes)
21
+ throw new Error("Gmail file input exceeds the maximum size");
22
+ return bytes;
23
+ }
24
+
25
+ /** Gmail returns base64url JSON, while the exact filesystem importer consumes
26
+ * byte streams. Stage only inside the worker; no URL or bytes reach a tool result. */
27
+ export async function materializeGmailFile(
28
+ request: GmailFileMaterializationRequest,
29
+ options: {
30
+ workspaceId: string;
31
+ storage: ObjectStorage;
32
+ downloadStorage: ObjectStorage;
33
+ materialize: ConnectorAttachmentMaterializer;
34
+ onCleanupFailure?: (key: string) => void;
35
+ },
36
+ ) {
37
+ if (!(await request.authorizeProviderRequest()))
38
+ throw new Error("Gmail file authority is unavailable");
39
+ const hash = createHash("sha256").update(request.bytes).digest("hex");
40
+ const key = `workspaces/${options.workspaceId}/private-connector-transfers/${crypto.randomUUID()}`;
41
+ try {
42
+ await options.storage.putObject({
43
+ key,
44
+ body: request.bytes,
45
+ contentType: request.mediaType,
46
+ sha256: hash,
47
+ });
48
+ const source = await options.downloadStorage.createGetUrl({ key, expiresInSeconds: 300 });
49
+ const envelope = await options.materialize({
50
+ serverId: request.serverId,
51
+ toolName: request.providerAttachmentId.value.endsWith(":raw")
52
+ ? "download_message"
53
+ : "download_attachment",
54
+ operationId: request.operationId,
55
+ connectionId: request.connectionId,
56
+ authorizeProviderRequest: request.authorizeProviderRequest,
57
+ attachments: [
58
+ {
59
+ providerAttachmentId: request.providerAttachmentId,
60
+ fileName: request.fileName,
61
+ mediaType: request.mediaType,
62
+ byteSize: request.bytes.byteLength,
63
+ contentSha256: hash,
64
+ source: { url: source.url, expiresAt: source.expiresAt.toISOString() },
65
+ },
66
+ ],
67
+ });
68
+ return { attachments: envelope.attachments };
69
+ } finally {
70
+ // The transfer object is never a retained workspace File or Knowledge entry.
71
+ let cleaned = false;
72
+ for (let attempt = 0; attempt < 3 && !cleaned; attempt++) {
73
+ try {
74
+ await options.storage.deleteObject(key);
75
+ cleaned = true;
76
+ } catch {
77
+ /* Idempotent storage cleanup must not erase delivery or uncertainty. */
78
+ }
79
+ }
80
+ if (!cleaned) {
81
+ try {
82
+ options.onCleanupFailure?.(key);
83
+ } catch {
84
+ /* Observability cannot replace a file result. */
85
+ }
86
+ }
87
+ }
88
+ }
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  allowedFirstPartyMcpToolsForSession,
3
3
  configuredStaticUsageLimits,
4
+ isModelAvailableForNewSelection,
4
5
  policyProviderIdForModel,
5
6
  resolveModelProvider,
6
7
  resolveTurnExecutionPolicyV1,
@@ -13,21 +14,18 @@ import {
13
14
  import {
14
15
  evaluateWorkspaceModelPolicy,
15
16
  mergeToolRefs,
17
+ readTurnExecutionPolicyV1,
16
18
  type SessionGoal,
17
19
  type ToolRef,
18
20
  } from "@opengeni/contracts";
19
21
  import { isCodexBilledModel } from "@opengeni/codex";
20
22
  import {
21
23
  enqueueSessionWorkflowWakeIfRunnable,
22
- getBillingBalance,
23
- getLatestStartedSessionTurn,
24
24
  getWorkspaceModelPolicy,
25
25
  getSessionGoal,
26
- isCodexBilledTurn,
26
+ getSessionTurn,
27
27
  materializeGoalContinuation,
28
28
  requireSession,
29
- sumUsageQuantity,
30
- type Database,
31
29
  } from "@opengeni/db";
32
30
  import type {
33
31
  ControlActivityServices,
@@ -39,6 +37,8 @@ import {
39
37
  resolveCatalogSettings,
40
38
  resolveWorkspaceCatalogSettings,
41
39
  } from "@opengeni/core";
40
+ import { agentRunAdmissionDenial } from "./agent-run-admission";
41
+ import { turnCredentialRestriction } from "./agent-turn/credential-restriction";
42
42
 
43
43
  export function createGoalActivities(services: () => Promise<ControlActivityServices>) {
44
44
  async function enqueueGoalRetryWake(input: MaybeContinueGoalInput): Promise<void> {
@@ -66,25 +66,25 @@ export function createGoalActivities(services: () => Promise<ControlActivityServ
66
66
  if (!existingGoal || existingGoal.status !== "active") {
67
67
  return { action: "none" };
68
68
  }
69
- let settings = (await resolveCatalogSettings(db, catalogSourceSettings)).settings;
70
69
  // Loaded before the budget check so the codex-billed predicate and the
71
70
  // synthesized turn use the SAME effective policy. An explicit per-turn
72
71
  // model can differ from the persisted session default; follow-up goal work
73
- // must preserve the newest policy that actually emitted `turn.started`.
74
- // Admission-rejected turns have no such event and cannot poison it.
72
+ // follows effective defaults: a started turn or a newer explicit settings
73
+ // boundary. Admission-rejected turns cannot poison that projection.
75
74
  // Kept below the goal-less fast path so a non-goal session still skips the
76
75
  // reads entirely.
77
76
  const session = await requireSession(db, input.workspaceId, input.sessionId);
78
- const latestStartedTurn = await getLatestStartedSessionTurn(
79
- db,
80
- input.workspaceId,
81
- input.sessionId,
82
- );
83
- const inheritedContinuationModel = latestStartedTurn?.model ?? session.model;
77
+ // Terminal sessions retain their goal for human recovery, but cannot
78
+ // continue. Do not validate an obsolete model before the locked guard gets
79
+ // the chance to reject that work; otherwise a deterministic error retries.
80
+ if (session.status === "failed" || session.status === "cancelled") {
81
+ return { action: "none" };
82
+ }
83
+ let settings = (await resolveCatalogSettings(db, catalogSourceSettings)).settings;
84
+ const inheritedContinuationModel = session.model;
84
85
  let continuationModel = inheritedContinuationModel;
85
- const continuationReasoningEffort =
86
- latestStartedTurn?.reasoningEffort ?? session.reasoningEffort;
87
- const continuationLatencyMode = latestStartedTurn?.latencyMode ?? session.latencyMode;
86
+ const continuationReasoningEffort = session.reasoningEffort;
87
+ const continuationLatencyMode = session.latencyMode;
88
88
  const workspaceModelPolicy = await getWorkspaceModelPolicy(db, input.workspaceId);
89
89
  if (
90
90
  inheritedContinuationModel.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX) ||
@@ -116,39 +116,27 @@ export function createGoalActivities(services: () => Promise<ControlActivityServ
116
116
  ) {
117
117
  modelPolicyBlocked = `session is locked to Codex remote compaction v2; model "${continuationModel}" is not a Codex subscription model`;
118
118
  }
119
- // A codex-model goal continuation is paid by the user's ChatGPT/Codex plan,
120
- // so it must not be budget-paused for zero OpenGeni credits. This file uses
121
- // BASE settings (no codex overlay); the predicate does its own credential read.
122
- const isCodexRun = await isCodexBilledTurn({
123
- db,
124
- settings,
125
- workspaceId: input.workspaceId,
119
+ const turnExecutionPolicy = modelPolicyBlocked
120
+ ? undefined
121
+ : resolveTurnExecutionPolicyV1(settings, {
122
+ modelId: continuationModel,
123
+ requestedModelId: null,
124
+ modelSource: "continuation",
125
+ reasoningEffort: continuationReasoningEffort,
126
+ reasoningSource: "continuation",
127
+ latencyMode: continuationLatencyMode,
128
+ latencyModeSource: "continuation",
129
+ });
130
+ const continuationPolicy: NonNullable<
131
+ Parameters<typeof materializeGoalContinuation>[1]["policy"]
132
+ > = {
126
133
  model: continuationModel,
127
- });
128
- const fundedWithoutCredits = goalContinuationFundedWithoutCredits(
129
- settings,
130
- continuationModel,
131
- isCodexRun,
132
- );
133
- // Budget exhaustion pauses the goal visibly instead of failing the
134
- // session. Computed up front and applied inside the locked decision so a
135
- // limits pause never consumes continuation budget.
136
- const budgetBlocked = await goalRunBudgetBlocked(
137
- settings,
138
- db,
139
- input.accountId,
140
- input.workspaceId,
141
- fundedWithoutCredits,
142
- );
143
- const turnExecutionPolicy = resolveTurnExecutionPolicyV1(settings, {
144
- modelId: continuationModel,
145
- requestedModelId: null,
146
- modelSource: "continuation",
147
134
  reasoningEffort: continuationReasoningEffort,
148
- reasoningSource: "continuation",
149
135
  latencyMode: continuationLatencyMode,
150
- latencyModeSource: "continuation",
151
- });
136
+ ...(turnExecutionPolicy ? { turnExecutionPolicy } : {}),
137
+ tools: withFirstPartyTools(settings, session.tools),
138
+ sandboxBackend: session.sandboxBackend,
139
+ };
152
140
  const decision = await materializeGoalContinuation(db, {
153
141
  accountId: input.accountId,
154
142
  workspaceId: input.workspaceId,
@@ -164,15 +152,45 @@ export function createGoalActivities(services: () => Promise<ControlActivityServ
164
152
  // A model-policy block takes precedence: it is deterministic (a budget
165
153
  // pause can clear on its own; a policy pause needs a model/policy change)
166
154
  // and rides the same visible-pause channel.
167
- budgetBlocked: modelPolicyBlocked ?? budgetBlocked,
168
- policy: {
169
- model: continuationModel,
170
- reasoningEffort: continuationReasoningEffort,
171
- latencyMode: continuationLatencyMode,
172
- turnExecutionPolicy,
173
- tools: withFirstPartyTools(settings, session.tools),
174
- sandboxBackend: session.sandboxBackend,
155
+ admission: async (tx, causalTurn) => {
156
+ // The materializer selects this exact causal row under its session/goal
157
+ // locks and freezes policy only after admission returns. Never infer
158
+ // credential authority from the generated continuation's payload.
159
+ const sourceTurn = causalTurn
160
+ ? await getSessionTurn(tx, input.workspaceId, causalTurn.id)
161
+ : null;
162
+ if (causalTurn && (!sourceTurn || sourceTurn.sessionId !== input.sessionId)) {
163
+ throw new Error("Goal continuation source turn is unavailable");
164
+ }
165
+ const sourcePolicy = readTurnExecutionPolicyV1(sourceTurn?.metadata);
166
+ if (turnExecutionPolicy) {
167
+ const credentialRestriction = turnCredentialRestriction(
168
+ sourcePolicy.kind === "valid" ? sourcePolicy.policy : turnExecutionPolicy,
169
+ session.metadata,
170
+ );
171
+ continuationPolicy.turnExecutionPolicy = credentialRestriction
172
+ ? { ...turnExecutionPolicy, credentialRestriction }
173
+ : turnExecutionPolicy;
174
+ }
175
+ const budgetBlocked = modelPolicyBlocked
176
+ ? null
177
+ : await goalRunBudgetBlocked(
178
+ { ...service, settings, db: tx },
179
+ {
180
+ accountId: input.accountId,
181
+ workspaceId: input.workspaceId,
182
+ model: continuationModel,
183
+ initiatingHumanSubjectId: causalTurn?.initiatingHumanSubjectId ?? null,
184
+ },
185
+ );
186
+ return {
187
+ budgetBlocked: modelPolicyBlocked ?? budgetBlocked?.message ?? null,
188
+ budgetPausedReason: modelPolicyBlocked
189
+ ? "limits"
190
+ : (budgetBlocked?.pausedReason ?? "limits"),
191
+ };
175
192
  },
193
+ policy: continuationPolicy,
176
194
  // Long-wait guidance is only given when `wait_for_input` is actually in this
177
195
  // session's effective first-party selection (the same source the worker
178
196
  // signs into the delegated token and the API uses to register tools), so
@@ -222,18 +240,21 @@ export function goalContinuationModelDecision(input: {
222
240
  providerId: policyProviderIdForModel(catalogSettings, modelId),
223
241
  modelId,
224
242
  }).allowed;
225
- if (
226
- resolveModelProvider(catalogSettings, input.inheritedModel) &&
227
- !policyBlocks(input.inheritedModel)
228
- ) {
229
- return { model: input.inheritedModel, blocked: null };
230
- }
231
243
  if (!resolveModelProvider(catalogSettings, input.inheritedModel)) {
232
244
  return {
233
245
  model: input.inheritedModel,
234
246
  blocked: `model "${input.inheritedModel}" is no longer in the deployment or workspace catalog; choose an available model before resuming the goal`,
235
247
  };
236
248
  }
249
+ if (!isModelAvailableForNewSelection(catalogSettings, input.inheritedModel)) {
250
+ return {
251
+ model: input.inheritedModel,
252
+ blocked: `model "${input.inheritedModel}" is retired from new selection; choose an available model before resuming the goal`,
253
+ };
254
+ }
255
+ if (!policyBlocks(input.inheritedModel)) {
256
+ return { model: input.inheritedModel, blocked: null };
257
+ }
237
258
  return {
238
259
  model: input.inheritedModel,
239
260
  blocked: `workspace model policy blocks model "${input.inheritedModel}"; pick an allowed model or change the workspace model policy`,
@@ -258,8 +279,9 @@ export function goalContinuationPrompt(
258
279
  ? [
259
280
  "Waiting on child sessions, background commands, or external events:",
260
281
  "- When further progress depends on work already in flight, do not sleep, loop, or poll session or command state repeatedly.",
261
- "- Re-check once; if the work is still in flight and the wait is long or uncertain, call opengeni__wait_for_input with a concrete reason and timeoutSeconds, then end your turn immediately. Relevant session input or the safety deadline will start a new turn, and this goal stays active.",
262
- "- If the immediately preceding user-facing update already reported this same unchanged wait, do not restate it or produce another equivalent final answer. Call opengeni__wait_for_input and end the turn. Report only material new state or a newly discovered blocker.",
282
+ "- If progress depends on work already in flight and the wait is long or uncertain, call opengeni__wait_for_input with a concrete reason and timeoutSeconds, then end your turn immediately. A preliminary status check is not required. Relevant session input or the safety deadline will start a new turn, and this goal stays active.",
283
+ "- No short execution wait is required first. Choose the safety deadline for the dependency or a meaningful monitoring cadence; an out-of-turn wait may span hours or days within the tool's limits. Do not schedule model wakeups merely to repeat reassurance. Honor explicit user/task/Skill check or update cadences within existing authority.",
284
+ "- If the immediately preceding user-facing update already reported this same unchanged wait, do not restate it or produce another equivalent final answer. Call opengeni__wait_for_input and end the turn. Report only material new state or a newly discovered blocker, unless an explicit user/task/Skill update cadence calls for an update.",
263
285
  "- If you are blocked on a human decision, use opengeni__goal_pause under the blocked audit below instead.",
264
286
  "",
265
287
  ]
@@ -270,18 +292,20 @@ export function goalContinuationPrompt(
270
292
  (options.humanInputRespondAvailable
271
293
  ? "If you know the answer to its question, answer it with opengeni__session_human_input_respond (pass the worker's sessionId, the requestId from the notice, and a response). "
272
294
  : "") +
273
- "Tool approvals can only be decided by a human. If you cannot resolve the blocker yourself, report the exact blocker (worker session id, the question) to the user and wait or pause instead of retrying the worker.",
295
+ "Tool approvals can only be decided by a human. If you cannot resolve the blocker yourself, report the exact blocker (worker session id, the question); if it prevents further goal progress, pause under the blocked audit instead of retrying the worker or treating a human decision as an in-flight wait.",
274
296
  "- `child_requires_action_resolved`, `child_paused`, `child_waiting_capacity`, and `child_progress` updates are informational: a resolved notice means the worker is moving again; a paused worker needs a human or you to resume it; a capacity wait resumes by itself; a progress note needs no action.",
275
297
  "",
276
298
  ];
277
299
  return [
278
- "Reconcile the active session goal against authoritative current state, then carry the work through to the full requested end state and verify it.",
300
+ "Automatic goal continuation (generated input, not a new user request or grant of authority). Resume established work toward the full applied objective and verify its requested end state.",
279
301
  "",
280
302
  "Goal recovery:",
303
+ "- Use the current applied goal frozen for this turn, including its success criteria, root constraints, and report requirements, as the authoritative objective. Pending proposals and older goal revisions do not replace it; change it only through the authorized goal-mutation controls, without widening authority.",
281
304
  "- Treat this continuation as re-entry into the full objective, not as a request to perform one step and stop.",
305
+ "- Resume from established work and relevant evidence rather than restarting discovery or a full reconciliation on every turn. Perform full reconciliation or a comprehensive audit when the goal, user, or applicable Skill calls for it, when uncertainty or recovery warrants it, or when required by risk or a gate.",
282
306
  "- Do not rely on previous assistant claims of progress or completion; use them only to locate authoritative evidence.",
283
307
  "- If authoritative evidence already proves the full objective, call opengeni__goal_complete instead of manufacturing more work.",
284
- "- Before repeating a state-setting action, verify whether its desired state already holds. If it does, do not repeat it; continue reconciling the overall goal.",
308
+ "- Before repeating a state-setting action, verify whether its desired state already holds, using relevant evidence that remains valid or a fresh check when needed. If it does, do not repeat it; continue the overall goal.",
285
309
  "",
286
310
  "Continuation behavior:",
287
311
  "- This goal persists across turns. A runtime boundary can end one turn without shrinking the objective; the next continuation resumes the same full objective.",
@@ -291,7 +315,7 @@ export function goalContinuationPrompt(
291
315
  "- Temporary rough edges are acceptable while the work is moving in the right direction. Completion still requires the requested end state to be true and verified.",
292
316
  "",
293
317
  "Work from evidence:",
294
- "Use the current workspace and external state as authoritative. Conversation context can help locate relevant work, but it is not proof of the current state. Inspect the current state before relying on it. Improve, replace, or remove existing work as needed to satisfy the actual objective.",
318
+ "Use authoritative workspace and external evidence, including prior tool results that remain relevant and valid. Reuse evidence only for the requirement, scope, version, and state it actually establishes; recheck changed, stale, uncertain, or insufficient evidence. Conversation summaries and assistant claims are pointers, not proof. Do not infer progress or evidence validity merely from this continuation. Improve, replace, or remove existing work as needed within authority to satisfy the actual objective.",
295
319
  "",
296
320
  "Progress visibility:",
297
321
  "If a planning tool is available and the next work is meaningfully multi-step, use it to show a concise plan tied to the real objective. Keep the plan current as steps complete or the next best action changes. Skip planning overhead for trivial one-step progress, and do not treat a plan update as a substitute for doing the work.",
@@ -302,22 +326,26 @@ export function goalContinuationPrompt(
302
326
  "- Treat alignment as movement toward the requested end state. An edit is aligned only if it makes the requested final state more true; useful-looking behavior that preserves a different end state is misaligned.",
303
327
  "",
304
328
  "Completion audit:",
305
- "Before deciding that the goal is achieved, treat completion as unproven and verify it against the actual current state:",
329
+ "Before deciding that the goal is achieved, treat completion as unproven and verify it against the actual current state. This full completion audit is required even when ordinary continuation did not need full reconciliation; reuse still-valid authoritative evidence, but refresh it when the requirement or gate requires a fresh check:",
306
330
  "- Derive concrete requirements from the objective and any referenced files, plans, specifications, issues, or user instructions.",
307
- "- Preserve the original scope; do not redefine success around the work that already exists.",
331
+ "- Preserve the applied objective's scope; do not redefine success around the work that already exists.",
308
332
  "- For every explicit requirement, named artifact, command, test, gate, invariant, and deliverable, identify and inspect the authoritative evidence that would prove it.",
309
333
  "- Match verification scope to requirement scope. Treat uncertain, indirect, incomplete, or missing evidence as not achieved and continue working.",
310
334
  "- The audit must prove completion, not merely fail to find obvious remaining work.",
335
+ "- For document report deliverables (a document the user asked for, or a large report meant to be kept or shared), follow the Documents Skill: create the durable native document first, inspect its relevant final head after the last edit, and provide the returned artifact reference. Declare report requirements through the available goal tools before authoring and satisfy every persisted report requirement with verified artifact delivery evidence before completion. Sandbox paths and raw file IDs do not prove report delivery. If artifact tooling or access is unavailable, keep that deliverable incomplete and state the blocker; never invent proof or silently substitute a local report. Ordinary chat answers, short progress updates, source-code links, and explicitly requested local-file work remain outside this report contract.",
311
336
  "",
312
337
  "Do not rely on intent, partial progress, memory of earlier work, or a plausible final answer as proof of completion. Call opengeni__goal_complete with concrete evidence only when the full objective is actually achieved and no required work remains.",
338
+ "Goal evidence is a short proof for the ledger, not the deliverable. After goal_complete succeeds, finish this same turn with the requested user-facing answer, or a concise summary and retained artifact link. Goal completion stops future automatic continuations; it does not send the answer or end this turn. Never compress a report into evidence or omit the final reply.",
339
+ "Goal progress notes are short human-readable milestone statuses, not raw transcripts or continuation instructions. Keep normal spaces and summarize detail instead of squeezing words into a ledger field. The text and successCriteria fields each allow 8192 UTF-8 bytes, progressNote allows 8192 UTF-8 bytes, rationale allows 2048 UTF-8 bytes, and evidence allows 8192 characters.",
313
340
  "",
314
341
  ...waitingGuidance,
315
342
  ...childNoticeGuidance,
316
343
  "Blocked audit:",
317
- "- Do not call opengeni__goal_pause the first time a blocker appears.",
318
- "- Pause only when the same blocking condition has repeated for at least three consecutive goal turns and meaningful progress is impossible without user input or an external-state change.",
344
+ "- Base persistence on evidence, not a fixed number of turns or retries. Investigate recoverable failures and try plausible safe alternatives within current authority when they could materially advance the goal; do not exhaust every imaginable alternative or repeat an unchanged failure without a reason to expect progress.",
345
+ "- A definitive missing permission, required human decision, or external prerequisite with no actionable authorized path can justify pausing immediately. State the concrete blocker, relevant evidence or attempted alternatives, and what must change to resume. Tool approvals remain human-only.",
346
+ "- If progress depends on work already in flight or a meaningful timed recheck, use the available waiting mechanism rather than pausing the goal; continue any independent authorized work first.",
319
347
  "- Do not pause merely because the work is hard, slow, uncertain, incomplete, or would benefit from clarification.",
320
- "- Once that threshold is satisfied, call opengeni__goal_pause with the concrete blocker instead of repeatedly reporting it while leaving the goal active.",
348
+ "- When no meaningful authorized progress remains and the blocker requires human input or an external change that cannot be handled by an available wait or monitoring mechanism, call opengeni__goal_pause with the concrete blocker instead of repeatedly reporting it while leaving the goal active.",
321
349
  "",
322
350
  "Do not call opengeni__goal_complete or opengeni__goal_pause unless the corresponding audit above is satisfied.",
323
351
  ].join("\n");
@@ -338,55 +366,22 @@ export function withFirstPartyTools(settings: Settings, tools: ToolRef[]): ToolR
338
366
  }
339
367
 
340
368
  /**
341
- * Non-throwing variant of the scheduled-run admission check: returns a human
342
- * readable reason when balance or monthly caps block another agent run.
369
+ * Goals share scheduled admission and pause visibly without synthesizing work.
343
370
  */
344
- async function goalRunBudgetBlocked(
345
- settings: Settings,
346
- db: Database,
347
- accountId: string,
348
- workspaceId: string,
349
- fundedWithoutCredits: boolean,
350
- ): Promise<string | null> {
351
- // Free, subscription, and workspace-funded continuations skip OpenGeni's
352
- // credit-balance gate and monthly model-cost cap. The agent-run COUNT cap
353
- // below is a volume quota (not a credit/cost gate) and remains enforced.
354
- if (
355
- !fundedWithoutCredits &&
356
- (settings.billingMode === "stripe" || settings.usageLimitsMode === "managed")
357
- ) {
358
- const balance = await getBillingBalance(db, accountId);
359
- if (balance.balanceMicros <= 0) {
360
- return "insufficient OpenGeni credits";
361
- }
362
- }
363
- if (settings.usageLimitsMode === "static" || settings.usageLimitsMode === "managed") {
364
- const limits = configuredStaticUsageLimits(settings);
365
- if (!fundedWithoutCredits && limits.maxMonthlyCostMicrosPerAccount) {
366
- const used = await sumUsageQuantity(db, {
367
- accountId,
368
- eventType: "model.cost",
369
- since: startOfUtcMonth(),
370
- });
371
- if (used >= limits.maxMonthlyCostMicrosPerAccount) {
372
- return `monthly model cost limit reached (${limits.maxMonthlyCostMicrosPerAccount} micros)`;
373
- }
374
- }
375
- if (limits.maxMonthlyAgentRunsPerWorkspace) {
376
- const used = await sumUsageQuantity(db, {
377
- workspaceId,
378
- eventType: "agent_run.created",
379
- since: startOfUtcMonth(),
380
- });
381
- if (used + 1 > limits.maxMonthlyAgentRunsPerWorkspace) {
382
- return `monthly agent run limit reached (${limits.maxMonthlyAgentRunsPerWorkspace})`;
383
- }
384
- }
371
+ export async function goalRunBudgetBlocked(
372
+ services: Parameters<typeof agentRunAdmissionDenial>[0],
373
+ input: Omit<Parameters<typeof agentRunAdmissionDenial>[1], "requestedAgentRuns">,
374
+ ): Promise<{ pausedReason: "limits" | "allowance"; message: string } | null> {
375
+ const denial = await agentRunAdmissionDenial(services, { ...input, requestedAgentRuns: 1 });
376
+ if (denial === null) return null;
377
+ if (denial === "allowance_exhausted") {
378
+ return { pausedReason: "allowance", message: "Opengeni usage allowance exhausted" };
385
379
  }
386
- return null;
387
- }
388
-
389
- function startOfUtcMonth(): Date {
390
- const now = new Date();
391
- return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
380
+ const limits = configuredStaticUsageLimits(services.settings);
381
+ const messages = {
382
+ insufficient_credits: "insufficient Opengeni credits",
383
+ monthly_model_cost_limit: `monthly model cost limit reached (${limits.maxMonthlyCostMicrosPerAccount} micros)`,
384
+ monthly_agent_run_limit: `monthly agent run limit reached (${limits.maxMonthlyAgentRunsPerWorkspace})`,
385
+ };
386
+ return { pausedReason: "limits", message: messages[denial] };
392
387
  }
@@ -90,9 +90,7 @@ export async function resolveGoogleDrivePublicationTarget(
90
90
  );
91
91
  if (frozen.length !== 1) return null;
92
92
  const delegation = frozen[0]!;
93
- const activated = delegation.userDelegation !== undefined;
94
93
  if (
95
- !activated &&
96
94
  !(await ports.getMembership(db, {
97
95
  accountId: workspace.accountId,
98
96
  workspaceId,
@@ -101,7 +99,7 @@ export async function resolveGoogleDrivePublicationTarget(
101
99
  ) {
102
100
  return null;
103
101
  }
104
- const originWorkspaceId = activated ? delegation.originWorkspaceId : workspaceId;
102
+ const originWorkspaceId = delegation.originWorkspaceId;
105
103
  if (!originWorkspaceId) return null;
106
104
  const connection = await ports.getConnection(
107
105
  db,
@@ -36,7 +36,7 @@ export class ImageGenerationRetentionFailedError extends Error {
36
36
  cause?: unknown,
37
37
  ) {
38
38
  super(
39
- "The image was generated, but OpenGeni could not save it. The paid provider request was not repeated.",
39
+ "The image was generated, but Opengeni could not save it. The paid provider request was not repeated.",
40
40
  cause === undefined ? undefined : { cause },
41
41
  );
42
42
  this.name = "ImageGenerationRetentionFailedError";