@opengeni/contracts 0.20.1 → 0.22.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/contracts",
3
- "version": "0.20.1",
3
+ "version": "0.22.0",
4
4
  "description": "Shared zod schemas and wire-contract types for the OpenGeni API.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
package/src/index.ts CHANGED
@@ -626,6 +626,81 @@ export const DEFAULT_FIRST_PARTY_MCP_PERMISSIONS = [
626
626
  "github:use",
627
627
  ] as const satisfies readonly Permission[];
628
628
 
629
+ /**
630
+ * Exact public catalog for tools served by the broad first-party `opengeni`
631
+ * MCP server. Adding a registration does not make it model-visible: the name
632
+ * must be admitted here and selected by the session policy.
633
+ *
634
+ * `files_get_download_url` intentionally is not in this catalog. It belongs to
635
+ * the dedicated `files` MCP server.
636
+ */
637
+ export const FIRST_PARTY_MCP_TOOL_NAMES = [
638
+ "set_session_title",
639
+ "goal_set",
640
+ "goal_update",
641
+ "goal_complete",
642
+ "goal_pause",
643
+ "memory_search",
644
+ "memory_save",
645
+ "memory_correct",
646
+ "sandboxes_list",
647
+ "sandbox_attach",
648
+ "sandbox_swap",
649
+ "run_on",
650
+ "sandbox_provision",
651
+ "rig_list",
652
+ "rig_get",
653
+ "rig_propose_change",
654
+ "rig_verify",
655
+ "rig_promote",
656
+ "sessions_list",
657
+ "session_get",
658
+ "session_events",
659
+ "session_create",
660
+ "session_send_message",
661
+ "session_pause",
662
+ "session_resume",
663
+ "session_steer",
664
+ "set_other_session_title",
665
+ "variable_set_list",
666
+ "environment_list",
667
+ "variable_set_set_variable",
668
+ "environment_set_variable",
669
+ "github_connect_link",
670
+ "github_token",
671
+ "github_repositories_list",
672
+ "social_connections_list",
673
+ "social_posts_recent",
674
+ "social_daily_analysis_context",
675
+ "scheduled_tasks_list",
676
+ "scheduled_tasks_get",
677
+ "scheduled_tasks_create",
678
+ "scheduled_tasks_update",
679
+ "scheduled_tasks_pause",
680
+ "scheduled_tasks_resume",
681
+ "scheduled_tasks_trigger",
682
+ "scheduled_tasks_delete",
683
+ "scheduled_task_runs_list",
684
+ "slack_bot_list_channels",
685
+ "slack_bot_channel_history",
686
+ "slack_bot_list_users",
687
+ "slack_bot_post_message",
688
+ ] as const;
689
+ export const FirstPartyMcpToolName = z.enum(FIRST_PARTY_MCP_TOOL_NAMES);
690
+ export type FirstPartyMcpToolName = z.infer<typeof FirstPartyMcpToolName>;
691
+
692
+ /**
693
+ * Ordinary sessions get only the small self-management surface. Authorization
694
+ * permissions remain an independent, additional boundary.
695
+ */
696
+ export const DEFAULT_FIRST_PARTY_MCP_TOOLS = [
697
+ "set_session_title",
698
+ "goal_set",
699
+ "goal_update",
700
+ "goal_complete",
701
+ "goal_pause",
702
+ ] as const satisfies readonly FirstPartyMcpToolName[];
703
+
629
704
  export function prefixedMcpToolName(registryId: string, toolName: string): string {
630
705
  return `${registryId}__${toolName}`;
631
706
  }
@@ -1139,6 +1214,9 @@ export const DelegatedAccessTokenPayload = z
1139
1214
  // Worker-asserted session scope for first-party MCP calls (HMAC-signed, not
1140
1215
  // agent-controlled); enables session-scoped tools such as goal management.
1141
1216
  sessionId: z.string().uuid().optional(),
