@opengeni/sdk 0.13.0 → 0.15.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/README.md +33 -11
- package/dist/index.d.ts +171 -59
- package/dist/index.js +360 -190
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +167 -53
- package/src/errors.ts +13 -0
- package/src/index.ts +26 -2
- package/src/stream.ts +3 -0
- package/src/types.ts +137 -39
- package/src/workspace-control-stream.ts +101 -0
package/src/types.ts
CHANGED
|
@@ -10,7 +10,6 @@ export type SessionStatus =
|
|
|
10
10
|
| "requires_action"
|
|
11
11
|
| "recovering"
|
|
12
12
|
| "waiting_capacity"
|
|
13
|
-
| "paused"
|
|
14
13
|
| "failed"
|
|
15
14
|
| "cancelled";
|
|
16
15
|
|
|
@@ -400,12 +399,7 @@ export type Session = {
|
|
|
400
399
|
queueVersion: number;
|
|
401
400
|
queueHeadPosition: number;
|
|
402
401
|
queueTailPosition: number;
|
|
403
|
-
|
|
404
|
-
controlGeneration: number;
|
|
405
|
-
controlReason: string | null;
|
|
406
|
-
controlChangedBy: string | null;
|
|
407
|
-
controlChangedAt: string | null;
|
|
408
|
-
workspaceRunExceptionGeneration: number | null;
|
|
402
|
+
effectiveControl: EffectiveSessionControl;
|
|
409
403
|
lastSequence: number;
|
|
410
404
|
/** Multi-account Codex (P1): the account this session is pinned to (null ⇒ follow workspace active). */
|
|
411
405
|
codexPinnedCredentialId?: string | null;
|
|
@@ -467,7 +461,8 @@ export type SessionTurnStatus =
|
|
|
467
461
|
| "completed"
|
|
468
462
|
| "failed"
|
|
469
463
|
| "cancelled"
|
|
470
|
-
| "superseded"
|
|
464
|
+
| "superseded"
|
|
465
|
+
| "withdrawn_for_edit";
|
|
471
466
|
|
|
472
467
|
export type SessionTurnSource =
|
|
473
468
|
| "user"
|
|
@@ -556,6 +551,7 @@ export const SESSION_EVENT_TYPES = [
|
|
|
556
551
|
"session.control.steer_requested",
|
|
557
552
|
"workspace.inference.paused",
|
|
558
553
|
"workspace.inference.resumed",
|
|
554
|
+
"session.queue.changed",
|
|
559
555
|
"session.queue.prompt.cancelled",
|
|
560
556
|
"session.queue.history",
|
|
561
557
|
"turn.event.rejected_late",
|
|
@@ -1355,6 +1351,11 @@ export type ClientAuthConfig =
|
|
|
1355
1351
|
| { mode: "configuredToken"; headerName: "authorization"; scheme: "bearer" }
|
|
1356
1352
|
| { mode: "managedSession"; session: "cookie" };
|
|
1357
1353
|
|
|
1354
|
+
// Kept value-identical to @opengeni/contracts and pinned by the SDK contract
|
|
1355
|
+
// parity suite. The SDK has no runtime dependency on the Zod contracts package.
|
|
1356
|
+
export const OPENGENI_API_CONTRACT_REVISION = "2026-07-session-control-v1" as const;
|
|
1357
|
+
export const OPENGENI_API_CONTRACT_HEADER = "x-opengeni-api-contract" as const;
|
|
1358
|
+
|
|
1358
1359
|
/**
|
|
1359
1360
|
* Public, unauthenticated-by-default client bootstrap config returned by
|
|
1360
1361
|
* `GET /v1/config/client`: which models + reasoning efforts are exposed, the
|
|
@@ -1364,6 +1365,8 @@ export type ClientAuthConfig =
|
|
|
1364
1365
|
*/
|
|
1365
1366
|
export type ClientConfig = {
|
|
1366
1367
|
deploymentRevision: string;
|
|
1368
|
+
apiContractRevision: typeof OPENGENI_API_CONTRACT_REVISION;
|
|
1369
|
+
serverVersion?: string | undefined;
|
|
1367
1370
|
defaultModel: string;
|
|
1368
1371
|
allowedModels: string[];
|
|
1369
1372
|
models: ClientModel[];
|
|
@@ -1419,11 +1422,13 @@ export type Workspace = {
|
|
|
1419
1422
|
externalId: string | null;
|
|
1420
1423
|
agentInstructions: string | null;
|
|
1421
1424
|
settings: Record<string, unknown>;
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1425
|
+
inferenceControl: {
|
|
1426
|
+
state: "active" | "paused";
|
|
1427
|
+
revision: number;
|
|
1428
|
+
reason: string | null;
|
|
1429
|
+
changedBy: string | null;
|
|
1430
|
+
changedAt: string | null;
|
|
1431
|
+
};
|
|
1427
1432
|
defaultRigId?: string | null;
|
|
1428
1433
|
createdAt: string;
|
|
1429
1434
|
updatedAt: string;
|
|
@@ -1561,29 +1566,83 @@ export type CompactSessionContextResult = {
|
|
|
1561
1566
|
|
|
1562
1567
|
// --- Turn queue --------------------------------------------------------------
|
|
1563
1568
|
|
|
1569
|
+
export type EffectiveControlBlocker = {
|
|
1570
|
+
kind: "session" | "workspace";
|
|
1571
|
+
sessionId?: string | undefined;
|
|
1572
|
+
displayName: string;
|
|
1573
|
+
actor: string | null;
|
|
1574
|
+
reason: string | null;
|
|
1575
|
+
changedAt: string | null;
|
|
1576
|
+
revision: number;
|
|
1577
|
+
};
|
|
1578
|
+
|
|
1579
|
+
export type EffectiveControlResumeOption = {
|
|
1580
|
+
scope: "selected" | "session" | "workspace";
|
|
1581
|
+
targetId?: string | undefined;
|
|
1582
|
+
selectedStateAfter: "active" | "paused";
|
|
1583
|
+
remainingPrimaryBlocker?: EffectiveControlBlocker | undefined;
|
|
1584
|
+
impactCopy: string;
|
|
1585
|
+
};
|
|
1586
|
+
|
|
1587
|
+
export type EffectiveSessionControl = {
|
|
1588
|
+
state: "active" | "paused";
|
|
1589
|
+
controlVersion: number;
|
|
1590
|
+
controlEtag: string;
|
|
1591
|
+
directState: "active" | "paused";
|
|
1592
|
+
primaryBlocker: EffectiveControlBlocker | null;
|
|
1593
|
+
additionalBlockerCount: number;
|
|
1594
|
+
blockers: EffectiveControlBlocker[];
|
|
1595
|
+
resumeOptions: EffectiveControlResumeOption[];
|
|
1596
|
+
override: { rootSessionId: string; revision: number } | null;
|
|
1597
|
+
settlement: { state: "stopping"; attemptCount: number } | null;
|
|
1598
|
+
};
|
|
1599
|
+
|
|
1600
|
+
export type SessionCommandReceipt = {
|
|
1601
|
+
id: string;
|
|
1602
|
+
action: string;
|
|
1603
|
+
operationKey: string;
|
|
1604
|
+
targetSessionId: string | null;
|
|
1605
|
+
targetTurnId: string | null;
|
|
1606
|
+
appliedControlRevision: number | null;
|
|
1607
|
+
appliedQueueVersion: number | null;
|
|
1608
|
+
appliedTurnVersion: number | null;
|
|
1609
|
+
appliedDraftRevision: number | null;
|
|
1610
|
+
createdAt: string;
|
|
1611
|
+
};
|
|
1612
|
+
|
|
1613
|
+
export type ComposerDraft = {
|
|
1614
|
+
revision: number;
|
|
1615
|
+
text: string;
|
|
1616
|
+
resources: ResourceRef[];
|
|
1617
|
+
tools: ToolRef[];
|
|
1618
|
+
model: string;
|
|
1619
|
+
reasoningEffort: ReasoningEffort;
|
|
1620
|
+
sourceTurnId: string | null;
|
|
1621
|
+
sourceTurnVersion: number | null;
|
|
1622
|
+
updatedAt: string | null;
|
|
1623
|
+
};
|
|
1624
|
+
|
|
1564
1625
|
export type SessionQueueSnapshot = {
|
|
1565
1626
|
version: number;
|
|
1566
|
-
|
|
1567
|
-
controlGeneration: number;
|
|
1568
|
-
workspaceInferenceState: "active" | "paused";
|
|
1569
|
-
workspaceInferenceGeneration: number;
|
|
1570
|
-
workspaceRunExceptionGeneration: number | null;
|
|
1627
|
+
effectiveControl: EffectiveSessionControl;
|
|
1571
1628
|
items: SessionTurn[];
|
|
1572
1629
|
};
|
|
1573
1630
|
|
|
1574
1631
|
export type SystemUpdateClassification = "success" | "failure" | "action_required" | "info";
|
|
1575
1632
|
|
|
1576
1633
|
export type SessionSystemUpdateKind =
|
|
1577
|
-
| "
|
|
1578
|
-
| "
|
|
1579
|
-
| "
|
|
1580
|
-
| "
|
|
1634
|
+
| "scheduled_occurrence"
|
|
1635
|
+
| "goal_continuation"
|
|
1636
|
+
| "agent_message"
|
|
1637
|
+
| "agent_steer_instruction"
|
|
1638
|
+
| "child_terminal_result";
|
|
1581
1639
|
|
|
1582
1640
|
export type SessionSystemUpdateState =
|
|
1583
1641
|
| "pending"
|
|
1584
1642
|
| "deferred"
|
|
1585
1643
|
| "delivered"
|
|
1586
1644
|
| "cancelled"
|
|
1645
|
+
| "superseded"
|
|
1587
1646
|
| "failed";
|
|
1588
1647
|
|
|
1589
1648
|
export type SessionSystemUpdate = {
|
|
@@ -1603,33 +1662,72 @@ export type SessionSystemUpdate = {
|
|
|
1603
1662
|
};
|
|
1604
1663
|
|
|
1605
1664
|
export type SessionControlResponse = {
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
expectedActiveTurnId: string | null;
|
|
1611
|
-
expectedExecutionGeneration: number | null;
|
|
1612
|
-
expectedAttemptId: string | null;
|
|
1613
|
-
deliveryEventId: string | null;
|
|
1614
|
-
shouldSignalControl: boolean;
|
|
1615
|
-
shouldWake: boolean;
|
|
1665
|
+
receipt: SessionCommandReceipt;
|
|
1666
|
+
effectiveControl: EffectiveSessionControl;
|
|
1667
|
+
interruptionCount: number;
|
|
1668
|
+
wakeCount: number;
|
|
1616
1669
|
};
|
|
1617
1670
|
|
|
1618
1671
|
export type WorkspaceInferenceControlResponse = {
|
|
1619
|
-
|
|
1672
|
+
receipt: SessionCommandReceipt;
|
|
1620
1673
|
state: "active" | "paused";
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1674
|
+
revision: number;
|
|
1675
|
+
interruptionCount: number;
|
|
1676
|
+
wakeCount: number;
|
|
1677
|
+
};
|
|
1678
|
+
|
|
1679
|
+
export type WorkspaceControlEvent = {
|
|
1680
|
+
id: string;
|
|
1681
|
+
workspaceId: string;
|
|
1682
|
+
/** Same monotonic value as revision; named sequence for SSE resume cursors. */
|
|
1683
|
+
sequence: number;
|
|
1684
|
+
revision: number;
|
|
1685
|
+
type: "workspace.control.changed";
|
|
1686
|
+
scope: "workspace" | "session";
|
|
1687
|
+
rootSessionId: string | null;
|
|
1688
|
+
action: "pause" | "resume";
|
|
1689
|
+
automatic: boolean;
|
|
1690
|
+
reason: string | null;
|
|
1691
|
+
actor: string;
|
|
1692
|
+
occurredAt: string;
|
|
1625
1693
|
};
|
|
1626
1694
|
|
|
1627
1695
|
export type SessionQueueMutationResponse = {
|
|
1696
|
+
receipt: SessionCommandReceipt;
|
|
1628
1697
|
snapshot: SessionQueueSnapshot;
|
|
1629
|
-
|
|
1630
|
-
shouldWake: boolean;
|
|
1698
|
+
draft?: ComposerDraft;
|
|
1631
1699
|
};
|
|
1632
1700
|
|
|
1701
|
+
export type MoveSessionQueueItemRequest = {
|
|
1702
|
+
clientEventId: string;
|
|
1703
|
+
expectedQueueVersion: number;
|
|
1704
|
+
beforeTurnId: string | null;
|
|
1705
|
+
};
|
|
1706
|
+
|
|
1707
|
+
export type EditSessionQueueItemRequest = {
|
|
1708
|
+
clientEventId: string;
|
|
1709
|
+
expectedTurnVersion: number;
|
|
1710
|
+
expectedDraftRevision: number;
|
|
1711
|
+
replaceDraft: boolean;
|
|
1712
|
+
};
|
|
1713
|
+
|
|
1714
|
+
export type SteerSessionQueueItemRequest = {
|
|
1715
|
+
clientEventId: string;
|
|
1716
|
+
expectedTurnVersion: number;
|
|
1717
|
+
controlEtag?: string;
|
|
1718
|
+
};
|
|
1719
|
+
|
|
1720
|
+
export type DeleteSessionQueueItemRequest = {
|
|
1721
|
+
clientEventId: string;
|
|
1722
|
+
expectedTurnVersion: number;
|
|
1723
|
+
reason?: string;
|
|
1724
|
+
};
|
|
1725
|
+
|
|
1726
|
+
export type SaveComposerDraftRequest = Omit<
|
|
1727
|
+
ComposerDraft,
|
|
1728
|
+
"revision" | "sourceTurnId" | "sourceTurnVersion" | "updatedAt"
|
|
1729
|
+
> & { expectedRevision: number };
|
|
1730
|
+
|
|
1633
1731
|
// --- Scheduled tasks: requests + runs ----------------------------------------
|
|
1634
1732
|
|
|
1635
1733
|
/** Input shape for agent config on create/update (server applies defaults). */
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { isAbortError, isRetryableStreamError, OpenGeniStreamError } from "./errors";
|
|
2
|
+
import { parseSseStream } from "./sse";
|
|
3
|
+
import type { StreamSessionEventsOptions } from "./stream";
|
|
4
|
+
import type { WorkspaceControlEvent } from "./types";
|
|
5
|
+
|
|
6
|
+
export type WorkspaceControlStreamTransport = {
|
|
7
|
+
/** The server replays every durable event after the cursor before going live. */
|
|
8
|
+
openStream: (
|
|
9
|
+
after: number,
|
|
10
|
+
signal: AbortSignal | undefined,
|
|
11
|
+
) => Promise<ReadableStream<Uint8Array>>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Reconnecting workspace invalidation stream. Control revisions are monotonic
|
|
16
|
+
* but can begin above one after the one-way migration, so unlike conversation
|
|
17
|
+
* events this stream intentionally permits sparse sequence values.
|
|
18
|
+
*/
|
|
19
|
+
export async function* streamWorkspaceControlEvents(
|
|
20
|
+
transport: WorkspaceControlStreamTransport,
|
|
21
|
+
options: StreamSessionEventsOptions = {},
|
|
22
|
+
): AsyncGenerator<WorkspaceControlEvent, void, void> {
|
|
23
|
+
const signal = options.signal;
|
|
24
|
+
const reconnect = options.reconnect ?? true;
|
|
25
|
+
const baseDelayMs = options.reconnectDelayMs ?? 500;
|
|
26
|
+
const maxDelayMs = options.maxReconnectDelayMs ?? 10_000;
|
|
27
|
+
const maxAttempts = options.maxReconnectAttempts ?? Number.POSITIVE_INFINITY;
|
|
28
|
+
let cursor = options.after ?? 0;
|
|
29
|
+
let failures = 0;
|
|
30
|
+
let delayMs = baseDelayMs;
|
|
31
|
+
let everConnected = false;
|
|
32
|
+
|
|
33
|
+
for (;;) {
|
|
34
|
+
if (signal?.aborted) return;
|
|
35
|
+
options.onStateChange?.(everConnected || failures > 0 ? "reconnecting" : "connecting");
|
|
36
|
+
const cursorAtOpen = cursor;
|
|
37
|
+
try {
|
|
38
|
+
const body = await transport.openStream(cursor, signal);
|
|
39
|
+
everConnected = true;
|
|
40
|
+
failures = 0;
|
|
41
|
+
delayMs = baseDelayMs;
|
|
42
|
+
await options.beforeLive?.();
|
|
43
|
+
options.onStateChange?.("live");
|
|
44
|
+
for await (const message of parseSseStream(body)) {
|
|
45
|
+
if (signal?.aborted) return;
|
|
46
|
+
const event = parseWorkspaceControlEvent(message.data);
|
|
47
|
+
if (!event || event.sequence <= cursor) continue;
|
|
48
|
+
cursor = event.sequence;
|
|
49
|
+
yield event;
|
|
50
|
+
}
|
|
51
|
+
if (!reconnect) return;
|
|
52
|
+
if (cursor === cursorAtOpen) await sleep(baseDelayMs, signal);
|
|
53
|
+
continue;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if (signal?.aborted || isAbortError(error)) return;
|
|
56
|
+
if (!reconnect || !isRetryableStreamError(error)) throw error;
|
|
57
|
+
failures += 1;
|
|
58
|
+
if (failures > maxAttempts) {
|
|
59
|
+
throw new OpenGeniStreamError(
|
|
60
|
+
`workspace control stream gave up after ${maxAttempts} reconnect attempts: ${
|
|
61
|
+
error instanceof Error ? error.message : String(error)
|
|
62
|
+
}`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
await sleep(delayMs, signal);
|
|
67
|
+
delayMs = Math.min(Math.max(delayMs * 2, baseDelayMs), maxDelayMs);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function parseWorkspaceControlEvent(data: string): WorkspaceControlEvent | null {
|
|
72
|
+
let value: unknown;
|
|
73
|
+
try {
|
|
74
|
+
value = JSON.parse(data);
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
if (
|
|
79
|
+
typeof value !== "object" ||
|
|
80
|
+
value === null ||
|
|
81
|
+
(value as { type?: unknown }).type !== "workspace.control.changed" ||
|
|
82
|
+
typeof (value as { id?: unknown }).id !== "string" ||
|
|
83
|
+
typeof (value as { sequence?: unknown }).sequence !== "number"
|
|
84
|
+
) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
return value as WorkspaceControlEvent;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function sleep(delayMs: number, signal: AbortSignal | undefined): Promise<void> {
|
|
91
|
+
if (signal?.aborted || delayMs <= 0) return;
|
|
92
|
+
await new Promise<void>((resolve) => {
|
|
93
|
+
const timer = setTimeout(done, delayMs);
|
|
94
|
+
function done() {
|
|
95
|
+
clearTimeout(timer);
|
|
96
|
+
signal?.removeEventListener("abort", done);
|
|
97
|
+
resolve();
|
|
98
|
+
}
|
|
99
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
100
|
+
});
|
|
101
|
+
}
|