@abloatai/ablo 0.9.1 → 0.9.3

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 (79) hide show
  1. package/AGENTS.md +84 -0
  2. package/CHANGELOG.md +40 -0
  3. package/README.md +53 -27
  4. package/dist/BaseSyncedStore.d.ts +2 -36
  5. package/dist/BaseSyncedStore.js +11 -55
  6. package/dist/NetworkMonitor.js +4 -1
  7. package/dist/SyncClient.d.ts +22 -5
  8. package/dist/SyncClient.js +77 -0
  9. package/dist/SyncEngineContext.js +5 -1
  10. package/dist/agent/index.js +1 -1
  11. package/dist/api/index.d.ts +1 -1
  12. package/dist/auth/index.js +3 -1
  13. package/dist/cli.cjs +302645 -0
  14. package/dist/client/Ablo.d.ts +19 -52
  15. package/dist/client/Ablo.js +30 -106
  16. package/dist/client/ApiClient.d.ts +1 -113
  17. package/dist/client/ApiClient.js +39 -238
  18. package/dist/client/auth.js +32 -2
  19. package/dist/client/createInternalComponents.js +1 -1
  20. package/dist/client/createModelProxy.d.ts +9 -0
  21. package/dist/client/createModelProxy.js +34 -10
  22. package/dist/client/httpClient.d.ts +5 -6
  23. package/dist/client/httpClient.js +2 -3
  24. package/dist/client/index.d.ts +1 -1
  25. package/dist/client/persistence.d.ts +6 -1
  26. package/dist/client/persistence.js +1 -1
  27. package/dist/client/registerDataSource.d.ts +4 -4
  28. package/dist/client/registerDataSource.js +39 -31
  29. package/dist/client/writeOptionsSchema.d.ts +50 -0
  30. package/dist/client/writeOptionsSchema.js +57 -0
  31. package/dist/core/index.d.ts +18 -26
  32. package/dist/core/index.js +22 -46
  33. package/dist/errorCodes.d.ts +13 -0
  34. package/dist/errorCodes.js +19 -4
  35. package/dist/index.d.ts +3 -0
  36. package/dist/index.js +8 -1
  37. package/dist/interfaces/index.d.ts +14 -4
  38. package/dist/mutators/UndoManager.d.ts +48 -5
  39. package/dist/mutators/UndoManager.js +166 -1
  40. package/dist/react/AbloProvider.d.ts +18 -8
  41. package/dist/react/index.d.ts +1 -1
  42. package/dist/react/index.js +1 -1
  43. package/dist/react/useUndoScope.js +7 -0
  44. package/dist/schema/ddl.js +2 -1
  45. package/dist/schema/field.js +2 -1
  46. package/dist/schema/serialize.js +2 -1
  47. package/dist/server/commit.d.ts +4 -5
  48. package/dist/server/storage-mode.d.ts +7 -0
  49. package/dist/server/storage-mode.js +6 -0
  50. package/dist/source/adapters/drizzle.js +3 -2
  51. package/dist/source/adapters/kysely.d.ts +68 -0
  52. package/dist/source/adapters/kysely.js +210 -0
  53. package/dist/source/adapters/memory.js +2 -1
  54. package/dist/source/adapters/prisma.js +3 -2
  55. package/dist/source/index.js +2 -1
  56. package/dist/transactions/TransactionQueue.d.ts +6 -7
  57. package/dist/transactions/TransactionQueue.js +33 -9
  58. package/dist/types/streams.d.ts +2 -1
  59. package/dist/utils/duration.js +3 -2
  60. package/dist/wire/frames.d.ts +6 -8
  61. package/docs/api.md +1 -1
  62. package/docs/cli.md +17 -4
  63. package/docs/client-behavior.md +1 -1
  64. package/docs/data-sources.md +129 -125
  65. package/docs/examples/ai-sdk-tool.md +11 -5
  66. package/docs/examples/existing-python-backend.md +26 -4
  67. package/docs/examples/nextjs.md +3 -2
  68. package/docs/examples/scoped-agent.md +38 -11
  69. package/docs/guarantees.md +2 -2
  70. package/docs/identity.md +86 -59
  71. package/docs/index.md +2 -2
  72. package/docs/integration-guide.md +89 -61
  73. package/docs/mcp.md +1 -1
  74. package/docs/quickstart.md +84 -37
  75. package/docs/react.md +39 -28
  76. package/docs/schema-contract.md +2 -4
  77. package/llms-full.txt +360 -0
  78. package/llms.txt +30 -18
  79. package/package.json +23 -3
