@promptev/context-engine 0.0.3 → 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/README.md CHANGED
@@ -186,7 +186,7 @@ One tool with actions:
186
186
 
187
187
  | Action | What it is for |
188
188
  |---|---|
189
- | `discover` | what documents there are and what each action can do call it first |
189
+ | `discover` | what documents there are and what is inside each one — sheets and columns, sections and pages, or JSON keys — plus the extracted fields by document type, a census of the corpus, and what each action can do. Call it first |
190
190
  | `search` | passages by meaning or keywords |
191
191
  | `get_doc` / `get_docs` | one whole document by id, or several at once |
192
192
  | `get_chunks` | walk one long document in order, a piece at a time |
@@ -204,6 +204,30 @@ as off rather than hidden, and called anyway it answers `{ success: false,
204
204
  error }` the model can relay. A truncated answer carries the `next_page` call
205
205
  that reads the rest.
206
206
 
207
+ `discover` answers with the SCHEMA, not an inventory, so one call is enough to
208
+ write one correct `compute` or `query_meta` call instead of three exploratory
209
+ ones. Every listed document carries `document_type`, `mode`, and a `structure`
210
+ whose shape follows the type — `sheets` (name, column headers, row count) for
211
+ a workbook, `sections` and `last_page` for a document with headings, `keys`
212
+ for JSON, and `chunks` as the floor for anything else. Beside them,
213
+ `fields_by_type` lists the extracted structured field names with each field's
214
+ data type and how many in-scope documents carry it, and `document_types` is a
215
+ census of the corpus. Both are keyed by `(kind, type)`: `kind` comes from the
216
+ mime and is always known, while `document_type` is free text an LLM wrote and
217
+ exists only where `extractStructured` was enabled — so a PDF invoice and a
218
+ spreadsheet of invoice rows stay separate rather than merging under one label.
219
+
220
+ Lists inside a `structure` are capped, with the remainder reported as
221
+ `more_columns` / `more_sections` / `more_keys` / `more_sheets`, because
222
+ `discover` is the first call of a conversation and an unbounded list lands in
223
+ the model's context before it has asked anything. **The cap is for the call
224
+ that did not name its documents:** `discover` and `list` take `document_ids`,
225
+ and a `discover` scoped to specific documents answers their structure whole
226
+ and narrows the field grouping and the census to them too. While the remainder
227
+ is small enough to be worth fetching, the truncated structure carries that
228
+ call already filled in; past that it says what to do instead, because a sheet
229
+ with hundreds of columns is one to compute over rather than read back.
230
+
207
231
  Omit `mode` on a search and it is worked out from the documents in scope — the
208
232
  graph leg is added when the deployment has a graph and something in scope was
209
233
  ingested that way, and it runs beside the other legs rather than ahead of
@@ -272,6 +296,46 @@ record. Resolve it server-side — on the adapters and the MCP gateway it is an
272
296
  still works this release, unscoped and with a `DeprecationWarning`; the next
273
297
  release refuses a gated call with no scope.
274
298
 
299
+ **Result budget** — the returned `result` is cut to 8,000 characters and 100
300
+ rows unless you say otherwise. Size it to your model's window, or pass `null`
301
+ to lift a limit. `responseMode: "tsv"` works for **any** tool: every array of
302
+ objects in the result — a db tool's `rows`, an HTTP API's `data.items`, a
303
+ function's returned list — comes back as a TSV string under the same key, and
304
+ the budget cuts at whole rows:
305
+
306
+ ```ts
307
+ const out = await engine.executeTool("db_sales", { query: "SELECT * FROM orders" }, {
308
+ principals: ["user:a"],
309
+ resultMaxChars: 200_000, // null = no character limit
310
+ resultMaxRows: null, // per table; a db tool's own max_rows (default 1000) still applies
311
+ responseMode: "tsv", // -> { success: true, columns: [...], rows: "id\tname\n1\t..." }
312
+ });
313
+ // a cut reports where: { _result_shaping: { rows: { rows_returned: 812, rows_omitted: 188 } } }
314
+ ```
315
+
316
+ In TSV, `\N` is NULL, nested values are compact JSON cells, and tab, newline,
317
+ CR and backslash inside a value are backslash-escaped. Parts of a result that
318
+ aren't arrays of objects stay JSON; a result that *is* one becomes the string
319
+ (wrapped as `{ result, _result_shaping }` when cut). The audit row always keeps
320
+ the original under its own 50KB cap, whatever you pass.
321
+
322
+ An **http tool** can set its format once, in its config — for every method
323
+ (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `QUERY`). Omitted, it is `json`; a
324
+ caller's explicit `responseMode` still wins:
325
+
326
+ ```ts
327
+ await engine.registerTool(new ToolConfig({
328
+ name: "orders",
329
+ kind: "http",
330
+ config: { method: "QUERY", url: "https://api.example.com/orders", response_mode: "tsv" }, // "json" (default) | "tsv"
331
+ acl: ["group:ops"],
332
+ }));
333
+ ```
334
+
335
+ The same conversion is a plain function for anything else:
336
+ `formatResult(result, "tsv")` (and `rowsToTsv(rows)`) from
337
+ `@promptev/context-engine` — `"json"`, the default, returns the input unchanged.
338
+
275
339
  A caller may only register or relabel a tool under principals it holds — filing
276
340
  one under another group's ACL is a 403.
277
341