@opengeni/worker-bundle 4.2.1-canary.39593846423001 → 4.3.1

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/code-search.d.ts +19 -14
  2. package/dist/activities/agent-turn/decision-model.d.ts +11 -0
  3. package/dist/activities/agent-turn/provider-dispatch-barrier.d.ts +32 -4
  4. package/dist/activities/agent-turn/tool-discovery.d.ts +21 -0
  5. package/dist/activities/model-call-fact-reconciler.d.ts +2 -1
  6. package/dist/{activities-control-KP6WCM3S.js → activities-control-7ZHF4LES.js} +108 -44
  7. package/dist/activities-control-7ZHF4LES.js.map +1 -0
  8. package/dist/{activities-turn-7ESD3YJB.js → activities-turn-7VX4IWPO.js} +193 -67
  9. package/dist/activities-turn-7VX4IWPO.js.map +1 -0
  10. package/dist/artifact-materializer-entry.js +0 -0
  11. package/dist/artifact-outbox-entry.js +0 -0
  12. package/dist/{chunk-3LJG6QJL.js → chunk-5KOLEDGM.js} +27 -3
  13. package/dist/chunk-5KOLEDGM.js.map +1 -0
  14. package/dist/{chunk-6AZ2MKJC.js → chunk-AICMJ5A2.js} +10 -10
  15. package/dist/{chunk-6AZ2MKJC.js.map → chunk-AICMJ5A2.js.map} +1 -1
  16. package/dist/{chunk-L5XPJLGD.js → chunk-WGFH4MZV.js} +2 -2
  17. package/dist/index.js +3 -3
  18. package/dist/observability-metrics.d.ts +4 -4
  19. package/dist/{sandbox-lease-Y5UB6B2S.js → sandbox-lease-G4H72KAK.js} +3 -3
  20. package/dist/workflow-bundle.js +1 -1
  21. package/package.json +27 -26
  22. package/src/activities/agent-turn/agent-build.ts +23 -3
  23. package/src/activities/agent-turn/code-search.ts +86 -58
  24. package/src/activities/agent-turn/decision-model.ts +43 -0
  25. package/src/activities/agent-turn/file-resources.ts +8 -11
  26. package/src/activities/agent-turn/provider-dispatch-barrier.ts +54 -3
  27. package/src/activities/agent-turn/run.ts +13 -1
  28. package/src/activities/agent-turn/tool-discovery.ts +78 -0
  29. package/src/activities/agent-turn/tool-environment.ts +20 -15
  30. package/src/activities/model-call-fact-reconciler.ts +109 -38
  31. package/src/activities/session-title-route.ts +3 -1
  32. package/src/observability-metrics.ts +14 -14
  33. package/dist/activities-control-KP6WCM3S.js.map +0 -1
  34. package/dist/activities-turn-7ESD3YJB.js.map +0 -1
  35. package/dist/chunk-3LJG6QJL.js.map +0 -1
  36. /package/dist/{chunk-L5XPJLGD.js.map → chunk-WGFH4MZV.js.map} +0 -0
  37. /package/dist/{sandbox-lease-Y5UB6B2S.js.map → sandbox-lease-G4H72KAK.js.map} +0 -0
@@ -3,8 +3,9 @@ import { type Settings } from "@opengeni/config";
3
3
  import { JevCircuitBreaker, type CodeSearchWorkspace } from "@opengeni/jev";
4
4
  import type { Observability } from "@opengeni/observability";
5
5
  import { type SandboxChannelAService } from "@opengeni/runtime/sandbox";
6
+ import { type WorkerDecisionSettings } from "./decision-model.js";
6
7
  /**
7
- * One breaker per worker process. Repeated Jev outages (including an exhausted
8
+ * One breaker per worker process. Repeated decision-provider outages (including an exhausted
8
9
  * account) make `code_search` calls on this worker fail at once for a cooldown
9
10
  * instead of each running a full retry cycle. The breaker never changes which
10
11
  * tools a turn is offered: the tool list and instructions are the start of the
@@ -15,40 +16,44 @@ export declare const codeSearchCircuitBreaker: JevCircuitBreaker;
15
16
  type CodeSearchChannel = Pick<SandboxChannelAService, "codeSearchRipgrep" | "codeSearchPathKinds" | "fsRead">;
16
17
  /** Adapt the turn's sandbox or Connected Machine to the code search engine. */