@@ -28,22 +28,6 @@ import type { IntentStream, IntentWaitOptions, PresenceStream, Snapshot } from '
28
28
  import type { ParticipantManager } from '../sync/participants.js';
29
29
  import type { ActiveIntent, Duration, Intent, TargetRange } from '../types/streams.js';
30
30
  import { type AbloApi, type AbloApiClientOptions, type AbloApiIntents } from './ApiClient.js';
31
- /**
32
- * Handle returned by `engine.beginTurn()`. While alive, every commit
33
- * automatically carries this turn's id on the wire. Call `close(stats?)`
34
- * when the turn finishes, or `dispose()` to abandon without recording
35
- * usage. Idempotent.
36
- */
37
- export interface Turn {
38
- readonly turnId: string;
39
- close(stats?: {
40
- readonly costInputTokens?: number;
41
- readonly costOutputTokens?: number;
42
- readonly costComputeMs?: number;
43
- }): Promise<void>;
44
- dispose(): void;
45
- [Symbol.asyncDispose](): Promise<void>;
46
- }
47
31
  /**
48
32
  * Async function that resolves an apiKey at request time. Use for
49
33
  * credential rotation — rotate from a vault, refresh from session
@@ -137,7 +121,7 @@ export interface AbloOptions<S extends SchemaRecord = SchemaRecord> {
137
121
  * Local persistence mode. Pass `indexeddb` only when you want offline
138
122
  * queueing and a reload-surviving browser cache.
139
123
  *
140
- * @default 'volatile'
124
+ * @default 'memory'
141
125
  */
142
126
  persistence?: AbloPersistence;
143
127
  /**
@@ -266,7 +250,7 @@ export interface InternalAbloOptions<S extends SchemaRecord = SchemaRecord> {
266
250
  /** ObjectPool size limit (default: 10000) */
267
251
  maxPoolSize?: number;
268
252
  /**
269
- * Local persistence mode. Defaults to `volatile` so Ablo behaves like a
253
+ * Local persistence mode. Defaults to `memory` so Ablo behaves like a
270
254
  * point solution for shared state instead of silently bolting IndexedDB
271
255
  * durability onto every browser consumer.
272
256
  *
@@ -278,7 +262,7 @@ export interface InternalAbloOptions<S extends SchemaRecord = SchemaRecord> {
278
262
  offline?: boolean;
279
263
  /**
280
264
  * @deprecated Internal/testing escape hatch. Use `persistence` in
281
- * production code. `true` maps to `volatile`; `false` maps to
265
+ * production code. `true` maps to `memory`; `false` maps to
282
266
  * `indexeddb` in browsers.
283
267
  */
284
268
  inMemory?: boolean;
