@m6d/cortex-server 2.0.1 → 2.2.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 +24 -0
- package/contracts/README.md +22 -8
- package/contracts/src/client-tools/index.ts +56 -0
- package/contracts/src/graph/index.ts +30 -0
- package/contracts/{runtime.ts → src/runtime/index.ts} +18 -0
- package/contracts/{wire.ts → src/wire/index.ts} +44 -0
- package/dist/contracts/{graph.d.ts → src/graph/index.d.ts} +8 -8
- package/dist/contracts/{runtime.d.ts → src/runtime/index.d.ts} +47 -0
- package/dist/contracts/{wire.d.ts → src/wire/index.d.ts} +40 -0
- package/dist/src/lib/adapters/database/message-content.d.ts +1 -0
- package/dist/src/lib/ai/cc-runtime.d.ts +2 -1
- package/dist/src/lib/ai/client-tools.d.ts +84 -0
- package/dist/src/lib/ai/tools/query-graph.tool.d.ts +1 -1
- package/dist/src/lib/ai/turn-tools.d.ts +19 -3
- package/dist/src/lib/cc/client.d.ts +18 -0
- package/dist/src/lib/cc/registry.d.ts +14 -1
- package/dist/src/lib/cc/types.d.ts +1 -1
- package/dist/src/lib/config.d.ts +9 -4
- package/dist/src/lib/graph/index.d.ts +1 -1
- package/dist/src/lib/graph/resolver.d.ts +1 -1
- package/dist/src/lib/index.d.ts +2 -1
- package/dist/src/lib/types.d.ts +2 -2
- package/dist/src/lib/ws/connections.d.ts +1 -1
- package/package.json +6 -3
- package/src/lib/ai/cc-runtime.ts +16 -1
- package/src/lib/ai/client-tools.ts +288 -0
- package/src/lib/ai/index.ts +72 -30
- package/src/lib/ai/tools/search-tools.tool.ts +6 -2
- package/src/lib/ai/turn-tools.ts +23 -1
- package/src/lib/cc/client.ts +5 -1
- package/src/lib/cc/format.ts +5 -2
- package/src/lib/cc/registry.ts +16 -2
- package/src/lib/cc/types.ts +1 -0
- package/src/lib/config.ts +8 -3
- package/src/lib/index.ts +4 -0
- package/src/lib/routes/chat.ts +23 -0
- package/tsconfig.json +1 -1
- package/contracts/graph.ts +0 -36
- /package/contracts/{graph → src/graph/clients}/embed.ts +0 -0
- /package/contracts/{graph → src/graph/clients}/neo4j.ts +0 -0
- /package/contracts/{graph → src/graph}/helpers.ts +0 -0
- /package/contracts/{graph → src/graph}/schema.ts +0 -0
- /package/contracts/{graph → src/graph}/types.ts +0 -0
- /package/contracts/{rich-text.ts → src/rich-text/index.ts} +0 -0
- /package/dist/contracts/{graph → src/graph/clients}/embed.d.ts +0 -0
- /package/dist/contracts/{graph → src/graph/clients}/neo4j.d.ts +0 -0
- /package/dist/contracts/{graph → src/graph}/helpers.d.ts +0 -0
- /package/dist/contracts/{graph → src/graph}/schema.d.ts +0 -0
- /package/dist/contracts/{graph → src/graph}/types.d.ts +0 -0
- /package/dist/contracts/{rich-text.d.ts → src/rich-text/index.d.ts} +0 -0
package/src/lib/ai/turn-tools.ts
CHANGED
|
@@ -29,7 +29,9 @@ type TurnToolsOptions = {
|
|
|
29
29
|
* configured, then the agent's own tools.
|
|
30
30
|
*
|
|
31
31
|
* User tools are appended last and deliberately win on name collision — an agent
|
|
32
|
-
* must be able to replace a built-in it doesn't want.
|
|
32
|
+
* must be able to replace a built-in it doesn't want. A user tool without an
|
|
33
|
+
* `execute` function is a static client tool: it passes through to the model
|
|
34
|
+
* declaration untouched, and the runtime streams its calls to the widget.
|
|
33
35
|
*/
|
|
34
36
|
export function buildTurnTools(options: TurnToolsOptions) {
|
|
35
37
|
const { config, cc, neo4j, thread, userId, token, session, requestContext, threadAttachments } =
|
|
@@ -110,6 +112,26 @@ export function createToolInstrumentation(
|
|
|
110
112
|
} satisfies ChatMiddleware;
|
|
111
113
|
}
|
|
112
114
|
|
|
115
|
+
/**
|
|
116
|
+
* CC client tools are declared like the request's client tools: no
|
|
117
|
+
* executor, so the call streams to the widget as a tool-call part and the run
|
|
118
|
+
* parks until the widget answers. The declaration carries the tool's published
|
|
119
|
+
* input schema — function-calling models ignore schemas described only in
|
|
120
|
+
* prose — with a permissive object as the fallback for older Control Centers.
|
|
121
|
+
* Names the consumer already claimed are skipped: those calls belong to the
|
|
122
|
+
* consumer's tool, and must neither be declared nor stamped as client tools.
|
|
123
|
+
*/
|
|
124
|
+
export function clientToolDeclarations(cc: CcRuntime | undefined, takenNames: ReadonlySet<string>) {
|
|
125
|
+
if (!cc) return [];
|
|
126
|
+
return [...cc.clientTools]
|
|
127
|
+
.filter(([name]) => !takenNames.has(name))
|
|
128
|
+
.map(([name, tool]) => ({
|
|
129
|
+
name,
|
|
130
|
+
description: tool.signature,
|
|
131
|
+
parameters: tool.inputSchema ?? { type: "object", additionalProperties: true },
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
|
|
113
135
|
function createCcTools(cc: CcRuntime | undefined) {
|
|
114
136
|
if (!cc) return [];
|
|
115
137
|
|
package/src/lib/cc/client.ts
CHANGED
|
@@ -21,6 +21,9 @@ export type ExecuteOptions = {
|
|
|
21
21
|
turnKey: string;
|
|
22
22
|
stepIndex: number;
|
|
23
23
|
callIndex: number;
|
|
24
|
+
/** Overrides the composed key — used where stability must outlive the turn
|
|
25
|
+
* counters, e.g. one initiate per tool call across reloads. */
|
|
26
|
+
idempotencyKey?: string;
|
|
24
27
|
timeoutMs?: number;
|
|
25
28
|
abortSignal?: AbortSignal;
|
|
26
29
|
};
|
|
@@ -138,7 +141,8 @@ export class ControlCenterClient {
|
|
|
138
141
|
if (!options.readOnly) {
|
|
139
142
|
headers.set(
|
|
140
143
|
"Idempotency-Key",
|
|
141
|
-
|
|
144
|
+
options.idempotencyKey ??
|
|
145
|
+
`${options.threadId}:${options.turnKey}:${options.stepIndex}:${options.callIndex}`,
|
|
142
146
|
);
|
|
143
147
|
}
|
|
144
148
|
|
package/src/lib/cc/format.ts
CHANGED
|
@@ -74,10 +74,13 @@ export function buildCcSection(cc: CcPromptInput) {
|
|
|
74
74
|
if (cc.resolved.sharedShapes.length > 0) {
|
|
75
75
|
parts.push(`## Response Shapes\n${formatSharedShapes(cc.resolved.sharedShapes)}`);
|
|
76
76
|
}
|
|
77
|
-
|
|
77
|
+
// Client tools are declared to the model as real tools, so they
|
|
78
|
+
// stay out of the sandbox-only Dynamic Tools section.
|
|
79
|
+
const dynamicTools = cc.resolved.tools.filter((tool) => !tool.embed);
|
|
80
|
+
if (dynamicTools.length > 0) {
|
|
78
81
|
parts.push(
|
|
79
82
|
"## Dynamic Tools\nCall these from executeCode via the `tools` global, e.g. `await tools.name(input)`.\n\n" +
|
|
80
|
-
formatToolSignatures(
|
|
83
|
+
formatToolSignatures(dynamicTools),
|
|
81
84
|
);
|
|
82
85
|
}
|
|
83
86
|
if (cc.resolved.services.length > 0) {
|
package/src/lib/cc/registry.ts
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import type { ControlCenterClient } from "./client";
|
|
2
|
-
import type { RuntimeAgentConfig } from "./types";
|
|
2
|
+
import type { RuntimeAgentConfig, ToolEmbed } from "./types";
|
|
3
3
|
|
|
4
4
|
export type CcToolBinding = {
|
|
5
5
|
toolId: string;
|
|
6
6
|
readOnly: boolean;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* A client tool resolved for this turn. Declared to the model as a real
|
|
11
|
+
* client-executed tool — never a sandbox binding — so its call streams to the
|
|
12
|
+
* widget as a tool-call part and parks the run until the flow settles.
|
|
13
|
+
*/
|
|
14
|
+
export type CcClientTool = {
|
|
15
|
+
toolId: string;
|
|
16
|
+
signature: string;
|
|
17
|
+
embed: ToolEmbed;
|
|
18
|
+
inputSchema?: Record<string, unknown>;
|
|
19
|
+
};
|
|
20
|
+
|
|
9
21
|
/**
|
|
10
22
|
* Turn-scoped and mutable: seeded from /resolve, extended by searchTools
|
|
11
23
|
* mid-turn so a tool discovered at step 3 is callable at step 4.
|
|
@@ -14,9 +26,10 @@ export type CcToolRegistry = Map<string, CcToolBinding>;
|
|
|
14
26
|
|
|
15
27
|
export function registerCcTools(
|
|
16
28
|
registry: CcToolRegistry,
|
|
17
|
-
tools: { name: string; toolId: string; readOnly: boolean }[],
|
|
29
|
+
tools: { name: string; toolId: string; readOnly: boolean; embed?: ToolEmbed }[],
|
|
18
30
|
) {
|
|
19
31
|
for (const tool of tools) {
|
|
32
|
+
if (tool.embed) continue;
|
|
20
33
|
registry.set(tool.name, { toolId: tool.toolId, readOnly: tool.readOnly });
|
|
21
34
|
}
|
|
22
35
|
}
|
|
@@ -50,6 +63,7 @@ export type CcRuntime = {
|
|
|
50
63
|
agentId: string;
|
|
51
64
|
config: RuntimeAgentConfig;
|
|
52
65
|
registry: CcToolRegistry;
|
|
66
|
+
clientTools: Map<string, CcClientTool>;
|
|
53
67
|
threadId: string;
|
|
54
68
|
turnKey: string;
|
|
55
69
|
userId: string;
|
package/src/lib/cc/types.ts
CHANGED
package/src/lib/config.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnyServerTool } from "@tanstack/ai";
|
|
1
|
+
import type { AnyClientTool, AnyServerTool } from "@tanstack/ai";
|
|
2
2
|
import type { DatabaseAdapter } from "./adapters/database/index";
|
|
3
3
|
import type { StorageAdapter } from "./adapters/storage/index";
|
|
4
4
|
import type { DomainDef } from "@cortex/contracts/graph";
|
|
@@ -6,8 +6,13 @@ import type { RequestInterceptorOptions } from "./ai/interceptors/request-interc
|
|
|
6
6
|
import type { ContextConfig } from "./ai/context/types";
|
|
7
7
|
import type { ChatMessage, Thread } from "./types";
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* An agent's tools, as `chat()` takes them. A tool with an `execute` function
|
|
11
|
+
* runs on the server; one without is a static client tool — declared to the
|
|
12
|
+
* model, streamed to the widget as a tool-call part, and answered by the host
|
|
13
|
+
* app (`hooks.onToolCall` or a `toolComponents` entry with `setOutput`).
|
|
14
|
+
*/
|
|
15
|
+
export type ToolSet = ReadonlyArray<AnyServerTool | AnyClientTool>;
|
|
11
16
|
|
|
12
17
|
type ModelConfig = {
|
|
13
18
|
baseURL: string;
|
package/src/lib/index.ts
CHANGED
|
@@ -33,6 +33,10 @@ export type {
|
|
|
33
33
|
} from "./ai/interceptors/request-interceptor";
|
|
34
34
|
export { createRequestInterceptor } from "./ai/interceptors/request-interceptor";
|
|
35
35
|
|
|
36
|
+
// Re-exported so consumers define tools (server or static client) without a
|
|
37
|
+
// direct @tanstack/ai import.
|
|
38
|
+
export { toolDefinition } from "@tanstack/ai";
|
|
39
|
+
|
|
36
40
|
// Tools (consumers may register custom tools or use built-in ones)
|
|
37
41
|
export { createQueryGraphTool } from "./ai/tools/query-graph.tool";
|
|
38
42
|
export { createExecuteCodeTool } from "./ai/tools/execute-code.tool";
|
package/src/lib/routes/chat.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { HTTPException } from "hono/http-exception";
|
|
|
6
6
|
import type { CortexAppEnv } from "@/types";
|
|
7
7
|
import { requireAuth } from "@/auth/middleware";
|
|
8
8
|
import { startTurn } from "@/ai/index";
|
|
9
|
+
import { initiateClientTool } from "@/ai/client-tools";
|
|
9
10
|
import { abortRun, getRunId, joinLog, offsetBelongsToRun } from "@/ai/active-runs";
|
|
10
11
|
import { notify } from "@/ws/connections";
|
|
11
12
|
import { requireOwnedThread } from "./owned-thread";
|
|
@@ -91,6 +92,28 @@ export function createChatRoutes() {
|
|
|
91
92
|
return resumeServerSentEventsResponse({ adapter: joinLog(chatId, runId) });
|
|
92
93
|
});
|
|
93
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Runs a client tool's initiate call for a pending tool call and
|
|
97
|
+
* returns the widget's embed payload. `embed: false` tells the
|
|
98
|
+
* widget the call is not an CC client tool — fall back to its
|
|
99
|
+
* default rendering. The payload never reaches the model.
|
|
100
|
+
*/
|
|
101
|
+
app.post("/chat/:chatId/tools/:toolCallId/initiate", requireAuth, async function (c) {
|
|
102
|
+
const config = c.get("agentConfig");
|
|
103
|
+
const { id: userId, token } = c.get("user");
|
|
104
|
+
const thread = await requireOwnedThread(c, c.req.param("chatId"));
|
|
105
|
+
|
|
106
|
+
return c.json(
|
|
107
|
+
await initiateClientTool({
|
|
108
|
+
config,
|
|
109
|
+
thread,
|
|
110
|
+
userId,
|
|
111
|
+
token,
|
|
112
|
+
toolCallId: c.req.param("toolCallId"),
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
|
|
94
117
|
app.post("/chat/:chatId/abort", requireAuth, async function (c) {
|
|
95
118
|
const agentId = c.get("agentId");
|
|
96
119
|
const userId = c.get("user").id;
|
package/tsconfig.json
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
// `vendor` copies them into ./contracts for build and pack. The first
|
|
24
24
|
// candidate exists only then (and in the published tarball) — inside
|
|
25
25
|
// the repo resolution falls through to the workspace source.
|
|
26
|
-
"@cortex/contracts/*": ["./contracts/*", "../../internal/contracts/*"],
|
|
26
|
+
"@cortex/contracts/*": ["./contracts/src/*", "../../internal/contracts/src/*"],
|
|
27
27
|
// Internal-only. tsc does not rewrite aliases on emit, so `build` runs
|
|
28
28
|
// tsc-alias to turn these back into relative paths in dist/**/*.d.ts —
|
|
29
29
|
// a consumer's tsc knows nothing about our `@/`. The published raw src
|
package/contracts/graph.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The graph contract — the knowledge-graph vocabulary, authoring types and
|
|
3
|
-
* clients that `@m6d/cortex-cli` writes with and `@m6d/cortex-server` reads
|
|
4
|
-
* with. `@m6d/cortex-server` re-exports all of it, so a project authoring
|
|
5
|
-
* domains still has exactly one import path.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export type { GraphSchema } from "./graph/schema";
|
|
9
|
-
export { GRAPH_SCHEMA, GRAPH_SCHEMA_VERSION } from "./graph/schema";
|
|
10
|
-
|
|
11
|
-
export type {
|
|
12
|
-
EndpointScalarType,
|
|
13
|
-
EndpointProperty,
|
|
14
|
-
ResponseKind,
|
|
15
|
-
AutoGenerated,
|
|
16
|
-
ConceptDef,
|
|
17
|
-
EndpointDef,
|
|
18
|
-
EndpointInput,
|
|
19
|
-
ServiceDef,
|
|
20
|
-
RuleDef,
|
|
21
|
-
DomainDef,
|
|
22
|
-
} from "./graph/types";
|
|
23
|
-
|
|
24
|
-
export {
|
|
25
|
-
defineConcept,
|
|
26
|
-
defineRule,
|
|
27
|
-
defineService,
|
|
28
|
-
defineDomain,
|
|
29
|
-
defineEndpoint,
|
|
30
|
-
} from "./graph/helpers";
|
|
31
|
-
|
|
32
|
-
export type { Neo4jConfig, Neo4jClient } from "./graph/neo4j";
|
|
33
|
-
export { createNeo4jClient } from "./graph/neo4j";
|
|
34
|
-
|
|
35
|
-
export type { EmbedFn, EmbeddingProviderConfig } from "./graph/embed";
|
|
36
|
-
export { createEmbedder } from "./graph/embed";
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|