@mindstudio-ai/agent 0.1.140 → 0.1.141

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/dist/index.js CHANGED
@@ -273,7 +273,6 @@ function getOrOpen(url, token) {
273
273
  pending: /* @__PURE__ */ new Map(),
274
274
  chunked: false,
275
275
  inbound: /* @__PURE__ */ new Map(),
276
- acksSeen: false,
277
276
  retired: false
278
277
  };
279
278
  const c = self;
@@ -297,7 +296,6 @@ function getOrOpen(url, token) {
297
296
  return;
298
297
  }
299
298
  if (msg.t === "db-ack") {
300
- c.acksSeen = true;
301
299
  const p = c.pending.get(msg.id);
302
300
  if (p && !p.acked) {
303
301
  p.acked = true;
@@ -373,15 +371,11 @@ async function executeDbBatchOverWs(url, token, databaseId, queries) {
373
371
  }
374
372
  return await new Promise((resolve, reject) => {
375
373
  const pending = { resolve, reject, timer: null, acked: false };
376
- if (self.acksSeen) {
377
- pending.timer = setTimeout(() => {
378
- forget(self, id);
379
- retire(self, "a request went unacknowledged");
380
- reject(new DbWsTransportError("ws ack timed out", true));
381
- }, WS_ACK_TIMEOUT_MS);
382
- } else {
383
- armIdleTimer(self, id, pending);
384
- }
374
+ pending.timer = setTimeout(() => {
375
+ forget(self, id);
376
+ retire(self, "a request went unacknowledged");
377
+ reject(new DbWsTransportError("ws ack timed out", true));
378
+ }, WS_ACK_TIMEOUT_MS);
385
379
  self.pending.set(id, pending);
386
380
  let started = false;
387
381
  let complete = false;
@@ -3410,7 +3404,15 @@ var DataSource = class {
3410
3404
  * as the new one is registered, before its build lands; a search in that
3411
3405
  * window sees neither. **Throws `mapper_skipped`** (HTTP 422) when the
3412
3406
  * mapper skipped the object — a specific thing it refused, which the caller
3413
- * should see — and `mapper_error` when the mapper threw.
3407
+ * should see — and `mapper_error` when the mapper threw, or when none of
3408
+ * its documents passed validation. A document that fails is left out and
3409
+ * reported in {@link AddResult.invalid}; the rest are registered.
3410
+ *
3411
+ * An object that maps to more than a thousand documents (a large export,
3412
+ * streamed out of the mapper as it parses) registers its first thousand
3413
+ * here and returns {@link AddResult.job}, a job that registers and builds
3414
+ * the rest; `documentCount` says how many there are in all. Another job on
3415
+ * the source refuses it with `data_source_busy`.
3414
3416
  */
3415
3417
  async add(content, options) {
3416
3418
  const bytes = typeof content === "string" ? Buffer.from(content) : Buffer.from(content);
@@ -3420,6 +3422,7 @@ var DataSource = class {
3420
3422
  ...options.contentType ? { contentType: options.contentType } : {},
3421
3423
  ...options.metadata !== void 0 ? { metadata: options.metadata } : {},
3422
3424
  ...options.entities !== void 0 ? { entities: options.entities } : {},
3425
+ ...options.budgetDollars !== void 0 ? { budgetDollars: options.budgetDollars } : {},
3423
3426
  body: bytes.toString("base64")
3424
3427
  });
3425
3428
  }
@@ -3631,6 +3634,15 @@ var DataSource = class {
3631
3634
  ...name ? { name } : {}
3632
3635
  });
3633
3636
  }
3637
+ /**
3638
+ * @internal A mapper frame's grant to store the next part of its spill: how
3639
+ * `defineMapper`'s executor hands the platform an object's documents a part
3640
+ * at a time once they outgrow the frame's record. Only a mapper frame's
3641
+ * token is granted one.
3642
+ */
3643
+ async _mapSpillGrant(spillId) {
3644
+ return this._call("map/spill", { slug: this._slug, spillId });
3645
+ }
3634
3646
  /**
3635
3647
  * @internal Content hash of some bytes, matching what the server computes.
3636
3648
  * Exposed for callers that want to check whether they already added a file.
@@ -3781,7 +3793,7 @@ function createEvents(call) {
3781
3793
  };
3782
3794
  }
3783
3795
 
3784
- // src/task/index.ts
3796
+ // src/task/local.ts
3785
3797
  import { randomUUID as randomUUID2 } from "crypto";
3786
3798
 
3787
3799
  // src/generated/metadata.ts
@@ -4926,6 +4938,49 @@ var stepMetadata = {
4926
4938
  }
4927
4939
  };
4928
4940
 
4941
+ // src/task/index.ts
4942
+ function resolveStepType(name) {
4943
+ const meta = stepMetadata[name];
4944
+ return meta ? meta.stepType : name;
4945
+ }
4946
+ function mapTools(tools) {
4947
+ return tools.map((t) => {
4948
+ if (typeof t === "object" && "execute" in t) {
4949
+ return {
4950
+ name: t.name,
4951
+ description: t.description,
4952
+ inputSchema: t.inputSchema ?? { type: "object", properties: {} }
4953
+ };
4954
+ }
4955
+ if (typeof t === "object" && "appMethod" in t) {
4956
+ return {
4957
+ appMethod: t.appMethod,
4958
+ ...t.description ? { description: t.description } : {},
4959
+ ...t.defaults ? { defaults: t.defaults } : {}
4960
+ };
4961
+ }
4962
+ const method = typeof t === "string" ? t : t.method;
4963
+ const stepType = resolveStepType(method);
4964
+ const defaults = typeof t === "object" ? t.defaults : void 0;
4965
+ return defaults ? { stepType, defaults } : { stepType };
4966
+ });
4967
+ }
4968
+ function sleep2(ms) {
4969
+ return new Promise((resolve) => setTimeout(resolve, ms));
4970
+ }
4971
+ function isDevMode() {
4972
+ return !!(process.env.CALLBACK_TOKEN || getRequestContext()?.callbackToken);
4973
+ }
4974
+ function logTaskResult(result) {
4975
+ if (!isDevMode()) {
4976
+ return;
4977
+ }
4978
+ const toolSummary = result.toolCalls.map((tc) => `${tc.name} (${tc.durationMs}ms) ${tc.success ? "\u2713" : "\u2717"}`).join(", ");
4979
+ console.log(
4980
+ `[task] ${result.turns} turn${result.turns === 1 ? "" : "s"}` + (toolSummary ? `: ${toolSummary}` : "") + ` | ${result.parsedSuccessfully ? "output OK" : "\u26A0 output not valid JSON"} | cost: ${result.usage.totalBillingCost} | trace: ${result.traceId}`
4981
+ );
4982
+ }
4983
+
4929
4984
  // src/task/schema.ts
4930
4985
  function describe(value) {
4931
4986
  if (value === void 0) {
@@ -5066,38 +5121,6 @@ function assertSupportedNode(schema, path) {
5066
5121
  assertSupportedNode(schema.items, `${path}[]`);
5067
5122
  }
5068
5123
  }
5069
- function buildExampleFromSchema(schema) {
5070
- if (schema.const !== void 0) {
5071
- return schema.const;
5072
- }
5073
- if (schema.enum !== void 0 && schema.enum.length > 0) {
5074
- return schema.enum[0];
5075
- }
5076
- const names = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
5077
- const name = names.find((n) => n !== "null") ?? names[0];
5078
- switch (name) {
5079
- case "object": {
5080
- const example = {};
5081
- for (const [key, propSchema] of Object.entries(schema.properties ?? {})) {
5082
- example[key] = buildExampleFromSchema(propSchema);
5083
- }
5084
- return example;
5085
- }
5086
- case "array":
5087
- return schema.items ? [buildExampleFromSchema(schema.items)] : [];
5088
- case "string":
5089
- return "...";
5090
- case "number":
5091
- case "integer":
5092
- return 0;
5093
- case "boolean":
5094
- return true;
5095
- case "null":
5096
- return null;
5097
- default:
5098
- return null;
5099
- }
5100
- }
5101
5124
  function stripCodeFences(text) {
5102
5125
  const match = /^```[a-zA-Z]*[ \t]*\r?\n([\s\S]*?)\r?\n?```$/.exec(
5103
5126
  text.trim()
@@ -5105,262 +5128,7 @@ function stripCodeFences(text) {
5105
5128
  return match ? match[1] : text;
5106
5129
  }
5107
5130
 
5108
- // src/task/index.ts
5109
- function resolveStepType(name) {
5110
- const meta = stepMetadata[name];
5111
- return meta ? meta.stepType : name;
5112
- }
5113
- function mapTools(tools) {
5114
- return tools.map((t) => {
5115
- if (typeof t === "object" && "execute" in t) {
5116
- return {
5117
- name: t.name,
5118
- description: t.description,
5119
- inputSchema: t.inputSchema ?? { type: "object", properties: {} }
5120
- };
5121
- }
5122
- if (typeof t === "object" && "appMethod" in t) {
5123
- return {
5124
- appMethod: t.appMethod,
5125
- ...t.description ? { description: t.description } : {},
5126
- ...t.defaults ? { defaults: t.defaults } : {}
5127
- };
5128
- }
5129
- const method = typeof t === "string" ? t : t.method;
5130
- const stepType = resolveStepType(method);
5131
- const defaults = typeof t === "object" ? t.defaults : void 0;
5132
- return defaults ? { stepType, defaults } : { stepType };
5133
- });
5134
- }
5135
- function buildTaskRequestBody(options) {
5136
- return {
5137
- prompt: options.prompt,
5138
- input: options.input,
5139
- tools: mapTools(options.tools),
5140
- // The legacy whole-task route requires structuredOutputExample and
5141
- // composes its own prompt server-side, so in schema mode we synthesize a
5142
- // skeleton example from the schema. Validation still happens client-side
5143
- // after the result comes back (see _runTaskInner).
5144
- structuredOutputExample: options.outputSchema ? JSON.stringify(buildExampleFromSchema(options.outputSchema)) : typeof options.structuredOutputExample === "string" ? options.structuredOutputExample : JSON.stringify(options.structuredOutputExample),
5145
- model: options.model,
5146
- ...options.maxTurns != null && { maxTurns: options.maxTurns },
5147
- ...options.appId != null && { appId: options.appId },
5148
- ...options.threadId != null && { threadId: options.threadId }
5149
- };
5150
- }
5151
- function sleep2(ms) {
5152
- return new Promise((resolve) => setTimeout(resolve, ms));
5153
- }
5154
- function isDevMode() {
5155
- return !!(process.env.CALLBACK_TOKEN || getRequestContext()?.callbackToken);
5156
- }
5157
- function logTaskResult(result) {
5158
- if (!isDevMode()) {
5159
- return;
5160
- }
5161
- const toolSummary = result.toolCalls.map((tc) => `${tc.name} (${tc.durationMs}ms) ${tc.success ? "\u2713" : "\u2717"}`).join(", ");
5162
- console.log(
5163
- `[task] ${result.turns} turn${result.turns === 1 ? "" : "s"}` + (toolSummary ? `: ${toolSummary}` : "") + ` | ${result.parsedSuccessfully ? "output OK" : "\u26A0 output not valid JSON"} | cost: ${result.usage.totalBillingCost} | trace: ${result.traceId}`
5164
- );
5165
- }
5166
- async function runTaskPoll(httpConfig, body) {
5167
- const traceId = randomUUID2();
5168
- const { data } = await request(
5169
- httpConfig,
5170
- "POST",
5171
- "/task",
5172
- body
5173
- );
5174
- const pollUrl = `${httpConfig.baseUrl}/developer/v2/task/poll/${data.taskToken}`;
5175
- let pollDelay = 300;
5176
- while (true) {
5177
- await sleep2(pollDelay);
5178
- pollDelay = Math.min(pollDelay * 1.5, 3e3);
5179
- const res = await fetch(pollUrl, {
5180
- headers: { "User-Agent": "@mindstudio-ai/agent" }
5181
- });
5182
- if (res.status === 502 || res.status === 503 || res.status === 504) {
5183
- continue;
5184
- }
5185
- if (res.status === 404) {
5186
- throw new MindStudioError(
5187
- "Task poll token not found or expired.",
5188
- "poll_token_expired",
5189
- 404
5190
- );
5191
- }
5192
- if (!res.ok) {
5193
- const errorBody = await res.json().catch(() => ({}));
5194
- throw new MindStudioError(
5195
- errorBody.message ?? errorBody.error ?? `Task poll failed: ${res.status} ${res.statusText}`,
5196
- errorCodeFrom(errorBody, "poll_error"),
5197
- res.status,
5198
- errorBody
5199
- );
5200
- }
5201
- const poll = await res.json();
5202
- if (poll.status === "pending") {
5203
- if (isDevMode() && poll.currentTurn != null) {
5204
- console.log(
5205
- `[task] running... turn ${poll.currentTurn}/${poll.maxTurns ?? "?"}`
5206
- );
5207
- }
5208
- continue;
5209
- }
5210
- if (poll.status === "error") {
5211
- throw new MindStudioError(
5212
- poll.error ?? "Task execution failed.",
5213
- "task_execution_error",
5214
- 500
5215
- );
5216
- }
5217
- const result = {
5218
- output: poll.output,
5219
- outputRaw: poll.outputRaw ?? "",
5220
- parsedSuccessfully: poll.parsedSuccessfully ?? true,
5221
- turns: poll.turns ?? 0,
5222
- usage: poll.usage ?? {
5223
- inputTokens: 0,
5224
- outputTokens: 0,
5225
- totalBillingCost: 0
5226
- },
5227
- toolCalls: poll.toolCalls ?? [],
5228
- traceId
5229
- };
5230
- logTaskResult(result);
5231
- return result;
5232
- }
5233
- }
5234
- async function runTaskStream(httpConfig, body, onEvent) {
5235
- const traceId = randomUUID2();
5236
- const url = `${httpConfig.baseUrl}/developer/v2/task`;
5237
- const res = await fetch(url, {
5238
- method: "POST",
5239
- headers: {
5240
- Authorization: `Bearer ${httpConfig.token}`,
5241
- "Content-Type": "application/json",
5242
- "User-Agent": "@mindstudio-ai/agent",
5243
- Accept: "text/event-stream"
5244
- },
5245
- body: JSON.stringify(body)
5246
- });
5247
- if (!res.ok) {
5248
- let message = `${res.status} ${res.statusText}`;
5249
- let code = "api_error";
5250
- let details;
5251
- try {
5252
- const text = await res.text();
5253
- try {
5254
- const errBody = JSON.parse(text);
5255
- details = errBody;
5256
- const errMsg = (typeof errBody.error === "string" ? errBody.error : void 0) ?? (typeof errBody.message === "string" ? errBody.message : void 0);
5257
- if (errMsg) {
5258
- message = errMsg;
5259
- }
5260
- code = errorCodeFrom(errBody, code);
5261
- } catch {
5262
- const stripped = text.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
5263
- if (stripped) {
5264
- message = stripped.slice(0, 200);
5265
- }
5266
- }
5267
- } catch {
5268
- }
5269
- throw new MindStudioError(`[task] ${message}`, code, res.status, details);
5270
- }
5271
- const reader = res.body.getReader();
5272
- const decoder = new TextDecoder();
5273
- let buffer = "";
5274
- let result = null;
5275
- while (true) {
5276
- const { done, value } = await reader.read();
5277
- if (done) {
5278
- break;
5279
- }
5280
- buffer += decoder.decode(value, { stream: true });
5281
- const lines = buffer.split("\n");
5282
- buffer = lines.pop() ?? "";
5283
- for (const line of lines) {
5284
- if (!line.startsWith("data: ")) {
5285
- continue;
5286
- }
5287
- try {
5288
- const event = JSON.parse(line.slice(6));
5289
- onEvent(event);
5290
- if (event.type === "error") {
5291
- throw new MindStudioError(
5292
- event.error ?? "Task execution failed.",
5293
- "task_execution_error",
5294
- 500
5295
- );
5296
- }
5297
- if (event.type === "done") {
5298
- result = {
5299
- output: event.output,
5300
- outputRaw: event.outputRaw ?? "",
5301
- parsedSuccessfully: event.parsedSuccessfully ?? true,
5302
- turns: event.turns ?? 0,
5303
- usage: event.usage ?? {
5304
- inputTokens: 0,
5305
- outputTokens: 0,
5306
- totalBillingCost: 0
5307
- },
5308
- toolCalls: event.toolCalls ?? [],
5309
- traceId
5310
- };
5311
- }
5312
- } catch (err) {
5313
- if (err instanceof MindStudioError) {
5314
- throw err;
5315
- }
5316
- }
5317
- }
5318
- }
5319
- if (buffer.startsWith("data: ")) {
5320
- try {
5321
- const event = JSON.parse(buffer.slice(6));
5322
- onEvent(event);
5323
- if (event.type === "error") {
5324
- throw new MindStudioError(
5325
- event.error ?? "Task execution failed.",
5326
- "task_execution_error",
5327
- 500
5328
- );
5329
- }
5330
- if (event.type === "done") {
5331
- result = {
5332
- output: event.output,
5333
- outputRaw: event.outputRaw ?? "",
5334
- parsedSuccessfully: event.parsedSuccessfully ?? true,
5335
- turns: event.turns ?? 0,
5336
- usage: event.usage ?? {
5337
- inputTokens: 0,
5338
- outputTokens: 0,
5339
- totalBillingCost: 0
5340
- },
5341
- toolCalls: event.toolCalls ?? [],
5342
- traceId
5343
- };
5344
- }
5345
- } catch (err) {
5346
- if (err instanceof MindStudioError) {
5347
- throw err;
5348
- }
5349
- }
5350
- }
5351
- if (!result) {
5352
- throw new MindStudioError(
5353
- "[task] Stream ended without a done event. The task execution may have been interrupted.",
5354
- "stream_error",
5355
- 500
5356
- );
5357
- }
5358
- logTaskResult(result);
5359
- return result;
5360
- }
5361
-
5362
5131
  // src/task/local.ts
5363
- import { randomUUID as randomUUID3 } from "crypto";
5364
5132
  var DEFAULT_MAX_TURNS = 20;
5365
5133
  var MAX_TURNS_LIMIT = 100;
5366
5134
  var MAX_SCHEMA_REPAIR_ATTEMPTS = 3;
@@ -5368,7 +5136,6 @@ var MAX_TOOL_OUTPUT_CHARS = 5e4;
5368
5136
  var MAX_TURN_ATTEMPTS = 5;
5369
5137
  var INITIAL_BACKOFF_MS = 1e3;
5370
5138
  var STALL_TIMEOUT_MS = 3e5;
5371
- var TURN_UNAVAILABLE_CODE = "task_turn_unavailable";
5372
5139
  var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
5373
5140
  function isPlainObject(value) {
5374
5141
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
@@ -5614,7 +5381,7 @@ ${structuredOutputExample}
5614
5381
  Math.max(options.maxTurns || DEFAULT_MAX_TURNS, 1),
5615
5382
  MAX_TURNS_LIMIT
5616
5383
  );
5617
- const traceId = randomUUID3();
5384
+ const traceId = randomUUID2();
5618
5385
  const messages = [
5619
5386
  { role: "user", content: JSON.stringify(options.input) }
5620
5387
  ];
@@ -5714,13 +5481,6 @@ ${structuredOutputExample}
5714
5481
  traceId
5715
5482
  });
5716
5483
  } catch (err) {
5717
- if (loopCount === 1 && err instanceof TurnError && err.status === 404 && err.errorCode === "not_found") {
5718
- throw new MindStudioError(
5719
- "Task turn endpoint unavailable.",
5720
- TURN_UNAVAILABLE_CODE,
5721
- 404
5722
- );
5723
- }
5724
5484
  return turnFailure(err);
5725
5485
  }
5726
5486
  accumulate(turn);
@@ -6945,108 +6705,75 @@ var MindStudioAgent = class {
6945
6705
  }
6946
6706
  async _runTaskInner(options) {
6947
6707
  const httpConfig = this._currentHttpConfig;
6948
- try {
6949
- return await runTaskLocal(
6950
- {
6951
- httpConfig,
6952
- executeStepTool: async (stepType, input) => {
6953
- try {
6954
- const result = await this.executeStep(stepType, input, {
6955
- requestSource: "v2-task"
6956
- });
6957
- const output = {};
6958
- let billingCost = 0;
6959
- for (const [key, value] of Object.entries(
6960
- result
6961
- )) {
6962
- if (key === "$billingCost" && typeof value === "number") {
6963
- billingCost = value;
6964
- }
6965
- if (!key.startsWith("$")) {
6966
- output[key] = value;
6967
- }
6708
+ return runTaskLocal(
6709
+ {
6710
+ httpConfig,
6711
+ executeStepTool: async (stepType, input) => {
6712
+ try {
6713
+ const result = await this.executeStep(stepType, input, {
6714
+ requestSource: "v2-task"
6715
+ });
6716
+ const output = {};
6717
+ let billingCost = 0;
6718
+ for (const [key, value] of Object.entries(
6719
+ result
6720
+ )) {
6721
+ if (key === "$billingCost" && typeof value === "number") {
6722
+ billingCost = value;
6968
6723
  }
6969
- return { output, billingCost, isError: false };
6970
- } catch (err) {
6971
- return {
6972
- output: {
6973
- error: err instanceof Error ? err.message : "Step execution failed"
6974
- },
6975
- billingCost: 0,
6976
- isError: true
6977
- };
6978
- }
6979
- },
6980
- executeMethodTool: async (methodId, input, call) => {
6981
- try {
6982
- const { data } = await request(
6983
- { ...httpConfig, maxRetries: 0 },
6984
- "POST",
6985
- "/task/invoke-method",
6986
- {
6987
- methodId,
6988
- input,
6989
- traceId: call.traceId,
6990
- toolCallId: call.toolCallId
6991
- }
6992
- );
6993
- if (data.error) {
6994
- return {
6995
- output: { error: data.error },
6996
- billingCost: 0,
6997
- isError: true
6998
- };
6724
+ if (!key.startsWith("$")) {
6725
+ output[key] = value;
6999
6726
  }
7000
- return {
7001
- output: data.output ?? null,
7002
- billingCost: 0,
7003
- isError: false
7004
- };
7005
- } catch (err) {
7006
- return {
7007
- output: {
7008
- error: err instanceof Error ? err.message : "Method execution failed"
7009
- },
7010
- billingCost: 0,
7011
- isError: true
7012
- };
7013
6727
  }
6728
+ return { output, billingCost, isError: false };
6729
+ } catch (err) {
6730
+ return {
6731
+ output: {
6732
+ error: err instanceof Error ? err.message : "Step execution failed"
6733
+ },
6734
+ billingCost: 0,
6735
+ isError: true
6736
+ };
7014
6737
  }
7015
6738
  },
7016
- options
7017
- );
7018
- } catch (err) {
7019
- if (err instanceof MindStudioError && err.code === TURN_UNAVAILABLE_CODE) {
7020
- if (options.tools.some((t) => typeof t === "object" && "execute" in t)) {
7021
- throw new MindStudioError(
7022
- "[task] This server does not support the per-turn task endpoint required for function tools.",
7023
- "task_function_tools_unsupported",
7024
- 404
7025
- );
7026
- }
7027
- const body = buildTaskRequestBody(options);
7028
- const result = options.onEvent ? await runTaskStream(httpConfig, body, options.onEvent) : await runTaskPoll(httpConfig, body);
7029
- if (options.outputSchema) {
7030
- const errors = result.parsedSuccessfully ? validateAgainstSchema(result.output, options.outputSchema) : [{ path: "$", message: "output was not valid JSON" }];
7031
- if (errors.length > 0) {
7032
- throw new MindStudioError(
7033
- "[task] Output did not conform to outputSchema (legacy task route, no repair turns).",
7034
- "task_output_schema_mismatch",
7035
- 422,
6739
+ executeMethodTool: async (methodId, input, call) => {
6740
+ try {
6741
+ const { data } = await request(
6742
+ { ...httpConfig, maxRetries: 0 },
6743
+ "POST",
6744
+ "/task/invoke-method",
7036
6745
  {
7037
- outputRaw: result.outputRaw,
7038
- errors,
7039
- turns: result.turns,
7040
- usage: result.usage,
7041
- toolCalls: result.toolCalls
6746
+ methodId,
6747
+ input,
6748
+ traceId: call.traceId,
6749
+ toolCallId: call.toolCallId
7042
6750
  }
7043
6751
  );
6752
+ if (data.error) {
6753
+ return {
6754
+ output: { error: data.error },
6755
+ billingCost: 0,
6756
+ isError: true
6757
+ };
6758
+ }
6759
+ return {
6760
+ output: data.output ?? null,
6761
+ billingCost: 0,
6762
+ isError: false
6763
+ };
6764
+ } catch (err) {
6765
+ return {
6766
+ output: {
6767
+ error: err instanceof Error ? err.message : "Method execution failed"
6768
+ },
6769
+ billingCost: 0,
6770
+ isError: true
6771
+ };
7044
6772
  }
7045
6773
  }
7046
- return result;
7047
- }
7048
- throw err;
7049
- }
6774
+ },
6775
+ options
6776
+ );
7050
6777
  }
7051
6778
  /**
7052
6779
  * Get the authenticated user's identity and organization info.
@@ -8419,8 +8146,18 @@ function defineJewel(method, config) {
8419
8146
  }
8420
8147
 
8421
8148
  // src/mapper/index.ts
8149
+ import { gzipSync } from "zlib";
8422
8150
  function documents(list) {
8423
- return { kind: "documents", documents: Array.isArray(list) ? list : [list] };
8151
+ return {
8152
+ kind: "documents",
8153
+ documents: isIterable(list) || isAsyncIterable(list) ? list : [list]
8154
+ };
8155
+ }
8156
+ function isIterable(value) {
8157
+ return value !== null && typeof value === "object" && typeof value[Symbol.iterator] === "function";
8158
+ }
8159
+ function isAsyncIterable(value) {
8160
+ return value !== null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function";
8424
8161
  }
8425
8162
  function passthrough(options) {
8426
8163
  return {
@@ -8440,6 +8177,8 @@ function deletes(externalIds) {
8440
8177
  }
8441
8178
  var DEFAULT_TIMEOUT_MS = 3e4;
8442
8179
  var DEFAULT_CONCURRENCY2 = 4;
8180
+ var PART_DOCUMENTS = 1e3;
8181
+ var PART_BYTES = 8 * 1024 * 1024;
8443
8182
  function errorInfo2(e) {
8444
8183
  if (e instanceof Error) {
8445
8184
  return { message: e.message, stack: e.stack };
@@ -8471,7 +8210,7 @@ function objectFor(input) {
8471
8210
  etag: input.etag,
8472
8211
  lastModified: input.lastModified,
8473
8212
  metadata: input.metadata,
8474
- entities: input.entities ?? null,
8213
+ entities: input.entities,
8475
8214
  bytes,
8476
8215
  text: async () => new TextDecoder("utf-8").decode(await bytes()),
8477
8216
  json: async () => JSON.parse(new TextDecoder("utf-8").decode(await bytes()))
@@ -8480,13 +8219,17 @@ function objectFor(input) {
8480
8219
  var OUTCOME_KINDS = /* @__PURE__ */ new Set(["documents", "passthrough", "skip", "deletes"]);
8481
8220
  function normalizeOutcome(value) {
8482
8221
  const outcome = value;
8222
+ const invalid = () => ({
8223
+ kind: "error",
8224
+ error: {
8225
+ message: `map returned an invalid outcome: ${JSON.stringify(value)?.slice(0, 200)}. Return documents(), passthrough(), skip() or deletes().`
8226
+ }
8227
+ });
8483
8228
  if (!outcome || typeof outcome !== "object" || !OUTCOME_KINDS.has(outcome.kind)) {
8484
- return {
8485
- kind: "error",
8486
- error: {
8487
- message: `map returned an invalid outcome: ${JSON.stringify(value)?.slice(0, 200)}. Return documents(), passthrough(), skip() or deletes().`
8488
- }
8489
- };
8229
+ return invalid();
8230
+ }
8231
+ if (outcome.kind === "documents" && !isIterable(outcome.documents) && !isAsyncIterable(outcome.documents)) {
8232
+ return invalid();
8490
8233
  }
8491
8234
  return outcome;
8492
8235
  }
@@ -8508,13 +8251,107 @@ async function withTimeout(work, timeoutMs, key) {
8508
8251
  }
8509
8252
  }
8510
8253
  }
