@oddington/sdk 0.1.37 → 0.1.39
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/README.md +217 -127
- package/dist/browser.js +457 -173
- package/dist/index.js +9 -4
- package/dist/types/packages/sdk/src/public.d.ts +25 -20
- package/dist/types/packages/sdk/src/routine-definition-types.d.ts +2 -2
- package/dist/types/shared/app-routine-contract.d.ts +1 -1
- package/dist/types/shared/data-contract.d.ts +80 -8
- package/dist/types/shared/data-validation-message.d.ts +23 -0
- package/dist/types/shared/files-contract.d.ts +3 -0
- package/dist/types/shared/inbox-contract.d.ts +11 -0
- package/dist/types/shared/inbox-member-contract.d.ts +1 -1
- package/dist/types/shared/inbox-member-types.d.ts +0 -6
- package/dist/types/shared/routine-ai-contract.d.ts +5 -3
- package/package.json +1 -1
- package/dist/types/shared/delimited-text.d.ts +0 -2
- package/dist/types/shared/inbox-files.d.ts +0 -12
package/README.md
CHANGED
|
@@ -4,13 +4,20 @@ The importable client for OS apps, routines and response handlers.
|
|
|
4
4
|
Node operations require Node.js 22.12.0 or later. The root import and hosted
|
|
5
5
|
Data and Files APIs work in browsers. Installing the package grants no authority.
|
|
6
6
|
|
|
7
|
+
When developing OS alongside a customer repository, use the repository's
|
|
8
|
+
[`dev:customer` helper](../../README.md) to run Bun tests and TypeScript checks
|
|
9
|
+
against the built local SDK without changing the customer's released pin,
|
|
10
|
+
lockfile or installed package. Build the SDK and CLI together first; the same
|
|
11
|
+
helper can execute that built CLI from the customer checkout.
|
|
12
|
+
|
|
7
13
|
Hosted Data and shared Files reach OS directly through `/api/data` and `/api/files`.
|
|
8
14
|
Inbox owns workspace-hosted `/api/inbox/check`, `/api/inbox/publish`,
|
|
9
|
-
`/api/inbox/resolve
|
|
15
|
+
`/api/inbox/resolve`. Request-key recovery and captured
|
|
10
16
|
publication validation are internal behavior of these operations. Retrying the
|
|
11
17
|
same key and content returns the original result; changed content conflicts.
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
Inbox File previews and downloads use package-root `files.read(fileId)` with
|
|
19
|
+
current workspace access. Item visibility does not grant File access. The SDK
|
|
20
|
+
exposes no raw-credential API.
|
|
14
21
|
|
|
15
22
|
Hosted apps use `inbox.questions.list({ routines })` for `AnswerableQuestion[]`.
|
|
16
23
|
Derive `routines` from the existing app declaration with
|
|
@@ -65,7 +72,7 @@ Each step runs at most once and returns `{ summary, data }`. Unknown or repeated
|
|
|
65
72
|
steps fail the Run even if routine code catches the error; unreached steps are
|
|
66
73
|
skipped. Omitted input normalizes to `{}`. A top-level schema can supply a validated
|
|
67
74
|
immutable default with `.default(value)`, such as
|
|
68
|
-
`schema.object({ files: schema.array(file()
|
|
75
|
+
`schema.object({ files: schema.array(file()) }).default({ files: [] })`.
|
|
69
76
|
Nested defaults are unsupported and rejected. Omitted steps permit no step call;
|
|
70
77
|
omitted result permits no returned value. `file()` declares a complete OS File descriptor.
|
|
71
78
|
Its optional `storage` selects local or remote placement and its optional `path`
|
|
@@ -87,7 +94,7 @@ this metadata when absent. Setup and safe inspection are documented in the
|
|
|
87
94
|
|
|
88
95
|
With `access: { ai: true }`, `os.ai.generateText` returns non-empty `{ text }`.
|
|
89
96
|
`os.ai.generateObject` returns `{ object }` validated against the requested
|
|
90
|
-
`schema`, with its inferred type. An invalid answer throws `
|
|
97
|
+
`schema`, with its inferred type. An invalid answer throws `AIError`
|
|
91
98
|
with code `invalid_output`, a clear bounded `detail`, and the original final
|
|
92
99
|
answer as optional `text` for routine-owned recovery. The text is bounded at
|
|
93
100
|
256 KiB and excluded from the error message and ordinary JSON serialization.
|
|
@@ -95,20 +102,47 @@ The routine can inspect it explicitly, apply its own recovery rules, report
|
|
|
95
102
|
an item failure and continue; it must not cast a rejected answer to the promised
|
|
96
103
|
type. OS does not coerce, drop fields, repair or retry automatically.
|
|
97
104
|
|
|
105
|
+
`generateObject` also accepts one exact outer Markdown code fence (empty or
|
|
106
|
+
`json` language tag), then applies the same strict schema checks. It does not
|
|
107
|
+
extract JSON from explanatory prose or repair the answer. `RoutineAIError`
|
|
108
|
+
remains a compatibility alias of `AIError`, so existing `instanceof` checks work.
|
|
109
|
+
|
|
110
|
+
Preserve safe AI error facts when reporting a per-document failure. Keep the
|
|
111
|
+
original answer (`error.text`) out of step data, business records and logs:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { AIError } from '@oddington/sdk';
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const { object } = await os.ai.generateObject({ input, instructions, schema });
|
|
118
|
+
// Apply the routine's business validation before using the result.
|
|
119
|
+
} catch (error) {
|
|
120
|
+
if (!(error instanceof AIError)) throw error;
|
|
121
|
+
return { status: 'unavailable', error: {
|
|
122
|
+
code: error.code, message: error.message,
|
|
123
|
+
...(error.detail ? { detail: error.detail } : {}),
|
|
124
|
+
} };
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
98
128
|
An AI call failure can be caught and handled without automatically failing the
|
|
99
129
|
Run. Uncaught routine or step exceptions still fail execution, and outstanding
|
|
100
130
|
AI calls drain before completion. The host retains authority, isolation,
|
|
101
131
|
cancellation and byte limits. Local and remote Runs use the same API.
|
|
102
132
|
|
|
103
133
|
The bounded grammar supports closed objects with required properties, arrays with
|
|
104
|
-
an
|
|
134
|
+
an optional positive safe-integer `maxItems` business bound, booleans, strings (at most 4096 code points),
|
|
105
135
|
finite numbers and safe integers with optional inclusive bounds, and Files.
|
|
136
|
+
Omitting `maxItems` imposes no item-count ceiling; the complete serialized input
|
|
137
|
+
still shares the Run's byte bound, including every File descriptor and other field.
|
|
106
138
|
Definitions allow at most 64 steps, 64 total object properties across their schemas,
|
|
107
|
-
and eight schema levels.
|
|
108
|
-
|
|
139
|
+
and eight schema levels. Run input, each summary, each detail value, and the result
|
|
140
|
+
accept at most 256 KiB of UTF-8 serialized JSON, without truncation. Step inputs
|
|
141
|
+
remain closed, immutable JSON but have no separate byte ceiling because they are
|
|
142
|
+
transient in-process values rather than transported or retained Run evidence.
|
|
109
143
|
Across 64 steps, retained summary plus detail is at most 32 MiB, plus 512 KiB for
|
|
110
|
-
Run input and result
|
|
111
|
-
|
|
144
|
+
Run input and result. Schema defaults share the input bound. Titles contain at
|
|
145
|
+
most 256 UTF-16 code units;
|
|
112
146
|
access dimensions and response handlers each have at most 64 entries. Functions,
|
|
113
147
|
accessors, sparse arrays, cycles and non-JSON values reject. The canonical routine
|
|
114
148
|
ID is a bounded lowercase alphanumeric/hyphen name, independent of its directory.
|
|
@@ -252,7 +286,7 @@ Local Data selects from the Host's exact published index; a warm selection
|
|
|
252
286
|
neither initiates nor waits for renewal or synchronous hosted authority checks.
|
|
253
287
|
Local Files returns `unsupported_context` after checking context. Neither path
|
|
254
288
|
falls back to hosted services. Local app routines use the same local execution
|
|
255
|
-
and
|
|
289
|
+
and live capability calls as the CLI.
|
|
256
290
|
|
|
257
291
|
Apps can assess a declared routine without starting it:
|
|
258
292
|
|
|
@@ -268,19 +302,17 @@ const detail = await run.step('read'); // Inferred summary and data for this new
|
|
|
268
302
|
const recent = await app.routines.receipts.runs.list();
|
|
269
303
|
const saved = await app.routines.receipts.runs.get(recent[0].id);
|
|
270
304
|
console.log(await saved.current());
|
|
271
|
-
// await saved.publish(); // Only in response to an explicit publication action.
|
|
272
305
|
```
|
|
273
306
|
|
|
274
307
|
The fixed build generates types from each routine definition; no app-authored
|
|
275
308
|
contract map is needed. Descriptions combine safe structure with live readiness
|
|
276
309
|
and connection availability. Starts validate the current definition and input;
|
|
277
|
-
retained reads
|
|
278
|
-
scope. No installation is needed. Bindings accept no endpoint, credential,
|
|
310
|
+
retained local reads check the established session and saved Run ownership. No installation is needed. Bindings accept no endpoint, credential,
|
|
279
311
|
command, path or configuration selector. A local app uses its live Local Host;
|
|
280
312
|
a hosted app uses its existing member session and workspace context to reach
|
|
281
313
|
the current remote Run owner through the same bindings. Operations reject with
|
|
282
314
|
`AppRoutineError.reason`: `authority_unavailable`, `execution_unavailable`, or
|
|
283
|
-
`invalid_input`. Starts
|
|
315
|
+
`invalid_input`. Starts are not automatically retried. A
|
|
284
316
|
disconnected watch ends and requires `current()`. History returns at most 20
|
|
285
317
|
summaries without deleting older Runs. Each JSON value is bounded to 256 KiB.
|
|
286
318
|
Transport envelopes allow 17 MiB per summary, 514 KiB per detail and 20 summaries
|
|
@@ -344,13 +376,18 @@ both shipped SDK inputs and every emitted helper, rejects unexpected inputs and
|
|
|
344
376
|
imports, and retains the full credential/server/customer leak scan. Node retains
|
|
345
377
|
the complete package-root exports; provider classes are never public.
|
|
346
378
|
|
|
347
|
-
Current auth measurement is **
|
|
348
|
-
bytes** and **
|
|
349
|
-
Data/Files fixture measures **
|
|
350
|
-
bytes**, with **
|
|
351
|
-
the local Questions transport
|
|
352
|
-
|
|
353
|
-
|
|
379
|
+
Current auth measurement is **186,220 minified bytes**, including **8,189 fixture
|
|
380
|
+
bytes** and **178,031 SDK bytes**, with **50,478 total gzip bytes**. Current
|
|
381
|
+
Data/Files fixture measures **58,294 total / 7,204 fixture / 51,090 SDK
|
|
382
|
+
bytes**, with **18,751 total gzip bytes**. The shipped runtime also carries
|
|
383
|
+
the local Questions transport, transparent Local Host renewal, and bounded
|
|
384
|
+
Data validation details and guidance.
|
|
385
|
+
Both fit their explicitly reviewed ceilings: **64,000 SDK bytes** for the pinned
|
|
386
|
+
Data/Files fixture and **256,000 SDK bytes** for the separately pinned auth fixture.
|
|
387
|
+
The combined runtime includes validation guidance and local Data recovery
|
|
388
|
+
without another dependency. These allowances leave room for ordinary feature growth while
|
|
389
|
+
catching substantial size regressions. Dependency, compatibility and private-code
|
|
390
|
+
audits remain separate checks.
|
|
354
391
|
Those ceilings are fixed review decisions, not values regenerated from later
|
|
355
392
|
measurements. The installed auth realm/host-handoff proof passes alongside the
|
|
356
393
|
Node exports, declarations, tarball leak scan and browser compatibility audits.
|
|
@@ -364,17 +401,18 @@ bytes; generated helpers count toward the SDK. Gzip is diagnostic only.
|
|
|
364
401
|
|
|
365
402
|
| Same named-import fixture | Legacy diagnostic | Current strict proof |
|
|
366
403
|
| --- | ---: | ---: |
|
|
367
|
-
| Total minified JavaScript bytes | 488,539 |
|
|
368
|
-
| Fixture-attributed bytes | 5,954 |
|
|
369
|
-
| SDK bytes | 482,585 |
|
|
370
|
-
| Total gzip bytes | 198,612 |
|
|
404
|
+
| Total minified JavaScript bytes | 488,539 | 58,294 |
|
|
405
|
+
| Fixture-attributed bytes | 5,954 | 7,204 |
|
|
406
|
+
| SDK bytes | 482,585 | 51,090 |
|
|
407
|
+
| Total gzip bytes | 198,612 | 18,751 |
|
|
371
408
|
|
|
372
409
|
The legacy diagnostic externalized three unresolved Node built-ins and failed
|
|
373
410
|
browser compatibility. The current strict proof has no imports, shims or warnings
|
|
374
|
-
and reduces SDK bytes by
|
|
375
|
-
|
|
411
|
+
and reduces SDK bytes by 89.4%. Its literal ceiling is **64,000 SDK bytes**.
|
|
412
|
+
The measurement includes
|
|
376
413
|
the Local Host bootstrap, local Questions transport, transparent renewal and
|
|
377
|
-
opaque browser-auth handoff that `os.init` must select at runtime
|
|
414
|
+
opaque browser-auth handoff that `os.init` must select at runtime, and bounded
|
|
415
|
+
Data validation errors. The verifier pins the fixture
|
|
378
416
|
hash and bundler version; changing either requires a reviewed measurement
|
|
379
417
|
comparison. Future ceiling increases require explicit review of added browser
|
|
380
418
|
capability and must never be automatically regenerated from an oversized output.
|
|
@@ -402,33 +440,29 @@ await files.enrich({ file: report.id, name: 'read.md', content: revisedMarkdown,
|
|
|
402
440
|
await files.upload(report.id); // Move a local File and every retained enrichment to remote storage.
|
|
403
441
|
```
|
|
404
442
|
|
|
405
|
-
Hosted Files retains remote originals and caller-produced named enrichments
|
|
406
|
-
|
|
407
|
-
`
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
retrying a completed upload returns the same File. Later reads and enrichments
|
|
422
|
-
follow remote storage; any remaining managed local bytes are disposable cache.
|
|
423
|
-
An unreferenced local File is disclosed remotely only when explicitly uploaded
|
|
424
|
-
or successfully published as a Source. An authorized hosted caller receives
|
|
425
|
-
non-retryable `local_only` (409) until upload succeeds; unrelated callers receive
|
|
426
|
-
`access_denied`.
|
|
443
|
+
Hosted Files retains remote originals and caller-produced named enrichments.
|
|
444
|
+
A managed local CLI parent stores local Files beneath the signed-in active
|
|
445
|
+
workspace's `~/.os/<workspace>/files` root; callers cannot configure another
|
|
446
|
+
root. Local Runs default new Files to local storage, hosted Runs to remote.
|
|
447
|
+
An existing local or remote File keeps its placement. Every completed File has
|
|
448
|
+
one workspace registry row, including local Files whose bytes stay on this
|
|
449
|
+
device. Request-key replay and reusable enrichments preserve the same identity
|
|
450
|
+
across member calls, Runs and restarts.
|
|
451
|
+
|
|
452
|
+
Explicit remote creation, upload and enrichment complete during their calls.
|
|
453
|
+
`upload(id)` verifies the original and every enrichment before changing the same
|
|
454
|
+
File's placement; retrying a completed upload returns that File. Later reads
|
|
455
|
+
and enrichments follow its pinned remote store. A Source reference leaves a
|
|
456
|
+
local File local. Inbox publication uploads that same File before exposing the
|
|
457
|
+
Ask or Digest. Completed effects survive later Run failure. A runtime without
|
|
458
|
+
the local bytes reports `local_only` (409).
|
|
427
459
|
Unnamed inputs require a safe `name`; streams also require declared `size`.
|
|
428
460
|
Blob and byte-array declarations must match their intrinsic size. Originals are
|
|
429
|
-
bounded to
|
|
430
|
-
|
|
431
|
-
|
|
461
|
+
bounded to 5 GiB per original and 16 MiB per enrichment. Large originals stream
|
|
462
|
+
through bounded multipart transfers; local writes and reads use bounded disk
|
|
463
|
+
buffers. Operations share four concurrent slots and idle/request deadlines;
|
|
464
|
+
later calls wait in invocation order without reading caller content, and
|
|
465
|
+
transport retries preserve the same bytes and request key.
|
|
432
466
|
Omitting a key generates one for that invocation. Supply a stable key to recover
|
|
433
467
|
across processes or fresh routine runs. Caller inputs remain owned by the caller.
|
|
434
468
|
Each request resolves current authority; hosted routine Files calls use the
|
|
@@ -441,24 +475,10 @@ versus received size. Streaming rejection marks partial received counts with
|
|
|
441
475
|
failure evidence without raw names, keys, file content or provider text. Older
|
|
442
476
|
errors without `detail` remain valid. Reads return the successful Web
|
|
443
477
|
Response without consuming its body. Local transfer is bounded and buffered
|
|
444
|
-
before constructing that Response.
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
retained enrichments under its current member or installed-routine creator.
|
|
449
|
-
It returns `void` only after physical cleanup and durable completion; a completed
|
|
450
|
-
retry succeeds. Independent retention returns `conflict` without changing the
|
|
451
|
-
File. `access_denied` and `integrity` remain terminal; `unavailable` and
|
|
452
|
-
`capacity` are retryable. Pending deletion denies reads, writes, new uses and
|
|
453
|
-
creation-key replay while the same current creator retries cleanup. Deletion
|
|
454
|
-
uses the authenticated parent for managed local Files and uploaded caches.
|
|
455
|
-
R2 cleanup verifies the registered immutable key and native revision, removes
|
|
456
|
-
only that object and confirms absence. Each physical writer owns a distinct
|
|
457
|
-
candidate key, so a delayed writer cannot recreate the deleted representation.
|
|
458
|
-
Pending admission is never success.
|
|
459
|
-
When a completion reply is lost, retry `files.delete` with the same File ID and
|
|
460
|
-
current creator authority. Keep stable creation keys separate from deletion
|
|
461
|
-
recovery: a completed deleted key is denied and cannot recreate the File.
|
|
478
|
+
before constructing that Response. Current workspace members can use every File in that workspace. A
|
|
479
|
+
routine's `files: "read"` permits get/read; `files: "read-write"` also permits
|
|
480
|
+
create/enrich/upload. References in Sources, Runs and Inbox items grant no
|
|
481
|
+
additional access. Files has no public delete operation.
|
|
462
482
|
|
|
463
483
|
`enrich` accepts a string encoded as UTF-8 or exact `Uint8Array` bytes, a safe
|
|
464
484
|
flat filename, and an optional MIME hint. OS verifies text/Markdown/JSON or
|
|
@@ -491,7 +511,7 @@ const changes = data.repository('general').changes();
|
|
|
491
511
|
changes.create({ ...statement, added: '2026-10-20' });
|
|
492
512
|
changes.create({ $ref: 'transactions/one',
|
|
493
513
|
sources: [source.csv.row(statement.$ref, parsedTransaction.recordPosition)] });
|
|
494
|
-
await changes.
|
|
514
|
+
await changes.apply();
|
|
495
515
|
```
|
|
496
516
|
|
|
497
517
|
The caller's CSV parser supplies `recordPosition` from the retained statement:
|
|
@@ -508,7 +528,12 @@ guarded Data update. Known objects match by OS File ID, Gmail connection/message
|
|
|
508
528
|
attachment, or Dropbox connection/File ID; newer Locations replace old path,
|
|
509
529
|
revision and URL facts. Distinct objects and custom provider values remain.
|
|
510
530
|
Helpers need no initialization and perform no I/O, choose no ref, and add no
|
|
511
|
-
changes. File retention and Data validation/
|
|
531
|
+
changes. File retention and Data validation/application remain explicit.
|
|
532
|
+
|
|
533
|
+
File Sources use the OS-defined fields `filename`, `media_type`, `size_bytes`,
|
|
534
|
+
and `sha256`. Keep the customer repository's Source schema aligned with this
|
|
535
|
+
definition when updating it. `mime_type` is not an alias for `media_type`;
|
|
536
|
+
do not rename a field produced by `source.file()` to fit an incompatible schema.
|
|
512
537
|
|
|
513
538
|
Microsoft helpers are also exported at the package root, with the corresponding
|
|
514
539
|
`OutlookSourceLocation` and `SharePointSourceLocation` types:
|
|
@@ -526,7 +551,7 @@ const itemLocation = source.location.sharepoint({
|
|
|
526
551
|
|
|
527
552
|
Construct these only after successful provider reads establish the coordinates
|
|
528
553
|
for the retained bytes, and save them alongside `source.location.os(file)` using
|
|
529
|
-
ordinary Data
|
|
554
|
+
an ordinary Data apply. Request `Prefer: IdType="ImmutableId"` consistently
|
|
530
555
|
when obtaining Outlook message and attachment IDs. These IDs are case-sensitive
|
|
531
556
|
and survive folder moves within the same mailbox, but not archive-mailbox moves
|
|
532
557
|
or export/reimport. Subjects, filenames and `internetMessageId` are not substitutes.
|
|
@@ -570,12 +595,11 @@ Binding is inert. Each operation resolves current workspace, endpoint and
|
|
|
570
595
|
short-lived authority from the trusted app host or managed routine context.
|
|
571
596
|
Customer calls cannot configure those values. Missing or expired context
|
|
572
597
|
rejects; a named handle does not expand repository access. During
|
|
573
|
-
`os routine run <name>`, the same calls
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
retain their normal checks. A dead local parent fails without hosted fallback.
|
|
598
|
+
`os routine run <name>`, the same calls read current schema-governed Markdown
|
|
599
|
+
in the trusted checkout, including staged, modified and untracked Records. Each
|
|
600
|
+
operation reads the files as it begins. Later calls see completed writes and
|
|
601
|
+
external edits. Current local Data acquires no hosted binding or index. A dead
|
|
602
|
+
local parent fails without hosted fallback.
|
|
579
603
|
|
|
580
604
|
Each graph selection sends one request and returns only selected canonical
|
|
581
605
|
values, expanded forward links and exact opaque public versions. Cursors
|
|
@@ -632,7 +656,9 @@ use `select` for a manual `after` cursor or explicit historical selection.
|
|
|
632
656
|
`all` consumes the same iterator and returns only after successful exhaustion.
|
|
633
657
|
Its required positive safe-integer `maxRecords` caps top-level Record occurrences
|
|
634
658
|
across all collections; expanded links do not count toward that ceiling. Overflow
|
|
635
|
-
|
|
659
|
+
names the queried collections and ceiling, tells the author to narrow the query
|
|
660
|
+
or increase `maxRecords`, and rejects with nonretryable `capacity` without a
|
|
661
|
+
partial or truncated result.
|
|
636
662
|
Invalid helper arguments use `invalid_query`; malformed pages, cursor cycles,
|
|
637
663
|
snapshot changes or inconsistent requested counts use nonretryable `unavailable`.
|
|
638
664
|
Selection and authority failures propagate unchanged, with no retry or restart.
|
|
@@ -706,15 +732,11 @@ projection, highlighting, embedding or historical version option.
|
|
|
706
732
|
|
|
707
733
|
Matches have deterministic order and complete canonical Records. Empty matches
|
|
708
734
|
succeed with the exact searched version. Members search latest published data;
|
|
709
|
-
routines search their
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
another publisher does not move the captured view. Search Records can supply
|
|
715
|
-
guards for subsequent Changes. Missing or incomplete search data, including
|
|
716
|
-
retained versions without it, fails retryably with `unavailable` without an
|
|
717
|
-
alternate read path.
|
|
735
|
+
hosted routines search their admitted hosted version. Local routines search the
|
|
736
|
+
schema-governed Markdown currently in the checkout, including staged, modified
|
|
737
|
+
and untracked Records. Each call refreshes its operation-local view; a later call
|
|
738
|
+
sees completed Changes writes and external edits. Search Records supply captured
|
|
739
|
+
versions for subsequent Changes. Explicit History keeps its hosted contract.
|
|
718
740
|
|
|
719
741
|
`where` combines field conditions with AND. Alongside exact scalar/null equality
|
|
720
742
|
and `$in`, declared one-valued `integer`, `number` and `date` fields accept
|
|
@@ -753,37 +775,106 @@ changes.create({
|
|
|
753
775
|
changes.update(first.invoices[0], { amount: 30 });
|
|
754
776
|
changes.delete(first.invoices[1]);
|
|
755
777
|
await changes.validate();
|
|
756
|
-
const
|
|
778
|
+
const applied = await changes.apply();
|
|
757
779
|
```
|
|
758
780
|
|
|
781
|
+
Use reserved `$body` to author a Record's Markdown content independently of its
|
|
782
|
+
schema fields, in the same guarded change:
|
|
783
|
+
|
|
784
|
+
```ts
|
|
785
|
+
changes.create({ $ref: 'emails/email-123', type: 'email', subject: 'Invoice',
|
|
786
|
+
$body: 'Hello,\n\nPlease find our invoice attached.\n' });
|
|
787
|
+
changes.update(existingEmail, { $body: revisedMarkdown }); // body-only change
|
|
788
|
+
const emails = await general.records.read(emailRefs, { body: true });
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
Omitted `$body` on create means empty; omitted on update preserves the existing
|
|
792
|
+
body exactly. A string deliberately replaces it, `''` clears it, and `null` is
|
|
793
|
+
invalid. Body-only edits preserve front matter, field-only edits preserve the
|
|
794
|
+
body, and both share the Record's exact-byte version guard. Bodies round-trip
|
|
795
|
+
without Markdown interpretation, formatting, trimming, or fence semantics.
|
|
796
|
+
Ordinary reads, selections and search stay body-free; explicitly requested
|
|
797
|
+
bodies use the same ordered batch/null-slot shape. Local change batches support
|
|
798
|
+
32 MiB of serialized JSON; hosted requests retain their separate 256 KiB limit.
|
|
799
|
+
Body-enabled read responses are bounded to 8 MiB, with an explicit error
|
|
800
|
+
instead of truncation. Hosted body reads use a protected immutable v3 index;
|
|
801
|
+
older indexes without body coverage are unavailable rather than falsely empty.
|
|
802
|
+
|
|
803
|
+
The customer routine owns content and authoring policy. For imported emails,
|
|
804
|
+
create with the prepared body, omit `$body` on ordinary reruns, and backfill
|
|
805
|
+
only bodies explicitly read as exactly empty. Existing notes and fenced blocks
|
|
806
|
+
remain ordinary authored content. A fresh version alone does not authorize
|
|
807
|
+
overwriting an existing nonempty body.
|
|
808
|
+
|
|
759
809
|
Creation captures a complete Record and stable `$ref`, without I/O or a caller
|
|
760
810
|
path. OS resolves exactly one collection and its schema-declared filing path.
|
|
761
811
|
Optional `$filing` supplies only unresolved named safe components and is never
|
|
762
812
|
stored or returned as Record data; missing components reject by name. Updates
|
|
763
813
|
capture only `$ref`, `$version` and explicit fields. Deletion captures only the
|
|
764
|
-
selected `$ref` and `$version`.
|
|
765
|
-
schema-declared links from any committed Record outside it refuse
|
|
766
|
-
even if a proposed update removes that link.
|
|
767
|
-
candidate graph without effects
|
|
768
|
-
|
|
814
|
+
selected `$ref` and `$version`. For hosted Data, links within the deletion set
|
|
815
|
+
are allowed; schema-declared links from any committed Record outside it refuse
|
|
816
|
+
the whole group, even if a proposed update removes that link. Hosted validation
|
|
817
|
+
checks the complete candidate graph without effects, and `apply()` repeats
|
|
818
|
+
authoritative validation before writing every create/update/delete together.
|
|
769
819
|
Success returns `{ version, created: [{ $ref, $version }], updated: [{ $ref, $version }], deleted: [{ $ref, $version }] }`
|
|
770
820
|
only after its exact index is ready. Later current reads discover that version.
|
|
771
821
|
Deleted identities keep their captured versions; deletion creates no new Record
|
|
772
822
|
version or tombstone. A Source deletion removes only that guarded Record, retaining
|
|
773
823
|
its File, enrichments and existing durable uses.
|
|
774
|
-
During
|
|
775
|
-
|
|
776
|
-
|
|
824
|
+
During a local Run, `changes.apply()` validates the touched Records, paths,
|
|
825
|
+
authored links and captured versions without auditing unrelated Record
|
|
826
|
+
contents, then writes individual complete files atomically. Completed paths
|
|
827
|
+
remain after a later filesystem failure. `validate()` grants no later
|
|
828
|
+
permission to write; subsequent operations read the current checkout. There is
|
|
829
|
+
no later Run publication, promotion or prepared repository state.
|
|
777
830
|
|
|
778
831
|
`invalid_change` and known-unapplied `conflict` reject the batch.
|
|
779
|
-
|
|
832
|
+
Unknown, missing, or invalid field failures include the Record ref and
|
|
833
|
+
field name in the SDK error's `message`, with guidance to check the collection
|
|
834
|
+
schema and its relative file location when known. Use
|
|
835
|
+
`repository.collections.schema(name)` to inspect that schema.
|
|
836
|
+
The error code and effect remain available for programmatic handling; the
|
|
837
|
+
message does not include rejected field values.
|
|
838
|
+
|
|
839
|
+
Known field failures also expose optional `error.detail`, exported as
|
|
840
|
+
`DataValidationDetail`. It contains `record`, `field`, `reason`, and, when
|
|
841
|
+
known, `schemaFile` and `expected`. Reasons are `unknown_field`, `missing_field`,
|
|
842
|
+
`invalid_value`, and `source_schema_mismatch`. `expected` contains the field
|
|
843
|
+
`type`, `many`, and `nullable`, with `decimalPlaces` for a bounded number or
|
|
844
|
+
`format: 'month'` for a month date. Enum/constant values and rejected values
|
|
845
|
+
are excluded. Older errors without detail remain valid.
|
|
846
|
+
Other Data errors use this same optional `detail` field for local read/write
|
|
847
|
+
recovery or record-limit facts. Check `'reason' in error.detail` before reading
|
|
848
|
+
field-validation properties.
|
|
849
|
+
|
|
850
|
+
When handling a failure, preserve the SDK error's message. For example, a
|
|
851
|
+
routine that catches Data errors and reports them in its own result can keep
|
|
852
|
+
the explanation alongside the stable code:
|
|
853
|
+
|
|
854
|
+
```ts
|
|
855
|
+
import { DataOperationError } from '@oddington/sdk';
|
|
856
|
+
|
|
857
|
+
try {
|
|
858
|
+
await changes.validate();
|
|
859
|
+
} catch (error) {
|
|
860
|
+
if (!(error instanceof DataOperationError)) throw error;
|
|
861
|
+
return { code: error.code, message: error.message,
|
|
862
|
+
...(error.detail ? { detail: error.detail } : {}) };
|
|
863
|
+
}
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
When the routine cannot continue, rethrow the original error to preserve its
|
|
867
|
+
type and guidance. A new generic `Error` loses the SDK's bounded failure facts.
|
|
868
|
+
|
|
869
|
+
`apply_pending` is retryable and may report `uncertain` or `applied`;
|
|
780
870
|
retry the same change set to reconcile its retained identity. A transport failure
|
|
781
|
-
also keeps that identity. Concurrent
|
|
871
|
+
also keeps that identity. Concurrent `apply()` calls coalesce, confirmed calls
|
|
782
872
|
replay the result, and changed intent requires a new change set. Updates are
|
|
783
|
-
bounded with creates and deletions to 10,000 total operations
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
873
|
+
bounded with creates and deletions to 10,000 total operations, with one operation
|
|
874
|
+
per ref. The complete serialized change request is limited to 32 MiB for local
|
|
875
|
+
file changes and 256 KiB for hosted writes. Search and history are outside this
|
|
876
|
+
Changes slice. Source Records use ordinary schema rules. Files checks access
|
|
877
|
+
when a consumer reads the referenced content.
|
|
787
878
|
|
|
788
879
|
Bind a declared Airtable connection with `connections.airtable('project-management')`
|
|
789
880
|
from the package root. Import and binding perform no network or credential work.
|
|
@@ -816,42 +907,41 @@ records separately. `AirtableOperationError` reports `unapplied`, `partial` or
|
|
|
816
907
|
work; never repeat the whole change blindly. Credentials must never be retained
|
|
817
908
|
in files, publication, logs, handler input, errors or receipts.
|
|
818
909
|
|
|
819
|
-
Managed routine launches supply workspace
|
|
820
|
-
The defaults read `OS_ROUTINE_WORKSPACE
|
|
821
|
-
`
|
|
822
|
-
existing `workspace`, `capability` and `workingRoot` options. Runtime context
|
|
910
|
+
Managed routine launches supply workspace and scoped capability.
|
|
911
|
+
The defaults read `OS_ROUTINE_WORKSPACE` and `OS_ROUTINE_RUN`. Hosts may supply
|
|
912
|
+
`workspace`, `capability` and `request` options. Runtime context
|
|
823
913
|
provides authority; callers cannot select repository remotes or expand grants.
|
|
824
914
|
`os routine run <name> [--input <file|->]` executes the conventional inert
|
|
825
|
-
definition with declared JSON input and
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
No-change success retains Run evidence without requiring publish. `--json`
|
|
830
|
-
returns typed Run state/counts/result, or confirmed publish receipts/links.
|
|
915
|
+
definition with declared JSON input. Data writes and hosted capability calls
|
|
916
|
+
complete live; successful earlier work remains after a later failure. The Run
|
|
917
|
+
retains exact local paths, safe hosted effects and Git provenance. `--json`
|
|
918
|
+
returns typed Run state, result/failure and retained local activity.
|
|
831
919
|
The former `prepare`, `run --local`, command/argv and authority-selection
|
|
832
920
|
flags are rejected. `os routine deploy <canonical-id>` explicitly delivers the
|
|
833
921
|
complete definition, including its response handlers; `--yes` provides automation
|
|
834
922
|
consent. `os routine run <canonical-id> --remote` runs the current successful
|
|
835
923
|
deployment with the same input/defaults, ordered steps and result validation.
|
|
836
924
|
Remote handles expose `current()`, `watch()` and `step(id)`; `runs.list()` and
|
|
837
|
-
`runs.get(id)` read retained bounded history. Remote summaries omit
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
Retain evidence with `
|
|
841
|
-
the
|
|
842
|
-
|
|
843
|
-
creates, updates and deletions. Local Runs read
|
|
844
|
-
|
|
845
|
-
|
|
925
|
+
`runs.get(id)` read retained bounded history. Remote summaries omit local activity.
|
|
926
|
+
App-started local routines use the same current-checkout and live-effects owner.
|
|
927
|
+
|
|
928
|
+
Retain evidence with shared `files.create({ file: bytes, name: 'evidence.txt' })`
|
|
929
|
+
and put the returned File into `fileBlock({ label, files })`. Inbox has no byte
|
|
930
|
+
intake API. Local publication uploads that same File before exposing the item. Use `data.repository(name)` for Record reads and `changes()` for guarded
|
|
931
|
+
creates, updates and deletions. Local Runs read current checkout files and leave
|
|
932
|
+
completed Data writes as ordinary uncommitted files for review, even after later
|
|
933
|
+
failure. Inbox publication has no Record methods;
|
|
846
934
|
managed Response handlers retain their separately injected repository capability.
|
|
847
935
|
|
|
848
936
|
Hosts publishing as a member supply the existing `request` option:
|
|
849
937
|
|
|
850
938
|
```ts
|
|
851
|
-
const inbox = routinePublicationClient({ workspace,
|
|
939
|
+
const inbox = routinePublicationClient({ workspace, request });
|
|
852
940
|
```
|
|
853
941
|
|
|
854
|
-
Here `request` is the host's authorized member transport
|
|
942
|
+
Here `request` is the host's authorized member transport and takes precedence
|
|
943
|
+
over ambient Run routing. The CLI uses its active workspace and shared Files to
|
|
944
|
+
upload referenced local Files before sending publication. A member can publish an
|
|
855
945
|
initial handlerless Ask or Digest where allowed; a member login supplies no
|
|
856
946
|
routine repository-write authority. Login, session storage and refresh belong
|
|
857
947
|
to `@oddington/cli`.
|