@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
@@ -0,0 +1,297 @@
1
+ import { expect, test, vi } from "vitest";
2
+ import { createChannel } from "./create-channel.js";
3
+ import { FakeAdapter } from "./testing/fake-adapter.js";
4
+ import { FakeAgent } from "./testing/fake-agent.js";
5
+ test("a Channel rejects construction without an explicit identity strategy", () => {
6
+ expect(() => createChannel({ adapters: [new FakeAdapter()] })).toThrow('createChannel: `identifyUser` must be "platform" or a callback');
7
+ });
8
+ test("a custom Channel identity callback maps the provider actor once before the message handler", async () => {
9
+ const identifyUser = vi.fn(() => ({ id: "person-1", name: "Pat Lee" }));
10
+ const adapter = new FakeAdapter({ platform: "intelligence" });
11
+ const channel = createChannel({
12
+ adapters: [adapter],
13
+ identifyUser,
14
+ });
15
+ let received;
16
+ channel.onMessage(({ message }) => {
17
+ received = message;
18
+ });
19
+ await channel.ɵruntime.start();
20
+ await adapter.getSink().onTurn({
21
+ conversationKey: "conversation-1",
22
+ replyTarget: {},
23
+ userText: "hello",
24
+ platform: "slack",
25
+ actor: {
26
+ id: "U123",
27
+ kind: "human",
28
+ name: "Pat on Slack",
29
+ handle: "pat",
30
+ },
31
+ identityContext: {
32
+ tenant: { id: "T123", name: "Acme" },
33
+ installation: { id: "I123" },
34
+ conversation: { id: "C123", kind: "channel" },
35
+ trigger: "message",
36
+ event: { id: "E123", occurredAt: "2026-07-31T12:00:00.000Z" },
37
+ raw: { type: "app_mention" },
38
+ },
39
+ });
40
+ expect(identifyUser).toHaveBeenCalledTimes(1);
41
+ expect(identifyUser).toHaveBeenCalledWith({
42
+ provider: "slack",
43
+ tenant: { id: "T123", name: "Acme" },
44
+ installation: { id: "I123" },
45
+ actor: {
46
+ id: "U123",
47
+ kind: "human",
48
+ name: "Pat on Slack",
49
+ handle: "pat",
50
+ },
51
+ conversation: { id: "C123", kind: "channel" },
52
+ trigger: "message",
53
+ event: { id: "E123", occurredAt: "2026-07-31T12:00:00.000Z" },
54
+ raw: { type: "app_mention" },
55
+ });
56
+ expect(received).toEqual(expect.objectContaining({
57
+ user: { id: "person-1", name: "Pat Lee" },
58
+ actor: {
59
+ id: "U123",
60
+ kind: "human",
61
+ name: "Pat on Slack",
62
+ handle: "pat",
63
+ },
64
+ }));
65
+ expect(Object.isFrozen(received.user)).toBe(true);
66
+ expect(Object.isFrozen(received.actor)).toBe(true);
67
+ });
68
+ test("a command resolves identity once and exposes the canonical user and provider actor", async () => {
69
+ const identifyUser = vi.fn(() => ({ id: "person-2", name: "Jo Kim" }));
70
+ const adapter = new FakeAdapter({ platform: "intelligence" });
71
+ const channel = createChannel({
72
+ adapters: [adapter],
73
+ identifyUser,
74
+ });
75
+ let received;
76
+ channel.onCommand("who", (context) => {
77
+ received = context;
78
+ });
79
+ await channel.ɵruntime.start();
80
+ await adapter.getSink().onCommand({
81
+ command: "who",
82
+ conversationKey: "conversation-2",
83
+ replyTarget: {},
84
+ text: "",
85
+ platform: "teams",
86
+ actor: { id: "29:actor", kind: "human", name: "Jo on Teams" },
87
+ identityContext: {
88
+ tenant: { id: "tenant-2" },
89
+ installation: { id: "install-2" },
90
+ conversation: { id: "conversation-2", kind: "channel" },
91
+ trigger: "command",
92
+ event: { id: "event-2" },
93
+ raw: { name: "who" },
94
+ },
95
+ });
96
+ expect(identifyUser).toHaveBeenCalledTimes(1);
97
+ expect(received).toEqual(expect.objectContaining({
98
+ user: { id: "person-2", name: "Jo Kim" },
99
+ actor: { id: "29:actor", kind: "human", name: "Jo on Teams" },
100
+ }));
101
+ });
102
+ test("an agent interrupt handler receives the event's canonical user and provider actor", async () => {
103
+ const identifyUser = vi.fn(() => ({ id: "person-3", name: "Sam Rao" }));
104
+ const adapter = new FakeAdapter({ platform: "intelligence" });
105
+ const agent = new FakeAgent([
106
+ (subscriber) => {
107
+ subscriber.onCustomEvent?.({
108
+ event: { name: "approval", value: { requestId: "request-1" } },
109
+ });
110
+ subscriber.onRunFinishedEvent?.({ event: {} });
111
+ },
112
+ ]);
113
+ const channel = createChannel({
114
+ adapters: [adapter],
115
+ identifyUser,
116
+ agent,
117
+ });
118
+ let received;
119
+ channel.onMessage(async ({ thread }) => {
120
+ await thread.runAgent();
121
+ });
122
+ channel.onInterrupt("approval", (context) => {
123
+ received = context;
124
+ });
125
+ await channel.ɵruntime.start();
126
+ await adapter.getSink().onTurn({
127
+ conversationKey: "conversation-3",
128
+ replyTarget: {},
129
+ userText: "approve",
130
+ platform: "slack",
131
+ actor: { id: "U3", kind: "human", name: "Sam on Slack" },
132
+ identityContext: {
133
+ tenant: { id: "T3" },
134
+ installation: { id: "I3" },
135
+ conversation: { id: "C3", kind: "channel" },
136
+ trigger: "message",
137
+ event: { id: "E3" },
138
+ raw: {},
139
+ },
140
+ });
141
+ expect(identifyUser).toHaveBeenCalledTimes(1);
142
+ expect(received).toEqual(expect.objectContaining({
143
+ payload: { requestId: "request-1" },
144
+ user: { id: "person-3", name: "Sam Rao" },
145
+ actor: { id: "U3", kind: "human", name: "Sam on Slack" },
146
+ }));
147
+ });
148
+ test("every non-message Channel trigger resolves identity once before its handler", async () => {
149
+ const identifyUser = vi.fn(({ actor }) => ({
150
+ id: `person:${actor.id}`,
151
+ name: actor.id,
152
+ }));
153
+ const adapter = new FakeAdapter({ platform: "intelligence" });
154
+ const channel = createChannel({
155
+ adapters: [adapter],
156
+ identifyUser,
157
+ });
158
+ const received = [];
159
+ channel.onThreadStarted((context) => {
160
+ received.push(context);
161
+ });
162
+ channel.onInteraction("approve", (context) => {
163
+ received.push(context);
164
+ });
165
+ channel.onReaction((context) => {
166
+ received.push(context);
167
+ });
168
+ channel.onModalSubmit("submit", (context) => {
169
+ received.push(context);
170
+ });
171
+ channel.onModalClose("close", (context) => {
172
+ received.push(context);
173
+ });
174
+ await channel.ɵruntime.start();
175
+ const sink = adapter.getSink();
176
+ const facts = (id, trigger) => ({
177
+ actor: { id, kind: "human" },
178
+ identityContext: {
179
+ tenant: { id: "tenant" },
180
+ installation: { id: "installation" },
181
+ conversation: { id: "conversation", kind: "channel" },
182
+ trigger,
183
+ event: { id: `event:${id}` },
184
+ raw: { trigger },
185
+ },
186
+ });
187
+ await sink.onThreadStarted({
188
+ conversationKey: "conversation",
189
+ replyTarget: {},
190
+ platform: "slack",
191
+ ...facts("thread-actor", "thread-start"),
192
+ });
193
+ await sink.onInteraction({
194
+ id: "approve",
195
+ conversationKey: "conversation",
196
+ replyTarget: {},
197
+ platform: "slack",
198
+ ...facts("interaction-actor", "interaction"),
199
+ });
200
+ await sink.onReaction({
201
+ added: true,
202
+ conversationKey: "conversation",
203
+ messageId: "message",
204
+ raw: {},
205
+ rawEmoji: "+1",
206
+ replyTarget: {},
207
+ platform: "slack",
208
+ ...facts("reaction-actor", "reaction"),
209
+ });
210
+ await sink.onModalSubmit({
211
+ callbackId: "submit",
212
+ values: {},
213
+ conversationKey: "conversation",
214
+ replyTarget: {},
215
+ platform: "slack",
216
+ raw: {},
217
+ ...facts("submit-actor", "modal-submit"),
218
+ });
219
+ await sink.onModalClose({
220
+ callbackId: "close",
221
+ conversationKey: "conversation",
222
+ replyTarget: {},
223
+ platform: "slack",
224
+ raw: {},
225
+ ...facts("close-actor", "modal-close"),
226
+ });
227
+ expect(identifyUser).toHaveBeenCalledTimes(5);
228
+ expect(received).toHaveLength(5);
229
+ expect(received).toEqual(expect.arrayContaining([
230
+ expect.objectContaining({
231
+ user: { id: "person:thread-actor", name: "thread-actor" },
232
+ actor: { id: "thread-actor", kind: "human" },
233
+ }),
234
+ expect.objectContaining({
235
+ user: { id: "person:interaction-actor", name: "interaction-actor" },
236
+ actor: { id: "interaction-actor", kind: "human" },
237
+ }),
238
+ expect.objectContaining({
239
+ user: { id: "person:reaction-actor", name: "reaction-actor" },
240
+ actor: { id: "reaction-actor", kind: "human" },
241
+ }),
242
+ expect.objectContaining({
243
+ user: { id: "person:submit-actor", name: "submit-actor" },
244
+ actor: { id: "submit-actor", kind: "human" },
245
+ }),
246
+ expect.objectContaining({
247
+ user: { id: "person:close-actor", name: "close-actor" },
248
+ actor: { id: "close-actor", kind: "human" },
249
+ }),
250
+ ]));
251
+ });
252
+ test("parallel actors in one conversation keep their own immutable identity", async () => {
253
+ const releases = new Map();
254
+ const identifyUser = vi.fn(async ({ actor }) => {
255
+ await new Promise((resolve) => releases.set(actor.id, resolve));
256
+ return { id: `person:${actor.id}`, name: actor.name ?? actor.id };
257
+ });
258
+ const adapter = new FakeAdapter({ platform: "intelligence" });
259
+ const channel = createChannel({ adapters: [adapter], identifyUser });
260
+ const received = [];
261
+ channel.onMessage(({ message }) => {
262
+ received.push(message);
263
+ });
264
+ await channel.ɵruntime.start();
265
+ const sink = adapter.getSink();
266
+ const event = (actorId) => ({
267
+ conversationKey: "shared-conversation",
268
+ replyTarget: {},
269
+ userText: `hello from ${actorId}`,
270
+ platform: "slack",
271
+ actor: { id: actorId, kind: "human", name: actorId.toUpperCase() },
272
+ identityContext: {
273
+ tenant: { id: "T1" },
274
+ installation: { id: "I1" },
275
+ conversation: { id: "C1", kind: "channel" },
276
+ trigger: "message",
277
+ event: { id: `event:${actorId}` },
278
+ raw: { actorId },
279
+ },
280
+ });
281
+ const first = sink.onTurn(event("ada"));
282
+ const second = sink.onTurn(event("grace"));
283
+ await vi.waitFor(() => expect(identifyUser).toHaveBeenCalledTimes(2));
284
+ releases.get("grace")?.();
285
+ releases.get("ada")?.();
286
+ await Promise.all([first, second]);
287
+ expect(received).toEqual(expect.arrayContaining([
288
+ expect.objectContaining({
289
+ user: { id: "person:ada", name: "ADA" },
290
+ actor: { id: "ada", kind: "human", name: "ADA" },
291
+ }),
292
+ expect.objectContaining({
293
+ user: { id: "person:grace", name: "GRACE" },
294
+ actor: { id: "grace", kind: "human", name: "GRACE" },
295
+ }),
296
+ ]));
297
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=channel-memory.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-memory.test.d.ts","sourceRoot":"","sources":["../src/channel-memory.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,199 @@
1
+ import { expect, test, vi } from "vitest";
2
+ import { createChannel } from "./create-channel.js";
3
+ import { FakeAdapter } from "./testing/fake-adapter.js";
4
+ import { FakeAgent } from "./testing/fake-agent.js";
5
+ test("an omitted Memory grant runs on a direct Channel without Intelligence", async () => {
6
+ const adapter = new FakeAdapter();
7
+ const agent = new FakeAgent();
8
+ const channel = createChannel({
9
+ identifyUser: "platform",
10
+ adapters: [adapter],
11
+ agent: () => agent,
12
+ });
13
+ let ran = false;
14
+ channel.onMessage(async ({ thread }) => {
15
+ await thread.runAgent();
16
+ ran = true;
17
+ });
18
+ await channel.ɵruntime.start();
19
+ await adapter.getSink().onTurn({
20
+ conversationKey: "conversation",
21
+ replyTarget: {},
22
+ userText: "hello",
23
+ platform: "slack",
24
+ });
25
+ expect(ran).toBe(true);
26
+ });
27
+ test("an explicit Memory grant fails before a direct Channel agent runs without Intelligence", async () => {
28
+ const adapter = new FakeAdapter();
29
+ const agent = new FakeAgent();
30
+ const channel = createChannel({
31
+ identifyUser: "platform",
32
+ adapters: [adapter],
33
+ agent: () => agent,
34
+ });
35
+ channel.onMessage(async ({ thread }) => {
36
+ await thread.runAgent({ memory: { project: "read" } });
37
+ });
38
+ await channel.ɵruntime.start();
39
+ await expect(adapter.getSink().onTurn({
40
+ conversationKey: "conversation",
41
+ replyTarget: {},
42
+ userText: "hello",
43
+ platform: "slack",
44
+ })).rejects.toMatchObject({ code: "channel_memory_unavailable" });
45
+ expect(agent.runAgentCalls).toBe(0);
46
+ });
47
+ test("project-only Memory works when Channel identity is null", async () => {
48
+ const adapter = new FakeAdapter();
49
+ const lifecycle = vi.fn(async (args) => args.execute({}, undefined));
50
+ adapter.supportsIntelligenceMemory = true;
51
+ adapter.runAgentLifecycle = lifecycle;
52
+ const channel = createChannel({
53
+ identifyUser: () => null,
54
+ adapters: [adapter],
55
+ agent: () => new FakeAgent(),
56
+ });
57
+ channel.onMessage(async ({ thread }) => {
58
+ await thread.runAgent({ memory: { project: "read-write" } });
59
+ });
60
+ await channel.ɵruntime.start();
61
+ await adapter.getSink().onTurn({
62
+ conversationKey: "conversation",
63
+ replyTarget: {},
64
+ userText: "hello",
65
+ platform: "slack",
66
+ });
67
+ expect(lifecycle).toHaveBeenCalledWith(expect.objectContaining({
68
+ memory: {
69
+ grant: { user: "none", project: "read-write" },
70
+ user: null,
71
+ },
72
+ }));
73
+ });
74
+ test("a custom identity callback may map a non-human actor to personal Memory", async () => {
75
+ const adapter = new FakeAdapter();
76
+ const lifecycle = vi.fn(async (args) => args.execute({}, undefined));
77
+ adapter.supportsIntelligenceMemory = true;
78
+ adapter.runAgentLifecycle = lifecycle;
79
+ const channel = createChannel({
80
+ identifyUser: ({ actor }) => ({
81
+ id: `service:${actor.id}`,
82
+ name: actor.name ?? actor.id,
83
+ }),
84
+ adapters: [adapter],
85
+ agent: () => new FakeAgent(),
86
+ });
87
+ channel.onMessage(async ({ thread }) => {
88
+ await thread.runAgent({ memory: { user: "read" } });
89
+ });
90
+ await channel.ɵruntime.start();
91
+ await adapter.getSink().onTurn({
92
+ conversationKey: "conversation",
93
+ replyTarget: {},
94
+ userText: "refresh",
95
+ platform: "slack",
96
+ actor: { id: "sync-bot", kind: "bot", name: "Sync Bot" },
97
+ });
98
+ expect(lifecycle).toHaveBeenCalledWith(expect.objectContaining({
99
+ memory: {
100
+ grant: { user: "read", project: "none" },
101
+ user: { id: "service:sync-bot", name: "Sync Bot" },
102
+ },
103
+ }));
104
+ });
105
+ test("personal Memory fails before execution when Channel identity is null", async () => {
106
+ const adapter = new FakeAdapter();
107
+ adapter.supportsIntelligenceMemory = true;
108
+ adapter.runAgentLifecycle = vi.fn();
109
+ const channel = createChannel({
110
+ identifyUser: () => null,
111
+ adapters: [adapter],
112
+ agent: () => new FakeAgent(),
113
+ });
114
+ channel.onMessage(async ({ thread }) => {
115
+ await thread.runAgent({ memory: { user: "read", project: "none" } });
116
+ });
117
+ await channel.ɵruntime.start();
118
+ await expect(adapter.getSink().onTurn({
119
+ conversationKey: "conversation",
120
+ replyTarget: {},
121
+ userText: "hello",
122
+ platform: "slack",
123
+ })).rejects.toMatchObject({ code: "channel_memory_user_required" });
124
+ expect(adapter.runAgentLifecycle).not.toHaveBeenCalled();
125
+ });
126
+ test("an invalid JavaScript Memory grant fails before agent execution", async () => {
127
+ const adapter = new FakeAdapter();
128
+ adapter.supportsIntelligenceMemory = true;
129
+ adapter.runAgentLifecycle = vi.fn();
130
+ const channel = createChannel({
131
+ identifyUser: "platform",
132
+ adapters: [adapter],
133
+ agent: () => new FakeAgent(),
134
+ });
135
+ channel.onMessage(async ({ thread }) => {
136
+ await thread.runAgent({
137
+ memory: { user: "admin" },
138
+ });
139
+ });
140
+ await channel.ɵruntime.start();
141
+ await expect(adapter.getSink().onTurn({
142
+ conversationKey: "conversation",
143
+ replyTarget: {},
144
+ userText: "hello",
145
+ platform: "slack",
146
+ actor: { id: "alice", kind: "human" },
147
+ })).rejects.toMatchObject({ code: "channel_memory_grant_invalid" });
148
+ expect(adapter.runAgentLifecycle).not.toHaveBeenCalled();
149
+ });
150
+ test("runAgent snapshots its Memory grant before queued execution", async () => {
151
+ const adapter = new FakeAdapter();
152
+ adapter.supportsIntelligenceMemory = true;
153
+ let release;
154
+ const gate = new Promise((resolve) => {
155
+ release = resolve;
156
+ });
157
+ const queuedAdapter = adapter;
158
+ queuedAdapter.trackThreadOperation = (_target, operation) => gate.then(operation);
159
+ const lifecycle = vi.fn(async (args) => args.execute(args.renderer.subscriber, undefined));
160
+ adapter.runAgentLifecycle = lifecycle;
161
+ const channel = createChannel({
162
+ identifyUser: "platform",
163
+ adapters: [adapter],
164
+ agent: new FakeAgent(),
165
+ });
166
+ let thread;
167
+ channel.onMessage((context) => {
168
+ thread = context.thread;
169
+ });
170
+ await channel.ɵruntime.start();
171
+ await adapter.getSink().onTurn({
172
+ conversationKey: "thread-queued",
173
+ replyTarget: {},
174
+ userText: "hello",
175
+ platform: "slack",
176
+ actor: { id: "U1", kind: "human" },
177
+ identityContext: {
178
+ tenant: { id: "T1" },
179
+ installation: { id: "I1" },
180
+ conversation: { id: "C1" },
181
+ trigger: "message",
182
+ event: { id: "E1" },
183
+ raw: {},
184
+ },
185
+ });
186
+ if (!thread)
187
+ throw new Error("message handler did not receive a Thread");
188
+ const memory = { user: "read", project: "none" };
189
+ const run = thread.runAgent({ memory });
190
+ memory.user = "none";
191
+ release();
192
+ await run;
193
+ expect(lifecycle).toHaveBeenCalledWith(expect.objectContaining({
194
+ memory: {
195
+ grant: { user: "read", project: "none" },
196
+ user: { id: "slack:T1:U1", name: "U1" },
197
+ },
198
+ }));
199
+ });
@@ -1,5 +1,5 @@
1
1
  import type { InferSchemaOutput, ObjectSchema } from "./standard-schema.js";
2
- import type { Thread, PlatformUser, ModalView } from "@copilotkit/channels-ui";
2
+ import type { ApplicationUser, ModalView, ProviderActor, Thread } from "@copilotkit/channels-ui";
3
3
  /**
4
4
  * Context handed to a slash-command handler. `text` is the raw argument string
5
5
  * (the form Slack delivers); `options` is the parsed, typed form, populated by
@@ -15,8 +15,10 @@ export interface CommandContext<TOptions = Record<string, never>> {
15
15
  text: string;
16
16
  /** Parsed, typed options (empty on surfaces that only deliver `text`). */
17
17
  options: TOptions;
18
- /** The invoking user, when the surface provides it. */
19
- user?: PlatformUser;
18
+ /** Canonical application user selected for this event. */
19
+ user: ApplicationUser | null;
20
+ /** Provider account that invoked the command. */
21
+ actor: ProviderActor;
20
22
  platform: string;
21
23
  /** Open a modal in response to this command (requires the surface's trigger; `undefined` when unavailable). */
22
24
  openModal?(view: ModalView): Promise<{
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAE5E,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAE/E;;;;;GAKG;AACH,MAAM,WAAW,cAAc,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;IAC9D,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,OAAO,EAAE,QAAQ,CAAC;IAClB,uDAAuD;IACvD,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,+GAA+G;IAC/G,SAAS,CAAC,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvE;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc,CAAC,MAAM,SAAS,YAAY,GAAG,YAAY;IACxE,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAC;IACb,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,SAAS,YAAY,EAC9D,OAAO,EAAE,cAAc,CAAC,MAAM,CAAC,GAC9B,cAAc,CAAC,MAAM,CAAC,CAExB;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMzD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,WAAW,CAUlE"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAE5E,OAAO,KAAK,EACV,eAAe,EACf,SAAS,EACT,aAAa,EACb,MAAM,EACP,MAAM,yBAAyB,CAAC;AAEjC;;;;;GAKG;AACH,MAAM,WAAW,cAAc,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;IAC9D,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,OAAO,EAAE,QAAQ,CAAC;IAClB,0DAA0D;IAC1D,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IAC7B,iDAAiD;IACjD,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,+GAA+G;IAC/G,SAAS,CAAC,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvE;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc,CAAC,MAAM,SAAS,YAAY,GAAG,YAAY;IACxE,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAC;IACb,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,SAAS,YAAY,EAC9D,OAAO,EAAE,cAAc,CAAC,MAAM,CAAC,GAC9B,cAAc,CAAC,MAAM,CAAC,CAExB;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMzD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,WAAW,CAUlE"}