@@ -479,6 +463,15 @@ export interface CommitCreateOptions {
479
463
  readonly idempotencyKey?: string | null;
480
464
  readonly readAt?: number | null;
481
465
  readonly onStale?: 'reject' | 'force' | 'flag' | 'merge' | null;
466
+ /**
467
+ * A claim handle from `ablo.<model>.claim({ id })` (or the HTTP claim
468
+ * surface). Same vocabulary as the per-model writes: the handle's
469
+ * snapshot watermark becomes the batch `readAt` default and `onStale`
470
+ * defaults to `'reject'`, so a commit that follows a claim is guarded
471
+ * against concurrent edits without re-stating the watermark by hand.
472
+ * Explicit `readAt`/`onStale` on the options win.
473
+ */
474
+ readonly claim?: ClaimHandle<Record<string, unknown>> | null;
482
475
  readonly operation?: CommitOperationInput;
483
476
  readonly operations?: readonly CommitOperationInput[];
484
477
  readonly wait?: CommitWait;
@@ -809,10 +802,13 @@ export type Ablo<S extends SchemaRecord> = {
809
802
  */
810
803
  readonly presence: PresenceStream;
811
804
  /**
812
- * Cooperative-mutex layer over presence announce "I'm about to do
813
- * X on Y" so peers can yield before colliding. Server enforces the
814
- * mutex; rejected announcements surface via `intents.onRejected(...)`.
815
- * Same socket as entity sync, no second connection.
805
+ * @internal the public coordination API is `ablo.<model>.claim`. This
806
+ * accessor is the internal stream `claim` is built on; it is NOT part of the
807
+ * supported public surface and will be moved off the public type (it currently
808
+ * stays only because internal SDK modules are still typed against it).
809
+ *
810
+ * Cooperative-mutex layer over presence — announce "I'm about to do X on Y" so
811
+ * peers can yield before colliding. Same socket as entity sync.
816
812
  */
817
813
  readonly intents: IntentResource;
818
814
  /**
@@ -861,18 +857,6 @@ export type Ablo<S extends SchemaRecord> = {
861
857
  snapshot<ModelName extends keyof S & string>(entities: {
862
858
  readonly [M in ModelName]: string | readonly string[];
863
859
  }): Snapshot<Schema<S>, ModelName>;
864
- /**
865
- * Open a turn — every commit issued while the returned handle is
866
- * alive carries `caused_by_task_id` on the wire so the server
867
- * stamps it onto each delta. Powers `agent_tasks` audit trails.
868
- * Server: `POST /api/agent/turn` with the capability bearer.
869
- */
870
- beginTurn(options: {
871
- readonly prompt: string;
872
- readonly parentTaskId?: string;
873
- readonly surface?: string;
874
- readonly metadata?: Record<string, unknown>;
875
- }): Promise<Turn>;
876
860
  /**
877
861
  * The internal BaseSyncedStore. Implements SyncStoreContract — pass to
878
862
  * SyncContext.Provider so the SDK's useModel/useModels/useMutations hooks
@@ -973,17 +957,6 @@ export declare namespace Ablo {
973
957
  type Options<S extends SchemaRecord = SchemaRecord> = AbloOptions<S>;
974
958
  type Api = AbloApi;
975
959
  type ApiIntents = AbloApiIntents;
976
- type Agent = import('./ApiClient.js').Agent;
977
- type AgentOptions = import('./ApiClient.js').AgentOptions;
978
- type AgentRunOptions = import('./ApiClient.js').AgentRunOptions;
979
- type AgentRunStatus = import('./ApiClient.js').AgentRunStatus;
980
- type AgentRunResult<T> = import('./ApiClient.js').AgentRunResult<T>;
981
- type AgentRunContext = import('./ApiClient.js').AgentRunContext;
982
- type AgentModelClient<T = Record<string, unknown>> = import('./ApiClient.js').AgentModelClient<T>;
983
- type AgentModelReadOptions = import('./ApiClient.js').AgentModelReadOptions;
984
- type AgentModelMutationOptions = import('./ApiClient.js').AgentModelMutationOptions;
985
- type AgentIntentOptions = import('./ApiClient.js').AgentIntentOptions;
986
- type AgentIntentInput = import('./ApiClient.js').AgentIntentInput;
987
960
  type Capability = import('./ApiClient.js').Capability;
988
961
  type CapabilityCreateOptions = import('./ApiClient.js').CapabilityCreateOptions;
989
962
  type CapabilityRecord = import('./ApiClient.js').CapabilityRecord;
@@ -991,11 +964,6 @@ export declare namespace Ablo {
991
964
  type CapabilityRevocation = import('./ApiClient.js').CapabilityRevocation;
992
965
  type CapabilityRotateOptions = import('./ApiClient.js').CapabilityRotateOptions;
993
966
  type RotatedCapability = import('./ApiClient.js').RotatedCapability;
994
- type Task = import('./ApiClient.js').Task;
995
- type TaskCreateOptions = import('./ApiClient.js').TaskCreateOptions;
996
- type TaskCloseOptions = import('./ApiClient.js').TaskCloseOptions;
997
- type TaskCloseResult = import('./ApiClient.js').TaskCloseResult;
998
- type TaskResource = import('./ApiClient.js').TaskResource;
999
967
  type IfClaimedPolicy = import('./Ablo.js').IfClaimedPolicy;
1000
968
  type ClaimedOptions = import('./Ablo.js').ClaimedOptions;
1001
969
  type EntityRef = _Streams.EntityRef;
@@ -1011,7 +979,6 @@ export declare namespace Ablo {
1011
979
  type IntentRejection = _Streams.IntentRejection;
1012
980
  type IntentLost = _Streams.IntentLost;
1013
981
  type Snapshot<TSchema extends _SchemaTypes.Schema = _SchemaTypes.Schema, K extends keyof TSchema['models'] = keyof TSchema['models']> = _Streams.Snapshot<TSchema, K>;
1014
- type Turn = import('./Ablo.js').Turn;
1015
982
  namespace Auth {
1016
983
  type Principal = _Streams.Principal;
1017
984
  type Session = _Streams.SessionRef;
@@ -19,7 +19,7 @@
19
19
  * await sync.reports.delete({ id: reportId });
20
20
  */
21
21
  import { z } from 'zod';
22
- import { AbloClaimedError, AbloError, AbloAuthenticationError, AbloConnectionError, AbloValidationError, translateHttpError, hasWireCode, toAbloError } from '../errors.js';
22
+ import { AbloClaimedError, AbloError, AbloAuthenticationError, AbloConnectionError, AbloValidationError, translateHttpError, toAbloError } from '../errors.js';
23
23
  import { LoadStrategy, PropertyType } from '../types/index.js';
24
24
  import { initSyncEngine } from '../context.js';
25
25
  import { noopObservability, browserOnlineStatus, defaultSessionErrorDetector, noopAnalytics, } from '../SyncEngineContext.js';
@@ -41,6 +41,7 @@ import { assertBrowserSafety, readProcessEnv, resolveApiKey, resolveApiKeyValue,
41
41
  import { registerDataSource } from './registerDataSource.js';
42
42
  import { shouldUseInMemoryPersistence, } from './persistence.js';
43
43
  import { createModelProxy } from './createModelProxy.js';
44
+ import { assertWriteOptions } from './writeOptionsSchema.js';
44
45
  // ── Config derivation from schema ─────────────────────────────────────────
45
46
  /**
46
47
  * Compute a create-priority map from schema `belongsTo` relations using
@@ -812,13 +813,6 @@ export function Ablo(options) {
812
813
  // becomes null, so the first Ablo's commits start throwing
813
814
  // `ws_not_ready` forever (terminal AgentJob writes hang on retry).
814
815
  syncClient.getTransactionQueue().setMutationExecutor(executor);
815
- // Active turn id, set by `beginTurn(...)`, cleared on close. While
816
- // set, every batch commit attaches `causedByTaskId` so server
817
- // delta rows get stamped with it. Single-turn-at-a-time per Ablo
818
- // — opening a second turn overwrites the active id without closing
819
- // the prior. Callers who need parallel turns construct multiple
820
- // Ablo instances, matching the SyncAgent semantics.
821
- let activeTurnId = null;
822
816
  // Presence + intent streams — built eagerly so `engine.presence`
823
817
  // and `engine.intents` return the same reference for the engine's
824
818
  // lifetime. The transport doesn't exist yet (BaseSyncedStore.initialize
@@ -1072,6 +1066,12 @@ export function Ablo(options) {
1072
1066
  return intent;
1073
1067
  return intent?.id;
1074
1068
  }
1069
+ function isClaimHandleValue(value) {
1070
+ return (typeof value === 'object' &&
1071
+ value !== null &&
1072
+ value.object === 'claim' &&
1073
+ typeof value.claimId === 'string');
1074
+ }
1075
1075
  function normalizeCommitOperation(op, defaults) {
1076
1076
  const model = op.model ?? op.target?.model;
1077
1077
  if (!model) {
@@ -1348,10 +1348,26 @@ export function Ablo(options) {
1348
1348
  const commits = {
1349
1349
  async create(commitOptions) {
1350
1350
  await ready();
1351
+ // Same runtime contract as the per-model writes — one schema.
1352
+ assertWriteOptions({
1353
+ idempotencyKey: commitOptions.idempotencyKey,
1354
+ readAt: commitOptions.readAt,
1355
+ onStale: commitOptions.onStale,
1356
+ wait: commitOptions.wait,
1357
+ intent: commitOptions.intent,
1358
+ }, 'commits.create');
1351
1359
  const clientTxId = createClientTxId(commitOptions.idempotencyKey);
1352
- const operations = normalizeCommitOperations(commitOptions);
1360
+ // A claim handle supplies the batch stale-guard defaults — same
1361
+ // semantics as `ablo.<model>.update({ id, data, claim })`, so the
1362
+ // two write doors speak one claim vocabulary. Explicit options win.
1363
+ const claim = commitOptions.claim ?? null;
1364
+ const operations = normalizeCommitOperations({
1365
+ ...commitOptions,
1366
+ readAt: commitOptions.readAt ?? claim?.readAt ?? null,
1367
+ onStale: commitOptions.onStale ?? (claim?.readAt !== undefined ? 'reject' : null),
1368
+ });
1353
1369
  const wait = commitOptions.wait ?? 'confirmed';
1354
- const intentId = normalizeIntentId(commitOptions.intent);
1370
+ const intentId = normalizeIntentId(commitOptions.intent) ?? claim?.claimId;
1355
1371
  void intentId; // The current wire clears intents by entity after commit.
1356
1372
  // Route through the TransactionQueue's commit lane so the call
1357
1373
  // tolerates WS disconnects: the envelope stays in memory until
@@ -1363,9 +1379,7 @@ export function Ablo(options) {
1363
1379
  // SyncClient we already hold from createInternalComponents —
1364
1380
  // no need to leak an accessor through BaseSyncedStore.
1365
1381
  const queue = syncClient.getTransactionQueue();
1366
- queue.enqueueCommit(clientTxId, operations, {
1367
- causedByTaskId: activeTurnId,
1368
- });
1382
+ queue.enqueueCommit(clientTxId, operations);
1369
1383
  if (wait === 'queued') {
1370
1384
  return { id: clientTxId, status: 'queued' };
1371
1385
  }
@@ -1431,6 +1445,7 @@ export function Ablo(options) {
1431
1445
  idempotencyKey: params.idempotencyKey,
1432
1446
  readAt: params.readAt,
1433
1447
  onStale: params.onStale,
1448
+ ...(isClaimHandleValue(params.claim) ? { claim: params.claim } : {}),
1434
1449
  wait: params.wait,
1435
1450
  operations: [
1436
1451
  {
@@ -1449,6 +1464,7 @@ export function Ablo(options) {
1449
1464
  idempotencyKey: params.idempotencyKey,
1450
1465
  readAt: params.readAt,
1451
1466
  onStale: params.onStale,
1467
+ ...(isClaimHandleValue(params.claim) ? { claim: params.claim } : {}),
1452
1468
  wait: params.wait,
1453
1469
  operations: [
1454
1470
  {
@@ -1467,6 +1483,7 @@ export function Ablo(options) {
1467
1483
  idempotencyKey: params.idempotencyKey,
1468
1484
  readAt: params.readAt,
1469
1485
  onStale: params.onStale,
1486
+ ...(isClaimHandleValue(params.claim) ? { claim: params.claim } : {}),
1470
1487
  wait: params.wait,
1471
1488
  operations: [
1472
1489
  {
@@ -1638,99 +1655,6 @@ export function Ablo(options) {
1638
1655
  entities,
1639
1656
  });
1640
1657
  },
1641
- // ── Turn handles ────────────────────────────────────────────────
1642
- //
1643
- // Open a turn — every commit issued while the returned handle is
1644
- // alive carries `caused_by_task_id` on the wire so the server
1645
- // stamps it onto each delta. The product surface this powers:
1646
- // `agent_tasks` audit trails ("which AI prompt produced this
1647
- // mutation"), parent/child turn chains, cost accounting per turn.
1648
- //
1649
- // POST /api/agent/turn (capability bearer) → returns turnId.
1650
- // POST /api/agent/turn/:id/close (capability bearer) → records
1651
- // final cost stats. Idempotent.
1652
- async beginTurn(beginOptions) {
1653
- const baseUrl = url.replace(/\/+$/, '');
1654
- const turnUrl = `${baseUrl.replace(/^ws/, 'http')}/api/agent/turn`;
1655
- const headers = authCredentials.withAuthHeaders({ 'Content-Type': 'application/json' });
1656
- const res = await fetch(turnUrl, {
1657
- method: 'POST',
1658
- headers,
1659
- body: JSON.stringify({
1660
- prompt: beginOptions.prompt,
1661
- parentTaskId: beginOptions.parentTaskId,
1662
- surface: beginOptions.surface,
1663
- metadata: beginOptions.metadata,
1664
- }),
1665
- });
1666
- if (!res.ok) {
1667
- const text = await res.text().catch(() => '');
1668
- let parsed = text;
1669
- if (text) {
1670
- try {
1671
- parsed = JSON.parse(text);
1672
- }
1673
- catch {
1674
- /* keep raw text */
1675
- }
1676
- }
1677
- // Preserve the server's structured envelope (code/message/doc_url) when
1678
- // present; fall back to turn_open_failed for a bare/non-Ablo body.
1679
- throw hasWireCode(parsed)
1680
- ? translateHttpError(res.status, parsed, res.headers.get('x-request-id') ?? undefined)
1681
- : new AbloError(`beginTurn failed: ${res.status} ${text}`, {
1682
- code: 'turn_open_failed',
1683
- httpStatus: res.status,
1684
- });
1685
- }
1686
- const json = (await res.json());
1687
- const turnId = json.turnId;
1688
- activeTurnId = turnId;
1689
- let closed = false;
1690
- const close = async (stats) => {
1691
- if (closed)
1692
- return;
1693
- closed = true;
1694
- if (activeTurnId === turnId)
1695
- activeTurnId = null;
1696
- const closeUrl = `${turnUrl}/${encodeURIComponent(turnId)}/close`;
1697
- const closeRes = await fetch(closeUrl, {
1698
- method: 'POST',
1699
- headers,
1700
- body: JSON.stringify({
1701
- costInputTokens: stats?.costInputTokens ?? 0,
1702
- costOutputTokens: stats?.costOutputTokens ?? 0,
1703
- costComputeMs: stats?.costComputeMs ?? 0,
1704
- }),
1705
- });
1706
- if (!closeRes.ok) {
1707
- const text = await closeRes.text().catch(() => '');
1708
- let parsed = text;
1709
- if (text) {
1710
- try {
1711
- parsed = JSON.parse(text);
1712
- }
1713
- catch {
1714
- /* keep raw text */
1715
- }
1716
- }
1717
- throw hasWireCode(parsed)
1718
- ? translateHttpError(closeRes.status, parsed, closeRes.headers.get('x-request-id') ?? undefined)
1719
- : new AbloError(`closeTurn failed: ${closeRes.status} ${text}`, {
1720
- code: 'turn_close_failed',
1721
- httpStatus: closeRes.status,
1722
- });
1723
- }
1724
- };
1725
- const dispose = () => {
1726
- if (closed)
1727
- return;
1728
- closed = true;
1729
- if (activeTurnId === turnId)
1730
- activeTurnId = null;
1731
- };
1732
- return { turnId, close, dispose, [Symbol.asyncDispose]: () => close() };
1733
- },
1734
1658
  };
1735
1659
  return engine;
1736
1660
  }
@@ -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,127 +115,15 @@ 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