@oddington/sdk 0.1.43 → 0.1.45

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
@@ -319,6 +319,52 @@ function validateRoutineSchemaValue(schema2, value, rejected) {
319
319
  check(schema2, value, "$");
320
320
  }
321
321
 
322
+ function validDropboxRootPath(value) {
323
+ return typeof value == "string" && value.length <= 2048 && value.startsWith("/") && !/[\\\x00-\x1f\x7f]/.test(value) && value.slice(1).split("/").every((segment) => segment.length > 0 && segment.trim() === segment && segment !== "." && segment !== "..");
324
+ }
325
+ function validMicrosoftId(value) {
326
+ return typeof value == "string" && /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(value);
327
+ }
328
+ function validMicrosoftMailbox(value) {
329
+ return typeof value == "string" && value.length <= 254 && /^[^\s@/?#\\\x00-\x1f\x7f]+@[^\s@/?#\\\x00-\x1f\x7f]+\.[^\s@/?#\\\x00-\x1f\x7f]+$/.test(value);
330
+ }
331
+ function sharePointHost(value) {
332
+ return /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.sharepoint\.com$/.test(value);
333
+ }
334
+ function canonicalSharePointSiteUrl(value) {
335
+ if (!(typeof value != "string" || value.length > 2048 || /[\s\\?#\x00-\x1f\x7f]/.test(value) || !/^https:\/\/[^/:@]+(?:\/|$)/i.test(value)))
336
+ try {
337
+ let url = new URL(value);
338
+ if (url.protocol !== "https:" || !sharePointHost(url.hostname) || url.username || url.password || url.port) return;
339
+ let rawPath = value.replace(/^https:\/\/[^/]+/i, "") || "/", path = rawPath.endsWith("/") ? rawPath.slice(0, -1) : rawPath, segments = path.slice(1).split("/");
340
+ if (path && segments.some((segment) => !segment)) return;
341
+ let encoded = path ? segments.map((segment) => {
342
+ let decoded = decodeURIComponent(segment);
343
+ if (decoded === "." || decoded === ".." || /[/%\\?#\x00-\x1f\x7f]/.test(decoded)) throw new Error();
344
+ return encodeURIComponent(decoded);
345
+ }).join("/") : "", canonical = `https://${url.hostname}/${encoded}`;
346
+ return canonical.length <= 2048 ? canonical : void 0;
347
+ } catch {
348
+ return;
349
+ }
350
+ }
351
+ function validSharePointSiteId(value, siteUrl) {
352
+ if (typeof value != "string" || value.length > 512) return !1;
353
+ let [host, collection, site, extra] = value.split(",");
354
+ return extra === void 0 && host === new URL(siteUrl).hostname && validMicrosoftId(collection) && validMicrosoftId(site);
355
+ }
356
+ function validGoogleScopes(value) {
357
+ return Array.isArray(value) && value.length > 0 && value.length <= 32 && value.every((scope) => typeof scope == "string" && scope.length <= 256 && /^(https:\/\/www\.googleapis\.com\/auth\/[A-Za-z0-9._-]+|https:\/\/mail\.google\.com\/|openid|email|profile)$/.test(scope)) && new Set(value).size === value.length;
358
+ }
359
+ var DROPBOX_FILE_SCOPES = ["account_info.read", "files.metadata.read", "files.content.read", "files.content.write"];
360
+ function validDropboxFileScopes(value) {
361
+ return Array.isArray(value) && value.length === DROPBOX_FILE_SCOPES.length && new Set(value).size === value.length && DROPBOX_FILE_SCOPES.every((scope) => value.includes(scope));
362
+ }
363
+
364
+ function validDataRepositoryName(value) {
365
+ return typeof value == "string" && /^[a-z0-9][a-z0-9._-]{0,127}$/.test(value) && !value.includes("..");
366
+ }
367
+
322
368
  var InboxContractError = class extends Error {
323
369
  constructor(message2) {
324
370
  super(message2), this.name = "InboxContractError";
@@ -575,9 +621,26 @@ function defineHandler(value) {
575
621
  function respond(handler, input) {
576
622
  return defineHandler(handler), handlerReference({ id: handler.id, input });
577
623
  }
624
+ function recordRequirements(value) {
625
+ let source2 = object(value, "Record requirements");
626
+ keys(source2, ["repository", "records"], "Record requirements"), validDataRepositoryName(source2.repository) || fail("Invalid requirements repository");
627
+ let repository = source2.repository, records = array(source2.records, "required Records").map((entry) => {
628
+ let row = object(entry, "required Record");
629
+ keys(row, ["ref", "version"], "required Record");
630
+ let { ref } = validateRecordReference({ repository, ref: row.ref });
631
+ return (typeof row.version != "string" || !row.version.length || row.version.length > 512) && fail("Invalid required Record version"), { ref, version: row.version };
632
+ });
633
+ return new Set(records.map((record4) => record4.ref)).size !== records.length && fail("Duplicate required Record"), { repository, records };
634
+ }
635
+ function requireRecords(repository, records) {
636
+ return recordRequirements({ repository, records: array(records, "required Records").map((entry) => {
637
+ let record4 = object(entry, "Data Record");
638
+ return { ref: record4.$ref, version: record4.$version };
639
+ }) });
640
+ }
578
641
  function question(value) {
579
642
  let source2 = object(value, "question");
580
- keys(source2, ["id", "to", "allowedResponders", "prompt", "instructions", "response", "handler"], "question");
643
+ keys(source2, ["id", "to", "allowedResponders", "prompt", "instructions", "response", "handler", "requirements"], "question");
581
644
  let to = personReference(source2.to, "question assignee"), allowedResponders = source2.allowedResponders === void 0 ? [to] : distinctPeople(source2.allowedResponders, "allowed responders");
582
645
  return allowedResponders.includes(to) || fail("Allowed responders must include the assignee"), {
583
646
  id: id(source2.id, "question ID"),
@@ -586,7 +649,8 @@ function question(value) {
586
649
  prompt: text(source2.prompt, "question prompt"),
587
650
  ...source2.instructions === void 0 ? {} : { instructions: text(source2.instructions, "instructions") },
588
651
  response: validateResponseDefinition(source2.response),
589
- ...Object.hasOwn(source2, "handler") ? { handler: handlerReference(source2.handler) } : {}
652
+ ...Object.hasOwn(source2, "handler") ? { handler: handlerReference(source2.handler) } : {},
653
+ ...Object.hasOwn(source2, "requirements") ? { requirements: recordRequirements(source2.requirements) } : {}
590
654
  };
591
655
  }
592
656
  function presentation(source2) {
@@ -998,6 +1062,42 @@ function acceptText(request2, text5) {
998
1062
  return validateObject(request2.schema, object5, text5), capture({ object: object5 }, "invalid_output", "output_too_large");
999
1063
  }
1000
1064
 
1065
+ var ROUTINE_STEP_SUMMARY_BYTES = 256 * 1024, ROUTINE_STEP_OUTPUT_BYTES = 1024 * 1024;
1066
+ function parseRoutineStepOutputDetail(value) {
1067
+ let invalid9 = () => {
1068
+ throw new AppRoutineContractError("invalid_value");
1069
+ };
1070
+ assertRoutineFields(value, ["step", "sizeBytes", "limitBytes", "summaryBytes", "dataBytes"]);
1071
+ let { step, sizeBytes, limitBytes, summaryBytes, dataBytes } = value;
1072
+ 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 });
1073
+ }
1074
+ function routineStepOutputMessage(value) {
1075
+ 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.";
1076
+ return `Step ${detail.step} returned ${size} of output${bound} Return fewer fields or process a smaller batch, then try again.`;
1077
+ }
1078
+ var RoutineStepOutputError = class extends Error {
1079
+ code = "step_output_too_large";
1080
+ detail;
1081
+ constructor(detail) {
1082
+ let captured = parseRoutineStepOutputDetail(detail);
1083
+ super(routineStepOutputMessage(captured)), this.detail = captured, Object.freeze(this);
1084
+ }
1085
+ };
1086
+ function captureRoutineStepOutput(step, summary2, data3) {
1087
+ assertRoutineJson(summary2), assertRoutineJson(data3);
1088
+ 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;
1089
+ if (summaryBytes > ROUTINE_STEP_SUMMARY_BYTES || sizeBytes > ROUTINE_STEP_OUTPUT_BYTES)
1090
+ throw new RoutineStepOutputError({ step, sizeBytes, limitBytes, summaryBytes, dataBytes });
1091
+ return freezeRoutineMetadata({ summary: JSON.parse(summaryJson), data: JSON.parse(dataJson) });
1092
+ }
1093
+ function captureRetainedStepOutput(step, value) {
1094
+ let hasSummary = Object.hasOwn(value, "summary"), hasData = Object.hasOwn(value, "data");
1095
+ if (hasSummary && hasData) return captureRoutineStepOutput(step, value.summary, value.data);
1096
+ 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)))
1097
+ throw new AppRoutineContractError("invalid_value");
1098
+ return freezeRoutineMetadata(JSON.parse(JSON.stringify(value)));
1099
+ }
1100
+
1001
1101
  var LIMIT = 256 * 1024, invalid3 = () => {
1002
1102
  throw new AppRoutineContractError("invalid_value");
1003
1103
  }, invalidContract3 = () => {
@@ -1122,7 +1222,7 @@ function captureRoutineInput(definition, input) {
1122
1222
  return capture2(contract.input, input === void 0 && Object.hasOwn(contract, "inputDefault") ? contract.inputDefault : input);
1123
1223
  }
1124
1224
  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) => {
1225
+ 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
1226
  let operation2 = (async () => {
1127
1227
  if (!active2 || broken || contract.access.ai !== !0) throw new AIError("access_denied");
1128
1228
  if (!runtime.ai) throw new AIError("capability_unavailable");
@@ -1192,10 +1292,10 @@ async function executeRoutineDefinition(definition, input, runtime) {
1192
1292
  await runtime.step(id3, { status: "running" }), activeStep = id3;
1193
1293
  let output = await steps[id3].run(saved, os2);
1194
1294
  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);
1295
+ let { summary: summary2, data: data3 } = captureRoutineStepOutput(id3, output.summary, output.data);
1196
1296
  return await runtime.step(id3, { status: "completed", summary: summary2, data: data3 }), activeStep = void 0, freezeRoutineMetadata({ summary: summary2, data: data3 });
1197
1297
  } catch (error) {
1198
- throw broken = !0, activeStep = void 0, await runtime.step(id3, { status: "failed" }), error;
1298
+ throw error instanceof RoutineStepOutputError && (outputFailure ??= error), broken = !0, activeStep = void 0, await runtime.step(id3, { status: "failed" }), error;
1199
1299
  }
1200
1300
  });
1201
1301
  return work = next.then(() => {
@@ -1211,8 +1311,12 @@ async function executeRoutineDefinition(definition, input, runtime) {
1211
1311
  }
1212
1312
  for (accepting = !1, await work; aiWork.size; ) await Promise.allSettled([...aiWork]);
1213
1313
  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");
1314
+ try {
1315
+ for (let step of contract.steps) reached.has(step.id) || await runtime.step(step.id, { status: "skipped" });
1316
+ } catch (error) {
1317
+ throw outputFailure ?? error;
1318
+ }
1319
+ if (broken) throw outputFailure ?? failure ?? new AppRoutineContractError("invalid_value");
1216
1320
  if (contract.result !== void 0) return capture2(contract.result, result2);
1217
1321
  if (result2 !== void 0) return invalid3();
1218
1322
  }
@@ -2334,48 +2438,6 @@ var files = {
2334
2438
  read
2335
2439
  };
2336
2440
 
2337
- function validDropboxRootPath(value) {
2338
- return typeof value == "string" && value.length <= 2048 && value.startsWith("/") && !/[\\\x00-\x1f\x7f]/.test(value) && value.slice(1).split("/").every((segment) => segment.length > 0 && segment.trim() === segment && segment !== "." && segment !== "..");
2339
- }
2340
- function validMicrosoftId(value) {
2341
- return typeof value == "string" && /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(value);
2342
- }
2343
- function validMicrosoftMailbox(value) {
2344
- return typeof value == "string" && value.length <= 254 && /^[^\s@/?#\\\x00-\x1f\x7f]+@[^\s@/?#\\\x00-\x1f\x7f]+\.[^\s@/?#\\\x00-\x1f\x7f]+$/.test(value);
2345
- }
2346
- function sharePointHost(value) {
2347
- return /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.sharepoint\.com$/.test(value);
2348
- }
2349
- function canonicalSharePointSiteUrl(value) {
2350
- if (!(typeof value != "string" || value.length > 2048 || /[\s\\?#\x00-\x1f\x7f]/.test(value) || !/^https:\/\/[^/:@]+(?:\/|$)/i.test(value)))
2351
- try {
2352
- let url = new URL(value);
2353
- if (url.protocol !== "https:" || !sharePointHost(url.hostname) || url.username || url.password || url.port) return;
2354
- let rawPath = value.replace(/^https:\/\/[^/]+/i, "") || "/", path = rawPath.endsWith("/") ? rawPath.slice(0, -1) : rawPath, segments = path.slice(1).split("/");
2355
- if (path && segments.some((segment) => !segment)) return;
2356
- let encoded = path ? segments.map((segment) => {
2357
- let decoded = decodeURIComponent(segment);
2358
- if (decoded === "." || decoded === ".." || /[/%\\?#\x00-\x1f\x7f]/.test(decoded)) throw new Error();
2359
- return encodeURIComponent(decoded);
2360
- }).join("/") : "", canonical = `https://${url.hostname}/${encoded}`;
2361
- return canonical.length <= 2048 ? canonical : void 0;
2362
- } catch {
2363
- return;
2364
- }
2365
- }
2366
- function validSharePointSiteId(value, siteUrl) {
2367
- if (typeof value != "string" || value.length > 512) return !1;
2368
- let [host, collection, site, extra] = value.split(",");
2369
- return extra === void 0 && host === new URL(siteUrl).hostname && validMicrosoftId(collection) && validMicrosoftId(site);
2370
- }
2371
- function validGoogleScopes(value) {
2372
- return Array.isArray(value) && value.length > 0 && value.length <= 32 && value.every((scope) => typeof scope == "string" && scope.length <= 256 && /^(https:\/\/www\.googleapis\.com\/auth\/[A-Za-z0-9._-]+|https:\/\/mail\.google\.com\/|openid|email|profile)$/.test(scope)) && new Set(value).size === value.length;
2373
- }
2374
- var DROPBOX_FILE_SCOPES = ["account_info.read", "files.metadata.read", "files.content.read", "files.content.write"];
2375
- function validDropboxFileScopes(value) {
2376
- return Array.isArray(value) && value.length === DROPBOX_FILE_SCOPES.length && new Set(value).size === value.length && DROPBOX_FILE_SCOPES.every((scope) => value.includes(scope));
2377
- }
2378
-
2379
2441
  var ROUTINE_CONNECTION_CREDENTIALS_ENV = "OS_ROUTINE_CONNECTION_CREDENTIALS";
2380
2442
  function parseRoutineConnectionNames(value) {
2381
2443
  if (!Array.isArray(value) || value.some((name2) => typeof name2 != "string" || !/^(gmail|airtable|dropbox|outlook|sharepoint)\/[a-z0-9][a-z0-9-]{0,62}$/.test(name2)) || new Set(value).size !== value.length)
@@ -2769,8 +2831,6 @@ function runtimeConnections() {
2769
2831
  }
2770
2832
  var connections = /* @__PURE__ */ runtimeConnections();
2771
2833
 
2772
- var LOCAL_ROUTINE_VALUE_BYTES = 256 * 1024;
2773
-
2774
2834
  function locationIdentity(location) {
2775
2835
  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
2836
  location.connection,
@@ -2865,14 +2925,41 @@ var source = {
2865
2925
  };
2866
2926
 
2867
2927
  var InboxOperationError = class extends Error {
2868
- constructor(status, reason) {
2869
- super(reason === "conflict" ? "This question already has an accepted answer" : reason === "unavailable" ? "Inbox request unavailable. Retry the same action." : reason === "invalid_input" ? "Invalid Inbox request" : "Inbox access unavailable");
2928
+ constructor(status, reason, requirementsRepository) {
2929
+ super(reason === "records_need_attention" ? "Records need to be pushed or reviewed." : reason === "records_unavailable" ? "Records temporarily unavailable. Try again shortly." : reason === "handler_unavailable" ? "Deploy the routine that owns this question\u2019s response handler." : reason === "conflict" ? "This question already has an accepted answer" : reason === "unavailable" ? "Inbox request unavailable. Retry the same action." : reason === "invalid_input" ? "Invalid Inbox request" : "Inbox access unavailable");
2870
2930
  this.status = status;
2871
2931
  this.reason = reason;
2932
+ this.requirementsRepository = requirementsRepository;
2872
2933
  }
2873
2934
  status;
2874
2935
  reason;
2875
- }, INBOX_RESULT_BYTES = 8 * 1024 * 1024, INBOX_INPUT_BYTES = 8192, INBOX_LOCAL_INPUT_BYTES = 256 * 1024, INBOX_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
2936
+ requirementsRepository;
2937
+ };
2938
+ function parseQuestionAvailability(value, available = !1) {
2939
+ let state = object4(value);
2940
+ return keys3(state, ["unavailableReason", "requirementsRepository"], []), "unavailableReason" in state ? (assert(!available && typeof state.unavailableReason == "string" && ["handler_unavailable", "records_need_attention", "records_unavailable"].includes(String(state.unavailableReason))), state.unavailableReason === "records_need_attention" ? assert(validDataRepositoryName(state.requirementsRepository)) : assert(!("requirementsRepository" in state)), {
2941
+ unavailableReason: state.unavailableReason,
2942
+ ...state.unavailableReason === "records_need_attention" ? { requirementsRepository: state.requirementsRepository } : {}
2943
+ }) : (assert(!("requirementsRepository" in state)), {});
2944
+ }
2945
+ function questionAvailability(value, available = !1) {
2946
+ return parseQuestionAvailability({
2947
+ ..."unavailableReason" in value ? { unavailableReason: value.unavailableReason } : {},
2948
+ ..."requirementsRepository" in value ? { requirementsRepository: value.requirementsRepository } : {}
2949
+ }, available);
2950
+ }
2951
+ function parseInboxUnavailableFailure(value, status) {
2952
+ try {
2953
+ if (status !== 503) return;
2954
+ let envelope = object4(value);
2955
+ keys3(envelope, ["error", "unavailableReason", "requirementsRepository"], ["error", "unavailableReason"]), assert(text4(envelope.error));
2956
+ let state = questionAvailability(envelope);
2957
+ return new InboxOperationError(503, state.unavailableReason, state.requirementsRepository);
2958
+ } catch {
2959
+ return;
2960
+ }
2961
+ }
2962
+ var INBOX_RESULT_BYTES = 8 * 1024 * 1024, INBOX_INPUT_BYTES = 8192, INBOX_LOCAL_INPUT_BYTES = 256 * 1024, INBOX_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
2876
2963
  function isAppQuestionOperation(operation2) {
2877
2964
  return operation2 === "questions.list" || operation2 === "questions.answer" || operation2 === "questions.searchRecords";
2878
2965
  }
@@ -3052,7 +3139,7 @@ function parseInboxItem(value) {
3052
3139
  assert(id2(person.id) && text4(person.name));
3053
3140
  }), array2(item.questions).forEach((value2) => {
3054
3141
  let question2 = object4(value2);
3055
- if (assert(text4(question2.id) && id2(question2.to) && text4(question2.prompt) && (question2.instructions === null || text4(question2.instructions, !0)) && typeof question2.available == "boolean"), responseDefinition(question2.response), assert(array2(question2.allowedResponders).every(id2)), question2.acceptedResponse !== null) {
3142
+ if (assert(text4(question2.id) && id2(question2.to) && text4(question2.prompt) && (question2.instructions === null || text4(question2.instructions, !0)) && typeof question2.available == "boolean"), questionAvailability(question2, question2.available), assert(!("requirements" in question2) && !("handler" in question2)), responseDefinition(question2.response), assert(array2(question2.allowedResponders).every(id2)), question2.acceptedResponse !== null) {
3056
3143
  let accepted = object4(question2.acceptedResponse);
3057
3144
  assert(id2(accepted.id) && id2(accepted.userId) && date(accepted.acceptedAt)), response(accepted.response), accepted.label === null ? delete accepted.label : "label" in accepted && assert(text4(accepted.label)), "acceptanceOrder" in accepted && assert(integer(accepted.acceptanceOrder));
3058
3145
  }
@@ -3085,11 +3172,11 @@ function parseAnswerableQuestion(value) {
3085
3172
  let entry = object4(json(value));
3086
3173
  keys3(
3087
3174
  entry,
3088
- ["itemId", "number", "revision", "title", "inboxUrl", "questionId", "routine", "prompt", "response", "answerable", "tags", "recordRefs", "instructions"],
3175
+ ["itemId", "number", "revision", "title", "inboxUrl", "questionId", "routine", "prompt", "response", "answerable", "tags", "recordRefs", "instructions", "unavailableReason", "requirementsRepository"],
3089
3176
  ["itemId", "number", "revision", "title", "inboxUrl", "questionId", "routine", "prompt", "response", "answerable"]
3090
3177
  ), assert(id2(entry.itemId) && integer(entry.number) && integer(entry.revision) && text4(entry.title) && text4(entry.inboxUrl));
3091
3178
  let link = new URL(entry.inboxUrl), slug = link.hostname.slice(0, -13);
3092
- return assert(link.hostname.endsWith(".oddington.ai") && slug.length <= 63 && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug) && !["auth", "www", "api", "os"].includes(slug) && entry.inboxUrl === `https://${slug}.oddington.ai/inbox/${entry.itemId}`), assert(isAppRoutineName(entry.routine) && text4(entry.questionId) && entry.questionId === entry.questionId.trim() && text4(entry.prompt) && (!("instructions" in entry) || text4(entry.instructions, !0)) && typeof entry.answerable == "boolean"), responseDefinition(entry.response), askMetadata(entry), entry;
3179
+ return assert(link.hostname.endsWith(".oddington.ai") && slug.length <= 63 && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug) && !["auth", "www", "api", "os"].includes(slug) && entry.inboxUrl === `https://${slug}.oddington.ai/inbox/${entry.itemId}`), assert(isAppRoutineName(entry.routine) && text4(entry.questionId) && entry.questionId === entry.questionId.trim() && text4(entry.prompt) && (!("instructions" in entry) || text4(entry.instructions, !0)) && typeof entry.answerable == "boolean"), questionAvailability(entry, entry.answerable), responseDefinition(entry.response), askMetadata(entry), entry;
3093
3180
  }
3094
3181
  function askMetadata(entry) {
3095
3182
  let tags = array2(entry.tags === void 0 ? [] : entry.tags);
@@ -3398,7 +3485,7 @@ function appContext(origin, active2) {
3398
3485
  observation === observed && (observation = void 0);
3399
3486
  }
3400
3487
  }
3401
- async function renew() {
3488
+ async function renew(initialConnection = !1) {
3402
3489
  if (renewal) return renewal;
3403
3490
  if (!binding || !pair || !alive()) throw new Error();
3404
3491
  renewal = (async () => {
@@ -3424,7 +3511,7 @@ function appContext(origin, active2) {
3424
3511
  await synchronize(requestController.signal);
3425
3512
  return;
3426
3513
  }
3427
- if (!response2.ok) throw new Error();
3514
+ if (!response2.ok) throw response2.status === 401 || response2.status === 403 ? new AppContextDenied() : new Error();
3428
3515
  if (!alive() || !validPair(value) || !bounded3(value.context) || (/* @__PURE__ */ new Set([value.credential, value.renewalCredential, pair.credential, pair.renewalCredential])).size !== 4) throw new Error();
3429
3516
  if (stored() !== previous) {
3430
3517
  await synchronize(requestController.signal);
@@ -3433,6 +3520,7 @@ function appContext(origin, active2) {
3433
3520
  let raw = JSON.stringify(value);
3434
3521
  globalThis.localStorage.setItem(sessionKey, raw), install(value.context, value, raw);
3435
3522
  } catch (error) {
3523
+ if (initialConnection && error instanceof AppContextDenied) throw error;
3436
3524
  try {
3437
3525
  if (alive() && stored() && stored() !== previous) {
3438
3526
  await synchronize(requestController.signal);
@@ -3561,8 +3649,32 @@ function appContext(origin, active2) {
3561
3649
  try {
3562
3650
  if (!alive()) throw new Error();
3563
3651
  if (!pair) {
3564
- let candidate2 = stored(), retained3 = readPair(candidate2);
3565
- pair = retained3, binding = { ...retained3.context }, credential = retained3.credential, serialized = candidate2;
3652
+ let candidate2 = stored();
3653
+ if (candidate2)
3654
+ try {
3655
+ let retained3 = readPair(candidate2);
3656
+ if (retained3.context.expiresAt <= Date.now()) {
3657
+ if (pair = retained3, binding = { ...retained3.context }, credential = retained3.credential, serialized = candidate2, await renew(!0), !current2()) throw new AppRenewalUnavailable();
3658
+ return context();
3659
+ }
3660
+ let value3 = await proof(retained3.credential);
3661
+ return stored() !== candidate2 ? resolveOnce() : (install(value3, retained3, candidate2), context());
3662
+ } catch (error) {
3663
+ if (error instanceof AppRenewalUnavailable || error instanceof AppResponseReadError || !(error instanceof AppContextDenied) || !alive() || expiry) throw error;
3664
+ pair = void 0, binding = void 0, credential = void 0, serialized = void 0;
3665
+ }
3666
+ let response2 = await untilAborted(fetch(`${origin}/_os/app/connect`, {
3667
+ method: "POST",
3668
+ credentials: "omit",
3669
+ redirect: "error",
3670
+ cache: "no-store",
3671
+ referrerPolicy: "no-referrer",
3672
+ signal: controller.signal
3673
+ }), controller.signal), value2 = await appResponse(response2, 4096, controller.signal);
3674
+ if (!response2.ok || response2.redirected || response2.url && response2.url !== `${origin}/_os/app/connect` || !alive() || !validPair(value2) || !bounded3(value2.context)) throw new Error();
3675
+ if (stored() !== candidate2 && stored()) return resolveOnce();
3676
+ let raw = JSON.stringify(value2);
3677
+ return globalThis.localStorage.setItem(sessionKey, raw), install(value2.context, value2, raw), context();
3566
3678
  }
3567
3679
  if (expireAccess(), binding.expiresAt <= Date.now()) {
3568
3680
  if (await renew(), !current2()) throw new AppRenewalUnavailable();
@@ -3592,7 +3704,7 @@ function appContext(origin, active2) {
3592
3704
  resolution2 = void 0;
3593
3705
  }), resolution2);
3594
3706
  return { resolve, retained: async () => {
3595
- if (!expiry && alive() && (binding ? current2() && resolution2 : readPair(stored()).context.expiresAt > Date.now()))
3707
+ if (!expiry && alive() && (binding ? current2() && resolution2 : !stored() || readPair(stored()).context.expiresAt > Date.now()))
3596
3708
  return resolve();
3597
3709
  if (!expiry || !current2()) throw new Error("Local app context unavailable.");
3598
3710
  return context();
@@ -3643,7 +3755,7 @@ async function send2(operation2, input, resolved) {
3643
3755
  };
3644
3756
  try {
3645
3757
  let response2 = await context.requestInbox(operation2, envelope.input), value = await appResponse(response2, INBOX_RESULT_BYTES, context.signal);
3646
- if (check2(), !response2.ok) throw new InboxOperationError(
3758
+ if (check2(), !response2.ok) throw parseInboxUnavailableFailure(value, response2.status) ?? new InboxOperationError(
3647
3759
  [400, 401, 403, 409].includes(response2.status) ? response2.status : 503,
3648
3760
  response2.status === 400 ? "invalid_input" : response2.status === 401 ? "authority_unavailable" : response2.status === 403 ? "access_denied" : response2.status === 409 ? "conflict" : "unavailable"
3649
3761
  );
@@ -3685,12 +3797,20 @@ async function send2(operation2, input, resolved) {
3685
3797
  throw response2.body?.cancel().catch(() => {
3686
3798
  }), error;
3687
3799
  }
3688
- if (!response2.ok)
3689
- throw response2.body?.cancel().catch(() => {
3690
- }), new InboxOperationError(
3800
+ if (!response2.ok) {
3801
+ let value2;
3802
+ try {
3803
+ let bytes2 = await limitedBody(response2, 8192);
3804
+ check(), bytes2 && (value2 = JSON.parse(new TextDecoder().decode(bytes2)));
3805
+ } catch {
3806
+ check();
3807
+ }
3808
+ let refusal = parseInboxUnavailableFailure(value2, response2.status);
3809
+ throw refusal || new InboxOperationError(
3691
3810
  [400, 401, 403, 409].includes(response2.status) ? response2.status : 503,
3692
3811
  response2.status === 400 ? "invalid_input" : response2.status === 401 ? "authority_unavailable" : response2.status === 403 ? "access_denied" : response2.status === 409 ? "conflict" : "unavailable"
3693
3812
  );
3813
+ }
3694
3814
  if (response2.status !== 200)
3695
3815
  throw response2.body?.cancel().catch(() => {
3696
3816
  }), unavailable3();
@@ -3929,6 +4049,7 @@ function createBrowserAuth(runtime) {
3929
4049
  let revision = c.revision;
3930
4050
  try {
3931
4051
  if (!allowedOrigin(origin)) throw runtime.failure("unavailable");
4052
+ runtime.prepare?.();
3932
4053
  let response2 = await fetch(`${origin}/api/auth`, { method: "GET", credentials: "same-origin", redirect: "error", cache: "no-store" });
3933
4054
  if (!response2.ok) throw runtime.failure("unavailable");
3934
4055
  let configuration = validateConfiguration(await response2.json());
@@ -4198,9 +4319,9 @@ var BrowserAuthError = class extends Error {
4198
4319
  this.name = "BrowserAuthError";
4199
4320
  }
4200
4321
  reason;
4201
- };
4322
+ }, providerModule, prepareProvider = () => providerModule ??= import("./auth-provider.js");
4202
4323
  async function provider(configuration, guard) {
4203
- let { AuthClient } = await import("./auth-provider.js");
4324
+ let { AuthClient } = await prepareProvider();
4204
4325
  if (!guard.current(guard.revision())) throw new BrowserAuthError("stale");
4205
4326
  let provenance = /* @__PURE__ */ new Map(), removalRevision, authorizationRequest, authorizationQueue = Promise.resolve(), storage = globalThis.localStorage, client = new AuthClient({
4206
4327
  url: `${configuration.providerUrl}/auth/v1`,
@@ -4282,7 +4403,16 @@ async function provider(configuration, guard) {
4282
4403
  }
4283
4404
  };
4284
4405
  }
4285
- var auth = /* @__PURE__ */ createBrowserAuth({ provider, failure: (reason) => new BrowserAuthError(reason) });
4406
+ var auth = /* @__PURE__ */ createBrowserAuth({
4407
+ // Preloading is opportunistic. Construction below still observes and
4408
+ // normalizes a failed import, without leaving an unhandled preload promise.
4409
+ prepare: () => {
4410
+ prepareProvider().catch(() => {
4411
+ });
4412
+ },
4413
+ provider,
4414
+ failure: (reason) => new BrowserAuthError(reason)
4415
+ });
4286
4416
 
4287
4417
  function routineRunHandle(operations2) {
4288
4418
  return Object.freeze({
@@ -4321,14 +4451,19 @@ function progress(value) {
4321
4451
  }
4322
4452
  function parseRunStep(value, expected) {
4323
4453
  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() : {
4454
+ 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();
4455
+ let output = captureRetainedStepOutput(v.id, {
4456
+ ...Object.hasOwn(v, "summary") ? { summary: v.summary } : {},
4457
+ ...Object.hasOwn(v, "data") ? { data: v.data } : {}
4458
+ });
4459
+ return {
4325
4460
  id: v.id,
4326
4461
  title: v.title,
4327
4462
  status: v.status,
4328
4463
  ...times(v),
4329
4464
  ...progress(v.progress),
4330
4465
  ...Object.hasOwn(v, "summary") ? { summary: bounded(v.summary) } : {},
4331
- ...Object.hasOwn(v, "data") ? { data: bounded(v.data) } : {}
4466
+ ...Object.hasOwn(output, "data") ? { data: output.data } : {}
4332
4467
  };
4333
4468
  }
4334
4469
  function parseRemoteRun(value, expected = {}) {
@@ -4343,7 +4478,11 @@ function parseRemoteRun(value, expected = {}) {
4343
4478
  if (v.failure !== void 0) {
4344
4479
  let f = record3(v.failure);
4345
4480
  if (v.status !== "failed" || typeof f.message != "string" || !f.message || f.message.length > 512) return invalid8();
4346
- failure = { message: f.message };
4481
+ if (f.code === "step_output_too_large") {
4482
+ let { code, message: message2, ...facts } = f, detail = parseRoutineStepOutputDetail(facts);
4483
+ if (!steps.some((step) => step.id === detail.step && step.status === "failed") || message2 !== routineStepOutputMessage(detail)) return invalid8();
4484
+ failure = { code, message: message2, ...detail };
4485
+ } else failure = { message: f.message };
4347
4486
  }
4348
4487
  return Object.hasOwn(v, "result") && v.status !== "succeeded" ? invalid8() : {
4349
4488
  id: v.id,
@@ -4430,12 +4569,23 @@ async function request(key, suffix, init2, maximum = SUMMARY_BYTES, retained, re
4430
4569
  function summary(value, key, context, id3) {
4431
4570
  if (context.kind !== "local")
4432
4571
  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 });
4572
+ 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
4573
  } catch {
4435
4574
  return fail3("execution_unavailable");
4436
4575
  }
4437
4576
  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;
4577
+ 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;
4578
+ }
4579
+ function validFailure(value, steps) {
4580
+ if (!only(value, "message,code,step,sizeBytes,limitBytes,summaryBytes,dataBytes") || !message(value.message)) return !1;
4581
+ if (value.code === void 0) return only(value, "message");
4582
+ if (value.code !== "step_output_too_large") return !1;
4583
+ try {
4584
+ let { code: _, message: message2, ...facts } = value, detail = parseRoutineStepOutputDetail(facts);
4585
+ return message2 === routineStepOutputMessage(detail) && steps.some((step) => step.id === detail.step && step.status === "failed");
4586
+ } catch {
4587
+ return !1;
4588
+ }
4439
4589
  }
4440
4590
  function only(value, allowed) {
4441
4591
  return !!value && typeof value == "object" && !Array.isArray(value) && Object.keys(value).every((key) => allowed.split(",").includes(key));
@@ -4445,7 +4595,16 @@ function optionalTimes(value) {
4445
4595
  return (value.startedAt === void 0 || timestamp(value.startedAt)) && (value.finishedAt === void 0 || timestamp(value.finishedAt));
4446
4596
  }
4447
4597
  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));
4598
+ 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) && (() => {
4599
+ try {
4600
+ return captureRetainedStepOutput(step.id, {
4601
+ ...Object.hasOwn(step, "summary") ? { summary: step.summary } : {},
4602
+ ...Object.hasOwn(step, "data") ? { data: step.data } : {}
4603
+ }), !0;
4604
+ } catch {
4605
+ return !1;
4606
+ }
4607
+ })();
4449
4608
  }
4450
4609
  function validLocalActivity(value) {
4451
4610
  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 +4642,7 @@ function handle(key, id3, context) {
4483
4642
  },
4484
4643
  async step(step) {
4485
4644
  (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);
4645
+ let value = await request(key, `runs/${id3}/steps/${step}`, void 0, ROUTINE_STEP_OUTPUT_BYTES + 4096, context, !0);
4487
4646
  if (context.kind !== "local")
4488
4647
  try {
4489
4648
  return only(value, "id,title,status,startedAt,finishedAt,progress,summary,data") || fail3("execution_unavailable"), parseRunStep(value, step);
@@ -4651,6 +4810,7 @@ export {
4651
4810
  question,
4652
4811
  recordBlock,
4653
4812
  recordsBlock,
4813
+ requireRecords,
4654
4814
  resolveAsk,
4655
4815
  respond,
4656
4816
  richTextBlock,