@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.
@@ -74,7 +74,35 @@ await engine.search(q, { principals: user.groups }); // scoped
74
74
  `compute`, `map_reduce`, `get_neighbors`, `traverse`, `find_related`,
75
75
  `community_summary`.
76
76
  `engine.searchKnowledgeBase({ action, principals, scope })` is the
77
- same function `createMcpApp` serves. `knowledgeToolDefinition()` gives
77
+ same function `createMcpApp` serves. `discover` returns the SCHEMA, not
78
+ just an inventory, and for EVERY document type. Each listed document carries
79
+ `structure` — `sheets` (name, column headers, row count; the sheet name is
80
+ the key `compute` exposes in `dfs`) for a workbook, `sections` and
81
+ `last_page` for a document with headings, `keys` for JSON, and `chunks` for
82
+ anything else — plus `document_type` and `mode`. Every list inside a
83
+ `structure` is capped, with the remainder reported as `more_columns` /
84
+ `more_sections` / `more_keys` / `more_sheets` (absent when nothing was cut)
85
+ and, while the remainder is worth fetching, a filled-in `next_action`. Past
86
+ `MAX_INVITED_ITEMS` the invitation is WITHHELD and replaced by `instead` —
87
+ a model cannot know what a full list costs until it has paid for it, so
88
+ offering a sixteen-thousand-column fetch would be the tool's decision, not
89
+ the caller's; a list that long is a signal to compute over the sheet or read
90
+ the document, never to list it. **The cap is for the call that did NOT name
91
+ its documents:** `discover` (and `list`) now accept `document_ids`, filtered
92
+ in SQL, and a `discover` scoped to specific documents answers their
93
+ structure WHOLE and narrows `fields_by_type` and `document_types` to them
94
+ too. The host's `scope` ceiling still wins — a named id outside it is
95
+ dropped, never reached.
96
+ Alongside them, `fields_by_type` and `document_types` are BOTH keyed by
97
+ `(kind, type)` — a `document_type` is a label an LLM wrote, so a PDF invoice
98
+ and a CSV of invoice rows can share one, and keying on the label alone would
99
+ merge their fields. `fields_by_type` rows carry `fields` (name, data type,
100
+ how many in-scope documents) plus `more_fields` (names past the cap);
101
+ `document_types` rows carry `documents` and `with_fields`. `type` is written
102
+ only where `extract_structured` was opted into, so it is null on a default
103
+ deployment — which is why `kind` is there. All of it is ACL- and
104
+ scope-filtered and masked by an output redaction policy. `list` returns the
105
+ same page WITHOUT the structure — browsing does not pay for it. `knowledgeToolDefinition()` gives
78
106
  the name, description and JSON Schema if you are wiring it into your own
79
107
  agent loop. `compute` and `mapReduce` accept a host callable that REPLACES
80
108
  the built-in one — that is the seam for your own permission, billing and
@@ -102,6 +130,31 @@ await engine.search(q, { principals: user.groups }); // scoped
102
130
  header before the LLM sees a schema, and sweeps the result.
103
131
  - Filing a document under an ACL the caller does not hold returns **403**, on
104
132
  ingest and on PATCH alike.
