@oddington/sdk 0.1.43 → 0.1.44

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
@@ -171,13 +171,22 @@ finite numbers and safe integers with optional inclusive bounds, and Files.
171
171
  Omitting `maxItems` imposes no item-count ceiling; the complete serialized input
172
172
  still shares the Run's byte bound, including every File descriptor and other field.
173
173
  Definitions allow at most 64 steps, 64 total object properties across their schemas,
174
- and eight schema levels. Run input, each summary, each detail value, and the result
175
- accept at most 256 KiB of UTF-8 serialized JSON, without truncation. Step inputs
176
- remain closed, immutable JSON but have no separate byte ceiling because they are
177
- transient in-process values rather than transported or retained Run evidence.
178
- Across 64 steps, retained summary plus detail is at most 32 MiB, plus 512 KiB for
179
- Run input and result. Schema defaults share the input bound. Titles contain at
180
- most 256 UTF-16 code units;
174
+ and eight schema levels. Run input, the result, and each step summary accept at
175
+ most 256 KiB of compact UTF-8 JSON. Each step accepts at most 1 MiB across its
176
+ summary and data together, measured as the sum of their compact UTF-8 JSON sizes
177
+ (including JSON quotes and escapes, excluding transport envelopes), without
178
+ truncation. Step inputs remain closed, immutable JSON but have no separate byte
179
+ ceiling because they are transient in-process values rather than transported or
180
+ retained Run evidence.
181
+ Across 64 steps, retained summary plus data is at most 64 MiB without a separate
182
+ aggregate output limit, plus 512 KiB for Run input and result. An oversized output
183
+ fails the step and Run once, even if routine code catches the rejection; later
184
+ steps do not execute and customer code is not automatically retried. The rejected
185
+ summary and data are not retained. The safe `step_output_too_large` failure keeps
186
+ only `step`, `sizeBytes`, `limitBytes`, `summaryBytes`, and `dataBytes` and explains
187
+ how to return fewer fields or process a smaller batch. `limitBytes` identifies
188
+ the violated summary or combined bound. Schema defaults share the input bound.
189
+ Titles contain at most 256 UTF-16 code units;
181
190
  access dimensions and response handlers each have at most 64 entries. Functions,
182
191
  accessors, sparse arrays, cycles and non-JSON values reject. The canonical routine
183
192
  ID is a bounded lowercase alphanumeric/hyphen name, independent of its directory.
@@ -186,8 +195,9 @@ No entry, workspace, credentials or placement are authored in this definition.
186
195
  Local Run history returns the newest 20 summaries per canonical routine, without
187
196
  deleting older Runs or unpublished changes. Step titles and bounded summary/detail
188
197
  values are saved with the accepted Run, so later source edits cannot rewrite them.
189
- Each checkout-private execution/preparation JSON file has a 64 MiB total bound,
190
- including private diagnostics; app summaries never include those diagnostics.
198
+ Checkout-private execution JSON has a 128 MiB read envelope for up to 64 MiB
199
+ of step output plus existing evidence; other local state files keep their 64 MiB
200
+ envelope, including private diagnostics; app summaries never include those diagnostics.
191
201
  Publication reads the existing confirmed-receipt journal independently from the
192
202
  successful execution status. Refresh reads materialized state; watching provides
193
203
  connected updates without replay or interrupted-execution recovery.
@@ -349,9 +359,11 @@ the current remote Run owner through the same bindings. Operations reject with
349
359
  `AppRoutineError.reason`: `authority_unavailable`, `execution_unavailable`, or
350
360
  `invalid_input`. Starts are not automatically retried. A
351
361
  disconnected watch ends and requires `current()`. History returns at most 20
352
- summaries without deleting older Runs. Each JSON value is bounded to 256 KiB.
353
- Transport envelopes allow 17 MiB per summary, 514 KiB per detail and 20 summaries
354
- per list; execution retains at most 10 MiB of report evidence. Raw process
362
+ summaries without deleting older Runs. Input, result and each step summary are
363
+ bounded to 256 KiB; each step's summary plus on-demand data is bounded to 1 MiB.
364
+ Transport envelopes allow 17 MiB per summary, 1 MiB plus 4 KiB per detail and
365
+ 20 summaries per list. Local child reports allow 1 MiB plus 64 KiB of framing;
366
+ non-output report evidence retains its 10 MiB bound independently of step output. Raw process
355
367
  output and private authority stay private.
