@madewithremy/admin 0.1.29 → 0.1.31

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/prod.js CHANGED
@@ -36,10 +36,10 @@ function fatal(message) {
36
36
  throw new CliError(message);
37
37
  }
38
38
  var AdminApiError = class extends Error {
39
- constructor(method, path7, status2, body) {
40
- super(`API ${method} ${path7} returned ${status2}: ${JSON.stringify(body)}`);
39
+ constructor(method, path8, status2, body) {
40
+ super(`API ${method} ${path8} returned ${status2}: ${JSON.stringify(body)}`);
41
41
  this.method = method;
42
- this.path = path7;
42
+ this.path = path8;
43
43
  this.status = status2;
44
44
  this.body = body;
45
45
  this.name = "AdminApiError";
@@ -1036,6 +1036,13 @@ function summarizeRelease(release) {
1036
1036
  publishedAt: release.publishedAt ?? null,
1037
1037
  ...release.previewUrl ? { previewUrl: release.previewUrl } : {}
1038
1038
  };
1039
+ const readers = (release.dataSources ?? []).filter((d) => d.reader);
1040
+ if (readers.length > 0 && release.status === "live") {
1041
+ summary2.readers = readers.map((d) => ({
1042
+ slug: d.slug,
1043
+ unread: d.readerUnread ?? 0
1044
+ }));
1045
+ }
1039
1046
  if (release.status !== "failed") {
1040
1047
  return summary2;
1041
1048
  }
@@ -3092,8 +3099,8 @@ Notes:
3092
3099
  - Public image URLs accept transform params, e.g. ?w=400&fit=cover.`;
3093
3100
 
3094
3101
  // src/commands/dataSources.ts
3095
- import fs5 from "fs";
3096
- import path5 from "path";
3102
+ import fs6 from "fs";
3103
+ import path6 from "path";
3097
3104
 
3098
3105
  // src/ops/dataSources.ts
3099
3106
  import { createHash as createHash2 } from "crypto";
@@ -3111,7 +3118,7 @@ var summarize = (d) => ({
3111
3118
  ...d.errorMessage ? { error: d.errorMessage } : {}
3112
3119
  });
3113
3120
  async function addDocument(ctx, params) {
3114
- const { slug, filename, content, metadata, onProgress } = params;
3121
+ const { slug, filename, content, metadata, entities: entities2, onProgress } = params;
3115
3122
  const contentHash = createHash2("sha256").update(content).digest("hex");
3116
3123
  const token = await call(
3117
3124
  ctx,
@@ -3121,7 +3128,8 @@ async function addDocument(ctx, params) {
3121
3128
  slug,
3122
3129
  filename,
3123
3130
  contentHash,
3124
- ...metadata ? { metadata } : {}
3131
+ ...metadata ? { metadata } : {},
3132
+ ...entities2 ? { entities: entities2 } : {}
3125
3133
  }
3126
3134
  );
3127
3135
  if (token.alreadyCurrent) {
@@ -3147,7 +3155,8 @@ async function addDocument(ctx, params) {
3147
3155
  contentHash,
3148
3156
  contentType: token.contentType,
3149
3157
  size: content.length,
3150
- ...metadata ? { metadata } : {}
3158
+ ...metadata ? { metadata } : {},
3159
+ ...entities2 ? { entities: entities2 } : {}
3151
3160
  }
3152
3161
  );
3153
3162
  return {
@@ -3324,6 +3333,20 @@ function count(ctx, params) {
3324
3333
  ...candidate ? { candidate: true } : {}
3325
3334
  });
3326
3335
  }
3336
+ function entities(ctx, params) {
3337
+ const { slug, filter, limit, candidate } = params;
3338
+ return call(
3339
+ ctx,
3340
+ "POST",
3341
+ `${base(ctx.appId)}/entities`,
3342
+ {
3343
+ slug,
3344
+ ...filter ? { filter } : {},
3345
+ ...limit !== void 0 ? { limit } : {},
3346
+ ...candidate ? { candidate: true } : {}
3347
+ }
3348
+ );
3349
+ }
3327
3350
  function configGet(ctx, slug) {
3328
3351
  return call(
3329
3352
  ctx,
@@ -3485,6 +3508,67 @@ function deleteSource(ctx, slug) {
3485
3508
  { slug }
3486
3509
  );
3487
3510
  }
3511
+ var DICTIONARY_PAGE_ENTRIES = 1e4;
3512
+ var DICTIONARY_PAGE_BYTES = 4e6;
3513
+ async function pushDictionary(ctx, params) {
3514
+ const { slug, entries } = params;
3515
+ const stage = await call(
3516
+ ctx,
3517
+ "POST",
3518
+ `${base(ctx.appId)}/dictionary/stage`,
3519
+ {
3520
+ slug,
3521
+ ...params.linkerVersion !== void 0 ? { linkerVersion: params.linkerVersion } : {}
3522
+ }
3523
+ );
3524
+ const perPage = Math.min(
3525
+ DICTIONARY_PAGE_ENTRIES,
3526
+ stage.pageMaxEntries || DICTIONARY_PAGE_ENTRIES
3527
+ );
3528
+ let pages2 = 0;
3529
+ let start2 = 0;
3530
+ while (start2 < entries.length) {
3531
+ let end = start2;
3532
+ let bytes = 0;
3533
+ while (end < entries.length && end - start2 < perPage) {
3534
+ const size = JSON.stringify(entries[end]).length;
3535
+ if (end > start2 && bytes + size > DICTIONARY_PAGE_BYTES) {
3536
+ break;
3537
+ }
3538
+ bytes += size;
3539
+ end += 1;
3540
+ }
3541
+ await call(ctx, "POST", `${base(ctx.appId)}/dictionary/page`, {
3542
+ slug,
3543
+ stageId: stage.stageId,
3544
+ page: pages2,
3545
+ entries: entries.slice(start2, end)
3546
+ });
3547
+ pages2 += 1;
3548
+ start2 = end;
3549
+ params.onProgress?.(start2, entries.length);
3550
+ }
3551
+ return call(
3552
+ ctx,
3553
+ "POST",
3554
+ `${base(ctx.appId)}/dictionary/commit`,
3555
+ { slug, stageId: stage.stageId, pages: pages2 }
3556
+ );
3557
+ }
3558
+ function dictionary(ctx, params) {
3559
+ return call(
3560
+ ctx,
3561
+ "POST",
3562
+ `${base(ctx.appId)}/dictionary`,
3563
+ { slug: params.slug }
3564
+ );
3565
+ }
3566
+ function link(ctx, params) {
3567
+ return call(ctx, "POST", `${base(ctx.appId)}/link`, {
3568
+ slug: params.slug,
3569
+ text: params.text
3570
+ });
3571
+ }
3488
3572
 
3489
3573
  // src/ops/dataSourceJobs.ts
3490
3574
  var DEFAULT_WAIT_TIMEOUT_MS2 = 60 * 60 * 1e3;
@@ -3643,6 +3727,39 @@ function describeProgress(job, start2) {
3643
3727
  // src/commands/_shared/dataSources.ts
3644
3728
  var DEFAULT_SOURCE = "default";
3645
3729
  var sourceOf = (a) => a.str("source") || DEFAULT_SOURCE;
3730
+ function parseKeyValuePairs(raw, flag) {
3731
+ if (raw === void 0) {
3732
+ return void 0;
3733
+ }
3734
+ const pairs2 = {};
3735
+ for (const part of raw.split(",")) {
3736
+ const trimmed = part.trim();
3737
+ if (!trimmed) {
3738
+ continue;
3739
+ }
3740
+ const eq = trimmed.indexOf("=");
3741
+ if (eq <= 0) {
3742
+ fatal(`--${flag} entries must be key=value (got "${trimmed}").`);
3743
+ }
3744
+ const key = trimmed.slice(0, eq).trim();
3745
+ const value = trimmed.slice(eq + 1).trim();
3746
+ pairs2[key] = value === "true" ? true : value === "false" ? false : value !== "" && !Number.isNaN(Number(value)) ? Number(value) : value;
3747
+ }
3748
+ if (Object.keys(pairs2).length === 0) {
3749
+ fatal(`--${flag} needs at least one key=value pair.`);
3750
+ }
3751
+ return pairs2;
3752
+ }
3753
+ function documentSelectorOf(raw) {
3754
+ if (raw.trimStart().startsWith("{")) {
3755
+ try {
3756
+ return JSON.parse(raw);
3757
+ } catch (err) {
3758
+ fatal(`--filter is not valid JSON: ${err.message}`);
3759
+ }
3760
+ }
3761
+ return { metadata: parseKeyValuePairs(raw, "filter") };
3762
+ }
3646
3763
  var timeoutOf = (a) => {
3647
3764
  const seconds = a.str("timeout");
3648
3765
  return seconds ? { timeoutMs: Number(seconds) * 1e3 } : {};
@@ -4658,9 +4775,116 @@ Retrieval evals:
4658
4775
  with --candidate, compare, promote or drop.
4659
4776
  `;
4660
4777
 
4661
- // src/commands/dataSourceJobs.ts
4778
+ // src/commands/dataSourceDictionary.ts
4662
4779
  import fs4 from "fs";
4663
4780
  import path4 from "path";
4781
+ var dataSourceDictionarySpecs = {
4782
+ "datasources dictionary push": {
4783
+ usage: "Usage: remy-admin datasources dictionary push [--source <slug>] [--linker-version <n>] <file.jsonl>",
4784
+ positionals: [{ name: "file", required: true }],
4785
+ flags: {
4786
+ source: { type: "string" },
4787
+ "linker-version": { type: "number", min: 1 }
4788
+ }
4789
+ },
4790
+ "datasources dictionary status": {
4791
+ usage: "Usage: remy-admin datasources dictionary status [--source <slug>]",
4792
+ flags: {
4793
+ source: { type: "string" }
4794
+ }
4795
+ },
4796
+ "datasources link": {
4797
+ usage: "Usage: remy-admin datasources link [--source <slug>] <text>",
4798
+ positionals: [{ name: "text", required: true }],
4799
+ flags: {
4800
+ source: { type: "string" }
4801
+ }
4802
+ }
4803
+ };
4804
+ function readEntries(file) {
4805
+ const abs = path4.isAbsolute(file) ? file : path4.join(WORKSPACE_DIR, file);
4806
+ let content;
4807
+ try {
4808
+ content = fs4.readFileSync(abs, "utf8");
4809
+ } catch (err) {
4810
+ fatal(`Could not read "${file}": ${err.message}`);
4811
+ }
4812
+ const entries = [];
4813
+ content.split("\n").forEach((line, i) => {
4814
+ const trimmed = line.trim();
4815
+ if (!trimmed) {
4816
+ return;
4817
+ }
4818
+ let parsed;
4819
+ try {
4820
+ parsed = JSON.parse(trimmed);
4821
+ } catch {
4822
+ fatal(`${file}:${i + 1} is not valid JSON.`);
4823
+ }
4824
+ if (typeof parsed?.id !== "string" || !Array.isArray(parsed?.names) || !parsed.names.every((name) => typeof name === "string")) {
4825
+ fatal(`${file}:${i + 1} needs { "id": "...", "names": ["...", ...] }.`);
4826
+ }
4827
+ entries.push({ id: parsed.id, names: parsed.names });
4828
+ });
4829
+ if (entries.length === 0) {
4830
+ fatal(`"${file}" has no entries.`);
4831
+ }
4832
+ return entries;
4833
+ }
4834
+ async function dictionaryPush(ctx, a) {
4835
+ const entries = readEntries(a.req("file"));
4836
+ const linkerVersion = a.num("linker-version");
4837
+ out(
4838
+ await pushDictionary(ctx, {
4839
+ slug: sourceOf(a),
4840
+ entries,
4841
+ ...linkerVersion !== void 0 ? { linkerVersion } : {},
4842
+ onProgress: (sent, total) => progress(`pushing ${sent.toLocaleString()}/${total.toLocaleString()}\u2026`)
4843
+ })
4844
+ );
4845
+ }
4846
+ async function dictionaryStatus(ctx, a) {
4847
+ out(await dictionary(ctx, { slug: sourceOf(a) }));
4848
+ }
4849
+ async function link2(ctx, a) {
4850
+ out(await link(ctx, { slug: sourceOf(a), text: a.req("text") }));
4851
+ }
4852
+ var dataSourceDictionaryHandlers = {
4853
+ "datasources dictionary push": dictionaryPush,
4854
+ "datasources dictionary status": dictionaryStatus,
4855
+ "datasources link": link2
4856
+ };
4857
+ var dataSourceDictionaryHelp = `
4858
+ Dictionary:
4859
+ remy-admin datasources dictionary push [--source <slug>] [--linker-version <n>] <file.jsonl>
4860
+ remy-admin datasources dictionary status [--source <slug>]
4861
+ remy-admin datasources link [--source <slug>] <text>
4862
+
4863
+ A dictionary is your own entity ids and the names each goes by, a
4864
+ projection of your tables: one JSONL line per entry,
4865
+ {"id": "P-101A", "names": ["P-101A", "Main Feed Pump"]}. Push it whole; it
4866
+ replaces the source's dictionary. From then on every passage carries the
4867
+ ids whose names its text mentions, beside the ones its document asserts,
4868
+ so --entities on search and count, and \`datasources entities\`, find a tag
4869
+ wherever it's written. Passages indexed before are re-tagged in the
4870
+ background (a relink; \`dictionary status\` shows it), with nothing
4871
+ re-embedded. An unchanged dictionary commits nothing.
4872
+
4873
+ Matching ignores case and a tag's separators (P-101A, P101A, p 101a), takes
4874
+ whole words only (P-101AB is not P-101A), and prefers the longest name; a
4875
+ name two ids claim tags both. A name needs three letters or digits (two in
4876
+ Chinese, Japanese or Korean). The push reports ambiguous names, the most
4877
+ claimed, and names that can't match their own text as written
4878
+ (\`unmatchable\`). Up to 250,000 entries, 16 names each, a million names.
4879
+
4880
+ \`link\` shows what the current dictionary finds in any text: check one
4881
+ there before relying on it. --linker-version moves a dictionary to a newer
4882
+ matching rule; without it a push keeps the one its dictionary has.
4883
+ `;
4884
+
4885
+ // src/commands/dataSourceJobs.ts
4886
+ import fs5 from "fs";
4887
+ import path5 from "path";
4664
4888
  var dataSourceJobsSpecs = {
4665
4889
  "datasources jobs start": {
4666
4890
  usage: "Usage: remy-admin datasources jobs start [--source <slug>] (--store <name> [--access <private|public>] [--prefix <p>] | --manifest <file.jsonl>) [--limit <n>] [--budget <dollars>] [--concurrency <n>] [--priority] [--approve] [--wait] [--timeout <sec>]",
@@ -4738,10 +4962,10 @@ async function jobsStart(ctx, a) {
4738
4962
  }
4739
4963
  let source;
4740
4964
  if (manifest) {
4741
- const abs = path4.isAbsolute(manifest) ? manifest : path4.join(WORKSPACE_DIR, manifest);
4965
+ const abs = path5.isAbsolute(manifest) ? manifest : path5.join(WORKSPACE_DIR, manifest);
4742
4966
  let content;
4743
4967
  try {
4744
- content = fs4.readFileSync(abs);
4968
+ content = fs5.readFileSync(abs);
4745
4969
  } catch (err) {
4746
4970
  fatal(`Could not read manifest "${manifest}": ${err.message}`);
4747
4971
  }
@@ -4867,12 +5091,13 @@ Bulk ingestion (jobs):
4867
5091
  A job loads a whole corpus at once. It reads either every object under a
4868
5092
  prefix of one of the app's file stores (\`files put\` fills one; an app can
4869
5093
  write one at runtime) or a manifest you upload: JSONL, one line per document,
4870
- {"url": "https://\u2026", "filename": "x.pdf", "externalId": "\u2026", "metadata": {\u2026}}.
4871
- filename, externalId and metadata are optional; the URL must be public.
5094
+ {"url": "https://\u2026", "filename": "x.pdf", "externalId": "\u2026", "metadata": {\u2026},
5095
+ "entities": ["\u2026"]}. Everything but the URL is optional; it must be public.
4872
5096
 
4873
- Nothing is spent until you say so. The platform counts the source, reads a
4874
- sample of about a hundred documents, and writes a plan: documents, chunks,
4875
- cost per stage (embedding, extraction, contextual) at today's rates, storage,
5097
+ Nothing past the sample runs until you say so. The platform counts the
5098
+ source, reads a sample of about a hundred documents, and writes a plan:
5099
+ documents, chunks, cost per stage (embedding, extraction, contextual, and
5100
+ the mapper's own model calls, priced from the sample's) at today's rates, storage,
4876
5101
  whether it fits where the source lives, and a duration. \`jobs status\` shows
4877
5102
  it; \`jobs approve\` starts the run. Two gates: the corpus has to fit the
4878
5103
  source's placement (a shared-pool source over the per-source cap answers
@@ -4982,6 +5207,7 @@ function quarantine(ctx, params) {
4982
5207
  "GET",
4983
5208
  `${base5(ctx.appId)}/jobs/${params.id}/quarantine${qs({
4984
5209
  ...params.kind ? { kind: params.kind } : {},
5210
+ ...params.stage ? { stage: params.stage } : {},
4985
5211
  ...params.reason ? { reason: params.reason } : {},
4986
5212
  ...params.cursor ? { cursor: params.cursor } : {},
4987
5213
  ...params.limit !== void 0 ? { limit: params.limit } : {}
@@ -5050,10 +5276,11 @@ var dataSourceMappersSpecs = {
5050
5276
  }
5051
5277
  },
5052
5278
  "datasources jobs quarantine": {
5053
- usage: "Usage: remy-admin datasources jobs quarantine <id> [--kind skip|error] [--reason <text>] [--cursor <id>] [--limit <n>]",
5279
+ usage: "Usage: remy-admin datasources jobs quarantine <id> [--kind skip|error] [--stage map|read] [--reason <text>] [--cursor <id>] [--limit <n>]",
5054
5280
  positionals: [{ name: "id", required: true }],
5055
5281
  flags: {
5056
5282
  kind: { type: "string" },
5283
+ stage: { type: "string" },
5057
5284
  reason: { type: "string" },
5058
5285
  cursor: { type: "string" },
5059
5286
  limit: { type: "number", min: 1 }
@@ -5206,18 +5433,32 @@ async function quarantine2(ctx, a) {
5206
5433
  const id = a.req("id");
5207
5434
  const limit = a.num("limit");
5208
5435
  const kind = kindOf(a);
5436
+ const stage = a.str("stage");
5437
+ if (stage !== void 0 && stage !== "map" && stage !== "read") {
5438
+ fatal(`--stage must be "map" or "read" (got "${stage}").`);
5439
+ }
5209
5440
  const reason = a.str("reason");
5210
5441
  const cursor = a.str("cursor");
5211
5442
  const result = await quarantine(ctx, {
5212
5443
  id,
5213
5444
  ...kind ? { kind } : {},
5445
+ ...stage ? { stage } : {},
5214
5446
  ...reason ? { reason } : {},
5215
5447
  ...cursor ? { cursor } : {},
5216
5448
  ...limit !== void 0 ? { limit } : {}
5217
5449
  });
5450
+ const { skipped, errors, readErrors } = result.counts;
5451
+ const notes = [
5452
+ ...skipped + errors > 0 ? [
5453
+ `Mapping: fix the mapper, push, \`datasources map deploy\`, then \`datasources jobs replay ${id}\` to run these through it again.`
5454
+ ] : [],
5455
+ ...readErrors > 0 ? [
5456
+ `Reading: fix the reader and publish, then \`datasources reader retry --source ${result.dataSource.slug} --job ${id}\` reads these again.`
5457
+ ] : []
5458
+ ];
5218
5459
  out({
5219
5460
  ...result,
5220
- note: result.counts.skipped + result.counts.errors === 0 ? "Nothing quarantined: the mapper produced documents (or deletions) for every object." : `Fix the mapper, push, \`datasources map deploy\`, then \`datasources jobs replay ${id}\` to run these through it again.`
5461
+ note: notes.length === 0 ? "Nothing quarantined: the mapper produced documents (or deletions) for every object, and the reader read every document." : notes.join(" ")
5221
5462
  });
5222
5463
  }
5223
5464
  async function replay2(ctx, a) {
@@ -5258,7 +5499,7 @@ Mapping raw objects into documents (mappers):
5258
5499
  remy-admin datasources map test [--source <slug>] (--store <name> [--prefix <p>] | --connector) [--keys k1,k2] [--limit <n>] [--dev] [--full]
5259
5500
  remy-admin datasources map deploy [--source <slug>] [--release <id>]
5260
5501
  remy-admin datasources remap [--source <slug>] [--budget <dollars>] [--limit <n>] [--wait] [--timeout <sec>]
5261
- remy-admin datasources jobs quarantine <id> [--kind skip|error] [--reason <text>] [--cursor <id>] [--limit <n>]
5502
+ remy-admin datasources jobs quarantine <id> [--kind skip|error] [--stage map|read] [--reason <text>] [--cursor <id>] [--limit <n>]
5262
5503
  remy-admin datasources jobs replay <id> [--kind skip|error] [--wait] [--timeout <sec>]
5263
5504
 
5264
5505
  A source takes every object it is given as one document. When the file is
@@ -5299,6 +5540,228 @@ Mapping raw objects into documents (mappers):
5299
5540
  pauses with pauseReason 'skips' for a look at the quarantine.
5300
5541
  `;
5301
5542
 
5543
+ // src/ops/dataSourceReaders.ts
5544
+ function base6(appId) {
5545
+ return `/_internal/v2/apps/${appId}/datasources`;
5546
+ }
5547
+ function readerTest(ctx, params) {
5548
+ return call(
5549
+ ctx,
5550
+ "POST",
5551
+ `${base6(ctx.appId)}/reader/test`,
5552
+ {
5553
+ slug: params.slug,
5554
+ ...params.documents ? { documents: params.documents } : {},
5555
+ ...params.limit !== void 0 ? { limit: params.limit } : {}
5556
+ }
5557
+ );
5558
+ }
5559
+ function readerStatus(ctx, params) {
5560
+ return call(
5561
+ ctx,
5562
+ "POST",
5563
+ `${base6(ctx.appId)}/reader/status`,
5564
+ { slug: params.slug }
5565
+ );
5566
+ }
5567
+ function readerRetry(ctx, params) {
5568
+ return call(
5569
+ ctx,
5570
+ "POST",
5571
+ `${base6(ctx.appId)}/reader/retry`,
5572
+ {
5573
+ slug: params.slug,
5574
+ ...params.jobId ? { jobId: params.jobId } : {}
5575
+ }
5576
+ );
5577
+ }
5578
+ function reread(ctx, params) {
5579
+ return call(
5580
+ ctx,
5581
+ "POST",
5582
+ `${base6(ctx.appId)}/reread`,
5583
+ {
5584
+ slug: params.slug,
5585
+ ...params.unread ? { unread: true } : {},
5586
+ ...params.filter ? { filter: params.filter } : {},
5587
+ ...params.budgetDollars !== void 0 ? { budgetDollars: params.budgetDollars } : {},
5588
+ ...params.limit !== void 0 ? { limit: params.limit } : {}
5589
+ }
5590
+ );
5591
+ }
5592
+
5593
+ // src/commands/dataSourceReaders.ts
5594
+ var dataSourceReadersSpecs = {
5595
+ "datasources reader test": {
5596
+ usage: "Usage: remy-admin datasources reader test --dev [--source <slug>] [--documents id1,id2,...] [--limit <n>]",
5597
+ flags: {
5598
+ source: { type: "string" },
5599
+ dev: { type: "boolean" },
5600
+ documents: { type: "string" },
5601
+ limit: { type: "number", min: 1 }
5602
+ }
5603
+ },
5604
+ "datasources reader status": {
5605
+ usage: "Usage: remy-admin datasources reader status [--source <slug>]",
5606
+ flags: {
5607
+ source: { type: "string" }
5608
+ }
5609
+ },
5610
+ "datasources reader retry": {
5611
+ usage: "Usage: remy-admin datasources reader retry [--source <slug>] [--job <id>]",
5612
+ flags: {
5613
+ source: { type: "string" },
5614
+ job: { type: "string" }
5615
+ }
5616
+ },
5617
+ "datasources reread": {
5618
+ usage: "Usage: remy-admin datasources reread [--source <slug>] [--unread] [--filter <k=v,...|json>] [--budget <dollars>] [--limit <n>] [--wait] [--timeout <sec>]",
5619
+ flags: {
5620
+ source: { type: "string" },
5621
+ unread: { type: "boolean" },
5622
+ filter: { type: "string" },
5623
+ budget: { type: "number", min: 0.01 },
5624
+ limit: { type: "number", min: 1 },
5625
+ wait: { type: "boolean" },
5626
+ timeout: { type: "string" }
5627
+ }
5628
+ }
5629
+ };
5630
+ async function readerTest2(ctx, a) {
5631
+ if (!a.bool("dev")) {
5632
+ fatal(
5633
+ "reader test runs the dev session's reader only: pass --dev. The live reader reads through deliveries and `datasources reread`."
5634
+ );
5635
+ }
5636
+ const slug = sourceOf(a);
5637
+ const raw = a.str("documents");
5638
+ const documents2 = raw?.split(",").map((id) => id.trim()).filter(Boolean);
5639
+ if (raw !== void 0 && documents2?.length === 0) {
5640
+ fatal("--documents needs at least one document id.");
5641
+ }
5642
+ const limit = a.num("limit");
5643
+ const result = await readerTest(ctx, {
5644
+ slug,
5645
+ ...documents2 ? { documents: documents2 } : {},
5646
+ ...limit !== void 0 ? { limit } : {}
5647
+ });
5648
+ out({
5649
+ dataSource: slug,
5650
+ ...result,
5651
+ note: "Ran the LOCAL reader through the dev session, writing into the dev session's database. Query its tables to check what it wrote; publish to make it the live reader, then `datasources reread --unread` reads what it has not."
5652
+ });
5653
+ if (result.failed > 0) {
5654
+ throw new CliError(
5655
+ `The reader failed on ${result.failed} of ${result.documents.length} document(s).`,
5656
+ EXIT.buildFailed
5657
+ );
5658
+ }
5659
+ }
5660
+ async function readerStatus2(ctx, a) {
5661
+ const slug = sourceOf(a);
5662
+ const result = await readerStatus(ctx, { slug });
5663
+ const { reader, deliveries, documents: documents2 } = result;
5664
+ out({
5665
+ dataSource: slug,
5666
+ ...result,
5667
+ note: !reader ? `The live release declares no reader for "${slug}". Declare one in mindstudio.json's dataSources and publish.` : deliveries.parked > 0 ? `${deliveries.parked} delivery(ies) failed six times and parked. Fix the reader and publish, then \`datasources reader retry\`.` : documents2.unread > 0 ? `${documents2.unread.toLocaleString()} live document(s) not read yet. \`datasources reread --unread\` plans reading them.` : "Every live document has been read."
5668
+ });
5669
+ }
5670
+ async function readerRetry2(ctx, a) {
5671
+ const slug = sourceOf(a);
5672
+ const jobId = a.str("job");
5673
+ const result = await readerRetry(ctx, {
5674
+ slug,
5675
+ ...jobId ? { jobId } : {}
5676
+ });
5677
+ out({
5678
+ dataSource: slug,
5679
+ ...jobId ? { job: jobId } : {},
5680
+ ...result,
5681
+ note: result.retried === 0 ? jobId ? `Job ${jobId}'s reader failed on nothing.` : "Nothing parked." : `Queued ${result.retried.toLocaleString()} delivery(ies) again; \`datasources reader status\` shows them drain.`
5682
+ });
5683
+ }
5684
+ async function reread2(ctx, a) {
5685
+ const slug = sourceOf(a);
5686
+ const raw = a.str("filter");
5687
+ const budget = a.num("budget");
5688
+ const limit = a.num("limit");
5689
+ const { job } = await reread(ctx, {
5690
+ slug,
5691
+ ...a.bool("unread") ? { unread: true } : {},
5692
+ ...raw !== void 0 ? { filter: documentSelectorOf(raw) } : {},
5693
+ ...budget !== void 0 ? { budgetDollars: budget } : {},
5694
+ ...limit !== void 0 ? { limit } : {}
5695
+ });
5696
+ if (!a.bool("wait")) {
5697
+ out({
5698
+ dataSource: slug,
5699
+ job,
5700
+ note: `Planning: the reread prices reading from the reader's history, or reads a first slice for real to measure it. \`datasources jobs status ${job.id}\` shows the plan; \`datasources jobs approve ${job.id}\` runs it.`
5701
+ });
5702
+ return;
5703
+ }
5704
+ await waitAndReport(ctx, a, job.id, "plan", {
5705
+ dataSource: slug,
5706
+ action: "reread"
5707
+ });
5708
+ }
5709
+ var dataSourceReadersHandlers = {
5710
+ "datasources reader test": readerTest2,
5711
+ "datasources reader status": readerStatus2,
5712
+ "datasources reader retry": readerRetry2,
5713
+ "datasources reread": reread2
5714
+ };
5715
+ var dataSourceReadersHelp = `
5716
+ Reading documents into the app (readers):
5717
+ remy-admin datasources reader test --dev [--source <slug>] [--documents id1,id2] [--limit <n>]
5718
+ remy-admin datasources reader status [--source <slug>]
5719
+ remy-admin datasources reader retry [--source <slug>] [--job <id>]
5720
+ remy-admin datasources reread [--source <slug>] [--unread] [--filter <k=v|json>] [--budget <dollars>] [--limit <n>] [--wait] [--timeout <sec>]
5721
+
5722
+ A READER is app code the platform runs over every document a source
5723
+ builds, writing what it finds into the app's own tables: the extraction an
5724
+ app would otherwise run from a cron over allDocuments(), with the
5725
+ platform's batching, retries, budgets and pauses. Declare it in
5726
+ mindstudio.json:
5727
+ "dataSources": [{ "slug": "archive", "reader": { "path": "dist/datasources/archive.reader.ts" } }]
5728
+ The file exports defineReader(Archive, { read, removed, cleared? }) from
5729
+ @mindstudio-ai/agent. read(document) gets the document (id, sourceId,
5730
+ filename, metadata, entities) and document.chunks(), its passages as
5731
+ indexed with each one's entities; removed(documentId) is called when a
5732
+ document leaves the source; cleared(sourceId) once when the source is
5733
+ deleted. Throwing fails that document, which is retried. A document can be
5734
+ read more than once, so read() replaces the document's rows as a set,
5735
+ keyed by its id, and removed() deletes them. Options on the declaration:
5736
+ timeoutMs (per document, default 60000, max 300000) and concurrency
5737
+ (frames in flight for the source, default 16, max 64).
5738
+
5739
+ The reader always runs against the LIVE release, its code and its
5740
+ database: nothing reads until the app publishes one. From then on every
5741
+ document whose build lands on the active version is read, every removed
5742
+ document delivered as removed, whether it came through add(), a sync or a
5743
+ job. A job over a reader source reads as its last stage: the job stays
5744
+ running until its documents are read, \`jobs status\` says "reading N of M",
5745
+ a document the reader fails on lands in the job's quarantine (stage read),
5746
+ and a reader failing on far more documents than it did at first pauses the
5747
+ job with pauseReason 'reader'.
5748
+
5749
+ The loop: \`reader test --dev\` runs the LOCAL reader through the dev
5750
+ session over real documents, writing into the dev session's database, and
5751
+ prints each outcome; publish, and \`releases wait\` reports each reader's
5752
+ unread documents; \`reread --unread\` reads them. \`reread\` is a job that
5753
+ plans first: it prices reading from what the live reader has done (after a
5754
+ hundred documents), or reads a first slice for real and measures spend,
5755
+ failures and the live database's growth, then waits for \`jobs approve\`.
5756
+ Without --unread it reads every built document again (a changed reader).
5757
+ \`reader status\` shows the reader, its deliveries (pending, in flight,
5758
+ retrying, parked, recent errors), unread documents, the measured cost per
5759
+ document and the live database's size. Deliveries outside jobs retry after
5760
+ 1, 5, 15, 60 and 240 minutes, then park; \`reader retry\` sends parked ones
5761
+ again, and \`reader retry --job <id>\` the documents a job's reader failed
5762
+ on.
5763
+ `;
5764
+
5302
5765
  // src/commands/dataSources.ts
5303
5766
  var INGEST_FLAGS = {
5304
5767
  chunking: { type: "string" },
@@ -5329,11 +5792,12 @@ var CONFIG_FLAGS = {
5329
5792
  };
5330
5793
  var dataSourcesSpecs = {
5331
5794
  "datasources add": {
5332
- usage: "Usage: remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--wait] [--timeout <sec>] <file...>",
5795
+ usage: "Usage: remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--entities <id,...>] [--wait] [--timeout <sec>] <file...>",
5333
5796
  positionals: [{ name: "file", required: true, variadic: true }],
5334
5797
  flags: {
5335
5798
  source: { type: "string" },
5336
5799
  metadata: { type: "string" },
5800
+ entities: { type: "string" },
5337
5801
  wait: { type: "boolean" },
5338
5802
  timeout: { type: "string" }
5339
5803
  }
@@ -5362,7 +5826,7 @@ var dataSourcesSpecs = {
5362
5826
  }
5363
5827
  },
5364
5828
  "datasources search": {
5365
- usage: "Usage: remy-admin datasources search [--source <slug>] [--top-k <n>] [--mode <hybrid|semantic|lexical>] [--filter <k=v,...|json>] [--phrase <text>] [--contains <words>] [--max-per-document <n>] [--rerank <true|false>] [--hybrid <true|false>] [--highlight] [--candidate] <query>",
5829
+ usage: "Usage: remy-admin datasources search [--source <slug>] [--top-k <n>] [--mode <hybrid|semantic|lexical>] [--filter <k=v,...|json>] [--entities <id,...>] [--phrase <text>] [--contains <words>] [--max-per-document <n>] [--rerank <true|false>] [--hybrid <true|false>] [--highlight] [--candidate] <query>",
5366
5830
  positionals: [{ name: "query", required: true }],
5367
5831
  flags: {
5368
5832
  source: { type: "string" },
@@ -5372,6 +5836,7 @@ var dataSourcesSpecs = {
5372
5836
  hybrid: { type: "string" },
5373
5837
  mode: { type: "string" },
5374
5838
  filter: { type: "string" },
5839
+ entities: { type: "string" },
5375
5840
  phrase: { type: "string" },
5376
5841
  contains: { type: "string" },
5377
5842
  "max-per-document": { type: "number", min: 1 },
@@ -5398,12 +5863,25 @@ var dataSourcesSpecs = {
5398
5863
  }
5399
5864
  },
5400
5865
  "datasources count": {
5401
- usage: "Usage: remy-admin datasources count [--source <slug>] [--filter <k=v,...|json>] [--contains <words>] [--phrase <text>] [--candidate]",
5866
+ usage: "Usage: remy-admin datasources count [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--contains <words>] [--phrase <text>] [--candidate]",
5867
+ flags: {
5868
+ source: { type: "string" },
5869
+ filter: { type: "string" },
5870
+ entities: { type: "string" },
5871
+ contains: { type: "string" },
5872
+ phrase: { type: "string" },
5873
+ candidate: { type: "boolean" }
5874
+ }
5875
+ },
5876
+ "datasources entities": {
5877
+ usage: "Usage: remy-admin datasources entities [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--contains <words>] [--phrase <text>] [--limit <n>] [--candidate]",
5402
5878
  flags: {
5403
5879
  source: { type: "string" },
5404
5880
  filter: { type: "string" },
5881
+ entities: { type: "string" },
5405
5882
  contains: { type: "string" },
5406
5883
  phrase: { type: "string" },
5884
+ limit: { type: "number", min: 1 },
5407
5885
  candidate: { type: "boolean" }
5408
5886
  }
5409
5887
  },
@@ -5449,22 +5927,24 @@ async function dataSourcesAdd(ctx, a) {
5449
5927
  const files = a.rest("file");
5450
5928
  const slug = sourceOf(a);
5451
5929
  const metadata = parseKeyValuePairs(a.str("metadata"), "metadata");
5930
+ const entities2 = parseIdList(a.str("entities"));
5452
5931
  const results = [];
5453
5932
  for (const file of files) {
5454
- const abs = path5.isAbsolute(file) ? file : path5.join(WORKSPACE_DIR, file);
5933
+ const abs = path6.isAbsolute(file) ? file : path6.join(WORKSPACE_DIR, file);
5455
5934
  let bytes;
5456
5935
  try {
5457
- bytes = fs5.readFileSync(abs);
5936
+ bytes = fs6.readFileSync(abs);
5458
5937
  } catch (err) {
5459
5938
  fatal(`Could not read file "${file}": ${err.message}`);
5460
5939
  }
5461
- const filename = path5.basename(file);
5940
+ const filename = path6.basename(file);
5462
5941
  results.push(
5463
5942
  await addDocument(ctx, {
5464
5943
  slug,
5465
5944
  filename,
5466
5945
  content: bytes,
5467
5946
  ...metadata ? { metadata } : {},
5947
+ ...entities2 ? { entities: entities2 } : {},
5468
5948
  onProgress: progress
5469
5949
  })
5470
5950
  );
@@ -5535,6 +6015,7 @@ async function dataSourcesStatus(ctx, a) {
5535
6015
  activeVersion: source.activeVersion,
5536
6016
  candidate: source.candidate,
5537
6017
  job: source.job,
6018
+ relink: source.relink,
5538
6019
  failures: (failures?.documents ?? []).map(summarize),
5539
6020
  moreFailures: !!failures?.nextCursor
5540
6021
  });
@@ -5550,16 +6031,7 @@ async function dataSourcesRm(ctx, a) {
5550
6031
  out({ deleted: documentId });
5551
6032
  return;
5552
6033
  }
5553
- let filter;
5554
- if (rawFilter.trimStart().startsWith("{")) {
5555
- try {
5556
- filter = JSON.parse(rawFilter);
5557
- } catch (err) {
5558
- fatal(`--filter is not valid JSON: ${err.message}`);
5559
- }
5560
- } else {
5561
- filter = { metadata: parseKeyValuePairs(rawFilter, "filter") };
5562
- }
6034
+ const filter = documentSelectorOf(rawFilter);
5563
6035
  const slug = sourceOf(a);
5564
6036
  const result = await rmWhereAll(ctx, {
5565
6037
  slug,
@@ -5614,6 +6086,24 @@ async function dataSourcesCount(ctx, a) {
5614
6086
  })
5615
6087
  );
5616
6088
  }
6089
+ async function dataSourcesEntities(ctx, a) {
6090
+ const filter = filterFromFlags(a);
6091
+ const limit = a.num("limit");
6092
+ out(
6093
+ await entities(ctx, {
6094
+ slug: sourceOf(a),
6095
+ ...filter ? { filter } : {},
6096
+ ...limit !== void 0 ? { limit } : {},
6097
+ ...a.bool("candidate") ? { candidate: true } : {}
6098
+ })
6099
+ );
6100
+ }
6101
+ function parseIdList(raw) {
6102
+ if (raw === void 0) {
6103
+ return void 0;
6104
+ }
6105
+ return raw.split(",").map((id) => id.trim()).filter((id) => id.length > 0);
6106
+ }
5617
6107
  function filterFromFlags(a) {
5618
6108
  let filter = {};
5619
6109
  const raw = a.str("filter");
@@ -5631,6 +6121,10 @@ function filterFromFlags(a) {
5631
6121
  }
5632
6122
  }
5633
6123
  }
6124
+ const entities2 = parseIdList(a.str("entities"));
6125
+ if (entities2?.length) {
6126
+ filter.entities = entities2;
6127
+ }
5634
6128
  const phrase = a.str("phrase");
5635
6129
  if (phrase) {
5636
6130
  filter.phrase = phrase;
@@ -5641,29 +6135,6 @@ function filterFromFlags(a) {
5641
6135
  }
5642
6136
  return Object.keys(filter).length ? filter : void 0;
5643
6137
  }
5644
- function parseKeyValuePairs(raw, flag) {
5645
- if (raw === void 0) {
5646
- return void 0;
5647
- }
5648
- const pairs2 = {};
5649
- for (const part of raw.split(",")) {
5650
- const trimmed = part.trim();
5651
- if (!trimmed) {
5652
- continue;
5653
- }
5654
- const eq = trimmed.indexOf("=");
5655
- if (eq <= 0) {
5656
- fatal(`--${flag} entries must be key=value (got "${trimmed}").`);
5657
- }
5658
- const key = trimmed.slice(0, eq).trim();
5659
- const value = trimmed.slice(eq + 1).trim();
5660
- pairs2[key] = value === "true" ? true : value === "false" ? false : value !== "" && !Number.isNaN(Number(value)) ? Number(value) : value;
5661
- }
5662
- if (Object.keys(pairs2).length === 0) {
5663
- fatal(`--${flag} needs at least one key=value pair.`);
5664
- }
5665
- return pairs2;
5666
- }
5667
6138
  function triState2(a, flag, key) {
5668
6139
  const raw = a.str(flag);
5669
6140
  if (raw === void 0) {
@@ -5890,6 +6361,7 @@ var dataSourcesHandlers = {
5890
6361
  "datasources create": dataSourcesCreate,
5891
6362
  "datasources config": dataSourcesConfig,
5892
6363
  "datasources count": dataSourcesCount,
6364
+ "datasources entities": dataSourcesEntities,
5893
6365
  "datasources move": dataSourcesMove,
5894
6366
  "datasources revectorize": dataSourcesRevectorize,
5895
6367
  "datasources promote": dataSourcesPromote,
@@ -5929,12 +6401,13 @@ Subcommands:
5929
6401
  delete Delete a whole data source \u2014 documents, vectors and versions
5930
6402
 
5931
6403
  Usage:
5932
- remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--wait] [--timeout <sec>] <file...>
6404
+ remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--entities <id,...>] [--wait] [--timeout <sec>] <file...>
5933
6405
  remy-admin datasources list
5934
6406
  remy-admin datasources status [--source <slug>] [--status error] [--all]
5935
6407
  remy-admin datasources rm [--source <slug>] (--document <id> | --filter <k=v,...|json>)
5936
6408
  remy-admin datasources search [--source <slug>] [search options] <query>
5937
- remy-admin datasources count [--source <slug>] [--filter <k=v,...|json>] [--contains <words>] [--phrase <text>] [--candidate]
6409
+ remy-admin datasources count [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--contains <words>] [--phrase <text>] [--candidate]
6410
+ remy-admin datasources entities [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--limit <n>] [--candidate]
5938
6411
  remy-admin datasources create --source <slug> [--name <name>] [--placement <resource-id|shared>] [rebuild settings...]
5939
6412
  remy-admin datasources config [--source <slug>] [--placement <resource-id|shared>] [settings...]
5940
6413
  remy-admin datasources move [--source <slug>] --to <resource-id|shared> [--chains <n>] [--restart] [--wait] [--timeout <sec>]
@@ -5955,6 +6428,15 @@ Count:
5955
6428
  count (none exists), and not the set search returns. The SDK's
5956
6429
  \`Source.count(filter)\` is the same call for an app.
5957
6430
 
6431
+ Entities:
6432
+ A document can assert what it is about as your own ids (\`add --entities\`,
6433
+ a mapper's \`entities\`, a manifest line's \`entities\`), and every passage
6434
+ carries them. \`--entities a,b\` on search and count narrows to passages
6435
+ about any of them. \`entities\` lists which entities the matching passages
6436
+ are about, most first, with how many passages carry each (approximate, and
6437
+ passages, not documents): filter on one entity to see what appears beside
6438
+ it. The SDK's \`Source.entities({ filter, limit })\` is the same call.
6439
+
5958
6440
  Search options:
5959
6441
  --top-k <n> Results to return (default 5, max 50)
5960
6442
  --mode <m> hybrid (default) | semantic | lexical. Lexical is
@@ -5963,11 +6445,12 @@ Search options:
5963
6445
  --filter <k=v,...> Match document metadata set at add time. Values
5964
6446
  coerce: true/false and numbers become typed.
5965
6447
  Pass a JSON object for the full grammar:
5966
- metadata, filename, documentIds, pages, contains,
5967
- phrase \u2014 e.g. --filter '{"pages":{"max":3}}'.
6448
+ metadata, filename, documentIds, entities, pages,
6449
+ contains, phrase \u2014 e.g. --filter '{"pages":{"max":3}}'.
5968
6450
  Metadata takes numeric ranges via {gte,lte}: store
5969
6451
  dates as integers (YYYYMMDD or epoch seconds), then
5970
6452
  --filter '{"metadata":{"date":{"gte":20250101}}}'
6453
+ --entities <id,...> Chunk must be about any of these entities
5971
6454
  --phrase <text> Chunk must contain this exact word sequence
5972
6455
  --contains <words> Chunk must contain ALL these words, any order
5973
6456
  --max-per-document <n> Cap hits per document; backfills from others
@@ -6056,6 +6539,8 @@ Notes:
6056
6539
  Re-adding an unchanged file is free: no upload, no re-embedding. Re-adding
6057
6540
  with a different --metadata updates the tags in place, also free.
6058
6541
  --metadata tags documents for search-time filtering (scalars, \u226416 keys).
6542
+ --entities names what a document is about, as your own ids (\u2264256, no spaces
6543
+ or commas); an empty --entities clears them. Also free on a re-add.
6059
6544
  rm --filter removes every matching document in pages of a thousand: k=v
6060
6545
  pairs match metadata; a JSON object takes the full selector \u2014 metadata,
6061
6546
  filename, documentIds, externalIdPrefix (the key a job or connector
@@ -6095,7 +6580,7 @@ Examples:
6095
6580
  remy-admin datasources revectorize --source archive-sample --max-chars 900 --wait
6096
6581
  remy-admin datasources eval run --set <setId> --candidate --label small-chunks --wait
6097
6582
  remy-admin datasources eval compare <runA> <runB>
6098
- ${dataSourceJobsHelp}${dataSourceConnectorsHelp}${dataSourceMappersHelp}${dataSourceEvalsHelp}`;
6583
+ ${dataSourceJobsHelp}${dataSourceConnectorsHelp}${dataSourceMappersHelp}${dataSourceReadersHelp}${dataSourceEvalsHelp}${dataSourceDictionaryHelp}`;
6099
6584
 
6100
6585
  // src/ops/voice.ts
6101
6586
  function normalizeE164(input) {
@@ -6455,6 +6940,86 @@ Notes:
6455
6940
  'remy-admin requests get <requestId>' for that call's input, output, stdout
6456
6941
  and error. Client tools run in the browser, so they have no requestId.`;
6457
6942
 
6943
+ // src/ops/tasks.ts
6944
+ function list10(ctx, params = {}) {
6945
+ return call(
6946
+ ctx,
6947
+ "GET",
6948
+ `/_internal/v2/apps/${ctx.appId}/task-traces${qs(params)}`
6949
+ );
6950
+ }
6951
+ function get9(ctx, traceId) {
6952
+ return call(
6953
+ ctx,
6954
+ "GET",
6955
+ `/_internal/v2/apps/${ctx.appId}/task-traces/${seg(traceId)}`
6956
+ );
6957
+ }
6958
+
6959
+ // src/commands/tasks.ts
6960
+ var tasksSpecs = {
6961
+ "tasks list": {
6962
+ usage: "Usage: remy-admin tasks list [--limit 20] [--cursor <nextCursor>]",
6963
+ flags: {
6964
+ limit: { type: "number", min: 1, max: 100 },
6965
+ cursor: { type: "string" }
6966
+ }
6967
+ },
6968
+ "tasks get": {
6969
+ usage: "Usage: remy-admin tasks get <traceId>",
6970
+ positionals: [{ name: "traceId", required: true }]
6971
+ }
6972
+ };
6973
+ async function list11(ctx, a) {
6974
+ out(
6975
+ await list10(ctx, {
6976
+ limit: a.num("limit"),
6977
+ cursor: a.str("cursor")
6978
+ })
6979
+ );
6980
+ }
6981
+ async function get10(ctx, a) {
6982
+ out(await get9(ctx, a.req("traceId")));
6983
+ }
6984
+ var tasksHandlers = {
6985
+ "tasks list": list11,
6986
+ "tasks get": get10
6987
+ };
6988
+ var tasksHelp = `remy-admin tasks \u2014 Log of the app's task agents (runTask), kept 90 days.
6989
+
6990
+ Subcommands:
6991
+ list Every task run, newest started first (transcripts excluded)
6992
+ get One run with its full transcript
6993
+
6994
+ Usage:
6995
+ remy-admin tasks list [--limit 20] [--cursor <nextCursor>]
6996
+ remy-admin tasks get <traceId>
6997
+
6998
+ Notes:
6999
+ Transcripts are how you iterate on a task agent: after a run, read 'get' for
7000
+ what the model was given, what it decided each turn, and which tools ran with
7001
+ which arguments, then fix the prompt and tool descriptions from that
7002
+ evidence rather than from guesses. The id is 'result.traceId' from runTask(),
7003
+ and the list's 'traceId'. An unknown id is 'task_trace_not_found' (404).
7004
+
7005
+ In the list, 'outcome' says how the run ended: 'answered' (the last turn
7006
+ replied), 'in_progress', 'stopped' (it asked for tools and nothing followed),
7007
+ or 'error' (a model call failed). 'toolErrorCount' and 'hasTurnError' point at
7008
+ the runs worth opening, 'requestId' is the method execution that started the
7009
+ task, and 'devSession' is true for your own runs through the dev tunnel.
7010
+ 'billingCost' is in nanodollars.
7011
+
7012
+ In a transcript, the first message is the task's input; 'assistant' messages
7013
+ are the model's turns (with 'turn', 'stopReason' and any 'thinking');
7014
+ 'user' messages carrying 'toolCallId' are tool results. A method tool's
7015
+ result also carries a 'requestId' \u2014 pass it to
7016
+ 'remy-admin requests get <requestId>' for that call's input, output, stdout
7017
+ and error.
7018
+
7019
+ Examples:
7020
+ remy-admin tasks list --limit 10
7021
+ remy-admin tasks get 0b9e\u2026`;
7022
+
6458
7023
  // src/ops/events.ts
6459
7024
  function channelsList(ctx, params = {}) {
6460
7025
  return call(
@@ -7396,14 +7961,14 @@ async function resolveDomainId(ctx, direction, domain) {
7396
7961
  }
7397
7962
  return match.id;
7398
7963
  }
7399
- function list10(ctx, params = {}) {
7964
+ function list12(ctx, params = {}) {
7400
7965
  return call(
7401
7966
  ctx,
7402
7967
  "GET",
7403
7968
  `/_internal/v2/apps/${ctx.appId}/outbound-email/messages${qs(params)}`
7404
7969
  );
7405
7970
  }
7406
- function get9(ctx, messageId) {
7971
+ function get11(ctx, messageId) {
7407
7972
  return call(
7408
7973
  ctx,
7409
7974
  "GET",
@@ -7595,7 +8160,7 @@ var emailSpecs = {
7595
8160
  };
7596
8161
  async function emailList(ctx, a) {
7597
8162
  out(
7598
- await list10(ctx, {
8163
+ await list12(ctx, {
7599
8164
  status: a.str("status"),
7600
8165
  kind: a.str("kind"),
7601
8166
  recipient: a.str("recipient"),
@@ -7609,7 +8174,7 @@ async function emailList(ctx, a) {
7609
8174
  );
7610
8175
  }
7611
8176
  async function emailGet(ctx, a) {
7612
- out(await get9(ctx, a.req("messageId")));
8177
+ out(await get11(ctx, a.req("messageId")));
7613
8178
  }
7614
8179
  async function emailStats(ctx, a) {
7615
8180
  out(await stats2(ctx, { start: a.str("start"), end: a.str("end") }));
@@ -7759,10 +8324,12 @@ Notes:
7759
8324
  method run, errors) with 'requests get <id>'.
7760
8325
 
7761
8326
  Custom domains \u2014 two independent directions:
7762
- 'domains' govern the From address (SES identity). 'add' returns
7763
- dnsInstructions: three DKIM CNAME records (required) and a
7764
- recommended SPF TXT \u2014 hand these to the user to create at
7765
- their DNS host, then 'verify <domain>' to re-check now.
8327
+ 'domains' govern the From address. 'add' returns
8328
+ dnsInstructions.records: the DNS records to publish, each
8329
+ marked required or not (on the cloud, three DKIM CNAMEs
8330
+ and a recommended SPF TXT) \u2014 hand these to the user to
8331
+ create at their DNS host, then 'verify <domain>' to
8332
+ re-check now.
7766
8333
  'list' also returns effectiveSender: what the app currently
7767
8334
  sends as, and which tier that address comes from.
7768
8335
  'inbound-domains' receive mail at the user's own domain via one MX record
@@ -7771,7 +8338,7 @@ Custom domains \u2014 two independent directions:
7771
8338
  with no setup; a custom inbound domain is optional polish.
7772
8339
 
7773
8340
  uiStatus per domain: pending (records not confirmed yet \u2014 sending verification
7774
- can take minutes after the CNAMEs resolve), verified, or action_needed (check
8341
+ can take minutes after the records resolve), verified, or action_needed (check
7775
8342
  verificationErrors; sending verification expires unstarted after ~72h \u2014 fix
7776
8343
  the records and 'verify' again).
7777
8344
 
@@ -7785,7 +8352,7 @@ Examples:
7785
8352
  `;
7786
8353
 
7787
8354
  // src/ops/cron.ts
7788
- function list11(ctx, params = {}) {
8355
+ function list13(ctx, params = {}) {
7789
8356
  return call(
7790
8357
  ctx,
7791
8358
  "GET",
@@ -7814,7 +8381,7 @@ var cronSpecs = {
7814
8381
  }
7815
8382
  };
7816
8383
  async function cronList(ctx, a) {
7817
- out(await list11(ctx, { runs: a.num("runs") }));
8384
+ out(await list13(ctx, { runs: a.num("runs") }));
7818
8385
  }
7819
8386
  async function cronRun(ctx, a) {
7820
8387
  out(await run2(ctx, a.req("route")));
@@ -7850,21 +8417,21 @@ Examples:
7850
8417
  // src/ops/infra.ts
7851
8418
  var DEFAULT_WAIT_TIMEOUT_MS4 = 10 * 60 * 1e3;
7852
8419
  var POLL_MS5 = 3e3;
7853
- function base6(appId) {
8420
+ function base7(appId) {
7854
8421
  return `/_internal/v2/apps/${appId}/infra`;
7855
8422
  }
7856
- function list12(ctx, params) {
8423
+ function list14(ctx, params) {
7857
8424
  return call(
7858
8425
  ctx,
7859
8426
  "GET",
7860
- `${base6(ctx.appId)}${params?.includeDestroyed ? "?includeDestroyed=true" : ""}`
8427
+ `${base7(ctx.appId)}${params?.includeDestroyed ? "?includeDestroyed=true" : ""}`
7861
8428
  );
7862
8429
  }
7863
- function get10(ctx, params) {
8430
+ function get12(ctx, params) {
7864
8431
  return call(
7865
8432
  ctx,
7866
8433
  "GET",
7867
- `${base6(ctx.appId)}/${encodeURIComponent(params.id)}`
8434
+ `${base7(ctx.appId)}/${encodeURIComponent(params.id)}`
7868
8435
  );
7869
8436
  }
7870
8437
  function logs(ctx, params) {
@@ -7879,11 +8446,11 @@ function logs(ctx, params) {
7879
8446
  return call(
7880
8447
  ctx,
7881
8448
  "GET",
7882
- `${base6(ctx.appId)}/${encodeURIComponent(params.id)}/logs${search2 ? `?${search2}` : ""}`
8449
+ `${base7(ctx.appId)}/${encodeURIComponent(params.id)}/logs${search2 ? `?${search2}` : ""}`
7883
8450
  );
7884
8451
  }
7885
8452
  function provision(ctx, params) {
7886
- return call(ctx, "POST", base6(ctx.appId), {
8453
+ return call(ctx, "POST", base7(ctx.appId), {
7887
8454
  offeringId: params.offeringId,
7888
8455
  ...params.name ? { name: params.name } : {}
7889
8456
  });
@@ -7892,28 +8459,28 @@ function hibernate(ctx, params) {
7892
8459
  return call(
7893
8460
  ctx,
7894
8461
  "POST",
7895
- `${base6(ctx.appId)}/${encodeURIComponent(params.id)}/hibernate`
8462
+ `${base7(ctx.appId)}/${encodeURIComponent(params.id)}/hibernate`
7896
8463
  );
7897
8464
  }
7898
8465
  function resume2(ctx, params) {
7899
8466
  return call(
7900
8467
  ctx,
7901
8468
  "POST",
7902
- `${base6(ctx.appId)}/${encodeURIComponent(params.id)}/resume`
8469
+ `${base7(ctx.appId)}/${encodeURIComponent(params.id)}/resume`
7903
8470
  );
7904
8471
  }
7905
8472
  function destroy(ctx, params) {
7906
8473
  return call(
7907
8474
  ctx,
7908
8475
  "POST",
7909
- `${base6(ctx.appId)}/${encodeURIComponent(params.id)}/destroy`
8476
+ `${base7(ctx.appId)}/${encodeURIComponent(params.id)}/destroy`
7910
8477
  );
7911
8478
  }
7912
8479
  function resize(ctx, params) {
7913
8480
  return call(
7914
8481
  ctx,
7915
8482
  "POST",
7916
- `${base6(ctx.appId)}/${encodeURIComponent(params.id)}/resize`,
8483
+ `${base7(ctx.appId)}/${encodeURIComponent(params.id)}/resize`,
7917
8484
  { offeringId: params.offeringId }
7918
8485
  );
7919
8486
  }
@@ -7921,7 +8488,7 @@ function rename(ctx, params) {
7921
8488
  return call(
7922
8489
  ctx,
7923
8490
  "POST",
7924
- `${base6(ctx.appId)}/${encodeURIComponent(params.id)}/rename`,
8491
+ `${base7(ctx.appId)}/${encodeURIComponent(params.id)}/rename`,
7925
8492
  { name: params.name }
7926
8493
  );
7927
8494
  }
@@ -7930,7 +8497,7 @@ async function waitForPhase(ctx, params) {
7930
8497
  const wanted = new Set(params.phases);
7931
8498
  const arrived = (resource2) => wanted.has(resource2.phase) && (params.until?.(resource2) ?? true);
7932
8499
  const outcome = await pollUntil(
7933
- async () => (await get10(ctx, { id: params.id })).resource,
8500
+ async () => (await get12(ctx, { id: params.id })).resource,
7934
8501
  (resource2) => arrived(resource2) || resource2.phase === "failed",
7935
8502
  {
7936
8503
  timeoutMs,
@@ -8045,10 +8612,10 @@ async function waitAndReport2(ctx, a, id, phases, summary2, until) {
8045
8612
  }
8046
8613
  }
8047
8614
  async function infraList(ctx, a) {
8048
- out(await list12(ctx, { includeDestroyed: a.bool("include-destroyed") }));
8615
+ out(await list14(ctx, { includeDestroyed: a.bool("include-destroyed") }));
8049
8616
  }
8050
8617
  async function infraGet(ctx, a) {
8051
- out(await get10(ctx, { id: a.req("id") }));
8618
+ out(await get12(ctx, { id: a.req("id") }));
8052
8619
  }
8053
8620
  var FOLLOW_POLL_MS = 4e3;
8054
8621
  async function infraLogs(ctx, a) {
@@ -8490,9 +9057,12 @@ var SPECS = {
8490
9057
  ...dataSourceJobsSpecs,
8491
9058
  ...dataSourceConnectorsSpecs,
8492
9059
  ...dataSourceEvalsSpecs,
9060
+ ...dataSourceDictionarySpecs,
8493
9061
  ...dataSourceMappersSpecs,
9062
+ ...dataSourceReadersSpecs,
8494
9063
  ...voiceSpecs,
8495
9064
  ...agentSpecs,
9065
+ ...tasksSpecs,
8496
9066
  ...eventsSpecs,
8497
9067
  ...jewelsSpecs,
8498
9068
  ...settingsSpecs,
@@ -8520,9 +9090,12 @@ var HANDLERS = {
8520
9090
  ...dataSourceJobsHandlers,
8521
9091
  ...dataSourceConnectorsHandlers,
8522
9092
  ...dataSourceEvalsHandlers,
9093
+ ...dataSourceDictionaryHandlers,
8523
9094
  ...dataSourceMappersHandlers,
9095
+ ...dataSourceReadersHandlers,
8524
9096
  ...voiceHandlers,
8525
9097
  ...agentHandlers,
9098
+ ...tasksHandlers,
8526
9099
  ...eventsHandlers,
8527
9100
  ...jewelsHandlers,
8528
9101
  ...settingsHandlers,
@@ -8549,6 +9122,7 @@ var GROUP_HELP = {
8549
9122
  datasources: dataSourcesHelp,
8550
9123
  voice: voiceHelp,
8551
9124
  agent: agentHelp,
9125
+ tasks: tasksHelp,
8552
9126
  events: eventsHelp,
8553
9127
  jewels: jewelsHelp,
8554
9128
  settings: settingsHelp,
@@ -8559,8 +9133,8 @@ var GROUP_HELP = {
8559
9133
  };
8560
9134
 
8561
9135
  // src/ctx.ts
8562
- import fs6 from "fs";
8563
- import path6 from "path";
9136
+ import fs7 from "fs";
9137
+ import path7 from "path";
8564
9138
 
8565
9139
  // src/parseJsonConfig.ts
8566
9140
  import JSON5 from "json5";
@@ -8589,10 +9163,10 @@ function parseJsonConfig(raw) {
8589
9163
  // src/ctx.ts
8590
9164
  var DEFAULT_BASE_URL = "https://api.mindstudio.ai";
8591
9165
  function loadWorkspaceAppId(workspaceDir = WORKSPACE_DIR) {
8592
- const manifestPath = path6.join(workspaceDir, "mindstudio.json");
9166
+ const manifestPath = path7.join(workspaceDir, "mindstudio.json");
8593
9167
  let raw;
8594
9168
  try {
8595
- raw = fs6.readFileSync(manifestPath, "utf-8");
9169
+ raw = fs7.readFileSync(manifestPath, "utf-8");
8596
9170
  } catch (err) {
8597
9171
  if (err.code === "ENOENT") {
8598
9172
  throw new Error(`mindstudio.json not found at ${manifestPath}`);
@@ -8632,6 +9206,7 @@ Commands:
8632
9206
  datasources Build and query searchable document corpora (RAG)
8633
9207
  voice Phone numbers, voice call log + transcripts, voice policy settings
8634
9208
  agent Agent (chat) conversation log + transcripts
9209
+ tasks Task agent (runTask) log + transcripts
8635
9210
  events Realtime events \u2014 live tail, test publish, channel activity
8636
9211
  jewels Monitor jewel shadowing; review + approve the proposal queue
8637
9212
  settings App settings \u2014 signup allowlist, test accounts, embed origins, toggles
@@ -8647,7 +9222,7 @@ with a dash, use --name=value; to pass a positional beginning with a dash, put
8647
9222
  -- before it.
8648
9223
 
8649
9224
  All output is JSON. Configuration is read from environment variables
8650
- (MINDSTUDIO_API_KEY, API_BASE_URL) and mindstudio.json (appId).`;
9225
+ (MINDSTUDIO_API_KEY, MINDSTUDIO_BASE_URL) and mindstudio.json (appId).`;
8651
9226
  var GROUPS = [
8652
9227
  "requests",
8653
9228
  "crashes",
@@ -8666,6 +9241,7 @@ var GROUPS = [
8666
9241
  "datasources",
8667
9242
  "voice",
8668
9243
  "agent",
9244
+ "tasks",
8669
9245
  "events",
8670
9246
  "jewels",
8671
9247
  "settings",
@@ -8763,7 +9339,7 @@ async function main() {
8763
9339
  apiKey,
8764
9340
  appId: appIdOverride ?? loadWorkspaceAppId(),
8765
9341
  originAppId,
8766
- baseUrl: process.env["API_BASE_URL"] || DEFAULT_BASE_URL
9342
+ baseUrl: process.env["MINDSTUDIO_BASE_URL"] || DEFAULT_BASE_URL
8767
9343
  };
8768
9344
  return HANDLERS[route.key](ctx, a);
8769
9345
  }