@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.
- package/README.md +35 -14
- package/dist/action-registry.d.ts +15 -3
- package/dist/action-registry.d.ts.map +1 -1
- package/dist/action-registry.js +61 -9
- package/dist/action-registry.test.js +107 -2
- package/dist/action-store.d.ts +23 -0
- package/dist/action-store.d.ts.map +1 -1
- package/dist/action-store.js +11 -0
- package/dist/canonical-run-loop.test.js +6 -1
- package/dist/channel-identity.test.d.ts +2 -0
- package/dist/channel-identity.test.d.ts.map +1 -0
- package/dist/channel-identity.test.js +297 -0
- package/dist/channel-memory.test.d.ts +2 -0
- package/dist/channel-memory.test.d.ts.map +1 -0
- package/dist/channel-memory.test.js +199 -0
- package/dist/commands.d.ts +5 -3
- package/dist/commands.d.ts.map +1 -1
- package/dist/create-channel.d.ts +30 -56
- package/dist/create-channel.d.ts.map +1 -1
- package/dist/create-channel.foundations.test.js +30 -7
- package/dist/create-channel.js +209 -72
- package/dist/create-channel.test.js +293 -66
- package/dist/hitl-continuation.test.d.ts +2 -0
- package/dist/hitl-continuation.test.d.ts.map +1 -0
- package/dist/hitl-continuation.test.js +393 -0
- package/dist/identity.d.ts +51 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +56 -0
- package/dist/identity.test.d.ts +2 -0
- package/dist/identity.test.d.ts.map +1 -0
- package/dist/identity.test.js +109 -0
- package/dist/index.d.ts +10 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/managed-v1-await-choice-guard.test.js +4 -1
- package/dist/memory.d.ts +23 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +25 -0
- package/dist/modal-submit.test.js +21 -6
- package/dist/open-modal.test.js +16 -4
- package/dist/platform-adapter.d.ts +29 -6
- package/dist/platform-adapter.d.ts.map +1 -1
- package/dist/platform-adapter.test.js +2 -0
- package/dist/reactions.test.js +49 -11
- package/dist/run-loop.test.js +30 -5
- package/dist/sanitize-agent-events.test.js +2 -0
- package/dist/source-platform.test.js +24 -9
- package/dist/state/kv-action-store.d.ts.map +1 -1
- package/dist/state/kv-action-store.js +1 -0
- package/dist/state/kv-action-store.test.js +11 -0
- package/dist/state/memory-store.d.ts +1 -0
- package/dist/state/memory-store.d.ts.map +1 -1
- package/dist/state/memory-store.js +9 -0
- package/dist/state/state-store.d.ts +2 -0
- package/dist/state/state-store.d.ts.map +1 -1
- package/dist/telemetry/create-channel-telemetry.test.js +4 -2
- package/dist/telemetry/e2e-telemetry.test.js +1 -0
- package/dist/telemetry/install-id.test.js +5 -0
- package/dist/testing/fake-adapter.d.ts +26 -9
- package/dist/testing/fake-adapter.d.ts.map +1 -1
- package/dist/testing/fake-adapter.js +65 -21
- package/dist/testing/state-store-conformance.d.ts.map +1 -1
- package/dist/testing/state-store-conformance.js +16 -0
- package/dist/thread-capabilities.test.js +29 -9
- package/dist/thread-ephemeral.test.js +8 -5
- package/dist/thread-promise-contract.test.js +4 -0
- package/dist/thread-reactions.test.js +8 -2
- package/dist/thread.d.ts +50 -7
- package/dist/thread.d.ts.map +1 -1
- package/dist/thread.js +163 -17
- package/dist/thread.test.js +4 -0
- package/dist/tools.d.ts +5 -3
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +2 -1
- package/dist/transcripts.d.ts +6 -13
- package/dist/transcripts.d.ts.map +1 -1
- package/dist/transcripts.js +12 -12
- package/dist/transcripts.test.js +28 -29
- package/dist/welcome.test.d.ts +2 -0
- package/dist/welcome.test.d.ts.map +1 -0
- package/dist/welcome.test.js +53 -0
- package/package.json +5 -5
|
@@ -21,6 +21,7 @@ test("an ingress source platform reaches the text thread and its tool context",
|
|
|
21
21
|
},
|
|
22
22
|
]);
|
|
23
23
|
const channel = createChannel({
|
|
24
|
+
identifyUser: "platform",
|
|
24
25
|
adapters: [adapter],
|
|
25
26
|
agent: () => agent,
|
|
26
27
|
tools: [
|
|
@@ -31,6 +32,8 @@ test("an ingress source platform reaches the text thread and its tool context",
|
|
|
31
32
|
handler: (_args, ctx) => {
|
|
32
33
|
observed.toolPlatform = ctx.platform;
|
|
33
34
|
observed.toolThreadPlatform = ctx.thread.platform;
|
|
35
|
+
observed.toolUser = ctx.user?.id;
|
|
36
|
+
observed.toolActor = ctx.actor.id;
|
|
34
37
|
return "captured";
|
|
35
38
|
},
|
|
36
39
|
},
|
|
@@ -47,30 +50,41 @@ test("an ingress source platform reaches the text thread and its tool context",
|
|
|
47
50
|
replyTarget: {},
|
|
48
51
|
userText: "hello",
|
|
49
52
|
platform: "slack",
|
|
53
|
+
actor: { id: "U1", kind: "human", name: "Ada" },
|
|
54
|
+
identityContext: {
|
|
55
|
+
tenant: { id: "T1" },
|
|
56
|
+
installation: { id: "I1" },
|
|
57
|
+
conversation: { id: "conversation-1", kind: "channel" },
|
|
58
|
+
trigger: "message",
|
|
59
|
+
event: { id: "E1" },
|
|
60
|
+
raw: {},
|
|
61
|
+
},
|
|
50
62
|
});
|
|
51
63
|
expect(observed).toEqual({
|
|
52
64
|
messagePlatform: "slack",
|
|
53
65
|
threadPlatform: "slack",
|
|
54
66
|
toolPlatform: "slack",
|
|
55
67
|
toolThreadPlatform: "slack",
|
|
68
|
+
toolUser: "slack:T1:U1",
|
|
69
|
+
toolActor: "U1",
|
|
56
70
|
});
|
|
57
71
|
});
|
|
58
72
|
test("an ingress source platform scopes identity resolution and event dedupe", async () => {
|
|
59
73
|
const state = new MemoryStore();
|
|
60
74
|
const dedupeSeen = vi.spyOn(state.dedup, "seen").mockResolvedValue(false);
|
|
61
|
-
const
|
|
75
|
+
const identifyUser = vi.fn(() => ({ id: "person-1", name: "Person One" }));
|
|
62
76
|
const adapter = new FakeAdapter({ platform: "intelligence" });
|
|
63
77
|
const channel = createChannel({
|
|
64
78
|
adapters: [adapter],
|
|
79
|
+
identifyUser,
|
|
65
80
|
store: {
|
|
66
81
|
adapter: state,
|
|
67
|
-
identity,
|
|
68
82
|
transcripts: {},
|
|
69
83
|
},
|
|
70
84
|
});
|
|
71
|
-
let
|
|
85
|
+
let userId;
|
|
72
86
|
channel.onMessage(({ message }) => {
|
|
73
|
-
|
|
87
|
+
userId = message.user?.id;
|
|
74
88
|
});
|
|
75
89
|
await channel.ɵruntime.start();
|
|
76
90
|
await adapter.getSink().onTurn({
|
|
@@ -79,14 +93,14 @@ test("an ingress source platform scopes identity resolution and event dedupe", a
|
|
|
79
93
|
userText: "hello",
|
|
80
94
|
platform: "teams",
|
|
81
95
|
eventId: "event-1",
|
|
82
|
-
|
|
96
|
+
actor: { id: "user-1", kind: "human" },
|
|
83
97
|
});
|
|
84
98
|
expect(dedupeSeen).toHaveBeenCalledWith("message:teams:created:event-1:event-1", 300_000);
|
|
85
|
-
expect(
|
|
86
|
-
|
|
87
|
-
|
|
99
|
+
expect(identifyUser).toHaveBeenCalledWith(expect.objectContaining({
|
|
100
|
+
provider: "teams",
|
|
101
|
+
actor: expect.objectContaining({ id: "user-1" }),
|
|
88
102
|
}));
|
|
89
|
-
expect(
|
|
103
|
+
expect(userId).toBe("person-1");
|
|
90
104
|
});
|
|
91
105
|
test("non-text ingress keeps source platform in handler contexts and dedupe keys", async () => {
|
|
92
106
|
const state = new MemoryStore();
|
|
@@ -94,6 +108,7 @@ test("non-text ingress keeps source platform in handler contexts and dedupe keys
|
|
|
94
108
|
const adapter = new FakeAdapter({ platform: "intelligence" });
|
|
95
109
|
const observed = {};
|
|
96
110
|
const channel = createChannel({
|
|
111
|
+
identifyUser: "platform",
|
|
97
112
|
adapters: [adapter],
|
|
98
113
|
store: { adapter: state },
|
|
99
114
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kv-action-store.d.ts","sourceRoot":"","sources":["../../src/state/kv-action-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAkB,MAAM,oBAAoB,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,uFAAuF;AACvF,wBAAgB,aAAa,CAC3B,KAAK,EAAE,UAAU,EACjB,IAAI,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B,WAAW,
|
|
1
|
+
{"version":3,"file":"kv-action-store.d.ts","sourceRoot":"","sources":["../../src/state/kv-action-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAkB,MAAM,oBAAoB,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,uFAAuF;AACvF,wBAAgB,aAAa,CAC3B,KAAK,EAAE,UAAU,EACjB,IAAI,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B,WAAW,CAQb"}
|
|
@@ -4,6 +4,7 @@ export function kvActionStore(state, opts) {
|
|
|
4
4
|
return {
|
|
5
5
|
put: (id, snap, ttlMs) => state.kv.set(key(id), snap, ttlMs ?? opts?.defaultTtlMs),
|
|
6
6
|
get: (id) => state.kv.get(key(id)),
|
|
7
|
+
consume: (id) => state.kv.consume(key(id)),
|
|
7
8
|
delete: (id) => state.kv.delete(key(id)),
|
|
8
9
|
};
|
|
9
10
|
}
|
|
@@ -18,4 +18,15 @@ describe("kvActionStore", () => {
|
|
|
18
18
|
await store.delete("ck:abc");
|
|
19
19
|
expect(await store.get("ck:abc")).toBeUndefined();
|
|
20
20
|
});
|
|
21
|
+
it("consumes a snapshot once via state.kv", async () => {
|
|
22
|
+
const state = new MemoryStore();
|
|
23
|
+
const store = kvActionStore(state);
|
|
24
|
+
const snap = {
|
|
25
|
+
path: [0, "onClick"],
|
|
26
|
+
conversationKey: "c",
|
|
27
|
+
};
|
|
28
|
+
await store.put("ck:once", snap);
|
|
29
|
+
expect(await store.consume("ck:once")).toEqual(snap);
|
|
30
|
+
expect(await store.consume("ck:once")).toBeUndefined();
|
|
31
|
+
});
|
|
21
32
|
});
|
|
@@ -8,6 +8,7 @@ export declare class MemoryStore implements StateStore {
|
|
|
8
8
|
get: <T>(key: string) => Promise<T | undefined>;
|
|
9
9
|
set: <T>(key: string, value: T, ttlMs?: number) => Promise<void>;
|
|
10
10
|
delete: (key: string) => Promise<void>;
|
|
11
|
+
consume: <T>(key: string) => Promise<T | undefined>;
|
|
11
12
|
};
|
|
12
13
|
list: {
|
|
13
14
|
append: <T>(key: string, value: T, opts?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../src/state/memory-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAQnD,qBAAa,WAAY,YAAW,UAAU;IAC5C,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,KAAK,CAA4D;IACzE,OAAO,CAAC,MAAM,CAAgC;IAE9C,EAAE;cACY,CAAC,OAAO,MAAM,KAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;cAQvC,CAAC,OAAO,MAAM,SAAS,CAAC,UAAU,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;sBAMhD,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../src/state/memory-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAQnD,qBAAa,WAAY,YAAW,UAAU;IAC5C,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,KAAK,CAA4D;IACzE,OAAO,CAAC,MAAM,CAAgC;IAE9C,EAAE;cACY,CAAC,OAAO,MAAM,KAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;cAQvC,CAAC,OAAO,MAAM,SAAS,CAAC,UAAU,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;sBAMhD,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;kBAG1B,CAAC,OAAO,MAAM,KAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;MASvD;IAEF,IAAI;iBACa,CAAC,OACT,MAAM,SACJ,CAAC,SACD;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,KACzC,OAAO,CAAC,MAAM,CAAC;gBAYJ,CAAC,OAAO,MAAM,yBAAoB,MAAM,KAAG,OAAO,CAAC,CAAC,EAAE,CAAC;oBASnD,MAAM,UAAU,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;sBAMpC,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;MAG1C;IAEF,IAAI;uBAEK,MAAM,SACJ;YAAE,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,KACxB,OAAO,CAAC;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC;uBAWf,MAAM,SAAS,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;MAI1D;IAEF,KAAK;oBACe,MAAM,SAAS,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;MAO1D;IAEF,KAAK;kBACa,CAAC,OACV,MAAM,SACJ,CAAC,SACD;YAAE,OAAO,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,aAAa,GAAG,aAAa,CAAA;SAAE,KAClE,OAAO,CAAC,MAAM,CAAC;kBAaF,CAAC,OAAO,MAAM,KAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;qBAIpC,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;MAE3C;CACH"}
|
|
@@ -24,6 +24,15 @@ export class MemoryStore {
|
|
|
24
24
|
delete: async (key) => {
|
|
25
25
|
this.kvMap.delete(key);
|
|
26
26
|
},
|
|
27
|
+
consume: async (key) => {
|
|
28
|
+
const e = this.kvMap.get(key);
|
|
29
|
+
if (!live(e)) {
|
|
30
|
+
this.kvMap.delete(key);
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
this.kvMap.delete(key);
|
|
34
|
+
return e.value;
|
|
35
|
+
},
|
|
27
36
|
};
|
|
28
37
|
list = {
|
|
29
38
|
append: async (key, value, opts) => {
|
|
@@ -12,6 +12,8 @@ export interface StateStore {
|
|
|
12
12
|
get<T>(key: string): Promise<T | undefined>;
|
|
13
13
|
set<T>(key: string, value: T, ttlMs?: number): Promise<void>;
|
|
14
14
|
delete(key: string): Promise<void>;
|
|
15
|
+
/** Atomically return and delete one live value. */
|
|
16
|
+
consume<T>(key: string): Promise<T | undefined>;
|
|
15
17
|
};
|
|
16
18
|
list: {
|
|
17
19
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-store.d.ts","sourceRoot":"","sources":["../../src/state/state-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE;QACF,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;QAC5C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7D,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"state-store.d.ts","sourceRoot":"","sources":["../../src/state/state-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE;QACF,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;QAC5C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7D,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACnC,mDAAmD;QACnD,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;KACjD,CAAC;IACF,IAAI,EAAE;QACJ;;;;WAIG;QACH,MAAM,CAAC,CAAC,EACN,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,CAAC,EACR,IAAI,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,GACzC,OAAO,CAAC,MAAM,CAAC,CAAC;QACnB;;;;WAIG;QACH,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;QACnE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACpC,CAAC;IACF,IAAI,EAAE;QACJ;;;;;WAKG;QACH,OAAO,CACL,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE;YAAE,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,GACxB,OAAO,CAAC;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC,CAAC;QACrC,kDAAkD;QAClD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACpD,CAAC;IACF,KAAK,EAAE;QACL,+EAA+E;QAC/E,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KACpD,CAAC;IACF,KAAK,EAAE;QACL,OAAO,CAAC,CAAC,EACP,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,CAAC,EACR,IAAI,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,aAAa,GAAG,aAAa,CAAA;SAAE,GAClE,OAAO,CAAC,MAAM,CAAC,CAAC;QACnB,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;QAChD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;KACrC,CAAC;CACH"}
|
|
@@ -21,6 +21,7 @@ describe("createChannel telemetry wiring", () => {
|
|
|
21
21
|
// telemetry) is resolved there, not at construction, so an adapter attached
|
|
22
22
|
// via addAdapter can still provide the persistence backend.
|
|
23
23
|
const channel = createChannel({
|
|
24
|
+
identifyUser: "platform",
|
|
24
25
|
adapters: [new FakeAdapter()],
|
|
25
26
|
components: [
|
|
26
27
|
function Card() {
|
|
@@ -37,14 +38,14 @@ describe("createChannel telemetry wiring", () => {
|
|
|
37
38
|
});
|
|
38
39
|
it("emits oss.channel.started on start, start_failed (category only) on a throwing adapter", async () => {
|
|
39
40
|
const ok = new FakeAdapter();
|
|
40
|
-
const channel = createChannel({ adapters: [ok] });
|
|
41
|
+
const channel = createChannel({ identifyUser: "platform", adapters: [ok] });
|
|
41
42
|
await channel.ɵruntime.start();
|
|
42
43
|
expect(capture.mock.calls.find((c) => c[0] === "oss.channel.started")?.[1]
|
|
43
44
|
.startedCount).toBe(1);
|
|
44
45
|
capture.mockClear();
|
|
45
46
|
const bad = new FakeAdapter();
|
|
46
47
|
bad.start = () => Promise.reject(Object.assign(new Error("xoxb-SECRET token bad"), { code: "EAUTH" }));
|
|
47
|
-
const bot2 = createChannel({ adapters: [bad] });
|
|
48
|
+
const bot2 = createChannel({ identifyUser: "platform", adapters: [bad] });
|
|
48
49
|
// All adapters failed → start() rejects (the channel is dead; the runtime
|
|
49
50
|
// reports status "error"). The start_failed telemetry is still captured
|
|
50
51
|
// before the throw.
|
|
@@ -57,6 +58,7 @@ describe("createChannel telemetry wiring", () => {
|
|
|
57
58
|
it("emits oss.channel.agent_run on a successful run", async () => {
|
|
58
59
|
const fake = new FakeAdapter();
|
|
59
60
|
const channel = createChannel({
|
|
61
|
+
identifyUser: "platform",
|
|
60
62
|
adapters: [fake],
|
|
61
63
|
agent: () => new FakeAgent(),
|
|
62
64
|
});
|
|
@@ -34,6 +34,7 @@ describe("oss.channel.* end-to-end (real ChannelTelemetry, only network boundary
|
|
|
34
34
|
it("flows configured -> started -> agent_run with anonymous_id + channel_session_id and no env config", async () => {
|
|
35
35
|
const fake = new FakeAdapter();
|
|
36
36
|
const channel = createChannel({
|
|
37
|
+
identifyUser: "platform",
|
|
37
38
|
adapters: [fake],
|
|
38
39
|
agent: () => new FakeAgent(),
|
|
39
40
|
});
|
|
@@ -10,6 +10,11 @@ class FakeDurableStore {
|
|
|
10
10
|
get: async (k) => this.map.get(k),
|
|
11
11
|
set: async (k, v) => void this.map.set(k, v),
|
|
12
12
|
delete: async (k) => void this.map.delete(k),
|
|
13
|
+
consume: async (k) => {
|
|
14
|
+
const value = this.map.get(k);
|
|
15
|
+
this.map.delete(k);
|
|
16
|
+
return value;
|
|
17
|
+
},
|
|
13
18
|
};
|
|
14
19
|
list = {};
|
|
15
20
|
lock = {};
|
|
@@ -1,13 +1,31 @@
|
|
|
1
|
-
import type { ChannelNode, MessageRef,
|
|
2
|
-
import type { PlatformAdapter, SurfaceCapabilities, IngressSink, IncomingTurn, IncomingThreadStart, InteractionEvent, IncomingCommand, RunRenderer, ReplyTarget, NativePayload, UserQuery, ConversationStore, IncomingReaction, IncomingModalSubmit, IncomingModalClose, ModalSubmitResult } from "../platform-adapter.js";
|
|
1
|
+
import type { ChannelNode, MessageRef, ProviderActor, ThreadMessage } from "@copilotkit/channels-ui";
|
|
2
|
+
import type { PlatformAdapter, SurfaceCapabilities, IngressSink, IncomingTurn, IncomingThreadStart, IncomingWelcome, InteractionEvent, IncomingCommand, RunRenderer, ReplyTarget, NativePayload, UserQuery, ConversationStore, IncomingReaction, IncomingModalSubmit, IncomingModalClose, ModalSubmitResult } from "../platform-adapter.js";
|
|
3
3
|
import type { CommandSpec } from "../commands.js";
|
|
4
4
|
import type { StateStore } from "../state/state-store.js";
|
|
5
|
+
type OptionalIdentity<T extends {
|
|
6
|
+
actor: unknown;
|
|
7
|
+
identityContext: unknown;
|
|
8
|
+
}> = Omit<T, "actor" | "identityContext"> & Partial<Pick<T, "actor" | "identityContext">>;
|
|
9
|
+
type FakeIngressSink = {
|
|
10
|
+
onTurn(turn: OptionalIdentity<Omit<IncomingTurn, "operation">> & {
|
|
11
|
+
operation?: IncomingTurn["operation"];
|
|
12
|
+
}): void | Promise<void>;
|
|
13
|
+
onInteraction(event: OptionalIdentity<InteractionEvent>): void | Promise<void>;
|
|
14
|
+
onCommand(event: OptionalIdentity<IncomingCommand>): void | Promise<void>;
|
|
15
|
+
onThreadStarted(event: OptionalIdentity<IncomingThreadStart>): void | Promise<void>;
|
|
16
|
+
onWelcome(event: OptionalIdentity<IncomingWelcome>): void | Promise<void>;
|
|
17
|
+
onReaction(event: OptionalIdentity<IncomingReaction>): void | Promise<void>;
|
|
18
|
+
onModalSubmit(event: OptionalIdentity<IncomingModalSubmit>): Promise<ModalSubmitResult | void>;
|
|
19
|
+
onModalClose(event: OptionalIdentity<IncomingModalClose>): void | Promise<void>;
|
|
20
|
+
};
|
|
5
21
|
/** A RunRenderer whose subscriber captures tool-call-end and custom (interrupt) events — used by run-loop tests. */
|
|
6
22
|
export declare function makeFakeRunRenderer(): RunRenderer;
|
|
7
23
|
export declare class FakeAdapter implements PlatformAdapter {
|
|
8
24
|
platform: string;
|
|
9
25
|
readonly capabilities: SurfaceCapabilities;
|
|
10
26
|
readonly ackDeadlineMs = 3000;
|
|
27
|
+
supportsIntelligenceMemory?: boolean;
|
|
28
|
+
runAgentLifecycle?: PlatformAdapter["runAgentLifecycle"];
|
|
11
29
|
/** When true, `start()` rejects (set via constructor `failStart`). */
|
|
12
30
|
readonly failStart: boolean;
|
|
13
31
|
/** When true, `registerCommands()` rejects (set via constructor `failRegisterCommands`). */
|
|
@@ -57,18 +75,15 @@ export declare class FakeAdapter implements PlatformAdapter {
|
|
|
57
75
|
/** History returned by getMessages(); override in tests. */
|
|
58
76
|
messages: ThreadMessage[];
|
|
59
77
|
/** User returned by lookupUser(); override in tests. */
|
|
60
|
-
user?:
|
|
78
|
+
user?: ProviderActor;
|
|
61
79
|
/** Optional persistence backend the adapter provides (test-only); exercises createChannel's store resolution. */
|
|
62
80
|
stateStore?: StateStore;
|
|
63
81
|
private sink?;
|
|
64
82
|
private counter;
|
|
65
83
|
private turnCounter;
|
|
66
84
|
/** Expose the registered sink so tests can invoke onTurn() directly for overlap/lock tests. */
|
|
67
|
-
getSink():
|
|
68
|
-
|
|
69
|
-
operation?: IncomingTurn["operation"];
|
|
70
|
-
}): void | Promise<void>;
|
|
71
|
-
};
|
|
85
|
+
getSink(): FakeIngressSink;
|
|
86
|
+
private withIdentity;
|
|
72
87
|
start(sink: IngressSink): Promise<void>;
|
|
73
88
|
stop(): Promise<void>;
|
|
74
89
|
render(ir: ChannelNode[]): NativePayload;
|
|
@@ -78,7 +93,7 @@ export declare class FakeAdapter implements PlatformAdapter {
|
|
|
78
93
|
delete(_ref: MessageRef): Promise<void>;
|
|
79
94
|
createRunRenderer(_target: ReplyTarget): RunRenderer;
|
|
80
95
|
decodeInteraction(raw: unknown): InteractionEvent | undefined;
|
|
81
|
-
lookupUser(_q: UserQuery): Promise<
|
|
96
|
+
lookupUser(_q: UserQuery): Promise<ProviderActor | undefined>;
|
|
82
97
|
getMessages(_target: ReplyTarget): Promise<ThreadMessage[]>;
|
|
83
98
|
/** Suggested-prompt calls recorded by the capability-gated method (when present). */
|
|
84
99
|
suggestedPromptsCalls: {
|
|
@@ -124,6 +139,7 @@ export declare class FakeAdapter implements PlatformAdapter {
|
|
|
124
139
|
openModal?: PlatformAdapter["openModal"];
|
|
125
140
|
emitTurn(partial: Partial<IncomingTurn>): void;
|
|
126
141
|
emitThreadStarted(partial?: Partial<IncomingThreadStart>): Promise<void> | void;
|
|
142
|
+
emitWelcome(partial?: Partial<IncomingWelcome>): Promise<void> | void;
|
|
127
143
|
emitInteraction(partial: Partial<InteractionEvent>): void;
|
|
128
144
|
emitCommand(partial: Partial<IncomingCommand> & {
|
|
129
145
|
command: string;
|
|
@@ -141,4 +157,5 @@ export declare class FakeAdapter implements PlatformAdapter {
|
|
|
141
157
|
registeredCommands?: readonly CommandSpec[];
|
|
142
158
|
registerCommands(commands: readonly CommandSpec[]): Promise<void>;
|
|
143
159
|
}
|
|
160
|
+
export {};
|
|
144
161
|
//# sourceMappingURL=fake-adapter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fake-adapter.d.ts","sourceRoot":"","sources":["../../src/testing/fake-adapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,
|
|
1
|
+
{"version":3,"file":"fake-adapter.d.ts","sourceRoot":"","sources":["../../src/testing/fake-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -37,6 +37,8 @@ export class FakeAdapter {
|
|
|
37
37
|
platform = "fake";
|
|
38
38
|
capabilities;
|
|
39
39
|
ackDeadlineMs = 3000;
|
|
40
|
+
supportsIntelligenceMemory;
|
|
41
|
+
runAgentLifecycle;
|
|
40
42
|
/** When true, `start()` rejects (set via constructor `failStart`). */
|
|
41
43
|
failStart;
|
|
42
44
|
/** When true, `registerCommands()` rejects (set via constructor `failRegisterCommands`). */
|
|
@@ -149,24 +151,54 @@ export class FakeAdapter {
|
|
|
149
151
|
const adapter = this;
|
|
150
152
|
return new Proxy(sink, {
|
|
151
153
|
get(target, property, receiver) {
|
|
152
|
-
if (property
|
|
153
|
-
return
|
|
154
|
+
if (property === "onTurn") {
|
|
155
|
+
return (turn) => {
|
|
156
|
+
const syntheticId = turn.eventId ?? `fake-message-${++adapter.turnCounter}`;
|
|
157
|
+
return sink.onTurn(adapter.withIdentity({
|
|
158
|
+
...turn,
|
|
159
|
+
operation: turn.operation ?? {
|
|
160
|
+
kind: "created",
|
|
161
|
+
logicalMessageId: syntheticId,
|
|
162
|
+
revisionId: syntheticId,
|
|
163
|
+
mentioned: true,
|
|
164
|
+
},
|
|
165
|
+
}, "message"));
|
|
166
|
+
};
|
|
154
167
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
mentioned: true,
|
|
164
|
-
},
|
|
165
|
-
});
|
|
168
|
+
const triggers = {
|
|
169
|
+
onInteraction: "interaction",
|
|
170
|
+
onCommand: "command",
|
|
171
|
+
onThreadStarted: "thread-start",
|
|
172
|
+
onWelcome: "welcome",
|
|
173
|
+
onReaction: "reaction",
|
|
174
|
+
onModalSubmit: "modal-submit",
|
|
175
|
+
onModalClose: "modal-close",
|
|
166
176
|
};
|
|
177
|
+
const trigger = triggers[property];
|
|
178
|
+
if (trigger) {
|
|
179
|
+
return (event) => target[property](adapter.withIdentity(event, trigger));
|
|
180
|
+
}
|
|
181
|
+
return Reflect.get(target, property, receiver);
|
|
167
182
|
},
|
|
168
183
|
});
|
|
169
184
|
}
|
|
185
|
+
withIdentity(event, trigger) {
|
|
186
|
+
const conversationKey = typeof event.conversationKey === "string" ? event.conversationKey : "c";
|
|
187
|
+
const actor = event.actor ??
|
|
188
|
+
{ id: "", kind: "unknown" };
|
|
189
|
+
return {
|
|
190
|
+
...event,
|
|
191
|
+
actor,
|
|
192
|
+
identityContext: event.identityContext ?? {
|
|
193
|
+
tenant: { id: "fake-tenant" },
|
|
194
|
+
installation: { id: "fake-installation" },
|
|
195
|
+
conversation: { id: conversationKey, kind: "test" },
|
|
196
|
+
trigger,
|
|
197
|
+
event: {},
|
|
198
|
+
raw: {},
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
170
202
|
async start(sink) {
|
|
171
203
|
if (this.failStart)
|
|
172
204
|
throw new Error("fake-adapter: start failed");
|
|
@@ -230,7 +262,7 @@ export class FakeAdapter {
|
|
|
230
262
|
// --- test helpers ---
|
|
231
263
|
emitTurn(partial) {
|
|
232
264
|
const syntheticId = partial.eventId ?? `fake-message-${++this.turnCounter}`;
|
|
233
|
-
void this.
|
|
265
|
+
void this.getSink().onTurn({
|
|
234
266
|
conversationKey: "c",
|
|
235
267
|
replyTarget: {},
|
|
236
268
|
userText: "",
|
|
@@ -245,13 +277,21 @@ export class FakeAdapter {
|
|
|
245
277
|
});
|
|
246
278
|
}
|
|
247
279
|
emitThreadStarted(partial) {
|
|
248
|
-
return this.
|
|
280
|
+
return this.getSink().onThreadStarted({
|
|
249
281
|
conversationKey: "c",
|
|
250
282
|
replyTarget: {},
|
|
251
283
|
platform: "fake",
|
|
252
284
|
...partial,
|
|
253
285
|
});
|
|
254
286
|
}
|
|
287
|
+
emitWelcome(partial) {
|
|
288
|
+
return this.getSink().onWelcome(this.withIdentity({
|
|
289
|
+
conversationKey: "c",
|
|
290
|
+
replyTarget: {},
|
|
291
|
+
platform: "fake",
|
|
292
|
+
...partial,
|
|
293
|
+
}, "welcome"));
|
|
294
|
+
}
|
|
255
295
|
emitInteraction(partial) {
|
|
256
296
|
const evt = {
|
|
257
297
|
id: "",
|
|
@@ -259,11 +299,11 @@ export class FakeAdapter {
|
|
|
259
299
|
replyTarget: {},
|
|
260
300
|
...partial,
|
|
261
301
|
};
|
|
262
|
-
this.interactionsSeen.push(evt);
|
|
263
|
-
void this.
|
|
302
|
+
this.interactionsSeen.push(this.withIdentity(evt, "interaction"));
|
|
303
|
+
void this.getSink().onInteraction(evt);
|
|
264
304
|
}
|
|
265
305
|
emitCommand(partial) {
|
|
266
|
-
return this.
|
|
306
|
+
return this.getSink().onCommand({
|
|
267
307
|
text: "",
|
|
268
308
|
conversationKey: "c",
|
|
269
309
|
replyTarget: {},
|
|
@@ -272,7 +312,7 @@ export class FakeAdapter {
|
|
|
272
312
|
});
|
|
273
313
|
}
|
|
274
314
|
emitReaction(partial) {
|
|
275
|
-
return this.
|
|
315
|
+
return this.getSink().onReaction({
|
|
276
316
|
added: true,
|
|
277
317
|
conversationKey: "c",
|
|
278
318
|
replyTarget: {},
|
|
@@ -282,7 +322,7 @@ export class FakeAdapter {
|
|
|
282
322
|
});
|
|
283
323
|
}
|
|
284
324
|
emitModalSubmit(partial) {
|
|
285
|
-
return this.
|
|
325
|
+
return this.getSink().onModalSubmit({
|
|
286
326
|
values: {},
|
|
287
327
|
platform: "fake",
|
|
288
328
|
raw: {},
|
|
@@ -290,7 +330,11 @@ export class FakeAdapter {
|
|
|
290
330
|
});
|
|
291
331
|
}
|
|
292
332
|
emitModalClose(partial) {
|
|
293
|
-
return this.
|
|
333
|
+
return this.getSink().onModalClose({
|
|
334
|
+
platform: "fake",
|
|
335
|
+
raw: {},
|
|
336
|
+
...partial,
|
|
337
|
+
});
|
|
294
338
|
}
|
|
295
339
|
/** Commands handed to the adapter via `registerCommands`; asserts the capability hook fires. */
|
|
296
340
|
registeredCommands;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-store-conformance.d.ts","sourceRoot":"","sources":["../../src/testing/state-store-conformance.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,gFAAgF;AAChF,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,EAC5C,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAC1C,IAAI,
|
|
1
|
+
{"version":3,"file":"state-store-conformance.d.ts","sourceRoot":"","sources":["../../src/testing/state-store-conformance.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,gFAAgF;AAChF,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,EAC5C,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAC1C,IAAI,CAwIN"}
|
|
@@ -17,11 +17,27 @@ export function runStateStoreConformance(name, make, teardown) {
|
|
|
17
17
|
expect(await s.kv.get("a")).toBeUndefined();
|
|
18
18
|
});
|
|
19
19
|
it("missing key is undefined", async () => expect(await s.kv.get("nope")).toBeUndefined());
|
|
20
|
+
it("atomically consumes one live value", async () => {
|
|
21
|
+
await s.kv.set("once", { n: 1 });
|
|
22
|
+
const results = await Promise.all([
|
|
23
|
+
s.kv.consume("once"),
|
|
24
|
+
s.kv.consume("once"),
|
|
25
|
+
]);
|
|
26
|
+
expect(results.filter((value) => value !== undefined)).toEqual([
|
|
27
|
+
{ n: 1 },
|
|
28
|
+
]);
|
|
29
|
+
expect(await s.kv.get("once")).toBeUndefined();
|
|
30
|
+
});
|
|
20
31
|
it("expires after ttl", async () => {
|
|
21
32
|
await s.kv.set("t", 1, 30);
|
|
22
33
|
await new Promise((r) => setTimeout(r, 60));
|
|
23
34
|
expect(await s.kv.get("t")).toBeUndefined();
|
|
24
35
|
});
|
|
36
|
+
it("does not return an expired value from consume", async () => {
|
|
37
|
+
await s.kv.set("expired-once", 1, 30);
|
|
38
|
+
await new Promise((r) => setTimeout(r, 60));
|
|
39
|
+
expect(await s.kv.consume("expired-once")).toBeUndefined();
|
|
40
|
+
});
|
|
25
41
|
});
|
|
26
42
|
describe("list", () => {
|
|
27
43
|
it("appends oldest-first and ranges", async () => {
|
|
@@ -4,18 +4,29 @@ import { FakeAdapter } from "./testing/fake-adapter.js";
|
|
|
4
4
|
describe("onThreadStarted routing", () => {
|
|
5
5
|
it("invokes registered handlers with the thread and user", async () => {
|
|
6
6
|
const fake = new FakeAdapter();
|
|
7
|
-
const channel = createChannel({
|
|
7
|
+
const channel = createChannel({
|
|
8
|
+
adapters: [fake],
|
|
9
|
+
identifyUser: ({ actor }) => ({
|
|
10
|
+
id: actor.id,
|
|
11
|
+
name: actor.name ?? actor.id,
|
|
12
|
+
}),
|
|
13
|
+
});
|
|
8
14
|
const seen = [];
|
|
9
|
-
channel.onThreadStarted(({ thread, user }) => {
|
|
10
|
-
seen.push({ user: user?.id, platform: thread.platform });
|
|
15
|
+
channel.onThreadStarted(({ thread, user, actor }) => {
|
|
16
|
+
seen.push({ user: user?.id, actor: actor.id, platform: thread.platform });
|
|
11
17
|
});
|
|
12
18
|
await channel.ɵruntime.start();
|
|
13
|
-
await fake.emitThreadStarted({
|
|
14
|
-
|
|
19
|
+
await fake.emitThreadStarted({
|
|
20
|
+
actor: { id: "U1", kind: "human", name: "Ada" },
|
|
21
|
+
});
|
|
22
|
+
expect(seen).toEqual([{ user: "U1", actor: "U1", platform: "fake" }]);
|
|
15
23
|
});
|
|
16
24
|
it("invokes every registered handler in order", async () => {
|
|
17
25
|
const fake = new FakeAdapter();
|
|
18
|
-
const channel = createChannel({
|
|
26
|
+
const channel = createChannel({
|
|
27
|
+
identifyUser: "platform",
|
|
28
|
+
adapters: [fake],
|
|
29
|
+
});
|
|
19
30
|
const order = [];
|
|
20
31
|
channel.onThreadStarted(() => {
|
|
21
32
|
order.push(1);
|
|
@@ -29,7 +40,10 @@ describe("onThreadStarted routing", () => {
|
|
|
29
40
|
});
|
|
30
41
|
it("is a no-op when no handler is registered", async () => {
|
|
31
42
|
const fake = new FakeAdapter();
|
|
32
|
-
const channel = createChannel({
|
|
43
|
+
const channel = createChannel({
|
|
44
|
+
identifyUser: "platform",
|
|
45
|
+
adapters: [fake],
|
|
46
|
+
});
|
|
33
47
|
await channel.ɵruntime.start();
|
|
34
48
|
// Should not throw.
|
|
35
49
|
await expect(Promise.resolve(fake.emitThreadStarted())).resolves.toBeUndefined();
|
|
@@ -38,7 +52,10 @@ describe("onThreadStarted routing", () => {
|
|
|
38
52
|
describe("Thread.setSuggestedPrompts / setTitle capability gating", () => {
|
|
39
53
|
it("delegates to the adapter when supported", async () => {
|
|
40
54
|
const fake = new FakeAdapter();
|
|
41
|
-
const channel = createChannel({
|
|
55
|
+
const channel = createChannel({
|
|
56
|
+
identifyUser: "platform",
|
|
57
|
+
adapters: [fake],
|
|
58
|
+
});
|
|
42
59
|
const results = [];
|
|
43
60
|
channel.onThreadStarted(async ({ thread }) => {
|
|
44
61
|
results.push(await thread.setSuggestedPrompts([{ title: "Triage", message: "Triage my issues" }], { title: "Try" }));
|
|
@@ -59,7 +76,10 @@ describe("Thread.setSuggestedPrompts / setTitle capability gating", () => {
|
|
|
59
76
|
});
|
|
60
77
|
it("returns { ok: false } without throwing when unsupported", async () => {
|
|
61
78
|
const fake = new FakeAdapter({ paneMethods: false });
|
|
62
|
-
const channel = createChannel({
|
|
79
|
+
const channel = createChannel({
|
|
80
|
+
identifyUser: "platform",
|
|
81
|
+
adapters: [fake],
|
|
82
|
+
});
|
|
63
83
|
const results = [];
|
|
64
84
|
channel.onThreadStarted(async ({ thread }) => {
|
|
65
85
|
results.push(await thread.setSuggestedPrompts([]));
|
|
@@ -2,10 +2,13 @@ import { describe, it, expect } from "vitest";
|
|
|
2
2
|
import { createChannel } from "./create-channel.js";
|
|
3
3
|
import { FakeAdapter } from "./testing/fake-adapter.js";
|
|
4
4
|
async function runOnMessage(fake, fn) {
|
|
5
|
-
const channel = createChannel({ adapters: [fake] });
|
|
5
|
+
const channel = createChannel({ identifyUser: "platform", adapters: [fake] });
|
|
6
6
|
channel.onMessage(fn);
|
|
7
7
|
await channel.ɵruntime.start();
|
|
8
|
-
fake.emitTurn({
|
|
8
|
+
fake.emitTurn({
|
|
9
|
+
userText: "hi",
|
|
10
|
+
actor: { id: "U1", kind: "human" },
|
|
11
|
+
});
|
|
9
12
|
await new Promise((r) => setTimeout(r, 0));
|
|
10
13
|
}
|
|
11
14
|
describe("Thread.postEphemeral", () => {
|
|
@@ -13,7 +16,7 @@ describe("Thread.postEphemeral", () => {
|
|
|
13
16
|
const fake = new FakeAdapter({ nativeEphemeral: true });
|
|
14
17
|
let res;
|
|
15
18
|
await runOnMessage(fake, async ({ thread, message }) => {
|
|
16
|
-
res = await thread.postEphemeral(message.
|
|
19
|
+
res = await thread.postEphemeral(message.actor, "psst", {
|
|
17
20
|
fallbackToDM: false,
|
|
18
21
|
});
|
|
19
22
|
});
|
|
@@ -26,7 +29,7 @@ describe("Thread.postEphemeral", () => {
|
|
|
26
29
|
const fake = new FakeAdapter({ nativeEphemeral: false });
|
|
27
30
|
let res;
|
|
28
31
|
await runOnMessage(fake, async ({ thread, message }) => {
|
|
29
|
-
res = await thread.postEphemeral(message.
|
|
32
|
+
res = await thread.postEphemeral(message.actor, "psst", {
|
|
30
33
|
fallbackToDM: true,
|
|
31
34
|
});
|
|
32
35
|
});
|
|
@@ -36,7 +39,7 @@ describe("Thread.postEphemeral", () => {
|
|
|
36
39
|
const fake = new FakeAdapter({ nativeEphemeral: false });
|
|
37
40
|
let res = "sentinel";
|
|
38
41
|
await runOnMessage(fake, async ({ thread, message }) => {
|
|
39
|
-
res = await thread.postEphemeral(message.
|
|
42
|
+
res = await thread.postEphemeral(message.actor, "psst", {
|
|
40
43
|
fallbackToDM: false,
|
|
41
44
|
});
|
|
42
45
|
});
|