@m6d/cortex-cli 1.2.0 → 1.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m6d/cortex-cli",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Scaffold and operate Cortex servers",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -16,7 +16,7 @@
16
16
  ],
17
17
  "scripts": {
18
18
  "check": "tsc --noEmit",
19
- "prepack": "cp -R ../../internal/contracts src/contracts && rm src/contracts/package.json src/contracts/tsconfig.json",
19
+ "prepack": "rm -rf src/contracts && cp -R ../../internal/contracts/src src/contracts",
20
20
  "postpack": "rm -rf src/contracts",
21
21
  "test": "bun test"
22
22
  },
@@ -0,0 +1,56 @@
1
+ /**
2
+ * The embed ↔ widget handshake for client tools.
3
+ *
4
+ * A page embedded by the chat widget (a hosted checkout, signing page, …)
5
+ * reports its outcome with:
6
+ *
7
+ * window.parent.postMessage(
8
+ * { type: "cortex:client-tool", status: "completed", reference: "session_123" },
9
+ * "*",
10
+ * );
11
+ *
12
+ * The widget accepts the message only when the event's origin matches the
13
+ * tool's configured `embedOrigin`. The payload is the page's claim, relayed to
14
+ * the agent as-is: cortex does not verify it, and anything that must actually
15
+ * be true lives in the integrating backend's own records.
16
+ *
17
+ * Like the wire seam, this file is compiled into the client SDKs and must stay
18
+ * free of runtime dependencies. It is also the spec any future SDK (Flutter)
19
+ * reimplements against.
20
+ */
21
+
22
+ export const CLIENT_TOOL_MESSAGE_TYPE = "cortex:client-tool";
23
+
24
+ export const CLIENT_TOOL_STATUSES = ["completed", "cancelled", "failed"] as const;
25
+
26
+ export type ClientToolStatus = (typeof CLIENT_TOOL_STATUSES)[number];
27
+
28
+ export type ClientToolHandshake = {
29
+ type: typeof CLIENT_TOOL_MESSAGE_TYPE;
30
+ status: ClientToolStatus;
31
+ /** Opaque id for the integrating backend's own bookkeeping (session id, envelope id, …). */
32
+ reference?: string;
33
+ /** Optional payload passed through to the agent as part of the tool result. */
34
+ result?: unknown;
35
+ };
36
+
37
+ /**
38
+ * Parse a `message` event into a handshake, or null when the origin doesn't
39
+ * match the tool's `embedOrigin` or the payload isn't a well-formed handshake.
40
+ * Call from the widget's `message` listener with `event.data` / `event.origin`.
41
+ */
42
+ export function parseClientToolHandshake(data: unknown, origin: string, embedOrigin: string) {
43
+ if (origin !== embedOrigin) return null;
44
+ if (typeof data !== "object" || data === null) return null;
45
+ const message = data as Record<string, unknown>;
46
+ if (message["type"] !== CLIENT_TOOL_MESSAGE_TYPE) return null;
47
+ const status = CLIENT_TOOL_STATUSES.find((known) => known === message["status"]);
48
+ if (!status) return null;
49
+ const reference = message["reference"];
50
+ return {
51
+ type: CLIENT_TOOL_MESSAGE_TYPE,
52
+ status,
53
+ ...(typeof reference === "string" ? { reference } : {}),
54
+ ...(message["result"] !== undefined ? { result: message["result"] } : {}),
55
+ } satisfies ClientToolHandshake;
56
+ }
@@ -0,0 +1,30 @@
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 "./schema";
9
+ export { GRAPH_SCHEMA, GRAPH_SCHEMA_VERSION } from "./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 "./types";
23
+
24
+ export { defineConcept, defineRule, defineService, defineDomain, defineEndpoint } from "./helpers";
25
+
26
+ export type { Neo4jConfig, Neo4jClient } from "./clients/neo4j";
27
+ export { createNeo4jClient } from "./clients/neo4j";
28
+
29
+ export type { EmbedFn, EmbeddingProviderConfig } from "./clients/embed";
30
+ export { createEmbedder } from "./clients/embed";
@@ -90,16 +90,15 @@ export const knowledgeChunkSchema = z.object({
90
90
  });
91
91
 
92
92
  /**
93
- * Runtime contract §5.4: present on signatures of `interactive` tools — flows
93
+ * Runtime contract §5.4: present on signatures of `embedded` tools — flows
94
94
  * the end user completes in an embedded surface inside the chat widget. The
95
95
  * tool's endpoint fields act as the *initiate* call (returns the embed URL);
96
- * `hasVerify` marks a second, server-trusted call that settles the result.
96
+ * the result the page reports back is relayed as-is verifying it is the
97
+ * integrating backend's job.
97
98
  */
