@opengeni/worker-bundle 2.2.1 → 2.3.0-canary.36935312914001

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 (37) hide show
  1. package/dist/activities/agent-turn/credential-restriction.d.ts +3 -0
  2. package/dist/activities/agent-turn/run-credentials.d.ts +2 -0
  3. package/dist/activities/scheduled-tasks.d.ts +4 -35
  4. package/dist/activities/types.d.ts +3 -0
  5. package/dist/activities/workspace-capture.d.ts +2 -0
  6. package/dist/{activities-control-55ECL47G.js → activities-control-RDA4E2DM.js} +102 -20
  7. package/dist/activities-control-RDA4E2DM.js.map +1 -0
  8. package/dist/{activities-turn-T7Y3BWYO.js → activities-turn-A7LDKRLD.js} +20 -7
  9. package/dist/activities-turn-A7LDKRLD.js.map +1 -0
  10. package/dist/{chunk-OWNLATJL.js → chunk-HXPW4HPN.js} +60 -24
  11. package/dist/chunk-HXPW4HPN.js.map +1 -0
  12. package/dist/{chunk-IJZU752L.js → chunk-YG27NEMX.js} +34 -1
  13. package/dist/chunk-YG27NEMX.js.map +1 -0
  14. package/dist/index.js +2 -2
  15. package/dist/observability-metrics.d.ts +16 -1
  16. package/dist/workflow-bundle.js +18 -12
  17. package/dist/workflows/scheduled-tasks.d.ts +4 -0
  18. package/package.json +21 -21
  19. package/src/activities/agent-turn/claim.ts +13 -1
  20. package/src/activities/agent-turn/credential-restriction.ts +14 -0
  21. package/src/activities/agent-turn/errors.ts +39 -13
  22. package/src/activities/agent-turn/run-credentials.ts +5 -0
  23. package/src/activities/agent-turn/run.ts +1 -0
  24. package/src/activities/agent-turn/tool-environment.ts +4 -0
  25. package/src/activities/automations.ts +23 -1
  26. package/src/activities/goals.ts +33 -8
  27. package/src/activities/sandbox-lease.ts +22 -0
  28. package/src/activities/scheduled-tasks.ts +89 -25
  29. package/src/activities/site-auth-maintenance.ts +1 -0
  30. package/src/activities/types.ts +3 -0
  31. package/src/activities/workspace-capture.ts +13 -2
  32. package/src/observability-metrics.ts +55 -1
  33. package/src/workflows/scheduled-tasks.ts +26 -13
  34. package/dist/activities-control-55ECL47G.js.map +0 -1
  35. package/dist/activities-turn-T7Y3BWYO.js.map +0 -1
  36. package/dist/chunk-IJZU752L.js.map +0 -1
  37. package/dist/chunk-OWNLATJL.js.map +0 -1
