@abloatai/ablo 0.9.0 → 0.9.2

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 (63) hide show
  1. package/AGENTS.md +84 -0
  2. package/CHANGELOG.md +40 -0
  3. package/README.md +15 -7
  4. package/dist/BaseSyncedStore.d.ts +10 -0
  5. package/dist/BaseSyncedStore.js +26 -0
  6. package/dist/SyncClient.d.ts +12 -0
  7. package/dist/SyncClient.js +15 -0
  8. package/dist/agent/index.js +1 -1
  9. package/dist/api/index.d.ts +1 -1
  10. package/dist/client/Ablo.d.ts +9 -51
  11. package/dist/client/Ablo.js +2 -104
  12. package/dist/client/ApiClient.d.ts +3 -115
  13. package/dist/client/ApiClient.js +0 -232
  14. package/dist/client/auth.js +32 -2
  15. package/dist/client/httpClient.d.ts +5 -6
  16. package/dist/client/httpClient.js +2 -3
  17. package/dist/client/index.d.ts +1 -1
  18. package/dist/errorCodes.js +3 -3
  19. package/dist/index.js +1 -1
  20. package/dist/interfaces/index.d.ts +4 -4
  21. package/dist/mutators/UndoManager.d.ts +100 -11
  22. package/dist/mutators/UndoManager.js +282 -13
  23. package/dist/react/AbloProvider.d.ts +18 -8
  24. package/dist/react/context.d.ts +31 -0
  25. package/dist/react/index.d.ts +1 -1
  26. package/dist/react/index.js +1 -1
  27. package/dist/react/useUndoScope.js +7 -0
  28. package/dist/schema/ddl.d.ts +8 -0
  29. package/dist/schema/ddl.js +10 -0
  30. package/dist/schema/index.d.ts +1 -1
  31. package/dist/schema/index.js +1 -1
  32. package/dist/server/commit.d.ts +4 -5
  33. package/dist/source/adapter.d.ts +18 -12
  34. package/dist/source/adapter.js +8 -7
  35. package/dist/source/adapters/drizzle.d.ts +15 -6
  36. package/dist/source/adapters/drizzle.js +87 -49
  37. package/dist/source/adapters/memory.d.ts +1 -1
  38. package/dist/source/adapters/memory.js +2 -2
  39. package/dist/source/adapters/prisma.d.ts +3 -3
  40. package/dist/source/adapters/prisma.js +6 -29
  41. package/dist/source/conformance.d.ts +1 -1
  42. package/dist/source/conformance.js +2 -2
  43. package/dist/source/contract.d.ts +3 -2
  44. package/dist/source/contract.js +3 -2
  45. package/dist/source/index.d.ts +1 -0
  46. package/dist/source/index.js +3 -2
  47. package/dist/source/migrations.d.ts +14 -0
  48. package/dist/source/migrations.js +39 -0
  49. package/dist/types/streams.d.ts +2 -1
  50. package/dist/wire/frames.d.ts +6 -8
  51. package/docs/api.md +1 -1
  52. package/docs/cli.md +18 -5
  53. package/docs/data-sources.md +68 -83
  54. package/docs/examples/ai-sdk-tool.md +11 -5
  55. package/docs/examples/existing-python-backend.md +26 -4
  56. package/docs/examples/nextjs.md +3 -2
  57. package/docs/examples/scoped-agent.md +38 -11
  58. package/docs/identity.md +86 -59
  59. package/docs/index.md +1 -1
  60. package/docs/integration-guide.md +85 -54
  61. package/docs/react.md +39 -28
  62. package/llms.txt +18 -11
  63. package/package.json +2 -2
@@ -5,7 +5,7 @@
5
5
  * IndexedDB, no WebSocket. It maps the public Model / Claim / Commit
6
6
  * nouns directly to HTTP routes on sync-server.
7
7
  */
8
- import type { AbloOptions, CommitReceipt, CommitResource, IntentCreateOptions, IntentHandle, IntentWaitOptions, ModelClient, ModelClaim, ModelMutationOptions, ModelReadOptions, ModelRead, ModelTarget, Turn } from './Ablo.js';
8
+ import type { AbloOptions, CommitResource, IntentCreateOptions, IntentHandle, IntentWaitOptions, ModelClient, ModelClaim, ModelTarget } from './Ablo.js';
9
9
  import type { Duration } from '../utils/duration.js';
