@opengeni/worker-bundle 2.2.0 → 2.2.1-canary.36927100590001

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 (88) 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 +1 -0
  4. package/dist/activities/agent-turn/claude-usage-observer.d.ts +13 -1
  5. package/dist/activities/agent-turn/compaction-prep.d.ts +20 -0
  6. package/dist/activities/agent-turn/credential-restriction.d.ts +3 -0
  7. package/dist/activities/agent-turn/final-reply.d.ts +18 -0
  8. package/dist/activities/agent-turn/governance-model.d.ts +6 -0
  9. package/dist/activities/agent-turn/model-call-admission.d.ts +15 -0
  10. package/dist/activities/agent-turn/run-credentials.d.ts +4 -0
  11. package/dist/activities/agent-turn/sandbox-establish.d.ts +1 -0
  12. package/dist/activities/agent-turn/session-title.d.ts +3 -1
  13. package/dist/activities/agent-turn/stream-attempt.d.ts +1 -0
  14. package/dist/activities/agent-turn/turn-context.d.ts +1 -0
  15. package/dist/activities/automations.d.ts +3 -1
  16. package/dist/activities/browser-deadline-checkpoint.d.ts +29 -0
  17. package/dist/activities/goals.d.ts +8 -0
  18. package/dist/activities/run-credentials.d.ts +4 -2
  19. package/dist/activities/scheduled-tasks.d.ts +4 -35
  20. package/dist/activities/types.d.ts +4 -1
  21. package/dist/activities/workspace-capture.d.ts +2 -0
  22. package/dist/{activities-control-MHNA3CYV.js → activities-control-4F2A734B.js} +502 -133
  23. package/dist/activities-control-4F2A734B.js.map +1 -0
  24. package/dist/activities-control.d.ts +30 -0
  25. package/dist/{activities-turn-FQ5OO2WW.js → activities-turn-TESOIOEX.js} +696 -251
  26. package/dist/activities-turn-TESOIOEX.js.map +1 -0
  27. package/dist/activities.d.ts +30 -0
  28. package/dist/browser-deadline-checkpoint-contract.d.ts +2 -0
  29. package/dist/{chunk-RZAAGRFQ.js → chunk-4PJTUBWQ.js} +220 -88
  30. package/dist/chunk-4PJTUBWQ.js.map +1 -0
  31. package/dist/{chunk-IJZU752L.js → chunk-YG27NEMX.js} +34 -1
  32. package/dist/chunk-YG27NEMX.js.map +1 -0
  33. package/dist/index.d.ts +15 -0
  34. package/dist/index.js +46 -24
  35. package/dist/index.js.map +1 -1
  36. package/dist/observability-metrics.d.ts +16 -1
  37. package/dist/sandbox-resume.d.ts +4 -1
  38. package/dist/workflow-bundle.js +89 -21
  39. package/dist/workflows/browser-deadline-checkpoint.d.ts +9 -0
  40. package/dist/workflows/scheduled-tasks.d.ts +4 -0
  41. package/dist/workflows.d.ts +1 -0
  42. package/package.json +21 -20
  43. package/src/activities/agent-run-admission.ts +17 -1
  44. package/src/activities/agent-turn/admission.ts +64 -1
  45. package/src/activities/agent-turn/agent-build.ts +24 -3
  46. package/src/activities/agent-turn/claim.ts +69 -15
  47. package/src/activities/agent-turn/claude-usage-observer.ts +33 -1
  48. package/src/activities/agent-turn/compaction-prep.ts +67 -16
  49. package/src/activities/agent-turn/credential-restriction.ts +14 -0
  50. package/src/activities/agent-turn/errors.ts +55 -0
  51. package/src/activities/agent-turn/failure-settlement.ts +26 -6
  52. package/src/activities/agent-turn/final-reply.ts +46 -0
  53. package/src/activities/agent-turn/governance-model.ts +12 -0
  54. package/src/activities/agent-turn/model-call-admission.ts +94 -0
  55. package/src/activities/agent-turn/run-credentials.ts +20 -3
  56. package/src/activities/agent-turn/run.ts +44 -5
  57. package/src/activities/agent-turn/sandbox-establish.ts +9 -5
  58. package/src/activities/agent-turn/session-title.ts +8 -2
  59. package/src/activities/agent-turn/stream-attempt.ts +144 -38
  60. package/src/activities/agent-turn/tool-environment.ts +55 -39
  61. package/src/activities/agent-turn/turn-context.ts +1 -0
  62. package/src/activities/automations.ts +78 -4
  63. package/src/activities/browser-deadline-checkpoint.ts +279 -0
  64. package/src/activities/capabilities.ts +52 -14
  65. package/src/activities/goals.ts +95 -98
  66. package/src/activities/knowledge-indexing.ts +29 -0
  67. package/src/activities/open-suffix-resume.ts +2 -1
  68. package/src/activities/run-credentials.ts +22 -16
  69. package/src/activities/sandbox-lease.ts +22 -0
  70. package/src/activities/scheduled-tasks.ts +172 -38
  71. package/src/activities/site-auth-maintenance.ts +1 -0
  72. package/src/activities/types.ts +4 -0
  73. package/src/activities/workspace-capture.ts +13 -2
  74. package/src/activities/workspace-credential-provider.ts +12 -0
  75. package/src/activities-control.ts +2 -0
  76. package/src/activities.ts +3 -0
  77. package/src/browser-deadline-checkpoint-contract.ts +5 -0
  78. package/src/index.ts +53 -25
  79. package/src/observability-metrics.ts +55 -1
  80. package/src/sandbox-resume.ts +23 -8
  81. package/src/sandbox-routing.ts +4 -2
  82. package/src/workflows/browser-deadline-checkpoint.ts +50 -0
  83. package/src/workflows/scheduled-tasks.ts +26 -13
  84. package/src/workflows.ts +4 -0
  85. package/dist/activities-control-MHNA3CYV.js.map +0 -1
  86. package/dist/activities-turn-FQ5OO2WW.js.map +0 -1
  87. package/dist/chunk-IJZU752L.js.map +0 -1
  88. package/dist/chunk-RZAAGRFQ.js.map +0 -1
