@copilotkit/channels 0.1.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/LICENSE +21 -0
- package/README.md +204 -0
- package/dist/action-registry.d.ts +59 -0
- package/dist/action-registry.d.ts.map +1 -0
- package/dist/action-registry.js +217 -0
- package/dist/action-registry.test.d.ts +2 -0
- package/dist/action-registry.test.d.ts.map +1 -0
- package/dist/action-registry.test.js +115 -0
- package/dist/action-store.d.ts +21 -0
- package/dist/action-store.d.ts.map +1 -0
- package/dist/action-store.js +23 -0
- package/dist/action-store.test.d.ts +2 -0
- package/dist/action-store.test.d.ts.map +1 -0
- package/dist/action-store.test.js +27 -0
- package/dist/codec.d.ts +25 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +1 -0
- package/dist/commands.d.ts +73 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +38 -0
- package/dist/commands.test.d.ts +2 -0
- package/dist/commands.test.d.ts.map +1 -0
- package/dist/commands.test.js +23 -0
- package/dist/create-bot.d.ts +171 -0
- package/dist/create-bot.d.ts.map +1 -0
- package/dist/create-bot.foundations.test.d.ts +2 -0
- package/dist/create-bot.foundations.test.d.ts.map +1 -0
- package/dist/create-bot.foundations.test.js +147 -0
- package/dist/create-bot.js +576 -0
- package/dist/create-bot.test.d.ts +2 -0
- package/dist/create-bot.test.d.ts.map +1 -0
- package/dist/create-bot.test.js +936 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/mint-id.d.ts +3 -0
- package/dist/mint-id.d.ts.map +1 -0
- package/dist/mint-id.js +20 -0
- package/dist/mint-id.test.d.ts +2 -0
- package/dist/mint-id.test.d.ts.map +1 -0
- package/dist/mint-id.test.js +16 -0
- package/dist/modal-submit.test.d.ts +2 -0
- package/dist/modal-submit.test.d.ts.map +1 -0
- package/dist/modal-submit.test.js +68 -0
- package/dist/platform-adapter.d.ts +312 -0
- package/dist/platform-adapter.d.ts.map +1 -0
- package/dist/platform-adapter.js +1 -0
- package/dist/platform-adapter.test.d.ts +2 -0
- package/dist/platform-adapter.test.d.ts.map +1 -0
- package/dist/platform-adapter.test.js +40 -0
- package/dist/reactions.test.d.ts +2 -0
- package/dist/reactions.test.d.ts.map +1 -0
- package/dist/reactions.test.js +197 -0
- package/dist/run-loop.d.ts +41 -0
- package/dist/run-loop.d.ts.map +1 -0
- package/dist/run-loop.js +102 -0
- package/dist/run-loop.test.d.ts +2 -0
- package/dist/run-loop.test.d.ts.map +1 -0
- package/dist/run-loop.test.js +125 -0
- package/dist/standard-schema.d.ts +52 -0
- package/dist/standard-schema.d.ts.map +1 -0
- package/dist/standard-schema.js +66 -0
- package/dist/state/duration.d.ts +3 -0
- package/dist/state/duration.d.ts.map +1 -0
- package/dist/state/duration.js +16 -0
- package/dist/state/duration.test.d.ts +2 -0
- package/dist/state/duration.test.d.ts.map +1 -0
- package/dist/state/duration.test.js +13 -0
- package/dist/state/kv-action-store.d.ts +7 -0
- package/dist/state/kv-action-store.d.ts.map +1 -0
- package/dist/state/kv-action-store.js +9 -0
- package/dist/state/kv-action-store.test.d.ts +2 -0
- package/dist/state/kv-action-store.test.d.ts.map +1 -0
- package/dist/state/kv-action-store.test.js +21 -0
- package/dist/state/memory-store.d.ts +41 -0
- package/dist/state/memory-store.d.ts.map +1 -0
- package/dist/state/memory-store.js +110 -0
- package/dist/state/memory-store.test.d.ts +2 -0
- package/dist/state/memory-store.test.d.ts.map +1 -0
- package/dist/state/memory-store.test.js +22 -0
- package/dist/state/state-conversation-store.d.ts +14 -0
- package/dist/state/state-conversation-store.d.ts.map +1 -0
- package/dist/state/state-conversation-store.js +22 -0
- package/dist/state/state-conversation-store.test.d.ts +2 -0
- package/dist/state/state-conversation-store.test.d.ts.map +1 -0
- package/dist/state/state-conversation-store.test.js +58 -0
- package/dist/state/state-store.d.ts +63 -0
- package/dist/state/state-store.d.ts.map +1 -0
- package/dist/state/state-store.js +1 -0
- package/dist/telemetry/bot-telemetry.d.ts +31 -0
- package/dist/telemetry/bot-telemetry.d.ts.map +1 -0
- package/dist/telemetry/bot-telemetry.js +70 -0
- package/dist/telemetry/bot-telemetry.test.d.ts +2 -0
- package/dist/telemetry/bot-telemetry.test.d.ts.map +1 -0
- package/dist/telemetry/bot-telemetry.test.js +56 -0
- package/dist/telemetry/create-bot-telemetry.test.d.ts +2 -0
- package/dist/telemetry/create-bot-telemetry.test.d.ts.map +1 -0
- package/dist/telemetry/create-bot-telemetry.test.js +71 -0
- package/dist/telemetry/e2e-telemetry.test.d.ts +2 -0
- package/dist/telemetry/e2e-telemetry.test.d.ts.map +1 -0
- package/dist/telemetry/e2e-telemetry.test.js +56 -0
- package/dist/telemetry/events-catalog.test.d.ts +2 -0
- package/dist/telemetry/events-catalog.test.d.ts.map +1 -0
- package/dist/telemetry/events-catalog.test.js +8 -0
- package/dist/telemetry/install-id.d.ts +8 -0
- package/dist/telemetry/install-id.d.ts.map +1 -0
- package/dist/telemetry/install-id.js +43 -0
- package/dist/telemetry/install-id.test.d.ts +2 -0
- package/dist/telemetry/install-id.test.d.ts.map +1 -0
- package/dist/telemetry/install-id.test.js +43 -0
- package/dist/telemetry/platform-allowlist-coverage.test.d.ts +2 -0
- package/dist/telemetry/platform-allowlist-coverage.test.d.ts.map +1 -0
- package/dist/telemetry/platform-allowlist-coverage.test.js +45 -0
- package/dist/telemetry/sanitize-error.d.ts +3 -0
- package/dist/telemetry/sanitize-error.d.ts.map +1 -0
- package/dist/telemetry/sanitize-error.js +31 -0
- package/dist/telemetry/sanitize-error.test.d.ts +2 -0
- package/dist/telemetry/sanitize-error.test.d.ts.map +1 -0
- package/dist/telemetry/sanitize-error.test.js +33 -0
- package/dist/testing/fake-adapter.d.ts +137 -0
- package/dist/testing/fake-adapter.d.ts.map +1 -0
- package/dist/testing/fake-adapter.js +273 -0
- package/dist/testing/fake-agent.d.ts +28 -0
- package/dist/testing/fake-agent.d.ts.map +1 -0
- package/dist/testing/fake-agent.js +36 -0
- package/dist/testing/state-store-conformance.d.ts +4 -0
- package/dist/testing/state-store-conformance.d.ts.map +1 -0
- package/dist/testing/state-store-conformance.js +117 -0
- package/dist/thread-capabilities.test.d.ts +2 -0
- package/dist/thread-capabilities.test.d.ts.map +1 -0
- package/dist/thread-capabilities.test.js +77 -0
- package/dist/thread-ephemeral.test.d.ts +2 -0
- package/dist/thread-ephemeral.test.d.ts.map +1 -0
- package/dist/thread-ephemeral.test.js +45 -0
- package/dist/thread-reactions.test.d.ts +2 -0
- package/dist/thread-reactions.test.d.ts.map +1 -0
- package/dist/thread-reactions.test.js +38 -0
- package/dist/thread.d.ts +152 -0
- package/dist/thread.d.ts.map +1 -0
- package/dist/thread.js +329 -0
- package/dist/thread.test.d.ts +2 -0
- package/dist/thread.test.d.ts.map +1 -0
- package/dist/thread.test.js +83 -0
- package/dist/tools.d.ts +67 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +38 -0
- package/dist/tools.test.d.ts +2 -0
- package/dist/tools.test.d.ts.map +1 -0
- package/dist/tools.test.js +31 -0
- package/dist/transcripts.d.ts +52 -0
- package/dist/transcripts.d.ts.map +1 -0
- package/dist/transcripts.js +67 -0
- package/dist/transcripts.test.d.ts +2 -0
- package/dist/transcripts.test.d.ts.map +1 -0
- package/dist/transcripts.test.js +108 -0
- package/package.json +72 -0
package/dist/tools.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { toJsonSchema, validateSchema } from "./standard-schema.js";
|
|
2
|
+
/**
|
|
3
|
+
* Define a {@link BotTool} with full type inference. The handler's `args` are
|
|
4
|
+
* inferred from `parameters`, and `ctx` is the generic {@link BotToolContext}
|
|
5
|
+
* ({@link Thread} + optional message/user/signal + platform). Reach for
|
|
6
|
+
* platform power via capability-gated `thread` methods (e.g.
|
|
7
|
+
* `thread.getMessages()`, `thread.lookupUser(query)`).
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* const tool = defineBotTool({
|
|
11
|
+
* name: "show_thing",
|
|
12
|
+
* description: "...",
|
|
13
|
+
* parameters: z.object({ id: z.string() }),
|
|
14
|
+
* async handler({ id }, { thread }) { // `id` and `ctx` fully typed
|
|
15
|
+
* await thread.post(<Thing id={id} />);
|
|
16
|
+
* return "Displayed the thing.";
|
|
17
|
+
* },
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function defineBotTool(tool) {
|
|
22
|
+
return tool;
|
|
23
|
+
}
|
|
24
|
+
export function toAgentToolDescriptors(tools) {
|
|
25
|
+
return tools.map((t) => ({
|
|
26
|
+
name: t.name,
|
|
27
|
+
description: t.description,
|
|
28
|
+
parameters: toJsonSchema(t.parameters),
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
export const parseToolArgs = validateSchema;
|
|
32
|
+
export function stringifyHandlerResult(value) {
|
|
33
|
+
if (value == null)
|
|
34
|
+
return "";
|
|
35
|
+
if (typeof value === "string")
|
|
36
|
+
return value;
|
|
37
|
+
return JSON.stringify(value);
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.test.d.ts","sourceRoot":"","sources":["../src/tools.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { toAgentToolDescriptors, parseToolArgs, stringifyHandlerResult, } from "./tools.js";
|
|
4
|
+
describe("tools", () => {
|
|
5
|
+
const tool = {
|
|
6
|
+
name: "t",
|
|
7
|
+
description: "d",
|
|
8
|
+
parameters: z.object({ q: z.string() }),
|
|
9
|
+
handler: () => "ok",
|
|
10
|
+
};
|
|
11
|
+
it("emits JSON-schema descriptor", () => {
|
|
12
|
+
const descriptors = toAgentToolDescriptors([tool]);
|
|
13
|
+
const d = descriptors[0];
|
|
14
|
+
if (!d)
|
|
15
|
+
throw new Error("expected a descriptor");
|
|
16
|
+
expect(d.name).toBe("t");
|
|
17
|
+
expect(d.parameters.type).toBe("object");
|
|
18
|
+
});
|
|
19
|
+
it("parses valid and rejects invalid args", async () => {
|
|
20
|
+
expect(await parseToolArgs(tool.parameters, { q: "x" })).toEqual({
|
|
21
|
+
ok: true,
|
|
22
|
+
value: { q: "x" },
|
|
23
|
+
});
|
|
24
|
+
expect((await parseToolArgs(tool.parameters, {})).ok).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
it("stringifies handler results", () => {
|
|
27
|
+
expect(stringifyHandlerResult("s")).toBe("s");
|
|
28
|
+
expect(stringifyHandlerResult({ a: 1 })).toBe('{"a":1}');
|
|
29
|
+
expect(stringifyHandlerResult(undefined)).toBe("");
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { StateStore } from "./state/state-store.js";
|
|
2
|
+
import type { PlatformUser, IncomingMessage } from "@copilotkit/channels-ui";
|
|
3
|
+
export interface TranscriptEntry {
|
|
4
|
+
role: "user" | "assistant";
|
|
5
|
+
text: string;
|
|
6
|
+
platform: string;
|
|
7
|
+
threadId: string;
|
|
8
|
+
userKey: string;
|
|
9
|
+
ts: number;
|
|
10
|
+
}
|
|
11
|
+
export type Identity = (ctx: {
|
|
12
|
+
adapter: string;
|
|
13
|
+
author: PlatformUser;
|
|
14
|
+
message: IncomingMessage;
|
|
15
|
+
}) => string | null | Promise<string | null>;
|
|
16
|
+
export interface TranscriptsConfig {
|
|
17
|
+
retention?: string | number;
|
|
18
|
+
maxPerUser?: number;
|
|
19
|
+
}
|
|
20
|
+
export declare class Transcripts {
|
|
21
|
+
private state;
|
|
22
|
+
private cfg;
|
|
23
|
+
private readonly retentionMs;
|
|
24
|
+
constructor(state: StateStore, cfg?: TranscriptsConfig);
|
|
25
|
+
/**
|
|
26
|
+
* Append a message to the user's transcript.
|
|
27
|
+
* No-ops silently when no `userKey` is resolved (from `opts.userKey` or `msg.userKey`).
|
|
28
|
+
*/
|
|
29
|
+
append(thread: {
|
|
30
|
+
platform: string;
|
|
31
|
+
conversationKey: string;
|
|
32
|
+
}, msg: {
|
|
33
|
+
role?: "user" | "assistant";
|
|
34
|
+
text: string;
|
|
35
|
+
userKey?: string;
|
|
36
|
+
}, opts?: {
|
|
37
|
+
userKey?: string;
|
|
38
|
+
}): Promise<void>;
|
|
39
|
+
list(q: {
|
|
40
|
+
userKey: string;
|
|
41
|
+
limit?: number;
|
|
42
|
+
platforms?: string[];
|
|
43
|
+
threadId?: string;
|
|
44
|
+
roles?: ("user" | "assistant")[];
|
|
45
|
+
}): Promise<TranscriptEntry[]>;
|
|
46
|
+
delete(q: {
|
|
47
|
+
userKey: string;
|
|
48
|
+
}): Promise<{
|
|
49
|
+
deleted: number;
|
|
50
|
+
}>;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=transcripts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcripts.d.ts","sourceRoot":"","sources":["../src/transcripts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE7E,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,eAAe,CAAC;CAC1B,KAAK,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAE7C,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAID,qBAAa,WAAW;IAIpB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG;IAJb,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;gBAGvC,KAAK,EAAE,UAAU,EACjB,GAAG,GAAE,iBAAsB;IAMrC;;;OAGG;IACG,MAAM,CACV,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,EACrD,GAAG,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,EACpE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1B,OAAO,CAAC,IAAI,CAAC;IA2BV,IAAI,CAAC,CAAC,EAAE;QACZ,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC;KAClC,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAcxB,MAAM,CAAC,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAKnE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { parseDuration } from "./state/duration.js";
|
|
2
|
+
const keyFor = (userKey) => `transcript:user:${userKey}`;
|
|
3
|
+
export class Transcripts {
|
|
4
|
+
state;
|
|
5
|
+
cfg;
|
|
6
|
+
retentionMs;
|
|
7
|
+
constructor(state, cfg = {}) {
|
|
8
|
+
this.state = state;
|
|
9
|
+
this.cfg = cfg;
|
|
10
|
+
this.retentionMs =
|
|
11
|
+
cfg.retention !== undefined ? parseDuration(cfg.retention) : undefined;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Append a message to the user's transcript.
|
|
15
|
+
* No-ops silently when no `userKey` is resolved (from `opts.userKey` or `msg.userKey`).
|
|
16
|
+
*/
|
|
17
|
+
async append(thread, msg, opts) {
|
|
18
|
+
const userKey = opts?.userKey ?? msg.userKey;
|
|
19
|
+
if (!userKey)
|
|
20
|
+
return; // identity unresolved → no-op
|
|
21
|
+
const entry = {
|
|
22
|
+
role: msg.role ?? "user",
|
|
23
|
+
text: msg.text,
|
|
24
|
+
platform: thread.platform,
|
|
25
|
+
threadId: thread.conversationKey,
|
|
26
|
+
userKey,
|
|
27
|
+
ts: Date.now(),
|
|
28
|
+
};
|
|
29
|
+
await this.state.list.append(keyFor(userKey), entry, {
|
|
30
|
+
maxLen: this.cfg.maxPerUser,
|
|
31
|
+
ttlMs: this.retentionMs,
|
|
32
|
+
});
|
|
33
|
+
if (this.retentionMs !== undefined) {
|
|
34
|
+
const cutoff = Date.now() - this.retentionMs;
|
|
35
|
+
const all = await this.state.list.range(keyFor(userKey));
|
|
36
|
+
const expired = all.filter((e) => e.ts < cutoff).length;
|
|
37
|
+
if (expired > 0) {
|
|
38
|
+
const survivors = all.length - expired;
|
|
39
|
+
if (survivors <= 0)
|
|
40
|
+
await this.state.list.delete(keyFor(userKey));
|
|
41
|
+
else
|
|
42
|
+
await this.state.list.trim(keyFor(userKey), survivors);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async list(q) {
|
|
47
|
+
let items = await this.state.list.range(keyFor(q.userKey));
|
|
48
|
+
if (this.retentionMs !== undefined) {
|
|
49
|
+
const cutoff = Date.now() - this.retentionMs;
|
|
50
|
+
items = items.filter((e) => e.ts >= cutoff);
|
|
51
|
+
}
|
|
52
|
+
if (q.platforms)
|
|
53
|
+
items = items.filter((e) => q.platforms.includes(e.platform));
|
|
54
|
+
if (q.threadId)
|
|
55
|
+
items = items.filter((e) => e.threadId === q.threadId);
|
|
56
|
+
if (q.roles)
|
|
57
|
+
items = items.filter((e) => q.roles.includes(e.role));
|
|
58
|
+
if (q.limit !== undefined)
|
|
59
|
+
items = items.slice(-q.limit);
|
|
60
|
+
return items; // oldest-first
|
|
61
|
+
}
|
|
62
|
+
async delete(q) {
|
|
63
|
+
const n = (await this.state.list.range(keyFor(q.userKey))).length;
|
|
64
|
+
await this.state.list.delete(keyFor(q.userKey));
|
|
65
|
+
return { deleted: n };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcripts.test.d.ts","sourceRoot":"","sources":["../src/transcripts.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { MemoryStore } from "./state/memory-store.js";
|
|
3
|
+
import { Transcripts } from "./transcripts.js";
|
|
4
|
+
const thread = {
|
|
5
|
+
platform: "slack",
|
|
6
|
+
conversationKey: "slack:C1:1",
|
|
7
|
+
};
|
|
8
|
+
describe("Transcripts", () => {
|
|
9
|
+
it("appends and lists oldest-first, filters compose", async () => {
|
|
10
|
+
const t = new Transcripts(new MemoryStore(), { maxPerUser: 100 });
|
|
11
|
+
await t.append(thread, { role: "user", text: "hi" }, { userKey: "u@x.com" });
|
|
12
|
+
await t.append(thread, { role: "assistant", text: "hello" }, { userKey: "u@x.com" });
|
|
13
|
+
const all = await t.list({ userKey: "u@x.com" });
|
|
14
|
+
expect(all.map((e) => e.role)).toEqual(["user", "assistant"]);
|
|
15
|
+
expect(await t.list({ userKey: "u@x.com", roles: ["user"] })).toHaveLength(1);
|
|
16
|
+
});
|
|
17
|
+
it("delete wipes and reports count", async () => {
|
|
18
|
+
const t = new Transcripts(new MemoryStore());
|
|
19
|
+
await t.append(thread, { role: "user", text: "x" }, { userKey: "k" });
|
|
20
|
+
expect(await t.delete({ userKey: "k" })).toEqual({ deleted: 1 });
|
|
21
|
+
expect(await t.list({ userKey: "k" })).toEqual([]);
|
|
22
|
+
});
|
|
23
|
+
it("TTL expiry: entries not visible after retention window", async () => {
|
|
24
|
+
const t = new Transcripts(new MemoryStore(), { retention: "30ms" });
|
|
25
|
+
await t.append(thread, { role: "user", text: "hi" }, { userKey: "u" });
|
|
26
|
+
await new Promise((r) => setTimeout(r, 60));
|
|
27
|
+
expect(await t.list({ userKey: "u" })).toEqual([]);
|
|
28
|
+
});
|
|
29
|
+
it("maxPerUser: only newest N entries are kept", async () => {
|
|
30
|
+
const t = new Transcripts(new MemoryStore(), { maxPerUser: 2 });
|
|
31
|
+
await t.append(thread, { role: "user", text: "1" }, { userKey: "u" });
|
|
32
|
+
await t.append(thread, { role: "user", text: "2" }, { userKey: "u" });
|
|
33
|
+
await t.append(thread, { role: "user", text: "3" }, { userKey: "u" });
|
|
34
|
+
const entries = await t.list({ userKey: "u" });
|
|
35
|
+
expect(entries).toHaveLength(2);
|
|
36
|
+
expect(entries.map((e) => e.text)).toEqual(["2", "3"]);
|
|
37
|
+
});
|
|
38
|
+
it("no userKey resolved: no-op, list returns empty for any key", async () => {
|
|
39
|
+
const t = new Transcripts(new MemoryStore());
|
|
40
|
+
// neither opts.userKey nor msg.userKey is set
|
|
41
|
+
await t.append(thread, { role: "user", text: "ghost" });
|
|
42
|
+
expect(await t.list({ userKey: "anyone" })).toEqual([]);
|
|
43
|
+
});
|
|
44
|
+
it("list with limit: returns only the last N entries", async () => {
|
|
45
|
+
const t = new Transcripts(new MemoryStore());
|
|
46
|
+
await t.append(thread, { role: "user", text: "a" }, { userKey: "u" });
|
|
47
|
+
await t.append(thread, { role: "user", text: "b" }, { userKey: "u" });
|
|
48
|
+
await t.append(thread, { role: "user", text: "c" }, { userKey: "u" });
|
|
49
|
+
const last = await t.list({ userKey: "u", limit: 1 });
|
|
50
|
+
expect(last).toHaveLength(1);
|
|
51
|
+
expect(last[0].text).toBe("c");
|
|
52
|
+
});
|
|
53
|
+
it("bogus retention string throws at construction", () => {
|
|
54
|
+
expect(() => new Transcripts(new MemoryStore(), { retention: "bogus" })).toThrow();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe("Transcripts retention", () => {
|
|
58
|
+
it("list() does not return entries older than the retention window", async () => {
|
|
59
|
+
const store = new MemoryStore();
|
|
60
|
+
const t = new Transcripts(store, { retention: "1h" });
|
|
61
|
+
vi.useFakeTimers();
|
|
62
|
+
try {
|
|
63
|
+
await t.append(thread, { role: "user", text: "old" }, { userKey: "u" });
|
|
64
|
+
vi.advanceTimersByTime(2 * 60 * 60 * 1000); // advance 2h
|
|
65
|
+
expect(await t.list({ userKey: "u" })).toEqual([]);
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
vi.useRealTimers();
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
it("append prunes entries older than retention while the list is kept alive", async () => {
|
|
72
|
+
// Isolates the prune path from the whole-key sliding TTL: each append
|
|
73
|
+
// refreshes the 1h key TTL, so the list never expires wholesale. A is
|
|
74
|
+
// only ever removed by the per-entry age prune — if the prune block were
|
|
75
|
+
// deleted, the whole-key TTL would keep all three entries alive and this
|
|
76
|
+
// would read length 3.
|
|
77
|
+
const store = new MemoryStore();
|
|
78
|
+
const t = new Transcripts(store, { retention: "1h" });
|
|
79
|
+
vi.useFakeTimers();
|
|
80
|
+
try {
|
|
81
|
+
await t.append(thread, { role: "user", text: "A" }, { userKey: "u" });
|
|
82
|
+
vi.advanceTimersByTime(50 * 60 * 1000); // +50m: A still within window, key TTL refreshed to +110m
|
|
83
|
+
await t.append(thread, { role: "user", text: "B" }, { userKey: "u" });
|
|
84
|
+
vi.advanceTimersByTime(50 * 60 * 1000); // +100m: key still live (expires +110m), but A is now 100m old
|
|
85
|
+
await t.append(thread, { role: "user", text: "C" }, { userKey: "u" });
|
|
86
|
+
const raw = await store.list.range("transcript:user:u");
|
|
87
|
+
expect(raw.map((e) => e.text)).toEqual(["B", "C"]); // A pruned by age, not whole-key expiry
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
vi.useRealTimers();
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
it("recent entries within the window are kept", async () => {
|
|
94
|
+
const store = new MemoryStore();
|
|
95
|
+
const t = new Transcripts(store, { retention: "1h" });
|
|
96
|
+
vi.useFakeTimers();
|
|
97
|
+
try {
|
|
98
|
+
await t.append(thread, { role: "user", text: "A" }, { userKey: "u" });
|
|
99
|
+
vi.advanceTimersByTime(30 * 60 * 1000); // advance 30m — A still within 1h
|
|
100
|
+
await t.append(thread, { role: "user", text: "B" }, { userKey: "u" });
|
|
101
|
+
const entries = await t.list({ userKey: "u" });
|
|
102
|
+
expect(entries).toHaveLength(2);
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
vi.useRealTimers();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@copilotkit/channels",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Platform-agnostic JSX channel engine for CopilotKit (createBot, Thread, PlatformAdapter, ActionStore).",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/CopilotKit/CopilotKit.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/CopilotKit/CopilotKit",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"ai",
|
|
13
|
+
"agent",
|
|
14
|
+
"bot",
|
|
15
|
+
"chatbot",
|
|
16
|
+
"copilotkit",
|
|
17
|
+
"slack",
|
|
18
|
+
"teams",
|
|
19
|
+
"discord",
|
|
20
|
+
"ag-ui"
|
|
21
|
+
],
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"main": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./testing": {
|
|
34
|
+
"types": "./dist/testing/state-store-conformance.d.ts",
|
|
35
|
+
"import": "./dist/testing/state-store-conformance.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist"
|
|
40
|
+
],
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@ag-ui/client": "0.0.57",
|
|
43
|
+
"@ag-ui/core": "0.0.57",
|
|
44
|
+
"zod-to-json-schema": "^3.24.1",
|
|
45
|
+
"@copilotkit/channels-ui": "~0.1.0",
|
|
46
|
+
"@copilotkit/shared": "^1.62.3",
|
|
47
|
+
"@copilotkit/core": "^1.62.3"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^22.10.0",
|
|
51
|
+
"typescript": "^5.6.3",
|
|
52
|
+
"vitest": "^4.1.3",
|
|
53
|
+
"zod": "^3.25.76",
|
|
54
|
+
"@copilotkit/typescript-config": "^1.55.0-next.8"
|
|
55
|
+
},
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"vitest": "^4.0.0"
|
|
58
|
+
},
|
|
59
|
+
"peerDependenciesMeta": {
|
|
60
|
+
"vitest": {
|
|
61
|
+
"optional": true
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"scripts": {
|
|
65
|
+
"build": "tsc -p tsconfig.json",
|
|
66
|
+
"check-types": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.check.json",
|
|
67
|
+
"test": "vitest run",
|
|
68
|
+
"test:watch": "vitest",
|
|
69
|
+
"publint": "publint .",
|
|
70
|
+
"attw": "attw --pack . --profile esm-only"
|
|
71
|
+
}
|
|
72
|
+
}
|