1217
+ // Model-visible first-party tool selection for a worker-bound session.
1218
+ // This is visibility only; permissions remain the authorization boundary.
1219
+ firstPartyMcpTools: z.array(FirstPartyMcpToolName).optional(),
1142
1220
  // The turn making the call (the caller's identity), HMAC-signed by the worker
1143
1221
  // at turn setup. Lets a tool classify WHO is calling from the token itself,
1144
1222
  // instead of racily re-reading the session's live active_turn_id — e.g. the
@@ -3608,21 +3686,6 @@ export const ComposerDraft = z.object({
3608
3686
  });
3609
3687
  export type ComposerDraft = z.infer<typeof ComposerDraft>;
3610
3688
 
3611
- export const SessionQueueSnapshot = z.object({
3612
- version: z.number().int().nonnegative(),
3613
- effectiveControl: EffectiveSessionControl,
3614
- /**
3615
- * True while the latest attempt is interrupted but has not durably proved
3616
- * quiescence: no more inference, user-visible output, or workspace-persistence
3617
- * authority. Temporal cancellation/terminalization is not that proof. This is
3618
- * distinct from ordinary capacity queueing, remains accurate with an empty
3619
- * visible queue, and is independent of Steer-row metadata or withdrawal.
3620
- */
3621
- stoppingPreviousAttempt: z.boolean(),
3622
- items: z.array(SessionTurn),
3623
- });
3624
- export type SessionQueueSnapshot = z.infer<typeof SessionQueueSnapshot>;
3625
-
3626
3689
  export const MoveSessionQueueItemRequest = z.object({
3627
3690
  clientEventId: SessionOperationKey,
3628
3691
  expectedQueueVersion: z.number().int().nonnegative(),
@@ -3974,7 +4037,6 @@ export type SessionSystemUpdatePayload = z.infer<typeof SessionSystemUpdatePaylo
3974
4037
 
3975
4038
  export const SessionSystemUpdateState = z.enum([
3976
4039
  "pending",
3977
- "deferred",
3978
4040
  "delivered",
3979
4041
  "cancelled",
3980
4042
  "superseded",
@@ -3994,11 +4056,104 @@ export const SessionSystemUpdate = z.object({
3994
4056
  lineage: z.record(z.string(), z.unknown()),
3995
4057
  state: SessionSystemUpdateState,
3996
4058
  deliveredTurnId: z.string().uuid().nullable(),
4059
+ /**
4060
+ * The exact durable model-memory row containing the coalesced batch that
4061
+ * delivered this update. Null until claim; every member of one batch shares
4062
+ * the same id.
4063
+ */
4064
+ deliveredHistoryItemId: z.string().uuid().nullable(),
3997
4065
  deliveredAt: z.string().nullable(),
3998
4066
  createdAt: z.string(),
3999
4067
  });
4000
4068
  export type SessionSystemUpdate = z.infer<typeof SessionSystemUpdate>;
4001
4069
 
4070
+ /**
4071
+ * Bounded queue projection of a canonical pending machine input. Full payload,
4072
+ * lineage, and dedupe data remain in canonical storage and never inflate the
4073
+ * hot queue response.
4074
+ */
4075
+ export const SessionPendingInputPreview = SessionSystemUpdate.pick({
4076
+ id: true,
4077
+ sessionId: true,
4078
+ kind: true,
4079
+ classification: true,
4080
+ sourceId: true,
4081
+ summary: true,
4082
+ createdAt: true,
4083
+ });
4084
+ export type SessionPendingInputPreview = z.infer<typeof SessionPendingInputPreview>;
4085
+
4086
+ export const SessionQueueSnapshot = z.object({
4087
+ version: z.number().int().nonnegative(),
4088
+ effectiveControl: EffectiveSessionControl,
4089
+ /**
4090
+ * True while the latest attempt is interrupted but has not durably proved
4091
+ * quiescence: no more inference, user-visible output, or workspace-persistence
4092
+ * authority. Temporal cancellation/terminalization is not that proof. This is
4093
+ * distinct from ordinary capacity queueing, remains accurate with an empty
4094
+ * visible queue, and is independent of Steer-row metadata or withdrawal.
4095
+ */
4096
+ stoppingPreviousAttempt: z.boolean(),
4097
+ items: z.array(SessionTurn),
4098
+ /** Canonical bounded previews; never reconstructed from session events. */
4099
+ pendingInputs: z.array(SessionPendingInputPreview),
4100
+ /**
4101
+ * Exact members of the next bounded machine-input batch that will join an
4102
+ * already-waiting human/API prompt. Null means the next machine-input claim
4103
+ * is standalone. This is a projection of canonical rows, not queue state.
4104
+ */
4105
+ pendingInputAttachment: z
4106
+ .object({
4107
+ turnId: z.string().uuid(),
4108
+ inputIds: z.array(z.string().uuid()).min(1),
4109
+ })
4110
+ .nullable(),
4111
+ });
4112
+ export type SessionQueueSnapshot = z.infer<typeof SessionQueueSnapshot>;
4113
+
4114
+ /**
4115
+ * Deterministic, protocol-safe model representation of one claimed machine
4116
+ * input batch. This exact string is persisted before inference and replayed on
4117
+ * every later turn; callers must not synthesize an equivalent transient copy.
4118
+ */
4119
+ export function renderSessionSystemUpdateBatch(
4120
+ updates: ReadonlyArray<
4121
+ Pick<
4122
+ SessionSystemUpdate,
4123
+ "id" | "kind" | "classification" | "sourceId" | "summary" | "payload" | "lineage"
4124
+ >
4125
+ >,
4126
+ ): string {
4127
+ if (updates.length === 0) {
4128
+ throw new TypeError("A durable machine-input batch requires at least one update");
4129
+ }
4130
+ return [
4131
+ "[OpenGeni internal updates]",
4132
+ "These platform updates were delivered together for this inference. They are not human prompts.",
4133
+ JSON.stringify({
4134
+ updates: updates.map((update) => ({
4135
+ id: update.id,
4136
+ kind: update.kind,
4137
+ classification: update.classification,
4138
+ sourceId: update.sourceId,
4139
+ summary: update.summary,
4140
+ payload: update.payload,
4141
+ lineage: update.lineage,
4142
+ })),
4143
+ }),
4144
+ ].join("\n");
4145
+ }
4146
+
4147
+ export function sessionSystemUpdateBatchHistoryItem(
4148
+ updates: Parameters<typeof renderSessionSystemUpdateBatch>[0],
4149
+ ): { type: "message"; role: "system"; content: string } {
4150
+ return {
4151
+ type: "message",
4152
+ role: "system",
4153
+ content: renderSessionSystemUpdateBatch(updates),
4154
+ };
4155
+ }
4156
+
4002
4157
  export const VariableSetVariableName = z
4003
4158
  .string()
4004
4159
  .regex(/^[A-Z][A-Z0-9_]*$/)
@@ -4464,6 +4619,41 @@ export const CapabilityPackSkill = z
4464
4619
  });
4465
4620
  export type CapabilityPackSkill = z.infer<typeof CapabilityPackSkill>;
4466
4621
 
4622
+ // Inline skill content fixed onto one session at creation. It intentionally
4623
+ // uses the exact same validated directory shape as a pack skill, but has a
4624
+ // different semantic owner and lifecycle. Session readers can inspect it; it
4625
+ // is configuration, never a secret store.
4626
+ export const SessionSkill = CapabilityPackSkill;
4627
+ export type SessionSkill = z.infer<typeof SessionSkill>;
4628
+
4629
+ export const SessionSkills = z
4630
+ .array(SessionSkill)
4631
+ .max(32)
4632
+ .transform((skills, ctx) => {
4633
+ const selected = new Map<string, { fingerprint: string; skill: SessionSkill }>();
4634
+ for (const skill of skills) {
4635
+ const key = skill.name.toLowerCase();
4636
+ const fingerprint = JSON.stringify({
4637
+ description: skill.description ?? null,
4638
+ files: [...skill.files]
4639
+ .sort((left, right) => left.path.localeCompare(right.path))
4640
+ .map(({ path, content }) => ({ path, content })),
4641
+ });
4642
+ const existing = selected.get(key);
4643
+ if (!existing) {
4644
+ selected.set(key, { fingerprint, skill });
4645
+ continue;
4646
+ }
4647
+ if (existing.fingerprint !== fingerprint) {
4648
+ ctx.addIssue({
4649
+ code: "custom",
4650
+ message: `conflicting session skill definitions: ${skill.name}`,
4651
+ });
4652
+ }
4653
+ }
4654
+ return [...selected.values()].map(({ skill }) => skill);
4655
+ });
4656
+
4467
4657
  function isSafePackSkillRelativePath(path: string): boolean {
4468
4658
  if (path.startsWith("/") || path.includes("\\")) {
4469
4659
  return false;
@@ -4727,15 +4917,16 @@ export const CreateConnectionRequest = z.object({
4727
4917
  });
4728
4918
  export type CreateConnectionRequest = z.infer<typeof CreateConnectionRequest>;
4729
4919
 
4730
- /**
4731
- * Write-only Slack bot installation input. `token` is accepted only by the
4732
- * dedicated validated endpoint and is never represented in a response schema.
4733
- */
4734
- export const ConnectOpenGeniSlackBotRequest = z.object({
4735
- token: z.string().trim().startsWith("xoxb-").max(8192),
4920
+ export const OpenGeniSlackBotInstallRequest = z.object({
4736
4921
  connectionId: z.string().uuid().optional(),
4737
4922
  });
4738
- export type ConnectOpenGeniSlackBotRequest = z.infer<typeof ConnectOpenGeniSlackBotRequest>;
4923
+ export type OpenGeniSlackBotInstallRequest = z.infer<typeof OpenGeniSlackBotInstallRequest>;
4924
+
4925
+ export const OpenGeniSlackBotInstallStart = z.object({
4926
+ authorizationUrl: z.string().url(),
4927
+ expiresAt: z.string().datetime({ offset: true }),
4928
+ });
4929
+ export type OpenGeniSlackBotInstallStart = z.infer<typeof OpenGeniSlackBotInstallStart>;
4739
4930
 
4740
4931
  export const UpdateConnectionRequest = z.object({
4741
4932
  providerDomain: z.string().min(1).optional(),
@@ -4887,15 +5078,15 @@ export const CapabilityCatalogItem = z.object({
4887
5078
  runtime: CapabilityRuntime.default({ available: false, notes: null }),
4888
5079
  enabled: z.boolean().default(false),
4889
5080
  enabledReason: z.string().nullable().default(null),
4890
- // The connection backing this enabled installation, when the enable-time
4891
- // connectionRef resolved to one (null for header/credential-free items
4892
- // that means "no connection involved", not "broken"). Lets the UI match
4893
- // connection health by id instead of guessing from providerDomain alone.
5081
+ // The non-secret connection binding stored with an enabled installation.
5082
+ // Workspace refs retain an exact row id. Subject refs deliberately omit it:
5083
+ // each caller resolves their own visible row by provider/kind at runtime.
4894
5084
  connectionRef: z
4895
5085
  .object({
4896
- connectionId: z.string().min(1),
5086
+ connectionId: z.string().min(1).optional(),
4897
5087
  providerDomain: z.string().min(1),
4898
5088
  kind: z.string().min(1),
5089
+ subjectScope: z.enum(["workspace", "subject"]).optional(),
4899
5090
  })
4900
5091
  .nullable()
4901
5092
  .default(null),
@@ -5011,6 +5202,7 @@ export const Session = z.object({
5011
5202
  // null when the session carried none.
5012
5203
  instructions: z.string().nullable(),
5013
5204
  resources: z.array(ResourceRef),
5205
+ skills: SessionSkills.default([]),
5014
5206
  tools: z.array(ToolRef),
5015
5207
  // Origin of the persisted tool allow-list. Optional for rolling client
5016
5208
  // compatibility; current servers emit it and legacy rows map to `legacy`.
@@ -5055,6 +5247,9 @@ export const Session = z.object({
5055
5247
  // Non-default first-party MCP token permissions (manager-style sessions);
5056
5248
  // null means the fixed worker default set.
5057
5249
  firstPartyMcpPermissions: z.array(Permission).nullable(),
5250
+ // null means the fixed minimal worker-visible default. An explicit array,
5251
+ // including [], is the exact model-visible first-party selection.
5252
+ firstPartyMcpTools: z.array(FirstPartyMcpToolName).nullable().default(null),
5058
5253
  // Per-session third-party MCP servers, metadata only. Credential values are
5059
5254
  // write-only and never appear here.
5060
5255
  mcpServers: z.array(SessionMcpServerMetadata).default([]),
@@ -5227,6 +5422,9 @@ export const SessionEventType = z.enum([
5227
5422
  "goal.continuation",
5228
5423
  "system.update.pending",
5229
5424
  "system.update.delivered",
5425
+ "system.update.superseded",
5426
+ "system.update.cancelled",
5427
+ "system.update.settled",
5230
5428
  "session.control.paused",
5231
5429
  "session.control.resumed",
5232
5430
  "session.control.steer_requested",
@@ -5427,6 +5625,9 @@ export const SESSION_EVENT_SEMANTIC_CLASS_TYPES = {
5427
5625
  "goal.continuation",
5428
5626
  "system.update.pending",
5429
5627
  "system.update.delivered",
5628
+ "system.update.superseded",
5629
+ "system.update.cancelled",
5630
+ "system.update.settled",
5430
5631
  "session.control.paused",
5431
5632
  "session.control.resumed",
5432
5633
  "session.control.steer_requested",
@@ -7285,6 +7486,9 @@ export const CreateSessionRequest = withVariableSetIdAlias({
7285
7486
  // authoritative. Top-level omission remains []. Presence is resolved from
7286
7487
  // the raw request because this Zod default erases absent-vs-empty.
7287
7488
  resources: z.array(ResourceRef).default([]),
7489
+ // Inline skills are fixed onto the session. Child omission inherits the
7490
+ // trusted parent's selection; an explicit array, including [], wins.
7491
+ skills: SessionSkills.default([]),
7288
7492
  // The same child omission rule applies to selected MCP tool refs. Top-level
7289
7493
  // omission still applies workspace-default capability MCP tools; explicit []
7290
7494
  // suppresses those defaults (the first-party OpenGeni server remains added).
@@ -7337,6 +7541,10 @@ export const CreateSessionRequest = withVariableSetIdAlias({
7337
7541
  // A goal-bearing session whose explicit/effective set omits goals:manage is
7338
7542
  // rejected; creation never silently expands a child beyond that set.
7339
7543
  firstPartyMcpPermissions: z.array(Permission).optional(),
7544
+ // Exact model-visible selection from the broad first-party OpenGeni MCP
7545
+ // catalog. Omit for the minimal default; [] intentionally exposes none.
7546
+ // This does not grant authority: every registered tool is permission-gated.
7547
+ firstPartyMcpTools: z.array(FirstPartyMcpToolName).optional(),
7340
7548
  // Third-party MCP servers attached only to this session. For an agent-created
7341
7549
  // child, omission snapshots its trusted immediate parent's server definitions,
7342
7550
  // policies, connection refs, and encrypted credentials. Explicit arrays,