@@ -10,6 +10,7 @@ import {
10
10
  SessionAgentAccess,
11
11
  SessionMemoryScope,
12
12
  normalizeAutomaticSessionTitle,
13
+ metadataWithTurnExecutionPolicyV1,
13
14
  scheduledOccurrencePayloadUtf8Bytes,
14
15
  stableJson,
15
16
  TurnExecutionPolicyV1,
@@ -38,6 +39,9 @@ import {
38
39
  agentConfigMayResolve,
39
40
  applySessionAgentConfigWriteThrough,
40
41
  resolveSessionAgentConfigForCreate,
42
+ recordSessionCreated,
43
+ recordUserMessageAccepted,
44
+ type ProductUsageMetricsSink,
41
45
  } from "@opengeni/core";
42
46
  import {
43
47
  appendSessionEvents,
@@ -126,6 +130,30 @@ type ScheduledTemporalActivityIdentity = {
126
130
  activityId: string;
127
131
  };
128
132
 
133
+ export function scheduledTaskRunExecutionPolicy(
134
+ policy: TurnExecutionPolicyV1,
135
+ input: DispatchScheduledTaskRunInput,
136
+ creatorRestriction?: "developer_setup",
137
+ ): TurnExecutionPolicyV1 {
138
+ return creatorRestriction === "developer_setup" ||
139
+ (input.triggerType !== "scheduled" && input.credentialRestriction === "developer_setup")
140
+ ? { ...policy, credentialRestriction: "developer_setup" }
141
+ : policy;
142
+ }
143
+
144
+ /** Only a creator ceiling is standing; a manual caller ceiling is per-run. */
145
+ export function scheduledSessionExecutionPolicyMetadata(
146
+ policy: unknown,
147
+ creatorRestriction?: "developer_setup",
148
+ ): Record<string, unknown> {
149
+ if (policy === undefined || policy === null) return {};
150
+ const acceptedPolicy = TurnExecutionPolicyV1.parse(policy);
151
+ return creatorRestriction === "developer_setup" &&
152
+ acceptedPolicy.credentialRestriction === "developer_setup"
153
+ ? metadataWithTurnExecutionPolicyV1({}, acceptedPolicy)
154
+ : {};
155
+ }
156
+
129
157
  export function scheduledTaskRunProducerKey(
130
158
  input: DispatchScheduledTaskRunInput,
131
159
  suppliedActivityIdentity?: ScheduledTemporalActivityIdentity,
@@ -195,6 +223,15 @@ export function scheduledTaskGeneratedSessionCreateIdempotencyKey(producerKey: s
195
223
  * alone, with no Intl call and so no host ICU or tzdata build to make the two
196
224
  * texts differ.
197
225
  */
226
+ /** A scheduler-generated run session: a service-created root session. */
227
+ function recordScheduledSessionCreated(observability: ProductUsageMetricsSink | undefined): void {
228
+ recordSessionCreated(observability, {
229
+ surface: "scheduled",
230
+ createdByKind: "service",
231
+ parentSessionId: null,
232
+ });
233
+ }
234
+
198
235
  export function scheduledTaskSessionTitle(taskName: string): string {
199
236
  return normalizeAutomaticSessionTitle(taskName) ?? "Scheduled run";
200
237
  }
@@ -334,6 +371,7 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
334
371
  try {
335
372
  return await recoverBoundScheduledTaskDispatch({
336
373
  db,
374
+ observability: baseService.observability,
337
375
  bus,
338
376
  settings,
339
377
  wakeSessionWorkflow,
@@ -994,23 +1032,28 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
994
1032
  }
995
1033
  }
996
1034
  : undefined;
997
- const turnExecutionPolicy: TurnExecutionPolicyV1 = resolveTurnExecutionPolicyV1(settings, {
998
- modelId: acceptedModel,
999
- requestedModelId: generatedTarget && task.agentConfig.model ? task.agentConfig.model : null,
1000
- modelSource: generatedTarget
1001
- ? task.agentConfig.model
1002
- ? "explicit"
1003
- : "deployment"
1004
- : "session",
1005
- reasoningEffort: acceptedReasoningEffort,
1006
- reasoningSource: generatedTarget
1007
- ? task.agentConfig.reasoningEffort
1008
- ? "explicit"
1009
- : "deployment"
1010
- : "session",
1011
- latencyMode: acceptedLatencyMode,
1012
- latencyModeSource: generatedTarget ? "deployment" : "session",
1013
- });
1035
+ const turnExecutionPolicy = scheduledTaskRunExecutionPolicy(
1036
+ resolveTurnExecutionPolicyV1(settings, {
1037
+ modelId: acceptedModel,
1038
+ requestedModelId:
1039
+ generatedTarget && task.agentConfig.model ? task.agentConfig.model : null,
1040
+ modelSource: generatedTarget
1041
+ ? task.agentConfig.model
1042
+ ? "explicit"
1043
+ : "deployment"
1044
+ : "session",
1045
+ reasoningEffort: acceptedReasoningEffort,
1046
+ reasoningSource: generatedTarget
1047
+ ? task.agentConfig.reasoningEffort
1048
+ ? "explicit"
1049
+ : "deployment"
1050
+ : "session",
1051
+ latencyMode: acceptedLatencyMode,
1052
+ latencyModeSource: generatedTarget ? "deployment" : "session",
1053
+ }),
1054
+ input,
1055
+ creatorPolicy?.credentialRestriction,
1056
+ );
1014
1057
  const deferredEvents: Array<{
1015
1058
  sessionId: string;
1016
1059
  events: Awaited<ReturnType<typeof appendSessionEvents>>;
@@ -1269,6 +1312,10 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
1269
1312
  : {}),
1270
1313
  metadata: {
1271
1314
  ...taskMetadata,
1315
+ ...scheduledSessionExecutionPolicyMetadata(
1316
+ turnExecutionPolicy,
1317
+ creatorPolicy?.credentialRestriction,
1318
+ ),
1272
1319
  model,
1273
1320
  reasoningEffort,
1274
1321
  scheduledTaskId: task.id,
@@ -1364,6 +1411,7 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
1364
1411
  }
1365
1412
  session = created.session;
1366
1413
  sessionCreated = created.created;
1414
+ if (sessionCreated) recordScheduledSessionCreated(baseService.observability);
1367
1415
  if (!sessionCreated) {
1368
1416
  await bindScheduledTaskRunSessionInTransaction(dispatchDb, {
1369
1417
  accountId: task.accountId,
@@ -1648,6 +1696,9 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
1648
1696
  run: { ...run, status: "skipped" as const, error },
1649
1697
  };
1650
1698
  }
1699
+ if (scheduledUpdate.added) {
1700
+ recordUserMessageAccepted(baseService.observability, { surface: "scheduled" });
1701
+ }
1651
1702
  if (scheduledUpdate.added && scheduledUpdate.events.length > 0) {
1652
1703
  if (deferPublications) {
1653
1704
  deferredEvents.push({
@@ -1791,6 +1842,9 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
1791
1842
  run: { ...run, status: "skipped" as const, error },
1792
1843
  };
1793
1844
  }
1845
+ if (bundled.added) {
1846
+ recordUserMessageAccepted(baseService.observability, { surface: "scheduled" });
1847
+ }
1794
1848
  if (bundled.added && bundled.events.length > 0) {
1795
1849
  if (deferPublications) {
1796
1850
  deferredEvents.push({
@@ -2202,6 +2256,7 @@ async function seedScheduledGeneratedSessionRoute(input: {
2202
2256
 
2203
2257
  async function recoverBoundScheduledTaskDispatch(input: {
2204
2258
  db: Database;
2259
+ observability?: ProductUsageMetricsSink | undefined;
2205
2260
  bus: ControlActivityServices["bus"];
2206
2261
  settings: ControlActivityServices["settings"];
2207
2262
  wakeSessionWorkflow: WakeSessionWorkflowSignal | null;
@@ -2216,6 +2271,11 @@ async function recoverBoundScheduledTaskDispatch(input: {
2216
2271
  const generatedSession =
2217
2272
  task.runMode === "new_session_per_run" ||
2218
2273
  (task.runMode === "reusable_session" && task.reusableSessionId === null);
2274
+ // The immutable creator policy determines standing session authority, not
2275
+ // a manual caller's per-run ceiling. Read it for creation and recovery checks.
2276
+ const recoveredCreatorPolicy = generatedSession
2277
+ ? await getScheduledTaskCreatorPolicy(input.db, task.workspaceId, task.id)
2278
+ : null;
2219
2279
  const frozenSlack = input.acceptedExecution.resolvedSlackBotConnection;
2220
2280
  if (frozenSlack) {
2221
2281
  const currentSlack = await requireOpenGeniSlackBotConnection(
@@ -2284,14 +2344,6 @@ async function recoverBoundScheduledTaskDispatch(input: {
2284
2344
  }
2285
2345
  const taskMetadata = { ...task.agentConfig.metadata };
2286
2346
  delete taskMetadata[OPENGENI_SLACK_BOT_SESSION_METADATA_KEY];
2287
- // Tools and permissions were frozen into the accepted execution. The
2288
- // creator session policy is immutable on the task row (a tombstoned task
2289
- // still answers), so re-reading it here is deterministic for the same run.
2290
- const recoveredCreatorPolicy = await getScheduledTaskCreatorPolicy(
2291
- input.db,
2292
- task.workspaceId,
2293
- task.id,
2294
- );
2295
2347
  const created = await createSessionWithIdempotencyKeyResult(input.db, {
2296
2348
  accountId: task.accountId,
2297
2349
  workspaceId: task.workspaceId,
@@ -2316,6 +2368,10 @@ async function recoverBoundScheduledTaskDispatch(input: {
2316
2368
  : {}),
2317
2369
  metadata: {
2318
2370
  ...taskMetadata,
2371
+ ...scheduledSessionExecutionPolicyMetadata(
2372
+ input.acceptedExecution.turnExecutionPolicy,
2373
+ recoveredCreatorPolicy?.credentialRestriction,
2374
+ ),
2319
2375
  model: input.acceptedExecution.resolvedModel,
2320
2376
  reasoningEffort: input.acceptedExecution.resolvedReasoningEffort,
2321
2377
  scheduledTaskId: task.id,
@@ -2374,6 +2430,7 @@ async function recoverBoundScheduledTaskDispatch(input: {
2374
2430
  });
2375
2431
  if (created.denied) throw new SessionSpawnDeniedDbError(created.denial);
2376
2432
  session = created.session;
2433
+ if (created.created) recordScheduledSessionCreated(input.observability);
2377
2434
  if (!created.created) {
2378
2435
  await bindScheduledTaskRunSessionInTransaction(input.db, {
2379
2436
  accountId: task.accountId,
@@ -2447,6 +2504,10 @@ async function recoverBoundScheduledTaskDispatch(input: {
2447
2504
  const expectedMetadata = metadataWithAgentConfigCreateIdentity(
2448
2505
  {
2449
2506
  ...expectedTaskMetadata,
2507
+ ...scheduledSessionExecutionPolicyMetadata(
2508
+ input.acceptedExecution.turnExecutionPolicy,
2509
+ recoveredCreatorPolicy?.credentialRestriction,
2510
+ ),
2450
2511
  model: input.acceptedExecution.resolvedModel,
2451
2512
  reasoningEffort: input.acceptedExecution.resolvedReasoningEffort,
2452
2513
  scheduledTaskId: task.id,
@@ -2669,6 +2730,9 @@ async function recoverBoundScheduledTaskDispatch(input: {
2669
2730
  ) {
2670
2731
  return { action: "blocked", reason: "scheduled_run_terminal" };
2671
2732
  }
2733
+ if (scheduledUpdate.added) {
2734
+ recordUserMessageAccepted(input.observability, { surface: "scheduled" });
2735
+ }
2672
2736
  if (scheduledUpdate.added && scheduledUpdate.events.length > 0) {
2673
2737
  await publishDurableSessionEvents(
2674
2738
  input.bus,
@@ -176,6 +176,7 @@ async function dispatchMaintenanceSession(
176
176
  initialMessage: maintenancePrompt(maintenance),
177
177
  // OpenGeni's own periodic check: no human or product surface entered it.
178
178
  surface: "system",
179
+ metrics: service.observability,
179
180
  resources: [],
180
181
  tools: [{ kind: "mcp", id: "opengeni" }],
181
182
  toolPolicy: { mode: "explicit", inheritedFromSessionId: null },
@@ -436,6 +436,7 @@ export type DispatchScheduledTaskRunInput = {
436
436
  triggerType: Extract<ScheduledTaskTriggerType, "scheduled">;
437
437
  agentRunUsageIdempotencyKey?: never;
438
438
  initiator?: never;
439
+ credentialRestriction?: never;
439
440
  }
440
441
  | {
441
442
  triggerType: Extract<
@@ -445,6 +446,8 @@ export type DispatchScheduledTaskRunInput = {
445
446
  agentRunUsageIdempotencyKey: string;
446
447
  /** Exact identity used by the API-side charge for this same trigger. */
447
448
  initiator: TurnInitiator;
449
+ /** Trusted API-side caller ceiling for this occurrence, not the schedule. */
450
+ credentialRestriction?: "developer_setup";
448
451
  }
449
452
  );
450
453
 
@@ -947,13 +947,24 @@ async function runCapture(
947
947
  }
948
948
 
949
949
  // ── 10. observe completion ────────────────────────────────────────────────
950
- const durationMs = Date.now() - startedAt;
950
+ recordWorkspaceCaptureCompleted(observability, Date.now() - startedAt);
951
+ }
952
+
953
+ /** Count one committed logical capture and observe its end-to-end duration. */
954
+ export function recordWorkspaceCaptureCompleted(
955
+ observability: Pick<Observability, "incrementCounter" | "observeHistogram">,
956
+ durationMs: number,
957
+ ): void {
951
958
  observability.incrementCounter({
952
959
  name: "opengeni_workspace_capture_total",
953
960
  labels: { result: "ok" },
954
961
  });
962
+ // Distinct from the physical `opengeni_workspace_capture_duration_seconds`
963
+ // {backend,outcome} histogram the runtime hook records in this same
964
+ // registry: one name cannot carry two label sets.
955
965
  observability.observeHistogram({
956
- name: "opengeni_workspace_capture_duration_seconds",
966
+ name: "opengeni_workspace_capture_revision_duration_seconds",
967
+ help: "Turn-end logical workspace revision capture duration for committed revisions.",
957
968
  value: durationMs / 1000,
958
969
  });
959
970
  }
@@ -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
@@ -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