@m6d/cortex-server 1.7.0 → 2.0.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 +8 -3
- package/contracts/README.md +23 -0
- package/contracts/graph/embed.ts +50 -0
- package/{src → contracts}/graph/helpers.ts +4 -10
- package/{src → contracts}/graph/neo4j.ts +14 -27
- package/contracts/graph/schema.ts +65 -0
- package/{src → contracts}/graph/types.ts +2 -29
- package/contracts/graph.ts +36 -0
- package/contracts/runtime.ts +208 -0
- package/contracts/wire.ts +143 -0
- package/dist/contracts/graph/embed.d.ts +22 -0
- package/dist/{src → contracts}/graph/helpers.d.ts +14 -12
- package/dist/{src → contracts}/graph/neo4j.d.ts +5 -5
- package/dist/contracts/graph/schema.d.ts +62 -0
- package/dist/{src → contracts}/graph/types.d.ts +1 -1
- package/dist/contracts/graph.d.ts +14 -0
- package/dist/contracts/runtime.d.ts +278 -0
- package/dist/contracts/wire.d.ts +120 -0
- package/dist/index.d.ts +1 -1
- package/dist/src/lib/adapters/database/index.d.ts +61 -0
- package/dist/src/lib/adapters/database/message-content.d.ts +30 -0
- package/dist/src/lib/adapters/database/mssql/attachments.d.ts +100 -0
- package/dist/src/lib/adapters/database/mssql/client.d.ts +8 -0
- package/dist/src/lib/adapters/database/mssql/index.d.ts +194 -0
- package/dist/src/lib/adapters/database/mssql/llm-requests.d.ts +17 -0
- package/dist/src/lib/adapters/database/mssql/messages.d.ts +37 -0
- package/dist/src/lib/adapters/database/mssql/threads.d.ts +50 -0
- package/dist/src/lib/adapters/database/postgres/attachments.d.ts +113 -0
- package/dist/src/lib/adapters/database/postgres/client.d.ts +8 -0
- package/dist/src/lib/adapters/database/postgres/index.d.ts +207 -0
- package/dist/src/lib/adapters/database/postgres/llm-requests.d.ts +17 -0
- package/dist/src/lib/adapters/database/postgres/messages.d.ts +37 -0
- package/dist/src/lib/adapters/database/postgres/threads.d.ts +50 -0
- package/{src/adapters/storage.ts → dist/src/lib/adapters/storage/index.d.ts} +1 -1
- package/dist/src/{adapters → lib/adapters/storage}/minio.d.ts +1 -1
- package/dist/src/lib/ai/active-runs.d.ts +63 -0
- package/dist/src/lib/ai/attachments.d.ts +30 -0
- package/dist/src/lib/ai/backend-url.d.ts +16 -0
- package/dist/src/lib/ai/cc-runtime.d.ts +61 -0
- package/dist/src/lib/ai/commit-gate.d.ts +21 -0
- package/dist/src/{ai → lib/ai}/context/builder.d.ts +6 -7
- package/dist/src/lib/ai/context/compression-middleware.d.ts +15 -0
- package/dist/src/lib/ai/context/compressor.d.ts +7 -0
- package/dist/src/lib/ai/context/intra-turn-compressor.d.ts +24 -0
- package/dist/src/lib/ai/context/optimize-thread-context.d.ts +9 -0
- package/dist/src/lib/ai/context/summarizer.d.ts +5 -0
- package/dist/src/{ai → lib/ai}/context/token-estimator.d.ts +5 -8
- package/dist/src/{ai → lib/ai}/context/types.d.ts +2 -0
- package/dist/src/{ai → lib/ai}/fetch.d.ts +1 -1
- package/dist/src/lib/ai/finish-turn.d.ts +23 -0
- package/dist/src/lib/ai/helpers.d.ts +38 -0
- package/dist/src/lib/ai/index.d.ts +7 -0
- package/dist/src/lib/ai/inspector.d.ts +41 -0
- package/dist/src/lib/ai/interceptors/request-interceptor.d.ts +13 -0
- package/dist/src/{ai → lib/ai}/prompt.d.ts +5 -4
- package/dist/src/lib/ai/redis-runs.d.ts +41 -0
- package/dist/src/lib/ai/redis-stream-log.d.ts +39 -0
- package/dist/src/lib/ai/tools/execute-code.tool.d.ts +18 -0
- package/dist/src/lib/ai/tools/query-graph.tool.d.ts +13 -0
- package/dist/src/lib/ai/tools/read-attachment.tool.d.ts +21 -0
- package/dist/src/lib/ai/tools/search-common.d.ts +17 -0
- package/dist/src/lib/ai/tools/search-knowledge.tool.d.ts +15 -0
- package/dist/src/lib/ai/tools/search-services.tool.d.ts +12 -0
- package/dist/src/lib/ai/tools/search-tools.tool.d.ts +12 -0
- package/dist/src/lib/ai/turn-tools.d.ts +39 -0
- package/dist/src/lib/ai/vision/rasterize-pdf.d.ts +26 -0
- package/dist/src/lib/ai/vision/rasterize-pdf.worker.d.ts +1 -0
- package/dist/src/lib/ai/vision/vision-reader.d.ts +45 -0
- package/dist/src/lib/cc/client.d.ts +183 -0
- package/dist/src/lib/cc/config-cache.d.ts +20 -0
- package/dist/src/lib/cc/format.d.ts +23 -0
- package/dist/src/lib/cc/registry.d.ts +34 -0
- package/dist/src/lib/cc/types.d.ts +27 -0
- package/dist/src/lib/config.d.ts +170 -0
- package/dist/src/lib/db/drizzle.config.mssql.d.ts +2 -0
- package/dist/src/lib/db/drizzle.config.pg.d.ts +2 -0
- package/dist/src/lib/db/migrate.d.ts +7 -0
- package/dist/src/{db/schema.d.ts → lib/db/schema.mssql.d.ts} +236 -24
- package/dist/src/lib/db/schema.pg.d.ts +546 -0
- package/dist/src/{factory.d.ts → lib/factory.d.ts} +2 -12
- package/dist/src/lib/graph/index.d.ts +10 -0
- package/dist/src/{graph → lib/graph}/resolver.d.ts +2 -3
- package/dist/src/{index.d.ts → lib/index.d.ts} +3 -4
- package/dist/src/lib/redis.d.ts +19 -0
- package/dist/src/{routes → lib/routes}/chat.d.ts +1 -1
- package/dist/src/{routes → lib/routes}/files.d.ts +1 -1
- package/dist/src/lib/routes/owned-thread.d.ts +17 -0
- package/dist/src/{routes → lib/routes}/threads.d.ts +1 -1
- package/dist/src/{routes → lib/routes}/ws.d.ts +1 -1
- package/dist/src/lib/types.d.ts +58 -0
- package/dist/src/{ws → lib/ws}/connections.d.ts +2 -1
- package/index.ts +1 -1
- package/package.json +35 -15
- package/src/lib/adapters/database/index.ts +78 -0
- package/src/lib/adapters/database/message-content.ts +39 -0
- package/src/lib/adapters/database/mssql/attachments.ts +90 -0
- package/src/lib/adapters/database/mssql/client.ts +11 -0
- package/src/lib/adapters/database/mssql/index.ts +23 -0
- package/src/lib/adapters/database/mssql/llm-requests.ts +43 -0
- package/src/lib/adapters/database/mssql/messages.ts +92 -0
- package/src/lib/adapters/database/mssql/threads.ts +83 -0
- package/src/lib/adapters/database/postgres/attachments.ts +88 -0
- package/src/lib/adapters/database/postgres/client.ts +11 -0
- package/src/lib/adapters/database/postgres/index.ts +23 -0
- package/src/lib/adapters/database/postgres/llm-requests.ts +45 -0
- package/src/lib/adapters/database/postgres/messages.ts +92 -0
- package/src/lib/adapters/database/postgres/threads.ts +84 -0
- package/{dist/src/adapters/storage.d.ts → src/lib/adapters/storage/index.ts} +1 -1
- package/src/{adapters → lib/adapters/storage}/minio.ts +7 -6
- package/src/lib/ai/active-runs.ts +157 -0
- package/src/lib/ai/attachments.ts +144 -0
- package/src/lib/ai/backend-url.ts +27 -0
- package/src/lib/ai/cc-runtime.ts +63 -0
- package/src/lib/ai/commit-gate.ts +116 -0
- package/src/{ai → lib/ai}/context/builder.ts +12 -14
- package/src/lib/ai/context/compression-middleware.ts +44 -0
- package/src/lib/ai/context/compressor.ts +267 -0
- package/src/lib/ai/context/intra-turn-compressor.ts +181 -0
- package/src/lib/ai/context/optimize-thread-context.ts +50 -0
- package/src/lib/ai/context/summarizer.ts +48 -0
- package/src/lib/ai/context/token-estimator.ts +65 -0
- package/src/{ai → lib/ai}/context/types.ts +4 -1
- package/src/{ai → lib/ai}/fetch.ts +10 -5
- package/src/lib/ai/finish-turn.ts +75 -0
- package/src/lib/ai/helpers.ts +100 -0
- package/src/lib/ai/index.ts +408 -0
- package/src/lib/ai/inspector.ts +112 -0
- package/src/lib/ai/interceptors/request-interceptor.ts +89 -0
- package/src/lib/ai/prompt.ts +355 -0
- package/src/lib/ai/redis-runs.ts +167 -0
- package/src/lib/ai/redis-stream-log.ts +219 -0
- package/src/lib/ai/tools/execute-code.tool.ts +267 -0
- package/src/{ai → lib/ai}/tools/query-graph.tool.ts +7 -8
- package/src/lib/ai/tools/read-attachment.tool.ts +59 -0
- package/src/lib/ai/tools/search-common.ts +15 -0
- package/src/lib/ai/tools/search-knowledge.tool.ts +33 -0
- package/src/lib/ai/tools/search-services.tool.ts +31 -0
- package/src/lib/ai/tools/search-tools.tool.ts +29 -0
- package/src/lib/ai/turn-tools.ts +154 -0
- package/src/lib/ai/vision/rasterize-pdf.ts +69 -0
- package/src/lib/ai/vision/rasterize-pdf.worker.ts +81 -0
- package/src/lib/ai/vision/vision-reader.ts +130 -0
- package/src/{auth → lib/auth}/middleware.ts +12 -3
- package/src/lib/cc/client.ts +273 -0
- package/src/lib/cc/config-cache.ts +32 -0
- package/src/lib/cc/format.ts +96 -0
- package/src/lib/cc/registry.ts +62 -0
- package/src/lib/cc/types.ts +44 -0
- package/src/lib/config.ts +212 -0
- package/src/lib/db/drizzle.config.mssql.ts +9 -0
- package/src/lib/db/drizzle.config.pg.ts +9 -0
- package/src/lib/db/migrate.ts +56 -0
- package/src/{db/migrations/20260309012148_cloudy_maria_hill → lib/db/migrations/mssql/20260326231647_nice_speedball}/migration.sql +12 -0
- package/src/{db/migrations/20260309012148_cloudy_maria_hill → lib/db/migrations/mssql/20260326231647_nice_speedball}/snapshot.json +106 -1
- package/src/lib/db/migrations/mssql/20260729092726_talented_wilson_fisk/migration.sql +20 -0
- package/src/lib/db/migrations/mssql/20260729092726_talented_wilson_fisk/snapshot.json +497 -0
- package/src/lib/db/migrations/mssql/20260801003652_perpetual_blue_shield/migration.sql +1 -0
- package/src/lib/db/migrations/mssql/20260801003652_perpetual_blue_shield/snapshot.json +516 -0
- package/src/lib/db/migrations/mssql/20260812174642_user_id_text/migration.sql +2 -0
- package/src/lib/db/migrations/mssql/20260812174642_user_id_text/snapshot.json +516 -0
- package/src/lib/db/migrations/pg/20260801084117_thick_ben_grimm/migration.sql +53 -0
- package/src/lib/db/migrations/pg/20260801084117_thick_ben_grimm/snapshot.json +584 -0
- package/src/lib/db/migrations/pg/20260812174622_user_id_text/migration.sql +2 -0
- package/src/lib/db/migrations/pg/20260812174622_user_id_text/snapshot.json +584 -0
- package/src/lib/db/schema.mssql.ts +94 -0
- package/src/lib/db/schema.pg.ts +95 -0
- package/src/lib/factory.ts +127 -0
- package/src/lib/graph/index.ts +19 -0
- package/src/lib/graph/resolver.ts +387 -0
- package/src/{index.ts → lib/index.ts} +13 -5
- package/src/lib/redis.ts +44 -0
- package/src/lib/routes/chat.ts +115 -0
- package/src/lib/routes/files.ts +129 -0
- package/src/lib/routes/owned-thread.ts +17 -0
- package/src/{routes → lib/routes}/threads.ts +22 -31
- package/src/{routes → lib/routes}/ws.ts +9 -3
- package/src/lib/types.ts +76 -0
- package/src/{ws → lib/ws}/connections.ts +9 -1
- package/tsconfig.json +48 -0
- package/dist/src/adapters/database.d.ts +0 -30
- package/dist/src/adapters/mssql.d.ts +0 -3
- package/dist/src/ai/active-streams.d.ts +0 -14
- package/dist/src/ai/context/compressor.d.ts +0 -7
- package/dist/src/ai/context/index.d.ts +0 -15
- package/dist/src/ai/context/summarizer.d.ts +0 -5
- package/dist/src/ai/helpers.d.ts +0 -5
- package/dist/src/ai/index.d.ts +0 -4
- package/dist/src/ai/interceptors/request-interceptor.d.ts +0 -12
- package/dist/src/ai/tools/call-endpoint.tool.d.ts +0 -7
- package/dist/src/ai/tools/capture-files.tool.d.ts +0 -6
- package/dist/src/ai/tools/execute-code.tool.d.ts +0 -4
- package/dist/src/ai/tools/query-graph.tool.d.ts +0 -5
- package/dist/src/cli/extract-endpoints.d.ts +0 -6
- package/dist/src/config.d.ts +0 -165
- package/dist/src/db/migrate.d.ts +0 -1
- package/dist/src/graph/expand-domains.d.ts +0 -2
- package/dist/src/graph/generate-cypher.d.ts +0 -22
- package/dist/src/graph/index.d.ts +0 -11
- package/dist/src/graph/seed.d.ts +0 -19
- package/dist/src/graph/validate.d.ts +0 -2
- package/dist/src/routes/index.d.ts +0 -4
- package/dist/src/types.d.ts +0 -74
- package/dist/src/ws/events.d.ts +0 -39
- package/dist/src/ws/index.d.ts +0 -3
- package/dist/src/ws/notify.d.ts +0 -2
- package/src/adapters/database.ts +0 -29
- package/src/adapters/mssql.ts +0 -195
- package/src/ai/active-streams.ts +0 -123
- package/src/ai/context/compressor.ts +0 -47
- package/src/ai/context/index.ts +0 -75
- package/src/ai/context/summarizer.ts +0 -50
- package/src/ai/context/token-estimator.ts +0 -60
- package/src/ai/helpers.ts +0 -32
- package/src/ai/index.ts +0 -277
- package/src/ai/interceptors/request-interceptor.ts +0 -61
- package/src/ai/prompt.ts +0 -126
- package/src/ai/tools/call-endpoint.tool.ts +0 -89
- package/src/ai/tools/capture-files.tool.ts +0 -20
- package/src/ai/tools/execute-code.tool.ts +0 -153
- package/src/cli/extract-endpoints.ts +0 -550
- package/src/config.ts +0 -182
- package/src/db/migrate.ts +0 -21
- package/src/db/migrations/20260315000000_add_context_meta/migration.sql +0 -1
- package/src/db/schema.ts +0 -70
- package/src/factory.ts +0 -171
- package/src/graph/expand-domains.ts +0 -276
- package/src/graph/generate-cypher.ts +0 -192
- package/src/graph/index.ts +0 -47
- package/src/graph/resolver.ts +0 -357
- package/src/graph/seed.ts +0 -162
- package/src/graph/validate.ts +0 -78
- package/src/routes/chat.ts +0 -86
- package/src/routes/files.ts +0 -100
- package/src/routes/index.ts +0 -4
- package/src/types.ts +0 -74
- package/src/ws/events.ts +0 -39
- package/src/ws/index.ts +0 -11
- package/src/ws/notify.ts +0 -9
- /package/dist/src/{auth → lib/auth}/middleware.d.ts +0 -0
package/src/lib/redis.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Redis } from "ioredis";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The two long-lived connections everything Redis-backed shares. A subscriber
|
|
5
|
+
* connection cannot issue normal commands, so pub/sub listening gets its own;
|
|
6
|
+
* the publisher carries everything else (claims, stream appends, PUBLISH).
|
|
7
|
+
* Blocking stream reads never run on either — each reader duplicates its own
|
|
8
|
+
* connection, or one blocked XREAD would stall every claim and append behind it.
|
|
9
|
+
*/
|
|
10
|
+
export type RedisConnections = {
|
|
11
|
+
publisher: Redis;
|
|
12
|
+
subscriber: Redis;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Without an `error` listener ioredis raises unhandled-error events, which can
|
|
17
|
+
* crash the process — with one, a flapping or unreachable Redis becomes a log
|
|
18
|
+
* line while ioredis reconnects underneath.
|
|
19
|
+
*/
|
|
20
|
+
export function withRedisLogging(client: Redis, role: string) {
|
|
21
|
+
client.on("error", (error: unknown) => console.error(`[cortex-server] redis ${role}:`, error));
|
|
22
|
+
return client;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Cached per URL on `globalThis` so hot reloads and repeated `createCortex`
|
|
27
|
+
* calls reuse the connections instead of opening new ones each time.
|
|
28
|
+
*/
|
|
29
|
+
const store = globalThis as typeof globalThis & {
|
|
30
|
+
__cortexRedis?: Map<string, RedisConnections>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export function getRedisConnections(url: string) {
|
|
34
|
+
store.__cortexRedis ??= new Map();
|
|
35
|
+
let connections = store.__cortexRedis.get(url);
|
|
36
|
+
if (!connections) {
|
|
37
|
+
connections = {
|
|
38
|
+
publisher: withRedisLogging(new Redis(url), "publisher"),
|
|
39
|
+
subscriber: withRedisLogging(new Redis(url), "subscriber"),
|
|
40
|
+
};
|
|
41
|
+
store.__cortexRedis.set(url, connections);
|
|
42
|
+
}
|
|
43
|
+
return connections;
|
|
44
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { EventType, chatParamsFromRequest, resumeServerSentEventsResponse } from "@tanstack/ai";
|
|
2
|
+
import type { AbortRunResult } from "@cortex/contracts/wire";
|
|
3
|
+
import { Hono } from "hono";
|
|
4
|
+
import { bodyLimit } from "hono/body-limit";
|
|
5
|
+
import { HTTPException } from "hono/http-exception";
|
|
6
|
+
import type { CortexAppEnv } from "@/types";
|
|
7
|
+
import { requireAuth } from "@/auth/middleware";
|
|
8
|
+
import { startTurn } from "@/ai/index";
|
|
9
|
+
import { abortRun, getRunId, joinLog, offsetBelongsToRun } from "@/ai/active-runs";
|
|
10
|
+
import { notify } from "@/ws/connections";
|
|
11
|
+
import { requireOwnedThread } from "./owned-thread";
|
|
12
|
+
import { toThreadSummary } from "@/types";
|
|
13
|
+
|
|
14
|
+
const CHAT_BODY_MAX_BYTES = 1024 * 1024;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The answer to "attach me to the run on this thread" when there is no longer
|
|
18
|
+
* one. An empty body would be the obvious reply, but the client reads every
|
|
19
|
+
* attach as a stream and treats a missing one as a transport failure — so the
|
|
20
|
+
* run is reported over, which is what actually happened, and the client settles
|
|
21
|
+
* the turn from the database like any other ending.
|
|
22
|
+
*/
|
|
23
|
+
function runEnded() {
|
|
24
|
+
const finished = { type: EventType.RUN_FINISHED, timestamp: Date.now() };
|
|
25
|
+
|
|
26
|
+
return new Response(`data: ${JSON.stringify(finished)}\n\n`, {
|
|
27
|
+
headers: {
|
|
28
|
+
"Content-Type": "text/event-stream",
|
|
29
|
+
"Cache-Control": "no-cache",
|
|
30
|
+
Connection: "keep-alive",
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function createChatRoutes() {
|
|
36
|
+
const app = new Hono<CortexAppEnv>();
|
|
37
|
+
|
|
38
|
+
app.post("/chat", requireAuth, bodyLimit({ maxSize: CHAT_BODY_MAX_BYTES }), async function (c) {
|
|
39
|
+
const config = c.get("agentConfig");
|
|
40
|
+
const { id: userId, token } = c.get("user");
|
|
41
|
+
|
|
42
|
+
let params: Awaited<ReturnType<typeof chatParamsFromRequest>>;
|
|
43
|
+
try {
|
|
44
|
+
params = await chatParamsFromRequest(c.req.raw);
|
|
45
|
+
} catch (thrown) {
|
|
46
|
+
// The AG-UI validator rejects a malformed body by throwing the
|
|
47
|
+
// 400 it wants sent; Hono has no such convention, so return it.
|
|
48
|
+
if (thrown instanceof Response) return thrown;
|
|
49
|
+
throw thrown;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const thread = await requireOwnedThread(c, params.threadId);
|
|
53
|
+
|
|
54
|
+
// A dropped connection re-POSTs the same body with Last-Event-ID.
|
|
55
|
+
// The run is already producing into its log, so this replays from
|
|
56
|
+
// where the client left off rather than starting a second turn. A
|
|
57
|
+
// run that has since finished sends the client back to the messages
|
|
58
|
+
// endpoint, which by then holds everything the log would have.
|
|
59
|
+
const resumeOffset = c.req.header("Last-Event-ID");
|
|
60
|
+
if (resumeOffset) {
|
|
61
|
+
const runId = await getRunId(thread.id);
|
|
62
|
+
if (!runId) return runEnded();
|
|
63
|
+
if (!offsetBelongsToRun(resumeOffset, runId)) {
|
|
64
|
+
throw new HTTPException(400, { message: "Invalid resume offset" });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return resumeServerSentEventsResponse({
|
|
68
|
+
adapter: joinLog(thread.id, runId, resumeOffset),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const requestContext = config.resolveRequestContext
|
|
73
|
+
? await config.resolveRequestContext(c.req.raw)
|
|
74
|
+
: {};
|
|
75
|
+
|
|
76
|
+
// Null means a stop beat the run to its own announcement; the run is
|
|
77
|
+
// over, which is what actually happened.
|
|
78
|
+
return (
|
|
79
|
+
(await startTurn(params, thread, userId, token, requestContext, config)) ?? runEnded()
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
/** Attaches a reload, a thread switch, or a second tab to the thread's live run. */
|
|
84
|
+
app.get("/chat/:chatId/stream", requireAuth, async function (c) {
|
|
85
|
+
const chatId = c.req.param("chatId");
|
|
86
|
+
await requireOwnedThread(c, chatId);
|
|
87
|
+
|
|
88
|
+
const runId = await getRunId(chatId);
|
|
89
|
+
if (!runId) return runEnded();
|
|
90
|
+
|
|
91
|
+
return resumeServerSentEventsResponse({ adapter: joinLog(chatId, runId) });
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
app.post("/chat/:chatId/abort", requireAuth, async function (c) {
|
|
95
|
+
const agentId = c.get("agentId");
|
|
96
|
+
const userId = c.get("user").id;
|
|
97
|
+
const chatId = c.req.param("chatId");
|
|
98
|
+
const thread = await requireOwnedThread(c, chatId);
|
|
99
|
+
|
|
100
|
+
// Only announce an ending that happened: the client's stop button can
|
|
101
|
+
// outlive the server run (it drains the durability log after the
|
|
102
|
+
// producer commits), and a run-finished for a run that already ended
|
|
103
|
+
// can land before its own run-started and pin the thread as running.
|
|
104
|
+
const aborted = await abortRun(chatId);
|
|
105
|
+
if (aborted) {
|
|
106
|
+
notify(userId, agentId, {
|
|
107
|
+
type: "thread:run-finished",
|
|
108
|
+
payload: { thread: toThreadSummary(thread, false) },
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return c.json({ aborted } satisfies AbortRunResult);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return app;
|
|
115
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ATTACHMENT_MAX_BYTES,
|
|
3
|
+
MAX_ATTACHMENTS_PER_MESSAGE,
|
|
4
|
+
checkAttachmentPolicy,
|
|
5
|
+
} from "@cortex/contracts/wire";
|
|
6
|
+
import { Hono } from "hono";
|
|
7
|
+
import { bodyLimit } from "hono/body-limit";
|
|
8
|
+
import { HTTPException } from "hono/http-exception";
|
|
9
|
+
import { toAttachmentSummary, type CortexAppEnv } from "@/types";
|
|
10
|
+
import { requireAuth } from "@/auth/middleware";
|
|
11
|
+
import type { ResolvedCortexAgentConfig } from "@/config";
|
|
12
|
+
import { requireOwnedThread } from "./owned-thread";
|
|
13
|
+
|
|
14
|
+
export function createFileRoutes() {
|
|
15
|
+
const app = new Hono<CortexAppEnv>();
|
|
16
|
+
|
|
17
|
+
app.post(
|
|
18
|
+
"/threads/:threadId/files",
|
|
19
|
+
requireAuth,
|
|
20
|
+
bodyLimit({ maxSize: ATTACHMENT_MAX_BYTES + 64 * 1024 }),
|
|
21
|
+
async function (c) {
|
|
22
|
+
const config = c.get("agentConfig");
|
|
23
|
+
const storage = requireStorage(config);
|
|
24
|
+
|
|
25
|
+
const threadId = c.req.param("threadId");
|
|
26
|
+
const userId = c.get("user").id;
|
|
27
|
+
await requireOwnedThread(c, threadId);
|
|
28
|
+
|
|
29
|
+
const file = (await c.req.parseBody())["file"];
|
|
30
|
+
if (!(file instanceof File)) {
|
|
31
|
+
throw new HTTPException(400, { message: "File is required" });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const policy = checkAttachmentPolicy(file.type, file.size);
|
|
35
|
+
if (policy !== "ok") {
|
|
36
|
+
throw new HTTPException(422, { message: `Attachment rejected: ${policy}` });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const storageKey = `attachments/${crypto.randomUUID()}`;
|
|
40
|
+
const bytes = Buffer.from(await file.arrayBuffer()).toString("base64");
|
|
41
|
+
if (!(await storage.put(storageKey, bytes))) {
|
|
42
|
+
throw new HTTPException(500, { message: "File upload failed" });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const attachment = await config.db.attachments
|
|
46
|
+
.createPending(
|
|
47
|
+
{
|
|
48
|
+
threadId,
|
|
49
|
+
userId,
|
|
50
|
+
messageId: null,
|
|
51
|
+
filename: file.name,
|
|
52
|
+
contentType: file.type,
|
|
53
|
+
sizeBytes: file.size,
|
|
54
|
+
storageKey,
|
|
55
|
+
status: "pending",
|
|
56
|
+
},
|
|
57
|
+
MAX_ATTACHMENTS_PER_MESSAGE,
|
|
58
|
+
)
|
|
59
|
+
.catch(async function (error: unknown) {
|
|
60
|
+
await storage.delete([storageKey]);
|
|
61
|
+
throw error;
|
|
62
|
+
});
|
|
63
|
+
if (!attachment) {
|
|
64
|
+
await storage.delete([storageKey]);
|
|
65
|
+
throw new HTTPException(422, { message: "Attachment limit reached" });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return c.json(toAttachmentSummary(attachment));
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
app.get("/files/:id", requireAuth, async function (c) {
|
|
73
|
+
const config = c.get("agentConfig");
|
|
74
|
+
const storage = requireStorage(config);
|
|
75
|
+
|
|
76
|
+
const attachment = await config.db.attachments.getById(c.req.param("id"), c.get("user").id);
|
|
77
|
+
if (!attachment) {
|
|
78
|
+
throw new HTTPException(404, { message: "File not found" });
|
|
79
|
+
}
|
|
80
|
+
await requireOwnedThread(c, attachment.threadId);
|
|
81
|
+
|
|
82
|
+
const filename = attachment.filename
|
|
83
|
+
.replace(/[\r\n"\\]/g, "")
|
|
84
|
+
.replace(/[^\u0020-\u007e]/g, "_");
|
|
85
|
+
|
|
86
|
+
return new Response(await storage.stream(attachment.storageKey), {
|
|
87
|
+
headers: {
|
|
88
|
+
"Content-Type": attachment.contentType,
|
|
89
|
+
"Content-Disposition": `attachment; filename="${filename}"; filename*=UTF-8''${encodeURIComponent(attachment.filename)}`,
|
|
90
|
+
"X-Content-Type-Options": "nosniff",
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
app.delete("/files/:id", requireAuth, async function (c) {
|
|
96
|
+
const config = c.get("agentConfig");
|
|
97
|
+
const storage = requireStorage(config);
|
|
98
|
+
|
|
99
|
+
const id = c.req.param("id");
|
|
100
|
+
const userId = c.get("user").id;
|
|
101
|
+
const attachment = await config.db.attachments.getById(id, userId);
|
|
102
|
+
if (!attachment) {
|
|
103
|
+
throw new HTTPException(404, { message: "File not found" });
|
|
104
|
+
}
|
|
105
|
+
await requireOwnedThread(c, attachment.threadId);
|
|
106
|
+
if (attachment.messageId !== null) {
|
|
107
|
+
throw new HTTPException(409, { message: "File is attached to a message" });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!(await storage.delete([attachment.storageKey]))) {
|
|
111
|
+
throw new HTTPException(502, { message: "File deletion failed" });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!(await config.db.attachments.remove(id, userId))) {
|
|
115
|
+
throw new HTTPException(404, { message: "File not found" });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return c.body(null, 204);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return app;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function requireStorage(config: ResolvedCortexAgentConfig) {
|
|
125
|
+
if (!config.storage) {
|
|
126
|
+
throw new HTTPException(503, { message: "File storage is not configured" });
|
|
127
|
+
}
|
|
128
|
+
return config.storage;
|
|
129
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Context } from "hono";
|
|
2
|
+
import { HTTPException } from "hono/http-exception";
|
|
3
|
+
import type { CortexAppEnv } from "@/types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Loads a thread and asserts it belongs to both the caller and the agent in the
|
|
7
|
+
* path. A thread owned by another agent 404s rather than 403 on purpose — a 403
|
|
8
|
+
* would confirm the thread exists, making cross-agent ids probeable.
|
|
9
|
+
*/
|
|
10
|
+
export async function requireOwnedThread(c: Context<CortexAppEnv>, threadId: string) {
|
|
11
|
+
const thread = await c.get("agentConfig").db.threads.getById(c.get("user").id, threadId);
|
|
12
|
+
if (!thread || thread.agentId !== c.get("agentId")) {
|
|
13
|
+
throw new HTTPException(404, { message: "Not found" });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return thread;
|
|
17
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import { zValidator } from "@hono/zod-validator";
|
|
3
3
|
import z from "zod";
|
|
4
|
-
import type { CortexAppEnv } from "
|
|
5
|
-
import { requireAuth } from "
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { toThreadSummary } from "
|
|
4
|
+
import type { CortexAppEnv } from "@/types";
|
|
5
|
+
import { requireAuth } from "@/auth/middleware";
|
|
6
|
+
import { abortRun, runningThreadIds } from "@/ai/active-runs";
|
|
7
|
+
import { notify } from "@/ws/connections";
|
|
8
|
+
import { requireOwnedThread } from "./owned-thread";
|
|
9
|
+
import { toThreadSummary } from "@/types";
|
|
10
10
|
|
|
11
11
|
export function createThreadRoutes() {
|
|
12
12
|
const app = new Hono<CortexAppEnv>();
|
|
@@ -14,21 +14,19 @@ export function createThreadRoutes() {
|
|
|
14
14
|
app.get("/threads", requireAuth, async function (c) {
|
|
15
15
|
const config = c.get("agentConfig");
|
|
16
16
|
const agentId = c.get("agentId");
|
|
17
|
-
const threads = await config.db.threads
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return c.json(threads);
|
|
17
|
+
const threads = await config.db.threads.list(c.get("user").id, agentId);
|
|
18
|
+
const running = await runningThreadIds(threads.map((thread) => thread.id));
|
|
19
|
+
return c.json(threads.map((thread) => toThreadSummary(thread, running.has(thread.id))));
|
|
21
20
|
});
|
|
22
21
|
|
|
23
22
|
app.post(
|
|
24
23
|
"/threads",
|
|
25
24
|
requireAuth,
|
|
26
|
-
zValidator("json", z.object({ prompt: z.string() })),
|
|
25
|
+
zValidator("json", z.object({ prompt: z.string().optional() })),
|
|
27
26
|
async function (c) {
|
|
28
27
|
const config = c.get("agentConfig");
|
|
29
28
|
const agentId = c.get("agentId");
|
|
30
29
|
const userId = c.get("user").id;
|
|
31
|
-
const { prompt } = c.req.valid("json");
|
|
32
30
|
const thread = await config.db.threads.create(userId, agentId);
|
|
33
31
|
|
|
34
32
|
notify(userId, agentId, {
|
|
@@ -36,8 +34,6 @@ export function createThreadRoutes() {
|
|
|
36
34
|
payload: { thread: toThreadSummary(thread, false) },
|
|
37
35
|
});
|
|
38
36
|
|
|
39
|
-
generateTitle(thread.id, prompt, userId, config);
|
|
40
|
-
|
|
41
37
|
return c.json(toThreadSummary(thread, false));
|
|
42
38
|
},
|
|
43
39
|
);
|
|
@@ -48,12 +44,9 @@ export function createThreadRoutes() {
|
|
|
48
44
|
const userId = c.get("user").id;
|
|
49
45
|
const threadId = c.req.param("threadId");
|
|
50
46
|
|
|
51
|
-
|
|
52
|
-
if (!thread || thread.agentId !== agentId) {
|
|
53
|
-
return c.body(null, 404);
|
|
54
|
-
}
|
|
47
|
+
await requireOwnedThread(c, threadId);
|
|
55
48
|
|
|
56
|
-
|
|
49
|
+
await abortRun(threadId);
|
|
57
50
|
await config.db.threads.delete(userId, threadId);
|
|
58
51
|
|
|
59
52
|
notify(userId, agentId, {
|
|
@@ -66,13 +59,9 @@ export function createThreadRoutes() {
|
|
|
66
59
|
|
|
67
60
|
app.get("/threads/:threadId/messages", requireAuth, async function (c) {
|
|
68
61
|
const config = c.get("agentConfig");
|
|
69
|
-
const agentId = c.get("agentId");
|
|
70
62
|
const userId = c.get("user").id;
|
|
71
63
|
const threadId = c.req.param("threadId");
|
|
72
|
-
|
|
73
|
-
if (!thread || thread.agentId !== agentId) {
|
|
74
|
-
return c.body(null, 404);
|
|
75
|
-
}
|
|
64
|
+
await requireOwnedThread(c, threadId);
|
|
76
65
|
|
|
77
66
|
const messages = await config.db.messages
|
|
78
67
|
.list(userId, threadId)
|
|
@@ -80,20 +69,22 @@ export function createThreadRoutes() {
|
|
|
80
69
|
return c.json(messages);
|
|
81
70
|
});
|
|
82
71
|
|
|
72
|
+
app.get("/messages/:messageId/llm-requests", requireAuth, async function (c) {
|
|
73
|
+
const config = c.get("agentConfig");
|
|
74
|
+
const messageId = c.req.param("messageId");
|
|
75
|
+
const requests = await config.db.llmRequests.listByMessageId(messageId);
|
|
76
|
+
return c.json(requests);
|
|
77
|
+
});
|
|
78
|
+
|
|
83
79
|
app.post(
|
|
84
80
|
"/threads/:threadId/session",
|
|
85
81
|
requireAuth,
|
|
86
|
-
zValidator("json", z.object({ session: z.record(z.unknown()) })),
|
|
82
|
+
zValidator("json", z.object({ session: z.record(z.string(), z.unknown()) })),
|
|
87
83
|
async function (c) {
|
|
88
84
|
const config = c.get("agentConfig");
|
|
89
|
-
const agentId = c.get("agentId");
|
|
90
|
-
const userId = c.get("user").id;
|
|
91
85
|
const threadId = c.req.param("threadId");
|
|
92
86
|
const { session } = c.req.valid("json");
|
|
93
|
-
|
|
94
|
-
if (!thread || thread.agentId !== agentId) {
|
|
95
|
-
return c.body(null, 404);
|
|
96
|
-
}
|
|
87
|
+
await requireOwnedThread(c, threadId);
|
|
97
88
|
|
|
98
89
|
await config.db.threads.updateSession(threadId, session);
|
|
99
90
|
return c.body(null, 204);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import { upgradeWebSocket } from "hono/bun";
|
|
3
|
-
import type { AppEnv } from "
|
|
4
|
-
import { addConnection, removeConnection } from "
|
|
3
|
+
import type { AppEnv } from "@/types";
|
|
4
|
+
import { addConnection, removeConnection } from "@/ws/connections";
|
|
5
5
|
|
|
6
6
|
type WsRouteOptions = {
|
|
7
7
|
useAgentParam?: boolean;
|
|
@@ -10,7 +10,9 @@ type WsRouteOptions = {
|
|
|
10
10
|
export function createWsRoute(options?: WsRouteOptions) {
|
|
11
11
|
const app = new Hono<AppEnv>();
|
|
12
12
|
const handleUpgrade = upgradeWebSocket(function (c) {
|
|
13
|
-
|
|
13
|
+
// upgradeWebSocket erases the app's env generic, so the context reads as any.
|
|
14
|
+
const user = c.get("user") as AppEnv["Variables"]["user"];
|
|
15
|
+
const userId = user?.id;
|
|
14
16
|
const agentId =
|
|
15
17
|
options?.useAgentParam === true ? c.req.param("agentId") : c.req.query("agentId");
|
|
16
18
|
|
|
@@ -40,6 +42,10 @@ export function createWsRoute(options?: WsRouteOptions) {
|
|
|
40
42
|
return c.json({ error: "agentId is required" }, 400);
|
|
41
43
|
}
|
|
42
44
|
|
|
45
|
+
// upgradeWebSocket types as a void middleware while tsc requires the route
|
|
46
|
+
// to produce a Response; the lint checker resolves the generics differently
|
|
47
|
+
// and would auto-strip the bridge cast, so pin it.
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
43
49
|
return handleUpgrade(c, next) as unknown as Response;
|
|
44
50
|
});
|
|
45
51
|
|
package/src/lib/types.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { MessagePart } from "@tanstack/ai";
|
|
2
|
+
import type { ResolvedCortexAgentConfig } from "./config";
|
|
3
|
+
import type { InferSelectModel } from "drizzle-orm";
|
|
4
|
+
import type { attachments, messages, threads } from "./db/schema.mssql";
|
|
5
|
+
|
|
6
|
+
export type {
|
|
7
|
+
CortexMessage,
|
|
8
|
+
MessageMetadata,
|
|
9
|
+
ThreadSummary,
|
|
10
|
+
TokenUsage,
|
|
11
|
+
} from "@cortex/contracts/wire";
|
|
12
|
+
|
|
13
|
+
import type { AttachmentSummary, CortexMessage, ThreadSummary } from "@cortex/contracts/wire";
|
|
14
|
+
|
|
15
|
+
/** A message as cortex persists and serves it: SDK parts plus cortex's envelope. */
|
|
16
|
+
export type ChatMessage = CortexMessage<MessagePart>;
|
|
17
|
+
|
|
18
|
+
export type Thread = InferSelectModel<typeof threads>;
|
|
19
|
+
|
|
20
|
+
export type StoredMessage = InferSelectModel<typeof messages>;
|
|
21
|
+
|
|
22
|
+
export type Attachment = InferSelectModel<typeof attachments>;
|
|
23
|
+
|
|
24
|
+
export type NewAttachment = typeof attachments.$inferInsert;
|
|
25
|
+
|
|
26
|
+
export type AppEnv = {
|
|
27
|
+
// Hono's env generics only stay assignable across helpers with the {} idiom.
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
29
|
+
Bindings: {};
|
|
30
|
+
Variables: {
|
|
31
|
+
user: { id: string; token: string } | undefined;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type AuthedAppEnv = {
|
|
36
|
+
// Hono's env generics only stay assignable across helpers with the {} idiom.
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
38
|
+
Bindings: {};
|
|
39
|
+
Variables: {
|
|
40
|
+
user: { id: string; token: string };
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type CortexAppEnv = {
|
|
45
|
+
// Hono's env generics only stay assignable across helpers with the {} idiom.
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
47
|
+
Bindings: {};
|
|
48
|
+
Variables: {
|
|
49
|
+
user: { id: string; token: string };
|
|
50
|
+
agentConfig: ResolvedCortexAgentConfig;
|
|
51
|
+
agentId: string;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/** Maps a stored row to the wire shape. Dates become ISO strings — the same bytes
|
|
56
|
+
* `JSON.stringify` would have produced, but now the type says so. */
|
|
57
|
+
export function toThreadSummary(thread: Thread, isRunning: boolean) {
|
|
58
|
+
return {
|
|
59
|
+
id: thread.id,
|
|
60
|
+
title: thread.title ?? undefined,
|
|
61
|
+
createdAt: thread.createdAt.toISOString(),
|
|
62
|
+
updatedAt: thread.updatedAt.toISOString(),
|
|
63
|
+
isRunning,
|
|
64
|
+
} satisfies ThreadSummary;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function toAttachmentSummary(attachment: Attachment) {
|
|
68
|
+
return {
|
|
69
|
+
id: attachment.id,
|
|
70
|
+
filename: attachment.filename,
|
|
71
|
+
contentType: attachment.contentType,
|
|
72
|
+
sizeBytes: attachment.sizeBytes,
|
|
73
|
+
status: attachment.status,
|
|
74
|
+
description: attachment.description ?? undefined,
|
|
75
|
+
} satisfies AttachmentSummary;
|
|
76
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { WSContext } from "hono/ws";
|
|
2
|
+
import type { WsEvent } from "@cortex/contracts/wire";
|
|
2
3
|
|
|
3
4
|
const connections = new Map<string, WSContext[]>();
|
|
4
5
|
|
|
@@ -24,6 +25,13 @@ export function removeConnection(userId: string, agentId: string, ws: WSContext)
|
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
function getConnections(userId: string, agentId: string) {
|
|
28
29
|
return connections.get(getConnectionKey(userId, agentId)) ?? [];
|
|
29
30
|
}
|
|
31
|
+
|
|
32
|
+
export function notify(userId: string, agentId: string, event: WsEvent) {
|
|
33
|
+
const message = JSON.stringify(event);
|
|
34
|
+
for (const ws of getConnections(userId, agentId)) {
|
|
35
|
+
ws.send(message);
|
|
36
|
+
}
|
|
37
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
// Environment setup & latest features
|
|
4
|
+
"lib": ["ESNext"],
|
|
5
|
+
// Named explicitly: auto-inclusion of @types/* is an editor-dependent
|
|
6
|
+
// heuristic, and the `Bun` global comes from here.
|
|
7
|
+
"types": ["bun"],
|
|
8
|
+
"target": "ESNext",
|
|
9
|
+
"module": "Preserve",
|
|
10
|
+
"moduleDetection": "force",
|
|
11
|
+
"jsx": "react-jsx",
|
|
12
|
+
"allowJs": true,
|
|
13
|
+
"baseUrl": ".",
|
|
14
|
+
|
|
15
|
+
// Bundler mode
|
|
16
|
+
"moduleResolution": "bundler",
|
|
17
|
+
"verbatimModuleSyntax": true,
|
|
18
|
+
"noEmit": true,
|
|
19
|
+
"paths": {
|
|
20
|
+
"@m6d/cortex-server": ["./index.ts"],
|
|
21
|
+
// The API contracts live in internal/contracts, a private workspace
|
|
22
|
+
// package that never publishes; this package ships raw source, so
|
|
23
|
+
// `vendor` copies them into ./contracts for build and pack. The first
|
|
24
|
+
// candidate exists only then (and in the published tarball) — inside
|
|
25
|
+
// the repo resolution falls through to the workspace source.
|
|
26
|
+
"@cortex/contracts/*": ["./contracts/*", "../../internal/contracts/*"],
|
|
27
|
+
// Internal-only. tsc does not rewrite aliases on emit, so `build` runs
|
|
28
|
+
// tsc-alias to turn these back into relative paths in dist/**/*.d.ts —
|
|
29
|
+
// a consumer's tsc knows nothing about our `@/`. The published raw src
|
|
30
|
+
// keeps `@/`, which resolves because Bun reads this file (hence its
|
|
31
|
+
// presence in package.json `files`).
|
|
32
|
+
"@/*": ["./src/lib/*"]
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
// Best practices
|
|
36
|
+
"strict": true,
|
|
37
|
+
"skipLibCheck": true,
|
|
38
|
+
"noFallthroughCasesInSwitch": true,
|
|
39
|
+
"noUncheckedIndexedAccess": true,
|
|
40
|
+
"noImplicitOverride": true,
|
|
41
|
+
|
|
42
|
+
// Some stricter flags (disabled by default)
|
|
43
|
+
"noUnusedLocals": false,
|
|
44
|
+
"noUnusedParameters": false,
|
|
45
|
+
"noPropertyAccessFromIndexSignature": false
|
|
46
|
+
},
|
|
47
|
+
"include": ["src/lib/**/*.ts", "scripts/**/*.ts", "index.ts"]
|
|
48
|
+
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { ToolUIPart, UIMessage } from "ai";
|
|
2
|
-
import type { Thread, StoredMessage, CapturedFileInput } from "../types";
|
|
3
|
-
import type { ThreadContextMeta } from "../ai/context/types.ts";
|
|
4
|
-
export type DatabaseAdapter = {
|
|
5
|
-
threads: {
|
|
6
|
-
list(userId: string, agentId: string): Promise<Thread[]>;
|
|
7
|
-
getById(userId: string, threadId: string): Promise<Thread | null>;
|
|
8
|
-
create(userId: string, agentId: string): Promise<Thread>;
|
|
9
|
-
delete(userId: string, threadId: string): Promise<void>;
|
|
10
|
-
touch(threadId: string): Promise<Thread>;
|
|
11
|
-
updateTitle(threadId: string, title: string): Promise<void>;
|
|
12
|
-
updateSession(threadId: string, session: Record<string, unknown>): Promise<void>;
|
|
13
|
-
updateContextMeta(threadId: string, meta: ThreadContextMeta): Promise<void>;
|
|
14
|
-
};
|
|
15
|
-
messages: {
|
|
16
|
-
list(userId: string, threadId: string, opts?: {
|
|
17
|
-
limit?: number;
|
|
18
|
-
}): Promise<StoredMessage[]>;
|
|
19
|
-
upsert(threadId: string, messages: UIMessage[]): Promise<void>;
|
|
20
|
-
};
|
|
21
|
-
capturedFiles: {
|
|
22
|
-
create(userId: string, messageId: string, toolPart: ToolUIPart, files: CapturedFileInput[]): Promise<{
|
|
23
|
-
id: string;
|
|
24
|
-
uploadId: string;
|
|
25
|
-
}[]>;
|
|
26
|
-
getById(id: string, userId: string): Promise<{
|
|
27
|
-
name: string;
|
|
28
|
-
} | null>;
|
|
29
|
-
};
|
|
30
|
-
};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
type ActiveStream = {
|
|
2
|
-
id: string;
|
|
3
|
-
abortController: AbortController;
|
|
4
|
-
buffer: string[];
|
|
5
|
-
subscribers: Set<ReadableStreamDefaultController<string>>;
|
|
6
|
-
isComplete: boolean;
|
|
7
|
-
};
|
|
8
|
-
export declare function registerStream(threadId: string, abortController: AbortController): ActiveStream;
|
|
9
|
-
export declare function attachSseStream(threadId: string, sseStream: ReadableStream<string>): void;
|
|
10
|
-
export declare function subscribe(threadId: string): ReadableStream<string> | null;
|
|
11
|
-
export declare function abortStream(threadId: string): boolean;
|
|
12
|
-
export declare function isStreamRunning(threadId: string): boolean;
|
|
13
|
-
export declare function removeStream(threadId: string, streamId?: string): void;
|
|
14
|
-
export {};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { UIMessage } from "ai";
|
|
2
|
-
import type { MessageMetadata } from "src/types.ts";
|
|
3
|
-
/**
|
|
4
|
-
* Returns a new array of messages with large tool outputs truncated
|
|
5
|
-
* to `maxTokensPerResult`. Does not mutate the input messages.
|
|
6
|
-
*/
|
|
7
|
-
export declare function compressToolResults(messages: UIMessage<MessageMetadata>[], maxTokensPerResult: number): UIMessage<MessageMetadata, import("ai").UIDataTypes, import("ai").UITools>[];
|