@opengeni/runtime 0.7.0 → 0.7.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/runtime",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -39,8 +39,8 @@
39
39
  "@openai/agents": "0.13.3",
40
40
  "@openai/agents-extensions": "0.13.3",
41
41
  "@opengeni/agent-proto": "^0.3.0",
42
- "@opengeni/codex": "^0.2.2",
43
- "@opengeni/config": "^0.5.0",
42
+ "@opengeni/codex": "^0.2.3",
43
+ "@opengeni/config": "^0.5.1",
44
44
  "@opengeni/contracts": "^0.10.0",
45
45
  "modal": "0.7.6",
46
46
  "openai": "6.47.0"
package/src/index.ts CHANGED
@@ -249,7 +249,12 @@ export type ResolveConnectionCredentialInput = {
249
249
  };
250
250
 
251
251
  export type ResolveConnectionCredentialResult =
252
- | { status: "ok"; headers: Record<string, string>; connectionId: string; expiresAt?: Date | null }
252
+ | {
253
+ status: "ok";
254
+ headers: Record<string, string>;
255
+ connectionId: string;
256
+ expiresAt?: Date | null;
257
+ }
253
258
  | {
254
259
  status: "auth_needed";
255
260
  reason: ToolAuthNeededPayload["reason"];
@@ -1070,7 +1075,6 @@ export type WorkspaceEnvironmentContext = {
1070
1075
 
1071
1076
  export type PersistentSessionSettings = {
1072
1077
  titleIsSet: boolean;
1073
- childNotificationsMode?: "digest" | "passive";
1074
1078
  };
1075
1079
 
1076
1080
  /**
@@ -1181,13 +1185,7 @@ export function appendPersistentSessionSettings(
1181
1185
  if (!settings) {
1182
1186
  return composed;
1183
1187
  }
1184
- const childState = settings.childNotificationsMode
1185
- ? `; spawned-worker completion notifications are ${settings.childNotificationsMode}`
1186
- : "";
1187
- const persistentTools = settings.childNotificationsMode
1188
- ? "opengeni__set_session_title or opengeni__set_child_notifications_mode"
1189
- : "opengeni__set_session_title";
1190
- return `${composed} Persistent session settings already in effect: the display title is ${settings.titleIsSet ? "set" : "not set"}${childState}. These settings persist across turns, continuations, and interruptions. Do not call ${persistentTools} merely to reassert an unchanged value; call them only when the current value actually needs to change.`;
1188
+ return `${composed} Persistent session settings already in effect: the display title is ${settings.titleIsSet ? "set" : "not set"}. This setting persists across turns, continuations, and interruptions. Do not call opengeni__set_session_title merely to reassert an unchanged value; call it only when the current value actually needs to change.`;
1191
1189
  }
1192
1190
 
1193
1191
  /**
@@ -1511,7 +1509,10 @@ function mcpToolRequiresApproval(policy: boolean | string[], unprefixedName: str
1511
1509
  }
1512
1510
 
1513
1511
  /** A per-server approval policy keyed by the server's `<id>__` tool prefix. */
1514
- type McpApprovalPolicy = { prefix: string; requireApproval: boolean | string[] };
1512
+ type McpApprovalPolicy = {
1513
+ prefix: string;
1514
+ requireApproval: boolean | string[];
1515
+ };
1515
1516
 
1516
1517
  /** The subset of the agent surface the approval wrap needs — including `clone`. */
1517
1518
  type ApprovalCapableAgent = {
@@ -1829,6 +1830,9 @@ export type PrepareToolsOptions = {
1829
1830
  // The calling turn's id, signed into the token so tools can classify the
1830
1831
  // caller from its own identity instead of the session's live active pointer.
1831
1832
  turnId?: string;
1833
+ // The exact executing attempt that owns the MCP call.
1834
+ attemptId?: string;
1835
+ executionGeneration?: number;
1832
1836
  subjectId?: string;
1833
1837
  subjectLabel?: string;
1834
1838
  // Overrides the fixed first-party MCP permission set for this session's
@@ -2134,7 +2138,9 @@ async function authNeededFetchResponse(
2134
2138
  if (request.method === "tools/call") {
2135
2139
  return mcpToolAuthNeededResponse(request.id);
2136
2140
  }
2137
- return new Response("Authentication required for MCP server connection", { status: 401 });
2141
+ return new Response("Authentication required for MCP server connection", {
2142
+ status: 401,
2143
+ });
2138
2144
  }
2139
2145
 
2140
2146
  async function publishAuthNeeded(
@@ -2295,7 +2301,10 @@ function mcpToolUnavailableMessage(reason: string): string {
2295
2301
  // transport error carries the raw response BODY in its `.message` (a broker
2296
2302
  // 401/403 body can echo request detail), so `.message` is never included; the
2297
2303
  // numeric `.code`/`.status` (e.g. 401) is safe and useful.
2298
- function safeMcpErrorFields(error: unknown): { errorClass: string; status?: number } {
2304
+ function safeMcpErrorFields(error: unknown): {
2305
+ errorClass: string;
2306
+ status?: number;
2307
+ } {
2299
2308
  const errorClass = error instanceof Error ? error.constructor.name : typeof error;
2300
2309
  const raw = (error as { code?: unknown; status?: unknown } | null)?.code;
2301
2310
  const status = typeof raw === "number" ? raw : undefined;
@@ -2383,6 +2392,8 @@ async function signFirstPartyDelegatedBearer(
2383
2392
  permissions: options.firstPartyPermissions ?? firstPartyMcpPermissions,
2384
2393
  ...(options.sessionId ? { sessionId: options.sessionId } : {}),
2385
2394
  ...(options.turnId ? { turnId: options.turnId } : {}),
2395
+ ...(options.attemptId ? { attemptId: options.attemptId } : {}),
2396
+ ...(options.executionGeneration ? { executionGeneration: options.executionGeneration } : {}),
2386
2397
  exp: Math.floor(Date.now() / 1000) + 60 * 60,
2387
2398
  });
2388
2399
  }
@@ -2635,7 +2646,10 @@ class PrefixedMcpServer implements MCPServer {
2635
2646
  }
2636
2647
  return tools
2637
2648
  .filter((tool) => this.isAllowed(tool.name))
2638
- .map((tool) => ({ ...tool, name: prefixedMcpToolName(this.name, tool.name) }));
2649
+ .map((tool) => ({
2650
+ ...tool,
2651
+ name: prefixedMcpToolName(this.name, tool.name),
2652
+ }));
2639
2653
  }
2640
2654
 
2641
2655
  async callTool(
@@ -2657,7 +2671,10 @@ class PrefixedMcpServer implements MCPServer {
2657
2671
  // This applies to ANY server — an auth-needed is recoverable once the user
2658
2672
  // re-links, so even a required tool degrades gracefully here.
2659
2673
  if (isAuthNeededMcpError(error)) {
2660
- return { isError: true, content: [{ type: "text", text: MCP_AUTH_NEEDED_MESSAGE }] };
2674
+ return {
2675
+ isError: true,
2676
+ content: [{ type: "text", text: MCP_AUTH_NEEDED_MESSAGE }],
2677
+ };
2661
2678
  }
2662
2679
  // Best-effort INVOCATION isolation (sibling to the listTools guard). When
2663
2680
  // the model calls a best-effort server's tool and the call throws for ANY
@@ -2679,7 +2696,12 @@ class PrefixedMcpServer implements MCPServer {
2679
2696
  );
2680
2697
  return {
2681
2698
  isError: true,
2682
- content: [{ type: "text", text: mcpToolUnavailableMessage(mcpErrorReason(fields)) }],
2699
+ content: [
2700
+ {
2701
+ type: "text",
2702
+ text: mcpToolUnavailableMessage(mcpErrorReason(fields)),
2703
+ },
2704
+ ],
2683
2705
  };
2684
2706
  }
2685
2707
  throw error;
@@ -2815,6 +2837,8 @@ export async function prepareRunInput(
2815
2837
  }
2816
2838
 
2817
2839
  export type RunAgentStreamOptions = {
2840
+ /** Abort the provider/tool loop when the owning activity is cancelled. */
2841
+ signal?: AbortSignal;
2818
2842
  sandboxClient?: unknown;
2819
2843
  sandboxEnvironment?: Record<string, string>;
2820
2844
  onRuntimeEvent?: (event: NormalizedRuntimeEvent) => Promise<void> | void;
@@ -3129,10 +3153,14 @@ export async function runAgentStream(
3129
3153
  overrides.contextCompactionSignalTokens || overrides.contextCompactionRequested,
3130
3154
  ),
3131
3155
  ...(overrides.contextCompactionSignalTokens
3132
- ? { contextCompactionSignalTokens: overrides.contextCompactionSignalTokens }
3156
+ ? {
3157
+ contextCompactionSignalTokens: overrides.contextCompactionSignalTokens,
3158
+ }
3133
3159
  : {}),
3134
3160
  ...(overrides.contextCompactionRequested
3135
- ? { contextCompactionRequested: overrides.contextCompactionRequested }
3161
+ ? {
3162
+ contextCompactionRequested: overrides.contextCompactionRequested,
3163
+ }
3136
3164
  : {}),
3137
3165
  }),
3138
3166
  genesisTitleInputFilter,
@@ -3143,6 +3171,7 @@ export async function runAgentStream(
3143
3171
  stream: true,
3144
3172
  maxTurns: settings.agentMaxModelCallsPerTurn,
3145
3173
  callModelInputFilter: ownedFilter,
3174
+ ...(overrides.signal ? { signal: overrides.signal } : {}),
3146
3175
  };
3147
3176
  ownedRunOptions.sandbox = {
3148
3177
  client: decoratedClient,
@@ -3215,7 +3244,9 @@ export async function runAgentStream(
3215
3244
  overrides.contextCompactionSignalTokens || overrides.contextCompactionRequested,
3216
3245
  ),
3217
3246
  ...(overrides.contextCompactionSignalTokens
3218
- ? { contextCompactionSignalTokens: overrides.contextCompactionSignalTokens }
3247
+ ? {
3248
+ contextCompactionSignalTokens: overrides.contextCompactionSignalTokens,
3249
+ }
3219
3250
  : {}),
3220
3251
  ...(overrides.contextCompactionRequested
3221
3252
  ? { contextCompactionRequested: overrides.contextCompactionRequested }
@@ -3233,6 +3264,7 @@ export async function runAgentStream(
3233
3264
  // raise the proactive compaction signal. This runs for turn-start replay AND
3234
3265
  // every mid-turn follow-up.
3235
3266
  callModelInputFilter,
3267
+ ...(overrides.signal ? { signal: overrides.signal } : {}),
3236
3268
  };
3237
3269
  if (client) {
3238
3270
  runOptions.sandbox = {
@@ -3282,7 +3314,9 @@ function appendSandboxFileDownloadFailureNote(
3282
3314
  * default runner. setDefaultModelProvider remains only as a boot-time fallback.
3283
3315
  */
3284
3316
  function runScopedRunner(settings: Settings): Runner {
3285
- return new Runner({ modelProvider: new MultiProviderModelProvider(settings) });
3317
+ return new Runner({
3318
+ modelProvider: new MultiProviderModelProvider(settings),
3319
+ });
3286
3320
  }
3287
3321
 
3288
3322
  export { MaxTurnsExceededError } from "@openai/agents";
@@ -3338,7 +3372,9 @@ export function withManifestRefreshOnResume(
3338
3372
  ? { supportsDefaultOptions: client.supportsDefaultOptions }
3339
3373
  : {}),
3340
3374
  ...(client.create
3341
- ? { create: async (...args: any[]) => await (client.create as any)(...args) }
3375
+ ? {
3376
+ create: async (...args: any[]) => await (client.create as any)(...args),
3377
+ }
3342
3378
  : {}),
3343
3379
  resume: async (state: SandboxSessionState) => {
3344
3380
  const session = await client.resume!(state);
@@ -3346,7 +3382,9 @@ export function withManifestRefreshOnResume(
3346
3382
  return session;
3347
3383
  },
3348
3384
  ...(client.delete
3349
- ? { delete: async (state: SandboxSessionState) => await client.delete!(state) }
3385
+ ? {
3386
+ delete: async (state: SandboxSessionState) => await client.delete!(state),
3387
+ }
3350
3388
  : {}),
3351
3389
  ...(client.serializeSessionState
3352
3390
  ? {
@@ -3395,7 +3433,11 @@ export async function applyMissingManifestEntries(
3395
3433
  state?: {
3396
3434
  manifest?:
3397
3435
  | Manifest
3398
- | { root?: string; entries?: Record<string, any>; environment?: Record<string, any> };
3436
+ | {
3437
+ root?: string;
3438
+ entries?: Record<string, any>;
3439
+ environment?: Record<string, any>;
3440
+ };
3399
3441
  };
3400
3442
  }
3401
3443
  ).state?.manifest;
@@ -3505,7 +3547,11 @@ export function manifestEnvironmentDrift(
3505
3547
  if (added.length === 0 && removed.length === 0 && changed.length === 0) {
3506
3548
  return null;
3507
3549
  }
3508
- return { added: added.sort(), removed: removed.sort(), changed: changed.sort() };
3550
+ return {
3551
+ added: added.sort(),
3552
+ removed: removed.sort(),
3553
+ changed: changed.sort(),
3554
+ };
3509
3555
  }
3510
3556
 
3511
3557
  async function reportManifestEnvironmentDrift(
@@ -3520,7 +3566,10 @@ async function reportManifestEnvironmentDrift(
3520
3566
  // Reporting must never break a resume: the drift itself is benign under the
3521
3567
  // env pin (the box keeps running on its baked env); only the SIGNAL matters.
3522
3568
  try {
3523
- await context.onRuntimeEvent?.({ type: "sandbox.env.drift", payload: drift });
3569
+ await context.onRuntimeEvent?.({
3570
+ type: "sandbox.env.drift",
3571
+ payload: drift,
3572
+ });
3524
3573
  } catch {
3525
3574
  // Swallow: a failed emit must not fail the turn.
3526
3575
  }
@@ -3549,7 +3598,11 @@ export async function pinProvidedSessionManifestEnvironment(
3549
3598
  state?: {
3550
3599
  manifest?:
3551
3600
  | Manifest
3552
- | { root?: string; entries?: Record<string, any>; environment?: Record<string, any> };
3601
+ | {
3602
+ root?: string;
3603
+ entries?: Record<string, any>;
3604
+ environment?: Record<string, any>;
3605
+ };
3553
3606
  };
3554
3607
  }
3555
3608
  ).state?.manifest;
@@ -3732,7 +3785,9 @@ export function withSandboxFileDownloads(
3732
3785
  }
3733
3786
  : {}),
3734
3787
  ...(client.delete
3735
- ? { delete: async (state: SandboxSessionState) => await client.delete!(state) }
3788
+ ? {
3789
+ delete: async (state: SandboxSessionState) => await client.delete!(state),
3790
+ }
3736
3791
  : {}),
3737
3792
  ...(client.serializeSessionState
3738
3793
  ? {
@@ -3935,7 +3990,11 @@ function structuredImageToDataUrl(value: unknown): string | null {
3935
3990
  if (v.type !== "image" || !v.image || typeof v.image !== "object") {
3936
3991
  return null;
3937
3992
  }
3938
- const image = v.image as { data?: unknown; mediaType?: unknown; url?: unknown };
3993
+ const image = v.image as {
3994
+ data?: unknown;
3995
+ mediaType?: unknown;
3996
+ url?: unknown;
3997
+ };
3939
3998
  if (typeof image.url === "string" && image.url.length > 0) {
3940
3999
  return image.url;
3941
4000
  }
@@ -4002,7 +4061,10 @@ export function normalizeSdkEvent(event: RunStreamEvent): NormalizedRuntimeEvent
4002
4061
  return out;
4003
4062
  }
4004
4063
  if (event.type === "agent_updated_stream_event") {
4005
- out.push({ type: "agent.updated", payload: { agent: (event as any).agent?.name ?? null } });
4064
+ out.push({
4065
+ type: "agent.updated",
4066
+ payload: { agent: (event as any).agent?.name ?? null },
4067
+ });
4006
4068
  return out;
4007
4069
  }
4008
4070
  if (event.type !== "run_item_stream_event") {
@@ -4164,7 +4226,9 @@ function inputTokenDetailsProp(raw: Record<string, unknown>): Partial<ModelRespo
4164
4226
  if (!details || typeof details !== "object") {
4165
4227
  return {};
4166
4228
  }
4167
- return { inputTokensDetails: details as Record<string, number> | Array<Record<string, number>> };
4229
+ return {
4230
+ inputTokensDetails: details as Record<string, number> | Array<Record<string, number>>,
4231
+ };
4168
4232
  }
4169
4233
 
4170
4234
  function outputTokenDetailsProp(
@@ -4282,7 +4346,9 @@ function objectStorageFileMount(settings: Settings, prefix: string): any {
4282
4346
  accountKey: config.accountKey,
4283
4347
  endpointUrl: config.endpointUrl,
4284
4348
  readOnly: true,
4285
- mountStrategy: inContainerMountStrategy({ pattern: { type: "rclone", mode: "fuse" } }),
4349
+ mountStrategy: inContainerMountStrategy({
4350
+ pattern: { type: "rclone", mode: "fuse" },
4351
+ }),
4286
4352
  });
4287
4353
  }
4288
4354
  if (settings.objectStorageBackend === "aws-s3" || settings.objectStorageBackend === "gcs") {
@@ -4574,7 +4640,9 @@ export function withSandboxLifecycleHooks(
4574
4640
  }
4575
4641
  : {}),
4576
4642
  ...(client.delete
4577
- ? { delete: async (state: SandboxSessionState) => await client.delete!(state) }
4643
+ ? {
4644
+ delete: async (state: SandboxSessionState) => await client.delete!(state),
4645
+ }
4578
4646
  : {}),
4579
4647
  ...(client.serializeSessionState
4580
4648
  ? {
@@ -5272,7 +5340,10 @@ export async function runRigSetupHook(
5272
5340
  const message = error instanceof Error ? error.message : String(error);
5273
5341
  await context.onRuntimeEvent?.({
5274
5342
  type: "rig.setup.failed",
5275
- payload: { ...payload, error: message.slice(-RIG_SETUP_OUTPUT_TAIL_LIMIT) },
5343
+ payload: {
5344
+ ...payload,
5345
+ error: message.slice(-RIG_SETUP_OUTPUT_TAIL_LIMIT),
5346
+ },
5276
5347
  });
5277
5348
  throw new Error(
5278
5349
  `Rig setup failed for rig "${rigSetup.rigName}" (version ${rigSetup.versionId}): ${message}`,
@@ -5304,7 +5375,10 @@ export async function runRigSetupHook(
5304
5375
  );
5305
5376
  await context.onRuntimeEvent?.({
5306
5377
  type: "rig.setup.failed",
5307
- payload: { ...payload, error: failure.message.slice(-RIG_SETUP_OUTPUT_TAIL_LIMIT) },
5378
+ payload: {
5379
+ ...payload,
5380
+ error: failure.message.slice(-RIG_SETUP_OUTPUT_TAIL_LIMIT),
5381
+ },
5308
5382
  });
5309
5383
  throw failure;
5310
5384
  }
@@ -5319,8 +5393,14 @@ export async function runRepositoryCloneHook(
5319
5393
  resources: Extract<ResourceRef, { kind: "repository" }>[],
5320
5394
  context: SandboxLifecycleHookContext = { environment: {} },
5321
5395
  ): Promise<void> {
5322
- const payload = { name: "repository-clone", repositoryCount: resources.length };
5323
- await context.onRuntimeEvent?.({ type: "sandbox.operation.started", payload });
5396
+ const payload = {
5397
+ name: "repository-clone",
5398
+ repositoryCount: resources.length,
5399
+ };
5400
+ await context.onRuntimeEvent?.({
5401
+ type: "sandbox.operation.started",
5402
+ payload,
5403
+ });
5324
5404
  try {
5325
5405
  // TOKEN-BROKER (B1): thread run-scoped provider tokens PER-EXEC, never on
5326
5406
  // the manifest. The SDK's ExecCommandArgs has no `environment` field (exec
@@ -5359,7 +5439,10 @@ export async function runRepositoryCloneHook(
5359
5439
  } else {
5360
5440
  throw new Error("Sandbox session does not support command execution");
5361
5441
  }
5362
- await context.onRuntimeEvent?.({ type: "sandbox.operation.completed", payload });
5442
+ await context.onRuntimeEvent?.({
5443
+ type: "sandbox.operation.completed",
5444
+ payload,
5445
+ });
5363
5446
  } catch (error) {
5364
5447
  await context.onRuntimeEvent?.({
5365
5448
  type: "sandbox.operation.failed",
@@ -5472,8 +5555,14 @@ export async function runAzureCliLoginHook(
5472
5555
  session: SandboxSessionLike,
5473
5556
  context: SandboxLifecycleHookContext = { environment: {} },
5474
5557
  ): Promise<void> {
5475
- const payload = { name: "azure-cli-login", command: "az login --service-principal" };
5476
- await context.onRuntimeEvent?.({ type: "sandbox.operation.started", payload });
5558
+ const payload = {
5559
+ name: "azure-cli-login",
5560
+ command: "az login --service-principal",
5561
+ };
5562
+ await context.onRuntimeEvent?.({
5563
+ type: "sandbox.operation.started",
5564
+ payload,
5565
+ });
5477
5566
  try {
5478
5567
  if (session.exec) {
5479
5568
  const result = await session.exec({
@@ -5496,7 +5585,10 @@ export async function runAzureCliLoginHook(
5496
5585
  } else {
5497
5586
  throw new Error("Sandbox session does not support command execution");
5498
5587
  }
5499
- await context.onRuntimeEvent?.({ type: "sandbox.operation.completed", payload });
5588
+ await context.onRuntimeEvent?.({
5589
+ type: "sandbox.operation.completed",
5590
+ payload,
5591
+ });
5500
5592
  } catch (error) {
5501
5593
  await context.onRuntimeEvent?.({
5502
5594
  type: "sandbox.operation.failed",