@mindstudio-ai/agent 0.1.117 → 0.1.119
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/cli.js +144 -13
- package/dist/index.d.ts +455 -16
- package/dist/index.js +271 -5
- package/dist/index.js.map +1 -1
- package/dist/postinstall.js +144 -13
- package/llms.txt +10 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2840,6 +2840,7 @@ function createFiles(call) {
|
|
|
2840
2840
|
|
|
2841
2841
|
// src/datasources/source.ts
|
|
2842
2842
|
import { createHash as createHash2 } from "crypto";
|
|
2843
|
+
var REMOVE_WHERE_MAX_PAGES = 100;
|
|
2843
2844
|
var DataSource = class {
|
|
2844
2845
|
constructor(_slug, _call) {
|
|
2845
2846
|
this._slug = _slug;
|
|
@@ -2864,6 +2865,15 @@ var DataSource = class {
|
|
|
2864
2865
|
* Alongside `results` and `latencyMs` comes {@link SearchRan} — what the
|
|
2865
2866
|
* search actually did, which is the first thing to check when results don't
|
|
2866
2867
|
* look like the options you passed.
|
|
2868
|
+
*
|
|
2869
|
+
* **Throws `index_warming`** (HTTP 503) when a large corpus's index is being
|
|
2870
|
+
* reloaded after an eviction: the platform keeps a working set of indexes
|
|
2871
|
+
* resident and reloads the rest on first use, which for a big source takes a
|
|
2872
|
+
* minute or two in the background. Treat it as "loading", never as "empty" —
|
|
2873
|
+
* tell the user the knowledge base is warming up and retry shortly. Small
|
|
2874
|
+
* corpora reload inside the search and never raise it. Sources on dedicated
|
|
2875
|
+
* capacity are never evicted, so they never raise it either; they raise
|
|
2876
|
+
* `capacity_hibernated` and friends instead when their capacity is parked.
|
|
2867
2877
|
*/
|
|
2868
2878
|
async search(query, options) {
|
|
2869
2879
|
const { results, mode, latencyMs } = await this._call("search", {
|
|
@@ -2878,10 +2888,29 @@ var DataSource = class {
|
|
|
2878
2888
|
...options?.rerank !== void 0 ? { rerank: options.rerank } : {},
|
|
2879
2889
|
...options?.hybrid !== void 0 ? { hybrid: options.hybrid } : {},
|
|
2880
2890
|
...options?.explain !== void 0 ? { explain: options.explain } : {},
|
|
2881
|
-
...options?.expand !== void 0 ? { expand: options.expand } : {}
|
|
2891
|
+
...options?.expand !== void 0 ? { expand: options.expand } : {},
|
|
2892
|
+
...options?.shareCitations !== void 0 ? { shareCitations: options.shareCitations } : {}
|
|
2882
2893
|
});
|
|
2883
2894
|
return { results: results ?? [], mode, latencyMs: latencyMs ?? 0 };
|
|
2884
2895
|
}
|
|
2896
|
+
/**
|
|
2897
|
+
* Mint an ABSOLUTE, signed link to one document that works with **no**
|
|
2898
|
+
* session — the same verb as `store.shareUrl()` for a file. Takes a
|
|
2899
|
+
* document id or anything carrying one (`hit.citation`). Expires (default
|
|
2900
|
+
* 24h; clamped to 60s–90d); each click still redirects to a fresh
|
|
2901
|
+
* short-lived storage URL, so the expiry you pass is the only one that
|
|
2902
|
+
* matters. For every citation of a search at once, pass
|
|
2903
|
+
* {@link SearchOptions.shareCitations} instead.
|
|
2904
|
+
*/
|
|
2905
|
+
async shareUrl(document, options) {
|
|
2906
|
+
const documentId = typeof document === "string" ? document : document.documentId;
|
|
2907
|
+
const res = await this._call("share", {
|
|
2908
|
+
slug: this._slug,
|
|
2909
|
+
documentId,
|
|
2910
|
+
...options?.expiresIn ? { expiresIn: options.expiresIn } : {}
|
|
2911
|
+
});
|
|
2912
|
+
return res.url;
|
|
2913
|
+
}
|
|
2885
2914
|
/**
|
|
2886
2915
|
* What is in the corpus, and how it was built.
|
|
2887
2916
|
*
|
|
@@ -2923,6 +2952,19 @@ var DataSource = class {
|
|
|
2923
2952
|
* bytes under its current configuration. Reconfiguring the source is what
|
|
2924
2953
|
* makes a re-add do work again — and that is an explicit, owner-triggered
|
|
2925
2954
|
* migration, never something a deploy causes.
|
|
2955
|
+
*
|
|
2956
|
+
* **On a source with a mapper** (`dataSources[].mapper` in mindstudio.json,
|
|
2957
|
+
* see `defineMapper`) the bytes are a raw object and the mapper decides
|
|
2958
|
+
* what they become: `documents` holds everything it produced (one object
|
|
2959
|
+
* can become many documents), `document` is the first of them for
|
|
2960
|
+
* convenience, and `outcome` says which way the mapper went. A
|
|
2961
|
+
* `passthrough` is one document from the bytes themselves; a `deletes`
|
|
2962
|
+
* outcome removes the named documents and returns none. A document whose
|
|
2963
|
+
* `externalId` (or `replaces`) names an existing one supersedes it as soon
|
|
2964
|
+
* as the new one is registered, before its build lands; a search in that
|
|
2965
|
+
* window sees neither. **Throws `mapper_skipped`** (HTTP 422) when the
|
|
2966
|
+
* mapper skipped the object — a specific thing it refused, which the caller
|
|
2967
|
+
* should see — and `mapper_error` when the mapper threw.
|
|
2926
2968
|
*/
|
|
2927
2969
|
async add(content, options) {
|
|
2928
2970
|
const bytes = typeof content === "string" ? Buffer.from(content) : Buffer.from(content);
|
|
@@ -2934,15 +2976,104 @@ var DataSource = class {
|
|
|
2934
2976
|
body: bytes.toString("base64")
|
|
2935
2977
|
});
|
|
2936
2978
|
}
|
|
2937
|
-
/**
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2979
|
+
/**
|
|
2980
|
+
* Documents in the corpus, with ingest status.
|
|
2981
|
+
*
|
|
2982
|
+
* Without options this is the first thousand, newest first: enough to show
|
|
2983
|
+
* a small corpus, not a way to walk a large one (a corpus of any size is
|
|
2984
|
+
* loaded and inspected from the CLI). To follow what {@link add} just
|
|
2985
|
+
* queued, pass the ids it returned: `documents({ ids })` answers only those,
|
|
2986
|
+
* at most two hundred per call.
|
|
2987
|
+
*/
|
|
2988
|
+
async documents(options) {
|
|
2989
|
+
const { documents: documents2 } = await this._call("documents", {
|
|
2990
|
+
slug: this._slug,
|
|
2991
|
+
...options?.ids?.length ? { ids: options.ids } : {}
|
|
2992
|
+
});
|
|
2993
|
+
return documents2 ?? [];
|
|
2941
2994
|
}
|
|
2942
2995
|
/** Remove a document and its vectors. */
|
|
2943
2996
|
async remove(documentId) {
|
|
2944
2997
|
await this._call("remove", { slug: this._slug, documentId });
|
|
2945
2998
|
}
|
|
2999
|
+
/**
|
|
3000
|
+
* Remove every document matching a selector — by metadata tags, filename,
|
|
3001
|
+
* ids, or the external id prefix a bulk load or connector recorded.
|
|
3002
|
+
*
|
|
3003
|
+
* Works in pages of a thousand and keeps going until nothing matches (or a
|
|
3004
|
+
* very large removal has run for a hundred pages, in which case `remaining`
|
|
3005
|
+
* says how many are left and a second call continues). Vectors, the catalog
|
|
3006
|
+
* row and the stored bytes all go; the removal is immediate and shared
|
|
3007
|
+
* across dev and prod like every data-source write.
|
|
3008
|
+
*
|
|
3009
|
+
* ```ts
|
|
3010
|
+
* await Policies.removeWhere({ metadata: { department: 'legal', year: 2019 } });
|
|
3011
|
+
* await Policies.removeWhere({ externalIdPrefix: 'archive/2019/' });
|
|
3012
|
+
* ```
|
|
3013
|
+
*/
|
|
3014
|
+
async removeWhere(selector) {
|
|
3015
|
+
let deleted = 0;
|
|
3016
|
+
let remaining = 0;
|
|
3017
|
+
for (let page = 0; page < REMOVE_WHERE_MAX_PAGES; page++) {
|
|
3018
|
+
const result = await this._call("remove-many", {
|
|
3019
|
+
slug: this._slug,
|
|
3020
|
+
filter: selector
|
|
3021
|
+
});
|
|
3022
|
+
deleted += result?.deleted ?? 0;
|
|
3023
|
+
remaining = result?.remaining ?? 0;
|
|
3024
|
+
if (remaining === 0 || !(result?.deleted > 0)) {
|
|
3025
|
+
break;
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
3028
|
+
return { deleted, remaining };
|
|
3029
|
+
}
|
|
3030
|
+
/**
|
|
3031
|
+
* Bring the source up to date with the S3 bucket it is connected to.
|
|
3032
|
+
*
|
|
3033
|
+
* Lists the bucket, diffs it against what was ingested before (by ETag), and
|
|
3034
|
+
* runs an ingestion job over what is new or changed; keys that disappeared
|
|
3035
|
+
* upstream are removed at the end when the connector's deletion policy says
|
|
3036
|
+
* so. Returns as soon as the job is started — it plans, then runs in the
|
|
3037
|
+
* background. Poll {@link job} for progress, or read the latest sync from
|
|
3038
|
+
* {@link jobs}.
|
|
3039
|
+
*
|
|
3040
|
+
* The job auto-approves under the connector's per-sync budget. A plan over
|
|
3041
|
+
* that budget, or over the capacity of where the source lives, waits in
|
|
3042
|
+
* `planned` for a human (`remy-admin datasources jobs approve <id>`) — so the
|
|
3043
|
+
* first backfill of a large bucket stops for approval by itself, and the
|
|
3044
|
+
* steady-state deltas run unattended. Nothing is spent when nothing changed.
|
|
3045
|
+
*
|
|
3046
|
+
* The connection itself — which bucket, which app secrets hold the keys — is
|
|
3047
|
+
* created by the app's owner with `remy-admin datasources connect`, never
|
|
3048
|
+
* from code. A nightly sync is an ordinary cron interface job whose method
|
|
3049
|
+
* calls this:
|
|
3050
|
+
*
|
|
3051
|
+
* ```ts
|
|
3052
|
+
* // methods/sync-archive.ts, scheduled "0 3 * * *" in the cron interface
|
|
3053
|
+
* export default async function () {
|
|
3054
|
+
* await Archive.sync();
|
|
3055
|
+
* }
|
|
3056
|
+
* ```
|
|
3057
|
+
*
|
|
3058
|
+
* @throws `connector_not_found` when the source is not connected;
|
|
3059
|
+
* `data_source_busy` while another job or a move is in flight.
|
|
3060
|
+
*/
|
|
3061
|
+
async sync(options) {
|
|
3062
|
+
return this._call("sync", {
|
|
3063
|
+
slug: this._slug,
|
|
3064
|
+
...options?.budgetDollars !== void 0 ? { budgetDollars: options.budgetDollars } : {}
|
|
3065
|
+
});
|
|
3066
|
+
}
|
|
3067
|
+
/** Recent ingestion jobs on this source (syncs and CLI loads), newest first. */
|
|
3068
|
+
async jobs() {
|
|
3069
|
+
const { jobs } = await this._call("jobs", { slug: this._slug });
|
|
3070
|
+
return jobs ?? [];
|
|
3071
|
+
}
|
|
3072
|
+
/** One job by id — progress, plan, failures, and why it paused if it did. */
|
|
3073
|
+
async job(id) {
|
|
3074
|
+
const { job } = await this._call("job", { slug: this._slug, id });
|
|
3075
|
+
return job;
|
|
3076
|
+
}
|
|
2946
3077
|
/**
|
|
2947
3078
|
* Create the data source if it doesn't exist yet.
|
|
2948
3079
|
*
|
|
@@ -7757,6 +7888,136 @@ function defineJewel(method, config) {
|
|
|
7757
7888
|
});
|
|
7758
7889
|
}
|
|
7759
7890
|
|
|
7891
|
+
// src/mapper/index.ts
|
|
7892
|
+
function documents(list) {
|
|
7893
|
+
return { kind: "documents", documents: Array.isArray(list) ? list : [list] };
|
|
7894
|
+
}
|
|
7895
|
+
function passthrough(options) {
|
|
7896
|
+
return options?.metadata !== void 0 ? { kind: "passthrough", metadata: options.metadata } : { kind: "passthrough" };
|
|
7897
|
+
}
|
|
7898
|
+
function skip(reason) {
|
|
7899
|
+
return { kind: "skip", reason };
|
|
7900
|
+
}
|
|
7901
|
+
function deletes(externalIds) {
|
|
7902
|
+
return {
|
|
7903
|
+
kind: "deletes",
|
|
7904
|
+
externalIds: Array.isArray(externalIds) ? externalIds : [externalIds]
|
|
7905
|
+
};
|
|
7906
|
+
}
|
|
7907
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
7908
|
+
var DEFAULT_CONCURRENCY = 4;
|
|
7909
|
+
function errorInfo2(e) {
|
|
7910
|
+
if (e instanceof Error) return { message: e.message, stack: e.stack };
|
|
7911
|
+
return { message: String(e) };
|
|
7912
|
+
}
|
|
7913
|
+
function objectFor(input) {
|
|
7914
|
+
let cached = null;
|
|
7915
|
+
const bytes = () => {
|
|
7916
|
+
if (!cached) {
|
|
7917
|
+
cached = (async () => {
|
|
7918
|
+
const response = await fetch(input.url);
|
|
7919
|
+
if (!response.ok) {
|
|
7920
|
+
throw new Error(
|
|
7921
|
+
`Could not read ${input.key}: HTTP ${response.status}`
|
|
7922
|
+
);
|
|
7923
|
+
}
|
|
7924
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
7925
|
+
})();
|
|
7926
|
+
}
|
|
7927
|
+
return cached;
|
|
7928
|
+
};
|
|
7929
|
+
return {
|
|
7930
|
+
key: input.key,
|
|
7931
|
+
size: input.size,
|
|
7932
|
+
contentType: input.contentType,
|
|
7933
|
+
etag: input.etag,
|
|
7934
|
+
lastModified: input.lastModified,
|
|
7935
|
+
metadata: input.metadata,
|
|
7936
|
+
bytes,
|
|
7937
|
+
text: async () => new TextDecoder("utf-8").decode(await bytes()),
|
|
7938
|
+
json: async () => JSON.parse(new TextDecoder("utf-8").decode(await bytes()))
|
|
7939
|
+
};
|
|
7940
|
+
}
|
|
7941
|
+
var OUTCOME_KINDS = /* @__PURE__ */ new Set(["documents", "passthrough", "skip", "deletes"]);
|
|
7942
|
+
function normalizeOutcome(value) {
|
|
7943
|
+
const outcome = value;
|
|
7944
|
+
if (!outcome || typeof outcome !== "object" || !OUTCOME_KINDS.has(outcome.kind)) {
|
|
7945
|
+
return {
|
|
7946
|
+
kind: "error",
|
|
7947
|
+
error: {
|
|
7948
|
+
message: `map returned an invalid outcome: ${JSON.stringify(value)?.slice(0, 200)}. Return documents(), passthrough(), skip() or deletes().`
|
|
7949
|
+
}
|
|
7950
|
+
};
|
|
7951
|
+
}
|
|
7952
|
+
return outcome;
|
|
7953
|
+
}
|
|
7954
|
+
async function withTimeout(work, timeoutMs, key) {
|
|
7955
|
+
let timer;
|
|
7956
|
+
try {
|
|
7957
|
+
return await Promise.race([
|
|
7958
|
+
work,
|
|
7959
|
+
new Promise((_, reject) => {
|
|
7960
|
+
timer = setTimeout(
|
|
7961
|
+
() => reject(new Error(`map timed out after ${timeoutMs}ms on ${key}`)),
|
|
7962
|
+
timeoutMs
|
|
7963
|
+
);
|
|
7964
|
+
})
|
|
7965
|
+
]);
|
|
7966
|
+
} finally {
|
|
7967
|
+
if (timer) clearTimeout(timer);
|
|
7968
|
+
}
|
|
7969
|
+
}
|
|
7970
|
+
function defineMapper(source, config) {
|
|
7971
|
+
const run = async (params) => {
|
|
7972
|
+
const startedAt = Date.now();
|
|
7973
|
+
const timeoutMs = params.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
7974
|
+
const concurrency = Math.max(1, params.concurrency ?? DEFAULT_CONCURRENCY);
|
|
7975
|
+
const objects = Array.isArray(params.objects) ? params.objects : [];
|
|
7976
|
+
const outcomes = new Array(objects.length);
|
|
7977
|
+
let next = 0;
|
|
7978
|
+
const worker = async () => {
|
|
7979
|
+
while (true) {
|
|
7980
|
+
const index = next++;
|
|
7981
|
+
if (index >= objects.length) return;
|
|
7982
|
+
const input = objects[index];
|
|
7983
|
+
const t = Date.now();
|
|
7984
|
+
let outcome;
|
|
7985
|
+
try {
|
|
7986
|
+
outcome = normalizeOutcome(
|
|
7987
|
+
await withTimeout(
|
|
7988
|
+
Promise.resolve().then(() => config.map(objectFor(input))),
|
|
7989
|
+
timeoutMs,
|
|
7990
|
+
input.key
|
|
7991
|
+
)
|
|
7992
|
+
);
|
|
7993
|
+
} catch (e) {
|
|
7994
|
+
outcome = { kind: "error", error: errorInfo2(e) };
|
|
7995
|
+
}
|
|
7996
|
+
outcomes[index] = {
|
|
7997
|
+
key: input.key,
|
|
7998
|
+
outcome,
|
|
7999
|
+
durationMs: Date.now() - t
|
|
8000
|
+
};
|
|
8001
|
+
}
|
|
8002
|
+
};
|
|
8003
|
+
await Promise.all(
|
|
8004
|
+
Array.from({ length: Math.min(concurrency, objects.length) }, worker)
|
|
8005
|
+
);
|
|
8006
|
+
return {
|
|
8007
|
+
v: 1,
|
|
8008
|
+
source: source.name,
|
|
8009
|
+
outcomes,
|
|
8010
|
+
startedAt,
|
|
8011
|
+
durationMs: Date.now() - startedAt
|
|
8012
|
+
};
|
|
8013
|
+
};
|
|
8014
|
+
return Object.assign(run, {
|
|
8015
|
+
kind: "mapper",
|
|
8016
|
+
source: source.name,
|
|
8017
|
+
config
|
|
8018
|
+
});
|
|
8019
|
+
}
|
|
8020
|
+
|
|
7760
8021
|
// src/generated/snippets.ts
|
|
7761
8022
|
var monacoSnippets = {
|
|
7762
8023
|
"activeCampaignAddNote": { fields: [["contactId", "string"], ["note", "string"]], outputKeys: [] },
|
|
@@ -8031,17 +8292,22 @@ export {
|
|
|
8031
8292
|
db,
|
|
8032
8293
|
index_default as default,
|
|
8033
8294
|
defineJewel,
|
|
8295
|
+
defineMapper,
|
|
8296
|
+
deletes,
|
|
8297
|
+
documents,
|
|
8034
8298
|
email,
|
|
8035
8299
|
events,
|
|
8036
8300
|
files,
|
|
8037
8301
|
getRequestContext,
|
|
8038
8302
|
mindstudio,
|
|
8039
8303
|
monacoSnippets,
|
|
8304
|
+
passthrough,
|
|
8040
8305
|
prerender,
|
|
8041
8306
|
reportIssue,
|
|
8042
8307
|
resolveUser,
|
|
8043
8308
|
runWithContext,
|
|
8044
8309
|
session,
|
|
8310
|
+
skip,
|
|
8045
8311
|
stepMetadata,
|
|
8046
8312
|
stream,
|
|
8047
8313
|
voice,
|