@mindstudio-ai/agent 0.1.139 → 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;
@@ -3261,7 +3255,8 @@ var DataSource = class {
3261
3255
  * it is who or what was written about then. No filter covers the corpus.
3262
3256
  * `chunks` is how many matching passages carry each id, not documents: a
3263
3257
  * long document's passages each count. Approximate counts, ordered most
3264
- * first; `limit` defaults to 20, at most 100.
3258
+ * first; `limit` defaults to 20, at most 1,000. One document's entities:
3259
+ * `{ filter: { documentIds: [id] } }`.
3265
3260
  *
3266
3261
  * Shares the search rate limit (300/minute per app), and shows the same
3267
3262
  * `index_warming` / `index_building` as search while a corpus loads.
@@ -3409,7 +3404,15 @@ var DataSource = class {
3409
3404
  * as the new one is registered, before its build lands; a search in that
3410
3405
  * window sees neither. **Throws `mapper_skipped`** (HTTP 422) when the
3411
3406
  * mapper skipped the object — a specific thing it refused, which the caller
3412
- * 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`.
3413
3416
  */
3414
3417
  async add(content, options) {
3415
3418
  const bytes = typeof content === "string" ? Buffer.from(content) : Buffer.from(content);
@@ -3419,6 +3422,7 @@ var DataSource = class {
3419
3422
  ...options.contentType ? { contentType: options.contentType } : {},
3420
3423
  ...options.metadata !== void 0 ? { metadata: options.metadata } : {},
3421
3424
  ...options.entities !== void 0 ? { entities: options.entities } : {},
3425
+ ...options.budgetDollars !== void 0 ? { budgetDollars: options.budgetDollars } : {},
3422
3426
  body: bytes.toString("base64")
3423
3427
  });
3424
3428
  }
@@ -3452,6 +3456,59 @@ var DataSource = class {
3452
3456
  });
3453
3457
  return { documents: documents2 ?? [], nextCursor: nextCursor ?? null };
3454
3458
  }
3459
+ /**
3460
+ * One page of the passages matching a filter, unranked: every page that
3461
+ * mentions an asset (`{ entities: ['P-1004A'] }`), every passage holding a
3462
+ * phrase, every chunk of some documents. Where {@link search} ranks and
3463
+ * stops at its top results and {@link count} gives a number, this lists
3464
+ * them all. Passages come document by document, each document's in order;
3465
+ * pass `nextCursor` back for the next page, which is null after the last.
3466
+ * `limit` defaults to 50, at most 200.
3467
+ *
3468
+ * Shares the search rate limit (300/minute per app), and shows the same
3469
+ * `index_warming` / `index_building` as search while a corpus loads.
3470
+ */
3471
+ async passages(options) {
3472
+ const { passages, nextCursor, latencyMs } = await this._call("passages", {
3473
+ slug: this._slug,
3474
+ ...options?.filter !== void 0 ? { filter: options.filter } : {},
3475
+ ...options?.cursor ? { cursor: options.cursor } : {},
3476
+ ...options?.limit !== void 0 ? { limit: options.limit } : {}
3477
+ });
3478
+ return {
3479
+ passages: passages ?? [],
3480
+ nextCursor: nextCursor ?? null,
3481
+ latencyMs: latencyMs ?? 0
3482
+ };
3483
+ }
3484
+ /**
3485
+ * Every passage matching a filter, a page at a time behind the scenes: the
3486
+ * walk over {@link passages}. Each page counts against the search rate
3487
+ * limit, so a large walk belongs in a background task.
3488
+ *
3489
+ * ```ts
3490
+ * for await (const passage of Manuals.allPassages({ entities: ['P-1004A'] })) {
3491
+ * mentions.push({ asset: 'P-1004A', ...passage.citation });
3492
+ * }
3493
+ * ```
3494
+ */
3495
+ async *allPassages(filter, options) {
3496
+ let cursor = null;
3497
+ while (true) {
3498
+ const page = await this.passages({
3499
+ ...filter !== void 0 ? { filter } : {},
3500
+ cursor,
3501
+ limit: options?.pageSize ?? 200
3502
+ });
3503
+ for (const passage of page.passages) {
3504
+ yield passage;
3505
+ }
3506
+ if (!page.nextCursor || page.passages.length === 0) {
3507
+ return;
3508
+ }
3509
+ cursor = page.nextCursor;
3510
+ }
3511
+ }
3455
3512
  /**
3456
3513
  * Every document in the corpus, oldest first, a page at a time behind the
3457
3514
  * scenes. This is how an app builds its own view of a large corpus (a
@@ -3577,6 +3634,15 @@ var DataSource = class {
3577
3634
  ...name ? { name } : {}
3578
3635
  });
3579
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
+ }
3580
3646
  /**
3581
3647
  * @internal Content hash of some bytes, matching what the server computes.
3582
3648
  * Exposed for callers that want to check whether they already added a file.
@@ -3727,7 +3793,7 @@ function createEvents(call) {
3727
3793
  };
3728
3794
  }
3729
3795
 
3730
- // src/task/index.ts
3796
+ // src/task/local.ts
3731
3797
  import { randomUUID as randomUUID2 } from "crypto";
3732
3798
 
3733
3799
  // src/generated/metadata.ts
@@ -4872,6 +4938,49 @@ var stepMetadata = {
4872
4938
  }
4873
4939
  };
4874
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
+
4875
4984
  // src/task/schema.ts
4876
4985
  function describe(value) {
4877
4986
  if (value === void 0) {
@@ -5012,38 +5121,6 @@ function assertSupportedNode(schema, path) {
5012
5121
  assertSupportedNode(schema.items, `${path}[]`);
5013
5122
  }
5014
5123
  }
5015
- function buildExampleFromSchema(schema) {
5016
- if (schema.const !== void 0) {
5017
- return schema.const;
5018
- }
5019
- if (schema.enum !== void 0 && schema.enum.length > 0) {
5020
- return schema.enum[0];
5021
- }
5022
- const names = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
5023
- const name = names.find((n) => n !== "null") ?? names[0];
5024
- switch (name) {
5025
- case "object": {
5026
- const example = {};
5027
- for (const [key, propSchema] of Object.entries(schema.properties ?? {})) {
5028
- example[key] = buildExampleFromSchema(propSchema);
5029
- }
5030
- return example;
5031
- }
5032
- case "array":
5033
- return schema.items ? [buildExampleFromSchema(schema.items)] : [];
5034
- case "string":
5035
- return "...";
5036
- case "number":
5037
- case "integer":
5038
- return 0;
5039
- case "boolean":
5040
- return true;
5041
- case "null":
5042
- return null;
5043
- default:
5044
- return null;
5045
- }
5046
- }
5047
5124
  function stripCodeFences(text) {
5048
5125
  const match = /^```[a-zA-Z]*[ \t]*\r?\n([\s\S]*?)\r?\n?```$/.exec(
5049
5126
  text.trim()
@@ -5051,262 +5128,7 @@ function stripCodeFences(text) {
5051
5128
  return match ? match[1] : text;
5052
5129
  }
5053
5130
 
5054
- // src/task/index.ts
5055
- function resolveStepType(name) {
5056
- const meta = stepMetadata[name];
5057
- return meta ? meta.stepType : name;
5058
- }
5059
- function mapTools(tools) {
5060
- return tools.map((t) => {
5061
- if (typeof t === "object" && "execute" in t) {
5062
- return {
5063
- name: t.name,
5064
- description: t.description,
5065
- inputSchema: t.inputSchema ?? { type: "object", properties: {} }
5066
- };
5067
- }
5068
- if (typeof t === "object" && "appMethod" in t) {
5069
- return {
5070
- appMethod: t.appMethod,
5071
- ...t.description ? { description: t.description } : {},
5072
- ...t.defaults ? { defaults: t.defaults } : {}
5073
- };
5074
- }
5075
- const method = typeof t === "string" ? t : t.method;
5076
- const stepType = resolveStepType(method);
5077
- const defaults = typeof t === "object" ? t.defaults : void 0;
5078
- return defaults ? { stepType, defaults } : { stepType };
5079
- });
5080
- }
5081
- function buildTaskRequestBody(options) {
5082
- return {
5083
- prompt: options.prompt,
5084
- input: options.input,
5085
- tools: mapTools(options.tools),
5086
- // The legacy whole-task route requires structuredOutputExample and
5087
- // composes its own prompt server-side, so in schema mode we synthesize a
5088
- // skeleton example from the schema. Validation still happens client-side
5089
- // after the result comes back (see _runTaskInner).
5090
- structuredOutputExample: options.outputSchema ? JSON.stringify(buildExampleFromSchema(options.outputSchema)) : typeof options.structuredOutputExample === "string" ? options.structuredOutputExample : JSON.stringify(options.structuredOutputExample),
5091
- model: options.model,
5092
- ...options.maxTurns != null && { maxTurns: options.maxTurns },
5093
- ...options.appId != null && { appId: options.appId },
5094
- ...options.threadId != null && { threadId: options.threadId }
5095
- };
5096
- }
5097
- function sleep2(ms) {
5098
- return new Promise((resolve) => setTimeout(resolve, ms));
5099
- }
5100
- function isDevMode() {
5101
- return !!(process.env.CALLBACK_TOKEN || getRequestContext()?.callbackToken);
5102
- }
5103
- function logTaskResult(result) {
5104
- if (!isDevMode()) {
5105
- return;
5106
- }
5107
- const toolSummary = result.toolCalls.map((tc) => `${tc.name} (${tc.durationMs}ms) ${tc.success ? "\u2713" : "\u2717"}`).join(", ");
5108
- console.log(
5109
- `[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}`
5110
- );
5111
- }
5112
- async function runTaskPoll(httpConfig, body) {
5113
- const traceId = randomUUID2();
5114
- const { data } = await request(
5115
- httpConfig,
5116
- "POST",
5117
- "/task",
5118
- body
5119
- );
5120
- const pollUrl = `${httpConfig.baseUrl}/developer/v2/task/poll/${data.taskToken}`;
5121
- let pollDelay = 300;
5122
- while (true) {
5123
- await sleep2(pollDelay);
5124
- pollDelay = Math.min(pollDelay * 1.5, 3e3);
5125
- const res = await fetch(pollUrl, {
5126
- headers: { "User-Agent": "@mindstudio-ai/agent" }
5127
- });
5128
- if (res.status === 502 || res.status === 503 || res.status === 504) {
5129
- continue;
5130
- }
5131
- if (res.status === 404) {
5132
- throw new MindStudioError(
5133
- "Task poll token not found or expired.",
5134
- "poll_token_expired",
5135
- 404
5136
- );
5137
- }
5138
- if (!res.ok) {
5139
- const errorBody = await res.json().catch(() => ({}));
5140
- throw new MindStudioError(
5141
- errorBody.message ?? errorBody.error ?? `Task poll failed: ${res.status} ${res.statusText}`,
5142
- errorCodeFrom(errorBody, "poll_error"),
5143
- res.status,
5144
- errorBody
5145
- );
5146
- }
5147
- const poll = await res.json();
5148
- if (poll.status === "pending") {
5149
- if (isDevMode() && poll.currentTurn != null) {
5150
- console.log(
5151
- `[task] running... turn ${poll.currentTurn}/${poll.maxTurns ?? "?"}`
5152
- );
5153
- }
5154
- continue;
5155
- }
5156
- if (poll.status === "error") {
5157
- throw new MindStudioError(
5158
- poll.error ?? "Task execution failed.",
5159
- "task_execution_error",
5160
- 500
5161
- );
5162
- }
5163
- const result = {
5164
- output: poll.output,
5165
- outputRaw: poll.outputRaw ?? "",
5166
- parsedSuccessfully: poll.parsedSuccessfully ?? true,
5167
- turns: poll.turns ?? 0,
5168
- usage: poll.usage ?? {
5169
- inputTokens: 0,
5170
- outputTokens: 0,
5171
- totalBillingCost: 0
5172
- },
5173
- toolCalls: poll.toolCalls ?? [],
5174
- traceId
5175
- };
5176
- logTaskResult(result);
5177
- return result;
5178
- }
5179
- }
5180
- async function runTaskStream(httpConfig, body, onEvent) {
5181
- const traceId = randomUUID2();
5182
- const url = `${httpConfig.baseUrl}/developer/v2/task`;
5183
- const res = await fetch(url, {
5184
- method: "POST",
5185
- headers: {
5186
- Authorization: `Bearer ${httpConfig.token}`,
5187
- "Content-Type": "application/json",
5188
- "User-Agent": "@mindstudio-ai/agent",
5189
- Accept: "text/event-stream"
5190
- },
5191
- body: JSON.stringify(body)
5192
- });
5193
- if (!res.ok) {
5194
- let message = `${res.status} ${res.statusText}`;
5195
- let code = "api_error";
5196
- let details;
5197
- try {
5198
- const text = await res.text();
5199
- try {
5200
- const errBody = JSON.parse(text);
5201
- details = errBody;
5202
- const errMsg = (typeof errBody.error === "string" ? errBody.error : void 0) ?? (typeof errBody.message === "string" ? errBody.message : void 0);
5203
- if (errMsg) {
5204
- message = errMsg;
5205
- }
5206
- code = errorCodeFrom(errBody, code);
5207
- } catch {
5208
- const stripped = text.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
5209
- if (stripped) {
5210
- message = stripped.slice(0, 200);
5211
- }
5212
- }
5213
- } catch {
5214
- }
5215
- throw new MindStudioError(`[task] ${message}`, code, res.status, details);
5216
- }
5217
- const reader = res.body.getReader();
5218
- const decoder = new TextDecoder();
5219
- let buffer = "";
5220
- let result = null;
5221
- while (true) {
5222
- const { done, value } = await reader.read();
5223
- if (done) {
5224
- break;
5225
- }
5226
- buffer += decoder.decode(value, { stream: true });
5227
- const lines = buffer.split("\n");
5228
- buffer = lines.pop() ?? "";
5229
- for (const line of lines) {
5230
- if (!line.startsWith("data: ")) {
5231
- continue;
5232
- }
5233
- try {
5234
- const event = JSON.parse(line.slice(6));
5235
- onEvent(event);
5236
- if (event.type === "error") {
5237
- throw new MindStudioError(
5238
- event.error ?? "Task execution failed.",
5239
- "task_execution_error",
5240
- 500
5241
- );
5242
- }
5243
- if (event.type === "done") {
5244
- result = {
5245
- output: event.output,
5246
- outputRaw: event.outputRaw ?? "",
5247
- parsedSuccessfully: event.parsedSuccessfully ?? true,
5248
- turns: event.turns ?? 0,
5249
- usage: event.usage ?? {
5250
- inputTokens: 0,
5251
- outputTokens: 0,
5252
- totalBillingCost: 0
5253
- },
5254
- toolCalls: event.toolCalls ?? [],
5255
- traceId
5256
- };
5257
- }
5258
- } catch (err) {
5259
- if (err instanceof MindStudioError) {
5260
- throw err;
5261
- }
5262
- }
5263
- }
5264
- }
5265
- if (buffer.startsWith("data: ")) {
5266
- try {
5267
- const event = JSON.parse(buffer.slice(6));
5268
- onEvent(event);
5269
- if (event.type === "error") {
5270
- throw new MindStudioError(
5271
- event.error ?? "Task execution failed.",
5272
- "task_execution_error",
5273
- 500
5274
- );
5275
- }
5276
- if (event.type === "done") {
5277
- result = {
5278
- output: event.output,
5279
- outputRaw: event.outputRaw ?? "",
5280
- parsedSuccessfully: event.parsedSuccessfully ?? true,
5281
- turns: event.turns ?? 0,
5282
- usage: event.usage ?? {
5283
- inputTokens: 0,
5284
- outputTokens: 0,
5285
- totalBillingCost: 0
5286
- },
5287
- toolCalls: event.toolCalls ?? [],
5288
- traceId
5289
- };
5290
- }
5291
- } catch (err) {
5292
- if (err instanceof MindStudioError) {
5293
- throw err;
5294
- }
5295
- }
5296
- }
5297
- if (!result) {
5298
- throw new MindStudioError(
5299
- "[task] Stream ended without a done event. The task execution may have been interrupted.",
5300
- "stream_error",
5301
- 500
5302
- );
5303
- }
5304
- logTaskResult(result);
5305
- return result;
5306
- }
5307
-
5308
5131
  // src/task/local.ts
5309
- import { randomUUID as randomUUID3 } from "crypto";
5310
5132
  var DEFAULT_MAX_TURNS = 20;
5311
5133
  var MAX_TURNS_LIMIT = 100;
5312
5134
  var MAX_SCHEMA_REPAIR_ATTEMPTS = 3;
@@ -5314,7 +5136,6 @@ var MAX_TOOL_OUTPUT_CHARS = 5e4;
5314
5136
  var MAX_TURN_ATTEMPTS = 5;
5315
5137
  var INITIAL_BACKOFF_MS = 1e3;
5316
5138
  var STALL_TIMEOUT_MS = 3e5;
5317
- var TURN_UNAVAILABLE_CODE = "task_turn_unavailable";
5318
5139
  var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
5319
5140
  function isPlainObject(value) {
5320
5141
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
@@ -5560,7 +5381,7 @@ ${structuredOutputExample}
5560
5381
  Math.max(options.maxTurns || DEFAULT_MAX_TURNS, 1),
5561
5382
  MAX_TURNS_LIMIT
5562
5383
  );
5563
- const traceId = randomUUID3();
5384
+ const traceId = randomUUID2();
5564
5385
  const messages = [
5565
5386
  { role: "user", content: JSON.stringify(options.input) }
5566
5387
  ];
@@ -5660,13 +5481,6 @@ ${structuredOutputExample}
5660
5481
  traceId
5661
5482
  });
5662
5483
  } catch (err) {
5663
- if (loopCount === 1 && err instanceof TurnError && err.status === 404 && err.errorCode === "not_found") {
5664
- throw new MindStudioError(
5665
- "Task turn endpoint unavailable.",
5666
- TURN_UNAVAILABLE_CODE,
5667
- 404
5668
- );
5669
- }
5670
5484
  return turnFailure(err);
5671
5485
  }
5672
5486
  accumulate(turn);
@@ -6891,108 +6705,75 @@ var MindStudioAgent = class {
6891
6705
  }
6892
6706
  async _runTaskInner(options) {
6893
6707
  const httpConfig = this._currentHttpConfig;
6894
- try {
6895
- return await runTaskLocal(
6896
- {
6897
- httpConfig,
6898
- executeStepTool: async (stepType, input) => {
6899
- try {
6900
- const result = await this.executeStep(stepType, input, {
6901
- requestSource: "v2-task"
6902
- });
6903
- const output = {};
6904
- let billingCost = 0;
6905
- for (const [key, value] of Object.entries(
6906
- result
6907
- )) {
6908
- if (key === "$billingCost" && typeof value === "number") {
6909
- billingCost = value;
6910
- }
6911
- if (!key.startsWith("$")) {
6912
- output[key] = value;
6913
- }
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;
6914
6723
  }
6915
- return { output, billingCost, isError: false };
6916
- } catch (err) {
6917
- return {
6918
- output: {
6919
- error: err instanceof Error ? err.message : "Step execution failed"
6920
- },
6921
- billingCost: 0,
6922
- isError: true
6923
- };
6924
- }
6925
- },
6926
- executeMethodTool: async (methodId, input, call) => {
6927
- try {
6928
- const { data } = await request(
6929
- { ...httpConfig, maxRetries: 0 },
6930
- "POST",
6931
- "/task/invoke-method",
6932
- {
6933
- methodId,
6934
- input,
6935
- traceId: call.traceId,
6936
- toolCallId: call.toolCallId
6937
- }
6938
- );
6939
- if (data.error) {
6940
- return {
6941
- output: { error: data.error },
6942
- billingCost: 0,
6943
- isError: true
6944
- };
6724
+ if (!key.startsWith("$")) {
6725
+ output[key] = value;
6945
6726
  }
6946
- return {
6947
- output: data.output ?? null,
6948
- billingCost: 0,
6949
- isError: false
6950
- };
6951
- } catch (err) {
6952
- return {
6953
- output: {
6954
- error: err instanceof Error ? err.message : "Method execution failed"
6955
- },
6956
- billingCost: 0,
6957
- isError: true
6958
- };
6959
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
+ };
6960
6737
  }
6961
6738
  },
6962
- options
6963
- );
6964
- } catch (err) {
6965
- if (err instanceof MindStudioError && err.code === TURN_UNAVAILABLE_CODE) {
6966
- if (options.tools.some((t) => typeof t === "object" && "execute" in t)) {
6967
- throw new MindStudioError(
6968
- "[task] This server does not support the per-turn task endpoint required for function tools.",
6969
- "task_function_tools_unsupported",
6970
- 404
6971
- );
6972
- }
6973
- const body = buildTaskRequestBody(options);
6974
- const result = options.onEvent ? await runTaskStream(httpConfig, body, options.onEvent) : await runTaskPoll(httpConfig, body);
6975
- if (options.outputSchema) {
6976
- const errors = result.parsedSuccessfully ? validateAgainstSchema(result.output, options.outputSchema) : [{ path: "$", message: "output was not valid JSON" }];
6977
- if (errors.length > 0) {
6978
- throw new MindStudioError(
6979
- "[task] Output did not conform to outputSchema (legacy task route, no repair turns).",
6980
- "task_output_schema_mismatch",
6981
- 422,
6739
+ executeMethodTool: async (methodId, input, call) => {
6740
+ try {
6741
+ const { data } = await request(
6742
+ { ...httpConfig, maxRetries: 0 },
6743
+ "POST",
6744
+ "/task/invoke-method",
6982
6745
  {
6983
- outputRaw: result.outputRaw,
6984
- errors,
6985
- turns: result.turns,
6986
- usage: result.usage,
6987
- toolCalls: result.toolCalls
6746
+ methodId,
6747
+ input,
6748
+ traceId: call.traceId,
6749
+ toolCallId: call.toolCallId
6988
6750
  }
6989
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
+ };
6990
6772
  }
6991
6773
  }
6992
- return result;
6993
- }
6994
- throw err;
6995
- }
6774
+ },
6775
+ options
6776
+ );
6996
6777
  }
6997
6778
  /**
6998
6779
  * Get the authenticated user's identity and organization info.
@@ -8365,8 +8146,18 @@ function defineJewel(method, config) {
8365
8146
  }
8366
8147
 
8367
8148
  // src/mapper/index.ts
8149
+ import { gzipSync } from "zlib";
8368
8150
  function documents(list) {
8369
- 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";
8370
8161
  }
8371
8162
  function passthrough(options) {
8372
8163
  return {
@@ -8386,6 +8177,8 @@ function deletes(externalIds) {
8386
8177
  }
8387
8178
  var DEFAULT_TIMEOUT_MS = 3e4;
8388
8179
  var DEFAULT_CONCURRENCY2 = 4;
8180
+ var PART_DOCUMENTS = 1e3;
8181
+ var PART_BYTES = 8 * 1024 * 1024;
8389
8182
  function errorInfo2(e) {
8390
8183
  if (e instanceof Error) {
8391
8184
  return { message: e.message, stack: e.stack };
@@ -8417,7 +8210,7 @@ function objectFor(input) {
8417
8210
  etag: input.etag,
8418
8211
  lastModified: input.lastModified,
8419
8212
  metadata: input.metadata,
8420
- entities: input.entities ?? null,
8213
+ entities: input.entities,
8421
8214
  bytes,
8422
8215
  text: async () => new TextDecoder("utf-8").decode(await bytes()),
8423
8216
  json: async () => JSON.parse(new TextDecoder("utf-8").decode(await bytes()))
@@ -8426,13 +8219,17 @@ function objectFor(input) {
8426
8219
  var OUTCOME_KINDS = /* @__PURE__ */ new Set(["documents", "passthrough", "skip", "deletes"]);
8427
8220
  function normalizeOutcome(value) {
8428
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
+ });
8429
8228
  if (!outcome || typeof outcome !== "object" || !OUTCOME_KINDS.has(outcome.kind)) {
8430
- return {
8431
- kind: "error",
8432
- error: {
8433
- message: `map returned an invalid outcome: ${JSON.stringify(value)?.slice(0, 200)}. Return documents(), passthrough(), skip() or deletes().`
8434
- }
8435
- };
8229
+ return invalid();
8230
+ }
8231
+ if (outcome.kind === "documents" && !isIterable(outcome.documents) && !isAsyncIterable(outcome.documents)) {
8232
+ return invalid();
8436
8233
  }
8437
8234
  return outcome;
8438
8235
  }
@@ -8454,13 +8251,107 @@ async function withTimeout(work, timeoutMs, key) {
8454
8251
  }
8455
8252
  }
8456
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
+ }
8457
8346
  function defineMapper(source, config) {
8347
+ const handle = source;
8458
8348
  const run = async (params) => {
8459
8349
  const startedAt = Date.now();
8460
8350
  const timeoutMs = params.timeoutMs ?? DEFAULT_TIMEOUT_MS;
8461
8351
  const concurrency = Math.max(1, params.concurrency ?? DEFAULT_CONCURRENCY2);
8462
8352
  const objects = Array.isArray(params.objects) ? params.objects : [];
8463
8353
  const outcomes = new Array(objects.length);
8354
+ const writer = params.spill?.id && typeof handle._mapSpillGrant === "function" ? new PartWriter(handle, params.spill.id) : null;
8464
8355
  let next = 0;
8465
8356
  const worker = async () => {
8466
8357
  while (true) {
@@ -8472,13 +8363,30 @@ function defineMapper(source, config) {
8472
8363
  const t = Date.now();
8473
8364
  let outcome;
8474
8365
  try {
8475
- outcome = normalizeOutcome(
8366
+ const mapped = normalizeOutcome(
8476
8367
  await withTimeout(
8477
8368
  Promise.resolve().then(() => config.map(objectFor(input))),
8478
8369
  timeoutMs,
8479
8370
  input.key
8480
8371
  )
8481
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
+ }
8482
8390
  } catch (e) {
8483
8391
  outcome = { kind: "error", error: errorInfo2(e) };
8484
8392
  }
@@ -8492,10 +8400,17 @@ function defineMapper(source, config) {
8492
8400
  await Promise.all(
8493
8401
  Array.from({ length: Math.min(concurrency, objects.length) }, worker)
8494
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
+ }
8495
8409
  return {
8496
8410
  v: 1,
8497
8411
  source: source.name,
8498
8412
  outcomes,
8413
+ parts: writer?.parts ?? [],
8499
8414
  startedAt,
8500
8415
  durationMs: Date.now() - startedAt
8501
8416
  };
@@ -8519,6 +8434,7 @@ function errorInfo3(e) {
8519
8434
  function refOf(delivery) {
8520
8435
  switch (delivery.kind) {
8521
8436
  case "read":
8437
+ case "relinked":
8522
8438
  return delivery.document.id;
8523
8439
  case "removed":
8524
8440
  return delivery.documentId;
@@ -8549,6 +8465,8 @@ async function withTimeout2(work, timeoutMs, ref) {
8549
8465
  function defineReader(source, config) {
8550
8466
  const documentFor = (delivery) => {
8551
8467
  let cached = delivery.chunks ? Promise.resolve(delivery.chunks) : null;
8468
+ let bytes = null;
8469
+ const { url } = delivery.document;
8552
8470
  return {
8553
8471
  ...delivery.document,
8554
8472
  chunks: () => {
@@ -8556,6 +8474,25 @@ function defineReader(source, config) {
8556
8474
  cached = source.chunks(delivery.document.id);
8557
8475
  }
8558
8476
  return cached;
8477
+ },
8478
+ bytes: () => {
8479
+ if (!bytes) {
8480
+ bytes = (async () => {
8481
+ if (!url) {
8482
+ throw new Error(
8483
+ `The file behind document ${delivery.document.id} is no longer kept.`
8484
+ );
8485
+ }
8486
+ const response = await fetch(url);
8487
+ if (!response.ok) {
8488
+ throw new Error(
8489
+ `Could not read the file behind document ${delivery.document.id}: HTTP ${response.status}`
8490
+ );
8491
+ }
8492
+ return new Uint8Array(await response.arrayBuffer());
8493
+ })();
8494
+ }
8495
+ return bytes;
8559
8496
  }
8560
8497
  };
8561
8498
  };
@@ -8564,6 +8501,9 @@ function defineReader(source, config) {
8564
8501
  case "read":
8565
8502
  await config.read(documentFor(delivery));
8566
8503
  return;
8504
+ case "relinked":
8505
+ await config.relinked?.(documentFor(delivery));
8506
+ return;
8567
8507
  case "removed":
8568
8508
  await config.removed(delivery.documentId);
8569
8509
  return;
@@ -8618,6 +8558,7 @@ function defineReader(source, config) {
8618
8558
  v: 1,
8619
8559
  source: source.name,
8620
8560
  outcomes,
8561
+ hooks: { relinked: typeof config.relinked === "function" },
8621
8562
  startedAt,
8622
8563
  durationMs: Date.now() - startedAt
8623
8564
  };