356
368
 
357
369
  The exact-origin browser credential survives document disposal, reload and tab
package/dist/browser.js CHANGED
@@ -998,6 +998,42 @@ function acceptText(request2, text5) {
998
998
  return validateObject(request2.schema, object5, text5), capture({ object: object5 }, "invalid_output", "output_too_large");
999
999
  }
1000
1000
 
1001
+ var ROUTINE_STEP_SUMMARY_BYTES = 256 * 1024, ROUTINE_STEP_OUTPUT_BYTES = 1024 * 1024;
1002
+ function parseRoutineStepOutputDetail(value) {
1003
+ let invalid9 = () => {
1004
+ throw new AppRoutineContractError("invalid_value");
1005
+ };
1006
+ assertRoutineFields(value, ["step", "sizeBytes", "limitBytes", "summaryBytes", "dataBytes"]);
1007
+ let { step, sizeBytes, limitBytes, summaryBytes, dataBytes } = value;
1008
+ return !isAppRoutineName(step) || ![sizeBytes, limitBytes, summaryBytes, dataBytes].every((bytes) => Number.isSafeInteger(bytes) && bytes > 0) || sizeBytes !== summaryBytes + dataBytes || limitBytes !== (summaryBytes > ROUTINE_STEP_SUMMARY_BYTES ? ROUTINE_STEP_SUMMARY_BYTES : ROUTINE_STEP_OUTPUT_BYTES) || (limitBytes === ROUTINE_STEP_SUMMARY_BYTES ? summaryBytes : sizeBytes) <= limitBytes ? invalid9() : freezeRoutineMetadata({ step, sizeBytes, limitBytes, summaryBytes, dataBytes });
1009
+ }
1010
+ function routineStepOutputMessage(value) {
1011
+ let detail = parseRoutineStepOutputDetail(value), size = `${(detail.sizeBytes / (1024 * 1024)).toFixed(1)} MB`, bound = detail.limitBytes === ROUTINE_STEP_SUMMARY_BYTES ? `, including ${(detail.summaryBytes / 1024).toFixed(1)} KB of summary. Each step's summary can return up to 256 KB.` : ". Each step can return up to 1 MB across its summary and data.";
1012
+ return `Step ${detail.step} returned ${size} of output${bound} Return fewer fields or process a smaller batch, then try again.`;
1013
+ }
1014
+ var RoutineStepOutputError = class extends Error {
1015
+ code = "step_output_too_large";
1016
+ detail;
1017
+ constructor(detail) {
1018
+ let captured = parseRoutineStepOutputDetail(detail);
1019
+ super(routineStepOutputMessage(captured)), this.detail = captured, Object.freeze(this);
1020
+ }
1021
+ };
1022
+ function captureRoutineStepOutput(step, summary2, data3) {
1023
+ assertRoutineJson(summary2), assertRoutineJson(data3);
1024
+ let summaryJson = JSON.stringify(summary2), dataJson = JSON.stringify(data3), summaryBytes = new TextEncoder().encode(summaryJson).byteLength, dataBytes = new TextEncoder().encode(dataJson).byteLength, sizeBytes = summaryBytes + dataBytes, limitBytes = summaryBytes > ROUTINE_STEP_SUMMARY_BYTES ? ROUTINE_STEP_SUMMARY_BYTES : ROUTINE_STEP_OUTPUT_BYTES;
1025
+ if (summaryBytes > ROUTINE_STEP_SUMMARY_BYTES || sizeBytes > ROUTINE_STEP_OUTPUT_BYTES)
1026
+ throw new RoutineStepOutputError({ step, sizeBytes, limitBytes, summaryBytes, dataBytes });
1027
+ return freezeRoutineMetadata({ summary: JSON.parse(summaryJson), data: JSON.parse(dataJson) });
1028
+ }
1029
+ function captureRetainedStepOutput(step, value) {
1030
+ let hasSummary = Object.hasOwn(value, "summary"), hasData = Object.hasOwn(value, "data");
1031
+ if (hasSummary && hasData) return captureRoutineStepOutput(step, value.summary, value.data);
1032
+ for (let key of ["summary", "data"]) if (Object.hasOwn(value, key) && (assertRoutineJson(value[key]), new TextEncoder().encode(JSON.stringify(value[key])).byteLength > (key === "summary" ? ROUTINE_STEP_SUMMARY_BYTES : ROUTINE_STEP_OUTPUT_BYTES)))
1033
+ throw new AppRoutineContractError("invalid_value");
1034
+ return freezeRoutineMetadata(JSON.parse(JSON.stringify(value)));
1035
+ }
1036
+
1001
1037
  var LIMIT = 256 * 1024, invalid3 = () => {
1002
1038
  throw new AppRoutineContractError("invalid_value");
1003
1039
  }, invalidContract3 = () => {
@@ -1122,7 +1158,7 @@ function captureRoutineInput(definition, input) {
1122
1158
  return capture2(contract.input, input === void 0 && Object.hasOwn(contract, "inputDefault") ? contract.inputDefault : input);
1123
1159
  }
1124
1160
  async function executeRoutineDefinition(definition, input, runtime) {
1125
- let captured = captureRoutineInput(definition, input), { contract, steps, run } = definitions.get(definition), active2 = !0, accepting = !0, broken = !1, work = Promise.resolve(), aiWork = /* @__PURE__ */ new Set(), generate = (kind, value) => {
1161
+ let captured = captureRoutineInput(definition, input), { contract, steps, run } = definitions.get(definition), active2 = !0, accepting = !0, broken = !1, outputFailure, work = Promise.resolve(), aiWork = /* @__PURE__ */ new Set(), generate = (kind, value) => {
1126
1162
  let operation2 = (async () => {
1127
1163
  if (!active2 || broken || contract.access.ai !== !0) throw new AIError("access_denied");
1128
1164
  if (!runtime.ai) throw new AIError("capability_unavailable");
@@ -1192,10 +1228,10 @@ async function executeRoutineDefinition(definition, input, runtime) {
1192
1228
  await runtime.step(id3, { status: "running" }), activeStep = id3;
1193
1229
  let output = await steps[id3].run(saved, os2);
1194
1230
  if (assertRoutineFields(output, ["summary", "data"]), !Object.hasOwn(output, "summary") || !Object.hasOwn(output, "data")) return invalid3();
1195
- let summary2 = captureJson(output.summary), data3 = captureJson(output.data);
1231
+ let { summary: summary2, data: data3 } = captureRoutineStepOutput(id3, output.summary, output.data);
1196
1232
  return await runtime.step(id3, { status: "completed", summary: summary2, data: data3 }), activeStep = void 0, freezeRoutineMetadata({ summary: summary2, data: data3 });
1197
1233
  } catch (error) {
1198
- throw broken = !0, activeStep = void 0, await runtime.step(id3, { status: "failed" }), error;
1234
+ throw error instanceof RoutineStepOutputError && (outputFailure ??= error), broken = !0, activeStep = void 0, await runtime.step(id3, { status: "failed" }), error;
1199
1235
  }
1200
1236
  });
1201
1237
  return work = next.then(() => {
@@ -1211,8 +1247,12 @@ async function executeRoutineDefinition(definition, input, runtime) {
1211
1247
  }
1212
1248
  for (accepting = !1, await work; aiWork.size; ) await Promise.allSettled([...aiWork]);
1213
1249
  active2 = !1;
1214
- for (let step of contract.steps) reached.has(step.id) || await runtime.step(step.id, { status: "skipped" });
1215
- if (broken) throw failure ?? new AppRoutineContractError("invalid_value");
1250
+ try {
1251
+ for (let step of contract.steps) reached.has(step.id) || await runtime.step(step.id, { status: "skipped" });
1252
+ } catch (error) {
1253
+ throw outputFailure ?? error;
1254
+ }
1255
+ if (broken) throw outputFailure ?? failure ?? new AppRoutineContractError("invalid_value");
1216
1256
  if (contract.result !== void 0) return capture2(contract.result, result2);
1217
1257
  if (result2 !== void 0) return invalid3();
1218
1258
  }
@@ -2769,8 +2809,6 @@ function runtimeConnections() {
2769
2809
  }
2770
2810
  var connections = /* @__PURE__ */ runtimeConnections();
2771
2811
 
2772
- var LOCAL_ROUTINE_VALUE_BYTES = 256 * 1024;
2773
-
2774
2812
  function locationIdentity(location) {
2775
2813
  let coordinates = location.provider === "os" ? [location.file] : location.provider === "gmail" ? [location.connection, location.message, location.attachment] : location.provider === "dropbox" ? [location.connection, location.file] : location.provider === "outlook" ? [
2776
2814
  location.connection,
@@ -3929,6 +3967,7 @@ function createBrowserAuth(runtime) {
3929
3967
  let revision = c.revision;
3930
3968
  try {
3931
3969
  if (!allowedOrigin(origin)) throw runtime.failure("unavailable");
3970
+ runtime.prepare?.();
3932
3971
  let response2 = await fetch(`${origin}/api/auth`, { method: "GET", credentials: "same-origin", redirect: "error", cache: "no-store" });
3933
3972
  if (!response2.ok) throw runtime.failure("unavailable");
3934
3973
  let configuration = validateConfiguration(await response2.json());
@@ -4198,9 +4237,9 @@ var BrowserAuthError = class extends Error {
4198
4237
  this.name = "BrowserAuthError";
4199
4238
  }
4200
4239
  reason;
4201
- };
4240
+ }, providerModule, prepareProvider = () => providerModule ??= import("./auth-provider.js");
4202
4241
  async function provider(configuration, guard) {
4203
- let { AuthClient } = await import("./auth-provider.js");
4242
+ let { AuthClient } = await prepareProvider();
4204
4243
  if (!guard.current(guard.revision())) throw new BrowserAuthError("stale");
4205
4244
  let provenance = /* @__PURE__ */ new Map(), removalRevision, authorizationRequest, authorizationQueue = Promise.resolve(), storage = globalThis.localStorage, client = new AuthClient({
4206
4245
  url: `${configuration.providerUrl}/auth/v1`,
@@ -4282,7 +4321,16 @@ async function provider(configuration, guard) {
4282
4321
  }
4283
4322
  };
4284
4323
  }
4285
- var auth = /* @__PURE__ */ createBrowserAuth({ provider, failure: (reason) => new BrowserAuthError(reason) });
4324
+ var auth = /* @__PURE__ */ createBrowserAuth({
4325
+ // Preloading is opportunistic. Construction below still observes and
4326
+ // normalizes a failed import, without leaving an unhandled preload promise.
4327
+ prepare: () => {
4328
+ prepareProvider().catch(() => {
4329
+ });
4330
+ },
4331
+ provider,
4332
+ failure: (reason) => new BrowserAuthError(reason)
4333
+ });
4286
4334
 
4287
4335
  function routineRunHandle(operations2) {
4288
4336
  return Object.freeze({
@@ -4321,14 +4369,19 @@ function progress(value) {
4321
4369
  }
4322
4370
  function parseRunStep(value, expected) {
4323
4371
  let v = record3(value);
4324
- return typeof v.id != "string" || !name.test(v.id) || expected !== void 0 && v.id !== expected || typeof v.title != "string" || !v.title.trim() || v.title.length > 256 || !["pending", "running", "completed", "failed", "skipped"].includes(v.status) ? invalid8() : {
4372
+ if (typeof v.id != "string" || !name.test(v.id) || expected !== void 0 && v.id !== expected || typeof v.title != "string" || !v.title.trim() || v.title.length > 256 || !["pending", "running", "completed", "failed", "skipped"].includes(v.status)) return invalid8();
4373
+ let output = captureRetainedStepOutput(v.id, {
4374
+ ...Object.hasOwn(v, "summary") ? { summary: v.summary } : {},
4375
+ ...Object.hasOwn(v, "data") ? { data: v.data } : {}
4376
+ });
4377
+ return {
4325
4378
  id: v.id,
4326
4379
  title: v.title,
4327
4380
  status: v.status,
4328
4381
  ...times(v),
4329
4382
  ...progress(v.progress),
4330
4383
  ...Object.hasOwn(v, "summary") ? { summary: bounded(v.summary) } : {},
4331
- ...Object.hasOwn(v, "data") ? { data: bounded(v.data) } : {}
4384
+ ...Object.hasOwn(output, "data") ? { data: output.data } : {}
4332
4385
  };
4333
4386
  }
4334
4387
  function parseRemoteRun(value, expected = {}) {
@@ -4343,7 +4396,11 @@ function parseRemoteRun(value, expected = {}) {
4343
4396
  if (v.failure !== void 0) {
4344
4397
  let f = record3(v.failure);
4345
4398
  if (v.status !== "failed" || typeof f.message != "string" || !f.message || f.message.length > 512) return invalid8();
4346
- failure = { message: f.message };
4399
+ if (f.code === "step_output_too_large") {
4400
+ let { code, message: message2, ...facts } = f, detail = parseRoutineStepOutputDetail(facts);
4401
+ if (!steps.some((step) => step.id === detail.step && step.status === "failed") || message2 !== routineStepOutputMessage(detail)) return invalid8();
4402
+ failure = { code, message: message2, ...detail };
4403
+ } else failure = { message: f.message };
4347
4404
  }
4348
4405
  return Object.hasOwn(v, "result") && v.status !== "succeeded" ? invalid8() : {
4349
4406
  id: v.id,
@@ -4430,12 +4487,23 @@ async function request(key, suffix, init2, maximum = SUMMARY_BYTES, retained, re
4430
4487
  function summary(value, key, context, id3) {
4431
4488
  if (context.kind !== "local")
4432
4489
  try {
4433
- return (!only(value, "id,routine,placement,status,createdAt,startedAt,finishedAt,steps,result,failure") || !Array.isArray(value.steps) || value.steps.some((step) => !only(step, "id,title,status,startedAt,finishedAt,progress,summary,detailAvailable")) || value.failure !== void 0 && !only(value.failure, "message")) && fail3("execution_unavailable"), parseRemoteRun(value, { routine: key, id: id3 });
4490
+ return (!only(value, "id,routine,placement,status,createdAt,startedAt,finishedAt,steps,result,failure") || !Array.isArray(value.steps) || value.steps.some((step) => !only(step, "id,title,status,startedAt,finishedAt,progress,summary,detailAvailable")) || value.failure !== void 0 && !validFailure(value.failure, value.steps)) && fail3("execution_unavailable"), parseRemoteRun(value, { routine: key, id: id3 });
4434
4491
  } catch {
4435
4492
  return fail3("execution_unavailable");
4436
4493
  }
4437
4494
  let run = value;
4438
- return (!only(run, "id,routine,placement,status,createdAt,startedAt,finishedAt,steps,result,failure,local") || typeof run.id != "string" || !uuid2.test(run.id) || id3 !== void 0 && run.id !== id3 || run.routine !== key || run.placement !== "local" || !["queued", "running", "succeeded", "failed"].includes(run.status) || !timestamp(run.createdAt) || !optionalTimes(run) || !Array.isArray(run.steps) || run.steps.length > 64 || run.steps.some((step) => !only(step, "id,title,status,startedAt,finishedAt,progress,summary,detailAvailable") || typeof step.detailAvailable != "boolean" || !validStep(step)) || new Set(run.steps.map((step) => step.id)).size !== run.steps.length || Object.hasOwn(run, "result") && (run.status !== "succeeded" || !bounded2(run.result)) || run.failure !== void 0 && (run.status !== "failed" || !only(run.failure, "message") || !message(run.failure.message)) || run.local !== void 0 && !validLocalActivity(run.local)) && fail3("execution_unavailable"), run;
4495
+ return (!only(run, "id,routine,placement,status,createdAt,startedAt,finishedAt,steps,result,failure,local") || typeof run.id != "string" || !uuid2.test(run.id) || id3 !== void 0 && run.id !== id3 || run.routine !== key || run.placement !== "local" || !["queued", "running", "succeeded", "failed"].includes(run.status) || !timestamp(run.createdAt) || !optionalTimes(run) || !Array.isArray(run.steps) || run.steps.length > 64 || run.steps.some((step) => !only(step, "id,title,status,startedAt,finishedAt,progress,summary,detailAvailable") || typeof step.detailAvailable != "boolean" || !validStep(step)) || new Set(run.steps.map((step) => step.id)).size !== run.steps.length || Object.hasOwn(run, "result") && (run.status !== "succeeded" || !bounded2(run.result)) || run.failure !== void 0 && (run.status !== "failed" || !validFailure(run.failure, run.steps)) || run.local !== void 0 && !validLocalActivity(run.local)) && fail3("execution_unavailable"), run;
4496
+ }
4497
+ function validFailure(value, steps) {
4498
+ if (!only(value, "message,code,step,sizeBytes,limitBytes,summaryBytes,dataBytes") || !message(value.message)) return !1;
4499
+ if (value.code === void 0) return only(value, "message");
4500
+ if (value.code !== "step_output_too_large") return !1;
4501
+ try {
4502
+ let { code: _, message: message2, ...facts } = value, detail = parseRoutineStepOutputDetail(facts);
4503
+ return message2 === routineStepOutputMessage(detail) && steps.some((step) => step.id === detail.step && step.status === "failed");
4504
+ } catch {
4505
+ return !1;
4506
+ }
4439
4507
  }
4440
4508
  function only(value, allowed) {
4441
4509
  return !!value && typeof value == "object" && !Array.isArray(value) && Object.keys(value).every((key) => allowed.split(",").includes(key));
@@ -4445,7 +4513,16 @@ function optionalTimes(value) {
4445
4513
  return (value.startedAt === void 0 || timestamp(value.startedAt)) && (value.finishedAt === void 0 || timestamp(value.finishedAt));
4446
4514
  }
4447
4515
  function validStep(step) {
4448
- return typeof step.id == "string" && identifier.test(step.id) && typeof step.title == "string" && step.title.length > 0 && step.title.length <= 256 && ["pending", "running", "completed", "failed", "skipped"].includes(step.status) && optionalTimes(step) && (step.progress === void 0 || only(step.progress, "completed,total") && Number.isSafeInteger(step.progress.completed) && Number.isSafeInteger(step.progress.total) && step.progress.completed > 0 && step.progress.completed <= step.progress.total) && (!Object.hasOwn(step, "summary") || bounded2(step.summary)) && (!Object.hasOwn(step, "data") || bounded2(step.data));
4516
+ return typeof step.id == "string" && identifier.test(step.id) && typeof step.title == "string" && step.title.length > 0 && step.title.length <= 256 && ["pending", "running", "completed", "failed", "skipped"].includes(step.status) && optionalTimes(step) && (step.progress === void 0 || only(step.progress, "completed,total") && Number.isSafeInteger(step.progress.completed) && Number.isSafeInteger(step.progress.total) && step.progress.completed > 0 && step.progress.completed <= step.progress.total) && (() => {
4517
+ try {
4518
+ return captureRetainedStepOutput(step.id, {
4519
+ ...Object.hasOwn(step, "summary") ? { summary: step.summary } : {},
4520
+ ...Object.hasOwn(step, "data") ? { data: step.data } : {}
4521
+ }), !0;
4522
+ } catch {
4523
+ return !1;
4524
+ }
4525
+ })();
4449
4526
  }
4450
4527
  function validLocalActivity(value) {
4451
4528
  let paths = (value2) => Array.isArray(value2) && value2.every((path) => typeof path == "string" && path.length > 0 && path.length <= 4096 && !path.startsWith("/") && !path.includes("\\") && !path.split("/").some((part) => !part || part === "." || part === "..") && !/[\x00-\x1f\x7f]/.test(path));
@@ -4483,7 +4560,7 @@ function handle(key, id3, context) {
4483
4560
  },
4484
4561
  async step(step) {
4485
4562
  (typeof step != "string" || !identifier.test(step)) && fail3("invalid_input");
4486
- let value = await request(key, `runs/${id3}/steps/${step}`, void 0, 514 * 1024, context, !0);
4563
+ let value = await request(key, `runs/${id3}/steps/${step}`, void 0, ROUTINE_STEP_OUTPUT_BYTES + 4096, context, !0);
4487
4564
  if (context.kind !== "local")
4488
4565
  try {
4489
4566
  return only(value, "id,title,status,startedAt,finishedAt,progress,summary,data") || fail3("execution_unavailable"), parseRunStep(value, step);