@opengeni/sdk 0.27.0 → 0.29.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/sdk",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "description": "Framework-agnostic TypeScript SDK for the OpenGeni API: typed client, session lifecycle, SSE event streaming with reconnect + replay-by-sequence, and proxy re-streaming helpers.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
package/src/index.ts CHANGED
@@ -24,6 +24,8 @@ export {
24
24
  export type { ProxySessionEventStreamOptions, SseReStreamOptions } from "./proxy";
25
25
  export { parseSseStream } from "./sse";
26
26
  export type { SseMessage } from "./sse";
27
+ export { normalizeMcpOutput } from "./mcp-output";
28
+ export type { NormalizedMcpOutput } from "./mcp-output";
27
29
  // Desktop (noVNC) transport contract — pure, zero-dep (the RFB import lives in
28
30
  // @opengeni/react). URL assembler + connection state machine + rotation fence.
29
31
  export { desktopSocketUrl, nextDesktopState, applyUrlRotation } from "./desktop";
@@ -235,8 +237,10 @@ export type {
235
237
  FileResourceRef,
236
238
  FileStatus,
237
239
  FileUploadData,
240
+ FirstPartyMcpToolName,
238
241
  GetPackResponse,
239
242
  GitHubAppInfo,
243
+ GitHubAppSetupMode,
240
244
  GitHubBindingStatus,
241
245
  GitHubInstallationBinding,
242
246
  GitHubInstallationLifecycle,
@@ -322,6 +326,7 @@ export type {
322
326
  SessionCommandReceipt,
323
327
  SteerSessionQueueItemRequest,
324
328
  WorkspaceInferenceControlResponse,
329
+ SessionPendingInputPreview,
325
330
  SessionSystemUpdate,
326
331
  SessionSystemUpdateKind,
327
332
  SessionSystemUpdateState,
@@ -0,0 +1,161 @@
1
+ /**
2
+ * A transport-tolerant MCP tool result.
3
+ *
4
+ * `value` is the canonical machine-readable payload after recognized MCP/JSON
5
+ * envelopes are removed. `text` is the best presentation string without
6
+ * discarding structured data. `raw` always retains the original evidence.
7
+ */
8
+ export type NormalizedMcpOutput = Readonly<{
9
+ raw: unknown;
10
+ value: unknown;
11
+ text: string;
12
+ isError: boolean;
13
+ }>;
14
+
15
+ const MAX_MCP_OUTPUT_DEPTH = 8;
16
+ const RESULT_ENVELOPE_KEYS = new Set(["result", "isError", "jsonrpc", "id", "_meta"]);
17
+
18
+ /** Normalize common direct, JSON, and standard MCP result envelopes without throwing. */
19
+ export function normalizeMcpOutput(output: unknown): NormalizedMcpOutput {
20
+ const normalized = normalizeValue(output, 0, new Set<object>());
21
+ return {
22
+ raw: output,
23
+ value: normalized.value,
24
+ text: normalized.text,
25
+ isError: normalized.isError,
26
+ };
27
+ }
28
+
29
+ type NormalizedValue = Omit<NormalizedMcpOutput, "raw">;
30
+
31
+ function normalizeValue(value: unknown, depth: number, ancestors: Set<object>): NormalizedValue {
32
+ if (value === null || value === undefined) {
33
+ return { value, text: "", isError: false };
34
+ }
35
+ if (typeof value === "string") {
36
+ return normalizeText(value, depth, ancestors);
37
+ }
38
+ if (typeof value !== "object") {
39
+ return { value, text: String(value), isError: false };
40
+ }
41
+ if (depth >= MAX_MCP_OUTPUT_DEPTH || ancestors.has(value)) {
42
+ return { value, text: safeStringify(value), isError: false };
43
+ }
44
+
45
+ ancestors.add(value);
46
+ try {
47
+ if (Array.isArray(value)) {
48
+ return { value, text: safeStringify(value), isError: false };
49
+ }
50
+
51
+ const record = value as Record<string, unknown>;
52
+ const envelopeError = record.isError === true;
53
+
54
+ if (record.type === "text" && typeof record.text === "string") {
55
+ const normalized = normalizeText(record.text, depth + 1, ancestors);
56
+ return {
57
+ value: normalized.value,
58
+ text: record.text,
59
+ isError: envelopeError || normalized.isError,
60
+ };
61
+ }
62
+
63
+ if ("structuredContent" in record) {
64
+ const normalized = normalizeValue(record.structuredContent, depth + 1, ancestors);
65
+ return {
66
+ value: normalized.value,
67
+ text: firstMcpText(record.content) ?? normalized.text,
68
+ isError: envelopeError || normalized.isError,
69
+ };
70
+ }
71
+
72
+ if (isMcpContent(record.content)) {
73
+ const text = firstMcpText(record.content);
74
+ if (text !== null) {
75
+ const normalized = normalizeText(text, depth + 1, ancestors);
76
+ return {
77
+ value: normalized.value,
78
+ text,
79
+ isError: envelopeError || normalized.isError,
80
+ };
81
+ }
82
+ return {
83
+ value,
84
+ text: safeStringify(value),
85
+ isError: envelopeError,
86
+ };
87
+ }
88
+
89
+ if (isResultEnvelope(record)) {
90
+ const normalized = normalizeValue(record.result, depth + 1, ancestors);
91
+ return {
92
+ value: normalized.value,
93
+ text: normalized.text,
94
+ isError: envelopeError || normalized.isError,
95
+ };
96
+ }
97
+
98
+ return {
99
+ value,
100
+ text: safeStringify(value),
101
+ isError: envelopeError,
102
+ };
103
+ } finally {
104
+ ancestors.delete(value);
105
+ }
106
+ }
107
+
108
+ function normalizeText(text: string, depth: number, ancestors: Set<object>): NormalizedValue {
109
+ try {
110
+ const parsed: unknown = JSON.parse(text);
111
+ const normalized = normalizeValue(parsed, depth + 1, ancestors);
112
+ return {
113
+ value: normalized.value,
114
+ text,
115
+ isError: normalized.isError,
116
+ };
117
+ } catch {
118
+ return { value: text, text, isError: false };
119
+ }
120
+ }
121
+
122
+ function isMcpContent(value: unknown): value is readonly Record<string, unknown>[] {
123
+ return (
124
+ Array.isArray(value) &&
125
+ value.some(
126
+ (part) =>
127
+ part !== null &&
128
+ typeof part === "object" &&
129
+ typeof (part as { type?: unknown }).type === "string",
130
+ )
131
+ );
132
+ }
133
+
134
+ function firstMcpText(value: unknown): string | null {
135
+ if (!Array.isArray(value)) {
136
+ return null;
137
+ }
138
+ for (const part of value) {
139
+ if (
140
+ part !== null &&
141
+ typeof part === "object" &&
142
+ (part as { type?: unknown }).type === "text" &&
143
+ typeof (part as { text?: unknown }).text === "string"
144
+ ) {
145
+ return (part as { text: string }).text;
146
+ }
147
+ }
148
+ return null;
149
+ }
150
+
151
+ function isResultEnvelope(record: Record<string, unknown>): boolean {
152
+ return "result" in record && Object.keys(record).every((key) => RESULT_ENVELOPE_KEYS.has(key));
153
+ }
154
+
155
+ function safeStringify(value: unknown): string {
156
+ try {
157
+ return JSON.stringify(value) ?? "";
158
+ } catch {
159
+ return String(value);
160
+ }
161
+ }
package/src/types.ts CHANGED
@@ -272,7 +272,7 @@ export type ToolRef = {
272
272
  };
273
273
 
274
274
  export type SessionToolPolicy = {
275
- mode: "workspace_default" | "explicit" | "inherited" | "legacy";
275
+ mode: "workspace_default" | "explicit" | "inherited";
276
276
  inheritedFromSessionId: string | null;
277
277
  };
278
278
 
@@ -282,9 +282,9 @@ export type UpdateSessionToolPolicyRequest =
282
282
  expectedVersion: number;
283
283
  }
284
284
  | {
285
- /** Omitted for compatibility with the original explicit-only API. */
286
- mode?: "explicit" | undefined;
285
+ mode: "explicit";
287
286
  tools: ToolRef[];
287
+ firstPartyMcpTools: FirstPartyMcpToolName[];
288
288
  expectedVersion: number;
289
289
  };
290
290
 
@@ -496,8 +496,8 @@ export type Session = {
496
496
  resources: ResourceRef[];
497
497
  skills: SessionSkill[];
498
498
  tools: ToolRef[];
499
- toolPolicy?: SessionToolPolicy | undefined;
500
- toolPolicyVersion?: number | undefined;
499
+ toolPolicy: SessionToolPolicy;
500
+ toolPolicyVersion: number;
501
501
  effectiveToolPolicy?: SessionEffectiveToolPolicy | undefined;
502
502
  metadata: Record<string, unknown>;
503
503
  /** Frozen creator fact; later turns carry their own independent initiator. */
@@ -517,6 +517,7 @@ export type Session = {
517
517
  rigId: string | null;
518
518
  rigVersionId: string | null;
519
519
  firstPartyMcpPermissions: string[] | null;
520
+ firstPartyMcpTools: FirstPartyMcpToolName[];
520
521
  mcpServers: SessionMcpServerMetadata[];
521
522
  parentSessionId: string | null;
522
523
  /** Immutable server-authored nested-agent lineage and policy snapshot. */
@@ -758,6 +759,9 @@ export const SESSION_EVENT_TYPES = [
758
759
  "goal.continuation",
759
760
  "system.update.pending",
760
761
  "system.update.delivered",
762
+ "system.update.superseded",
763
+ "system.update.cancelled",
764
+ "system.update.settled",
761
765
  "session.control.paused",
762
766
  "session.control.resumed",
763
767
  "session.control.steer_requested",
@@ -1594,6 +1598,7 @@ export type CreateSessionRequest = {
1594
1598
  expectedNewSessionDraftRevision?: number | undefined;
1595
1599
  maxNestedAgentDepth?: number | undefined;
1596
1600
  firstPartyMcpPermissions?: string[] | undefined;
1601
+ firstPartyMcpTools?: FirstPartyMcpToolName[] | undefined;
1597
1602
  mcpServers?: SessionMcpServerInput[] | undefined;
1598
1603
  // Shared-sandbox placement (mirror of `@opengeni/contracts` CreateSessionRequest.sandbox,
1599
1604
  // addendum 05 §D.1). Three-way union; OMITTED ⇒ the context-dependent server default
@@ -1659,6 +1664,58 @@ export type KnownPermission = (typeof KNOWN_PERMISSIONS)[number];
1659
1664
  */
1660
1665
  export type Permission = KnownPermission | (string & {});
1661
1666
 
1667
+ export type FirstPartyMcpToolName =
1668
+ | "set_session_title"
1669
+ | "goal_set"
1670
+ | "goal_update"
1671
+ | "goal_complete"
1672
+ | "goal_pause"
1673
+ | "memory_search"
1674
+ | "memory_save"
1675
+ | "memory_correct"
1676
+ | "sandboxes_list"
1677
+ | "sandbox_attach"
1678
+ | "sandbox_swap"
1679
+ | "run_on"
1680
+ | "sandbox_provision"
1681
+ | "rig_list"
1682
+ | "rig_get"
1683
+ | "rig_propose_change"
1684
+ | "rig_verify"
1685
+ | "rig_promote"
1686
+ | "sessions_list"
1687
+ | "session_get"
1688
+ | "session_events"
1689
+ | "session_create"
1690
+ | "session_send_message"
1691
+ | "session_pause"
1692
+ | "session_resume"
1693
+ | "session_steer"
1694
+ | "set_other_session_title"
1695
+ | "variable_set_list"
1696
+ | "environment_list"
1697
+ | "variable_set_set_variable"
1698
+ | "environment_set_variable"
1699
+ | "github_connect_link"
1700
+ | "github_token"
1701
+ | "github_repositories_list"
1702
+ | "social_connections_list"
1703
+ | "social_posts_recent"
1704
+ | "social_daily_analysis_context"
1705
+ | "scheduled_tasks_list"
1706
+ | "scheduled_tasks_get"
1707
+ | "scheduled_tasks_create"
1708
+ | "scheduled_tasks_update"
1709
+ | "scheduled_tasks_pause"
1710
+ | "scheduled_tasks_resume"
1711
+ | "scheduled_tasks_trigger"
1712
+ | "scheduled_tasks_delete"
1713
+ | "scheduled_task_runs_list"
1714
+ | "slack_bot_list_channels"
1715
+ | "slack_bot_channel_history"
1716
+ | "slack_bot_list_users"
1717
+ | "slack_bot_post_message";
1718
+
1662
1719
  export type ProductAccessMode = "local" | "configured" | "managed";
1663
1720
 
1664
1721
  export type ModelCapabilitySupportV1 = "supported" | "unsupported" | "unknown";
@@ -2332,9 +2389,6 @@ export type ComposerDraft = {
2332
2389
  revision: number;
2333
2390
  text: string;
2334
2391
  resources: ResourceRef[];
2335
- tools: ToolRef[];
2336
- /** False inherits the session policy; true preserves an explicit array. */
2337
- toolsProvided: boolean;
2338
2392
  model: string;
2339
2393
  reasoningEffort: ReasoningEffort;
2340
2394
  sourceTurnId: string | null;
@@ -2350,6 +2404,7 @@ export type NewSessionDraftOptions = {
2350
2404
  rigId?: string | undefined;
2351
2405
  goal?: GoalSpec | undefined;
2352
2406
  firstPartyMcpPermissions?: Permission[] | undefined;
2407
+ firstPartyMcpTools?: FirstPartyMcpToolName[] | undefined;
2353
2408
  };
2354
2409
 
2355
2410
  export type NewSessionDraft = {
@@ -2371,8 +2426,20 @@ export type SessionQueueSnapshot = {
2371
2426
  /** The latest interrupted attempt has not yet durably proved physical quiescence. */
2372
2427
  stoppingPreviousAttempt: boolean;
2373
2428
  items: SessionTurn[];
2429
+ /** Canonical pending machine inputs. Events only invalidate this snapshot. */
2430
+ pendingInputs: SessionPendingInputPreview[];
2431
+ /** Exact next bounded input batch that will join an already-waiting prompt. */
2432
+ pendingInputAttachment: {
2433
+ turnId: string;
2434
+ inputIds: string[];
2435
+ } | null;
2374
2436
  };
2375
2437
 
2438
+ export type SessionPendingInputPreview = Pick<
2439
+ SessionSystemUpdate,
2440
+ "id" | "sessionId" | "kind" | "classification" | "sourceId" | "summary" | "createdAt"
2441
+ >;
2442
+
2376
2443
  export type SystemUpdateClassification = "success" | "failure" | "action_required" | "info";
2377
2444
 
2378
2445
  export type SessionSystemUpdateKind =
@@ -2384,7 +2451,6 @@ export type SessionSystemUpdateKind =
2384
2451
 
2385
2452
  export type SessionSystemUpdateState =
2386
2453
  | "pending"
2387
- | "deferred"
2388
2454
  | "delivered"
2389
2455
  | "cancelled"
2390
2456
  | "superseded"
@@ -2402,6 +2468,7 @@ export type SessionSystemUpdate = {
2402
2468
  lineage: Record<string, unknown>;
2403
2469
  state: SessionSystemUpdateState;
2404
2470
  deliveredTurnId: string | null;
2471
+ deliveredHistoryItemId: string | null;
2405
2472
  deliveredAt: string | null;
2406
2473
  createdAt: string;
2407
2474
  };
@@ -3409,6 +3476,8 @@ export type GitHubRepositoryScope = "all" | "selected";
3409
3476
 
3410
3477
  export type GitHubBindingStatus = "disabled" | "unbound" | "bound";
3411
3478
 
3479
+ export type GitHubAppSetupMode = "platform" | "operator";
3480
+
3412
3481
  export type GitHubInstallationLifecycle = "active" | "suspended" | "deleted" | "unverified";
3413
3482
 
3414
3483
  export type GitHubInstallationBinding = {
@@ -3419,6 +3488,8 @@ export type GitHubInstallationBinding = {
3419
3488
  lifecycle: GitHubInstallationLifecycle;
3420
3489
  repositoryScope: GitHubRepositoryScope;
3421
3490
  repositoryCount: number;
3491
+ /** OpenGeni-owned entry point for changing the installation's repository allowlist. */
3492
+ configureUrl: string | null;
3422
3493
  createdAt: string;
3423
3494
  updatedAt: string;
3424
3495
  };
@@ -3427,12 +3498,14 @@ export type GitHubAppInfo = {
3427
3498
  configured: boolean;
3428
3499
  /** Truthful workspace binding state; server App credentials alone are not a binding. */
3429
3500
  status: GitHubBindingStatus;
3501
+ /** Platform deployments expose installation only; operator deployments may create an App. */
3502
+ setupMode: GitHubAppSetupMode;
3430
3503
  appId: string | null;
3431
3504
  clientId: string | null;
3432
3505
  appSlug: string | null;
3433
- /** Fresh GitHub-controlled installation/configuration consent entry point. */
3506
+ /** Fresh OAuth-first existing-installation discovery and install entry point. */
3434
3507
  installUrl: string | null;
3435
- /** Compatibility alias for installUrl; no repository-admin chooser is exposed. */
3508
+ /** Compatibility alias for installUrl. */
3436
3509
  linkUrl: string | null;
3437
3510
  /** Installation bindings owned independently by this workspace. */
3438
3511
  installations: GitHubInstallationBinding[];
@@ -3547,7 +3620,6 @@ export type UserMessageEventInput = {
3547
3620
  text: string;
3548
3621
  turnInstructions?: string | undefined;
3549
3622
  resources?: ResourceRef[] | undefined;
3550
- tools?: ToolRef[] | undefined;
3551
3623
  model?: string | undefined;
3552
3624
  reasoningEffort?: ReasoningEffort | undefined;
3553
3625
  mcpCredentialUpdates?: SessionMcpCredentialUpdateInput[] | undefined;