17
18
  export declare function codeSearchWorkspaceFromChannel(channel: CodeSearchChannel): CodeSearchWorkspace;
18
- /** Jev work done by one completed `code_search` call, for per-workspace usage records. */
19
+ /** Typed inference used by one code-search call, owned by the deployment. */
19
20
  export type CodeSearchUsage = {
20
21
  operationId: string;
21
- jevRequests: number;
22
- jevInputTokens: number;
23
- jevCostUsd: number;
22
+ decisionRequests: number;
23
+ decisionInputTokens: number | null;
24
+ decisionCostUsd: number | null;
25
+ model: string | null;
26
+ provider: string | null;
27
+ costSources: Array<"provider-reported" | "estimate" | "unknown">;
24
28
  };
25
29
  /**
26
- * The model-facing `code_search` tool. Jev scores candidates inside the worker;
27
- * the sandbox only runs read-only ripgrep and file reads, so the Jev key never
28
- * leaves this process. A Jev failure is reported to the model instead of
30
+ * The model-facing `code_search` tool. A typed decision model scores candidates
31
+ * inside the worker; the sandbox only runs read-only ripgrep and file reads, so
32
+ * provider credentials never leave this process. A provider failure is reported instead of
29
33
  * degrading to keyword-only ranking, which lowered answer quality in testing.
30
34
  */
31
35
  export declare function createCodeSearchAttemptToolDefinition(input: {
32
- settings: Pick<Settings, "jevApiKey" | "jevBaseUrl" | "jevModel" | "jevRequestTimeoutMs">;
33
- apiKey: string;
36
+ settings: WorkerDecisionSettings;
37
+ /** Compatibility for existing direct Jev constructor callers. */
38
+ apiKey?: string;
34
39
  workspace: () => Promise<CodeSearchWorkspace>;
35
40
  observability: Observability;
36
- /** Records Jev usage against the workspace. Failures are logged, never surfaced. */
41
+ /** Records decision usage against the workspace. Persistence failures are logged. */
37
42
  recordUsage?: (usage: CodeSearchUsage) => Promise<void>;
38
43
  breaker?: JevCircuitBreaker;
39
44
  fetch?: typeof fetch;
40
45
  }): AttemptToolDefinition;
41
46
  /**
42
47
  * The `code_search` definition for one turn, or none. It is offered only when
43
- * the session's frozen decision and the deployment enable it, a usable Jev key
48
+ * the session's frozen decision and the deployment enable it, a usable typed model
44
49
  * exists, and the turn has compute that can run its POSIX shell commands (not
45
50
  * a Windows Connected Machine). Every input is durable, so the tool list stays
46
- * the same from turn to turn and on every worker. Transient Jev health never
51
+ * the same from turn to turn and on every worker. Transient provider health never
47
52
  * hides the tool; the breaker only refuses calls.
48
53
  */