@@ -2,12 +2,18 @@ import { createHash } from "node:crypto";
2
2
  import { errorCodeToJSON } from "@opengeni/agent-proto";
3
3
  import {
4
4
  BundledSkillId,
5
+ CREDIT_GRANT_CLASSES,
5
6
  SandboxBackend,
6
7
  type SessionEventType,
7
8
  type SkillReadKind,
8
9
  type SkillUseSource,
9
10
  } from "@opengeni/contracts";
10
- import type { SessionEventAppendPhaseObservation } from "@opengeni/db";
11
+ import {
12
+ ACTIVE_USER_WINDOWS,
13
+ type ActiveUserWindow,
14
+ type CreditGrantTotals,
15
+ type SessionEventAppendPhaseObservation,
16
+ } from "@opengeni/db";
11
17
  import {
12
18
  natsSubscriptionTerminationCounter,
13
19
  type EventBusOptions,
@@ -1122,6 +1128,54 @@ export function recordCreditBalanceGauges(
1122
1128
  creditBalanceGaugeAccounts.set(observability, current);
1123
1129
  }
1124
1130
 
1131
+ /**
1132
+ * Distinct managed people with authenticated browser activity in each fixed
1133
+ * window (migration 0565 presence). Every control worker publishes the same
1134
+ * global value, so dashboards must aggregate with `max()` across pods, never
1135
+ * `sum()`. API keys, services and embedded hosts are never counted.
1136
+ */
1137
+ export function recordActiveUserGauges(
1138
+ observability: Observability,
1139
+ counts: Record<ActiveUserWindow, number>,
1140
+ ): void {
1141
+ for (const window of ACTIVE_USER_WINDOWS) {
1142
+ observability.setGauge({
1143
+ name: "opengeni_active_users",
1144
+ help: "Distinct managed users with authenticated activity within the window (global; use max across pods).",
1145
+ labels: { window },
1146
+ value: counts[window],
1147
+ });
1148
+ }
1149
+ }
1150
+
1151
+ /**
1152
+ * Positive credit grants observed by the ledger trigger since migration 0565,
1153
+ * by closed class. These are cumulative database totals published as gauges by
1154
+ * every control worker: aggregate with `max()` across pods, then `increase()`.
1155
+ * Counting in the database covers the grants no application process writes:
1156
+ * the verified-signup trial grant (a setup trigger) and operator grants.
1157
+ */
1158
+ export function recordCreditGrantGauges(
1159
+ observability: Observability,
1160
+ totals: CreditGrantTotals,
1161
+ ): void {
1162
+ for (const grantClass of CREDIT_GRANT_CLASSES) {
1163
+ const total = totals[grantClass];
1164
+ observability.setGauge({
1165
+ name: "opengeni_credit_grants_total",
1166
+ help: "Positive credit grants observed in the ledger since migration 0565, by grant class (global cumulative; use max across pods).",
1167
+ labels: { grant_class: grantClass },
1168
+ value: total.count,
1169
+ });
1170
+ observability.setGauge({
1171
+ name: "opengeni_credit_granted_micros_total",
1172
+ help: "Credit micros granted in the ledger since migration 0565, by grant class (global cumulative; use max across pods).",
1173
+ labels: { grant_class: grantClass },
1174
+ value: total.micros,
1175
+ });
1176
+ }
1177
+ }
1178
+
1125
1179
  /**
1126
1180
  * The deployment-level runtime switch for the one-time verified signup trial
1127
1181
  * credit (migration 0521): 1 while it allows grants, 0 when an operator has
@@ -210,6 +210,9 @@ export type ResumeBoxIds = {
210
210
  * never conflicts).
211
211
  */
212
212
  image?: string;
213
+ /** Deployment/workspace image pins apply only to new creates; an existing
214
+ * group keeps its image. Omission preserves explicit image matching (B3). */
215
+ imagePolicy?: "require_match" | "new_creates_only";
213
216
  /**
214
217
  * RIG IS SHARED STATE (M3): the frozen rig version this run rides. Threaded to
215
218
  * acquireLease, which stamps it on the cold-create and conflicts on a live box
@@ -445,9 +448,10 @@ export async function waitForSandboxExecReadiness(
445
448
  established: EstablishedSandboxSession,
446
449
  timeoutMs = MODAL_EXEC_READINESS_TIMEOUT_MS,
447
450
  identity: { sandboxGroupId?: string | null } = {},
451
+ signal?: AbortSignal,
448
452
  ): Promise<void> {
449
453
  try {
450
- await verifySandboxExecReadiness(established, timeoutMs);
454
+ await verifySandboxExecReadiness(established, timeoutMs, signal);
451
455
  } catch (error) {
452
456
  if (error instanceof SandboxExecReadinessError && error.code === "exec_probe_timeout") {
453
457
  throw new SandboxExecReadinessTimeoutError(established.backendId, timeoutMs, {
@@ -1347,11 +1351,12 @@ async function resumeBoxForTurnOnce(
1347
1351
  },
1348
1352
  os,
1349
1353
  // IMAGE IS SHARED STATE (B3): thread the resolved image so the lease stamps it +
1350
- // conflicts on a live box already running a different image. A
1354
+ // conflicts on a live box already running a different required image. A
1351
1355
  // SandboxImageConflictError propagates while another holder is active; a
1352
1356
  // solo change requests a capture-and-drain rotation and this attempt retries
1353
1357
  // after the cold successor can safely stamp the new image.
1354
1358
  ...(ids.image ? { image: ids.image } : {}),
1359
+ ...(ids.imagePolicy ? { imagePolicy: ids.imagePolicy } : {}),
1355
1360
  // RIG IS SHARED STATE (M3): thread the frozen rig version so the lease stamps it
1356
1361
  // + conflicts on a live box under a different rig. A SandboxRigConflictError
1357
1362
  // propagates while another holder is active; a solo change uses the same
@@ -1917,9 +1922,14 @@ async function resumeBoxForTurnOnce(
1917
1922
  sandboxGroupId: ids.sandboxGroupId,
1918
1923
  });
1919
1924
  } else {
1920
- await waitForSandboxExecReadiness(established, MODAL_EXEC_READINESS_TIMEOUT_MS, {
1921
- sandboxGroupId: ids.sandboxGroupId,
1922
- });
1925
+ await waitForSandboxExecReadiness(
1926
+ established,
1927
+ MODAL_EXEC_READINESS_TIMEOUT_MS,
1928
+ {
1929
+ sandboxGroupId: ids.sandboxGroupId,
1930
+ },
1931
+ cancellationSignal,
1932
+ );
1923
1933
  }
1924
1934
  await maybeRenewProviderExpiration(true);
1925
1935
  throwIfReleasedOrCancelled();
@@ -2117,9 +2127,14 @@ async function resumeBoxForTurnOnce(
2117
2127
  if (services.verifyAttachedSandboxReadiness) {
2118
2128
  await services.verifyAttachedSandboxReadiness(established);
2119
2129
  } else {
2120
- await waitForSandboxExecReadiness(established, MODAL_EXEC_READINESS_TIMEOUT_MS, {
2121
- sandboxGroupId: ids.sandboxGroupId,
2122
- });
2130
+ await waitForSandboxExecReadiness(
2131
+ established,
2132
+ MODAL_EXEC_READINESS_TIMEOUT_MS,
2133
+ {
2134
+ sandboxGroupId: ids.sandboxGroupId,
2135
+ },
2136
+ cancellationSignal,
2137
+ );
2123
2138
  }
2124
2139
  providerRenewalTarget = {
2125
2140
  backend: ids.backend,
@@ -496,7 +496,7 @@ function afterPersistableHomeMutation(
496
496
  op: string;
497
497
  backend: ResolvedActiveBackend;
498
498
  admission: unknown;
499
- outcome: "resolved" | "rejected";
499
+ outcome: "resolved" | "rejected" | "outcome_unknown";
500
500
  result?: unknown;
501
501
  retainedProcess?: RoutingRetainedProcess;
502
502
  }) => Promise<void | RoutingMutationSettlementResult>)
@@ -523,7 +523,7 @@ function afterPersistableHomeMutation(
523
523
  ) {
524
524
  throw new Error("Persistable home mutation settlement lacked its bound admission");
525
525
  }
526
- if (outcome === "resolved" && retainedProcess) {
526
+ if ((outcome === "resolved" || outcome === "outcome_unknown") && retainedProcess) {
527
527
  try {
528
528
  await retainWorkspaceProviderCommand(services.db, {
529
529
  accountId: fence.accountId,
@@ -582,6 +582,8 @@ function afterPersistableHomeMutation(
582
582
  }
583
583
  return;
584
584
  }
585
+ if (outcome === "outcome_unknown")
586
+ throw new Error("Outcome-unknown command settlement requires its exact retained invocation");
585
587
  await verifyWorkspaceMutationSettlement(services.db, {
586
588
  accountId: fence.accountId,
587
589
  workspaceId: ids.workspaceId,
@@ -0,0 +1,50 @@
1
+ import {
2
+ ParentClosePolicy,
3
+ WorkflowIdReusePolicy,
4
+ log,
5
+ proxyActivities,
6
+ startChild,
7
+ workflowInfo,
8
+ } from "@temporalio/workflow";
9
+ import type { BrowserDeadlineCheckpointTarget } from "@opengeni/db";
10
+ import type * as activities from "../activities";
11
+
12
+ const inventory = proxyActivities<Pick<typeof activities, "listDueBrowserCheckpoints">>({
13
+ startToCloseTimeout: "5 minutes",
14
+ heartbeatTimeout: "20 seconds",
15
+ });
16
+ const checkpoint = proxyActivities<Pick<typeof activities, "checkpointBrowserBeforeDeadline">>({
17
+ startToCloseTimeout: "25 minutes",
18
+ heartbeatTimeout: "20 seconds",
19
+ retry: { initialInterval: "1 second", maximumInterval: "30 seconds" },
20
+ });
21
+
22
+ export async function browserDeadlineCheckpointWorkflow(target: BrowserDeadlineCheckpointTarget) {
23
+ return await checkpoint.checkpointBrowserBeforeDeadline(target);
24
+ }
25
+
26
+ /** Runs from the existing lease-sweep dispatch. Inventory never performs provider
27
+ * I/O; each exact browser has an independent durable capture/cleanup child. */
28
+ export async function browserDeadlineCheckpointSweepWorkflow(): Promise<void> {
29
+ const targets = await inventory.listDueBrowserCheckpoints();
30
+ await Promise.all(
31
+ targets.map(async (target) => {
32
+ try {
33
+ await startChild(browserDeadlineCheckpointWorkflow, {
34
+ workflowId: `browser-deadline:${target.browserSessionId}:${target.leaseEpoch}:${target.controllerGeneration}`,
35
+ taskQueue: workflowInfo().taskQueue,
36
+ workflowIdReusePolicy: WorkflowIdReusePolicy.ALLOW_DUPLICATE,
37
+ parentClosePolicy: ParentClosePolicy.ABANDON,
38
+ args: [target],
39
+ });
40
+ } catch (error) {
41
+ if (error instanceof Error && error.name === "WorkflowExecutionAlreadyStartedError") return;
42
+ // Another sweep retries start; it cannot retire the browser's authority.
43
+ log.warn("browser deadline checkpoint child start deferred", {
44
+ browserSessionId: target.browserSessionId,
45
+ error: error instanceof Error ? error.name : "unknown",
46
+ });
47
+ }
48
+ }),
49
+ );
50
+ }
@@ -7,6 +7,7 @@ import {
7
7
  WorkflowIdReusePolicy,
8
8
  } from "@temporalio/workflow";
9
9
  import type { TurnInitiator } from "@opengeni/contracts";
10
+ import type { DispatchScheduledTaskRunInput } from "../activities/types";
10
11
  import { scheduledTaskActivity } from "./activities";
11
12
  import {
12
13
  knowledgeSourceSyncWake,
@@ -26,14 +27,38 @@ export type ScheduledTaskFireWorkflowInput = ScheduledTaskFireWorkflowBase &
26
27
  triggerType: "scheduled";
27
28
  agentRunUsageIdempotencyKey?: never;
28
29
  initiator?: never;
30
+ credentialRestriction?: never;
29
31
  }
30
32
  | {
31
33
  triggerType: "manual" | "initial" | "provider_event" | "retry" | "repair";
32
34
  agentRunUsageIdempotencyKey: string;
33
35
  initiator: TurnInitiator;
36
+ credentialRestriction?: "developer_setup";
34
37
  }
35
38
  );
