@copilotkit/channels-core 0.5.0 → 0.5.1-canary.1785633429

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.
Files changed (82) hide show
  1. package/README.md +35 -14
  2. package/dist/action-registry.d.ts +15 -3
  3. package/dist/action-registry.d.ts.map +1 -1
  4. package/dist/action-registry.js +61 -9
  5. package/dist/action-registry.test.js +107 -2
  6. package/dist/action-store.d.ts +23 -0
  7. package/dist/action-store.d.ts.map +1 -1
  8. package/dist/action-store.js +11 -0
  9. package/dist/canonical-run-loop.test.js +6 -1
  10. package/dist/channel-identity.test.d.ts +2 -0
  11. package/dist/channel-identity.test.d.ts.map +1 -0
  12. package/dist/channel-identity.test.js +297 -0
  13. package/dist/channel-memory.test.d.ts +2 -0
  14. package/dist/channel-memory.test.d.ts.map +1 -0
  15. package/dist/channel-memory.test.js +199 -0
  16. package/dist/commands.d.ts +5 -3
  17. package/dist/commands.d.ts.map +1 -1
  18. package/dist/create-channel.d.ts +30 -56
  19. package/dist/create-channel.d.ts.map +1 -1
  20. package/dist/create-channel.foundations.test.js +30 -7
  21. package/dist/create-channel.js +209 -72
  22. package/dist/create-channel.test.js +293 -66
  23. package/dist/hitl-continuation.test.d.ts +2 -0
  24. package/dist/hitl-continuation.test.d.ts.map +1 -0
  25. package/dist/hitl-continuation.test.js +393 -0
  26. package/dist/identity.d.ts +51 -0
  27. package/dist/identity.d.ts.map +1 -0
  28. package/dist/identity.js +56 -0
  29. package/dist/identity.test.d.ts +2 -0
  30. package/dist/identity.test.d.ts.map +1 -0
  31. package/dist/identity.test.js +109 -0
  32. package/dist/index.d.ts +10 -5
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +5 -1
  35. package/dist/managed-v1-await-choice-guard.test.js +4 -1
  36. package/dist/memory.d.ts +23 -0
  37. package/dist/memory.d.ts.map +1 -0
  38. package/dist/memory.js +25 -0
  39. package/dist/modal-submit.test.js +21 -6
  40. package/dist/open-modal.test.js +16 -4
  41. package/dist/platform-adapter.d.ts +29 -6
  42. package/dist/platform-adapter.d.ts.map +1 -1
  43. package/dist/platform-adapter.test.js +2 -0
  44. package/dist/reactions.test.js +49 -11
  45. package/dist/run-loop.test.js +30 -5
  46. package/dist/sanitize-agent-events.test.js +2 -0
  47. package/dist/source-platform.test.js +24 -9
  48. package/dist/state/kv-action-store.d.ts.map +1 -1
  49. package/dist/state/kv-action-store.js +1 -0
  50. package/dist/state/kv-action-store.test.js +11 -0
  51. package/dist/state/memory-store.d.ts +1 -0
  52. package/dist/state/memory-store.d.ts.map +1 -1
  53. package/dist/state/memory-store.js +9 -0
  54. package/dist/state/state-store.d.ts +2 -0
  55. package/dist/state/state-store.d.ts.map +1 -1
  56. package/dist/telemetry/create-channel-telemetry.test.js +4 -2
  57. package/dist/telemetry/e2e-telemetry.test.js +1 -0
  58. package/dist/telemetry/install-id.test.js +5 -0
  59. package/dist/testing/fake-adapter.d.ts +26 -9
  60. package/dist/testing/fake-adapter.d.ts.map +1 -1
  61. package/dist/testing/fake-adapter.js +65 -21
  62. package/dist/testing/state-store-conformance.d.ts.map +1 -1
  63. package/dist/testing/state-store-conformance.js +16 -0
  64. package/dist/thread-capabilities.test.js +29 -9
  65. package/dist/thread-ephemeral.test.js +8 -5
  66. package/dist/thread-promise-contract.test.js +4 -0
  67. package/dist/thread-reactions.test.js +8 -2
  68. package/dist/thread.d.ts +50 -7
  69. package/dist/thread.d.ts.map +1 -1
  70. package/dist/thread.js +163 -17
  71. package/dist/thread.test.js +4 -0
  72. package/dist/tools.d.ts +5 -3
  73. package/dist/tools.d.ts.map +1 -1
  74. package/dist/tools.js +2 -1
  75. package/dist/transcripts.d.ts +6 -13
  76. package/dist/transcripts.d.ts.map +1 -1
  77. package/dist/transcripts.js +12 -12
  78. package/dist/transcripts.test.js +28 -29
  79. package/dist/welcome.test.d.ts +2 -0
  80. package/dist/welcome.test.d.ts.map +1 -0
  81. package/dist/welcome.test.js +53 -0
  82. package/package.json +5 -5
