@playcraft/common 0.0.59 → 0.1.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.
Files changed (30) hide show
  1. package/dist/cloud-workspace/runtime/chat-realtime-admission.d.ts +53 -0
  2. package/dist/cloud-workspace/runtime/chat-realtime-admission.d.ts.map +1 -0
  3. package/dist/cloud-workspace/runtime/chat-realtime-admission.js +95 -0
  4. package/dist/cloud-workspace/runtime/chat-realtime-admission.js.map +1 -0
  5. package/dist/cloud-workspace/runtime/chat-realtime.d.ts +197 -0
  6. package/dist/cloud-workspace/runtime/chat-realtime.d.ts.map +1 -0
  7. package/dist/cloud-workspace/runtime/chat-realtime.js +299 -0
  8. package/dist/cloud-workspace/runtime/chat-realtime.js.map +1 -0
  9. package/dist/cloud-workspace/runtime/errors.d.ts +1 -1
  10. package/dist/cloud-workspace/runtime/index.d.ts +5 -2
  11. package/dist/cloud-workspace/runtime/index.d.ts.map +1 -1
  12. package/dist/cloud-workspace/runtime/index.js +5 -2
  13. package/dist/cloud-workspace/runtime/index.js.map +1 -1
  14. package/dist/cloud-workspace/runtime/operations.d.ts +11 -0
  15. package/dist/cloud-workspace/runtime/operations.d.ts.map +1 -1
  16. package/dist/cloud-workspace/runtime/operations.js +15 -1
  17. package/dist/cloud-workspace/runtime/operations.js.map +1 -1
  18. package/dist/cloud-workspace/runtime/terminal-session.d.ts +78 -0
  19. package/dist/cloud-workspace/runtime/terminal-session.d.ts.map +1 -0
  20. package/dist/cloud-workspace/runtime/terminal-session.js +178 -0
  21. package/dist/cloud-workspace/runtime/terminal-session.js.map +1 -0
  22. package/dist/cloud-workspace/runtime/terminals.d.ts +36 -1
  23. package/dist/cloud-workspace/runtime/terminals.d.ts.map +1 -1
  24. package/dist/cloud-workspace/runtime/terminals.js +46 -1
  25. package/dist/cloud-workspace/runtime/terminals.js.map +1 -1
  26. package/dist/database/operators.d.ts +2 -0
  27. package/dist/database/operators.d.ts.map +1 -0
  28. package/dist/database/operators.js +2 -0
  29. package/dist/database/operators.js.map +1 -0
  30. package/package.json +5 -1