98
- export const toolInteractionSchema = z.object({
99
+ export const toolEmbedSchema = z.object({
99
100
  surface: z.enum(["inline", "modal"]),
100
101
  embedOrigin: z.url(),
101
- hasVerify: z.boolean(),
102
- resultDelivery: z.enum(["agent", "endpoint", "both"]),
103
102
  });
104
103
 
105
104
  export const toolSignatureSchema = z.object({
@@ -111,8 +110,8 @@ export const toolSignatureSchema = z.object({
111
110
  signature: z.string(),
112
111
  score: z.number().nullable(),
113
112
  pinned: z.boolean(),
114
- interaction: toolInteractionSchema.optional(),
115
- /** Interactive tools only: the published input JSON Schema, verbatim.
113
+ embed: toolEmbedSchema.optional(),
114
+ /** Client tools only: the published input JSON Schema, verbatim.
116
115
  * Client-executed declarations need a real schema — the rendered
117
116
  * `signature` text alone is not enough for function calling. */
118
117
  inputSchema: z.record(z.string(), z.unknown()).optional(),
@@ -133,7 +132,6 @@ export const runtimeAgentConfigSchema = z.object({
133
132
  agentId: agentSlugSchema,
134
133
  systemPrompt: z.string(),
135
134
  promptVariables: z.array(z.enum(PROMPT_VARIABLES)),
136
- catalogBlurb: z.string(),
137
135
  defaultLocale: localeSchema,
138
136
  metaTools: z.object({
139
137
  searchKnowledge: z.boolean(),
@@ -212,9 +210,6 @@ export const searchKnowledgeResponseSchema = z.object({
212
210
 
213
211
  export const executeRequestSchema = z.object({
214
212
  input: z.record(z.string(), z.unknown()).default({}),
215
- // Interactive tools only: absent | "initiate" targets the tool's endpoint
216
- // fields, "verify" targets its verify endpoint config.
217
- phase: z.enum(["initiate", "verify"]).optional(),
218
213
  context: z
219
214
  .object({
220
215
  threadId: z.string().max(128).optional(),
@@ -257,7 +252,7 @@ export const runtimeErrorSchema = z.object({
257
252
  }),
258
253
  });
259
254
 
260
- export type ToolInteraction = z.infer<typeof toolInteractionSchema>;
255
+ export type ToolEmbed = z.infer<typeof toolEmbedSchema>;
261
256
  export type RuntimeAgentConfig = z.infer<typeof runtimeAgentConfigSchema>;
262
257
  export type ResolveRequest = z.input<typeof resolveRequestSchema>;
263
258
  export type ResolveResponse = z.infer<typeof resolveResponseSchema>;
@@ -71,16 +71,14 @@ export type TokenUsage = {
71
71
  };
72
72
 
73
73
  /**
74
- * How to launch one interactive tool: stamped by the server, per tool name, on
74
+ * How to launch one client tool: stamped by the server, per tool name, on
75
75
  * the metadata of an assistant message that may carry its pending call. Kept in
76
76
  * the message so the binding survives server restarts while a run is parked.
77
77
  */
78
- export type InteractiveToolBinding = {
78
+ export type ClientToolBinding = {
79
79
  toolId: string;
80
80
  surface: "inline" | "modal";
81
81
  embedOrigin: string;
82
- hasVerify: boolean;
83
- resultDelivery: "agent" | "endpoint" | "both";
84
82
  };
85
83
 
86
84
  export type MessageMetadata = {
@@ -89,10 +87,7 @@ export type MessageMetadata = {
89
87
  isAborted?: boolean;
90
88
  tokenUsage?: TokenUsage;
91
89
  attachments?: AttachmentSummary[];
92
- interactiveTools?: Record<string, InteractiveToolBinding>;
93
- /** Per tool call: the trusted reference its initiate call returned. A
94
- * verified completion must match it — the browser's word is never enough. */
95
- interactiveReferences?: Record<string, string>;
90
+ clientTools?: Record<string, ClientToolBinding>;
96
91
  };
97
92
 
98
93
  /**
@@ -111,15 +106,32 @@ export type CortexMessage<TPart = unknown> = {
111
106
  };
112
107
 
113
108
  /**
114
- * Response of `POST /chat/:chatId/tools/:toolCallId/initiate`. `interactive:
115
- * false` means the pending call is not an interactive CC tool the widget
109
+ * Key under the AG-UI request's `forwardedProps` carrying the answers to
110
+ * parked client tool calls on a continuation request. The server owns the
111
+ * transcript (a continuation sends `messages: []`), so the answered assistant
112
+ * message itself never crosses the wire — the SDK's park-boundary snapshot
113
+ * does not preserve its id, and round-tripping it duplicated the stored
114
+ * message.
115
+ */
116
+ export const CLIENT_TOOL_ANSWERS_KEY = "clientToolAnswers";
117
+
118
+ /** One answered client tool call, applied to the stored transcript server-side. */
119
+ export type ClientToolAnswer = {
120
+ toolCallId: string;
121
+ output: unknown;
122
+ state: "complete" | "error";
123
+ };
124
+
125
+ /**
126
+ * Response of `POST /chat/:chatId/tools/:toolCallId/initiate`. `embed:
127
+ * false` means the pending call is not a CC client tool — the widget
116
128
  * falls back to its default rendering for the call. The payload is for the
117
129
  * widget only; it never reaches the LLM.
118
130
  */
119
- export type InteractiveInitiateResult =
120
- | { interactive: false }
131
+ export type ClientToolInitiateResult =
132
+ | { embed: false }
121
133
  | {
122
- interactive: true;
134
+ embed: true;
123
135
  embedUrl: string;
124
136
  surface: "inline" | "modal";
125
137
  embedOrigin: string;
package/tsconfig.json CHANGED
@@ -22,7 +22,7 @@
22
22
  // vendors it into src/contracts, where the first candidate finds it in
23
23
  // the published tarball. Inside the repo that copy doesn't exist and
24
24
  // resolution falls through to the workspace source.
25
- "@cortex/contracts/*": ["./src/contracts/*", "../../internal/contracts/*"],
25
+ "@cortex/contracts/*": ["./src/contracts/*", "../../internal/contracts/src/*"],
26
26
  // Internal-only, and the reason this file ships in package.json `files`:
27
27
  // the CLI runs as raw TypeScript from inside node_modules, so Bun has to
28
28
  // read this config to resolve `@/`.
@@ -1,23 +0,0 @@
1
- # @cortex/contracts
2
-
3
- The shapes both sides of a cortex boundary must agree on. Private on purpose —
4
- it never publishes; each publishable package carries its own copy:
5
-
6
- - `@m6d/cortex-server` and `@m6d/cortex-cli` publish raw source, so their
7
- `prepack` vendors a copy of this directory into the tarball (`contracts/` and
8
- `src/contracts/` respectively), resolved there by their shipped tsconfig paths.
9
- - `@m6d/cortex-angular` and `@m6d/cortex-react` compile the parts they import
10
- into their build artifacts.
11
-
12
- Three boundaries, one subpath each:
13
-
14
- | import | boundary |
15
- | --------------------------- | ------------------------------------------------------------- |
16
- | `@cortex/contracts/wire` | chat client ↔ server (HTTP/WebSocket types) |
17
- | `@cortex/contracts/runtime` | cortex-cc console ↔ server runtime API (zod schemas) |
18
- | `@cortex/contracts/graph` | graph authoring (cli) ↔ server (vocabulary, helpers, clients) |
19
-
20
- Everything is consumed as TypeScript source — no build, no dist. Nothing here
21
- has a runtime dependency beyond zod (for `/runtime`), and nothing here may grow
22
- one: these files are compiled into an Angular library, a React library, a CLI
23
- and a Bun server alike.
@@ -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";
@@ -1,53 +0,0 @@
1
- /**
2
- * The embed ↔ widget handshake for interactive tools.
3
- *
4
- * A page embedded by the chat widget (a hosted checkout, signing page, …)
5
- * reports its outcome with:
6
- *
7
- * window.parent.postMessage(
8
- * { type: "cortex:interactive", status: "completed", reference: "session_123" },
9
- * "*",
10
- * );
11
- *
12
- * The widget accepts the message only when the event's origin matches the
13
- * tool's configured `embedOrigin`, and treats the payload as untrusted — for
14
- * tools with a verify endpoint the server re-checks the reference before the
15
- * agent sees a result.
16
- *
17
- * Like `wire.ts`, this file is compiled into the client SDKs and must stay
18
- * free of runtime dependencies. It is also the spec any future SDK (Flutter)
19
- * reimplements against.
20
- */
21
-
22
- export const INTERACTIVE_MESSAGE_TYPE = "cortex:interactive";
23
-
24
- export const INTERACTIVE_STATUSES = ["completed", "cancelled", "failed"] as const;
25
-
26
- export type InteractiveStatus = (typeof INTERACTIVE_STATUSES)[number];
27
-
28
- export type InteractiveHandshake = {
29
- type: typeof INTERACTIVE_MESSAGE_TYPE;
30
- status: InteractiveStatus;
31
- /** Opaque id the tool's verify endpoint resolves (session id, envelope id, …). */
32
- reference?: string;
33
- };
34
-
35
- /**
36
- * Parse a `message` event into a handshake, or null when the origin doesn't
37
- * match the tool's `embedOrigin` or the payload isn't a well-formed handshake.
38
- * Call from the widget's `message` listener with `event.data` / `event.origin`.
39
- */
40
- export function parseInteractiveHandshake(data: unknown, origin: string, embedOrigin: string) {
41
- if (origin !== embedOrigin) return null;
42
- if (typeof data !== "object" || data === null) return null;
43
- const message = data as Record<string, unknown>;
44
- if (message["type"] !== INTERACTIVE_MESSAGE_TYPE) return null;
45
- const status = INTERACTIVE_STATUSES.find((known) => known === message["status"]);
46
- if (!status) return null;
47
- const reference = message["reference"];
48
- return {
49
- type: INTERACTIVE_MESSAGE_TYPE,
50
- status,
51
- ...(typeof reference === "string" ? { reference } : {}),
52
- } satisfies InteractiveHandshake;
53
- }