@@ -1,5 +1,5 @@
1
1
  import { parseDuration } from "./state/duration.js";
2
- const keyFor = (userKey) => `transcript:user:${userKey}`;
2
+ const keyFor = (userId) => `transcript:user:${userId}`;
3
3
  export class Transcripts {
4
4
  state;
5
5
  cfg;
@@ -12,39 +12,39 @@ export class Transcripts {
12
12
  }
13
13
  /**
14
14
  * Append a message to the user's transcript.
15
- * No-ops silently when no `userKey` is resolved (from `opts.userKey` or `msg.userKey`).
15
+ * No-ops silently when no canonical application user ID is supplied.
16
16
  */
17
17
  async append(thread, msg, opts) {
18
- const userKey = opts?.userKey ?? msg.userKey;
19
- if (!userKey)
18
+ const userId = opts.userId;
19
+ if (!userId)
20
20
  return; // identity unresolved → no-op
21
21
  const entry = {
22
22
  role: msg.role ?? "user",
23
23
  text: msg.text,
24
24
  platform: thread.platform,
25
25
  threadId: thread.conversationKey,
26
- userKey,
26
+ userId,
27
27
  ts: Date.now(),
28
28
  };
29
- await this.state.list.append(keyFor(userKey), entry, {
29
+ await this.state.list.append(keyFor(userId), entry, {
30
30
  maxLen: this.cfg.maxPerUser,
31
31
  ttlMs: this.retentionMs,
32
32
  });
33
33
  if (this.retentionMs !== undefined) {
34
34
  const cutoff = Date.now() - this.retentionMs;
35
- const all = await this.state.list.range(keyFor(userKey));
35
+ const all = await this.state.list.range(keyFor(userId));
36
36
  const expired = all.filter((e) => e.ts < cutoff).length;
37
37
  if (expired > 0) {
38
38
  const survivors = all.length - expired;
39
39
  if (survivors <= 0)
40
- await this.state.list.delete(keyFor(userKey));
40
+ await this.state.list.delete(keyFor(userId));
41
41
  else
42
- await this.state.list.trim(keyFor(userKey), survivors);
42
+ await this.state.list.trim(keyFor(userId), survivors);
43
43
  }
44
44
  }
45
45
  }
46
46
  async list(q) {
47
- let items = await this.state.list.range(keyFor(q.userKey));
47
+ let items = await this.state.list.range(keyFor(q.userId));
48
48
  if (this.retentionMs !== undefined) {
49
49
  const cutoff = Date.now() - this.retentionMs;
50
50
  items = items.filter((e) => e.ts >= cutoff);
@@ -60,8 +60,8 @@ export class Transcripts {
60
60
  return items; // oldest-first
61
61
  }
62
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));
63
+ const n = (await this.state.list.range(keyFor(q.userId))).length;
64
+ await this.state.list.delete(keyFor(q.userId));
65
65
  return { deleted: n };
66
66
  }
67
67
  }
@@ -8,45 +8,44 @@ const thread = {
8
8
  describe("Transcripts", () => {
9
9
  it("appends and lists oldest-first, filters compose", async () => {
10
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" });
11
+ await t.append(thread, { role: "user", text: "hi" }, { userId: "u@x.com" });
12
+ await t.append(thread, { role: "assistant", text: "hello" }, { userId: "u@x.com" });
13
+ const all = await t.list({ userId: "u@x.com" });
14
14
  expect(all.map((e) => e.role)).toEqual(["user", "assistant"]);
15
- expect(await t.list({ userKey: "u@x.com", roles: ["user"] })).toHaveLength(1);
15
+ expect(await t.list({ userId: "u@x.com", roles: ["user"] })).toHaveLength(1);
16
16
  });
17
17
  it("delete wipes and reports count", async () => {
18
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([]);
19
+ await t.append(thread, { role: "user", text: "x" }, { userId: "k" });
20
+ expect(await t.delete({ userId: "k" })).toEqual({ deleted: 1 });
21
+ expect(await t.list({ userId: "k" })).toEqual([]);
22
22
  });
23
23
  it("TTL expiry: entries not visible after retention window", async () => {
24
24
  const t = new Transcripts(new MemoryStore(), { retention: "30ms" });
25
- await t.append(thread, { role: "user", text: "hi" }, { userKey: "u" });
25
+ await t.append(thread, { role: "user", text: "hi" }, { userId: "u" });
26
26
  await new Promise((r) => setTimeout(r, 60));
27
- expect(await t.list({ userKey: "u" })).toEqual([]);
27
+ expect(await t.list({ userId: "u" })).toEqual([]);
28
28
  });
29
29
  it("maxPerUser: only newest N entries are kept", async () => {
30
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" });
31
+ await t.append(thread, { role: "user", text: "1" }, { userId: "u" });
32
+ await t.append(thread, { role: "user", text: "2" }, { userId: "u" });
33
+ await t.append(thread, { role: "user", text: "3" }, { userId: "u" });
34
+ const entries = await t.list({ userId: "u" });
35
35
  expect(entries).toHaveLength(2);
36
36
  expect(entries.map((e) => e.text)).toEqual(["2", "3"]);
37
37
  });
38
- it("no userKey resolved: no-op, list returns empty for any key", async () => {
38
+ it("no userId resolved: no-op, list returns empty for any key", async () => {
39
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([]);
40
+ await t.append(thread, { role: "user", text: "ghost" }, { userId: null });
41
+ expect(await t.list({ userId: "anyone" })).toEqual([]);
43
42
  });
44
43
  it("list with limit: returns only the last N entries", async () => {
45
44
  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 });
45
+ await t.append(thread, { role: "user", text: "a" }, { userId: "u" });
46
+ await t.append(thread, { role: "user", text: "b" }, { userId: "u" });
47
+ await t.append(thread, { role: "user", text: "c" }, { userId: "u" });
48
+ const last = await t.list({ userId: "u", limit: 1 });
50
49
  expect(last).toHaveLength(1);
51
50
  expect(last[0].text).toBe("c");
52
51
  });
@@ -60,9 +59,9 @@ describe("Transcripts retention", () => {
60
59
  const t = new Transcripts(store, { retention: "1h" });
61
60
  vi.useFakeTimers();
62
61
  try {
63
- await t.append(thread, { role: "user", text: "old" }, { userKey: "u" });
62
+ await t.append(thread, { role: "user", text: "old" }, { userId: "u" });
64
63
  vi.advanceTimersByTime(2 * 60 * 60 * 1000); // advance 2h
65
- expect(await t.list({ userKey: "u" })).toEqual([]);
64
+ expect(await t.list({ userId: "u" })).toEqual([]);
66
65
  }
67
66
  finally {
68
67
  vi.useRealTimers();
@@ -78,11 +77,11 @@ describe("Transcripts retention", () => {
78
77
  const t = new Transcripts(store, { retention: "1h" });
79
78
  vi.useFakeTimers();
80
79
  try {
81
- await t.append(thread, { role: "user", text: "A" }, { userKey: "u" });
80
+ await t.append(thread, { role: "user", text: "A" }, { userId: "u" });
82
81
  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" });
82
+ await t.append(thread, { role: "user", text: "B" }, { userId: "u" });
84
83
  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" });
84
+ await t.append(thread, { role: "user", text: "C" }, { userId: "u" });
86
85
  const raw = await store.list.range("transcript:user:u");
87
86
  expect(raw.map((e) => e.text)).toEqual(["B", "C"]); // A pruned by age, not whole-key expiry
88
87
  }
@@ -95,10 +94,10 @@ describe("Transcripts retention", () => {
95
94
  const t = new Transcripts(store, { retention: "1h" });
96
95
  vi.useFakeTimers();
97
96
  try {
98
- await t.append(thread, { role: "user", text: "A" }, { userKey: "u" });
97
+ await t.append(thread, { role: "user", text: "A" }, { userId: "u" });
99
98
  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" });
99
+ await t.append(thread, { role: "user", text: "B" }, { userId: "u" });
100
+ const entries = await t.list({ userId: "u" });
102
101
  expect(entries).toHaveLength(2);
103
102
  }
104
103
  finally {
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=welcome.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"welcome.test.d.ts","sourceRoot":"","sources":["../src/welcome.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,53 @@
1
+ import { expect, test, vi } from "vitest";
2
+ import { createChannel } from "./create-channel.js";
3
+ import { FakeAdapter } from "./testing/fake-adapter.js";
4
+ test("routes a welcome lifecycle event without synthesizing a message turn", async () => {
5
+ const adapter = new FakeAdapter();
6
+ const onWelcome = vi.fn();
7
+ const onMessage = vi.fn();
8
+ const channel = createChannel({
9
+ identifyUser: () => ({ id: "app-user-1", name: "Ada App" }),
10
+ adapters: [adapter],
11
+ });
12
+ channel.onWelcome(onWelcome);
13
+ channel.onMessage(onMessage);
14
+ await channel.ɵruntime.start();
15
+ await adapter.emitWelcome({
16
+ conversationKey: "teams:tenant:conversation",
17
+ replyTarget: { conversationId: "conversation" },
18
+ platform: "teams",
19
+ actor: { id: "provider-user-1", kind: "human", name: "Ada Provider" },
20
+ });
21
+ expect(onWelcome).toHaveBeenCalledOnce();
22
+ expect(onWelcome).toHaveBeenCalledWith(expect.objectContaining({
23
+ user: { id: "app-user-1", name: "Ada App" },
24
+ actor: {
25
+ id: "provider-user-1",
26
+ kind: "human",
27
+ name: "Ada Provider",
28
+ },
29
+ platform: "teams",
30
+ }));
31
+ expect(onMessage).not.toHaveBeenCalled();
32
+ });
33
+ test("exposes submitted input values separately from the action envelope", async () => {
34
+ const adapter = new FakeAdapter();
35
+ const handler = vi.fn();
36
+ const channel = createChannel({
37
+ identifyUser: "platform",
38
+ adapters: [adapter],
39
+ });
40
+ channel.onInteraction("approve", handler);
41
+ await channel.ɵruntime.start();
42
+ adapter.emitInteraction({
43
+ id: "approve",
44
+ value: { decision: "yes" },
45
+ values: { reason: "ready", priority: "high" },
46
+ platform: "teams",
47
+ });
48
+ await vi.waitFor(() => expect(handler).toHaveBeenCalledOnce());
49
+ expect(handler).toHaveBeenCalledWith(expect.objectContaining({
50
+ action: { id: "approve", value: { decision: "yes" } },
51
+ values: { reason: "ready", priority: "high" },
52
+ }));
53
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@copilotkit/channels-core",
3
- "version": "0.5.0",
3
+ "version": "0.5.1-canary.1785633429",
4
4
  "description": "Platform-agnostic JSX channel engine for CopilotKit (createChannel, Thread, PlatformAdapter, ActionStore).",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -50,10 +50,10 @@
50
50
  "dependencies": {
51
51
  "@ag-ui/client": "0.0.57",
52
52
  "@ag-ui/core": "0.0.57",
53
- "zod-to-json-schema": "^3.24.1",
54
- "@copilotkit/core": "^1.64.1",
55
- "@copilotkit/shared": "^1.64.1",
56
- "@copilotkit/channels-ui": "~0.5.0"
53
+ "@copilotkit/channels-ui": "0.5.1-canary.1785633429",
54
+ "@copilotkit/core": "1.64.3-canary.1785633429",
55
+ "@copilotkit/shared": "1.64.3-canary.1785633429",
56
+ "zod-to-json-schema": "^3.24.1"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@types/node": "^22.10.0",