@anchrd/intel-ui 0.8.8 → 0.9.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/README.md +2 -2
- package/package.json +5 -2
- package/src/agent/agent-avatar/agent-avatar.tsx +34 -0
- package/src/agent/agent-calendar/agent-calendar.tsx +79 -0
- package/src/agent/agent-chat/agent-chat.tsx +116 -0
- package/src/agent/agent-cron/agent-cron.ts +132 -0
- package/src/agent/agent-definition/agent-definition.ts +64 -0
- package/src/agent/agent-entry-title/agent-entry-title.ts +29 -0
- package/src/agent/agent-log/agent-log.tsx +159 -0
- package/src/agent/agent-models/agent-models.ts +63 -0
- package/src/agent/agent-profile/agent-profile.tsx +512 -0
- package/src/agent/agent-state/agent-state.ts +48 -0
- package/src/agent/agent.tsx +361 -0
- package/src/app/app-sidebar/app-sidebar.tsx +2 -2
- package/src/app/app-tree/app-tree.tsx +151 -23
- package/src/app/app.tsx +2 -2
- package/src/app/header-search/header-search.tsx +16 -16
- package/src/app/tree-move/tree-move.tsx +10 -10
- package/src/archive/archive.tsx +8 -8
- package/src/components/ui/avatar.tsx +39 -0
- package/src/components/ui/select.tsx +163 -0
- package/src/components/ui/tabs.tsx +52 -0
- package/src/data/agent-runtime/agent-runtime.ts +93 -0
- package/src/data/intel-data-provider/intel-data-provider.ts +201 -120
- package/src/data/intel-data-provider/intel-data-provider.types.ts +112 -54
- package/src/entry-picker/entry-picker.tsx +53 -17
- package/src/flow-runs/flow-runs.tsx +1 -1
- package/src/flows/flows.tsx +20 -20
- package/src/folder-contents/folder-contents.tsx +7 -7
- package/src/graph-pane/graph-pane.tsx +1 -1
- package/src/hooks/use-capabilities.ts +22 -0
- package/src/i18n/en.json +148 -64
- package/src/kind-icon.ts +5 -2
- package/src/{knowledge-editor/knowledge-editor.tsx → node-editor/node-editor.tsx} +20 -20
- package/src/{knowledge-graph → node-graph}/graph-notice.tsx +1 -1
- package/src/{knowledge-graph/knowledge-graph.tsx → node-graph/node-graph.tsx} +4 -4
- package/src/{knowledge-table/knowledge-table.tsx → node-table/node-table.tsx} +14 -14
- package/src/{knowledge/knowledge.tsx → nodes/nodes.tsx} +46 -29
- package/src/resource-menu/resource-menu.tsx +105 -62
- package/src/router/router.tsx +5 -5
- package/src/title-row/title-row.tsx +13 -5
- package/vite.config.ts +4 -0
- /package/src/{knowledge-graph/knowledge-graph.ts → node-graph/node-graph.ts} +0 -0
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
AppendTableRowsInput,
|
|
3
|
+
AppendTableRowsResult,
|
|
4
4
|
ArchiveFlowInput,
|
|
5
|
-
|
|
5
|
+
ArchiveNodeInput,
|
|
6
|
+
BundleImportResult,
|
|
6
7
|
CompleteFlowRunStepInput,
|
|
8
|
+
CreateAgentInput,
|
|
9
|
+
CreatedAgent,
|
|
7
10
|
CreateFlowInput,
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
CreateNodeInput,
|
|
12
|
+
DefineTableInput,
|
|
10
13
|
Flow,
|
|
11
14
|
FlowDocument,
|
|
12
15
|
FlowList,
|
|
@@ -16,40 +19,50 @@ import {
|
|
|
16
19
|
FlowRunList,
|
|
17
20
|
FlowRunStep,
|
|
18
21
|
FlowValidation,
|
|
19
|
-
|
|
20
|
-
KnowledgeGraph,
|
|
21
|
-
KnowledgeLinkList,
|
|
22
|
-
KnowledgeNode,
|
|
23
|
-
KnowledgeNodeList,
|
|
24
|
-
KnowledgeTable,
|
|
25
|
-
KnowledgeVersionList,
|
|
22
|
+
IntelCapabilities,
|
|
26
23
|
ListFlowRunsInput,
|
|
27
24
|
ListFlowsInput,
|
|
28
|
-
|
|
25
|
+
ListNodesInput,
|
|
26
|
+
Node,
|
|
27
|
+
NodeAgent,
|
|
28
|
+
NodeDocument,
|
|
29
|
+
NodeGraph,
|
|
30
|
+
NodeLinkList,
|
|
31
|
+
NodeList,
|
|
32
|
+
NodeTable,
|
|
33
|
+
NodeVersionList,
|
|
29
34
|
PreviewFlowPublishInput,
|
|
30
35
|
ProblemDetails,
|
|
31
36
|
PublishFlowInput,
|
|
32
37
|
RelationGraph,
|
|
33
38
|
RelationGraphInput,
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
ResolveNodeLinksInput,
|
|
40
|
+
ResolveNodeLinksResult,
|
|
36
41
|
ResourceGrantList,
|
|
42
|
+
RevokeGrantInput,
|
|
37
43
|
RevokeGrantResult,
|
|
38
|
-
|
|
44
|
+
SaveAgentDefinitionInput,
|
|
45
|
+
SaveAttachmentInput,
|
|
39
46
|
SaveFlowVersionInput,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
SearchKnowledgeResult,
|
|
47
|
+
SaveNodeVersionInput,
|
|
48
|
+
SearchInput,
|
|
49
|
+
SearchResult,
|
|
44
50
|
SessionUser,
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
ShareInput,
|
|
52
|
+
ShareResult,
|
|
47
53
|
StartFlowRunInput,
|
|
48
54
|
ToolCatalog,
|
|
49
55
|
UpdateFlowInput,
|
|
50
|
-
|
|
56
|
+
UpdateNodeInput,
|
|
51
57
|
} from "@anchrd/intel-contract";
|
|
52
58
|
import type { z } from "zod";
|
|
59
|
+
import {
|
|
60
|
+
AgentManualRun,
|
|
61
|
+
AgentRunDetail,
|
|
62
|
+
AgentRunList,
|
|
63
|
+
AgentState,
|
|
64
|
+
agentRuntimePath,
|
|
65
|
+
} from "@/data/agent-runtime/agent-runtime.ts";
|
|
53
66
|
import { createBrowserSignIn } from "@/data/sign-in/sign-in.ts";
|
|
54
67
|
import type { IntelDataProvider, TreeEntry } from "./intel-data-provider.types.ts";
|
|
55
68
|
|
|
@@ -82,10 +95,10 @@ export function loginPath(returnTo?: string): string {
|
|
|
82
95
|
* copies of this would keep returning something plausible and slowly stop agreeing on what a flow
|
|
83
96
|
* row is (#30).
|
|
84
97
|
*/
|
|
85
|
-
// ⚠️ `openable`
|
|
86
|
-
// (`listTreeChildren`);
|
|
87
|
-
//
|
|
88
|
-
//
|
|
98
|
+
// ⚠️ `openable` defaults to false here — more precisely: unknown. Whoever knows the level sets it
|
|
99
|
+
// (`listTreeChildren`); whoever only builds a single row does not know and therefore claims the
|
|
100
|
+
// more reserved of the two. An arrow that is wrongly missing costs a click on the row; one that
|
|
101
|
+
// is wrongly there costs trust (#59).
|
|
89
102
|
export function flowEntry(flow: Flow, openable = false): TreeEntry {
|
|
90
103
|
return { type: "flow", id: flow.id, title: flow.title, kind: "flow", openable, flow };
|
|
91
104
|
}
|
|
@@ -170,39 +183,37 @@ export function createIntelDataProvider(
|
|
|
170
183
|
return await request(`/flows${search ? `?${search}` : ""}`, FlowList);
|
|
171
184
|
}
|
|
172
185
|
|
|
173
|
-
async function
|
|
174
|
-
|
|
175
|
-
): Promise<z.infer<typeof KnowledgeNodeList>> {
|
|
176
|
-
const query = ListKnowledgeNodesInput.parse(input);
|
|
186
|
+
async function listNodes(input: Partial<ListNodesInput> = {}): Promise<z.infer<typeof NodeList>> {
|
|
187
|
+
const query = ListNodesInput.parse(input);
|
|
177
188
|
const params = new URLSearchParams({ includeArchived: String(query.includeArchived) });
|
|
178
189
|
if (query.parentId !== null) params.set("parentId", query.parentId);
|
|
179
190
|
// Only the asked-for case travels: `archivedOnly` overrides the folder on the server, so
|
|
180
191
|
// sending it as `false` on every tree read would be noise on the one request it never applies
|
|
181
192
|
// to (#113).
|
|
182
193
|
if (query.archivedOnly) params.set("archivedOnly", "true");
|
|
183
|
-
return await request(`/
|
|
194
|
+
return await request(`/nodes?${params}`, NodeList);
|
|
184
195
|
}
|
|
185
196
|
|
|
186
197
|
return {
|
|
187
198
|
async getSession() {
|
|
188
199
|
return await request("/session", SessionUser);
|
|
189
200
|
},
|
|
190
|
-
|
|
201
|
+
async getCapabilities() {
|
|
202
|
+
return await request("/capabilities", IntelCapabilities);
|
|
203
|
+
},
|
|
204
|
+
listNodes,
|
|
191
205
|
async listTreeChildren(parentId) {
|
|
192
206
|
// Both sides of one folder, asked for in parallel and merged here rather than on the server:
|
|
193
207
|
// the folder is shared, the two records are not, and no endpoint may return a row that is a
|
|
194
208
|
// bit of both (ADR-0004).
|
|
195
|
-
const [
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
// Leser. Der Baum weiß es damit beim Zeichnen, ohne die Ebene darunter vorzuladen: Das wäre
|
|
201
|
-
// ein Ladevorgang pro Zeile statt pro Ordner.
|
|
202
|
-
const openable = new Set([...knowledge.withChildren, ...flows.withCalls]);
|
|
209
|
+
const [nodes, flows] = await Promise.all([listNodes({ parentId }), listFlows({ parentId })]);
|
|
210
|
+
// ⚠️ Both sides say which of their rows have something to expand (#59) — for THIS reader.
|
|
211
|
+
// The tree therefore knows it while drawing, without preloading the level below: that would
|
|
212
|
+
// be one load per row instead of per folder.
|
|
213
|
+
const openable = new Set([...nodes.withChildren, ...flows.withCalls]);
|
|
203
214
|
const entries: TreeEntry[] = [
|
|
204
|
-
...
|
|
205
|
-
type: "
|
|
215
|
+
...nodes.items.map((node) => ({
|
|
216
|
+
type: "node" as const,
|
|
206
217
|
id: node.id,
|
|
207
218
|
title: node.title,
|
|
208
219
|
kind: node.kind,
|
|
@@ -220,115 +231,125 @@ export function createIntelDataProvider(
|
|
|
220
231
|
left.id.localeCompare(right.id),
|
|
221
232
|
);
|
|
222
233
|
},
|
|
223
|
-
async
|
|
224
|
-
return await request(`/
|
|
234
|
+
async getNode(nodeId) {
|
|
235
|
+
return await request(`/nodes/${encodeURIComponent(nodeId)}`, NodeDocument);
|
|
225
236
|
},
|
|
226
|
-
async
|
|
227
|
-
return await request("/
|
|
237
|
+
async createNodes(input) {
|
|
238
|
+
return await request("/nodes", Node, {
|
|
228
239
|
method: "POST",
|
|
229
|
-
body: JSON.stringify(
|
|
240
|
+
body: JSON.stringify(CreateNodeInput.parse(input)),
|
|
230
241
|
});
|
|
231
242
|
},
|
|
232
|
-
async
|
|
233
|
-
return await request(`/
|
|
243
|
+
async getNodeGraph(limit = 250) {
|
|
244
|
+
return await request(`/nodes/graph?limit=${encodeURIComponent(limit)}`, NodeGraph);
|
|
234
245
|
},
|
|
235
|
-
async
|
|
236
|
-
return await request(`/
|
|
246
|
+
async listNodeLinks(nodeId) {
|
|
247
|
+
return await request(`/nodes/${encodeURIComponent(nodeId)}/links`, NodeLinkList);
|
|
237
248
|
},
|
|
238
249
|
// ⚠️ POST, not GET. The identifiers of the documents a text links to belong in a body: a
|
|
239
250
|
// query string of them would end up in logs and referrers.
|
|
240
|
-
async
|
|
241
|
-
return await request("/
|
|
251
|
+
async resolveNodeLinks(input) {
|
|
252
|
+
return await request("/nodes/links/resolve", ResolveNodeLinksResult, {
|
|
242
253
|
method: "POST",
|
|
243
|
-
body: JSON.stringify(
|
|
254
|
+
body: JSON.stringify(ResolveNodeLinksInput.parse(input)),
|
|
244
255
|
});
|
|
245
256
|
},
|
|
246
|
-
async
|
|
247
|
-
const parsed =
|
|
248
|
-
return await request(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
);
|
|
257
|
+
async saveNode(input) {
|
|
258
|
+
const parsed = SaveNodeVersionInput.parse(input);
|
|
259
|
+
return await request(`/nodes/${encodeURIComponent(parsed.nodeId)}/versions`, NodeDocument, {
|
|
260
|
+
method: "POST",
|
|
261
|
+
body: JSON.stringify(parsed),
|
|
262
|
+
});
|
|
253
263
|
},
|
|
254
|
-
async
|
|
255
|
-
const parsed =
|
|
256
|
-
return await request(
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
);
|
|
264
|
+
async saveNodeAttachment(input) {
|
|
265
|
+
const parsed = SaveAttachmentInput.parse(input);
|
|
266
|
+
return await request(`/nodes/${encodeURIComponent(parsed.nodeId)}/attachment`, NodeDocument, {
|
|
267
|
+
method: "POST",
|
|
268
|
+
body: JSON.stringify(parsed),
|
|
269
|
+
});
|
|
261
270
|
},
|
|
262
|
-
async
|
|
263
|
-
return await (await response(`/
|
|
271
|
+
async getNodeAttachment(nodeId) {
|
|
272
|
+
return await (await response(`/nodes/${encodeURIComponent(nodeId)}/attachment`)).blob();
|
|
273
|
+
},
|
|
274
|
+
async exportNodeBundle(nodeId) {
|
|
275
|
+
const path =
|
|
276
|
+
nodeId === null ? "/nodes/export" : `/nodes/${encodeURIComponent(nodeId)}/export`;
|
|
277
|
+
return await (await response(path)).blob();
|
|
278
|
+
},
|
|
279
|
+
async exportFlowBundle(flowId) {
|
|
280
|
+
return await (await response(`/flows/${encodeURIComponent(flowId)}/export`)).blob();
|
|
281
|
+
},
|
|
282
|
+
// The body IS the zip, so the idempotency key travels as a header — the same reading the
|
|
283
|
+
// server does (#137).
|
|
284
|
+
async importNodeBundle(input) {
|
|
285
|
+
const path =
|
|
286
|
+
input.nodeId === null
|
|
287
|
+
? "/nodes/import"
|
|
288
|
+
: `/nodes/${encodeURIComponent(input.nodeId)}/import`;
|
|
289
|
+
return await request(path, BundleImportResult, {
|
|
290
|
+
method: "POST",
|
|
291
|
+
body: input.zip,
|
|
292
|
+
headers: {
|
|
293
|
+
"content-type": "application/zip",
|
|
294
|
+
"idempotency-key": input.idempotencyKey,
|
|
295
|
+
},
|
|
296
|
+
});
|
|
264
297
|
},
|
|
265
|
-
async
|
|
266
|
-
return await request(`/
|
|
298
|
+
async getNodeTable(nodeId) {
|
|
299
|
+
return await request(`/nodes/${encodeURIComponent(nodeId)}/table`, NodeTable);
|
|
267
300
|
},
|
|
268
|
-
async
|
|
269
|
-
const parsed =
|
|
270
|
-
return await request(
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
method: "POST",
|
|
275
|
-
body: JSON.stringify(parsed),
|
|
276
|
-
},
|
|
277
|
-
);
|
|
301
|
+
async defineTable(input) {
|
|
302
|
+
const parsed = DefineTableInput.parse(input);
|
|
303
|
+
return await request(`/nodes/${encodeURIComponent(parsed.nodeId)}/table`, NodeTable, {
|
|
304
|
+
method: "POST",
|
|
305
|
+
body: JSON.stringify(parsed),
|
|
306
|
+
});
|
|
278
307
|
},
|
|
279
|
-
async
|
|
280
|
-
const parsed =
|
|
308
|
+
async appendTableRows(input) {
|
|
309
|
+
const parsed = AppendTableRowsInput.parse(input);
|
|
281
310
|
return await request(
|
|
282
|
-
`/
|
|
283
|
-
|
|
311
|
+
`/nodes/${encodeURIComponent(parsed.nodeId)}/table/rows`,
|
|
312
|
+
AppendTableRowsResult,
|
|
284
313
|
{ method: "POST", body: JSON.stringify(parsed) },
|
|
285
314
|
);
|
|
286
315
|
},
|
|
287
|
-
async
|
|
288
|
-
return await request(
|
|
289
|
-
`/knowledge/${encodeURIComponent(nodeId)}/versions`,
|
|
290
|
-
KnowledgeVersionList,
|
|
291
|
-
);
|
|
316
|
+
async listNodeVersions(nodeId) {
|
|
317
|
+
return await request(`/nodes/${encodeURIComponent(nodeId)}/versions`, NodeVersionList);
|
|
292
318
|
},
|
|
293
|
-
async
|
|
294
|
-
const parsed =
|
|
295
|
-
return await request(`/
|
|
319
|
+
async updateNode(input) {
|
|
320
|
+
const parsed = UpdateNodeInput.parse(input);
|
|
321
|
+
return await request(`/nodes/${encodeURIComponent(parsed.nodeId)}`, Node, {
|
|
296
322
|
method: "PATCH",
|
|
297
323
|
body: JSON.stringify(parsed),
|
|
298
324
|
});
|
|
299
325
|
},
|
|
300
|
-
async
|
|
301
|
-
const parsed =
|
|
302
|
-
return await request(
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
);
|
|
326
|
+
async archiveNode(input) {
|
|
327
|
+
const parsed = ArchiveNodeInput.parse(input);
|
|
328
|
+
return await request(`/nodes/${encodeURIComponent(parsed.nodeId)}/archive`, Node, {
|
|
329
|
+
method: "POST",
|
|
330
|
+
body: JSON.stringify(parsed),
|
|
331
|
+
});
|
|
307
332
|
},
|
|
308
|
-
async
|
|
309
|
-
return await request("/
|
|
333
|
+
async searchNodes(input) {
|
|
334
|
+
return await request("/nodes/search", SearchResult, {
|
|
310
335
|
method: "POST",
|
|
311
|
-
body: JSON.stringify(
|
|
336
|
+
body: JSON.stringify(SearchInput.parse(input)),
|
|
312
337
|
});
|
|
313
338
|
},
|
|
314
|
-
async
|
|
315
|
-
return await request(
|
|
316
|
-
`/knowledge/${encodeURIComponent(resourceId)}/grants`,
|
|
317
|
-
ResourceGrantList,
|
|
318
|
-
);
|
|
339
|
+
async listGrants(resourceId) {
|
|
340
|
+
return await request(`/nodes/${encodeURIComponent(resourceId)}/grants`, ResourceGrantList);
|
|
319
341
|
},
|
|
320
|
-
async
|
|
321
|
-
const parsed =
|
|
322
|
-
return await request(
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
);
|
|
342
|
+
async shareNode(input) {
|
|
343
|
+
const parsed = ShareInput.parse(input);
|
|
344
|
+
return await request(`/nodes/${encodeURIComponent(parsed.resourceId)}/grants`, ShareResult, {
|
|
345
|
+
method: "POST",
|
|
346
|
+
body: JSON.stringify(parsed),
|
|
347
|
+
});
|
|
327
348
|
},
|
|
328
|
-
async
|
|
329
|
-
const parsed =
|
|
349
|
+
async revokeGrant(input) {
|
|
350
|
+
const parsed = RevokeGrantInput.parse(input);
|
|
330
351
|
return await request(
|
|
331
|
-
`/
|
|
352
|
+
`/nodes/${encodeURIComponent(parsed.resourceId)}/grants/${encodeURIComponent(parsed.grantId)}/revoke`,
|
|
332
353
|
RevokeGrantResult,
|
|
333
354
|
{ method: "POST", body: JSON.stringify(parsed) },
|
|
334
355
|
);
|
|
@@ -440,6 +461,66 @@ export function createIntelDataProvider(
|
|
|
440
461
|
async listTools() {
|
|
441
462
|
return await request("/tools", ToolCatalog);
|
|
442
463
|
},
|
|
464
|
+
async getAgent(nodeId) {
|
|
465
|
+
return await request(`/nodes/${encodeURIComponent(nodeId)}/agent`, NodeAgent);
|
|
466
|
+
},
|
|
467
|
+
// ⚠️ `CreatedAgent`, not `NodeAgent`: the create answer carries the Gate application key once
|
|
468
|
+
// (#182), and the read schema is strict — parsing this response with it would reject the one
|
|
469
|
+
// response in the product that has a credential in it.
|
|
470
|
+
async createAgent(input) {
|
|
471
|
+
return await request("/nodes/agents", CreatedAgent, {
|
|
472
|
+
method: "POST",
|
|
473
|
+
body: JSON.stringify(CreateAgentInput.parse(input)),
|
|
474
|
+
});
|
|
475
|
+
},
|
|
476
|
+
async saveAgentDefinition(input) {
|
|
477
|
+
const parsed = SaveAgentDefinitionInput.parse(input);
|
|
478
|
+
return await request(`/nodes/${encodeURIComponent(parsed.nodeId)}/agent`, NodeAgent, {
|
|
479
|
+
method: "POST",
|
|
480
|
+
body: JSON.stringify(parsed),
|
|
481
|
+
});
|
|
482
|
+
},
|
|
483
|
+
/**
|
|
484
|
+
* The agent runtime, asked through intel (#178).
|
|
485
|
+
*
|
|
486
|
+
* ⚠️ Plain `request`, with no second origin and no bearer of its own — that is the whole change.
|
|
487
|
+
* The runtime used to be a Worker this page called directly, which meant CORS it does not
|
|
488
|
+
* answer and a Gate token the browser cannot have. Intel now proxies `/agents/:agentId/*` and
|
|
489
|
+
* attaches the caller's own token server-side, so a 401 from here really is intel's session
|
|
490
|
+
* expiring and the sign-in that follows is the right one.
|
|
491
|
+
*
|
|
492
|
+
* ⚠️ A 403 is NOT that. It means the reader may not drive agents, which the agent page renders
|
|
493
|
+
* as an answer rather than as a failure.
|
|
494
|
+
*/
|
|
495
|
+
async listAgentRuns(agentId) {
|
|
496
|
+
return await request(agentRuntimePath(agentId, "/runs"), AgentRunList);
|
|
497
|
+
},
|
|
498
|
+
async getAgentRun(agentId, runId) {
|
|
499
|
+
return await request(
|
|
500
|
+
agentRuntimePath(agentId, `/runs/${encodeURIComponent(runId)}`),
|
|
501
|
+
AgentRunDetail,
|
|
502
|
+
);
|
|
503
|
+
},
|
|
504
|
+
async getAgentState(agentId) {
|
|
505
|
+
return await request(agentRuntimePath(agentId, "/state"), AgentState);
|
|
506
|
+
},
|
|
507
|
+
async pauseAgent(agentId) {
|
|
508
|
+
return await request(agentRuntimePath(agentId, "/pause"), AgentState, { method: "POST" });
|
|
509
|
+
},
|
|
510
|
+
async resumeAgent(agentId) {
|
|
511
|
+
return await request(agentRuntimePath(agentId, "/resume"), AgentState, { method: "POST" });
|
|
512
|
+
},
|
|
513
|
+
async runAgentNow(agentId, target) {
|
|
514
|
+
return await request(agentRuntimePath(agentId, "/run"), AgentManualRun, {
|
|
515
|
+
method: "POST",
|
|
516
|
+
body: JSON.stringify({ target }),
|
|
517
|
+
});
|
|
518
|
+
},
|
|
519
|
+
agentChatApi(agentId) {
|
|
520
|
+
// assistant-ui dials this itself, so it needs the path a browser can use — the same one every
|
|
521
|
+
// other call above goes to, intel's own `/api/v1`.
|
|
522
|
+
return `${baseUrl}/api/v1${agentRuntimePath(agentId, "/chat")}`;
|
|
523
|
+
},
|
|
443
524
|
|
|
444
525
|
// The Worker serves the connect route, so this navigation belongs to the data layer for the
|
|
445
526
|
// same reason `loginPath` does. `silent=1` is what makes it a redirect nobody has to watch.
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
AgentScheduleTarget,
|
|
3
|
+
AppendTableRowsInput,
|
|
4
|
+
AppendTableRowsResult,
|
|
4
5
|
ArchiveFlowInput,
|
|
5
|
-
|
|
6
|
+
ArchiveNodeInput,
|
|
7
|
+
BundleImportResult,
|
|
6
8
|
CompleteFlowRunStepInput,
|
|
9
|
+
CreateAgentInput,
|
|
10
|
+
CreatedAgent,
|
|
7
11
|
CreateFlowInput,
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
CreateNodeInput,
|
|
13
|
+
DefineTableInput,
|
|
10
14
|
Flow,
|
|
11
15
|
FlowDocument,
|
|
12
16
|
FlowList,
|
|
@@ -16,92 +20,114 @@ import type {
|
|
|
16
20
|
FlowRunList,
|
|
17
21
|
FlowRunStep,
|
|
18
22
|
FlowValidation,
|
|
19
|
-
|
|
20
|
-
KnowledgeGraph,
|
|
21
|
-
KnowledgeLinkList,
|
|
22
|
-
KnowledgeNode,
|
|
23
|
-
KnowledgeNodeKind,
|
|
24
|
-
KnowledgeNodeList,
|
|
25
|
-
KnowledgeTable,
|
|
26
|
-
KnowledgeVersionList,
|
|
23
|
+
IntelCapabilities,
|
|
27
24
|
ListFlowRunsInput,
|
|
28
25
|
ListFlowsInput,
|
|
29
|
-
|
|
26
|
+
ListNodesInput,
|
|
27
|
+
Node,
|
|
28
|
+
NodeAgent,
|
|
29
|
+
NodeDocument,
|
|
30
|
+
NodeGraph,
|
|
31
|
+
NodeKind,
|
|
32
|
+
NodeLinkList,
|
|
33
|
+
NodeList,
|
|
34
|
+
NodeTable,
|
|
35
|
+
NodeVersionList,
|
|
30
36
|
PreviewFlowPublishInput,
|
|
31
37
|
PublishFlowInput,
|
|
32
38
|
RelationGraph,
|
|
33
39
|
RelationGraphScope,
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
ResolveNodeLinksInput,
|
|
41
|
+
ResolveNodeLinksResult,
|
|
36
42
|
ResourceGrantList,
|
|
43
|
+
RevokeGrantInput,
|
|
37
44
|
RevokeGrantResult,
|
|
38
|
-
|
|
45
|
+
SaveAgentDefinitionInput,
|
|
46
|
+
SaveAttachmentInput,
|
|
39
47
|
SaveFlowVersionInput,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
SearchKnowledgeResult,
|
|
48
|
+
SaveNodeVersionInput,
|
|
49
|
+
SearchInput,
|
|
50
|
+
SearchResult,
|
|
44
51
|
SessionUser,
|
|
45
|
-
|
|
46
|
-
|
|
52
|
+
ShareInput,
|
|
53
|
+
ShareResult,
|
|
47
54
|
StartFlowRunInput,
|
|
48
55
|
ToolCatalog,
|
|
49
56
|
UpdateFlowInput,
|
|
50
|
-
|
|
57
|
+
UpdateNodeInput,
|
|
51
58
|
} from "@anchrd/intel-contract";
|
|
59
|
+
import type {
|
|
60
|
+
AgentManualRun,
|
|
61
|
+
AgentRunDetail,
|
|
62
|
+
AgentRunList,
|
|
63
|
+
AgentState,
|
|
64
|
+
} from "@/data/agent-runtime/agent-runtime.ts";
|
|
52
65
|
|
|
53
|
-
// One row of the shared tree.
|
|
66
|
+
// One row of the shared tree. Nodes and Flows share the folder, not their nature (ADR-0004), so
|
|
54
67
|
// this is a union that keeps each side's record whole — never a merged "node" that is a bit of both.
|
|
55
|
-
// ⚠️ `openable`
|
|
56
|
-
//
|
|
57
|
-
//
|
|
68
|
+
// ⚠️ `openable` means "has something to expand that THIS reader may see" (#59) — not "is of a kind
|
|
69
|
+
// that can be expanded". The difference is the whole point: an arrow on a row with nothing beneath
|
|
70
|
+
// it promises content that expanding does not deliver.
|
|
58
71
|
export type TreeEntry =
|
|
59
72
|
| {
|
|
60
|
-
type: "
|
|
73
|
+
type: "node";
|
|
61
74
|
id: string;
|
|
62
75
|
title: string;
|
|
63
|
-
kind:
|
|
76
|
+
kind: NodeKind;
|
|
64
77
|
openable: boolean;
|
|
65
|
-
node:
|
|
78
|
+
node: Node;
|
|
66
79
|
}
|
|
67
80
|
| { type: "flow"; id: string; title: string; kind: "flow"; openable: boolean; flow: Flow };
|
|
68
81
|
|
|
69
82
|
export interface IntelDataProvider {
|
|
70
83
|
getSession(): Promise<SessionUser>;
|
|
71
|
-
|
|
84
|
+
// What this installation is equipped to do (#190) — deployment facts, not the caller's
|
|
85
|
+
// permissions. `/session` stays identity only on purpose; this is the separate question.
|
|
86
|
+
getCapabilities(): Promise<IntelCapabilities>;
|
|
87
|
+
listNodes(input?: Partial<ListNodesInput>): Promise<NodeList>;
|
|
72
88
|
// One level of the shared tree: the documents and the flows filed in the same folder, in one
|
|
73
89
|
// sorted list. Per level rather than recursive, so opening a folder is what costs a request.
|
|
74
90
|
listTreeChildren(parentId: string | null): Promise<TreeEntry[]>;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
91
|
+
getNode(nodeId: string): Promise<NodeDocument>;
|
|
92
|
+
createNodes(input: CreateNodeInput): Promise<Node>;
|
|
93
|
+
getNodeGraph(limit?: number): Promise<NodeGraph>;
|
|
94
|
+
listNodeLinks(nodeId: string): Promise<NodeLinkList>;
|
|
79
95
|
// The titles of the documents a text links to, for the reader. A target they may not see, or one
|
|
80
96
|
// that is gone, is absent from the answer — the two look the same on purpose (#41).
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
//
|
|
97
|
+
resolveNodeLinks(input: ResolveNodeLinksInput): Promise<ResolveNodeLinksResult>;
|
|
98
|
+
saveNode(input: SaveNodeVersionInput): Promise<NodeDocument>;
|
|
99
|
+
saveNodeAttachment(input: SaveAttachmentInput): Promise<NodeDocument>;
|
|
100
|
+
getNodeAttachment(nodeId: string): Promise<Blob>;
|
|
101
|
+
// One subtree — or one single node — as the zip bundle the server streams (#136). `null` is the
|
|
102
|
+
// root: the whole installation as this caller may read it.
|
|
103
|
+
exportNodeBundle(nodeId: string | null): Promise<Blob>;
|
|
104
|
+
// The same bundle shape for a single flow, through the flow's own export route.
|
|
105
|
+
exportFlowBundle(flowId: string): Promise<Blob>;
|
|
106
|
+
// A zip bundle — or a folder zipped on the client — as a new subtree under the target folder
|
|
107
|
+
// (#137). `nodeId` null files at the root. Always new nodes; the server is all-or-nothing.
|
|
108
|
+
importNodeBundle(input: {
|
|
109
|
+
nodeId: string | null;
|
|
110
|
+
zip: Blob;
|
|
111
|
+
idempotencyKey: string;
|
|
112
|
+
}): Promise<BundleImportResult>;
|
|
113
|
+
// A table as columns and rows. The CSV stays canonical — `getNode` still answers with it,
|
|
86
114
|
// which is what the download uses — and this is the same bytes read by the server's one reader.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
input: AppendKnowledgeTableRowsInput,
|
|
91
|
-
): Promise<AppendKnowledgeTableRowsResult>;
|
|
115
|
+
getNodeTable(nodeId: string): Promise<NodeTable>;
|
|
116
|
+
defineTable(input: DefineTableInput): Promise<NodeTable>;
|
|
117
|
+
appendTableRows(input: AppendTableRowsInput): Promise<AppendTableRowsResult>;
|
|
92
118
|
// Walks the browser through the portal's OAuth flow without asking anybody anything: Gate is the
|
|
93
119
|
// identity provider Cloudflare Access consumes, so a signed-in person is already known there
|
|
94
120
|
// (#60). It navigates away — the caller renders no button for it and gets no answer back.
|
|
95
121
|
startPortalSignIn(returnTo?: string): void;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
122
|
+
listNodeVersions(nodeId: string): Promise<NodeVersionList>;
|
|
123
|
+
updateNode(input: UpdateNodeInput): Promise<Node>;
|
|
124
|
+
archiveNode(input: ArchiveNodeInput): Promise<Node>;
|
|
125
|
+
searchNodes(input: SearchInput): Promise<SearchResult>;
|
|
126
|
+
listGrants(resourceId: string): Promise<ResourceGrantList>;
|
|
101
127
|
// The grant, and what the grant does not cover: the documents the flows in this folder read that
|
|
102
128
|
// the new principal still cannot. A warning, never a refusal (ADR-0004 §4).
|
|
103
|
-
|
|
104
|
-
|
|
129
|
+
shareNode(input: ShareInput): Promise<ShareResult>;
|
|
130
|
+
revokeGrant(input: RevokeGrantInput): Promise<RevokeGrantResult>;
|
|
105
131
|
listFlows(input?: ListFlowsInput): Promise<FlowList>;
|
|
106
132
|
getFlow(flowId: string): Promise<FlowDocument>;
|
|
107
133
|
// What a flow calls, read out of its graph. It answers a different question from `listTreeChildren`
|
|
@@ -128,7 +154,7 @@ export interface IntelDataProvider {
|
|
|
128
154
|
publishFlow(input: PublishFlowInput): Promise<Flow>;
|
|
129
155
|
startFlow(input: StartFlowRunInput): Promise<FlowRunStep>;
|
|
130
156
|
// What this flow has done, newest first, one page at a time. ⚠️ It carries what a run did and
|
|
131
|
-
// never what it produced: a run reads
|
|
157
|
+
// never what it produced: a run reads nodes with the rights of whoever started it, so its
|
|
132
158
|
// result is not automatically readable for everyone who may read the flow.
|
|
133
159
|
listFlowRuns(input: ListFlowRunsInput): Promise<FlowRunList>;
|
|
134
160
|
getFlowRun(runId: string): Promise<FlowRunStep>;
|
|
@@ -138,5 +164,37 @@ export interface IntelDataProvider {
|
|
|
138
164
|
// Reading the catalog is the whole of the tool surface here: calling a tool belongs to a flow
|
|
139
165
|
// or to the Intel MCP surface, not to the screen that shows what the portal offers.
|
|
140
166
|
listTools(): Promise<ToolCatalog>;
|
|
167
|
+
|
|
168
|
+
// An agent node and the definition its current version carries. `definition` is `null` in the one
|
|
169
|
+
// window where the node exists and no version has been written — the same window a document's
|
|
170
|
+
// content is `null` in.
|
|
171
|
+
getAgent(nodeId: string): Promise<NodeAgent>;
|
|
172
|
+
// ⚠️ `CreatedAgent`: the create answer carries the Gate application key ONCE (#182). Nothing in
|
|
173
|
+
// the UI stores or displays it yet — showing it is its own ticket — but the type has to be honest
|
|
174
|
+
// about what comes back, or the strict read schema would reject the response.
|
|
175
|
+
createAgent(input: CreateAgentInput): Promise<CreatedAgent>;
|
|
176
|
+
// ⚠️ Every change to references, schedules or the model writes a NEW definition version; nothing
|
|
177
|
+
// is edited in place (ADR-0005). `baseVersionId` is the version the editor read, so a parallel
|
|
178
|
+
// change is refused rather than silently overwritten.
|
|
179
|
+
saveAgentDefinition(input: SaveAgentDefinitionInput): Promise<NodeAgent>;
|
|
180
|
+
|
|
181
|
+
// ⚠️ The seven below are answered by the agent RUNTIME, a separate Worker — but they are asked of
|
|
182
|
+
// intel, which proxies them with the caller's own Gate token (#178). One origin, one cookie, no
|
|
183
|
+
// CORS. What that costs is that a 403 here means "you may not drive agents", which is a state the
|
|
184
|
+
// agent page renders as an answer rather than as a failure.
|
|
185
|
+
listAgentRuns(agentId: string): Promise<AgentRunList>;
|
|
186
|
+
getAgentRun(agentId: string, runId: string): Promise<AgentRunDetail>;
|
|
187
|
+
// Whether the agent is switched off. Runtime state, deliberately not part of the definition: a
|
|
188
|
+
// definition is versioned, shared and read into model context (#179).
|
|
189
|
+
getAgentState(agentId: string): Promise<AgentState>;
|
|
190
|
+
pauseAgent(agentId: string): Promise<AgentState>;
|
|
191
|
+
resumeAgent(agentId: string): Promise<AgentState>;
|
|
192
|
+
// Fires one of the agent's own schedules now. The target must be one it already schedules — a run
|
|
193
|
+
// acts with the AGENT's grants, so naming any node here would be borrowing them.
|
|
194
|
+
runAgentNow(agentId: string, target: AgentScheduleTarget): Promise<AgentManualRun>;
|
|
195
|
+
// Where assistant-ui posts. A URL rather than a method: the chat is an SSE stream the transport
|
|
196
|
+
// owns end to end, and handing it a string is the whole of what it needs from this layer.
|
|
197
|
+
agentChatApi(agentId: string): string;
|
|
198
|
+
|
|
141
199
|
logout(): Promise<void>;
|
|
142
200
|
}
|