36
39
 
40
+ export function scheduledTaskDispatchInput(
41
+ input: ScheduledTaskFireWorkflowInput,
42
+ producerKey: string,
43
+ ): DispatchScheduledTaskRunInput {
44
+ const base = {
45
+ workspaceId: input.workspaceId,
46
+ taskId: input.taskId,
47
+ producerKey,
48
+ };
49
+ return input.triggerType !== "scheduled"
50
+ ? {
51
+ ...base,
52
+ triggerType: input.triggerType,
53
+ agentRunUsageIdempotencyKey: input.agentRunUsageIdempotencyKey,
54
+ initiator: input.initiator,
55
+ ...(input.credentialRestriction === "developer_setup"
56
+ ? { credentialRestriction: input.credentialRestriction }
57
+ : {}),
58
+ }
59
+ : { ...base, triggerType: "scheduled" };
60
+ }
61
+
37
62
  export async function scheduledTaskFireWorkflow(
38
63
  input: ScheduledTaskFireWorkflowInput,
39
64
  ): Promise<void> {
@@ -47,20 +72,8 @@ export async function scheduledTaskFireWorkflow(
47
72
  ) {
48
73
  return;
49
74
  }
50
- const base = {
51
- workspaceId: input.workspaceId,
52
- taskId: input.taskId,
53
- producerKey: workflowInfo().workflowId,
54
- };
55
75
  const result = await scheduledTaskActivity.dispatchScheduledTaskRun(
56
- input.triggerType !== "scheduled"
57
- ? {
58
- ...base,
59
- triggerType: input.triggerType,
60
- agentRunUsageIdempotencyKey: input.agentRunUsageIdempotencyKey,
61
- initiator: input.initiator,
62
- }
63
- : { ...base, triggerType: "scheduled" },
76
+ scheduledTaskDispatchInput(input, workflowInfo().workflowId),
64
77
  );
65
78
  if (result.action !== "knowledge_source_sync") return;
66
79
 
package/src/workflows.ts CHANGED
@@ -28,6 +28,10 @@ export {
28
28
  sandboxReaperWorkflowV2,
29
29
  } from "./workflows/sandbox-reaper";
30
30
  export { fileUploadReaperWorkflow } from "./workflows/file-upload-reaper";
31
+ export {
32
+ browserDeadlineCheckpointWorkflow,
33
+ browserDeadlineCheckpointSweepWorkflow,
34
+ } from "./workflows/browser-deadline-checkpoint";
31
35
  export { siteAuthMaintenanceWorkflow } from "./workflows/site-auth-maintenance";
32
36
  export {
33
37
  videoGenerationWorkflow,