@promptev/context-engine 0.0.2 → 0.0.4

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.
@@ -62,6 +62,59 @@ await engine.search(q, { principals: user.groups }); // scoped
62
62
  no default. If you hit a `TypeError`, supply real ones — do **not** silence
63
63
  it with `principals: () => null`, which disables access control on a public
64
64
  port. Anonymous is `() => []`. Trusted is `() => TRUSTED`.
65
+ - **Search picks its own mode unless you name one.** `search({ mode: null })` (the
66
+ default) adds the graph leg when the deployment has a graph AND something in
67
+ scope was ingested with `mode: "graph"` — a model cannot know which documents
68
+ those are, so it is not asked. Pass `mode: "hybrid"` to force the old
69
+ behaviour. INGESTION is unchanged and stays explicit. An unreachable graph
70
+ database is skipped rather than waited on, and every graph failure degrades
71
+ to the hybrid legs instead of failing the search.
72
+ - **The knowledge tool is one call with THIRTEEN actions** — `discover`,
73
+ `search`, `get_doc`, `get_docs`, `get_chunks`, `list`, `query_meta`,
74
+ `compute`, `map_reduce`, `get_neighbors`, `traverse`, `find_related`,
75
+ `community_summary`.
76
+ `engine.searchKnowledgeBase({ action, principals, scope })` is the
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
106
+ the name, description and JSON Schema if you are wiring it into your own
107
+ agent loop. `compute` and `mapReduce` accept a host callable that REPLACES
108
+ the built-in one — that is the seam for your own permission, billing and
109
+ approval rules, and supplying one makes the action available regardless of
110
+ the deployment's own gate.
111
+ - **`scope` is REQUIRED on `createMcpApp`** and on
112
+ `engine.searchKnowledgeBase`: the ceiling of source ids the tool may ever
113
+ reach. Like `principals` it is host-supplied and NEVER a tool argument — a
114
+ model can name any source id, and a tool that believed it would cross
115
+ tenants. Pass `scope: UNSCOPED` only to mean the whole corpus on purpose. A
116
+ caller narrows within the ceiling; an id outside it is dropped silently, and
117
+ a request left with nothing returns nothing (never everything).
65
118
  - **`acl=null` on `updateDocument` UNRESTRICTS the document.** "Leave it
66
119
  alone" is expressed by omitting the argument (the `UNSET` sentinel), because
67
120
  `null` already means unrestricted and cannot also mean absent. Same for
@@ -130,11 +183,14 @@ app.route(
130
183
  `createFastifyPlugin` (`@promptev/context-engine/fastify`) and
131
184
  `createMcpApp` (`@promptev/context-engine/mcp`) take the same two
132
185
  arguments. MCP has no request object: `principals` is a zero-argument
133
- callable resolved fresh on every tool call. The CLI `context-engine mcp`
186
+ callable resolved fresh on every tool call, and `createMcpApp` also requires
187
+ `scope` — the source-id ceiling, resolved the same way. The CLI `context-engine mcp`
134
188
  binds `127.0.0.1` and answers as anonymous (`principals=[]`).
135
189
 
136
190
  Not on a supported framework? Call `engine.search()` / `engine.ingest()`
137
- from your own handler — the routers are thin wrappers over the same core.
191
+ from your own handler — the routers are thin wrappers over the same core. For
192
+ an agent, `engine.searchKnowledgeBase({ action, principals, scope })` is the
193
+ same one tool the MCP server serves, with no transport in the way.
138
194
 
139
195
  ## Lifecycle
140
196
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promptev/context-engine",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Promptev Context Engine — language-agnostic ingestion + hybrid retrieval (TypeScript)",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Promptev Inc",
@@ -62,6 +62,59 @@ await engine.search(q, { principals: user.groups }); // scoped
62
62
  no default. If you hit a `TypeError`, supply real ones — do **not** silence
63
63
  it with `principals: () => null`, which disables access control on a public
64
64
  port. Anonymous is `() => []`. Trusted is `() => TRUSTED`.
65
+ - **Search picks its own mode unless you name one.** `search({ mode: null })` (the
66
+ default) adds the graph leg when the deployment has a graph AND something in
67
+ scope was ingested with `mode: "graph"` — a model cannot know which documents
68
+ those are, so it is not asked. Pass `mode: "hybrid"` to force the old
69
+ behaviour. INGESTION is unchanged and stays explicit. An unreachable graph
70
+ database is skipped rather than waited on, and every graph failure degrades
71
+ to the hybrid legs instead of failing the search.
72
+ - **The knowledge tool is one call with THIRTEEN actions** — `discover`,
73
+ `search`, `get_doc`, `get_docs`, `get_chunks`, `list`, `query_meta`,
74
+ `compute`, `map_reduce`, `get_neighbors`, `traverse`, `find_related`,
75
+ `community_summary`.
76
+ `engine.searchKnowledgeBase({ action, principals, scope })` is the
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
106
+ the name, description and JSON Schema if you are wiring it into your own
107
+ agent loop. `compute` and `mapReduce` accept a host callable that REPLACES
108
+ the built-in one — that is the seam for your own permission, billing and
109
+ approval rules, and supplying one makes the action available regardless of
110
+ the deployment's own gate.
111
+ - **`scope` is REQUIRED on `createMcpApp`** and on
112
+ `engine.searchKnowledgeBase`: the ceiling of source ids the tool may ever
113
+ reach. Like `principals` it is host-supplied and NEVER a tool argument — a
114
+ model can name any source id, and a tool that believed it would cross
115
+ tenants. Pass `scope: UNSCOPED` only to mean the whole corpus on purpose. A
116
+ caller narrows within the ceiling; an id outside it is dropped silently, and
117
+ a request left with nothing returns nothing (never everything).
65
118
  - **`acl=null` on `updateDocument` UNRESTRICTS the document.** "Leave it
66
119
  alone" is expressed by omitting the argument (the `UNSET` sentinel), because
67
120
  `null` already means unrestricted and cannot also mean absent. Same for
@@ -130,11 +183,14 @@ app.route(
130
183
  `createFastifyPlugin` (`@promptev/context-engine/fastify`) and
131
184
  `createMcpApp` (`@promptev/context-engine/mcp`) take the same two
132
185
  arguments. MCP has no request object: `principals` is a zero-argument
133
- callable resolved fresh on every tool call. The CLI `context-engine mcp`
186
+ callable resolved fresh on every tool call, and `createMcpApp` also requires
187
+ `scope` — the source-id ceiling, resolved the same way. The CLI `context-engine mcp`
134
188
  binds `127.0.0.1` and answers as anonymous (`principals=[]`).
135
189
 
136
190
  Not on a supported framework? Call `engine.search()` / `engine.ingest()`
137
- from your own handler — the routers are thin wrappers over the same core.
191
+ from your own handler — the routers are thin wrappers over the same core. For
192
+ an agent, `engine.searchKnowledgeBase({ action, principals, scope })` is the
193
+ same one tool the MCP server serves, with no transport in the way.
138
194
 
139
195
  ## Lifecycle
140
196