8254
+ var PartWriter = class {
8255
+ constructor(source, spillId) {
8256
+ this.source = source;
8257
+ this.spillId = spillId;
8258
+ }
8259
+ source;
8260
+ spillId;
8261
+ parts = [];
8262
+ /** Objects whose documents did not all store, with why. */
8263
+ failed = /* @__PURE__ */ new Map();
8264
+ lines = [];
8265
+ objects = /* @__PURE__ */ new Set();
8266
+ bytes = 0;
8267
+ uploading = Promise.resolve();
8268
+ async write(o, i, d) {
8269
+ const line = JSON.stringify({ o, i, d });
8270
+ this.lines.push(line);
8271
+ this.objects.add(o);
8272
+ this.bytes += line.length;
8273
+ if (this.lines.length >= PART_DOCUMENTS || this.bytes >= PART_BYTES) {
8274
+ await this.flush();
8275
+ }
8276
+ }
8277
+ /** Store what the buffer holds; resolves once every upload so far is done. */
8278
+ async flush() {
8279
+ if (this.lines.length > 0) {
8280
+ const lines = this.lines;
8281
+ const objects = this.objects;
8282
+ this.lines = [];
8283
+ this.objects = /* @__PURE__ */ new Set();
8284
+ this.bytes = 0;
8285
+ this.uploading = this.uploading.then(() => this.store(lines, objects));
8286
+ }
8287
+ await this.uploading;
8288
+ }
8289
+ async store(lines, objects) {
8290
+ try {
8291
+ const grant = await this.source._mapSpillGrant(this.spillId);
8292
+ const form = new FormData();
8293
+ for (const [k, v] of Object.entries(grant.uploadFields)) {
8294
+ form.append(k, v);
8295
+ }
8296
+ form.append(
8297
+ "file",
8298
+ new Blob(
8299
+ [new Uint8Array(gzipSync(Buffer.from(lines.join("\n") + "\n")))],
8300
+ { type: "application/gzip" }
8301
+ ),
8302
+ `${grant.part}.ndjson.gz`
8303
+ );
8304
+ const response = await fetch(grant.uploadUrl, {
8305
+ method: "POST",
8306
+ body: form
8307
+ });
8308
+ if (!response.ok) {
8309
+ throw new Error(
8310
+ `Could not store part ${grant.part} of the documents: HTTP ${response.status}`
8311
+ );
8312
+ }
8313
+ this.parts.push({ part: grant.part, count: lines.length });
8314
+ } catch (e) {
8315
+ const err = e instanceof Error ? e : new Error(String(e));
8316
+ for (const o of objects) {
8317
+ if (!this.failed.has(o)) {
8318
+ this.failed.set(o, err);
8319
+ }
8320
+ }
8321
+ }
8322
+ }
8323
+ };
8324
+ async function writeDocuments(list, ctx) {
8325
+ const iterator = isAsyncIterable(list) ? list[Symbol.asyncIterator]() : list[Symbol.iterator]();
8326
+ let count = 0;
8327
+ try {
8328
+ while (true) {
8329
+ const step = await withTimeout(
8330
+ Promise.resolve(iterator.next()),
8331
+ ctx.timeoutMs,
8332
+ ctx.key
8333
+ );
8334
+ if (step.done) {
8335
+ return count;
8336
+ }
8337
+ await ctx.writer.write(ctx.o, count, step.value);
8338
+ count += 1;
8339
+ }
8340
+ } catch (e) {
8341
+ await Promise.resolve(iterator.return?.()).catch(() => {
8342
+ });
8343
+ throw e;
8344
+ }
8345
+ }
8511
8346
  function defineMapper(source, config) {
8347
+ const handle = source;
8512
8348
  const run = async (params) => {
8513
8349
  const startedAt = Date.now();
8514
8350
  const timeoutMs = params.timeoutMs ?? DEFAULT_TIMEOUT_MS;
8515
8351
  const concurrency = Math.max(1, params.concurrency ?? DEFAULT_CONCURRENCY2);
8516
8352
  const objects = Array.isArray(params.objects) ? params.objects : [];
8517
8353
  const outcomes = new Array(objects.length);
8354
+ const writer = params.spill?.id && typeof handle._mapSpillGrant === "function" ? new PartWriter(handle, params.spill.id) : null;
8518
8355
  let next = 0;
8519
8356
  const worker = async () => {
8520
8357
  while (true) {
@@ -8526,13 +8363,30 @@ function defineMapper(source, config) {
8526
8363
  const t = Date.now();
8527
8364
  let outcome;
8528
8365
  try {
8529
- outcome = normalizeOutcome(
8366
+ const mapped = normalizeOutcome(
8530
8367
  await withTimeout(
8531
8368
  Promise.resolve().then(() => config.map(objectFor(input))),
8532
8369
  timeoutMs,
8533
8370
  input.key
8534
8371
  )
8535
8372
  );
8373
+ if (mapped.kind !== "documents") {
8374
+ outcome = mapped;
8375
+ } else if (!writer) {
8376
+ throw new Error(
8377
+ "documents() needs the platform to store what it produces, and this frame was given nowhere to store it."
8378
+ );
8379
+ } else {
8380
+ outcome = {
8381
+ kind: "documents",
8382
+ count: await writeDocuments(mapped.documents, {
8383
+ o: index,
8384
+ key: input.key,
8385
+ timeoutMs,
8386
+ writer
8387
+ })
8388
+ };
8389
+ }
8536
8390
  } catch (e) {
8537
8391
  outcome = { kind: "error", error: errorInfo2(e) };
8538
8392
  }
@@ -8546,10 +8400,17 @@ function defineMapper(source, config) {
8546
8400
  await Promise.all(
8547
8401
  Array.from({ length: Math.min(concurrency, objects.length) }, worker)
8548
8402
  );
8403
+ await writer?.flush();
8404
+ for (const [o, err] of writer?.failed ?? []) {
8405
+ if (outcomes[o]?.outcome.kind === "documents") {
8406
+ outcomes[o].outcome = { kind: "error", error: errorInfo2(err) };
8407
+ }
8408
+ }
8549
8409
  return {
8550
8410
  v: 1,
8551
8411
  source: source.name,
8552
8412
  outcomes,
8413
+ parts: writer?.parts ?? [],
8553
8414
  startedAt,
8554
8415
  durationMs: Date.now() - startedAt
8555
8416
  };