133
+ - **`executeTool` cuts its `result` to 8,000 characters and 100 rows by
134
+ default.** A cut result is `{ _truncated, _original_size }` — a model
135
+ reading only that prefix will say "not found" about data that was there.
136
+ Size the budget to the model's window with `resultMaxChars` /
137
+ `resultMaxRows` (omitted = default, `null` = no limit; rows are per table,
138
+ and a db tool's own `max_rows`, default 1000, still applies first).
139
+ `responseMode: "tsv"` works for ANY tool: every array of objects in the
140
+ result (db `rows`, an HTTP body's list, a function's returned list) becomes
141
+ a TSV string under the same key — header first, `\N` = NULL, nested values
142
+ as JSON cells, tab/newline/backslash escaped — and the budget cuts at whole
143
+ rows, reporting `_result_shaping: { "<dotted.path>": { rows_returned,
144
+ rows_omitted } }`. Everything else stays JSON. The audit row keeps the
145
+ original under its own 50KB cap regardless. An http tool can store
146
+ `response_mode: "tsv"` in its config (every method, `QUERY` included;
147
+ omitted = `json`); an explicit `responseMode` on the call wins. The
148
+ conversion alone is `formatResult(result, "tsv")`.
149
+
150
+ ```ts
151
+ const out = await engine.executeTool("db_sales", { query: "SELECT * FROM orders" }, {
152
+ principals,
153
+ resultMaxChars: 200_000,
154
+ resultMaxRows: null,
155
+ responseMode: "tsv",
156
+ });
157
+ ```
105
158
 
106
159
  ## Search
107
160
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promptev/context-engine",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Promptev Context Engine — language-agnostic ingestion + hybrid retrieval (TypeScript)",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Promptev Inc",
@@ -74,7 +74,35 @@ await engine.search(q, { principals: user.groups }); // scoped
74
74
  `compute`, `map_reduce`, `get_neighbors`, `traverse`, `find_related`,
75
75
  `community_summary`.
76
76
  `engine.searchKnowledgeBase({ action, principals, scope })` is the
77
- same function `createMcpApp` serves. `knowledgeToolDefinition()` gives
77
+ same function `createMcpApp` serves. `discover` returns the SCHEMA, not
78
+ just an inventory, and for EVERY document type. Each listed document carries
79
+ `structure` — `sheets` (name, column headers, row count; the sheet name is
80
+ the key `compute` exposes in `dfs`) for a workbook, `sections` and
81
+ `last_page` for a document with headings, `keys` for JSON, and `chunks` for
82
+ anything else — plus `document_type` and `mode`. Every list inside a
83
+ `structure` is capped, with the remainder reported as `more_columns` /
84
+ `more_sections` / `more_keys` / `more_sheets` (absent when nothing was cut)
85
+ and, while the remainder is worth fetching, a filled-in `next_action`. Past
86
+ `MAX_INVITED_ITEMS` the invitation is WITHHELD and replaced by `instead` —
87
+ a model cannot know what a full list costs until it has paid for it, so
88
+ offering a sixteen-thousand-column fetch would be the tool's decision, not
89
+ the caller's; a list that long is a signal to compute over the sheet or read
90
+ the document, never to list it. **The cap is for the call that did NOT name
91
+ its documents:** `discover` (and `list`) now accept `document_ids`, filtered
92
+ in SQL, and a `discover` scoped to specific documents answers their
93
+ structure WHOLE and narrows `fields_by_type` and `document_types` to them
94
+ too. The host's `scope` ceiling still wins — a named id outside it is
95
+ dropped, never reached.
96
+ Alongside them, `fields_by_type` and `document_types` are BOTH keyed by
97
+ `(kind, type)` — a `document_type` is a label an LLM wrote, so a PDF invoice
98
+ and a CSV of invoice rows can share one, and keying on the label alone would
99
+ merge their fields. `fields_by_type` rows carry `fields` (name, data type,
100
+ how many in-scope documents) plus `more_fields` (names past the cap);
101
+ `document_types` rows carry `documents` and `with_fields`. `type` is written
102
+ only where `extract_structured` was opted into, so it is null on a default
103
+ deployment — which is why `kind` is there. All of it is ACL- and
104
+ scope-filtered and masked by an output redaction policy. `list` returns the
105
+ same page WITHOUT the structure — browsing does not pay for it. `knowledgeToolDefinition()` gives
78
106
  the name, description and JSON Schema if you are wiring it into your own
79
107
  agent loop. `compute` and `mapReduce` accept a host callable that REPLACES
80
108
  the built-in one — that is the seam for your own permission, billing and
@@ -102,6 +130,31 @@ await engine.search(q, { principals: user.groups }); // scoped
102
130
  header before the LLM sees a schema, and sweeps the result.
103
131
  - Filing a document under an ACL the caller does not hold returns **403**, on
104
132
  ingest and on PATCH alike.
133
+ - **`executeTool` cuts its `result` to 8,000 characters and 100 rows by
134
+ default.** A cut result is `{ _truncated, _original_size }` — a model
135
+ reading only that prefix will say "not found" about data that was there.
136
+ Size the budget to the model's window with `resultMaxChars` /
137
+ `resultMaxRows` (omitted = default, `null` = no limit; rows are per table,
138
+ and a db tool's own `max_rows`, default 1000, still applies first).
139
+ `responseMode: "tsv"` works for ANY tool: every array of objects in the
140
+ result (db `rows`, an HTTP body's list, a function's returned list) becomes
141
+ a TSV string under the same key — header first, `\N` = NULL, nested values
142
+ as JSON cells, tab/newline/backslash escaped — and the budget cuts at whole
143
+ rows, reporting `_result_shaping: { "<dotted.path>": { rows_returned,
144
+ rows_omitted } }`. Everything else stays JSON. The audit row keeps the
145
+ original under its own 50KB cap regardless. An http tool can store
146
+ `response_mode: "tsv"` in its config (every method, `QUERY` included;
147
+ omitted = `json`); an explicit `responseMode` on the call wins. The
148
+ conversion alone is `formatResult(result, "tsv")`.
149
+
150
+ ```ts
151
+ const out = await engine.executeTool("db_sales", { query: "SELECT * FROM orders" }, {
152
+ principals,
153
+ resultMaxChars: 200_000,
154
+ resultMaxRows: null,
155
+ responseMode: "tsv",
156
+ });
157
+ ```
105
158
 
106
159
  ## Search
107
160