@promptev/context-engine 0.0.4 → 0.0.5

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.cjs CHANGED
@@ -11833,7 +11833,7 @@ var SCHEMAS = {
11833
11833
  url: { type: "string", description: "Endpoint URL, may contain {path} params" },
11834
11834
  method: {
11835
11835
  type: "string",
11836
- enum: ["GET", "POST", "PUT", "PATCH", "DELETE"],
11836
+ enum: ["GET", "POST", "PUT", "PATCH", "DELETE", "QUERY"],
11837
11837
  default: "GET"
11838
11838
  },
11839
11839
  headers: {
@@ -11869,6 +11869,15 @@ var SCHEMAS = {
11869
11869
  llmQueryParameters: {
11870
11870
  type: "object",
11871
11871
  description: "LLM-filled parameters sent as the query string"
11872
+ },
11873
+ // How the response comes back to the caller, whatever the method. `tsv`
11874
+ // turns every array of objects in it into a TSV string (`formatResult`
11875
+ // in tools/response-mode.ts); a caller's explicit `responseMode` wins.
11876
+ response_mode: {
11877
+ type: "string",
11878
+ enum: ["json", "tsv"],
11879
+ default: "json",
11880
+ description: "Return the response as JSON, or its arrays of objects as TSV"
11872
11881
  }
11873
11882
  },
11874
11883
  required: ["url", "method"]
@@ -13034,6 +13043,89 @@ function findTool(tools, callName) {
13034
13043
  return tools.find((t) => t.callName === callName);
13035
13044
  }
13036
13045
 
13046
+ // src/tools/response-mode.ts
13047
+ var RESPONSE_MODES = ["json", "tsv"];
13048
+ function validateResponseMode(value, name = "responseMode") {
13049
+ if (!RESPONSE_MODES.includes(value)) {
13050
+ throw new Error(`${name} must be one of json, tsv, got ${JSON.stringify(value)}`);
13051
+ }
13052
+ return value;
13053
+ }
13054
+ function tsvCell(value) {
13055
+ if (value === null || value === void 0) return "\\N";
13056
+ let text;
13057
+ if (typeof value === "string") text = value;
13058
+ else if (value instanceof Date) text = value.toISOString();
13059
+ else if (typeof value === "bigint") text = value.toString();
13060
+ else {
13061
+ try {
13062
+ text = JSON.stringify(value) ?? String(value);
13063
+ } catch {
13064
+ text = String(value);
13065
+ }
13066
+ }
13067
+ return text.replaceAll("\\", "\\\\").replaceAll(" ", "\\t").replaceAll("\n", "\\n").replaceAll("\r", "\\r");
13068
+ }
13069
+ function isPlainObject2(value) {
13070
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
13071
+ const proto = Object.getPrototypeOf(value);
13072
+ return proto === Object.prototype || proto === null;
13073
+ }
13074
+ function isTable(value) {
13075
+ return Array.isArray(value) && value.length > 0 && value.every(isPlainObject2);
13076
+ }
13077
+ function tsvLines(rows) {
13078
+ const columns = [];
13079
+ const seen = /* @__PURE__ */ new Set();
13080
+ for (const row of rows) {
13081
+ for (const key of Object.keys(row)) {
13082
+ if (!seen.has(key)) {
13083
+ seen.add(key);
13084
+ columns.push(key);
13085
+ }
13086
+ }
13087
+ }
13088
+ return [
13089
+ columns.map(tsvCell).join(" "),
13090
+ ...rows.map((row) => columns.map((c) => tsvCell(row[c])).join(" "))
13091
+ ];
13092
+ }
13093
+ function rowsToTsv(rows) {
13094
+ return rows.length ? tsvLines(rows).join("\n") : "";
13095
+ }
13096
+ function collectTables(value, path, out) {
13097
+ if (isTable(value)) {
13098
+ out.set(JSON.stringify(path), { path, lines: tsvLines(value) });
13099
+ } else if (isPlainObject2(value)) {
13100
+ for (const [key, child] of Object.entries(value)) collectTables(child, [...path, key], out);
13101
+ } else if (Array.isArray(value)) {
13102
+ value.forEach((child, index) => {
13103
+ collectTables(child, [...path, index], out);
13104
+ });
13105
+ }
13106
+ }
13107
+ function buildTsv(value, path, tables, kept) {
13108
+ const id = JSON.stringify(path);
13109
+ const table = tables.get(id);
13110
+ if (table) return table.lines.slice(0, (kept.get(id) ?? 0) + 1).join("\n");
13111
+ if (isPlainObject2(value)) {
13112
+ const out = {};
13113
+ for (const [k, v] of Object.entries(value)) out[k] = buildTsv(v, [...path, k], tables, kept);
13114
+ return out;
13115
+ }
13116
+ if (Array.isArray(value)) return value.map((v, i) => buildTsv(v, [...path, i], tables, kept));
13117
+ return value;
13118
+ }
13119
+ function formatResult(result, responseMode = "json") {
13120
+ if (validateResponseMode(responseMode) === "json") return result;
13121
+ const root = { result };
13122
+ const tables = /* @__PURE__ */ new Map();
13123
+ collectTables(root, [], tables);
13124
+ if (!tables.size) return result;
13125
+ const kept = new Map([...tables].map(([id, t]) => [id, t.lines.length - 1]));
13126
+ return buildTsv(root, [], tables, kept).result;
13127
+ }
13128
+
13037
13129
  // src/tools/governance.ts
13038
13130
  var RESULT_MAX_CHARS = 8e3;
13039
13131
  var RESULT_MAX_ROWS = 100;
@@ -13065,12 +13157,90 @@ function stripUnderscoreArgs(args) {
13065
13157
  }
13066
13158
  return out;
13067
13159
  }
13160
+ function validateResultBudget(name, value, fallback) {
13161
+ if (value === void 0) return fallback;
13162
+ if (value === null) return null;
13163
+ if (typeof value !== "number" || !Number.isInteger(value)) {
13164
+ throw new TypeError(`${name} must be a positive integer or null, got ${typeof value}`);
13165
+ }
13166
+ if (value < 1) throw new Error(`${name} must be a positive integer or null, got ${value}`);
13167
+ return value;
13168
+ }
13169
+ function serialize(value) {
13170
+ try {
13171
+ return JSON.stringify(value) ?? JSON.stringify(String(value));
13172
+ } catch {
13173
+ return JSON.stringify(String(value));
13174
+ }
13175
+ }
13176
+ function clip(serialized, maxChars) {
13177
+ return {
13178
+ _truncated: serialized.slice(0, maxChars),
13179
+ _original_size: serialized.length,
13180
+ _note: "tool result exceeded the context budget and was truncated"
13181
+ };
13182
+ }
13183
+ function shapeTsv(result, maxChars, maxRows) {
13184
+ const wrapped = !isPlainObject2(result);
13185
+ const root = wrapped ? { result } : result;
13186
+ const tables = /* @__PURE__ */ new Map();
13187
+ collectTables(root, [], tables);
13188
+ if (!tables.size) return null;
13189
+ const sizes = new Map([...tables].map(([id, t]) => [id, t.lines.length - 1]));
13190
+ const kept = new Map([...sizes].map(([id, n]) => [id, maxRows === null ? n : Math.min(n, maxRows)]));
13191
+ const render = () => {
13192
+ const built = buildTsv(root, [], tables, kept);
13193
+ const notes = {};
13194
+ for (const [id, t] of tables) {
13195
+ const k = kept.get(id);
13196
+ const n = sizes.get(id);
13197
+ if (k < n) notes[t.path.join(".")] = { rows_returned: k, rows_omitted: n - k };
13198
+ }
13199
+ if (Object.keys(notes).length) {
13200
+ built._result_shaping = notes;
13201
+ return built;
13202
+ }
13203
+ return wrapped ? built.result : built;
13204
+ };
13205
+ const fits = () => maxChars === null || serialize(render()).length <= maxChars;
13206
+ const capped2 = [...tables.keys()].some((id) => kept.get(id) < sizes.get(id));
13207
+ if (fits()) return [render(), capped2];
13208
+ const initial = new Map(kept);
13209
+ const tableSize = (id) => tables.get(id).lines.slice(0, kept.get(id) + 1).join("\n").length;
13210
+ const bySize = [...tables.keys()].sort((a, b) => tableSize(b) - tableSize(a));
13211
+ for (const id of bySize) {
13212
+ let lo = 0;
13213
+ let hi = kept.get(id) - 1;
13214
+ let best = null;
13215
+ while (lo <= hi) {
13216
+ const mid = Math.floor((lo + hi) / 2);
13217
+ kept.set(id, mid);
13218
+ if (fits()) {
13219
+ best = mid;
13220
+ lo = mid + 1;
13221
+ } else {
13222
+ hi = mid - 1;
13223
+ }
13224
+ }
13225
+ if (best !== null) {
13226
+ kept.set(id, best);
13227
+ return [render(), true];
13228
+ }
13229
+ kept.set(id, 0);
13230
+ }
13231
+ for (const [id, n] of initial) kept.set(id, n);
13232
+ return [clip(serialize(render()), maxChars), true];
13233
+ }
13068
13234
  function shapeResult(result, opts = {}) {
13069
- const maxChars = opts.maxChars ?? RESULT_MAX_CHARS;
13070
- const maxRows = opts.maxRows ?? RESULT_MAX_ROWS;
13235
+ const maxChars = opts.maxChars === void 0 ? RESULT_MAX_CHARS : opts.maxChars;
13236
+ const maxRows = opts.maxRows === void 0 ? RESULT_MAX_ROWS : opts.maxRows;
13237
+ if (opts.responseMode === "tsv") {
13238
+ const tsv = shapeTsv(result, maxChars, maxRows);
13239
+ if (tsv) return tsv;
13240
+ }
13071
13241
  let truncated = false;
13072
13242
  let shaped = result;
13073
- if (result && typeof result === "object" && Array.isArray(result.rows) && result.rows.length > maxRows) {
13243
+ if (maxRows !== null && result && typeof result === "object" && Array.isArray(result.rows) && result.rows.length > maxRows) {
13074
13244
  const rows = result.rows;
13075
13245
  const kept = rows.slice(0, maxRows);
13076
13246
  shaped = {
@@ -13080,22 +13250,9 @@ function shapeResult(result, opts = {}) {
13080
13250
  };
13081
13251
  truncated = true;
13082
13252
  }
13083
- let serialized;
13084
- try {
13085
- serialized = JSON.stringify(shaped);
13086
- } catch {
13087
- serialized = JSON.stringify(String(shaped));
13088
- }
13089
- if (serialized.length > maxChars) {
13090
- return [
13091
- {
13092
- _truncated: serialized.slice(0, maxChars),
13093
- _original_size: serialized.length,
13094
- _note: "tool result exceeded the context budget and was truncated"
13095
- },
13096
- true
13097
- ];
13098
- }
13253
+ if (maxChars === null) return [shaped, truncated];
13254
+ const serialized = serialize(shaped);
13255
+ if (serialized.length > maxChars) return [clip(serialized, maxChars), true];
13099
13256
  return [shaped, truncated];
13100
13257
  }
13101
13258
  function redactToolResult(result, policy, opts) {
@@ -13206,9 +13363,29 @@ function canonicalToPublic(ct) {
13206
13363
  params_schema: ct.paramsSchema
13207
13364
  };
13208
13365
  }
13366
+ function configuredResponseMode(ct, config) {
13367
+ if (ct.kind !== "http" || config.response_mode == null) return "json";
13368
+ try {
13369
+ return validateResponseMode(config.response_mode, "config.response_mode");
13370
+ } catch {
13371
+ console.warn(
13372
+ `tool ${ct.callName} has an unknown config response_mode ${JSON.stringify(config.response_mode)}; returning json`
13373
+ );
13374
+ return "json";
13375
+ }
13376
+ }
13377
+ function checkConfigResponseMode(kind, config) {
13378
+ if (kind !== "http" || !config || config.response_mode == null) return;
13379
+ try {
13380
+ validateResponseMode(config.response_mode, "config.response_mode");
13381
+ } catch (exc) {
13382
+ throw new ConfigTemplateError(exc.message);
13383
+ }
13384
+ }
13209
13385
  async function registerTool(engine, tc) {
13210
13386
  canonicalFromConfig(tc);
13211
13387
  const config = tc.config ?? {};
13388
+ checkConfigResponseMode(tc.kind, config);
13212
13389
  if (containsSentinel(config)) {
13213
13390
  throw new ConfigTemplateError(
13214
13391
  `config contains the ${JSON.stringify(REDACTED_SENTINEL)} placeholder \u2014 a redacted template cannot be registered as a new tool; re-enter the secret values`
@@ -13244,6 +13421,12 @@ async function updateTool(engine, id, opts) {
13244
13421
  if (!row || !aclVisible(row.acl != null ? [...row.acl] : null, principals)) {
13245
13422
  throw new exports.EngineActionError(`tool not found: ${id}`);
13246
13423
  }
13424
+ if ("config" in fields) {
13425
+ checkConfigResponseMode(
13426
+ "kind" in fields ? fields.kind : row.kind,
13427
+ fields.config
13428
+ );
13429
+ }
13247
13430
  const sets = [];
13248
13431
  const params = [];
13249
13432
  let i = 1;
@@ -13553,6 +13736,9 @@ function warnUnscopedApproval(callName) {
13553
13736
  async function executeTool(engine, callName, args, opts = {}) {
13554
13737
  const runtimeArgs = args ?? {};
13555
13738
  const approvalScope = validateApprovalScope(opts.approvalScope);
13739
+ const resultMaxChars = validateResultBudget("resultMaxChars", opts.resultMaxChars, RESULT_MAX_CHARS);
13740
+ const resultMaxRows = validateResultBudget("resultMaxRows", opts.resultMaxRows, RESULT_MAX_ROWS);
13741
+ let responseMode = opts.responseMode === void 0 || opts.responseMode === null ? null : validateResponseMode(opts.responseMode);
13556
13742
  const ct = findTool(await mergedTools(engine, opts.sourceId ?? null), callName);
13557
13743
  if (!ct) throw new exports.EngineActionError(`tool not found: ${callName}`);
13558
13744
  if (!toolVisible(ct, opts.principals ?? null)) {
@@ -13595,6 +13781,7 @@ async function executeTool(engine, callName, args, opts = {}) {
13595
13781
  if (ct.kind !== "function" && ct.id != null) {
13596
13782
  config = await decryptCtConfig(engine, ct.id);
13597
13783
  }
13784
+ responseMode ??= configuredResponseMode(ct, config);
13598
13785
  const actorType = opts.actor?.type ?? null;
13599
13786
  const actorId = opts.actor?.id ?? null;
13600
13787
  let rawResult = null;
@@ -13618,7 +13805,16 @@ async function executeTool(engine, callName, args, opts = {}) {
13618
13805
  hooks: engine.hooks
13619
13806
  });
13620
13807
  rawResult = redacted;
13621
- [shaped, truncated] = shapeResult(rawResult);
13808
+ let toShape = rawResult;
13809
+ if (responseMode === "tsv" && ct.kind === "db" && isPlainObject2(rawResult)) {
13810
+ const { text: _preview, ...rest } = rawResult;
13811
+ toShape = rest;
13812
+ }
13813
+ [shaped, truncated] = shapeResult(toShape, {
13814
+ maxChars: resultMaxChars,
13815
+ maxRows: resultMaxRows,
13816
+ responseMode
13817
+ });
13622
13818
  } catch (e) {
13623
13819
  exc = e;
13624
13820
  success = false;
@@ -14274,7 +14470,10 @@ var ContextEngine = class _ContextEngine {
14274
14470
  principals: resolvePrincipals(opts.principals, "executeTool"),
14275
14471
  actor: opts.actor,
14276
14472
  source: opts.source ?? "api",
14277
- approvalScope: opts.approvalScope
14473
+ approvalScope: opts.approvalScope,
14474
+ resultMaxChars: opts.resultMaxChars,
14475
+ resultMaxRows: opts.resultMaxRows,
14476
+ responseMode: opts.responseMode
14278
14477
  });
14279
14478
  }
14280
14479
  };
@@ -14592,6 +14791,7 @@ exports.encryptDict = encryptDict;
14592
14791
  exports.extract = extract2;
14593
14792
  exports.extractStructuredData = extractStructuredData;
14594
14793
  exports.fieldSummary = fieldSummary;
14794
+ exports.formatResult = formatResult;
14595
14795
  exports.functionTool = functionTool;
14596
14796
  exports.getDocumentText = getDocumentText;
14597
14797
  exports.getSecretKey = getSecretKey;
@@ -14606,6 +14806,7 @@ exports.resolveApproval = resolveApproval;
14606
14806
  exports.resolveFields = resolveFields;
14607
14807
  exports.resolvePrincipals = resolvePrincipals;
14608
14808
  exports.resolveScope = resolveScope;
14809
+ exports.rowsToTsv = rowsToTsv;
14609
14810
  exports.rrfFuse = rrfFuse;
14610
14811
  exports.runMigrate = runMigrate;
14611
14812
  exports.runSearch = runSearch;