10
10
  export type AbloApiClientOptions = Omit<AbloOptions, 'schema'> & {
11
11
  readonly schema?: null | undefined;
@@ -115,133 +115,21 @@ export interface CapabilityResource {
115
115
  */
116
116
  mint(options: CapabilityCreateOptions): Promise<Capability>;
117
117
  }
118
- export interface TaskCreateOptions {
119
- readonly prompt: string;
120
- readonly parentTaskId?: string;
121
- readonly surface?: string;
122
- readonly metadata?: Record<string, unknown>;
123
- }
124
- export interface TaskCloseOptions {
125
- readonly costInputTokens?: number;
126
- readonly costOutputTokens?: number;
127
- readonly costComputeMs?: number;
128
- }
129
- export interface Task {
130
- readonly id: string;
131
- readonly turnId: string;
132
- readonly promptHash?: string;
133
- readonly openedAt?: string;
134
- close(stats?: TaskCloseOptions): Promise<TaskCloseResult>;
135
- }
136
- export interface TaskCloseResult {
137
- readonly id: string;
138
- readonly turnId: string;
139
- readonly closed: boolean;
140
- readonly alreadyClosed?: boolean;
141
- readonly endedAt?: string;
142
- }
143
- export interface TaskResource {
144
- create(options: TaskCreateOptions): Promise<Task>;
145
- close(id: string, stats?: TaskCloseOptions): Promise<TaskCloseResult>;
146
- /**
147
- * Alias for `create`. Kept for the agent-run vocabulary; `create` is
148
- * the canonical SDK verb.
149
- */
150
- open(options: TaskCreateOptions): Promise<Task>;
151
- }
152
- export interface AgentOptions {
153
- readonly can: readonly string[];
154
- readonly syncGroups?: readonly string[];
155
- readonly label?: string;
156
- readonly userMeta?: Record<string, unknown>;
157
- /**
158
- * Internal lease for the run capability. Most callers should omit it.
159
- * The SDK revokes the capability when `run` finishes; the lease exists
160
- * to clean up crashed or abandoned runs.
161
- */
162
- readonly lease?: Duration;
163
- readonly leaseSeconds?: number;
164
- }
165
- export interface AgentRunOptions extends TaskCreateOptions {
166
- readonly signal?: AbortSignal;
167
- readonly costInputTokens?: number;
168
- readonly costOutputTokens?: number;
169
- readonly costComputeMs?: number;
170
- }
171
- export type AgentRunStatus = 'done' | 'failed' | 'cancelled';
172
- export interface AgentRunDone<T> {
173
- readonly status: 'done';
174
- readonly task: Task;
175
- readonly value: T;
176
- }
177
- export interface AgentRunFailed {
178
- readonly status: 'failed';
179
- readonly task?: Task;
180
- readonly error: unknown;
181
- }
182
- export interface AgentRunCancelled {
183
- readonly status: 'cancelled';
184
- readonly task?: Task;
185
- readonly error?: unknown;
186
- }
187
- export type AgentRunResult<T> = AgentRunDone<T> | AgentRunFailed | AgentRunCancelled;
188
- export interface AgentIntentOptions {
189
- readonly action: string;
190
- readonly field?: string;
191
- readonly ttl?: Duration;
192
- readonly target?: Partial<ModelTarget>;
193
- }
194
- export type AgentIntentInput = string | AgentIntentOptions;
195
- export interface AgentModelReadOptions extends ModelReadOptions {
196
- }
197
- export interface AgentModelMutationOptions extends Omit<ModelMutationOptions, 'intent'> {
198
- readonly intent?: AgentIntentInput | {
199
- readonly id: string;
200
- } | null;
201
- }
202
- export interface AgentModelClient<T = Record<string, unknown>> {
203
- retrieve(params: AgentModelReadOptions & {
204
- readonly id: string;
205
- }): Promise<ModelRead<T>>;
206
- create(params: AgentModelMutationOptions & {
207
- readonly data: Record<string, unknown>;
208
- readonly id?: string | null;
209
- }): Promise<CommitReceipt>;
210
- update(params: AgentModelMutationOptions & {
211
- readonly id: string;
212
- readonly data: Record<string, unknown>;
213
- }): Promise<CommitReceipt>;
214
- delete(params: AgentModelMutationOptions & {
215
- readonly id: string;
216
- }): Promise<CommitReceipt>;
217
- }
218
- export interface AgentRunContext {
219
- readonly task: Task;
220
- readonly ablo: AbloApi;
221
- model<T = Record<string, unknown>>(name: string): AgentModelClient<T>;
222
- }
223
- export interface Agent {
224
- readonly id: string;
225
- run<T>(options: AgentRunOptions, handler: (context: AgentRunContext) => Promise<T> | T): Promise<AgentRunResult<T>>;
226
- }
227
118
  export interface AbloApi {
228
119
  ready(): Promise<void>;
229
120
  waitForFlush(): Promise<void>;
230
121
  dispose(): Promise<void>;
231
122
  purge(): Promise<void>;
232
123
  readonly capabilities: CapabilityResource;
233
- readonly tasks: TaskResource;
234
124
  readonly intents: AbloApiIntents;
235
125
  readonly commits: CommitResource;
236
- agent(id: string, options: AgentOptions): Agent;
237
126
  model<T = Record<string, unknown>>(name: string): ModelClient<T>;
238
- beginTurn(options: TaskCreateOptions): Promise<Turn>;
239
127
  /**
240
128
  * Resolve the active bearer credential this client authenticates with — the
241
129
  * same token its own requests carry in `Authorization`. Returns `null` when
242
130
  * no credential is configured. Async because the API key may be supplied as
243
- * an async setter. Use it to authenticate side-band requests to the same
244
- * sync-server (e.g. the S3 presign endpoint) without re-minting.
131
+ * an async setter. Use it to authenticate a side-band request to the same
132
+ * server with the credential this client already holds — no re-mint.
245
133
  */
246
134
  getAuthToken(): Promise<string | null>;
247
135
  }
@@ -9,7 +9,6 @@ import { AbloClaimedError, AbloAuthenticationError, AbloConnectionError, AbloVal
9
9
  import { assertBrowserSafety, readProcessEnv, resolveApiKey, resolveApiKeyValue, resolveAuthToken, resolveBaseURL, resolveBootstrapBaseUrl, } from './auth.js';
10
10
  import { toSeconds } from '../utils/duration.js';
11
11
  const DEFAULT_AGENT_LEASE = '10m';
12
- const DEFAULT_INTENT_LEASE = '2m';
13
12
  export function createProtocolClient(options) {
14
13
  const env = readProcessEnv();
15
14
  const authInput = { options, env };
@@ -100,144 +99,6 @@ export function createProtocolClient(options) {
100
99
  schema: null,
101
100
  });
102
101
  }
103
- function createAgent(id, agentOptions) {
104
- return {
105
- id,
106
- async run(runOptions, handler) {
107
- if (runOptions.signal?.aborted) {
108
- return { status: 'cancelled' };
109
- }
110
- let capability = null;
111
- let task = null;
112
- try {
113
- const leaseOptions = agentOptions.leaseSeconds !== undefined
114
- ? { leaseSeconds: agentOptions.leaseSeconds }
115
- : { lease: agentOptions.lease ?? DEFAULT_AGENT_LEASE };
116
- capability = await capabilities.create({
117
- participantKind: 'agent',
118
- participantId: id,
119
- syncGroups: agentOptions.syncGroups ?? ['default'],
120
- operations: agentOptions.can,
121
- label: agentOptions.label ?? id,
122
- userMeta: agentOptions.userMeta,
123
- ...leaseOptions,
124
- });
125
- const agentClient = capability.client();
126
- task = await agentClient.tasks.create({
127
- prompt: runOptions.prompt,
128
- parentTaskId: runOptions.parentTaskId,
129
- surface: runOptions.surface ?? 'agent',
130
- metadata: runOptions.metadata,
131
- });
132
- const context = createAgentRunContext(agentClient, task);
133
- const value = await handler(context);
134
- await task.close({
135
- costInputTokens: runOptions.costInputTokens,
136
- costOutputTokens: runOptions.costOutputTokens,
137
- costComputeMs: runOptions.costComputeMs,
138
- });
139
- return { status: 'done', task, value };
140
- }
141
- catch (error) {
142
- if (task) {
143
- await task.close({
144
- costInputTokens: runOptions.costInputTokens,
145
- costOutputTokens: runOptions.costOutputTokens,
146
- costComputeMs: runOptions.costComputeMs,
147
- }).catch(() => { });
148
- }
149
- if (isAbortError(error) || runOptions.signal?.aborted) {
150
- return { status: 'cancelled', task: task ?? undefined, error };
151
- }
152
- return { status: 'failed', task: task ?? undefined, error };
153
- }
154
- finally {
155
- if (capability) {
156
- await capabilities.revoke(capability.id).catch(() => { });
157
- }
158
- }
159
- },
160
- };
161
- }
162
- function createAgentRunContext(agentClient, task) {
163
- return {
164
- task,
165
- ablo: agentClient,
166
- model(name) {
167
- return createAgentModelClient(agentClient, name);
168
- },
169
- };
170
- }
171
- function createAgentModelClient(agentClient, name) {
172
- const base = agentClient.model(name);
173
- return {
174
- retrieve(params) {
175
- // Reads are never blocked by a claim (coordination.md): a claim
176
- // serializes WRITERS, not readers. So — unlike the create/update/
177
- // delete paths below — retrieve does NOT apply the agent claimed
178
- // default; options pass through and the read path's `'return'`
179
- // default keeps a claimed row readable. A caller can still opt into
180
- // gating with an explicit `ifClaimed` (developer's choice).
181
- return base.retrieve(params);
182
- },
183
- create(params) {
184
- const id = params.id ?? createModelId();
185
- return withAgentIntent(agentClient, name, id, params, (commitIntent) => base.create({
186
- ...stripAgentRuntimeOptions(params),
187
- id,
188
- data: params.data,
189
- intent: commitIntent,
190
- }));
191
- },
192
- update(params) {
193
- return withAgentIntent(agentClient, name, params.id, params, (commitIntent) => base.update({
194
- ...stripAgentRuntimeOptions(params),
195
- id: params.id,
196
- data: params.data,
197
- intent: commitIntent,
198
- }));
199
- },
200
- delete(params) {
201
- return withAgentIntent(agentClient, name, params.id, params, (commitIntent) => base.delete({
202
- ...stripAgentRuntimeOptions(params),
203
- id: params.id,
204
- intent: commitIntent,
205
- }));
206
- },
207
- };
208
- }
209
- async function withAgentIntent(agentClient, modelName, id, mutationOptions, commit) {
210
- const intentInput = mutationOptions?.intent;
211
- const targetOverride = intentInput != null && typeof intentInput === 'object' && !isIntentHandleRef(intentInput)
212
- ? intentInput.target ?? {}
213
- : {};
214
- const target = {
215
- ...targetOverride,
216
- model: targetOverride.model ?? modelName,
217
- id: targetOverride.id ?? id,
218
- ...(intentInput != null && typeof intentInput === 'object' && !isIntentHandleRef(intentInput) && intentInput.field
219
- ? { field: intentInput.field }
220
- : {}),
221
- };
222
- await applyClaimedPolicy(target, withAgentClaimedDefault(mutationOptions), 'wait');
223
- if (intentInput == null || isIntentHandleRef(intentInput)) {
224
- return commit(intentInput);
225
- }
226
- const action = typeof intentInput === 'string' ? intentInput : intentInput.action;
227
- const intent = await agentClient.intents.create({
228
- target,
229
- action,
230
- ttl: typeof intentInput === 'object'
231
- ? intentInput.ttl ?? DEFAULT_INTENT_LEASE
232
- : DEFAULT_INTENT_LEASE,
233
- });
234
- try {
235
- return await commit(intent);
236
- }
237
- finally {
238
- await intent.release().catch(() => { });
239
- }
240
- }
241
102
  function normalizeCommitOperation(op, defaults) {
242
103
  const model = op.model ?? op.target?.model;
243
104
  if (!model) {
@@ -476,51 +337,6 @@ export function createProtocolClient(options) {
476
337
  return capabilities.create(options);
477
338
  },
478
339
  };
479
- const tasks = {
480
- async create(taskOptions) {
481
- const body = await requestJson('/v1/tasks', {
482
- method: 'POST',
483
- body: JSON.stringify({
484
- prompt: taskOptions.prompt,
485
- parentTaskId: taskOptions.parentTaskId,
486
- surface: taskOptions.surface,
487
- metadata: taskOptions.metadata,
488
- }),
489
- });
490
- const id = body.id ?? body.taskId ?? body.turnId;
491
- if (!id) {
492
- throw new AbloValidationError('Task create response did not include an id.', { code: 'task_id_missing' });
493
- }
494
- return {
495
- id,
496
- turnId: id,
497
- promptHash: body.promptHash,
498
- openedAt: body.openedAt,
499
- close: (stats) => tasks.close(id, stats),
500
- };
501
- },
502
- async close(id, stats) {
503
- const body = await requestJson(`/v1/tasks/${encodeURIComponent(id)}/close`, {
504
- method: 'POST',
505
- body: JSON.stringify({
506
- costInputTokens: stats?.costInputTokens ?? 0,
507
- costOutputTokens: stats?.costOutputTokens ?? 0,
508
- costComputeMs: stats?.costComputeMs ?? 0,
509
- }),
510
- });
511
- const closedId = body.id ?? body.taskId ?? body.turnId ?? id;
512
- return {
513
- id: closedId,
514
- turnId: closedId,
515
- closed: body.closed ?? body.alreadyClosed ?? true,
516
- alreadyClosed: body.alreadyClosed,
517
- endedAt: body.endedAt,
518
- };
519
- },
520
- open(options) {
521
- return tasks.create(options);
522
- },
523
- };
524
340
  const intents = {
525
341
  async create(intentOptions) {
526
342
  const intentId = createIntentId();
@@ -780,35 +596,14 @@ export function createProtocolClient(options) {
780
596
  async dispose() { },
781
597
  async purge() { },
782
598
  capabilities,
783
- tasks,
784
599
  intents,
785
600
  commits,
786
601
  model,
787
- agent: createAgent,
788
602
  async getAuthToken() {
789
603
  // Mirror `authHeaders()`: a configured API key wins, else the
790
604
  // construction-time auth token. Resolve the (possibly async) key setter.
791
605
  return (await resolveApiKeyValue(configuredApiKey)) ?? configuredAuthToken ?? null;
792
606
  },
793
- async beginTurn(turnOptions) {
794
- const task = await tasks.create(turnOptions);
795
- let closed = false;
796
- const close = async (stats) => {
797
- if (closed)
798
- return;
799
- closed = true;
800
- await task.close(stats);
801
- };
802
- const dispose = () => {
803
- closed = true;
804
- };
805
- return {
806
- turnId: task.id,
807
- close,
808
- dispose,
809
- [Symbol.asyncDispose]: close,
810
- };
811
- },
812
607
  };
813
608
  }
814
609
  function normalizeIntentId(intent) {
@@ -816,33 +611,6 @@ function normalizeIntentId(intent) {
816
611
  return intent;
817
612
  return intent?.id;
818
613
  }
819
- function withAgentClaimedDefault(options) {
820
- return {
821
- ifClaimed: 'fail',
822
- ...(options ?? {}),
823
- };
824
- }
825
- function stripAgentRuntimeOptions(options) {
826
- if (!options)
827
- return undefined;
828
- const { intent: _intent, ifClaimed: _ifClaimed, claimedTimeout: _claimedTimeout, claimedPollInterval: _claimedPollInterval, maxQueueDepth: _maxQueueDepth, ...rest } = options;
829
- return rest;
830
- }
831
- function isIntentHandleRef(input) {
832
- return (typeof input === 'object' &&
833
- input !== null &&
834
- 'id' in input &&
835
- typeof input.id === 'string' &&
836
- !('action' in input));
837
- }
838
- function isAbortError(error) {
839
- return (typeof DOMException !== 'undefined' &&
840
- error instanceof DOMException &&
841
- error.name === 'AbortError') || (typeof error === 'object' &&
842
- error !== null &&
843
- 'name' in error &&
844
- error.name === 'AbortError');
845
- }
846
614
  function parseBody(bodyText) {
847
615
  if (bodyText.length === 0)
848
616
  return null;
@@ -146,8 +146,38 @@ export function resolveBootstrapBaseUrl(input) {
146
146
  // legitimately arrive as `wss://…` — normalize it here rather than
147
147
  // faceplanting at fetch time. The derive branch below already does this;
148
148
  // the override branch silently skipped it.
149
- return normalizeAbloHostedBaseUrl(input.bootstrapBaseUrl).replace(/^ws/, 'http');
149
+ return ensureApiSuffix(normalizeAbloHostedBaseUrl(input.bootstrapBaseUrl).replace(/^ws/, 'http'));
150
150
  }
151
151
  const url = normalizeAbloHostedBaseUrl(input.url);
152
- return `${url.replace(/^ws/, 'http')}/api`;
152
+ return ensureApiSuffix(url.replace(/^ws/, 'http'));
153
+ }
154
+ /**
155
+ * Guarantee the HTTP base ends in the `/api` route segment the sync-server
156
+ * mounts every endpoint under (`apps/sync-server/src/index.ts` — `app.route('/api', …)`).
157
+ *
158
+ * The derive branch always appended `/api`; the override branch did NOT,
159
+ * trusting the caller (apps/web passes `${baseUrl}/api`). But a hosted
160
+ * customer setting a custom `baseURL`/`bootstrapBaseUrl` (their own subdomain,
161
+ * staging, etc.) without the suffix sent every credential exchange to
162
+ * `…/auth/capability` instead of `…/api/auth/capability` → a 404 surfaced as
163
+ * `exchange_failed`. Since the SDK hardcodes routes relative to this base and
164
+ * there is no valid Ablo deployment that serves them off the root, normalizing
165
+ * to a single trailing `/api` here is always correct — and idempotent for
166
+ * callers who already include it.
167
+ */
168
+ function ensureApiSuffix(httpBase) {
169
+ const trimmed = httpBase.replace(/\/+$/, '');
170
+ try {
171
+ const u = new URL(trimmed);
172
+ const segments = u.pathname.split('/').filter(Boolean);
173
+ if (segments[segments.length - 1] === 'api')
174
+ return trimmed;
175
+ u.pathname = `${u.pathname.replace(/\/+$/, '')}/api`;
176
+ return u.toString().replace(/\/+$/, '');
177
+ }
178
+ catch {
179
+ // Should be unreachable post-`normalizeAbloHostedBaseUrl` (which yields an
180
+ // absolute URL), but fall back to a string check rather than throwing.
181
+ return /\/api$/.test(trimmed) ? trimmed : `${trimmed}/api`;
182
+ }
153
183
  }
@@ -22,8 +22,8 @@
22
22
  * wraps it in a typed proxy facade so server code gets the SAME `client.<model>`
23
23
  * surface as the browser client — typed proxies, stateless transport.
24
24
  */
25
- import { type AbloApiClientOptions, type TaskCreateOptions } from './ApiClient.js';
26
- import type { CommitReceipt, CommitResource, HttpClaimApi, ModelRead, ModelReadOptions, Turn } from './Ablo.js';
25
+ import { type AbloApiClientOptions } from './ApiClient.js';
26
+ import type { CommitReceipt, CommitResource, HttpClaimApi, ModelRead, ModelReadOptions } from './Ablo.js';
27
27
  import type { ModelCreateParams, ModelDeleteParams, ModelLoadOptions, ModelRetrieveParams, ModelUpdateParams } from './createModelProxy.js';
28
28
  import type { Schema, SchemaRecord, InferModel, InferCreate } from '../schema/schema.js';
29
29
  export interface AbloHttpClientOptions<S extends SchemaRecord> extends Omit<AbloApiClientOptions, 'schema'> {
@@ -47,7 +47,7 @@ export interface HttpModelClient<T, C = T> {
47
47
  }
48
48
  /**
49
49
  * The honest type of the stateless HTTP client: typed model proxies (the
50
- * request/response subset) + `commits` + `beginTurn` + `dispose`. Reaching for a
50
+ * request/response subset) + `commits` + `dispose`. Reaching for a
51
51
  * stateful-only capability (`get`/`getAll`/`getCount`, `onChange`,
52
52
  * `claim.state`/`queue`/`reorder`) is a COMPILE error here, not a latent runtime
53
53
  * `undefined` — the type matches what the transport can actually do.
@@ -56,7 +56,6 @@ export type AbloHttpClient<S extends SchemaRecord> = {
56
56
  readonly [K in keyof S & string]: HttpModelClient<InferModel<Schema<S>, K>, InferCreate<Schema<S>, K>>;
57
57
  } & {
58
58
  readonly commits: CommitResource;
59
- beginTurn(options: TaskCreateOptions): Promise<Turn>;
60
59
  dispose(): Promise<void>;
61
60
  /** Resolve the bearer credential this client authenticates with (see `AbloApi.getAuthToken`). */
62
61
  getAuthToken(): Promise<string | null>;
@@ -65,7 +64,7 @@ export type AbloHttpClient<S extends SchemaRecord> = {
65
64
  };
66
65
  /**
67
66
  * Stateless, typed HTTP client. Each `client.<model>` resolves to the protocol
68
- * client's `model(name)`; `commits`, `beginTurn`, `tasks`, `dispose`, etc. pass
69
- * through. No socket is ever opened; identity is the Bearer credential.
67
+ * client's `model(name)`; `commits`, `dispose`, etc. pass through. No socket is
68
+ * ever opened; identity is the Bearer credential.
70
69
  */
71
70
  export declare function createAbloHttpClient<S extends SchemaRecord>(options: AbloHttpClientOptions<S>): AbloHttpClient<S>;
@@ -36,14 +36,13 @@ const PROTOCOL_MEMBERS = new Set([
36
36
  'dispose',
37
37
  'purge',
38
38
  'commits',
39
- 'beginTurn',
40
39
  'model',
41
40
  'getAuthToken',
42
41
  ]);
43
42
  /**
44
43
  * Stateless, typed HTTP client. Each `client.<model>` resolves to the protocol
45
- * client's `model(name)`; `commits`, `beginTurn`, `tasks`, `dispose`, etc. pass
46
- * through. No socket is ever opened; identity is the Bearer credential.
44
+ * client's `model(name)`; `commits`, `dispose`, etc. pass through. No socket is
45
+ * ever opened; identity is the Bearer credential.
47
46
  */
48
47
  export function createAbloHttpClient(options) {
49
48
  // The schema is type-level only; the protocol client is schema-agnostic.
@@ -32,5 +32,5 @@
32
32
  export { Ablo, computeFKDepthPriority, type AbloOptions, type InternalAbloOptions, type ClaimedOptions, type IfClaimedPolicy, type IntentWaitOptions, type ModelCountOptions, type ModelListOptions, type ModelListScope, type ModelLoadOptions, type ModelOperations, type ModelReadOptions, } from './Ablo.js';
33
33
  export { ABLO_DEFAULT_BASE_URL, ABLO_HOSTED_API_DOMAIN, ABLO_HOSTED_HTTP_BASE_URL, normalizeAbloHostedBaseUrl, } from './auth.js';
34
34
  export type { AbloPersistence } from './persistence.js';
35
- export type { AbloApi, AbloApiClientOptions, AbloApiIntents, Agent, AgentIntentInput, AgentIntentOptions, AgentOptions, AgentModelClient, AgentModelReadOptions, AgentModelMutationOptions, AgentRunContext, AgentRunDone, AgentRunFailed, AgentRunCancelled, AgentRunOptions, AgentRunResult, AgentRunStatus, Capability, CapabilityCreateOptions, CapabilityParticipantKind, CapabilityRecord, CapabilityResource, CapabilityRevocation, CapabilityScope, Task, TaskCloseOptions, TaskCloseResult, TaskCreateOptions, TaskResource, } from './ApiClient.js';
35
+ export type { AbloApi, AbloApiClientOptions, AbloApiIntents, Capability, CapabilityCreateOptions, CapabilityParticipantKind, CapabilityRecord, CapabilityResource, CapabilityRevocation, CapabilityScope, } from './ApiClient.js';
36
36
  export type { EngineParticipant, JoinedParticipant, ParticipantJoinOptions, ParticipantManager, ParticipantScope, ParticipantStatus, ScopedIntents, ScopedPresence, } from '../sync/participants.js';
@@ -176,8 +176,8 @@ export const ERROR_CODES = {
176
176
  check_violation: wire('validation', 400, false, 'A value violates a database check constraint.'),
177
177
  constraint_violation: wire('validation', 400, false, 'A database integrity constraint was violated.'),
178
178
  // ── tenant / unknown model (400) ───────────────────────────────────
179
- server_execute_unknown_model: wire('tenant', 400, false, 'The server-execute request named a model not in the tenant schema.'),
180
- mutate_create_unknown_model: wire('tenant', 400, false, 'A create targeted a model not in the tenant schema.'),
179
+ server_execute_unknown_model: wire('tenant', 400, false, 'Wrote to a model the server does not know. The server keeps its own copy of the schema — run `ablo push` (or keep `ablo dev` running) to upload `ablo/schema.ts` before writing to new or changed models.'),
180
+ mutate_create_unknown_model: wire('tenant', 400, false, 'Created a model the server does not know. Run `ablo push` (or keep `ablo dev` running) to upload `ablo/schema.ts` first — the server keeps its own copy of the schema.'),
181
181
  tenant_model_columns_unknown: wire('tenant', 400, false, "The tenant model's columns could not be resolved."),
182
182
  tenant_model_missing_organization_id: wire('tenant', 400, false, 'The tenant model is missing the organization_id column required for isolation.'),
183
183
  // ── schema migration / declaration (validation) ────────────────────
@@ -286,7 +286,7 @@ export const ERROR_CODES = {
286
286
  provisioner_unavailable: wire('server', 503, false, 'No database provisioner is configured.'),
287
287
  invalid_model: wire('validation', 400, false, 'The request named an invalid model.'),
288
288
  invalid_id: wire('validation', 400, false, 'The request carried an invalid id.'),
289
- unknown_model: wire('tenant', 400, false, 'The request named a model not in the tenant schema.'),
289
+ unknown_model: wire('tenant', 400, false, 'Named a model the server does not know. Run `ablo push` (or keep `ablo dev` running) to upload `ablo/schema.ts` — the server keeps its own copy of the schema.'),
290
290
  model_not_tenant_scoped: wire('tenant', 400, false, 'The model is not tenant-scoped and cannot be queried this way.'),
291
291
  schema_table_invalid: wire('schema', 500, false, "The model's table identifier is invalid."),
292
292
  schema_scope_invalid: wire('schema', 500, false, "The model's scope predicate could not be built."),
package/dist/index.js CHANGED
@@ -61,7 +61,7 @@ export { ABLO_DEFAULT_BASE_URL, ABLO_HOSTED_API_DOMAIN, ABLO_HOSTED_HTTP_BASE_UR
61
61
  // Participant types live under `Ablo.Participant.*` —
62
62
  // `Ablo.Participant.Joined`, `Ablo.Participant.Manager`,
63
63
  // `Ablo.Participant.JoinOptions`, etc. Same dot-access shape as
64
- // `Ablo.Peer`, `Ablo.Claim`, `Ablo.Turn`. No flat re-exports.
64
+ // `Ablo.Peer`, `Ablo.Claim`. No flat re-exports.
65
65
  // Advanced — most apps never import this. Principal constructors for
66
66
  // delegated agent paths (`Ablo({ kind: 'agent', as: session({...}) })`).
67
67
  // The default `Ablo({ schema, apiKey })` resolves identity from the key;
@@ -164,10 +164,10 @@ export interface MutationOptions {
164
164
  readonly id: string;
165
165
  } | null;
166
166
  /**
167
- * Active agent turn id to stamp on every delta row produced by this
168
- * commit. Forwarded as the wire-level `causedByTaskId` field on the
169
- * `{ type: 'commit' }` envelope. Set automatically by the SDK while
170
- * `beginTurn(...)` is open.
167
+ * Dormant agent-task lineage field, forwarded as the wire-level
168
+ * `causedByTaskId`. Turns/tasks were removed from the SDK; nothing
169
+ * populates this anymore (write attribution rides on the claim/intent
170
+ * id). Kept optional for wire-compat; always `null` from the client.
171
171
  */
172
172
  causedByTaskId?: string | null;
173
173
  }