@@ -0,0 +1,53 @@
1
+ import { CHAT_REALTIME_V2 } from './chat-realtime.js';
2
+ /**
3
+ * Section 9 rows. A logical runtime may advertise chatRealtimeV2 only when every row
4
+ * passed and the section 1 measurement conditions were actually met.
5
+ */
6
+ export declare const CHAT_REALTIME_ACCEPTANCE_ROWS: readonly ["paced_output", "archive_delay", "approval_and_stop", "early_notification_crash", "durable_before_live", "out_of_order_prefix", "refresh_snapshot", "non_owner_replica", "slow_subscriber", "ten_thousand_fragments", "revoke_and_disconnect", "crash_and_loss", "sqlite_commit_boundaries", "checkpoint_pin_disk", "native_jsonl_and_migration", "sandbox_disk_engine", "entry_proxy"];
7
+ export type ChatRealtimeAcceptanceRow = (typeof CHAT_REALTIME_ACCEPTANCE_ROWS)[number];
8
+ export type ChatRealtimeRowStatus = 'passed' | 'failed' | 'untested';
9
+ export type ChatRealtimeAdmissionInput = {
10
+ logicalRuntimeId: string;
11
+ rows: Record<ChatRealtimeAcceptanceRow, ChatRealtimeRowStatus>;
12
+ networkBudgetMet: boolean;
13
+ percentileSampleMet: boolean;
14
+ browserPaintObserved: boolean;
15
+ fixedCodexVersion: string | null;
16
+ protocolMinor: number;
17
+ replacesCursorSemantics: boolean;
18
+ deletesUnconfirmedSources: boolean;
19
+ };
20
+ export type ChatRealtimeAdmission = {
21
+ advertise: true;
22
+ mode: typeof CHAT_REALTIME_V2;
23
+ logicalRuntimeId: string;
24
+ } | {
25
+ advertise: false;
26
+ code: 'chat_realtime_unavailable';
27
+ logicalRuntimeId: string;
28
+ blockedBy: string[];
29
+ };
30
+ export type RealtimeTurnAdmission = {
31
+ ok: true;
32
+ mode: 'conversation' | typeof CHAT_REALTIME_V2;
33
+ } | {
34
+ ok: false;
35
+ code: 'chat_realtime_unavailable';
36
+ history: 'read';
37
+ fallback: 'none';
38
+ };
39
+ export declare function closedChatRealtimeAdmission(logicalRuntimeId: string): ChatRealtimeAdmissionInput;
40
+ /** Production advertisement. There is no environment override. */
41
+ export declare function advertisedChatModes(logicalRuntimeId: string): readonly string[];
42
+ export declare function mayAdvertiseChatRealtime(input: ChatRealtimeAdmissionInput): ChatRealtimeAdmission;
43
+ /**
44
+ * A requested realtime upload is refused when this logical runtime does not advertise it.
45
+ * Refusal keeps history reads and does not select a whole-turn upload mode.
46
+ * An omitted upload stays on the existing conversation path.
47
+ */
48
+ export declare function admitRealtimeTurn(input: {
49
+ requestedUpload?: string;
50
+ localModes: readonly string[];
51
+ remoteModes: readonly string[];
52
+ }): RealtimeTurnAdmission;
53
+ //# sourceMappingURL=chat-realtime-admission.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-realtime-admission.d.ts","sourceRoot":"","sources":["../../../src/cloud-workspace/runtime/chat-realtime-admission.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAyB,MAAM,oBAAoB,CAAC;AAE7E;;;GAGG;AACH,eAAO,MAAM,6BAA6B,mYAkBhC,CAAC;AAEX,MAAM,MAAM,yBAAyB,GAAG,CAAC,OAAO,6BAA6B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvF,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC;AAErE,MAAM,MAAM,0BAA0B,GAAG;IACvC,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC,yBAAyB,EAAE,qBAAqB,CAAC,CAAC;IAC/D,gBAAgB,EAAE,OAAO,CAAC;IAC1B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,OAAO,CAAC;IACjC,yBAAyB,EAAE,OAAO,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAC7B;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,gBAAgB,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAE,GAC5E;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,2BAA2B,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAE3G,MAAM,MAAM,qBAAqB,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,cAAc,GAAG,OAAO,gBAAgB,CAAA;CAAE,GAC5D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,2BAA2B,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAExF,wBAAgB,2BAA2B,CAAC,gBAAgB,EAAE,MAAM,GAAG,0BAA0B,CAehG;AAED,kEAAkE;AAClE,wBAAgB,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAG/E;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,0BAA0B,GAAG,qBAAqB,CAsBjG;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC,GAAG,qBAAqB,CAYxB"}
@@ -0,0 +1,95 @@
1
+ import { CLOUD_WORKSPACE_PROTOCOL_MINOR } from './protocol.js';
2
+ import { CHAT_REALTIME_V2, negotiateChatRealtime } from './chat-realtime.js';
3
+ /**
4
+ * Section 9 rows. A logical runtime may advertise chatRealtimeV2 only when every row
5
+ * passed and the section 1 measurement conditions were actually met.
6
+ */
7
+ export const CHAT_REALTIME_ACCEPTANCE_ROWS = [
8
+ 'paced_output',
9
+ 'archive_delay',
10
+ 'approval_and_stop',
11
+ 'early_notification_crash',
12
+ 'durable_before_live',
13
+ 'out_of_order_prefix',
14
+ 'refresh_snapshot',
15
+ 'non_owner_replica',
16
+ 'slow_subscriber',
17
+ 'ten_thousand_fragments',
18
+ 'revoke_and_disconnect',
19
+ 'crash_and_loss',
20
+ 'sqlite_commit_boundaries',
21
+ 'checkpoint_pin_disk',
22
+ 'native_jsonl_and_migration',
23
+ 'sandbox_disk_engine',
24
+ 'entry_proxy',
25
+ ];
26
+ export function closedChatRealtimeAdmission(logicalRuntimeId) {
27
+ const rows = Object.fromEntries(CHAT_REALTIME_ACCEPTANCE_ROWS.map((row) => [row, 'untested']));
28
+ return {
29
+ logicalRuntimeId,
30
+ rows,
31
+ networkBudgetMet: false,
32
+ percentileSampleMet: false,
33
+ browserPaintObserved: false,
34
+ fixedCodexVersion: null,
35
+ protocolMinor: CLOUD_WORKSPACE_PROTOCOL_MINOR,
36
+ replacesCursorSemantics: false,
37
+ deletesUnconfirmedSources: false,
38
+ };
39
+ }
40
+ /** Production advertisement. There is no environment override. */
41
+ export function advertisedChatModes(logicalRuntimeId) {
42
+ const decision = mayAdvertiseChatRealtime(closedChatRealtimeAdmission(logicalRuntimeId));
43
+ return decision.advertise ? [decision.mode] : [];
44
+ }
45
+ export function mayAdvertiseChatRealtime(input) {
46
+ const blockedBy = [];
47
+ if (!input.logicalRuntimeId.trim())
48
+ blockedBy.push('logical_runtime');
49
+ for (const row of CHAT_REALTIME_ACCEPTANCE_ROWS) {
50
+ if (input.rows[row] !== 'passed')
51
+ blockedBy.push(row);
52
+ }
53
+ if (!input.networkBudgetMet)
54
+ blockedBy.push('network_budget');
55
+ if (!input.percentileSampleMet)
56
+ blockedBy.push('percentile_sample');
57
+ if (!input.browserPaintObserved)
58
+ blockedBy.push('browser_paint');
59
+ if (!input.fixedCodexVersion?.trim())
60
+ blockedBy.push('fixed_codex_version');
61
+ if (input.protocolMinor !== CLOUD_WORKSPACE_PROTOCOL_MINOR)
62
+ blockedBy.push('protocol_minor');
63
+ if (input.replacesCursorSemantics)
64
+ blockedBy.push('cursor_semantics');
65
+ if (input.deletesUnconfirmedSources)
66
+ blockedBy.push('unconfirmed_sources');
67
+ if (blockedBy.length > 0) {
68
+ return {
69
+ advertise: false,
70
+ code: 'chat_realtime_unavailable',
71
+ logicalRuntimeId: input.logicalRuntimeId,
72
+ blockedBy,
73
+ };
74
+ }
75
+ return { advertise: true, mode: CHAT_REALTIME_V2, logicalRuntimeId: input.logicalRuntimeId };
76
+ }
77
+ /**
78
+ * A requested realtime upload is refused when this logical runtime does not advertise it.
79
+ * Refusal keeps history reads and does not select a whole-turn upload mode.
80
+ * An omitted upload stays on the existing conversation path.
81
+ */
82
+ export function admitRealtimeTurn(input) {
83
+ if (input.requestedUpload === undefined || input.requestedUpload === 'conversation') {
84
+ return { ok: true, mode: 'conversation' };
85
+ }
86
+ if (input.requestedUpload !== CHAT_REALTIME_V2) {
87
+ return { ok: false, code: 'chat_realtime_unavailable', history: 'read', fallback: 'none' };
88
+ }
89
+ const negotiated = negotiateChatRealtime({ local: input.localModes, remote: input.remoteModes });
90
+ if (!negotiated.ok) {
91
+ return { ok: false, code: 'chat_realtime_unavailable', history: 'read', fallback: 'none' };
92
+ }
93
+ return { ok: true, mode: CHAT_REALTIME_V2 };
94
+ }
95
+ //# sourceMappingURL=chat-realtime-admission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-realtime-admission.js","sourceRoot":"","sources":["../../../src/cloud-workspace/runtime/chat-realtime-admission.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,197 @@
1
+ import { z } from 'zod';
2
+ export declare const CHAT_REALTIME_V2: "chatRealtimeV2";
3
+ export type ChatRealtimeNegotiation = {
4
+ ok: true;
5
+ mode: typeof CHAT_REALTIME_V2;
6
+ } | {
7
+ ok: false;
8
+ code: 'chat_realtime_unavailable';
9
+ };
10
+ /** Both sides must advertise the mode. Absence keeps history reads and refuses a new realtime turn. */
11
+ export declare function negotiateChatRealtime(input: {
12
+ local: readonly string[];
13
+ remote: readonly string[];
14
+ }): ChatRealtimeNegotiation;
15
+ export declare const TURN_STATUSES: readonly ["starting", "running", "waiting_for_user", "completed", "failed", "interrupted"];
16
+ export type TurnStatus = (typeof TURN_STATUSES)[number];
17
+ export declare function canTransitionTurn(from: TurnStatus, to: TurnStatus): boolean;
18
+ export declare const SAVE_STATUSES: readonly ["syncing", "delayed", "saved", "gap"];
19
+ export type SaveStatus = (typeof SAVE_STATUSES)[number];
20
+ export declare function canTransitionSave(from: SaveStatus, to: SaveStatus): boolean;
21
+ export declare const replayPinEpochSchema: z.ZodObject<{
22
+ runtimeId: z.ZodString;
23
+ generation: z.ZodNumber;
24
+ runtimeEpoch: z.ZodNumber;
25
+ retainedFrom: z.ZodNumber;
26
+ highWater: z.ZodNumber;
27
+ }, z.core.$strict>;
28
+ export type ReplayPinEpoch = z.infer<typeof replayPinEpochSchema>;
29
+ export declare const replayPinResultSchema: z.ZodObject<{
30
+ pinId: z.ZodString;
31
+ epochs: z.ZodArray<z.ZodObject<{
32
+ runtimeId: z.ZodString;
33
+ generation: z.ZodNumber;
34
+ runtimeEpoch: z.ZodNumber;
35
+ retainedFrom: z.ZodNumber;
36
+ highWater: z.ZodNumber;
37
+ }, z.core.$strict>>;
38
+ followBoundary: z.ZodObject<{
39
+ runtimeId: z.ZodString;
40
+ generation: z.ZodNumber;
41
+ runtimeEpoch: z.ZodNumber;
42
+ sourceSequence: z.ZodNumber;
43
+ }, z.core.$strict>;
44
+ }, z.core.$strict>;
45
+ export type ReplayPinResult = z.infer<typeof replayPinResultSchema>;
46
+ export declare const sourceAckEpochSchema: z.ZodObject<{
47
+ runtimeId: z.ZodString;
48
+ generation: z.ZodNumber;
49
+ runtimeEpoch: z.ZodNumber;
50
+ confirmedThrough: z.ZodNumber;
51
+ }, z.core.$strict>;
52
+ export declare const sourceAckSchema: z.ZodObject<{
53
+ leaseFence: z.ZodNumber;
54
+ epochs: z.ZodArray<z.ZodObject<{
55
+ runtimeId: z.ZodString;
56
+ generation: z.ZodNumber;
57
+ runtimeEpoch: z.ZodNumber;
58
+ confirmedThrough: z.ZodNumber;
59
+ }, z.core.$strict>>;
60
+ }, z.core.$strict>;
61
+ export type SourceAck = z.infer<typeof sourceAckSchema>;
62
+ export declare function isMonotonicAck(previous: number, next: number): boolean;
63
+ /** Retained lower bound past the cloud coverage means that prefix can no longer be read. */
64
+ export declare function retainedRangeHasGap(retainedFrom: number, coveredThrough: number): boolean;
65
+ export declare const liveChatFrameSchema: z.ZodObject<{
66
+ source: z.ZodObject<{
67
+ runtimeId: z.ZodString;
68
+ generation: z.ZodNumber;
69
+ runtimeEpoch: z.ZodNumber;
70
+ sourceSequence: z.ZodNumber;
71
+ }, z.core.$strict>;
72
+ sessionId: z.ZodOptional<z.ZodString>;
73
+ workspaceId: z.ZodOptional<z.ZodString>;
74
+ operationId: z.ZodOptional<z.ZodString>;
75
+ type: z.ZodString;
76
+ payload: z.ZodUnknown;
77
+ occurredAt: z.ZodString;
78
+ payloadHash: z.ZodString;
79
+ kind: z.ZodLiteral<"live">;
80
+ persistence: z.ZodLiteral<"local">;
81
+ }, z.core.$strict>;
82
+ export type LiveChatFrame = z.infer<typeof liveChatFrameSchema>;
83
+ export declare const durableChatFrameSchema: z.ZodObject<{
84
+ source: z.ZodObject<{
85
+ runtimeId: z.ZodString;
86
+ generation: z.ZodNumber;
87
+ runtimeEpoch: z.ZodNumber;
88
+ sourceSequence: z.ZodNumber;
89
+ }, z.core.$strict>;
90
+ sessionId: z.ZodOptional<z.ZodString>;
91
+ workspaceId: z.ZodOptional<z.ZodString>;
92
+ operationId: z.ZodOptional<z.ZodString>;
93
+ type: z.ZodString;
94
+ payload: z.ZodUnknown;
95
+ occurredAt: z.ZodString;
96
+ payloadHash: z.ZodString;
97
+ kind: z.ZodLiteral<"durable">;
98
+ streamKind: z.ZodEnum<{
99
+ session: "session";
100
+ runtime: "runtime";
101
+ workspace: "workspace";
102
+ }>;
103
+ streamId: z.ZodString;
104
+ sequence: z.ZodNumber;
105
+ }, z.core.$strict>;
106
+ export type DurableChatFrame = z.infer<typeof durableChatFrameSchema>;
107
+ export declare const chatControlFrameSchema: z.ZodObject<{
108
+ kind: z.ZodLiteral<"control">;
109
+ event: z.ZodEnum<{
110
+ "source.ready": "source.ready";
111
+ "stream.ready": "stream.ready";
112
+ "stream.gap": "stream.gap";
113
+ "save.delayed": "save.delayed";
114
+ }>;
115
+ }, z.core.$strict>;
116
+ export type ChatControlFrame = z.infer<typeof chatControlFrameSchema>;
117
+ export declare const browserChatFrameSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
118
+ source: z.ZodObject<{
119
+ runtimeId: z.ZodString;
120
+ generation: z.ZodNumber;
121
+ runtimeEpoch: z.ZodNumber;
122
+ sourceSequence: z.ZodNumber;
123
+ }, z.core.$strict>;
124
+ sessionId: z.ZodOptional<z.ZodString>;
125
+ workspaceId: z.ZodOptional<z.ZodString>;
126
+ operationId: z.ZodOptional<z.ZodString>;
127
+ type: z.ZodString;
128
+ payload: z.ZodUnknown;
129
+ occurredAt: z.ZodString;
130
+ payloadHash: z.ZodString;
131
+ kind: z.ZodLiteral<"live">;
132
+ persistence: z.ZodLiteral<"local">;
133
+ }, z.core.$strict>, z.ZodObject<{
134
+ source: z.ZodObject<{
135
+ runtimeId: z.ZodString;
136
+ generation: z.ZodNumber;
137
+ runtimeEpoch: z.ZodNumber;
138
+ sourceSequence: z.ZodNumber;
139
+ }, z.core.$strict>;
140
+ sessionId: z.ZodOptional<z.ZodString>;
141
+ workspaceId: z.ZodOptional<z.ZodString>;
142
+ operationId: z.ZodOptional<z.ZodString>;
143
+ type: z.ZodString;
144
+ payload: z.ZodUnknown;
145
+ occurredAt: z.ZodString;
146
+ payloadHash: z.ZodString;
147
+ kind: z.ZodLiteral<"durable">;
148
+ streamKind: z.ZodEnum<{
149
+ session: "session";
150
+ runtime: "runtime";
151
+ workspace: "workspace";
152
+ }>;
153
+ streamId: z.ZodString;
154
+ sequence: z.ZodNumber;
155
+ }, z.core.$strict>, z.ZodObject<{
156
+ kind: z.ZodLiteral<"control">;
157
+ event: z.ZodEnum<{
158
+ "source.ready": "source.ready";
159
+ "stream.ready": "stream.ready";
160
+ "stream.gap": "stream.gap";
161
+ "save.delayed": "save.delayed";
162
+ }>;
163
+ }, z.core.$strict>], "kind">;
164
+ export type BrowserChatFrame = z.infer<typeof browserChatFrameSchema>;
165
+ /** Live and channel-status frames omit the SSE id. Durable uses the public sequence. */
166
+ export declare function encodeSseFrame(frame: BrowserChatFrame): string;
167
+ export type BrowserChatState = {
168
+ ready: boolean;
169
+ gap: boolean;
170
+ seenSourceEvents: string[];
171
+ durableCursor: number;
172
+ texts: string[];
173
+ saveStatus: SaveStatus;
174
+ turnStatus: TurnStatus;
175
+ durableSources: string[];
176
+ };
177
+ export declare function createBrowserChatState(): BrowserChatState;
178
+ export declare function applyBrowserChatFrame(state: BrowserChatState, frame: BrowserChatFrame): BrowserChatState;
179
+ export type ChatRealtimeFixture = {
180
+ readonly turnStatus: TurnStatus;
181
+ readonly frames: readonly BrowserChatFrame[];
182
+ emitText(text: string): LiveChatFrame;
183
+ requestApproval(approvalId: string): LiveChatFrame;
184
+ resumeAfterApproval(): void;
185
+ completeTurn(): LiveChatFrame;
186
+ delayArchive(): ChatControlFrame;
187
+ flushArchive(): DurableChatFrame[];
188
+ };
189
+ export declare function createChatRealtimeFixture(input: {
190
+ runtimeId?: string;
191
+ generation?: number;
192
+ runtimeEpoch?: number;
193
+ sessionId?: string;
194
+ operationId?: string;
195
+ now?: () => string;
196
+ }): ChatRealtimeFixture;
197
+ //# sourceMappingURL=chat-realtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-realtime.d.ts","sourceRoot":"","sources":["../../../src/cloud-workspace/runtime/chat-realtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,299 @@
1
+ import { z } from 'zod';
2
+ import { eventStreamKindSchema, sourceEventIdSchema, sourceEventKey } from './events.js';
3
+ import { hashOperationPayload } from './operations.js';
4
+ export const CHAT_REALTIME_V2 = 'chatRealtimeV2';
5
+ /** Both sides must advertise the mode. Absence keeps history reads and refuses a new realtime turn. */
6
+ export function negotiateChatRealtime(input) {
7
+ if (input.local.includes(CHAT_REALTIME_V2) && input.remote.includes(CHAT_REALTIME_V2)) {
8
+ return { ok: true, mode: CHAT_REALTIME_V2 };
9
+ }
10
+ return { ok: false, code: 'chat_realtime_unavailable' };
11
+ }
12
+ export const TURN_STATUSES = [
13
+ 'starting',
14
+ 'running',
15
+ 'waiting_for_user',
16
+ 'completed',
17
+ 'failed',
18
+ 'interrupted',
19
+ ];
20
+ const TURN_TRANSITIONS = {
21
+ starting: ['running', 'failed', 'interrupted'],
22
+ running: ['waiting_for_user', 'completed', 'failed', 'interrupted'],
23
+ waiting_for_user: ['running', 'failed', 'interrupted'],
24
+ completed: [],
25
+ failed: [],
26
+ interrupted: [],
27
+ };
28
+ export function canTransitionTurn(from, to) {
29
+ return TURN_TRANSITIONS[from].includes(to);
30
+ }
31
+ export const SAVE_STATUSES = ['syncing', 'delayed', 'saved', 'gap'];
32
+ const SAVE_TRANSITIONS = {
33
+ syncing: ['delayed', 'saved', 'gap'],
34
+ delayed: ['syncing', 'saved', 'gap'],
35
+ saved: ['syncing', 'delayed', 'gap'],
36
+ gap: [],
37
+ };
38
+ export function canTransitionSave(from, to) {
39
+ return SAVE_TRANSITIONS[from].includes(to);
40
+ }
41
+ const sourceEpochSchema = z
42
+ .object({
43
+ runtimeId: z.string().min(1).max(128),
44
+ generation: z.number().int().nonnegative(),
45
+ runtimeEpoch: z.number().int().nonnegative(),
46
+ })
47
+ .strict();
48
+ export const replayPinEpochSchema = sourceEpochSchema
49
+ .safeExtend({
50
+ retainedFrom: z.number().int().positive(),
51
+ highWater: z.number().int().nonnegative(),
52
+ })
53
+ .strict();
54
+ export const replayPinResultSchema = z
55
+ .object({
56
+ pinId: z.string().min(1).max(128),
57
+ epochs: z.array(replayPinEpochSchema).min(1),
58
+ followBoundary: sourceEventIdSchema,
59
+ })
60
+ .strict();
61
+ export const sourceAckEpochSchema = sourceEpochSchema
62
+ .safeExtend({
63
+ confirmedThrough: z.number().int().nonnegative(),
64
+ })
65
+ .strict();
66
+ export const sourceAckSchema = z
67
+ .object({
68
+ leaseFence: z.number().int().nonnegative(),
69
+ epochs: z.array(sourceAckEpochSchema).min(1),
70
+ })
71
+ .strict();
72
+ export function isMonotonicAck(previous, next) {
73
+ return Number.isInteger(previous) && Number.isInteger(next) && next >= previous && previous >= 0;
74
+ }
75
+ /** Retained lower bound past the cloud coverage means that prefix can no longer be read. */
76
+ export function retainedRangeHasGap(retainedFrom, coveredThrough) {
77
+ return retainedFrom > coveredThrough + 1;
78
+ }
79
+ const frameBodySchema = {
80
+ source: sourceEventIdSchema,
81
+ sessionId: z.string().min(1).max(128).optional(),
82
+ workspaceId: z.string().min(1).max(128).optional(),
83
+ operationId: z.string().min(1).max(128).optional(),
84
+ type: z.string().min(1).max(128),
85
+ payload: z.unknown(),
86
+ occurredAt: z.string().min(1),
87
+ payloadHash: z.string().length(64),
88
+ };
89
+ export const liveChatFrameSchema = z
90
+ .object({
91
+ kind: z.literal('live'),
92
+ persistence: z.literal('local'),
93
+ ...frameBodySchema,
94
+ })
95
+ .strict();
96
+ export const durableChatFrameSchema = z
97
+ .object({
98
+ kind: z.literal('durable'),
99
+ streamKind: eventStreamKindSchema,
100
+ streamId: z.string().min(1).max(128),
101
+ sequence: z.number().int().positive(),
102
+ ...frameBodySchema,
103
+ })
104
+ .strict();
105
+ export const chatControlFrameSchema = z
106
+ .object({
107
+ kind: z.literal('control'),
108
+ event: z.enum(['source.ready', 'stream.ready', 'stream.gap', 'save.delayed']),
109
+ })
110
+ .strict();
111
+ export const browserChatFrameSchema = z.discriminatedUnion('kind', [
112
+ liveChatFrameSchema,
113
+ durableChatFrameSchema,
114
+ chatControlFrameSchema,
115
+ ]);
116
+ /** Live and channel-status frames omit the SSE id. Durable uses the public sequence. */
117
+ export function encodeSseFrame(frame) {
118
+ const parsed = browserChatFrameSchema.parse(frame);
119
+ const data = JSON.stringify(parsed);
120
+ if (parsed.kind === 'durable') {
121
+ return `id: ${parsed.sequence}\nevent: durable\ndata: ${data}\n\n`;
122
+ }
123
+ if (parsed.kind === 'live') {
124
+ return `event: live\ndata: ${data}\n\n`;
125
+ }
126
+ return `event: ${parsed.event}\ndata: ${data}\n\n`;
127
+ }
128
+ export function createBrowserChatState() {
129
+ return {
130
+ ready: false,
131
+ gap: false,
132
+ seenSourceEvents: [],
133
+ durableCursor: 0,
134
+ texts: [],
135
+ saveStatus: 'syncing',
136
+ turnStatus: 'starting',
137
+ durableSources: [],
138
+ };
139
+ }
140
+ function textOf(payload) {
141
+ if (!payload || typeof payload !== 'object' || !('text' in payload))
142
+ return undefined;
143
+ const text = payload.text;
144
+ return typeof text === 'string' ? text : undefined;
145
+ }
146
+ function nextTurn(current, type) {
147
+ if (type === 'approval.requested' && canTransitionTurn(current, 'waiting_for_user'))
148
+ return 'waiting_for_user';
149
+ if (type === 'message.delta' && canTransitionTurn(current, 'running'))
150
+ return 'running';
151
+ if (type === 'turn.completed' && canTransitionTurn(current, 'completed'))
152
+ return 'completed';
153
+ if (type === 'turn.failed' && canTransitionTurn(current, 'failed'))
154
+ return 'failed';
155
+ if (type === 'turn.interrupted' && canTransitionTurn(current, 'interrupted'))
156
+ return 'interrupted';
157
+ return current;
158
+ }
159
+ function nextSave(current, to) {
160
+ return canTransitionSave(current, to) ? to : current;
161
+ }
162
+ export function applyBrowserChatFrame(state, frame) {
163
+ const parsed = browserChatFrameSchema.parse(frame);
164
+ if (parsed.kind === 'control') {
165
+ if (parsed.event === 'source.ready')
166
+ return state;
167
+ if (parsed.event === 'stream.gap')
168
+ return { ...state, gap: true, ready: false, saveStatus: nextSave(state.saveStatus, 'gap') };
169
+ if (parsed.event === 'save.delayed')
170
+ return { ...state, saveStatus: nextSave(state.saveStatus, 'delayed') };
171
+ if (state.gap)
172
+ return state;
173
+ return { ...state, ready: true };
174
+ }
175
+ if (parsed.kind === 'durable' && parsed.sequence <= state.durableCursor)
176
+ return state;
177
+ if (parsed.kind === 'durable' && parsed.sequence !== state.durableCursor + 1) {
178
+ return {
179
+ ...state,
180
+ gap: true,
181
+ ready: false,
182
+ saveStatus: nextSave(state.saveStatus, 'gap'),
183
+ };
184
+ }
185
+ const key = sourceEventKey(parsed.source);
186
+ const seen = state.seenSourceEvents.includes(key);
187
+ const texts = !seen && parsed.type === 'message.delta' ? [...state.texts, textOf(parsed.payload) ?? ''] : state.texts;
188
+ const seenSourceEvents = seen ? state.seenSourceEvents : [...state.seenSourceEvents, key];
189
+ const turnStatus = seen ? state.turnStatus : nextTurn(state.turnStatus, parsed.type);
190
+ if (parsed.kind === 'live') {
191
+ const keep = state.saveStatus === 'gap' || state.saveStatus === 'delayed' || state.durableSources.includes(key);
192
+ const saveStatus = keep ? state.saveStatus : nextSave(state.saveStatus, 'syncing');
193
+ return { ...state, texts, seenSourceEvents, turnStatus, saveStatus };
194
+ }
195
+ const durableSources = state.durableSources.includes(key) ? state.durableSources : [...state.durableSources, key];
196
+ const archiveCaughtUp = seenSourceEvents.every((item) => durableSources.includes(item));
197
+ const saveTarget = archiveCaughtUp ? 'saved' : state.saveStatus === 'delayed' ? 'delayed' : 'syncing';
198
+ return {
199
+ ...state,
200
+ texts,
201
+ seenSourceEvents,
202
+ turnStatus,
203
+ durableSources,
204
+ durableCursor: parsed.sequence,
205
+ saveStatus: state.saveStatus === 'gap' ? 'gap' : nextSave(state.saveStatus, saveTarget),
206
+ };
207
+ }
208
+ export function createChatRealtimeFixture(input) {
209
+ const runtimeId = input.runtimeId ?? 'rt_1';
210
+ const generation = input.generation ?? 1;
211
+ const runtimeEpoch = input.runtimeEpoch ?? 1;
212
+ const sessionId = input.sessionId ?? 'session_1';
213
+ const operationId = input.operationId ?? 'op_1';
214
+ const now = input.now ?? (() => '2026-09-24T00:00:00.000Z');
215
+ const frames = [];
216
+ const pending = [];
217
+ let sequence = 0;
218
+ let turnStatus = 'starting';
219
+ let publicSequence = 0;
220
+ function source() {
221
+ sequence += 1;
222
+ return { runtimeId, generation, runtimeEpoch, sourceSequence: sequence };
223
+ }
224
+ function pushLive(type, payload) {
225
+ const frame = liveChatFrameSchema.parse({
226
+ kind: 'live',
227
+ persistence: 'local',
228
+ source: source(),
229
+ sessionId,
230
+ operationId,
231
+ type,
232
+ payload,
233
+ occurredAt: now(),
234
+ payloadHash: hashOperationPayload(payload),
235
+ });
236
+ frames.push(frame);
237
+ pending.push(frame);
238
+ return frame;
239
+ }
240
+ return {
241
+ get turnStatus() {
242
+ return turnStatus;
243
+ },
244
+ get frames() {
245
+ return frames;
246
+ },
247
+ emitText(text) {
248
+ if (turnStatus !== 'starting' && turnStatus !== 'running') {
249
+ throw new Error(`turn_not_generating:${turnStatus}`);
250
+ }
251
+ turnStatus = 'running';
252
+ return pushLive('message.delta', { text });
253
+ },
254
+ requestApproval(approvalId) {
255
+ if (turnStatus !== 'running')
256
+ throw new Error(`approval_requires_running:${turnStatus}`);
257
+ turnStatus = 'waiting_for_user';
258
+ return pushLive('approval.requested', { approvalId });
259
+ },
260
+ resumeAfterApproval() {
261
+ if (turnStatus !== 'waiting_for_user')
262
+ throw new Error(`approval_not_waiting:${turnStatus}`);
263
+ turnStatus = 'running';
264
+ },
265
+ completeTurn() {
266
+ if (turnStatus !== 'running')
267
+ throw new Error(`turn_not_running:${turnStatus}`);
268
+ turnStatus = 'completed';
269
+ return pushLive('turn.completed', {});
270
+ },
271
+ delayArchive() {
272
+ const frame = chatControlFrameSchema.parse({ kind: 'control', event: 'save.delayed' });
273
+ frames.push(frame);
274
+ return frame;
275
+ },
276
+ flushArchive() {
277
+ const archived = pending.splice(0).map((live) => {
278
+ publicSequence += 1;
279
+ const durable = durableChatFrameSchema.parse({
280
+ kind: 'durable',
281
+ streamKind: 'session',
282
+ streamId: sessionId,
283
+ sequence: publicSequence,
284
+ source: live.source,
285
+ sessionId: live.sessionId,
286
+ operationId: live.operationId,
287
+ type: live.type,
288
+ payload: live.payload,
289
+ occurredAt: live.occurredAt,
290
+ payloadHash: live.payloadHash,
291
+ });
292
+ frames.push(durable);
293
+ return durable;
294
+ });
295
+ return archived;
296
+ },
297
+ };
298
+ }
299
+ //# sourceMappingURL=chat-realtime.js.map