@oddington/sdk 0.1.4 → 0.1.6

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 CHANGED
@@ -42,23 +42,71 @@ const report = await files.create({
42
42
  const descriptor = await files.get(report.id);
43
43
  const response = await files.read(descriptor.id);
44
44
  await response.text(); // Also supports blob(), arrayBuffer() and body streaming.
45
+ const reading = await files.enrich({ file: report.id, name: 'read.md', content: readingMarkdown });
46
+ await files.enrich({ file: report.id, name: 'extracted.json', content: JSON.stringify(extractedValue) });
47
+ const current = await files.get(report.id); // Discover retained enrichment descriptors.
48
+ const context = await files.read(current.id, ['read.md', 'extracted.json']);
49
+ await context['read.md'].text();
50
+ await context['extracted.json'].json(); // Each body is independently consumed.
51
+ await files.enrich({ file: report.id, name: 'read.md', content: revisedMarkdown, replace: reading.sha256 });
52
+ await files.upload(report.id); // Move a local File and every retained enrichment to remote storage.
45
53
  ```
46
54
 
47
- Hosted Files retains remote originals only, with empty `enrichments`. It rejects
48
- `local`, `both` and explicit routine local preparation with `unsupported_context`.
55
+ Hosted Files retains remote originals and caller-produced named enrichments;
56
+ `storage: 'local'` requires a managed CLI parent. Configure that parent with
57
+ `ODDINGTON_FILES_ROOT` pointing to an empty or already OS-managed dedicated
58
+ directory outside tracked Git content. `ODDINGTON_FILES_DEFAULT=local|remote`
59
+ selects its default; without it, remote remains the default. Per-File `storage`
60
+ overrides it. The root stays private to the parent. Local creation without a
61
+ configured root fails with `unsupported_context`. Files have one durable home;
62
+ `both` is invalid. Local retention establishes no hosted registration.
63
+ Fresh runs recover the same creator's File by its request key, and verify
64
+ retained bytes before returning a descriptor or content. A dead parent fails
65
+ closed. `os routine prepare` seals checked local File references and defers
66
+ new remote Files, their enrichments and explicit uploads until `os routine publish`; legacy Inbox
67
+ file preparation remains additive. Existing hosted Files are read-only during
68
+ preparation.
69
+ `upload(id)` preserves the File descriptor, creator access and existing Source
70
+ references. Local bytes remain authoritative until all remote objects verify;
71
+ retrying a completed upload returns the same File. Later reads and enrichments
72
+ follow remote storage; any remaining managed local bytes are disposable cache.
73
+ An unreferenced local File is disclosed remotely only when explicitly uploaded
74
+ or successfully published as a Source. An authorized hosted caller receives
75
+ non-retryable `local_only` (409) until upload succeeds; unrelated callers receive
76
+ `access_denied`.
49
77
  Unnamed inputs require a safe `name`; streams also require declared `size`.
50
78
  Blob and byte-array declarations must match their intrinsic size. Originals are
51
- bounded to 16 MiB, metadata to 4 KiB, and descriptor/error bodies to 16 KiB.
52
- Creates allow four concurrent buffered operations and a 30-second deadline;
79
+ bounded to 16 MiB per object, metadata to 4 KiB, and descriptor/error bodies to 16 KiB.
80
+ Operations share four concurrent slots and a 30-second deadline;
53
81
  transport retries at most once with the same prepared bytes and request key.
54
82
  Omitting a key generates one for that invocation. Supply a stable key to recover
55
83
  across processes or fresh routine runs. Caller inputs remain owned by the caller.
56
- Each request resolves current authority; routine Files calls go directly to the
57
- selected workspace's HTTPS OS host. `FileOperationError` exposes a bounded code,
84
+ Each request resolves current authority; hosted routine Files calls use the
85
+ selected workspace's HTTPS OS host, and managed calls use the authenticated
86
+ private parent transport. `FileOperationError` exposes a bounded code,
58
87
  status and retryability without server details. Reads return the successful Web
59
- Response without consuming its body. Creation grants private creator access;
88
+ Response without consuming its body. Local transfer is bounded and buffered
89
+ before constructing that Response. Creation grants private creator access;
60
90
  workspace membership alone never grants another creator's File.
61
91
 
92
+ `enrich` accepts a string encoded as UTF-8 or exact `Uint8Array` bytes, a safe
93
+ flat filename, and an optional MIME hint. OS verifies text/Markdown/JSON or
94
+ reduces the content to generic binary metadata; it performs no extraction or
95
+ model work. The first enrichment atomically promotes the storage bundle while
96
+ preserving original bytes, File identity, name, path and access. An identical
97
+ retry returns the retained descriptor. Different content conflicts unless
98
+ `replace` matches the current enrichment hash; repeating a completed replacement
99
+ still succeeds with its preceding hash. There is no enrichment request key.
100
+
101
+ `read(id)` returns the original; `read(id, name)` returns one enrichment.
102
+ `read(id, names)` validates at most 16 distinct safe names, deduplicates repeats,
103
+ and returns an object of unconsumed Responses after all reads succeed. An empty
104
+ list returns an empty object in a valid runtime context. Missing authorized names
105
+ return `invalid_input`; a failed keyed request cancels obtained bodies. Each name
106
+ is read independently with fresh authority; there is no cross-name revision snapshot.
107
+ Names cannot contain separators or use the reserved `original` basename with
108
+ or without an extension. Enrichments share their parent File's access and retention.
109
+
62
110
  Read a named repository through the authenticated host context:
63
111
 
64
112
  ```ts
@@ -81,7 +129,7 @@ Binding is inert. Each operation resolves current workspace, endpoint and
81
129
  short-lived authority from the trusted app host or managed routine context.
82
130
  Customer calls cannot configure those values. Missing or expired context
83
131
  rejects; a named handle does not expand repository access. During explicit
84
- `os routine run --local`, the same calls use the matched checkout's committed
132
+ `os routine prepare`, the same calls use the matched checkout's committed
85
133
  version through a private parent transport. Dirty files are ignored. An OS
86
134
  cache outside the checkout reuses derived data; reads require no remote access
87
135
  or preparation storage. Later accepted record effects advance the run's view
@@ -92,8 +140,41 @@ Each graph selection sends one request and returns only selected canonical
92
140
  values, expanded forward links and exact opaque public versions. Cursors
93
141
  continue the exact retained artifact and collection selection, even after
94
142
  replacement; repeat its options and links while omitting unrelated collections.
95
- No automatic retry, separate version fetch, search or history operation is
96
- included. Collection schemas omit Markdown storage hints.
143
+ There is no automatic retry or separate version fetch. History is not included.
144
+ Collection schemas omit Markdown storage hints.
145
+
146
+ Use bounded plain-text lookup to find full Records and guards:
147
+
148
+ ```ts
149
+ const found = await general.records.search('acme overdue', {
150
+ collections: ['invoices'], limit: 20,
151
+ });
152
+ const searchedVersion = found.$meta.version;
153
+ for (const { collection, record } of found.matches) {
154
+ console.log(collection, record.$ref, record.$version);
155
+ }
156
+ ```
157
+
158
+ Search sends one request. The trimmed query has 1–256 Unicode code points;
159
+ case-insensitive whitespace-separated terms match stable refs and structured
160
+ scalar values, including link refs and array values. Every term must match;
161
+ field names and Markdown bodies are excluded. Omitted options search all
162
+ accessible collections with a limit of 20. Supplied collections contain 1–16
163
+ distinct accessible names; the integer limit is 1–100. Invalid queries or
164
+ unsupported options fail with `invalid_query`. There is no cursor, count,
165
+ projection, highlighting, embedding or historical version option.
166
+
167
+ Matches have deterministic order and complete canonical Records. Empty matches
168
+ succeed with the exact searched version. Members search latest published data;
169
+ routines search their captured version, advancing only through their own
170
+ publication. Immediate `os routine run` uses hosted search. During
171
+ `os routine prepare`, search uses the same committed local Index as select,
172
+ ignores dirty/untracked files and advances only to that run's exact proposed
173
+ version after its own Changes publication. Validation does not advance reads;
174
+ another publisher does not move the captured view. Search Records can supply
175
+ guards for subsequent Changes. Missing or incomplete search data, including
176
+ retained versions without it, fails retryably with `unavailable` without an
177
+ alternate read path.
97
178
 
98
179
  `where` combines field conditions with AND. Alongside exact scalar/null equality
99
180
  and `$in`, declared one-valued `integer`, `number` and `date` fields accept
@@ -120,21 +201,37 @@ eligibility. Repeat the same bounds when continuing a cursor.
120
201
  For reads, only `capacity` and `unavailable` are retryable; an error never includes private
121
202
  server details. Server boundaries own query bounds and authorization.
122
203
 
123
- Guarded updates use the same handle in hosted and explicitly local routines:
204
+ Schema-driven creates, guarded updates and guarded deletions use the same handle
205
+ in immediate and prepared routines:
124
206
 
125
207
  ```ts
126
208
  const changes = general.changes();
209
+ changes.create({
210
+ $ref: 'invoices/INV-124', $filing: { year: '2026' },
211
+ type: 'invoice', number: 'INV-124', status: 'draft', amount: 1200,
212
+ });
127
213
  changes.update(first.invoices[0], { amount: 30 });
214
+ changes.delete(first.invoices[1]);
128
215
  await changes.validate();
129
216
  const publication = await changes.publish();
130
217
  ```
131
218
 
132
- Preparation captures only the Record's `$ref`, `$version` and explicit set,
133
- without I/O. Validation checks the complete committed result without effects;
134
- publication repeats guarded validation and applies the whole update batch.
135
- Success returns `{ version, created: [], updated: [{ $ref, $version }], deleted: [] }`
219
+ Creation captures a complete Record and stable `$ref`, without I/O or a caller
220
+ path. OS resolves exactly one collection and its schema-declared filing path.
221
+ Optional `$filing` supplies only unresolved named safe components and is never
222
+ stored or returned as Record data; missing components reject by name. Updates
223
+ capture only `$ref`, `$version` and explicit fields. Deletion captures only the
224
+ selected `$ref` and `$version`. Links within the deletion set are allowed;
225
+ schema-declared links from any committed Record outside it refuse the whole group,
226
+ even if a proposed update removes that link. Validation checks the complete
227
+ candidate graph without effects; publication repeats authoritative validation
228
+ and applies every create/update/delete in one commit.
229
+ Success returns `{ version, created: [{ $ref, $version }], updated: [{ $ref, $version }], deleted: [{ $ref, $version }] }`
136
230
  only after its exact index is ready. Later current reads discover that version.
137
- During `os routine run --local`, publication retains the exact proposal and
231
+ Deleted identities keep their captured versions; deletion creates no new Record
232
+ version or tombstone. A Source deletion removes only that guarded Record, retaining
233
+ its File, enrichments and existing durable uses.
234
+ During `os routine prepare`, publication retains the exact proposal and
138
235
  advances only run-local reads. `os routine publish` later applies and indexes
139
236
  that immutable proposal; unrelated staged and unstaged work is preserved.
140
237
 
@@ -143,8 +240,10 @@ that immutable proposal; unrelated staged and unstaged work is preserved.
143
240
  retry the same change set to reconcile its retained identity. A transport failure
144
241
  also keeps that identity. Concurrent publication calls coalesce, confirmed calls
145
242
  replay the result, and changed intent requires a new change set. Updates are
146
- bounded to 10,000 Records and the 256 KiB Data request limit. Create, delete,
147
- `$filing`, search and history are outside this supported Changes slice.
243
+ bounded with creates and deletions to 10,000 total operations and the 256 KiB Data
244
+ request limit, with one operation per ref. Search and history are outside this
245
+ Changes slice. Source Records use ordinary schema rules; Data also validates OS
246
+ File identity/access and registers durable uses from its exact ready artifact.
148
247
 
149
248
  Bind a declared Airtable connection with `connections.airtable('project-management')`
150
249
  from the package root. Import and binding perform no network or credential work.
@@ -182,8 +281,14 @@ The defaults read `ODDINGTON_ROUTINE_WORKSPACE`, `ODDINGTON_ROUTINE_RUN` and
182
281
  `ODDINGTON_INBOX_WORKING_ROOT` (or the current directory). Callers may supply the
183
282
  existing `workspace`, `capability` and `workingRoot` options. Runtime context
184
283
  provides authority; callers cannot select repository remotes or expand grants.
185
- The same calls prepare locally when launched with `os routine run --local`, and
186
- the CLI owns explicit publication and recovery of that prepared run.
284
+ `os routine run` publishes as capabilities are called. The same calls prepare
285
+ work when launched with `os routine prepare`, and `os routine publish` owns
286
+ explicit publication and recovery of that prepared run. Preparation may contact
287
+ OS for authentication and reads, and declared connections may contact their
288
+ providers; it does not promise offline execution. Execution location and File
289
+ storage are independent of the publication lifecycle. `os routine run --local`
290
+ remains a deprecated compatibility alias for `os routine prepare` and prints
291
+ the replacement command.
187
292
 
188
293
  Retain evidence with `inbox.files.add({ path: 'evidence.txt' })`. Paths stay within
189
294
  the invocation's working root and uploaded bytes are verified against the retained