@abloatai/ablo 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +208 -0
- package/LICENSE +201 -0
- package/NOTICE +12 -0
- package/README.md +230 -0
- package/dist/BaseSyncedStore.d.ts +709 -0
- package/dist/BaseSyncedStore.js +1843 -0
- package/dist/Database.d.ts +344 -0
- package/dist/Database.js +1259 -0
- package/dist/LazyReferenceCollection.d.ts +181 -0
- package/dist/LazyReferenceCollection.js +460 -0
- package/dist/Model.d.ts +339 -0
- package/dist/Model.js +715 -0
- package/dist/ModelRegistry.d.ts +200 -0
- package/dist/ModelRegistry.js +535 -0
- package/dist/NetworkMonitor.d.ts +27 -0
- package/dist/NetworkMonitor.js +73 -0
- package/dist/ObjectPool.d.ts +202 -0
- package/dist/ObjectPool.js +1106 -0
- package/dist/SyncClient.d.ts +489 -0
- package/dist/SyncClient.js +1555 -0
- package/dist/SyncEngineContext.d.ts +46 -0
- package/dist/SyncEngineContext.js +74 -0
- package/dist/adapters/alwaysOnline.d.ts +16 -0
- package/dist/adapters/alwaysOnline.js +19 -0
- package/dist/adapters/inMemoryStorage.d.ts +30 -0
- package/dist/adapters/inMemoryStorage.js +94 -0
- package/dist/agent/Agent.d.ts +358 -0
- package/dist/agent/Agent.js +500 -0
- package/dist/agent/index.d.ts +115 -0
- package/dist/agent/index.js +128 -0
- package/dist/agent/session.d.ts +90 -0
- package/dist/agent/session.js +156 -0
- package/dist/agent/types.d.ts +73 -0
- package/dist/agent/types.js +10 -0
- package/dist/ai-sdk/coordination-context.d.ts +51 -0
- package/dist/ai-sdk/coordination-context.js +107 -0
- package/dist/ai-sdk/index.d.ts +68 -0
- package/dist/ai-sdk/index.js +68 -0
- package/dist/ai-sdk/intent-broadcast.d.ts +77 -0
- package/dist/ai-sdk/intent-broadcast.js +72 -0
- package/dist/ai-sdk/wrap.d.ts +67 -0
- package/dist/ai-sdk/wrap.js +45 -0
- package/dist/api/index.d.ts +10 -0
- package/dist/api/index.js +9 -0
- package/dist/auth/index.d.ts +137 -0
- package/dist/auth/index.js +246 -0
- package/dist/client/Ablo.d.ts +835 -0
- package/dist/client/Ablo.js +1440 -0
- package/dist/client/ApiClient.d.ts +200 -0
- package/dist/client/ApiClient.js +659 -0
- package/dist/client/auth.d.ts +79 -0
- package/dist/client/auth.js +81 -0
- package/dist/client/createInternalComponents.d.ts +44 -0
- package/dist/client/createInternalComponents.js +88 -0
- package/dist/client/createModelProxy.d.ts +152 -0
- package/dist/client/createModelProxy.js +199 -0
- package/dist/client/identity.d.ts +63 -0
- package/dist/client/identity.js +156 -0
- package/dist/client/index.d.ts +36 -0
- package/dist/client/index.js +33 -0
- package/dist/client/persistence.d.ts +7 -0
- package/dist/client/persistence.js +11 -0
- package/dist/client/validateAbloOptions.d.ts +42 -0
- package/dist/client/validateAbloOptions.js +43 -0
- package/dist/config/index.d.ts +10 -0
- package/dist/config/index.js +12 -0
- package/dist/context.d.ts +27 -0
- package/dist/context.js +58 -0
- package/dist/core/DatabaseManager.d.ts +108 -0
- package/dist/core/DatabaseManager.js +361 -0
- package/dist/core/QueryProcessor.d.ts +77 -0
- package/dist/core/QueryProcessor.js +262 -0
- package/dist/core/QueryView.d.ts +64 -0
- package/dist/core/QueryView.js +219 -0
- package/dist/core/StoreManager.d.ts +131 -0
- package/dist/core/StoreManager.js +334 -0
- package/dist/core/ViewRegistry.d.ts +20 -0
- package/dist/core/ViewRegistry.js +55 -0
- package/dist/core/index.d.ts +34 -0
- package/dist/core/index.js +59 -0
- package/dist/core/openIDBWithTimeout.d.ts +27 -0
- package/dist/core/openIDBWithTimeout.js +63 -0
- package/dist/core/query-utils.d.ts +37 -0
- package/dist/core/query-utils.js +60 -0
- package/dist/errors.d.ts +235 -0
- package/dist/errors.js +243 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +82 -0
- package/dist/interfaces/headless.d.ts +95 -0
- package/dist/interfaces/headless.js +41 -0
- package/dist/interfaces/index.d.ts +321 -0
- package/dist/interfaces/index.js +8 -0
- package/dist/mutators/RecordingTransaction.d.ts +36 -0
- package/dist/mutators/RecordingTransaction.js +216 -0
- package/dist/mutators/Transaction.d.ts +48 -0
- package/dist/mutators/Transaction.js +64 -0
- package/dist/mutators/UndoManager.d.ts +114 -0
- package/dist/mutators/UndoManager.js +143 -0
- package/dist/mutators/defineMutators.d.ts +55 -0
- package/dist/mutators/defineMutators.js +28 -0
- package/dist/policy/index.d.ts +19 -0
- package/dist/policy/index.js +18 -0
- package/dist/policy/types.d.ts +74 -0
- package/dist/policy/types.js +17 -0
- package/dist/principal.d.ts +44 -0
- package/dist/principal.js +49 -0
- package/dist/query/client.d.ts +43 -0
- package/dist/query/client.js +84 -0
- package/dist/query/index.d.ts +6 -0
- package/dist/query/index.js +5 -0
- package/dist/query/types.d.ts +143 -0
- package/dist/query/types.js +36 -0
- package/dist/react/AbloProvider.d.ts +205 -0
- package/dist/react/AbloProvider.js +398 -0
- package/dist/react/ClientSideSuspense.d.ts +36 -0
- package/dist/react/ClientSideSuspense.js +17 -0
- package/dist/react/DefaultFallback.d.ts +24 -0
- package/dist/react/DefaultFallback.js +43 -0
- package/dist/react/SyncGroupProvider.d.ts +19 -0
- package/dist/react/SyncGroupProvider.js +44 -0
- package/dist/react/context.d.ts +161 -0
- package/dist/react/context.js +35 -0
- package/dist/react/index.d.ts +64 -0
- package/dist/react/index.js +73 -0
- package/dist/react/internalContext.d.ts +35 -0
- package/dist/react/internalContext.js +3 -0
- package/dist/react/useAblo.d.ts +72 -0
- package/dist/react/useAblo.js +63 -0
- package/dist/react/useCurrentUserId.d.ts +21 -0
- package/dist/react/useCurrentUserId.js +33 -0
- package/dist/react/useErrorListener.d.ts +20 -0
- package/dist/react/useErrorListener.js +39 -0
- package/dist/react/useIntent.d.ts +29 -0
- package/dist/react/useIntent.js +42 -0
- package/dist/react/useMutate.d.ts +83 -0
- package/dist/react/useMutate.js +122 -0
- package/dist/react/useMutationFailureListener.d.ts +26 -0
- package/dist/react/useMutationFailureListener.js +38 -0
- package/dist/react/useMutators.d.ts +56 -0
- package/dist/react/useMutators.js +66 -0
- package/dist/react/usePresence.d.ts +32 -0
- package/dist/react/usePresence.js +41 -0
- package/dist/react/useQuery.d.ts +123 -0
- package/dist/react/useQuery.js +145 -0
- package/dist/react/useReactive.d.ts +35 -0
- package/dist/react/useReactive.js +111 -0
- package/dist/react/useReader.d.ts +69 -0
- package/dist/react/useReader.js +73 -0
- package/dist/react/useSyncStatus.d.ts +61 -0
- package/dist/react/useSyncStatus.js +76 -0
- package/dist/react/useUndoScope.d.ts +36 -0
- package/dist/react/useUndoScope.js +73 -0
- package/dist/realtime/index.d.ts +10 -0
- package/dist/realtime/index.js +9 -0
- package/dist/schema/field.d.ts +134 -0
- package/dist/schema/field.js +264 -0
- package/dist/schema/index.d.ts +29 -0
- package/dist/schema/index.js +38 -0
- package/dist/schema/model.d.ts +326 -0
- package/dist/schema/model.js +89 -0
- package/dist/schema/queries.d.ts +203 -0
- package/dist/schema/queries.js +145 -0
- package/dist/schema/relation.d.ts +172 -0
- package/dist/schema/relation.js +104 -0
- package/dist/schema/schema.d.ts +259 -0
- package/dist/schema/schema.js +188 -0
- package/dist/schema/sugar.d.ts +129 -0
- package/dist/schema/sugar.js +94 -0
- package/dist/source/index.d.ts +423 -0
- package/dist/source/index.js +320 -0
- package/dist/source/pushQueue.d.ts +112 -0
- package/dist/source/pushQueue.js +249 -0
- package/dist/stores/ObjectStore.d.ts +103 -0
- package/dist/stores/ObjectStore.js +371 -0
- package/dist/stores/ObjectStoreContract.d.ts +39 -0
- package/dist/stores/ObjectStoreContract.js +1 -0
- package/dist/stores/SyncActionStore.d.ts +101 -0
- package/dist/stores/SyncActionStore.js +481 -0
- package/dist/sync/BootstrapHelper.d.ts +127 -0
- package/dist/sync/BootstrapHelper.js +434 -0
- package/dist/sync/ConnectionManager.d.ts +136 -0
- package/dist/sync/ConnectionManager.js +465 -0
- package/dist/sync/HydrationCoordinator.d.ts +137 -0
- package/dist/sync/HydrationCoordinator.js +468 -0
- package/dist/sync/NetworkProbe.d.ts +43 -0
- package/dist/sync/NetworkProbe.js +113 -0
- package/dist/sync/OfflineFlush.d.ts +9 -0
- package/dist/sync/OfflineFlush.js +22 -0
- package/dist/sync/OfflineTransactionStore.d.ts +37 -0
- package/dist/sync/OfflineTransactionStore.js +263 -0
- package/dist/sync/SyncWebSocket.d.ts +663 -0
- package/dist/sync/SyncWebSocket.js +1336 -0
- package/dist/sync/createIntentStream.d.ts +33 -0
- package/dist/sync/createIntentStream.js +243 -0
- package/dist/sync/createPresenceStream.d.ts +46 -0
- package/dist/sync/createPresenceStream.js +192 -0
- package/dist/sync/createSnapshot.d.ts +33 -0
- package/dist/sync/createSnapshot.js +124 -0
- package/dist/sync/participants.d.ts +114 -0
- package/dist/sync/participants.js +336 -0
- package/dist/sync/schemas.d.ts +79 -0
- package/dist/sync/schemas.js +78 -0
- package/dist/testing/fixtures/bootstrap.d.ts +45 -0
- package/dist/testing/fixtures/bootstrap.js +53 -0
- package/dist/testing/fixtures/deltas.d.ts +86 -0
- package/dist/testing/fixtures/deltas.js +139 -0
- package/dist/testing/fixtures/models.d.ts +82 -0
- package/dist/testing/fixtures/models.js +270 -0
- package/dist/testing/helpers/react-wrapper.d.ts +66 -0
- package/dist/testing/helpers/react-wrapper.js +64 -0
- package/dist/testing/helpers/sync-engine-harness.d.ts +55 -0
- package/dist/testing/helpers/sync-engine-harness.js +70 -0
- package/dist/testing/helpers/wait.d.ts +25 -0
- package/dist/testing/helpers/wait.js +44 -0
- package/dist/testing/index.d.ts +21 -0
- package/dist/testing/index.js +32 -0
- package/dist/testing/mocks/MockMutationExecutor.d.ts +65 -0
- package/dist/testing/mocks/MockMutationExecutor.js +139 -0
- package/dist/testing/mocks/MockNetworkMonitor.d.ts +20 -0
- package/dist/testing/mocks/MockNetworkMonitor.js +46 -0
- package/dist/testing/mocks/MockSyncContext.d.ts +64 -0
- package/dist/testing/mocks/MockSyncContext.js +100 -0
- package/dist/testing/mocks/MockSyncStore.d.ts +88 -0
- package/dist/testing/mocks/MockSyncStore.js +171 -0
- package/dist/testing/mocks/MockWebSocket.d.ts +66 -0
- package/dist/testing/mocks/MockWebSocket.js +117 -0
- package/dist/transactions/OptimisticEchoTracker.d.ts +82 -0
- package/dist/transactions/OptimisticEchoTracker.js +104 -0
- package/dist/transactions/TransactionQueue.d.ts +499 -0
- package/dist/transactions/TransactionQueue.js +1895 -0
- package/dist/transactions/index.d.ts +16 -0
- package/dist/transactions/index.js +7 -0
- package/dist/transactions/mutation-error-handler.d.ts +5 -0
- package/dist/transactions/mutation-error-handler.js +39 -0
- package/dist/types/global.d.ts +107 -0
- package/dist/types/global.js +38 -0
- package/dist/types/index.d.ts +241 -0
- package/dist/types/index.js +70 -0
- package/dist/types/streams.d.ts +495 -0
- package/dist/types/streams.js +11 -0
- package/dist/utils/asyncIterator.d.ts +41 -0
- package/dist/utils/asyncIterator.js +142 -0
- package/dist/utils/duration.d.ts +28 -0
- package/dist/utils/duration.js +47 -0
- package/dist/utils/mobx-setup.d.ts +42 -0
- package/dist/utils/mobx-setup.js +381 -0
- package/docs/api-keys.md +24 -0
- package/docs/api.md +230 -0
- package/docs/audit.md +81 -0
- package/docs/capabilities.md +163 -0
- package/docs/client-behavior.md +202 -0
- package/docs/data-sources.md +214 -0
- package/docs/examples/agent-human.md +84 -0
- package/docs/examples/ai-sdk-tool.md +92 -0
- package/docs/examples/existing-python-backend.md +249 -0
- package/docs/examples/nextjs.md +88 -0
- package/docs/examples/server-agent.md +86 -0
- package/docs/guarantees.md +148 -0
- package/docs/index.md +97 -0
- package/docs/integration-guide.md +493 -0
- package/docs/interaction-model.md +140 -0
- package/docs/mcp/claude-code.md +43 -0
- package/docs/mcp/cursor.md +53 -0
- package/docs/mcp/windsurf.md +46 -0
- package/docs/mcp.md +59 -0
- package/docs/quickstart.md +152 -0
- package/docs/react.md +115 -0
- package/docs/roadmap.md +45 -0
- package/examples/README.md +54 -0
- package/examples/data-source/README.md +102 -0
- package/examples/data-source/ablo-driver.ts +89 -0
- package/examples/data-source/customer-server.ts +208 -0
- package/examples/data-source/run.ts +101 -0
- package/examples/data-source/schema.ts +25 -0
- package/examples/quickstart.ts +54 -0
- package/examples/tsconfig.json +16 -0
- package/llms.txt +143 -0
- package/package.json +147 -0
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stateless API client for `Ablo({ apiKey })`.
|
|
3
|
+
*
|
|
4
|
+
* This is the hosted-API product surface: no schema, no object pool, no
|
|
5
|
+
* IndexedDB, no WebSocket. It maps the public Resource / Intent / Commit
|
|
6
|
+
* nouns directly to HTTP routes on sync-server.
|
|
7
|
+
*/
|
|
8
|
+
import { AbloBusyError, AbloAuthenticationError, AbloConnectionError, AbloValidationError, translateHttpError, } from '../errors.js';
|
|
9
|
+
import { assertBrowserSafety, readProcessEnv, resolveApiKey, resolveApiKeyValue, resolveAuthToken, resolveBaseURL, resolveBootstrapBaseUrl, } from './auth.js';
|
|
10
|
+
import { toSeconds } from '../utils/duration.js';
|
|
11
|
+
const DEFAULT_AGENT_LEASE = '10m';
|
|
12
|
+
const DEFAULT_INTENT_LEASE = '2m';
|
|
13
|
+
export function createProtocolClient(options) {
|
|
14
|
+
const env = readProcessEnv();
|
|
15
|
+
const authInput = { options, env };
|
|
16
|
+
const configuredApiKey = resolveApiKey(authInput);
|
|
17
|
+
const configuredAuthToken = resolveAuthToken(authInput);
|
|
18
|
+
assertBrowserSafety({
|
|
19
|
+
apiKey: configuredApiKey,
|
|
20
|
+
dangerouslyAllowBrowser: options.dangerouslyAllowBrowser,
|
|
21
|
+
});
|
|
22
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
23
|
+
if (typeof fetchImpl !== 'function') {
|
|
24
|
+
throw new AbloConnectionError('Ablo API client requires a fetch implementation. Pass `fetch` in Ablo({ ... }) for this runtime.', { code: 'fetch_unavailable' });
|
|
25
|
+
}
|
|
26
|
+
const url = resolveBaseURL(authInput);
|
|
27
|
+
const apiBaseUrl = resolveBootstrapBaseUrl({
|
|
28
|
+
url,
|
|
29
|
+
bootstrapBaseUrl: options.bootstrapBaseUrl,
|
|
30
|
+
}).replace(/\/+$/, '');
|
|
31
|
+
async function authHeaders() {
|
|
32
|
+
const apiKey = await resolveApiKeyValue(configuredApiKey);
|
|
33
|
+
const token = apiKey ?? configuredAuthToken;
|
|
34
|
+
if (!token) {
|
|
35
|
+
throw new AbloAuthenticationError('Ablo({ apiKey }) requires an API key. Pass `apiKey` or set ABLO_API_KEY.', { code: 'api_key_required' });
|
|
36
|
+
}
|
|
37
|
+
const headers = {
|
|
38
|
+
'Content-Type': 'application/json',
|
|
39
|
+
Authorization: `Bearer ${token}`,
|
|
40
|
+
};
|
|
41
|
+
for (const [key, value] of Object.entries(options.defaultHeaders ?? {})) {
|
|
42
|
+
if (value == null) {
|
|
43
|
+
delete headers[key];
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
headers[key] = value;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return headers;
|
|
50
|
+
}
|
|
51
|
+
function endpoint(path) {
|
|
52
|
+
const target = new URL(`${apiBaseUrl}${path.startsWith('/') ? path : `/${path}`}`);
|
|
53
|
+
for (const [key, value] of Object.entries(options.defaultQuery ?? {})) {
|
|
54
|
+
if (value !== undefined)
|
|
55
|
+
target.searchParams.set(key, value);
|
|
56
|
+
}
|
|
57
|
+
return target.toString();
|
|
58
|
+
}
|
|
59
|
+
async function requestJson(path, init) {
|
|
60
|
+
const { idempotencyKey, ...requestInit } = init;
|
|
61
|
+
const headers = await authHeaders();
|
|
62
|
+
if (idempotencyKey)
|
|
63
|
+
headers['Idempotency-Key'] = idempotencyKey;
|
|
64
|
+
const res = await fetchImpl(endpoint(path), {
|
|
65
|
+
...requestInit,
|
|
66
|
+
headers: {
|
|
67
|
+
...headers,
|
|
68
|
+
...requestInit.headers,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
const bodyText = await res.text();
|
|
72
|
+
const body = parseBody(bodyText);
|
|
73
|
+
if (!res.ok) {
|
|
74
|
+
throw translateHttpError(res.status, body ?? `Ablo API request failed: ${res.status} ${res.statusText}`, res.headers.get('x-request-id') ?? undefined);
|
|
75
|
+
}
|
|
76
|
+
return body;
|
|
77
|
+
}
|
|
78
|
+
function createClientTxId(idempotencyKey) {
|
|
79
|
+
if (idempotencyKey && idempotencyKey.length > 0)
|
|
80
|
+
return idempotencyKey;
|
|
81
|
+
return typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
|
|
82
|
+
? crypto.randomUUID()
|
|
83
|
+
: `tx_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
84
|
+
}
|
|
85
|
+
function createIntentId() {
|
|
86
|
+
return typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
|
|
87
|
+
? `int_${crypto.randomUUID()}`
|
|
88
|
+
: `int_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
89
|
+
}
|
|
90
|
+
function createResourceId() {
|
|
91
|
+
return typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
|
|
92
|
+
? crypto.randomUUID()
|
|
93
|
+
: `id_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
94
|
+
}
|
|
95
|
+
function childClient(authToken) {
|
|
96
|
+
return createProtocolClient({
|
|
97
|
+
...options,
|
|
98
|
+
apiKey: null,
|
|
99
|
+
authToken,
|
|
100
|
+
schema: null,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
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
|
+
resource(name) {
|
|
167
|
+
return createAgentResourceClient(agentClient, name);
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function createAgentResourceClient(agentClient, name) {
|
|
172
|
+
const base = agentClient.resource(name);
|
|
173
|
+
return {
|
|
174
|
+
retrieve(id, options) {
|
|
175
|
+
return base.retrieve(id, withAgentBusyDefault(options));
|
|
176
|
+
},
|
|
177
|
+
create(data, mutationOptions) {
|
|
178
|
+
const id = mutationOptions?.id ?? createResourceId();
|
|
179
|
+
return withAgentIntent(agentClient, name, id, mutationOptions, (commitIntent) => base.create(data, {
|
|
180
|
+
...stripAgentRuntimeOptions(mutationOptions),
|
|
181
|
+
id,
|
|
182
|
+
intent: commitIntent,
|
|
183
|
+
}));
|
|
184
|
+
},
|
|
185
|
+
update(id, data, mutationOptions) {
|
|
186
|
+
return withAgentIntent(agentClient, name, id, mutationOptions, (commitIntent) => base.update(id, data, {
|
|
187
|
+
...stripAgentRuntimeOptions(mutationOptions),
|
|
188
|
+
intent: commitIntent,
|
|
189
|
+
}));
|
|
190
|
+
},
|
|
191
|
+
delete(id, mutationOptions) {
|
|
192
|
+
return withAgentIntent(agentClient, name, id, mutationOptions, (commitIntent) => base.delete(id, {
|
|
193
|
+
...stripAgentRuntimeOptions(mutationOptions),
|
|
194
|
+
intent: commitIntent,
|
|
195
|
+
}));
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
async function withAgentIntent(agentClient, resourceName, id, mutationOptions, commit) {
|
|
200
|
+
const intentInput = mutationOptions?.intent;
|
|
201
|
+
const targetOverride = intentInput != null && typeof intentInput === 'object' && !isIntentHandleRef(intentInput)
|
|
202
|
+
? intentInput.target ?? {}
|
|
203
|
+
: {};
|
|
204
|
+
const target = {
|
|
205
|
+
...targetOverride,
|
|
206
|
+
resource: targetOverride.resource ?? resourceName,
|
|
207
|
+
id: targetOverride.id ?? id,
|
|
208
|
+
...(intentInput != null && typeof intentInput === 'object' && !isIntentHandleRef(intentInput) && intentInput.field
|
|
209
|
+
? { field: intentInput.field }
|
|
210
|
+
: {}),
|
|
211
|
+
};
|
|
212
|
+
await applyBusyPolicy(target, withAgentBusyDefault(mutationOptions), 'wait');
|
|
213
|
+
if (intentInput == null || isIntentHandleRef(intentInput)) {
|
|
214
|
+
return commit(intentInput);
|
|
215
|
+
}
|
|
216
|
+
const action = typeof intentInput === 'string' ? intentInput : intentInput.action;
|
|
217
|
+
const intent = await agentClient.intents.create({
|
|
218
|
+
target,
|
|
219
|
+
action,
|
|
220
|
+
ttl: typeof intentInput === 'object'
|
|
221
|
+
? intentInput.ttl ?? DEFAULT_INTENT_LEASE
|
|
222
|
+
: DEFAULT_INTENT_LEASE,
|
|
223
|
+
});
|
|
224
|
+
try {
|
|
225
|
+
return await commit(intent);
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
await intent.release().catch(() => { });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function normalizeCommitOperation(op, defaults) {
|
|
232
|
+
const resource = op.resource ?? op.target?.resource;
|
|
233
|
+
if (!resource) {
|
|
234
|
+
throw new AbloValidationError('Commit operation requires `resource` or `target.resource`.', { code: 'commit_operation_resource_required' });
|
|
235
|
+
}
|
|
236
|
+
const id = op.id ?? op.target?.id ?? null;
|
|
237
|
+
return {
|
|
238
|
+
action: op.action,
|
|
239
|
+
resource,
|
|
240
|
+
id,
|
|
241
|
+
data: op.data ?? null,
|
|
242
|
+
transactionId: op.transactionId ?? null,
|
|
243
|
+
readAt: op.readAt ?? defaults.readAt ?? null,
|
|
244
|
+
onStale: op.onStale ?? defaults.onStale ?? null,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function normalizeCommitOperations(commitOptions) {
|
|
248
|
+
if (commitOptions.operation && commitOptions.operations) {
|
|
249
|
+
throw new AbloValidationError('Pass either `operation` or `operations`, not both.', { code: 'commit_operations_ambiguous' });
|
|
250
|
+
}
|
|
251
|
+
const inputOperations = commitOptions.operation
|
|
252
|
+
? [commitOptions.operation]
|
|
253
|
+
: commitOptions.operations ?? [];
|
|
254
|
+
if (inputOperations.length === 0) {
|
|
255
|
+
throw new AbloValidationError('Commit requires at least one operation.', { code: 'commit_operation_required' });
|
|
256
|
+
}
|
|
257
|
+
return inputOperations.map((op) => normalizeCommitOperation(op, commitOptions));
|
|
258
|
+
}
|
|
259
|
+
async function listIntents(target) {
|
|
260
|
+
const params = new URLSearchParams();
|
|
261
|
+
if (target?.resource)
|
|
262
|
+
params.set('resource', target.resource);
|
|
263
|
+
if (target?.id)
|
|
264
|
+
params.set('id', target.id);
|
|
265
|
+
if (target?.field)
|
|
266
|
+
params.set('field', target.field);
|
|
267
|
+
const suffix = params.toString();
|
|
268
|
+
const body = await requestJson(`/v1/intents${suffix ? `?${suffix}` : ''}`, { method: 'GET' });
|
|
269
|
+
return body.intents ?? [];
|
|
270
|
+
}
|
|
271
|
+
function busyError(target, intents, code) {
|
|
272
|
+
const label = [target.resource, target.id, target.field].filter(Boolean).join('/');
|
|
273
|
+
const holder = intents[0];
|
|
274
|
+
const suffix = holder
|
|
275
|
+
? ` held by ${holder.actor} (${holder.action})`
|
|
276
|
+
: ' held by another participant';
|
|
277
|
+
return new AbloBusyError(`Resource is busy: ${label || 'target'}${suffix}.`, { code, intents });
|
|
278
|
+
}
|
|
279
|
+
function delay(ms, signal) {
|
|
280
|
+
if (signal?.aborted) {
|
|
281
|
+
return Promise.reject(new AbloConnectionError('Intent wait aborted.', {
|
|
282
|
+
code: 'intent_wait_aborted',
|
|
283
|
+
cause: signal.reason,
|
|
284
|
+
}));
|
|
285
|
+
}
|
|
286
|
+
return new Promise((resolve, reject) => {
|
|
287
|
+
const timeout = setTimeout(done, ms);
|
|
288
|
+
function cleanup() {
|
|
289
|
+
clearTimeout(timeout);
|
|
290
|
+
signal?.removeEventListener('abort', onAbort);
|
|
291
|
+
}
|
|
292
|
+
function done() {
|
|
293
|
+
cleanup();
|
|
294
|
+
resolve();
|
|
295
|
+
}
|
|
296
|
+
function onAbort() {
|
|
297
|
+
cleanup();
|
|
298
|
+
reject(new AbloConnectionError('Intent wait aborted.', {
|
|
299
|
+
code: 'intent_wait_aborted',
|
|
300
|
+
cause: signal?.reason,
|
|
301
|
+
}));
|
|
302
|
+
}
|
|
303
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
async function waitForNoIntents(target, options) {
|
|
307
|
+
const startedAt = Date.now();
|
|
308
|
+
const pollInterval = options?.pollInterval;
|
|
309
|
+
for (;;) {
|
|
310
|
+
const intents = await listIntents(target);
|
|
311
|
+
if (intents.length === 0)
|
|
312
|
+
return;
|
|
313
|
+
if (pollInterval == null) {
|
|
314
|
+
throw new AbloValidationError('Cannot wait for intents over the schema-less HTTP client without `pollInterval`. ' +
|
|
315
|
+
'Use the schema client for event-driven intent waits, pass `ifBusy: "return"`, ' +
|
|
316
|
+
'or provide an explicit poll interval for this runtime.', { code: 'intent_wait_poll_interval_required' });
|
|
317
|
+
}
|
|
318
|
+
if (options?.timeout != null && Date.now() - startedAt >= options.timeout) {
|
|
319
|
+
throw busyError(target, intents, 'resource_busy_timeout');
|
|
320
|
+
}
|
|
321
|
+
const remaining = options?.timeout == null
|
|
322
|
+
? pollInterval
|
|
323
|
+
: Math.max(0, Math.min(pollInterval, options.timeout - (Date.now() - startedAt)));
|
|
324
|
+
await delay(remaining, options?.signal);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
async function applyBusyPolicy(target, options, defaultPolicy = 'return') {
|
|
328
|
+
const policy = options?.ifBusy ?? defaultPolicy;
|
|
329
|
+
if (policy === 'return')
|
|
330
|
+
return;
|
|
331
|
+
const intents = await listIntents(target);
|
|
332
|
+
if (intents.length === 0)
|
|
333
|
+
return;
|
|
334
|
+
if (policy === 'fail')
|
|
335
|
+
throw busyError(target, intents, 'resource_busy');
|
|
336
|
+
await waitForNoIntents(target, {
|
|
337
|
+
timeout: options?.busyTimeout,
|
|
338
|
+
pollInterval: options?.busyPollInterval,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
const commits = {
|
|
342
|
+
async create(commitOptions) {
|
|
343
|
+
const clientTxId = createClientTxId(commitOptions.idempotencyKey);
|
|
344
|
+
const operations = normalizeCommitOperations(commitOptions);
|
|
345
|
+
const body = await requestJson('/v1/commits', {
|
|
346
|
+
method: 'POST',
|
|
347
|
+
idempotencyKey: clientTxId,
|
|
348
|
+
body: JSON.stringify({
|
|
349
|
+
clientTxId,
|
|
350
|
+
idempotencyKey: clientTxId,
|
|
351
|
+
intent: normalizeIntentId(commitOptions.intent),
|
|
352
|
+
operations,
|
|
353
|
+
}),
|
|
354
|
+
});
|
|
355
|
+
// `requestJson` throws via `translateHttpError` on any non-2xx,
|
|
356
|
+
// so reaching here implies success. Narrow `status` to the
|
|
357
|
+
// `CommitWait`-compatible subset; `'rejected'` only appears on
|
|
358
|
+
// the rejection body (already thrown).
|
|
359
|
+
const status = body.status === 'queued' ? 'queued' : 'confirmed';
|
|
360
|
+
return {
|
|
361
|
+
id: body.id ?? body.clientTxId ?? clientTxId,
|
|
362
|
+
status,
|
|
363
|
+
lastSyncId: body.lastSyncId,
|
|
364
|
+
};
|
|
365
|
+
},
|
|
366
|
+
};
|
|
367
|
+
const capabilities = {
|
|
368
|
+
async create(capabilityOptions) {
|
|
369
|
+
const ttlSeconds = capabilityOptions.ttlSeconds ??
|
|
370
|
+
capabilityOptions.leaseSeconds ??
|
|
371
|
+
toSeconds(capabilityOptions.ttl ?? capabilityOptions.lease ?? DEFAULT_AGENT_LEASE);
|
|
372
|
+
const body = await requestJson('/v1/capabilities', {
|
|
373
|
+
method: 'POST',
|
|
374
|
+
body: JSON.stringify({
|
|
375
|
+
participantKind: capabilityOptions.participantKind ?? 'agent',
|
|
376
|
+
participantId: capabilityOptions.participantId,
|
|
377
|
+
syncGroups: capabilityOptions.syncGroups,
|
|
378
|
+
operations: capabilityOptions.operations,
|
|
379
|
+
ttlSeconds,
|
|
380
|
+
label: capabilityOptions.label,
|
|
381
|
+
wideScope: capabilityOptions.wideScope,
|
|
382
|
+
userMeta: capabilityOptions.userMeta,
|
|
383
|
+
}),
|
|
384
|
+
});
|
|
385
|
+
const id = body.capabilityId ?? body.id;
|
|
386
|
+
if (!id) {
|
|
387
|
+
throw new AbloValidationError('Capability create response did not include an id.', { code: 'capability_id_missing' });
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
id,
|
|
391
|
+
token: body.token,
|
|
392
|
+
expiresAt: body.expiresAt,
|
|
393
|
+
organizationId: body.organizationId,
|
|
394
|
+
scope: body.scope,
|
|
395
|
+
userMeta: body.userMeta,
|
|
396
|
+
client: () => childClient(body.token),
|
|
397
|
+
};
|
|
398
|
+
},
|
|
399
|
+
async retrieve(id) {
|
|
400
|
+
const body = await requestJson(`/v1/capabilities/${encodeURIComponent(id)}`, { method: 'GET' });
|
|
401
|
+
return {
|
|
402
|
+
id: body.capabilityId ?? body.id ?? id,
|
|
403
|
+
organizationId: body.organizationId,
|
|
404
|
+
participantKind: body.participantKind,
|
|
405
|
+
participantId: body.participantId,
|
|
406
|
+
label: body.label,
|
|
407
|
+
status: body.status,
|
|
408
|
+
issuedAt: body.issuedAt,
|
|
409
|
+
expiresAt: body.expiresAt,
|
|
410
|
+
revokedAt: body.revokedAt,
|
|
411
|
+
lastUsedAt: body.lastUsedAt,
|
|
412
|
+
operations: body.operations ?? [],
|
|
413
|
+
syncGroups: body.syncGroups ?? [],
|
|
414
|
+
};
|
|
415
|
+
},
|
|
416
|
+
async revoke(id) {
|
|
417
|
+
const body = await requestJson(`/v1/capabilities/${encodeURIComponent(id)}`, { method: 'DELETE' });
|
|
418
|
+
return {
|
|
419
|
+
id: body.capabilityId ?? body.id ?? id,
|
|
420
|
+
deleted: body.deleted ?? true,
|
|
421
|
+
activeSessionsClosed: body.activeSessionsClosed,
|
|
422
|
+
};
|
|
423
|
+
},
|
|
424
|
+
mint(options) {
|
|
425
|
+
return capabilities.create(options);
|
|
426
|
+
},
|
|
427
|
+
};
|
|
428
|
+
const tasks = {
|
|
429
|
+
async create(taskOptions) {
|
|
430
|
+
const body = await requestJson('/v1/tasks', {
|
|
431
|
+
method: 'POST',
|
|
432
|
+
body: JSON.stringify({
|
|
433
|
+
prompt: taskOptions.prompt,
|
|
434
|
+
parentTaskId: taskOptions.parentTaskId,
|
|
435
|
+
surface: taskOptions.surface,
|
|
436
|
+
metadata: taskOptions.metadata,
|
|
437
|
+
}),
|
|
438
|
+
});
|
|
439
|
+
const id = body.id ?? body.taskId ?? body.turnId;
|
|
440
|
+
if (!id) {
|
|
441
|
+
throw new AbloValidationError('Task create response did not include an id.', { code: 'task_id_missing' });
|
|
442
|
+
}
|
|
443
|
+
return {
|
|
444
|
+
id,
|
|
445
|
+
turnId: id,
|
|
446
|
+
promptHash: body.promptHash,
|
|
447
|
+
openedAt: body.openedAt,
|
|
448
|
+
close: (stats) => tasks.close(id, stats),
|
|
449
|
+
};
|
|
450
|
+
},
|
|
451
|
+
async close(id, stats) {
|
|
452
|
+
const body = await requestJson(`/v1/tasks/${encodeURIComponent(id)}/close`, {
|
|
453
|
+
method: 'POST',
|
|
454
|
+
body: JSON.stringify({
|
|
455
|
+
costInputTokens: stats?.costInputTokens ?? 0,
|
|
456
|
+
costOutputTokens: stats?.costOutputTokens ?? 0,
|
|
457
|
+
costComputeMs: stats?.costComputeMs ?? 0,
|
|
458
|
+
}),
|
|
459
|
+
});
|
|
460
|
+
const closedId = body.id ?? body.taskId ?? body.turnId ?? id;
|
|
461
|
+
return {
|
|
462
|
+
id: closedId,
|
|
463
|
+
turnId: closedId,
|
|
464
|
+
closed: body.closed ?? body.alreadyClosed ?? true,
|
|
465
|
+
alreadyClosed: body.alreadyClosed,
|
|
466
|
+
endedAt: body.endedAt,
|
|
467
|
+
};
|
|
468
|
+
},
|
|
469
|
+
open(options) {
|
|
470
|
+
return tasks.create(options);
|
|
471
|
+
},
|
|
472
|
+
};
|
|
473
|
+
const intents = {
|
|
474
|
+
async create(intentOptions) {
|
|
475
|
+
const intentId = createIntentId();
|
|
476
|
+
const body = await requestJson('/v1/intents', {
|
|
477
|
+
method: 'POST',
|
|
478
|
+
body: JSON.stringify({
|
|
479
|
+
intentId,
|
|
480
|
+
target: intentOptions.target,
|
|
481
|
+
action: intentOptions.action,
|
|
482
|
+
ttl: intentOptions.ttl,
|
|
483
|
+
}),
|
|
484
|
+
});
|
|
485
|
+
const id = body.intent?.id ?? intentId;
|
|
486
|
+
let released = false;
|
|
487
|
+
const release = async () => {
|
|
488
|
+
if (released)
|
|
489
|
+
return;
|
|
490
|
+
released = true;
|
|
491
|
+
await requestJson(`/v1/intents/${encodeURIComponent(id)}`, { method: 'DELETE' });
|
|
492
|
+
};
|
|
493
|
+
return {
|
|
494
|
+
id,
|
|
495
|
+
release,
|
|
496
|
+
revoke: () => {
|
|
497
|
+
void release().catch(() => { });
|
|
498
|
+
},
|
|
499
|
+
[Symbol.asyncDispose]: release,
|
|
500
|
+
};
|
|
501
|
+
},
|
|
502
|
+
list: listIntents,
|
|
503
|
+
waitFor(target, options) {
|
|
504
|
+
return waitForNoIntents(target, options);
|
|
505
|
+
},
|
|
506
|
+
};
|
|
507
|
+
async function retrieveResource(resourceName, id, options) {
|
|
508
|
+
await applyBusyPolicy({ resource: resourceName, id }, options);
|
|
509
|
+
const query = await requestJson(`/v1/resources/${encodeURIComponent(resourceName)}/${encodeURIComponent(id)}`, {
|
|
510
|
+
method: 'GET',
|
|
511
|
+
});
|
|
512
|
+
const data = query.data;
|
|
513
|
+
if (!data) {
|
|
514
|
+
throw new AbloValidationError(`Resource not found: ${resourceName}/${id}`, { code: 'resource_not_found' });
|
|
515
|
+
}
|
|
516
|
+
return {
|
|
517
|
+
data,
|
|
518
|
+
stamp: query.stamp ?? 0,
|
|
519
|
+
intents: query.intents ?? [],
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
function resource(name) {
|
|
523
|
+
return {
|
|
524
|
+
retrieve(id, options) {
|
|
525
|
+
return retrieveResource(name, id, options);
|
|
526
|
+
},
|
|
527
|
+
async create(data, mutationOptions) {
|
|
528
|
+
const id = mutationOptions?.id ?? createResourceId();
|
|
529
|
+
await applyBusyPolicy({ resource: name, id }, mutationOptions);
|
|
530
|
+
return commits.create({
|
|
531
|
+
intent: mutationOptions?.intent,
|
|
532
|
+
idempotencyKey: mutationOptions?.idempotencyKey,
|
|
533
|
+
readAt: mutationOptions?.readAt,
|
|
534
|
+
onStale: mutationOptions?.onStale,
|
|
535
|
+
wait: mutationOptions?.wait,
|
|
536
|
+
timeout: mutationOptions?.timeout,
|
|
537
|
+
operations: [
|
|
538
|
+
{
|
|
539
|
+
action: 'create',
|
|
540
|
+
resource: name,
|
|
541
|
+
id,
|
|
542
|
+
data,
|
|
543
|
+
},
|
|
544
|
+
],
|
|
545
|
+
});
|
|
546
|
+
},
|
|
547
|
+
async update(id, data, mutationOptions) {
|
|
548
|
+
await applyBusyPolicy({ resource: name, id }, mutationOptions);
|
|
549
|
+
return commits.create({
|
|
550
|
+
intent: mutationOptions?.intent,
|
|
551
|
+
idempotencyKey: mutationOptions?.idempotencyKey,
|
|
552
|
+
readAt: mutationOptions?.readAt,
|
|
553
|
+
onStale: mutationOptions?.onStale,
|
|
554
|
+
wait: mutationOptions?.wait,
|
|
555
|
+
timeout: mutationOptions?.timeout,
|
|
556
|
+
operations: [
|
|
557
|
+
{
|
|
558
|
+
action: 'update',
|
|
559
|
+
resource: name,
|
|
560
|
+
id,
|
|
561
|
+
data,
|
|
562
|
+
},
|
|
563
|
+
],
|
|
564
|
+
});
|
|
565
|
+
},
|
|
566
|
+
async delete(id, mutationOptions) {
|
|
567
|
+
await applyBusyPolicy({ resource: name, id }, mutationOptions);
|
|
568
|
+
return commits.create({
|
|
569
|
+
intent: mutationOptions?.intent,
|
|
570
|
+
idempotencyKey: mutationOptions?.idempotencyKey,
|
|
571
|
+
readAt: mutationOptions?.readAt,
|
|
572
|
+
onStale: mutationOptions?.onStale,
|
|
573
|
+
wait: mutationOptions?.wait,
|
|
574
|
+
timeout: mutationOptions?.timeout,
|
|
575
|
+
operations: [
|
|
576
|
+
{
|
|
577
|
+
action: 'delete',
|
|
578
|
+
resource: name,
|
|
579
|
+
id,
|
|
580
|
+
},
|
|
581
|
+
],
|
|
582
|
+
});
|
|
583
|
+
},
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
return {
|
|
587
|
+
async ready() { },
|
|
588
|
+
async waitForFlush() { },
|
|
589
|
+
async dispose() { },
|
|
590
|
+
async purge() { },
|
|
591
|
+
capabilities,
|
|
592
|
+
tasks,
|
|
593
|
+
intents,
|
|
594
|
+
commits,
|
|
595
|
+
resource,
|
|
596
|
+
agent: createAgent,
|
|
597
|
+
async beginTurn(turnOptions) {
|
|
598
|
+
const task = await tasks.create(turnOptions);
|
|
599
|
+
let closed = false;
|
|
600
|
+
const close = async (stats) => {
|
|
601
|
+
if (closed)
|
|
602
|
+
return;
|
|
603
|
+
closed = true;
|
|
604
|
+
await task.close(stats);
|
|
605
|
+
};
|
|
606
|
+
const dispose = () => {
|
|
607
|
+
closed = true;
|
|
608
|
+
};
|
|
609
|
+
return {
|
|
610
|
+
turnId: task.id,
|
|
611
|
+
close,
|
|
612
|
+
dispose,
|
|
613
|
+
[Symbol.asyncDispose]: close,
|
|
614
|
+
};
|
|
615
|
+
},
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
function normalizeIntentId(intent) {
|
|
619
|
+
if (typeof intent === 'string')
|
|
620
|
+
return intent;
|
|
621
|
+
return intent?.id;
|
|
622
|
+
}
|
|
623
|
+
function withAgentBusyDefault(options) {
|
|
624
|
+
return {
|
|
625
|
+
ifBusy: 'fail',
|
|
626
|
+
...(options ?? {}),
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
function stripAgentRuntimeOptions(options) {
|
|
630
|
+
if (!options)
|
|
631
|
+
return undefined;
|
|
632
|
+
const { intent: _intent, ifBusy: _ifBusy, busyTimeout: _busyTimeout, busyPollInterval: _busyPollInterval, ...rest } = options;
|
|
633
|
+
return rest;
|
|
634
|
+
}
|
|
635
|
+
function isIntentHandleRef(input) {
|
|
636
|
+
return (typeof input === 'object' &&
|
|
637
|
+
input !== null &&
|
|
638
|
+
'id' in input &&
|
|
639
|
+
typeof input.id === 'string' &&
|
|
640
|
+
!('action' in input));
|
|
641
|
+
}
|
|
642
|
+
function isAbortError(error) {
|
|
643
|
+
return (typeof DOMException !== 'undefined' &&
|
|
644
|
+
error instanceof DOMException &&
|
|
645
|
+
error.name === 'AbortError') || (typeof error === 'object' &&
|
|
646
|
+
error !== null &&
|
|
647
|
+
'name' in error &&
|
|
648
|
+
error.name === 'AbortError');
|
|
649
|
+
}
|
|
650
|
+
function parseBody(bodyText) {
|
|
651
|
+
if (bodyText.length === 0)
|
|
652
|
+
return null;
|
|
653
|
+
try {
|
|
654
|
+
return JSON.parse(bodyText);
|
|
655
|
+
}
|
|
656
|
+
catch {
|
|
657
|
+
return bodyText;
|
|
658
|
+
}
|
|
659
|
+
}
|