@opengeni/contracts 1.0.2 → 1.2.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.
@@ -200,9 +200,22 @@ export type ClientAgentConfig = z.infer<typeof ClientAgentConfig>;
200
200
  // Capability registry
201
201
  // ---------------------------------------------------------------------------
202
202
 
203
- /** A first-party tool's owner: a capability, or pure runtime mechanics. */
203
+ /** A built-in MCP server's owner: a capability, or pure runtime mechanics. */
204
204
  export type AgentToolCapability = AgentCapabilityId | "runtime";
205
205
 
206
+ /**
207
+ * A first-party tool's owner: a capability, pure runtime mechanics, or the
208
+ * attached compute (a managed sandbox or Connected Machine).
209
+ */
210
+ export type AgentFirstPartyToolOwner = AgentToolCapability | "sandbox";
211
+
212
+ /** Owners no capability toggles: the runtime or the attached compute derives them. */
213
+ export function isDerivedAgentToolOwner(
214
+ owner: AgentFirstPartyToolOwner,
215
+ ): owner is "runtime" | "sandbox" {
216
+ return owner === "runtime" || owner === "sandbox";
217
+ }
218
+
206
219
  /**
207
220
  * Exhaustive map from every `FIRST_PARTY_MCP_TOOL_NAMES` entry to exactly one
208
221
  * owner. The `Record` type makes an unmapped new name a compile error, and
@@ -210,15 +223,18 @@ export type AgentToolCapability = AgentCapabilityId | "runtime";
210
223
  *
211
224
  * `runtime` tools are mechanics every agent needs regardless of what it may do:
212
225
  * - `wait_for_input`: ends the turn and waits for the next input.
213
- * - `command_read` / `command_wait`: read/await the session's own background
214
- * sandbox commands (the shell already exists whenever a sandbox is attached).
215
226
  * - `set_session_title`: session titling.
227
+ *
228
+ * `sandbox` tools are derived from attached compute and never toggled:
229
+ * - `command_read` / `command_wait`: read/await the session's own background
230
+ * commands. A command can only exist on a managed sandbox or Connected
231
+ * Machine, so a turn with neither attached never receives them.
216
232
  */
217
233
  export const FIRST_PARTY_MCP_TOOL_CAPABILITIES = {
218
234
  set_session_title: "runtime",
219
235
  wait_for_input: "runtime",
220
- command_wait: "runtime",
221
- command_read: "runtime",
236
+ command_wait: "sandbox",
237
+ command_read: "sandbox",
222
238
 
223
239
  goal_set: "goals",
224
240
  goal_update: "goals",
@@ -400,7 +416,7 @@ export const FIRST_PARTY_MCP_TOOL_CAPABILITIES = {
400
416
  editable_artifact_apply: "artifacts",
401
417
  editable_artifact_export: "artifacts",
402
418
  editable_artifact_export_status: "artifacts",
403
- } as const satisfies Record<FirstPartyMcpToolName, AgentToolCapability>;
419
+ } as const satisfies Record<FirstPartyMcpToolName, AgentFirstPartyToolOwner>;
404
420
 
405
421
  /** Owner of a non-MCP tool: a capability, runtime mechanics, or the sandbox. */
406
422
  export type AgentFunctionToolClass = AgentCapabilityId | "runtime" | "sandbox";
@@ -493,7 +509,7 @@ export const AGENT_BUILTIN_MCP_SERVER_CAPABILITIES = {
493
509
  "google-drive-publishing": "workspaceConnectors",
494
510
  } as const satisfies Record<string, AgentToolCapability>;
495
511
 