49
54
  export declare function codeSearchToolDefinitions(input: {
50
55
  enabled: boolean;
51
- settings: Pick<Settings, "jevApiKey" | "jevBaseUrl" | "jevModel" | "jevRequestTimeoutMs">;
56
+ settings: WorkerDecisionSettings;
52
57
  backend: Settings["sandboxBackend"];
53
58
  /** The turn's Connected Machine workspace root, when a machine is primary. */
54
59
  machineWorkspaceRoot?: string | null;
@@ -0,0 +1,11 @@
1
+ import { type DecisionModelSettings, type Settings } from "@opengeni/config";
2
+ import { type DecisionModel } from "@opengeni/decisions";
3
+ export type WorkerDecisionSettings = DecisionModelSettings & Partial<Pick<Settings, "jevApiKey" | "jevBaseUrl" | "jevModel" | "jevRequestTimeoutMs">>;
4
+ /** One deployment-owned category, consumed by code search and tool discovery. */
5
+ export declare function configuredDecisionModel(settings: WorkerDecisionSettings, options?: {
6
+ allowLegacyCodeSearch?: boolean;
7
+ fetch?: typeof fetch;
8
+ }): {
9
+ id: string;
10
+ model: DecisionModel;
11
+ } | undefined;
@@ -4,16 +4,44 @@ export declare class SandboxCapabilitiesChangedError extends Error {
4
4
  readonly code = "native_capabilities_changed_this_attempt";
5
5
  constructor();
6
6
  }
7
+ type ActiveRoutePointer = {
8
+ activeSandboxId: string | null;
9
+ activeEpoch: number;
10
+ };
11
+ /**
12
+ * Ends an attempt between tool batches when its route moved somewhere it can
13
+ * no longer serve. A swap to a machine with another workspace root (or back
14
+ * to a home this attempt cannot reach) used to surface only on the first
15
+ * sandbox call of the following batch: the model's whole next response was
16
+ * discarded with the attempt, including sibling calls that had already run.
17
+ * Checking before the next model request ends the attempt at a clean point
18
+ * instead: the batch that moved the route is complete and durable, and the
19
+ * next attempt starts on the new route.
20
+ */
21
+ export declare class ActiveRouteDispatchBoundary {
22
+ private readonly deps;
23
+ private observed;
24
+ constructor(deps: {
25
+ /** The attempt's routed sandbox session, once one is established. */
26
+ session: () => {
27
+ assertActiveRouteContinuable?: () => Promise<void>;
28
+ } | null | undefined;
29
+ /** True for a typed transition that must continue in a fresh attempt. */
30
+ isRouteTransition: (error: unknown) => boolean;
31
+ });
32
+ check(active: ActiveRoutePointer | null): Promise<void>;
33
+ }
7
34
  /**
8
35
  * Persist the SDK's complete prior model/tool history before another provider
9
36
  * request can start. The first request has no prior model/tool rows to append.
10
37
  * Follow-up requests run only after the preceding complete call/result batch is
11
- * replay-safe.
38
+ * replay-safe. A route that moved during that batch to somewhere this attempt
39
+ * cannot serve ends the attempt here, after the batch is durable.
12
40
  */
13
41
  export declare function checkpointHistoryBeforeProviderDispatch(historySink: Pick<TurnHistorySink, "reconcileConversationTruth">, route?: {
14
42
  effectiveSandboxBackend: Settings["sandboxBackend"];
15
43
  routingEnabled: boolean;
16
- readActiveSandbox: () => Promise<{
17
- activeSandboxId: string | null;
18
- } | null>;
44
+ readActiveSandbox: () => Promise<ActiveRoutePointer | null>;
45
+ activeRoute?: ActiveRouteDispatchBoundary;
19
46
  }): Promise<void>;
47
+ export {};
@@ -0,0 +1,21 @@
1
+ import type { Settings } from "@opengeni/config";
2
+ import { type Database } from "@opengeni/db";
3
+ import type { DecisionUsage, DecisionModel } from "@opengeni/decisions";
4
+ /** Explicitly configured typed inference, never a borrowed customer key/subscription. */
5
+ export declare function configuredToolDecisionModel(input: {
6
+ settings: Settings;
7
+ recordUsage: (modelId: string, usage: DecisionUsage, requestId: string) => Promise<void>;
8
+ onUnavailable: () => void;
9
+ fetch?: typeof fetch;
10
+ }): DecisionModel | undefined;
11
+ export declare function recordToolDecisionUsage(input: {
12
+ db: Database;
13
+ accountId: string;
14
+ workspaceId: string;
15
+ sessionId: string;
16
+ turnId: string;
17
+ turnAttemptId: string;
18
+ modelId: string;
19
+ usage: DecisionUsage;
20
+ requestId: string;
21
+ }): Promise<void>;
@@ -11,7 +11,7 @@ export type ReconcileRecentModelCallFactsResult = {
11
11
  missing: number;
12
12
  repaired: number;
13
13
  unrepaired: number;
14
- /** Workspaces with more missing facts than one bounded pass examines. */
14
+ /** Workspaces with a durable scan still unfinished at the end of this run. */
15
15
  truncatedWorkspaces: number;
16
16
  failedWorkspaces: number;
17
17
  budgetExhausted: boolean;
@@ -22,6 +22,7 @@ export type ModelCallFactReconcilerOptions = {
22
22
  startAfterWorkspaceId?: () => string;
23
23
  reconcile?: typeof reconcileModelCallFacts;
24
24
  listWorkspaces?: typeof listWorkspaceIdsAfter;
25
+ signal?: AbortSignal;
25
26
  };
26
27
  /**
27
28
  * Rebuild missing Insights call facts for recently charged model calls. Each
@@ -23,7 +23,7 @@ import {
23
23
  withFirstPartyTools,
24
24
  workflowIdForSession,
25
25
  writeSessionUsageReceipts
26
- } from "./chunk-3LJG6QJL.js";
26
+ } from "./chunk-5KOLEDGM.js";
27
27
  import "./chunk-K225DMA7.js";
28
28
  import {
29
29
  agentRunAdmissionDenial,
@@ -32,13 +32,13 @@ import {
32
32
  import {
33
33
  createBrowserDeadlineCheckpointActivities,
34
34
  createSandboxLeaseActivities
35
- } from "./chunk-L5XPJLGD.js";
35
+ } from "./chunk-WGFH4MZV.js";
36
36
  import "./chunk-IGTC55IK.js";
37
37
  import "./chunk-24GP3BX5.js";
38
38
  import {
39
39
  recordTurnsQueuedGauge,
40
40
  recordWorkerDeathRecoveryMetrics
41
- } from "./chunk-6AZ2MKJC.js";
41
+ } from "./chunk-AICMJ5A2.js";
42
42
  import {
43
43
  deliverFailedChildTurnToParent,
44
44
  notifyParentOfChildIdle,
@@ -973,6 +973,7 @@ function createFileUploadReaperActivities(services, options = {}) {
973
973
 
974
974
  // src/activities/model-call-fact-reconciler.ts
975
975
  import { randomUUID as randomUUID2 } from "crypto";
976
+ import { Context } from "@temporalio/activity";
976
977
  import { listWorkspaceIdsAfter, reconcileModelCallFacts } from "@opengeni/db";
977
978
  var MODEL_CALL_FACT_RECONCILE_LOOKBACK_MS = 2 * 60 * 60 * 1e3;
978
979
  var MODEL_CALL_FACT_RECONCILE_SETTLE_MS = 10 * 60 * 1e3;
@@ -988,64 +989,127 @@ function createModelCallFactReconcilerActivities(services, options = {}) {
988
989
  async function reconcileRecentModelCallFacts() {
989
990
  const { db, observability } = await services();
990
991
  const startedAt = now();
991
- const until = new Date(startedAt - MODEL_CALL_FACT_RECONCILE_SETTLE_MS);
992
- const since = new Date(until.getTime() - MODEL_CALL_FACT_RECONCILE_LOOKBACK_MS);
993
- const result = {
994
- workspaces: 0,
995
- missing: 0,
996
- repaired: 0,
997
- unrepaired: 0,
998
- truncatedWorkspaces: 0,
999
- failedWorkspaces: 0,
1000
- budgetExhausted: false
1001
- };
1002
- const start = startAfterWorkspaceId();
1003
- let cursor = start;
1004
- let wrapped = false;
1005
- while (true) {
1006
- const page = await listWorkspaces(db, { afterId: cursor, limit: WORKSPACE_PAGE_SIZE });
1007
- const ids = wrapped ? page.filter((id) => id <= start) : page;
1008
- for (const workspaceId of ids) {
1009
- if (now() - startedAt >= runBudgetMs) {
992
+ let activity;
993
+ try {
994
+ activity = Context.current();
995
+ } catch {
996
+ }
997
+ const callerSignal = options.signal ?? activity?.cancellationSignal;
998
+ callerSignal?.throwIfAborted();
999
+ const deadlineAtMs = Date.now() + runBudgetMs;
1000
+ const deadlineError = new Error("Model fact reconciliation run budget exhausted");
1001
+ const deadline = new AbortController();
1002
+ const signal = callerSignal ? AbortSignal.any([callerSignal, deadline.signal]) : deadline.signal;
1003
+ const timer = setTimeout(() => deadline.abort(deadlineError), Math.max(0, runBudgetMs));
1004
+ timer.unref();
1005
+ try {
1006
+ const until = new Date(startedAt - MODEL_CALL_FACT_RECONCILE_SETTLE_MS);
1007
+ const since = new Date(until.getTime() - MODEL_CALL_FACT_RECONCILE_LOOKBACK_MS);
1008
+ const result = {
1009
+ workspaces: 0,
1010
+ missing: 0,
1011
+ repaired: 0,
1012
+ unrepaired: 0,
1013
+ truncatedWorkspaces: 0,
1014
+ failedWorkspaces: 0,
1015
+ budgetExhausted: false
1016
+ };
1017
+ const start = startAfterWorkspaceId();
1018
+ let cursor = start;
1019
+ let wrapped = false;
1020
+ const pending = /* @__PURE__ */ new Set();
1021
+ const exhausted = () => {
1022
+ callerSignal?.throwIfAborted();
1023
+ if (deadline.signal.aborted || now() - startedAt >= runBudgetMs) {
1010
1024
  result.budgetExhausted = true;
1011
- break;
1025
+ return true;
1012
1026
  }
1013
- result.workspaces += 1;
1027
+ return false;
1028
+ };
1029
+ const advance = async (workspaceId) => {
1014
1030
  try {
1031
+ signal.throwIfAborted();
1032
+ activity?.heartbeat({ workspaceId });
1015
1033
  const outcome = await reconcile(db, {
1016
1034
  workspaceId,
1017
1035
  since,
1018
1036
  until,
1019
- limit: MODEL_CALL_FACT_RECONCILE_WORKSPACE_LIMIT
1037
+ limit: MODEL_CALL_FACT_RECONCILE_WORKSPACE_LIMIT,
1038
+ signal,
1039
+ deadlineAtMs
1020
1040
  });
1021
1041
  result.missing += outcome.missing;
1022
1042
  result.repaired += outcome.repaired;
1023
1043
  result.unrepaired += outcome.unrepaired;
1024
- if (outcome.truncated) result.truncatedWorkspaces += 1;
1044
+ if (outcome.truncated) pending.add(workspaceId);
1045
+ else pending.delete(workspaceId);
1025
1046
  } catch (error) {
1026
- result.failedWorkspaces += 1;
1047
+ if (signal.aborted && signal.reason === deadlineError) {
1048
+ pending.add(workspaceId);
1049
+ result.budgetExhausted = true;
1050
+ return;
1051
+ }
1052
+ signal?.throwIfAborted();
1053
+ pending.delete(workspaceId);
1054
+ result.failedWorkspaces++;
1027
1055
  observability.warn("model call fact reconciliation failed for a workspace", {
1028
1056
  workspaceId,
1029
1057
  errorName: error instanceof Error ? error.name : "unknown"
1030
1058
  });
1031
1059
  }
1060
+ };
1061
+ while (true) {
1062
+ if (exhausted()) break;
1063
+ let page;
1064
+ try {
1065
+ page = await listWorkspaces(db, {
1066
+ afterId: cursor,
1067
+ limit: WORKSPACE_PAGE_SIZE,
1068
+ signal,
1069
+ deadlineAtMs
1070
+ });
1071
+ } catch (error) {
1072
+ callerSignal?.throwIfAborted();
1073
+ if (deadline.signal.aborted) {
1074
+ result.budgetExhausted = true;
1075
+ break;
1076
+ }
1077
+ throw error;
1078
+ }
1079
+ const ids = wrapped ? page.filter((id) => id <= start) : page;
1080
+ for (const workspaceId of ids) {
1081
+ if (exhausted()) break;
1082
+ result.workspaces += 1;
1083
+ await advance(workspaceId);
1084
+ if (result.budgetExhausted) break;
1085
+ }
1086
+ if (result.budgetExhausted) break;
1087
+ if (wrapped && ids.length < page.length) break;
1088
+ if (page.length < WORKSPACE_PAGE_SIZE) {
1089
+ if (wrapped) break;
1090
+ wrapped = true;
1091
+ cursor = null;
1092
+ continue;
1093
+ }
1094
+ cursor = page[page.length - 1];
1032
1095
  }
1033
- if (result.budgetExhausted) break;
1034
- if (wrapped && ids.length < page.length) break;
1035
- if (page.length < WORKSPACE_PAGE_SIZE) {
1036
- if (wrapped) break;
1037
- wrapped = true;
1038
- cursor = null;
1039
- continue;
1096
+ while (pending.size && !result.budgetExhausted) {
1097
+ for (const workspaceId of [...pending]) {
1098
+ if (exhausted()) break;
1099
+ await advance(workspaceId);
1100
+ if (result.budgetExhausted) break;
1101
+ }
1040
1102
  }
1041
- cursor = page[page.length - 1];
1042
- }
1043
- if (result.failedWorkspaces > 0 || result.truncatedWorkspaces > 0 || result.budgetExhausted) {
1044
- observability.warn("model call fact reconciliation incomplete", { ...result });
1045
- } else if (result.missing > 0) {
1046
- observability.info("model call fact reconciliation", { ...result });
1103
+ result.truncatedWorkspaces = pending.size;
1104
+ if (result.failedWorkspaces > 0 || result.truncatedWorkspaces > 0 || result.budgetExhausted) {
1105
+ observability.warn("model call fact reconciliation incomplete", { ...result });
1106
+ } else if (result.missing > 0) {
1107
+ observability.info("model call fact reconciliation", { ...result });
1108
+ }
1109
+ return result;
1110
+ } finally {
1111
+ clearTimeout(timer);
1047
1112
  }
1048
- return result;
1049
1113
  }
1050
1114
  return { reconcileRecentModelCallFacts };
1051
1115
  }
@@ -2654,7 +2718,7 @@ import {
2654
2718
  resolveTurnExecutionPolicyV1 as resolveTurnExecutionPolicyV12,
2655
2719
  TurnExecutionPolicyModelUnavailableError
2656
2720
  } from "@opengeni/config";
2657
- import { Context } from "@temporalio/activity";
2721
+ import { Context as Context2 } from "@temporalio/activity";
2658
2722
  import { createHash as createHash2 } from "crypto";
2659
2723
 
2660
2724
  // src/scheduled-alert-occurrence.ts
@@ -2784,7 +2848,7 @@ function scheduledTaskRunProducerKey(input, suppliedActivityIdentity) {
2784
2848
  let info = suppliedActivityIdentity;
2785
2849
  if (!info) {
2786
2850
  try {
2787
- info = Context.current().info;
2851
+ info = Context2.current().info;
2788
2852
  } catch {
2789
2853
  throw new Error(
2790
2854
  "scheduled task delivery without a producer key requires a Temporal activity execution"
@@ -6350,4 +6414,4 @@ export {
6350
6414
  createControlActivities,
6351
6415
  createControlActivitiesFromServices
6352
6416
  };
6353
- //# sourceMappingURL=activities-control-KP6WCM3S.js.map
6417
+ //# sourceMappingURL=activities-control-7ZHF4LES.js.map