@abloatai/ablo 0.6.0 → 0.7.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 (74) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +64 -35
  3. package/dist/BaseSyncedStore.d.ts +1 -1
  4. package/dist/BaseSyncedStore.js +1 -1
  5. package/dist/client/Ablo.d.ts +1 -0
  6. package/dist/client/Ablo.js +1 -0
  7. package/dist/client/createModelProxy.d.ts +26 -3
  8. package/dist/client/createModelProxy.js +4 -1
  9. package/dist/client/validateAbloOptions.js +2 -2
  10. package/dist/coordination/index.d.ts +6 -0
  11. package/dist/coordination/index.js +6 -0
  12. package/dist/coordination/schema.d.ts +329 -0
  13. package/dist/coordination/schema.js +209 -0
  14. package/dist/core/QueryView.d.ts +4 -1
  15. package/dist/core/QueryView.js +1 -1
  16. package/dist/core/query-utils.d.ts +7 -10
  17. package/dist/core/query-utils.js +2 -3
  18. package/dist/errorCodes.d.ts +264 -0
  19. package/dist/errorCodes.js +251 -0
  20. package/dist/errors.d.ts +51 -6
  21. package/dist/errors.js +56 -3
  22. package/dist/index.d.ts +3 -2
  23. package/dist/index.js +2 -2
  24. package/dist/policy/index.d.ts +1 -1
  25. package/dist/policy/index.js +1 -1
  26. package/dist/policy/types.d.ts +31 -0
  27. package/dist/policy/types.js +15 -0
  28. package/dist/react/AbloProvider.d.ts +12 -0
  29. package/dist/react/AbloProvider.js +11 -3
  30. package/dist/schema/ddl.d.ts +62 -0
  31. package/dist/schema/ddl.js +317 -0
  32. package/dist/schema/diff.d.ts +6 -0
  33. package/dist/schema/diff.js +21 -3
  34. package/dist/schema/field.d.ts +16 -19
  35. package/dist/schema/field.js +30 -17
  36. package/dist/schema/index.d.ts +7 -4
  37. package/dist/schema/index.js +9 -3
  38. package/dist/schema/model.d.ts +87 -25
  39. package/dist/schema/model.js +33 -3
  40. package/dist/schema/relation.d.ts +17 -0
  41. package/dist/schema/roles.d.ts +148 -0
  42. package/dist/schema/roles.js +149 -0
  43. package/dist/schema/schema.d.ts +2 -112
  44. package/dist/schema/schema.js +50 -62
  45. package/dist/schema/select.d.ts +25 -0
  46. package/dist/schema/select.js +55 -0
  47. package/dist/schema/serialize.d.ts +13 -9
  48. package/dist/schema/serialize.js +14 -10
  49. package/dist/schema/sugar.d.ts +20 -3
  50. package/dist/schema/sugar.js +5 -1
  51. package/dist/schema/tenancy.d.ts +66 -0
  52. package/dist/schema/tenancy.js +58 -0
  53. package/dist/sync/HydrationCoordinator.d.ts +2 -0
  54. package/dist/sync/HydrationCoordinator.js +23 -17
  55. package/dist/sync/createIntentStream.d.ts +2 -1
  56. package/dist/sync/createIntentStream.js +46 -1
  57. package/dist/sync/participants.js +5 -14
  58. package/dist/types/streams.d.ts +53 -33
  59. package/docs/api-keys.md +44 -0
  60. package/docs/api.md +11 -22
  61. package/docs/cli.md +212 -0
  62. package/docs/client-behavior.md +1 -1
  63. package/docs/coordination.md +61 -12
  64. package/docs/data-sources.md +2 -2
  65. package/docs/examples/existing-python-backend.md +3 -3
  66. package/docs/examples/scoped-agent.md +78 -0
  67. package/docs/guarantees.md +5 -2
  68. package/docs/identity.md +139 -68
  69. package/docs/index.md +6 -0
  70. package/docs/integration-guide.md +31 -35
  71. package/docs/interaction-model.md +3 -0
  72. package/docs/react.md +3 -3
  73. package/docs/roadmap.md +14 -2
  74. package/package.json +8 -1