496
- export function firstPartyMcpToolCapability(tool: FirstPartyMcpToolName): AgentToolCapability {
512
+ export function firstPartyMcpToolCapability(tool: FirstPartyMcpToolName): AgentFirstPartyToolOwner {
497
513
  return FIRST_PARTY_MCP_TOOL_CAPABILITIES[tool];
498
514
  }
499
515
 
@@ -508,10 +524,21 @@ export type AgentToolEnvironment = {
508
524
  media?: boolean | undefined;
509
525
  hasDeferredTools?: boolean;
510
526
  routerInHistory?: boolean;
527
+ /**
528
+ * Whether the turn has a managed sandbox or Connected Machine attached.
529
+ * `false` withholds the `sandbox`-owned first-party tools, which could never
530
+ * find a command; undefined means not yet known and keeps them.
531
+ */
532
+ sandboxAttached?: boolean;
511
533
  };
512
534
 
535
+ /** Runtime mechanics every configured agent receives. */
513
536
  export const AGENT_RUNTIME_MECHANIC_TOOL_NAMES = [
514
537
  "wait_for_input",
538
+ ] as const satisfies readonly FirstPartyMcpToolName[];
539
+
540
+ /** First-party tools a configured agent receives whenever compute is attached. */
541
+ export const AGENT_SANDBOX_MECHANIC_TOOL_NAMES = [
515
542
  "command_read",
516
543
  "command_wait",
517
544
  ] as const satisfies readonly FirstPartyMcpToolName[];
@@ -519,7 +546,9 @@ export const AGENT_RUNTIME_MECHANIC_TOOL_NAMES = [
519
546
  /**
520
547
  * The one turn-time capability gate. Null means the historical attachment
521
548
  * rules, including unconditional Skill tools and router. Resource-derived
522
- * sandbox tools, product MCPs and runtime mechanics are never toggled.
549
+ * sandbox tools, product MCPs and runtime mechanics are never toggled; the
550
+ * `sandbox`-owned first-party tools follow `environment.sandboxAttached` for
551
+ * every session, configured or not.
523
552
  */
524
553
  export function resolveAgentToolFamilies(
525
554
  config: ResolvedAgentConfig | null | undefined,
@@ -534,8 +563,10 @@ export function resolveAgentToolFamilies(
534
563
  const humanInput = enabled("humanInput") && environment.humanInput !== false;
535
564
  const media = enabled("media") && environment.media !== false;
536
565
  const skills = !config ? "manage" : enabled("skills") ? config.capabilities.skills : false;
566
+ const sandboxTools = environment.sandboxAttached !== false;
537
567
  const allowsFirstPartyTool = (name: FirstPartyMcpToolName): boolean => {
538
- const owner = FIRST_PARTY_MCP_TOOL_CAPABILITIES[name];
568
+ const owner: AgentFirstPartyToolOwner = FIRST_PARTY_MCP_TOOL_CAPABILITIES[name];
569
+ if (owner === "sandbox") return sandboxTools;
539
570
  return owner === "runtime" || enabled(owner);
540
571
  };
541
572
  return {
@@ -549,11 +580,14 @@ export function resolveAgentToolFamilies(
549
580
  allowsFirstPartyTool,
550
581
  firstPartyTools(names: readonly FirstPartyMcpToolName[]): FirstPartyMcpToolName[] {
551
582
  return !config
552
- ? [...names]
583
+ ? names.filter(
584
+ (name) => sandboxTools || FIRST_PARTY_MCP_TOOL_CAPABILITIES[name] !== "sandbox",
585
+ )
553
586
  : [
554
587
  ...new Set([
555
588
  ...names.filter(allowsFirstPartyTool),
556
589
  ...AGENT_RUNTIME_MECHANIC_TOOL_NAMES,
590
+ ...(sandboxTools ? AGENT_SANDBOX_MECHANIC_TOOL_NAMES : []),
557
591
  ]),
558
592
  ];
559
593
  },
@@ -1203,8 +1237,8 @@ export function agentConfigFirstPartyMcpTools(
1203
1237
  explicit?: readonly FirstPartyMcpToolName[],
1204
1238
  ): FirstPartyMcpToolName[] {
1205
1239
  for (const tool of explicit ?? []) {
1206
- const owner = FIRST_PARTY_MCP_TOOL_CAPABILITIES[tool];
1207
- if (owner !== "runtime" && capabilityFilteredOut(config, owner)) {
1240
+ const owner: AgentFirstPartyToolOwner = FIRST_PARTY_MCP_TOOL_CAPABILITIES[tool];
1241
+ if (!isDerivedAgentToolOwner(owner) && capabilityFilteredOut(config, owner)) {
1208
1242
  throw new AgentConfigError(
1209
1243
  "agent_config_conflict",
1210
1244
  `firstPartyMcpTools lists ${tool}, but agent capability ${owner} is off`,
@@ -1213,8 +1247,8 @@ export function agentConfigFirstPartyMcpTools(
1213
1247
  }
1214
1248
  }
1215
1249
  return baseline.filter((tool) => {
1216
- const owner = FIRST_PARTY_MCP_TOOL_CAPABILITIES[tool];
1217
- return owner === "runtime" || !capabilityFilteredOut(config, owner);
1250
+ const owner: AgentFirstPartyToolOwner = FIRST_PARTY_MCP_TOOL_CAPABILITIES[tool];
1251
+ return isDerivedAgentToolOwner(owner) || !capabilityFilteredOut(config, owner);
1218
1252
  });
1219
1253
  }
1220
1254
 
@@ -1229,9 +1263,9 @@ export function agentConfigAddedFirstPartyMcpTools(
1229
1263
  defaults: readonly FirstPartyMcpToolName[],
1230
1264
  ): FirstPartyMcpToolName[] {
1231
1265
  return defaults.filter((tool) => {
1232
- const owner = FIRST_PARTY_MCP_TOOL_CAPABILITIES[tool];
1266
+ const owner: AgentFirstPartyToolOwner = FIRST_PARTY_MCP_TOOL_CAPABILITIES[tool];
1233
1267
  return (
1234
- owner !== "runtime" &&
1268
+ !isDerivedAgentToolOwner(owner) &&
1235
1269
  !agentCapabilityEnabled(previous, owner) &&
1236
1270
  agentCapabilityEnabled(next.capabilities, owner)
1237
1271
  );
@@ -1325,7 +1359,7 @@ export function legacyEffectiveAgentCapabilities(input: {
1325
1359
  */
1326
1360
  defaultServerIds?: Iterable<string> | undefined;
1327
1361
  }): ResolvedAgentCapabilities {
1328
- const selected = new Set<AgentToolCapability>(
1362
+ const selected = new Set<AgentFirstPartyToolOwner>(
1329
1363
  input.firstPartyMcpTools.map((tool) => FIRST_PARTY_MCP_TOOL_CAPABILITIES[tool]),
1330
1364
  );
1331
1365
  const ids = new Set(input.tools.map((tool) => tool.id));
@@ -20,6 +20,8 @@ export const ArtifactCatalogItem = z
20
20
  createdAt: z.string().datetime({ offset: true }),
21
21
  updatedAt: z.string().datetime({ offset: true }),
22
22
  status: z.enum(["active", "archived"]),
23
+ /** Shared workspace metadata, not content-access authority. */
24
+ pinned: z.boolean().optional(),
23
25
  sourceSessionId: z.string().uuid().optional(),
24
26
  versionId: z.string().min(1).max(128).optional(),
25
27
  file: RetainedArtifactReferenceSchema.optional(),
@@ -58,3 +60,15 @@ export const ArtifactCatalogListResponse = z
58
60
  })
59
61
  .strict();
60
62
  export type ArtifactCatalogListResponse = z.infer<typeof ArtifactCatalogListResponse>;
63
+
64
+ export const UpdateArtifactPinRequest = z.object({ pinned: z.boolean() }).strict();
65
+ export type UpdateArtifactPinRequest = z.infer<typeof UpdateArtifactPinRequest>;
66
+
67
+ export const ArtifactPinResponse = z
68
+ .object({
69
+ kind: ArtifactCatalogKind,
70
+ artifactId: z.string().min(1).max(128),
71
+ pinned: z.boolean(),
72
+ })
73
+ .strict();
74
+ export type ArtifactPinResponse = z.infer<typeof ArtifactPinResponse>;
@@ -32,6 +32,19 @@ export function resolveBundledSkillSelection(
32
32
  return selected === undefined ? undefined : BundledSkillSelection.parse(selected);
33
33
  }
34
34
 
35
+ /**
36
+ * Freeze the defaults a session's agent configuration implies. An agent that
37
+ * starts from `capabilities: "none"` carries no bundled Opengeni guides unless
38
+ * the request lists them; an explicit list (including `[]`) is kept exactly,
39
+ * and `"all"` or legacy (null) configurations keep the bundled defaults.
40
+ */
41
+ export function bundledSkillSelectionForAgentConfig(
42
+ selection: BundledSkillId[] | undefined,
43
+ agentConfig: { from: "all" | "none" } | null | undefined,
44
+ ): BundledSkillId[] | undefined {
45
+ return selection === undefined && agentConfig?.from === "none" ? [] : selection;
46
+ }
47
+
35
48
  // Immutable session configuration, following the existing create-identity
36
49
  // metadata convention. Callers cannot set this through arbitrary metadata.
37
50
  const BUNDLED_SKILL_SELECTION_KEY = "_opengeni_bundled_skill_ids_v1";
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { modelLogoUrl } from "./model-display";
1
2
  import {
2
3
  ClaudeProviderAccountAuthoritySnapshotV1,
3
4
  WORKSPACE_CLAUDE_PROVIDER_ACCOUNT_AUTHORITY_SNAPSHOT_V1,
@@ -5003,11 +5004,26 @@ export type GitHubAppApiPort = {
5003
5004
  }) => Promise<GitHubAppRepositoryBranchPage>;
5004
5005
  };
5005
5006
 
5007
+ export const PromotionalCreditScope = z.object({
5008
+ label: z.string().trim().min(1).max(120),
5009
+ eligibleModelIds: z.array(z.string().trim().min(1).max(200)).min(1).max(40),
5010
+ });
5011
+ export type PromotionalCreditScope = z.infer<typeof PromotionalCreditScope>;
5012
+
5013
+ export const PromotionalCreditBalance = PromotionalCreditScope.extend({
5014
+ grantId: z.string().uuid(),
5015
+ remainingMicros: z.number().int().nonnegative(),
5016
+ });
5017
+ export type PromotionalCreditBalance = z.infer<typeof PromotionalCreditBalance>;
5018
+
5006
5019
  export const BillingBalance = z.object({
5007
5020
  accountId: z.string().uuid(),
5008
5021
  balanceMicros: z.number().int(),
5009
5022
  currency: z.literal("usd"),
5010
5023
  updatedAt: z.string(),
5024
+ /** General credits, including unrestricted legacy grants. May be negative after use. */
5025
+ generalBalanceMicros: z.number().int().optional(),
5026
+ promotionalCredits: z.array(PromotionalCreditBalance).optional(),
5011
5027
  });
5012
5028
  export type BillingBalance = z.infer<typeof BillingBalance>;
5013
5029
 
@@ -5038,6 +5054,7 @@ export const CreateCheckoutResponse = z.object({
5038
5054
  url: z.string().url(),
5039
5055
  /** The credits this checkout grants once it completes. */
5040
5056
  amountUsd: z.number().optional(),
5057
+ promotionalScope: PromotionalCreditScope.optional(),
5041
5058
  });
5042
5059
  export type CreateCheckoutResponse = z.infer<typeof CreateCheckoutResponse>;
5043
5060
 
@@ -5055,6 +5072,7 @@ export const BillingCheckoutStatus = z.object({
5055
5072
  currency: z.literal("usd"),
5056
5073
  /** True when a coupon covered the whole checkout, so nothing was charged. */
5057
5074
  free: z.boolean(),
5075
+ promotionalScope: PromotionalCreditScope.optional(),
5058
5076
  }),
5059
5077
  balance: BillingBalance.nullable(),
5060
5078
  });
@@ -6709,6 +6727,16 @@ export const SessionGoalContinuation = z.object({
6709
6727
  });
6710
6728
  export type SessionGoalContinuation = z.infer<typeof SessionGoalContinuation>;
6711
6729
 
6730
+ /** New admission pauses are specific; the wire field remains open for older peers. */
6731
+ export type GoalAdmissionPausedReason =
6732
+ | "model_unavailable"
6733
+ | "model_policy"
6734
+ | "credits"
6735
+ | "budget"
6736
+ | "usage_limit"
6737
+ | "usage_policy"
6738
+ | "allowance";
6739
+
6712
6740
  export const SessionGoal = z.object({
6713
6741
  id: z.string().uuid(),
6714
6742
  accountId: z.string().uuid(),
@@ -10430,7 +10458,8 @@ export type KnowledgeSourceSyncRunSummary = z.infer<typeof KnowledgeSourceSyncRu
10430
10458
  * means every occurrence is refused until the task or a resource it names
10431
10459
  * changes. Known reasons: `scheduled_authority_unavailable`,
10432
10460
  * `machine_target_unavailable`, `machine_enrollment_inactive`,
10433
- * `variable_set_unavailable`, `rig_version_unavailable` (terminal) and
10461
+ * `variable_set_unavailable`, `rig_version_unavailable`, `scheduled_model_unavailable`
10462
+ * (terminal) and
10434
10463
  * `insufficient_credits`, `allowance_exhausted`, `monthly_model_cost_limit`, `monthly_agent_run_limit`
10435
10464
  * (transient). Readers must tolerate new reasons.
10436
10465
  */
@@ -18098,6 +18127,10 @@ export const ClientModel = /* @__PURE__ */ defineModelContractSchema(() =>
18098
18127
  label: z.string(),
18099
18128
  /** Optional curated compact label for dense UI (e.g. mobile composer). */
18100
18129
  shortLabel: z.string().min(1).max(64).optional(),
18130
+ logoUrl: z
18131
+ .string()
18132
+ .refine((value) => modelLogoUrl({ id: "", logoUrl: value }) !== null)
18133
+ .optional(),
18101
18134
  provider: z.string(), // provider id
18102
18135
  providerLabel: z.string(),
18103
18136
  api: z.enum(["responses", "chat", "anthropic-messages"]),
@@ -18218,6 +18251,8 @@ export const WorkspaceModelCatalogModel =
18218
18251
  /* @__PURE__ */ defineModelContractSchema(() =>
18219
18252
  ClientModel.extend({
18220
18253
  credentialReadiness: ModelCredentialReadinessV1,
18254
+ /** Current funding status, without exposing the organization's balance. */
18255
+ creditFunding: z.enum(["promotional", "general", "unavailable"]).optional(),
18221
18256
  /** Exact workspace-policy verdict without exposing provider identity. */
18222
18257
  policyAllowed: z.boolean().optional(),
18223
18258
  availability: ModelAvailabilityV1,
@@ -28,10 +28,23 @@ export type ModelDisplayInput =
28
28
  | {
29
29
  id: string;
30
30
  label?: string | null | undefined;
31
+ logoUrl?: string | null | undefined;
31
32
  upstreamModelId?: string | null | undefined;
32
33
  deployment?: { upstreamModelId?: string | null | undefined } | null | undefined;
33
34
  };
34
35
 
36
+ /** Catalog maker logo; remote images must use HTTPS without embedded credentials. */
37
+ export function modelLogoUrl(input: ModelDisplayInput): string | null {
38
+ const value = typeof input === "string" ? undefined : input.logoUrl;
39
+ if (!value || value.length > 2048 || value !== value.trim()) return null;
40
+ try {
41
+ const url = new URL(value);
42
+ return url.protocol === "https:" && !url.username && !url.password ? value : null;
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+
35
48
  const WORDS: Readonly<Record<string, string>> = {
36
49
  gpt: "GPT",
37
50
  chatgpt: "ChatGPT",