@anchrd/intel-api 0.14.0 → 0.16.0
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/adapters/cloudflare/cloudflare.js +0 -68
- package/dist/adapters/cloudflare/cloudflare.types.d.ts +0 -39
- package/dist/adapters/db/db-flows.js +3 -2
- package/dist/adapters/db/db-grants.d.ts +1 -1
- package/dist/adapters/db/db-grants.js +1 -1
- package/dist/adapters/db/db.js +16 -113
- package/dist/adapters/openid/openid.js +1 -1
- package/dist/adapters/remote-tools/remote-tools.js +1 -1
- package/dist/bundle/bundle.js +31 -141
- package/dist/bundle/bundle.types.d.ts +1 -1
- package/dist/cli/cli.js +18 -11
- package/dist/flows/flows.d.ts +1 -1
- package/dist/flows/flows.js +1 -1
- package/dist/flows/flows.types.d.ts +4 -1
- package/dist/http/http.js +43 -238
- package/dist/http/http.types.d.ts +0 -8
- package/dist/indexing/indexing.js +18 -89
- package/dist/intel/intel.js +4 -9
- package/dist/intel/intel.types.d.ts +0 -6
- package/dist/mcp/mcp.js +84 -361
- package/dist/mcp/mcp.types.d.ts +2 -7
- package/dist/nodes/document-links/document-links.d.ts +6 -8
- package/dist/nodes/document-links/document-links.js +8 -31
- package/dist/nodes/nodes.js +24 -890
- package/dist/nodes/nodes.types.d.ts +14 -173
- package/dist/tools/tool-servers/tool-servers.d.ts +1 -1
- package/dist/tools/tool-servers/tool-servers.js +1 -1
- package/dist/tools/tools.js +37 -148
- package/dist/tools/tools.types.d.ts +1 -22
- package/migrations/0018_no_context_policy_at_last.sql +13 -6
- package/migrations/0019_one_name_for_the_grants.sql +52 -0
- package/package.json +2 -2
- package/dist/adapters/cloudflare-api/cloudflare-api.d.ts +0 -22
- package/dist/adapters/cloudflare-api/cloudflare-api.js +0 -306
- package/dist/adapters/cloudflare-api/cloudflare-api.types.d.ts +0 -64
- package/dist/adapters/cloudflare-api/cloudflare-api.types.js +0 -1
- package/dist/adapters/gate-applications/gate-applications.d.ts +0 -23
- package/dist/adapters/gate-applications/gate-applications.js +0 -88
- package/dist/adapters/tool-delegation/tool-delegation.d.ts +0 -22
- package/dist/adapters/tool-delegation/tool-delegation.js +0 -90
- package/dist/agent-costs/agent-costs.d.ts +0 -16
- package/dist/agent-costs/agent-costs.js +0 -105
- package/dist/agent-costs/agent-costs.types.d.ts +0 -30
- package/dist/agent-costs/agent-costs.types.js +0 -1
- package/dist/agent-runtime/agent-runtime.d.ts +0 -16
- package/dist/agent-runtime/agent-runtime.js +0 -150
- package/dist/agent-runtime/agent-runtime.types.d.ts +0 -122
- package/dist/agent-runtime/agent-runtime.types.js +0 -1
- package/dist/model-catalog/model-catalog.d.ts +0 -2
- package/dist/model-catalog/model-catalog.js +0 -99
- package/dist/model-catalog/model-catalog.types.d.ts +0 -15
- package/dist/model-catalog/model-catalog.types.js +0 -1
- package/dist/nodes/board/board.d.ts +0 -61
- package/dist/nodes/board/board.js +0 -826
- package/dist/nodes/board/board.types.d.ts +0 -38
- package/dist/nodes/board/board.types.js +0 -1
- package/migrations/0013_agents_in_the_tree.sql +0 -76
- package/migrations/0014_agent_applications.sql +0 -25
- package/migrations/0015_tools_delegated_from_a_connection.sql +0 -15
- package/migrations/0016_boards_in_the_tree.sql +0 -80
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { upgradeStoredBoard } from "../nodes/board/board.js";
|
|
1
|
+
import { BlockNoteDocument, BlockNoteMediaType } from "@anchrd/intel-contract/node";
|
|
3
2
|
export class PermanentIndexingError extends Error {
|
|
4
3
|
}
|
|
5
|
-
// The key under which a node
|
|
6
|
-
// the bare node id in the index itself, which is why nothing written before anchrd/intel#301 has
|
|
7
|
-
// be renamed or embedded again.
|
|
4
|
+
// The key under which a node with exactly one vector is filed — since #390 that is every kind. It
|
|
5
|
+
// is the bare node id in the index itself, which is why nothing written before anchrd/intel#301 has
|
|
6
|
+
// to be renamed or embedded again.
|
|
8
7
|
const wholeNodeChunkKey = "";
|
|
9
8
|
// What the pass hands to `purgeVectors` at its end: the names it has just written, in case the node
|
|
10
9
|
// was archived while it worked and the record of them was refused (anchrd/intel#348).
|
|
@@ -21,58 +20,6 @@ function indexText(mediaType, content) {
|
|
|
21
20
|
throw new PermanentIndexingError(`Node version content is not a valid BlockNote document: ${error instanceof Error ? error.message : "unknown parse failure"}`);
|
|
22
21
|
}
|
|
23
22
|
}
|
|
24
|
-
/**
|
|
25
|
-
* One passage per task, never one per board (#285) — and since anchrd/intel#301 one VECTOR per task
|
|
26
|
-
* too, which is what `key` carries.
|
|
27
|
-
*
|
|
28
|
-
* ⚠️ This is the whole reason a board is chunked at all. "Where do I stand with X" has to land on a
|
|
29
|
-
* CARD: indexed as one blob, a board of three hundred tasks matches on any of them and answers with
|
|
30
|
-
* whichever thousand characters the snippet function happened to cut — text from a task that may
|
|
31
|
-
* have nothing to do with the question. Each chunk carries the task's own title, which is the
|
|
32
|
-
* column the FTS table weights highest.
|
|
33
|
-
*
|
|
34
|
-
* ⚠️ The status list is not indexed, and neither is a task's own status. "Backlog" and "Done" appear
|
|
35
|
-
* on every board in the installation, so they are the words most likely to match and the least
|
|
36
|
-
* likely to mean anything — and in the semantic half they would drag every card of every board
|
|
37
|
-
* towards every question phrased as a state.
|
|
38
|
-
*
|
|
39
|
-
* ⚠️ A task on the `archived` shelf is left out of BOTH halves (anchrd/intel#301). That shelf is
|
|
40
|
-
* what a board has instead of deleting a card (`ArchivedBoardStatusId`), so it is the same statement
|
|
41
|
-
* `archived_at` makes about a node one level up — and an archived node has never been searchable.
|
|
42
|
-
* Answering "where do I stand with X" out of a card somebody swept away is the failure the ticket
|
|
43
|
-
* names. A card in a `terminal` status is NOT swept: "we finished it" is an answer to where the work
|
|
44
|
-
* stands, and the only thing that says a card no longer counts is the shelf.
|
|
45
|
-
*/
|
|
46
|
-
function boardChunks(content) {
|
|
47
|
-
let parsed;
|
|
48
|
-
try {
|
|
49
|
-
parsed = JSON.parse(content);
|
|
50
|
-
}
|
|
51
|
-
catch (error) {
|
|
52
|
-
throw new PermanentIndexingError(`Node version content is not valid JSON: ${error instanceof Error ? error.message : "unknown parse failure"}`);
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* ⚠️ Upgraded first, exactly like `parseStoredBoard` — this is the SECOND place a stored board
|
|
56
|
-
* body is parsed, and it reads bodies nobody has rewritten since (anchrd/intel#318).
|
|
57
|
-
*
|
|
58
|
-
* `reindex` walks every current version in the installation, so an old board arrives here as it
|
|
59
|
-
* was written: without `terminal` (#311) or with a repeated `dependsOn`. Without the upgrade the
|
|
60
|
-
* refusal below is PERMANENT — the version is marked failed and never retried — so that board
|
|
61
|
-
* would simply stop being findable, quietly, while its screen still worked. A rule added at one
|
|
62
|
-
* parse site and not the other is how a fix looks complete and is not.
|
|
63
|
-
*/
|
|
64
|
-
const board = BoardDocument.safeParse(upgradeStoredBoard(parsed));
|
|
65
|
-
if (!board.success) {
|
|
66
|
-
throw new PermanentIndexingError("Node version content is not a valid board document");
|
|
67
|
-
}
|
|
68
|
-
return board.data.tasks
|
|
69
|
-
.filter((task) => task.status !== ArchivedBoardStatusId)
|
|
70
|
-
.map((task) => ({
|
|
71
|
-
key: task.id,
|
|
72
|
-
title: task.title,
|
|
73
|
-
text: [task.title, ...task.labels, task.description].filter(Boolean).join("\n\n"),
|
|
74
|
-
}));
|
|
75
|
-
}
|
|
76
23
|
/**
|
|
77
24
|
* What one chunk is embedded as (anchrd/intel#301).
|
|
78
25
|
*
|
|
@@ -85,9 +32,9 @@ function boardChunks(content) {
|
|
|
85
32
|
* compare against. That cost is one embedding per node, paid when that node is next saved or when
|
|
86
33
|
* an administrator runs `reindex`, and never per card.
|
|
87
34
|
*
|
|
88
|
-
* ⚠️
|
|
89
|
-
*
|
|
90
|
-
*
|
|
35
|
+
* ⚠️ Only the WHOLE-node chunk is prefixed, and the distinction is what kept a rename cheap while
|
|
36
|
+
* a node could hold many chunks: a per-chunk prefix would have put the node's title into every
|
|
37
|
+
* fingerprint, so renaming it once cost one embedding per chunk.
|
|
91
38
|
*/
|
|
92
39
|
function embeddedText(target, chunk) {
|
|
93
40
|
return chunk.key === wholeNodeChunkKey ? `${target.title}\n\n${chunk.text}` : chunk.text;
|
|
@@ -95,14 +42,13 @@ function embeddedText(target, chunk) {
|
|
|
95
42
|
/**
|
|
96
43
|
* The vectors this node needs, minus the ones it already has (anchrd/intel#301).
|
|
97
44
|
*
|
|
98
|
-
* ⚠️ This is the answer to "
|
|
99
|
-
* document
|
|
100
|
-
*
|
|
101
|
-
* was embedded is what tells an untouched card from a changed one.
|
|
45
|
+
* ⚠️ This is the answer to "an unchanged chunk must not be embedded again". A save writes the
|
|
46
|
+
* whole document, so without this every save would re-embed everything it holds. The fingerprint of
|
|
47
|
+
* the exact text that was embedded is what tells an untouched chunk from a changed one.
|
|
102
48
|
*
|
|
103
49
|
* ⚠️ The comparison is against D1 and deliberately not against Vectorize. Vectorize writes are
|
|
104
50
|
* asynchronous — a vector upserted a moment ago is not readable yet — so a check made there would
|
|
105
|
-
* answer about the save before last and re-embed a
|
|
51
|
+
* answer about the save before last and re-embed a node that had just been saved twice.
|
|
106
52
|
*
|
|
107
53
|
* ⚠️ The record is written only after the upsert returned, in the caller. A pass that dies in
|
|
108
54
|
* between leaves the record short, so the next one embeds again; the opposite order would leave a
|
|
@@ -127,9 +73,9 @@ async function replaceVectors(deps, target, chunks) {
|
|
|
127
73
|
const stale = stored.map((record) => record.chunkKey).filter((key) => !wanted.has(key));
|
|
128
74
|
// ⚠️ The one-time sweep of the vector this node had BEFORE it was chunked. An empty record with a
|
|
129
75
|
// chunked node means this is the first pass since anchrd/intel#301 (or since `reindex` emptied
|
|
130
|
-
// the record), and the whole-
|
|
76
|
+
// the record), and the whole-node vector written under the bare node id is still sitting there —
|
|
131
77
|
// matching questions and answering with the wrong card's passage. Once a record exists this
|
|
132
|
-
// branch is never taken again, so it costs one call per
|
|
78
|
+
// branch is never taken again, so it costs one call per node rather than one per save.
|
|
133
79
|
if (stored.length === 0 && !wanted.has(wholeNodeChunkKey))
|
|
134
80
|
stale.push(wholeNodeChunkKey);
|
|
135
81
|
await semantic.upsert(target, changed);
|
|
@@ -207,22 +153,6 @@ export function createIndexing(deps) {
|
|
|
207
153
|
await purgeVectors(deps, versionId);
|
|
208
154
|
return;
|
|
209
155
|
}
|
|
210
|
-
// ⚠️ An agent's body is a definition, not something to read: node IDs, a cron line and a model
|
|
211
|
-
// name (ADR-0005 §4). Indexing it verbatim would fill the index with identifiers and, worse,
|
|
212
|
-
// put them into the passage a searcher is shown — the definition names nodes the searcher may
|
|
213
|
-
// have no access to, and a snippet is the one place their IDs would become visible without
|
|
214
|
-
// the tree being asked. What makes an agent findable is what a person wrote about it, so the
|
|
215
|
-
// text is its description, and the FTS table indexes the title on its own (#139).
|
|
216
|
-
if (target.kind === "agent") {
|
|
217
|
-
const chunks = [
|
|
218
|
-
{ key: wholeNodeChunkKey, title: target.title, text: target.description ?? "" },
|
|
219
|
-
];
|
|
220
|
-
await deps.repository.replace(target, chunks);
|
|
221
|
-
await replaceVectors(deps, target, chunks);
|
|
222
|
-
await purgeVectors(deps, versionId, writtenKeys(chunks));
|
|
223
|
-
await deps.repository.markIndexed(versionId, deps.now().toISOString());
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
156
|
// An attachment is one object of bytes; text kinds may be several, because a table's content
|
|
227
157
|
// is the join of its versions (#40). A missing segment fails the whole pass rather than
|
|
228
158
|
// indexing a table with a hole in it.
|
|
@@ -240,12 +170,11 @@ export function createIndexing(deps) {
|
|
|
240
170
|
if (text === undefined) {
|
|
241
171
|
throw new PermanentIndexingError(`No document converter is configured for ${target.mediaType}`);
|
|
242
172
|
}
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
: [{ key: wholeNodeChunkKey, title: target.title, text }];
|
|
173
|
+
// ⚠️ One chunk per node since #390, and the LIST is what stays. Both indexes read it, so
|
|
174
|
+
// they cannot come to disagree about what a node is made of — which is the property that
|
|
175
|
+
// let a board be one passage and one vector per card (#285, anchrd/intel#301) without
|
|
176
|
+
// either half being told about it separately.
|
|
177
|
+
const chunks = [{ key: wholeNodeChunkKey, title: target.title, text }];
|
|
249
178
|
await deps.repository.replace(target, chunks);
|
|
250
179
|
await replaceVectors(deps, target, chunks);
|
|
251
180
|
// ⚠️ Asked again, at the END of a pass that started on a live node (anchrd/intel#348). The
|
package/dist/intel/intel.js
CHANGED
|
@@ -72,9 +72,6 @@ export function createIntel(deps) {
|
|
|
72
72
|
flows: deps.flows,
|
|
73
73
|
tools: deps.tools,
|
|
74
74
|
bundle: deps.bundle,
|
|
75
|
-
agents: deps.agents,
|
|
76
|
-
agentCosts: deps.agentCosts,
|
|
77
|
-
models: deps.models,
|
|
78
75
|
resource,
|
|
79
76
|
resourceMetadataUrl,
|
|
80
77
|
...(deps.auth ? { auth: deps.auth } : {}),
|
|
@@ -87,13 +84,11 @@ export function createIntel(deps) {
|
|
|
87
84
|
}
|
|
88
85
|
return await handleMcp(context.req.raw, {
|
|
89
86
|
authorization,
|
|
90
|
-
// ⚠️ The very token this call was authorized with, so
|
|
91
|
-
// caller and not as Intel. There is no cookie path on `/mcp`, so `bearer` is the
|
|
92
|
-
// `authorize` just used — read again rather than passed out of it, because a
|
|
93
|
-
// returned a credential beside its answer would be one somebody logs.
|
|
87
|
+
// ⚠️ The very token this call was authorized with, so a tool that has to speak to Gate does
|
|
88
|
+
// it as the caller and not as Intel. There is no cookie path on `/mcp`, so `bearer` is the
|
|
89
|
+
// same value `authorize` just used — read again rather than passed out of it, because a
|
|
90
|
+
// function that returned a credential beside its answer would be one somebody logs.
|
|
94
91
|
bearer: bearer(context.req.raw.headers) ?? "",
|
|
95
|
-
agents: deps.agents,
|
|
96
|
-
agentCosts: deps.agentCosts,
|
|
97
92
|
flows: deps.flows,
|
|
98
93
|
nodes: deps.nodes,
|
|
99
94
|
tools: deps.tools,
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import type { GateClient } from "@anchrd/gate-sdk";
|
|
2
|
-
import type { AgentCostsService } from "../agent-costs/agent-costs.types.js";
|
|
3
|
-
import type { AgentRuntimeService } from "../agent-runtime/agent-runtime.types.js";
|
|
4
2
|
import type { BrowserAuth } from "../auth/auth.types.js";
|
|
5
3
|
import type { BundleService } from "../bundle/bundle.types.js";
|
|
6
4
|
import type { FlowService } from "../flows/flows.types.js";
|
|
7
|
-
import type { ModelCatalogService } from "../model-catalog/model-catalog.types.js";
|
|
8
5
|
import type { NodeService } from "../nodes/nodes.types.js";
|
|
9
6
|
import type { ToolService } from "../tools/tools.types.js";
|
|
10
7
|
export interface IntelDeps {
|
|
@@ -15,8 +12,5 @@ export interface IntelDeps {
|
|
|
15
12
|
flows: FlowService;
|
|
16
13
|
tools: ToolService;
|
|
17
14
|
bundle: BundleService;
|
|
18
|
-
agents: AgentRuntimeService;
|
|
19
|
-
agentCosts: AgentCostsService;
|
|
20
|
-
models: ModelCatalogService;
|
|
21
15
|
auth?: BrowserAuth;
|
|
22
16
|
}
|