@@ -0,0 +1,329 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Coordination wire schema — the ONE canonical source for the three layers
4
+ * that keep humans and agents from clobbering each other on a shared row.
5
+ * See `packages/sync-engine/docs/coordination.md` ("The model — three layers,
6
+ * one decision") for the conceptual model. The layers, outer-to-inner:
7
+ *
8
+ * 1. PRESENCE (observation) — who is working where; NEVER enforces.
9
+ * 2. PESSIMISTIC (claims/leases) — `intent_begin`/`intent_abandon`;
10
+ * mutual exclusion between participants.
11
+ * 3. OPTIMISTIC (stale-context) — `readAt` + `onStale` write-guard;
12
+ * last-writer-wins lost-update detection.
13
+ *
14
+ * Both the SDK (`types/streams.ts`) and the sync-server (`hub/types.ts`,
15
+ * `presence/*`) derive their TypeScript types from THESE schemas via
16
+ * `z.infer`, instead of re-declaring overlapping shapes. That collapses the
17
+ * field drift this surface accreted — e.g. the SDK's intent view dropping
18
+ * `status`/`error`, `onStale` declared 5×, `IntentStatus` declared 2× — into
19
+ * a single definition that the wire ingest can also validate at runtime.
20
+ */
21
+ /** A line/column span within a text-bearing field (slide body, doc, cell). */
22
+ export declare const targetRangeSchema: z.ZodObject<{
23
+ startLine: z.ZodNumber;
24
+ endLine: z.ZodNumber;
25
+ startColumn: z.ZodOptional<z.ZodNumber>;
26
+ endColumn: z.ZodOptional<z.ZodNumber>;
27
+ }, z.core.$strip>;
28
+ export type TargetRange = z.infer<typeof targetRangeSchema>;
29
+ export declare const participantKindSchema: z.ZodEnum<{
30
+ user: "user";
31
+ agent: "agent";
32
+ system: "system";
33
+ }>;
34
+ export type ParticipantKind = z.infer<typeof participantKindSchema>;
35
+ /**
36
+ * What a claim / intent / activity points at. The common locator shared by
37
+ * all three layers — an entity, optionally narrowed to a path, range, or
38
+ * field, with opaque app metadata.
39
+ */
40
+ export declare const targetRefSchema: z.ZodObject<{
41
+ entityType: z.ZodString;
42
+ entityId: z.ZodString;
43
+ path: z.ZodOptional<z.ZodString>;
44
+ range: z.ZodOptional<z.ZodObject<{
45
+ startLine: z.ZodNumber;
46
+ endLine: z.ZodNumber;
47
+ startColumn: z.ZodOptional<z.ZodNumber>;
48
+ endColumn: z.ZodOptional<z.ZodNumber>;
49
+ }, z.core.$strip>>;
50
+ field: z.ZodOptional<z.ZodString>;
51
+ meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
52
+ }, z.core.$strip>;
53
+ export type TargetRef = z.infer<typeof targetRefSchema>;
54
+ /**
55
+ * Mode applied when a write's snapshot watermark (`readAt`) is older than the
56
+ * target row's latest delta. `'reject'` is the default whenever `readAt` is
57
+ * present. `'flag'` and `'merge'` are reserved — the wire accepts them, the
58
+ * server does not yet enforce them.
59
+ */
60
+ export declare const onStaleModeSchema: z.ZodEnum<{
61
+ reject: "reject";
62
+ force: "force";
63
+ flag: "flag";
64
+ merge: "merge";
65
+ }>;
66
+ export type OnStaleMode = z.infer<typeof onStaleModeSchema>;
67
+ /**
68
+ * The optimistic guard carried on a commit operation. `readAt` is the
69
+ * snapshot watermark from `context.capture` (null/absent ⇒ unguarded write).
70
+ * `bypass` is the explicit, recorded override of a *foreign* pessimistic
71
+ * claim — see the claim layer below.
72
+ */
73
+ export declare const writeGuardSchema: z.ZodObject<{
74
+ readAt: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
75
+ onStale: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
76
+ reject: "reject";
77
+ force: "force";
78
+ flag: "flag";
79
+ merge: "merge";
80
+ }>>>;
81
+ bypass: z.ZodOptional<z.ZodBoolean>;
82
+ }, z.core.$strip>;
83
+ export type WriteGuard = z.infer<typeof writeGuardSchema>;
84
+ /**
85
+ * Lifecycle of an intent — the Stripe `PaymentIntent.status` shape. Absent on
86
+ * the wire ⇒ `'active'` (additive back-compat). The server stamps `'active'`
87
+ * on `intent_begin` and emits a single terminal frame (`committed` /
88
+ * `canceled` / `expired`) as the claim ends, so contenders learn *how* it
89
+ * resolved, not merely that it vanished.
90
+ */
91
+ export declare const intentStatusSchema: z.ZodEnum<{
92
+ active: "active";
93
+ committed: "committed";
94
+ expired: "expired";
95
+ canceled: "canceled";
96
+ }>;
97
+ export type IntentStatus = z.infer<typeof intentStatusSchema>;
98
+ /** Why a claim ended in a non-success terminal state. */
99
+ export declare const intentErrorSchema: z.ZodObject<{
100
+ code: z.ZodString;
101
+ message: z.ZodOptional<z.ZodString>;
102
+ heldBy: z.ZodOptional<z.ZodString>;
103
+ heldByIntentId: z.ZodOptional<z.ZodString>;
104
+ heldByExpiresAt: z.ZodOptional<z.ZodNumber>;
105
+ }, z.core.$strip>;
106
+ export type IntentError = z.infer<typeof intentErrorSchema>;
107
+ /**
108
+ * A declared pending-mutation intent — the unit broadcast in presence
109
+ * `activeIntents`. Clients supply the descriptive `targetRef` fields, an
110
+ * `action`, and a chosen `intentId`; the SERVER stamps `declaredAt` /
111
+ * `expiresAt` and may set `status` / `error`.
112
+ *
113
+ * `status` and `error` are OPTIONAL: this single shape serves both the
114
+ * server (which sets them) and the SDK view (which historically omitted
115
+ * them). The superset is structurally assignable wherever the leaner view
116
+ * was used, so the two prior copies collapse into this one without breaking
117
+ * SDK consumers.
118
+ */
119
+ export declare const intentClaimSchema: z.ZodObject<{
120
+ entityType: z.ZodString;
121
+ entityId: z.ZodString;
122
+ path: z.ZodOptional<z.ZodString>;
123
+ range: z.ZodOptional<z.ZodObject<{
124
+ startLine: z.ZodNumber;
125
+ endLine: z.ZodNumber;
126
+ startColumn: z.ZodOptional<z.ZodNumber>;
127
+ endColumn: z.ZodOptional<z.ZodNumber>;
128
+ }, z.core.$strip>>;
129
+ field: z.ZodOptional<z.ZodString>;
130
+ meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
131
+ intentId: z.ZodString;
132
+ action: z.ZodString;
133
+ declaredAt: z.ZodNumber;
134
+ expiresAt: z.ZodNumber;
135
+ status: z.ZodOptional<z.ZodEnum<{
136
+ active: "active";
137
+ committed: "committed";
138
+ expired: "expired";
139
+ canceled: "canceled";
140
+ }>>;
141
+ error: z.ZodOptional<z.ZodObject<{
142
+ code: z.ZodString;
143
+ message: z.ZodOptional<z.ZodString>;
144
+ heldBy: z.ZodOptional<z.ZodString>;
145
+ heldByIntentId: z.ZodOptional<z.ZodString>;
146
+ heldByExpiresAt: z.ZodOptional<z.ZodNumber>;
147
+ }, z.core.$strip>>;
148
+ }, z.core.$strip>;
149
+ export type IntentClaim = z.infer<typeof intentClaimSchema>;
150
+ /**
151
+ * `intent_begin` payload (client → server). The descriptive target + action,
152
+ * plus an optional duration hint and the opt-in fair-queue flag. The server
153
+ * stamps the lifecycle/timestamp fields, so they are NOT part of the inbound
154
+ * shape — this is exactly what the WS ingest validates.
155
+ */
156
+ export declare const intentBeginPayloadSchema: z.ZodObject<{
157
+ entityType: z.ZodString;
158
+ entityId: z.ZodString;
159
+ path: z.ZodOptional<z.ZodString>;
160
+ range: z.ZodOptional<z.ZodObject<{
161
+ startLine: z.ZodNumber;
162
+ endLine: z.ZodNumber;
163
+ startColumn: z.ZodOptional<z.ZodNumber>;
164
+ endColumn: z.ZodOptional<z.ZodNumber>;
165
+ }, z.core.$strip>>;
166
+ field: z.ZodOptional<z.ZodString>;
167
+ meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
168
+ intentId: z.ZodString;
169
+ action: z.ZodString;
170
+ estimatedMs: z.ZodOptional<z.ZodNumber>;
171
+ queue: z.ZodOptional<z.ZodBoolean>;
172
+ }, z.core.$strip>;
173
+ export type IntentBeginPayload = z.infer<typeof intentBeginPayloadSchema>;
174
+ /**
175
+ * `intent_abandon` payload (client → server). `entityType`/`entityId` are
176
+ * carried so the server can DEQUEUE a still-*waiting* (not held) intent from
177
+ * the FIFO line — the held-intent path needs only `intentId`. (The previous
178
+ * wire type omitted these two even though the handler reads them; the schema
179
+ * documents what the code actually uses.)
180
+ */
181
+ export declare const intentAbandonPayloadSchema: z.ZodObject<{
182
+ intentId: z.ZodString;
183
+ entityType: z.ZodOptional<z.ZodString>;
184
+ entityId: z.ZodOptional<z.ZodString>;
185
+ }, z.core.$strip>;
186
+ export type IntentAbandonPayload = z.infer<typeof intentAbandonPayloadSchema>;
187
+ /**
188
+ * `intent_reorder` payload (client → server). A privileged participant (e.g. a
189
+ * supervisor over its sub-agents) re-ranks the FIFO wait queue for an entity:
190
+ * `order` lists waiters by `heldBy`+`intentId` in the desired priority. Waiters
191
+ * not listed keep their relative order behind the listed ones. The server gates
192
+ * who may call this; an unauthorized sender is dropped. Unlike `intent_abandon`
193
+ * (acts on the caller's own entry), reorder acts on OTHER participants' queue
194
+ * positions — hence the authorization gate.
195
+ */
196
+ export declare const intentReorderPayloadSchema: z.ZodObject<{
197
+ entityType: z.ZodString;
198
+ entityId: z.ZodString;
199
+ order: z.ZodArray<z.ZodObject<{
200
+ heldBy: z.ZodString;
201
+ intentId: z.ZodString;
202
+ }, z.core.$strip>>;
203
+ }, z.core.$strip>;
204
+ export type IntentReorderPayload = z.infer<typeof intentReorderPayloadSchema>;
205
+ export declare const commitOperationTypeSchema: z.ZodEnum<{
206
+ CREATE: "CREATE";
207
+ UPDATE: "UPDATE";
208
+ DELETE: "DELETE";
209
+ ARCHIVE: "ARCHIVE";
210
+ UNARCHIVE: "UNARCHIVE";
211
+ }>;
212
+ export type CommitOperationType = z.infer<typeof commitOperationTypeSchema>;
213
+ /**
214
+ * A single mutation in a commit batch, as it arrives on the wire. Extends the
215
+ * optimistic `writeGuard` (`readAt`/`onStale`/`bypass`) — the structural link
216
+ * that makes "every write is stale-guarded" legible in the type, not just in
217
+ * prose.
218
+ */
219
+ export declare const commitOperationSchema: z.ZodObject<{
220
+ readAt: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
221
+ onStale: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
222
+ reject: "reject";
223
+ force: "force";
224
+ flag: "flag";
225
+ merge: "merge";
226
+ }>>>;
227
+ bypass: z.ZodOptional<z.ZodBoolean>;
228
+ type: z.ZodEnum<{
229
+ CREATE: "CREATE";
230
+ UPDATE: "UPDATE";
231
+ DELETE: "DELETE";
232
+ ARCHIVE: "ARCHIVE";
233
+ UNARCHIVE: "UNARCHIVE";
234
+ }>;
235
+ model: z.ZodString;
236
+ id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
237
+ input: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
238
+ transactionId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
239
+ }, z.core.$strip>;
240
+ export type CommitOperation = z.infer<typeof commitOperationSchema>;
241
+ export declare const presenceKindSchema: z.ZodEnum<{
242
+ enter: "enter";
243
+ update: "update";
244
+ leave: "leave";
245
+ }>;
246
+ export type PresenceKind = z.infer<typeof presenceKindSchema>;
247
+ /** What a participant is actively working on (agents fill this in). */
248
+ export declare const presenceActivitySchema: z.ZodObject<{
249
+ entityType: z.ZodString;
250
+ entityId: z.ZodString;
251
+ path: z.ZodOptional<z.ZodString>;
252
+ range: z.ZodOptional<z.ZodObject<{
253
+ startLine: z.ZodNumber;
254
+ endLine: z.ZodNumber;
255
+ startColumn: z.ZodOptional<z.ZodNumber>;
256
+ endColumn: z.ZodOptional<z.ZodNumber>;
257
+ }, z.core.$strip>>;
258
+ field: z.ZodOptional<z.ZodString>;
259
+ meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
260
+ action: z.ZodString;
261
+ detail: z.ZodOptional<z.ZodString>;
262
+ }, z.core.$strip>;
263
+ export type PresenceActivity = z.infer<typeof presenceActivitySchema>;
264
+ /**
265
+ * Full `presence_update` frame as the server broadcasts it. The activity +
266
+ * `activeIntents` are the observation surface for the other two layers —
267
+ * rendered, never acted on as enforcement.
268
+ */
269
+ export declare const presenceUpdateFrameSchema: z.ZodObject<{
270
+ kind: z.ZodEnum<{
271
+ enter: "enter";
272
+ update: "update";
273
+ leave: "leave";
274
+ }>;
275
+ userId: z.ZodOptional<z.ZodString>;
276
+ syncGroups: z.ZodOptional<z.ZodArray<z.ZodString>>;
277
+ timestamp: z.ZodOptional<z.ZodNumber>;
278
+ status: z.ZodString;
279
+ timezone: z.ZodOptional<z.ZodString>;
280
+ customStatus: z.ZodOptional<z.ZodString>;
281
+ activity: z.ZodOptional<z.ZodObject<{
282
+ entityType: z.ZodString;
283
+ entityId: z.ZodString;
284
+ path: z.ZodOptional<z.ZodString>;
285
+ range: z.ZodOptional<z.ZodObject<{
286
+ startLine: z.ZodNumber;
287
+ endLine: z.ZodNumber;
288
+ startColumn: z.ZodOptional<z.ZodNumber>;
289
+ endColumn: z.ZodOptional<z.ZodNumber>;
290
+ }, z.core.$strip>>;
291
+ field: z.ZodOptional<z.ZodString>;
292
+ meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
293
+ action: z.ZodString;
294
+ detail: z.ZodOptional<z.ZodString>;
295
+ }, z.core.$strip>>;
296
+ isAgent: z.ZodOptional<z.ZodBoolean>;
297
+ activeIntents: z.ZodOptional<z.ZodArray<z.ZodObject<{
298
+ entityType: z.ZodString;
299
+ entityId: z.ZodString;
300
+ path: z.ZodOptional<z.ZodString>;
301
+ range: z.ZodOptional<z.ZodObject<{
302
+ startLine: z.ZodNumber;
303
+ endLine: z.ZodNumber;
304
+ startColumn: z.ZodOptional<z.ZodNumber>;
305
+ endColumn: z.ZodOptional<z.ZodNumber>;
306
+ }, z.core.$strip>>;
307
+ field: z.ZodOptional<z.ZodString>;
308
+ meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
309
+ intentId: z.ZodString;
310
+ action: z.ZodString;
311
+ declaredAt: z.ZodNumber;
312
+ expiresAt: z.ZodNumber;
313
+ status: z.ZodOptional<z.ZodEnum<{
314
+ active: "active";
315
+ committed: "committed";
316
+ expired: "expired";
317
+ canceled: "canceled";
318
+ }>>;
319
+ error: z.ZodOptional<z.ZodObject<{
320
+ code: z.ZodString;
321
+ message: z.ZodOptional<z.ZodString>;
322
+ heldBy: z.ZodOptional<z.ZodString>;
323
+ heldByIntentId: z.ZodOptional<z.ZodString>;
324
+ heldByExpiresAt: z.ZodOptional<z.ZodNumber>;
325
+ }, z.core.$strip>>;
326
+ }, z.core.$strip>>>;
327
+ delegatedFrom: z.ZodOptional<z.ZodNullable<z.ZodString>>;
328
+ }, z.core.$strip>;
329
+ export type PresenceUpdateFrame = z.infer<typeof presenceUpdateFrameSchema>;
@@ -0,0 +1,209 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Coordination wire schema — the ONE canonical source for the three layers
4
+ * that keep humans and agents from clobbering each other on a shared row.
5
+ * See `packages/sync-engine/docs/coordination.md` ("The model — three layers,
6
+ * one decision") for the conceptual model. The layers, outer-to-inner:
7
+ *
8
+ * 1. PRESENCE (observation) — who is working where; NEVER enforces.
9
+ * 2. PESSIMISTIC (claims/leases) — `intent_begin`/`intent_abandon`;
10
+ * mutual exclusion between participants.
11
+ * 3. OPTIMISTIC (stale-context) — `readAt` + `onStale` write-guard;
12
+ * last-writer-wins lost-update detection.
13
+ *
14
+ * Both the SDK (`types/streams.ts`) and the sync-server (`hub/types.ts`,
15
+ * `presence/*`) derive their TypeScript types from THESE schemas via
16
+ * `z.infer`, instead of re-declaring overlapping shapes. That collapses the
17
+ * field drift this surface accreted — e.g. the SDK's intent view dropping
18
+ * `status`/`error`, `onStale` declared 5×, `IntentStatus` declared 2× — into
19
+ * a single definition that the wire ingest can also validate at runtime.
20
+ */
21
+ // ─────────────────────────────────────────────────────────────────────────
22
+ // Shared primitives
23
+ // ─────────────────────────────────────────────────────────────────────────
24
+ /** A line/column span within a text-bearing field (slide body, doc, cell). */
25
+ export const targetRangeSchema = z.object({
26
+ startLine: z.number(),
27
+ endLine: z.number(),
28
+ startColumn: z.number().optional(),
29
+ endColumn: z.number().optional(),
30
+ });
31
+ export const participantKindSchema = z.enum(['user', 'agent', 'system']);
32
+ /**
33
+ * What a claim / intent / activity points at. The common locator shared by
34
+ * all three layers — an entity, optionally narrowed to a path, range, or
35
+ * field, with opaque app metadata.
36
+ */
37
+ export const targetRefSchema = z.object({
38
+ entityType: z.string(),
39
+ entityId: z.string(),
40
+ path: z.string().optional(),
41
+ range: targetRangeSchema.optional(),
42
+ field: z.string().optional(),
43
+ meta: z.record(z.string(), z.unknown()).optional(),
44
+ });
45
+ // ─────────────────────────────────────────────────────────────────────────
46
+ // Layer 3 — OPTIMISTIC stale-context (the write-guard)
47
+ // ─────────────────────────────────────────────────────────────────────────
48
+ /**
49
+ * Mode applied when a write's snapshot watermark (`readAt`) is older than the
50
+ * target row's latest delta. `'reject'` is the default whenever `readAt` is
51
+ * present. `'flag'` and `'merge'` are reserved — the wire accepts them, the
52
+ * server does not yet enforce them.
53
+ */
54
+ export const onStaleModeSchema = z.enum(['reject', 'force', 'flag', 'merge']);
55
+ /**
56
+ * The optimistic guard carried on a commit operation. `readAt` is the
57
+ * snapshot watermark from `context.capture` (null/absent ⇒ unguarded write).
58
+ * `bypass` is the explicit, recorded override of a *foreign* pessimistic
59
+ * claim — see the claim layer below.
60
+ */
61
+ export const writeGuardSchema = z.object({
62
+ readAt: z.number().nullish(),
63
+ onStale: onStaleModeSchema.nullish(),
64
+ bypass: z.boolean().optional(),
65
+ });
66
+ // ─────────────────────────────────────────────────────────────────────────
67
+ // Layer 2 — PESSIMISTIC claim / intent-lease
68
+ // ─────────────────────────────────────────────────────────────────────────
69
+ /**
70
+ * Lifecycle of an intent — the Stripe `PaymentIntent.status` shape. Absent on
71
+ * the wire ⇒ `'active'` (additive back-compat). The server stamps `'active'`
72
+ * on `intent_begin` and emits a single terminal frame (`committed` /
73
+ * `canceled` / `expired`) as the claim ends, so contenders learn *how* it
74
+ * resolved, not merely that it vanished.
75
+ */
76
+ export const intentStatusSchema = z.enum([
77
+ 'active',
78
+ 'committed',
79
+ 'expired',
80
+ 'canceled',
81
+ ]);
82
+ /** Why a claim ended in a non-success terminal state. */
83
+ export const intentErrorSchema = z.object({
84
+ code: z.string(),
85
+ message: z.string().optional(),
86
+ /** Participant already holding the target (conflict rejections). */
87
+ heldBy: z.string().optional(),
88
+ heldByIntentId: z.string().optional(),
89
+ heldByExpiresAt: z.number().optional(),
90
+ });
91
+ /**
92
+ * A declared pending-mutation intent — the unit broadcast in presence
93
+ * `activeIntents`. Clients supply the descriptive `targetRef` fields, an
94
+ * `action`, and a chosen `intentId`; the SERVER stamps `declaredAt` /
95
+ * `expiresAt` and may set `status` / `error`.
96
+ *
97
+ * `status` and `error` are OPTIONAL: this single shape serves both the
98
+ * server (which sets them) and the SDK view (which historically omitted
99
+ * them). The superset is structurally assignable wherever the leaner view
100
+ * was used, so the two prior copies collapse into this one without breaking
101
+ * SDK consumers.
102
+ */
103
+ export const intentClaimSchema = targetRefSchema.extend({
104
+ intentId: z.string(),
105
+ /** Verb the agent expects: 'update' | 'create' | 'editing' | 'reviewing' … */
106
+ action: z.string(),
107
+ /** Server-stamped declaration time (epoch ms). */
108
+ declaredAt: z.number(),
109
+ /** Server-computed TTL deadline (epoch ms). Readers treat as advisory. */
110
+ expiresAt: z.number(),
111
+ status: intentStatusSchema.optional(),
112
+ error: intentErrorSchema.optional(),
113
+ });
114
+ /**
115
+ * `intent_begin` payload (client → server). The descriptive target + action,
116
+ * plus an optional duration hint and the opt-in fair-queue flag. The server
117
+ * stamps the lifecycle/timestamp fields, so they are NOT part of the inbound
118
+ * shape — this is exactly what the WS ingest validates.
119
+ */
120
+ export const intentBeginPayloadSchema = targetRefSchema.extend({
121
+ intentId: z.string(),
122
+ action: z.string(),
123
+ /** Hint for `expiresAt`; the server caps it. */
124
+ estimatedMs: z.number().optional(),
125
+ /**
126
+ * Opt into the fair wait queue: when the target is already held, the server
127
+ * enqueues this claim (FIFO) and replies `intent_queued` → later
128
+ * `intent_granted`, instead of `intent_rejected`. Clients that set this MUST
129
+ * handle the grant.
130
+ */
131
+ queue: z.boolean().optional(),
132
+ });
133
+ /**
134
+ * `intent_abandon` payload (client → server). `entityType`/`entityId` are
135
+ * carried so the server can DEQUEUE a still-*waiting* (not held) intent from
136
+ * the FIFO line — the held-intent path needs only `intentId`. (The previous
137
+ * wire type omitted these two even though the handler reads them; the schema
138
+ * documents what the code actually uses.)
139
+ */
140
+ export const intentAbandonPayloadSchema = z.object({
141
+ intentId: z.string(),
142
+ entityType: z.string().optional(),
143
+ entityId: z.string().optional(),
144
+ });
145
+ /**
146
+ * `intent_reorder` payload (client → server). A privileged participant (e.g. a
147
+ * supervisor over its sub-agents) re-ranks the FIFO wait queue for an entity:
148
+ * `order` lists waiters by `heldBy`+`intentId` in the desired priority. Waiters
149
+ * not listed keep their relative order behind the listed ones. The server gates
150
+ * who may call this; an unauthorized sender is dropped. Unlike `intent_abandon`
151
+ * (acts on the caller's own entry), reorder acts on OTHER participants' queue
152
+ * positions — hence the authorization gate.
153
+ */
154
+ export const intentReorderPayloadSchema = z.object({
155
+ entityType: z.string(),
156
+ entityId: z.string(),
157
+ order: z.array(z.object({ heldBy: z.string(), intentId: z.string() })),
158
+ });
159
+ // ─────────────────────────────────────────────────────────────────────────
160
+ // Commit operation — carries the optimistic write-guard (Layer 3)
161
+ // ─────────────────────────────────────────────────────────────────────────
162
+ export const commitOperationTypeSchema = z.enum([
163
+ 'CREATE',
164
+ 'UPDATE',
165
+ 'DELETE',
166
+ 'ARCHIVE',
167
+ 'UNARCHIVE',
168
+ ]);
169
+ /**
170
+ * A single mutation in a commit batch, as it arrives on the wire. Extends the
171
+ * optimistic `writeGuard` (`readAt`/`onStale`/`bypass`) — the structural link
172
+ * that makes "every write is stale-guarded" legible in the type, not just in
173
+ * prose.
174
+ */
175
+ export const commitOperationSchema = writeGuardSchema.extend({
176
+ type: commitOperationTypeSchema,
177
+ model: z.string(),
178
+ id: z.string().nullish(),
179
+ input: z.record(z.string(), z.unknown()).nullish(),
180
+ /** Per-op client tx id, echoed on the broadcast delta. */
181
+ transactionId: z.string().nullish(),
182
+ });
183
+ // ─────────────────────────────────────────────────────────────────────────
184
+ // Layer 1 — PRESENCE (observation only; never enforces)
185
+ // ─────────────────────────────────────────────────────────────────────────
186
+ export const presenceKindSchema = z.enum(['enter', 'update', 'leave']);
187
+ /** What a participant is actively working on (agents fill this in). */
188
+ export const presenceActivitySchema = targetRefSchema.extend({
189
+ action: z.string(),
190
+ detail: z.string().optional(),
191
+ });
192
+ /**
193
+ * Full `presence_update` frame as the server broadcasts it. The activity +
194
+ * `activeIntents` are the observation surface for the other two layers —
195
+ * rendered, never acted on as enforcement.
196
+ */
197
+ export const presenceUpdateFrameSchema = z.object({
198
+ kind: presenceKindSchema,
199
+ userId: z.string().optional(),
200
+ syncGroups: z.array(z.string()).optional(),
201
+ timestamp: z.number().optional(),
202
+ status: z.string(),
203
+ timezone: z.string().optional(),
204
+ customStatus: z.string().optional(),
205
+ activity: presenceActivitySchema.optional(),
206
+ isAgent: z.boolean().optional(),
207
+ activeIntents: z.array(intentClaimSchema).optional(),
208
+ delegatedFrom: z.string().nullish(),
209
+ });
@@ -20,7 +20,10 @@ export interface QueryViewOptions<T> {
20
20
  order?: 'asc' | 'desc';
21
21
  limit?: number;
22
22
  offset?: number;
23
- scope?: ModelScope;
23
+ /** Lifecycle filter — `live` (default), `archived`, or `all`. Named `state`
24
+ * (GitHub's open/closed/all precedent) so it doesn't collide with the
25
+ * sync-group `scope`. */
26
+ state?: ModelScope;
24
27
  }
25
28
  export declare class QueryView<T extends Record<string, unknown>> implements IncrementalView {
26
29
  /** The full (unlimited) internal result set, kept sorted. */
@@ -50,7 +50,7 @@ export class QueryView {
50
50
  this.sortDir = options.order === 'desc' ? -1 : 1;
51
51
  this.limitN = options.limit;
52
52
  this.offsetN = options.offset ?? 0;
53
- this.scope = options.scope ?? ModelScope.live;
53
+ this.scope = options.state ?? ModelScope.live;
54
54
  // Check for FK-index optimization: single-field where with an indexed FK
55
55
  this.fkField = null;
56
56
  this.fkValue = null;
@@ -1,18 +1,15 @@
1
1
  /**
2
- * query-utils — Pure query helpers shared between QueryView (MobX) and
3
- * AgentQueryView (headless). One source of truth for sort, filter, and
4
- * binary insertion logic.
2
+ * query-utils — Pure query helpers for the MobX `QueryView`. One source of
3
+ * truth for sort, filter, and binary insertion logic.
5
4
  *
6
5
  * No MobX, no ObjectPool, no Model — just arrays and values.
7
6
  */
8
7
  /**
9
- * The incremental-update contract that both QueryView and
10
- * AgentQueryView satisfy. Their respective registries (ViewRegistry,
11
- * AgentViewRegistry) store views as this base type so they can
12
- * dispatch to many views with different `T` parameters from one Set
13
- * `View<T>` is invariant in T, so without this shared base the
14
- * registries would have to widen via `unknown as View<Record<...>>`
15
- * at every register/unregister/notify call.
8
+ * The incremental-update contract a view satisfies. `ViewRegistry` stores views
9
+ * as this base type so it can dispatch to many views with different `T`
10
+ * parameters from one Set `View<T>` is invariant in T, so without this shared
11
+ * base the registry would have to widen via `unknown as View<Record<...>>` at
12
+ * every register/unregister/notify call.
16
13
  */
17
14
  export interface IncrementalView {
18
15
  handleAdded(entity: Record<string, unknown>): void;
@@ -1,7 +1,6 @@
1
1
  /**
2
- * query-utils — Pure query helpers shared between QueryView (MobX) and
3
- * AgentQueryView (headless). One source of truth for sort, filter, and
4
- * binary insertion logic.
2
+ * query-utils — Pure query helpers for the MobX `QueryView`. One source of
3
+ * truth for sort, filter, and binary insertion logic.
5
4
  *
6
5
  * No MobX, no ObjectPool, no Model — just arrays and values.
7
6
  */