@copilotkit/channels-core 0.9.0 → 0.9.1
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 +2 -2
- package/dist/canonical-run-loop.test.js +110 -3
- package/dist/channel-memory.test.js +2 -0
- package/dist/codec.d.ts +6 -4
- package/dist/codec.d.ts.map +1 -1
- package/dist/create-channel.d.ts +19 -2
- package/dist/create-channel.d.ts.map +1 -1
- package/dist/create-channel.js +6 -0
- package/dist/create-channel.test.js +8 -0
- package/dist/hitl-continuation.test.js +17 -4
- package/dist/identity.d.ts +44 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +31 -0
- package/dist/identity.test.js +28 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/interrupt-event-echo.test.d.ts +2 -0
- package/dist/interrupt-event-echo.test.d.ts.map +1 -0
- package/dist/interrupt-event-echo.test.js +161 -0
- package/dist/platform-adapter.d.ts +3 -1
- package/dist/platform-adapter.d.ts.map +1 -1
- package/dist/run-loop.d.ts +21 -2
- package/dist/run-loop.d.ts.map +1 -1
- package/dist/run-loop.js +46 -5
- package/dist/run-loop.test.js +56 -0
- package/dist/sanitize-agent-events.test.js +27 -6
- package/dist/testing/fake-agent.d.ts +3 -1
- package/dist/testing/fake-agent.d.ts.map +1 -1
- package/dist/testing/fake-agent.js +4 -1
- package/dist/thread.d.ts +22 -0
- package/dist/thread.d.ts.map +1 -1
- package/dist/thread.js +67 -1
- package/package.json +6 -6
package/dist/thread.js
CHANGED
|
@@ -2,7 +2,17 @@ import { runAgentLoop } from "./run-loop.js";
|
|
|
2
2
|
import { errorClass, normalizePlatform } from "./telemetry/sanitize-error.js";
|
|
3
3
|
import { toAgentToolDescriptors } from "./tools.js";
|
|
4
4
|
import { validateSchema } from "./standard-schema.js";
|
|
5
|
+
import { channelActorIdentity } from "./identity.js";
|
|
5
6
|
import { hasMemoryAccess, resolveMemoryGrant } from "./memory.js";
|
|
7
|
+
/**
|
|
8
|
+
* Default retention for a captured interrupt value (7 days) — deliberately the
|
|
9
|
+
* same default the action store uses, so the retained value and the button that
|
|
10
|
+
* consumes it expire together. If a channel configures a LONGER
|
|
11
|
+
* `actionRetentionMs`, the value can lapse before the button does; a resume then
|
|
12
|
+
* simply omits `command.interruptEvent`, which is exactly the pre-existing
|
|
13
|
+
* behaviour rather than a new failure.
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_INTERRUPT_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
|
|
6
16
|
/** A Channel run requested Memory without an attached Intelligence backend. */
|
|
7
17
|
export class ChannelMemoryUnavailableError extends Error {
|
|
8
18
|
code = "channel_memory_unavailable";
|
|
@@ -254,6 +264,33 @@ export class Thread {
|
|
|
254
264
|
state() {
|
|
255
265
|
return this.trackOperation(() => this.store.kv.get(`threadstate:${this.deps.conversationKey}`));
|
|
256
266
|
}
|
|
267
|
+
interruptEventKey() {
|
|
268
|
+
return `interruptevent:${this.deps.conversationKey}`;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Retain the raw value of a captured interrupt so a later {@link resume} can
|
|
272
|
+
* echo it back to the agent as `command.interruptEvent`.
|
|
273
|
+
*
|
|
274
|
+
* PERSISTED, not in-memory, because the resume arrives in a DIFFERENT run —
|
|
275
|
+
* possibly in a different process after a restart, which is the whole reason
|
|
276
|
+
* this HITL path exists. Stored per conversation, matching the fact that a
|
|
277
|
+
* renderer tracks a single pending interrupt: a second interrupt in the same
|
|
278
|
+
* conversation legitimately supersedes the first.
|
|
279
|
+
*
|
|
280
|
+
* Best-effort: an agent that needs no correlation data (LangGraph) resumes
|
|
281
|
+
* fine without this, so a store failure must not take the interrupt down.
|
|
282
|
+
*/
|
|
283
|
+
async rememberInterruptEvent(value) {
|
|
284
|
+
if (value === undefined)
|
|
285
|
+
return;
|
|
286
|
+
try {
|
|
287
|
+
await this.store.kv.set(this.interruptEventKey(), value, this.deps.interruptRetentionMs ?? DEFAULT_INTERRUPT_RETENTION_MS);
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
console.warn("[channel] could not retain the interrupt value; a resume will omit " +
|
|
291
|
+
"command.interruptEvent (fine for LangGraph, breaks Mastra):", err);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
257
294
|
/** Read the conversation's messages (returns `[]` when the adapter can't read history). */
|
|
258
295
|
getMessages() {
|
|
259
296
|
return this.trackOperation(async () => (await this.deps.adapter.getMessages?.(this.deps.replyTarget)) ?? []);
|
|
@@ -348,8 +385,28 @@ export class Thread {
|
|
|
348
385
|
initiator: claimed.initiator,
|
|
349
386
|
};
|
|
350
387
|
this.activeContinuation = continuation;
|
|
388
|
+
// Echo the originating interrupt back to the agent. Some AG-UI bridges
|
|
389
|
+
// (e.g. @ag-ui/mastra) key their resume off it — it carries the correlation
|
|
390
|
+
// ids identifying WHICH suspended call to continue — and silently ignore a
|
|
391
|
+
// resume without it. LangGraph needs no correlation data and ignores the
|
|
392
|
+
// extra field, so this is additive for existing agents.
|
|
393
|
+
//
|
|
394
|
+
// `consume` is atomic take-and-delete, matching the one-use continuation
|
|
395
|
+
// claimed just above: a replayed click must not resurrect a spent resume.
|
|
396
|
+
// Omitted entirely when absent, so the wire shape is byte-identical to
|
|
397
|
+
// before for any flow that never captured an interrupt value.
|
|
398
|
+
let interruptEvent;
|
|
399
|
+
try {
|
|
400
|
+
interruptEvent = await this.store.kv.consume(this.interruptEventKey());
|
|
401
|
+
}
|
|
402
|
+
catch (err) {
|
|
403
|
+
console.warn("[channel] could not read the retained interrupt value; resuming " +
|
|
404
|
+
"without command.interruptEvent:", err);
|
|
405
|
+
}
|
|
351
406
|
try {
|
|
352
|
-
return await this.run(
|
|
407
|
+
return await this.run(interruptEvent === undefined
|
|
408
|
+
? { resume: value }
|
|
409
|
+
: { resume: value, interruptEvent }, { memory });
|
|
353
410
|
}
|
|
354
411
|
finally {
|
|
355
412
|
if (this.activeContinuation === continuation) {
|
|
@@ -480,6 +537,7 @@ export class Thread {
|
|
|
480
537
|
// reported as agent_run_failed (with the right stage) instead of being
|
|
481
538
|
// hidden behind an already-sent success event.
|
|
482
539
|
let stage = "agent";
|
|
540
|
+
const identity = channelActorIdentity(this.deps.actor, this.platform);
|
|
483
541
|
try {
|
|
484
542
|
const loopArgs = {
|
|
485
543
|
agent: session.agent,
|
|
@@ -487,6 +545,9 @@ export class Thread {
|
|
|
487
545
|
tools,
|
|
488
546
|
toolDescriptors,
|
|
489
547
|
context,
|
|
548
|
+
// The platform's word for who spoke, taken from this Thread's ingress
|
|
549
|
+
// rather than from the run's caller. See `channelActorIdentity`.
|
|
550
|
+
...(identity ? { identity } : {}),
|
|
490
551
|
makeToolCtx: () => ({
|
|
491
552
|
thread: this,
|
|
492
553
|
message: this.deps.message,
|
|
@@ -496,6 +557,10 @@ export class Thread {
|
|
|
496
557
|
platform: this.platform,
|
|
497
558
|
}),
|
|
498
559
|
handleInterrupt: async (interrupt) => {
|
|
560
|
+
// Retain BEFORE dispatching: the handler posts the picker, and the
|
|
561
|
+
// click that resumes it can arrive at any later moment, so the value
|
|
562
|
+
// has to already be durable by the time the button exists.
|
|
563
|
+
await this.rememberInterruptEvent(interrupt.value);
|
|
499
564
|
const h = this.deps.interruptHandlers.get(interrupt.eventName);
|
|
500
565
|
if (h)
|
|
501
566
|
await h({
|
|
@@ -515,6 +580,7 @@ export class Thread {
|
|
|
515
580
|
tools: toolDescriptors,
|
|
516
581
|
context,
|
|
517
582
|
isResume: initialResume !== undefined,
|
|
583
|
+
user: this.deps.user,
|
|
518
584
|
memory: extra?.memory,
|
|
519
585
|
execute: (subscriber, canonicalRun) => runAgentLoop({
|
|
520
586
|
...loopArgs,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@copilotkit/channels-core",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Platform-agnostic JSX channel engine for CopilotKit (createChannel, Thread, PlatformAdapter, ActionStore).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -48,12 +48,12 @@
|
|
|
48
48
|
"dist"
|
|
49
49
|
],
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@ag-ui/client": "0.0.
|
|
52
|
-
"@ag-ui/core": "0.0.
|
|
51
|
+
"@ag-ui/client": "0.0.59",
|
|
52
|
+
"@ag-ui/core": "0.0.59",
|
|
53
53
|
"zod-to-json-schema": "^3.24.1",
|
|
54
|
-
"@copilotkit/channels-ui": "~0.9.
|
|
55
|
-
"@copilotkit/
|
|
56
|
-
"@copilotkit/
|
|
54
|
+
"@copilotkit/channels-ui": "~0.9.1",
|
|
55
|
+
"@copilotkit/core": "^1.70.0",
|
|
56
|
+
"@copilotkit/shared": "^1.70.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@types/node": "^22.10.0",
|