@copilotkit/runtime 1.56.3 → 1.56.4-canary.1777529757
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/dist/agent/converters/tanstack.cjs +121 -25
- package/dist/agent/converters/tanstack.cjs.map +1 -1
- package/dist/agent/converters/tanstack.d.cts.map +1 -1
- package/dist/agent/converters/tanstack.d.mts.map +1 -1
- package/dist/agent/converters/tanstack.mjs +121 -25
- package/dist/agent/converters/tanstack.mjs.map +1 -1
- package/dist/lib/runtime/agent-integrations/langgraph/agent.cjs +8 -1
- package/dist/lib/runtime/agent-integrations/langgraph/agent.cjs.map +1 -1
- package/dist/lib/runtime/agent-integrations/langgraph/agent.d.cts.map +1 -1
- package/dist/lib/runtime/agent-integrations/langgraph/agent.d.mts.map +1 -1
- package/dist/lib/runtime/agent-integrations/langgraph/agent.mjs +8 -1
- package/dist/lib/runtime/agent-integrations/langgraph/agent.mjs.map +1 -1
- package/dist/package.cjs +6 -6
- package/dist/package.mjs +6 -6
- package/dist/v2/index.d.cts +2 -2
- package/dist/v2/index.d.mts +2 -2
- package/dist/v2/runtime/core/fetch-handler.cjs +2 -0
- package/dist/v2/runtime/core/fetch-handler.cjs.map +1 -1
- package/dist/v2/runtime/core/fetch-handler.d.cts.map +1 -1
- package/dist/v2/runtime/core/fetch-handler.d.mts.map +1 -1
- package/dist/v2/runtime/core/fetch-handler.mjs +2 -0
- package/dist/v2/runtime/core/fetch-handler.mjs.map +1 -1
- package/dist/v2/runtime/core/runtime.d.mts +0 -1
- package/dist/v2/runtime/core/runtime.d.mts.map +1 -1
- package/dist/v2/runtime/endpoints/express.cjs +5 -5
- package/dist/v2/runtime/endpoints/express.cjs.map +1 -1
- package/dist/v2/runtime/endpoints/express.mjs +5 -5
- package/dist/v2/runtime/endpoints/express.mjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-connect.cjs +2 -3
- package/dist/v2/runtime/handlers/handle-connect.cjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-connect.mjs +2 -3
- package/dist/v2/runtime/handlers/handle-connect.mjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/connect.cjs +21 -31
- package/dist/v2/runtime/handlers/intelligence/connect.cjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/connect.mjs +22 -31
- package/dist/v2/runtime/handlers/intelligence/connect.mjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/run.cjs +111 -26
- package/dist/v2/runtime/handlers/intelligence/run.cjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/run.mjs +111 -26
- package/dist/v2/runtime/handlers/intelligence/run.mjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/intelligence-utils.cjs +7 -3
- package/dist/v2/runtime/handlers/shared/intelligence-utils.cjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/intelligence-utils.mjs +7 -3
- package/dist/v2/runtime/handlers/shared/intelligence-utils.mjs.map +1 -1
- package/dist/v2/runtime/index.d.cts +1 -1
- package/dist/v2/runtime/index.d.mts +1 -2
- package/dist/v2/runtime/index.d.mts.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.cjs +5 -2
- package/dist/v2/runtime/intelligence-platform/client.cjs.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.d.cts +16 -18
- package/dist/v2/runtime/intelligence-platform/client.d.cts.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.d.mts +16 -18
- package/dist/v2/runtime/intelligence-platform/client.d.mts.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.mjs +5 -2
- package/dist/v2/runtime/intelligence-platform/client.mjs.map +1 -1
- package/dist/v2/runtime/runner/agent-runner.cjs.map +1 -1
- package/dist/v2/runtime/runner/agent-runner.d.cts +0 -1
- package/dist/v2/runtime/runner/agent-runner.d.cts.map +1 -1
- package/dist/v2/runtime/runner/agent-runner.d.mts +0 -1
- package/dist/v2/runtime/runner/agent-runner.d.mts.map +1 -1
- package/dist/v2/runtime/runner/agent-runner.mjs.map +1 -1
- package/dist/v2/runtime/runner/index.d.cts +1 -1
- package/dist/v2/runtime/runner/index.d.mts +1 -1
- package/dist/v2/runtime/runner/intelligence.cjs +30 -5
- package/dist/v2/runtime/runner/intelligence.cjs.map +1 -1
- package/dist/v2/runtime/runner/intelligence.d.cts +7 -1
- package/dist/v2/runtime/runner/intelligence.d.cts.map +1 -1
- package/dist/v2/runtime/runner/intelligence.d.mts +7 -1
- package/dist/v2/runtime/runner/intelligence.d.mts.map +1 -1
- package/dist/v2/runtime/runner/intelligence.mjs +30 -5
- package/dist/v2/runtime/runner/intelligence.mjs.map +1 -1
- package/dist/v2/runtime/telemetry/instance-created.cjs +33 -0
- package/dist/v2/runtime/telemetry/instance-created.cjs.map +1 -0
- package/dist/v2/runtime/telemetry/instance-created.mjs +33 -0
- package/dist/v2/runtime/telemetry/instance-created.mjs.map +1 -0
- package/dist/v2/runtime/telemetry/telemetry-client.cjs +1 -38
- package/dist/v2/runtime/telemetry/telemetry-client.cjs.map +1 -1
- package/dist/v2/runtime/telemetry/telemetry-client.mjs +1 -37
- package/dist/v2/runtime/telemetry/telemetry-client.mjs.map +1 -1
- package/package.json +7 -7
- package/src/agent/__tests__/agent-test-helpers.ts +31 -1
- package/src/agent/__tests__/converter-tanstack.test.ts +280 -0
- package/src/agent/converters/tanstack.ts +167 -10
- package/src/lib/runtime/agent-integrations/langgraph/agent.ts +8 -1
- package/src/v2/runtime/__tests__/express-fetch-bridge.test.ts +1 -1
- package/src/v2/runtime/__tests__/express-single-telemetry.integration.test.ts +65 -0
- package/src/v2/runtime/__tests__/express-telemetry.integration.test.ts +101 -0
- package/src/v2/runtime/__tests__/handle-connect.test.ts +155 -48
- package/src/v2/runtime/__tests__/handle-run.test.ts +380 -29
- package/src/v2/runtime/__tests__/hono-single-telemetry.integration.test.ts +46 -0
- package/src/v2/runtime/__tests__/hono-telemetry.integration.test.ts +99 -0
- package/src/v2/runtime/__tests__/intelligence-run-telemetry.test.ts +194 -0
- package/src/v2/runtime/__tests__/sse-response-telemetry.test.ts +108 -0
- package/src/v2/runtime/__tests__/telemetry.test.ts +0 -61
- package/src/v2/runtime/core/fetch-handler.ts +3 -0
- package/src/v2/runtime/endpoints/express.ts +9 -3
- package/src/v2/runtime/handlers/handle-connect.ts +1 -2
- package/src/v2/runtime/handlers/intelligence/connect.ts +48 -68
- package/src/v2/runtime/handlers/intelligence/run.ts +162 -21
- package/src/v2/runtime/handlers/shared/intelligence-utils.ts +21 -1
- package/src/v2/runtime/intelligence-platform/__tests__/client.test.ts +33 -39
- package/src/v2/runtime/intelligence-platform/client.ts +36 -31
- package/src/v2/runtime/runner/__tests__/intelligence-runner.test.ts +15 -7
- package/src/v2/runtime/runner/agent-runner.ts +0 -1
- package/src/v2/runtime/runner/intelligence.ts +47 -6
- package/src/v2/runtime/telemetry/__tests__/instance-created.test.ts +96 -0
- package/src/v2/runtime/telemetry/instance-created.ts +44 -0
- package/src/v2/runtime/telemetry/telemetry-client.ts +1 -57
- package/dist/v2/runtime/intelligence-platform/index.d.mts +0 -2
- package/dist/v2/runtime/telemetry/utils.cjs +0 -15
- package/dist/v2/runtime/telemetry/utils.cjs.map +0 -1
- package/dist/v2/runtime/telemetry/utils.mjs +0 -14
- package/dist/v2/runtime/telemetry/utils.mjs.map +0 -1
- package/src/v2/runtime/telemetry/utils.ts +0 -15
|
@@ -1,76 +1,33 @@
|
|
|
1
|
-
import { BaseEvent, EventType, RunStartedEvent } from "@ag-ui/client";
|
|
2
1
|
import { CopilotIntelligenceRuntimeLike } from "../../core/runtime";
|
|
3
|
-
import
|
|
4
|
-
ConnectThreadBootstrapResponse,
|
|
5
|
-
ConnectThreadLiveResponse,
|
|
6
|
-
} from "../../intelligence-platform/client";
|
|
7
|
-
import { isPlatformNotFoundError } from "../shared/intelligence-utils";
|
|
2
|
+
import { getPlatformErrorStatus } from "../shared/intelligence-utils";
|
|
8
3
|
import { resolveIntelligenceUser } from "../shared/resolve-intelligence-user";
|
|
9
4
|
import { isHandlerResponse } from "../shared/json-response";
|
|
10
5
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Builds browser-facing realtime connection metadata owned by the runtime.
|
|
8
|
+
*/
|
|
9
|
+
function buildRealtimeConnectionInfo(params: {
|
|
10
|
+
clientUrl: string;
|
|
14
11
|
threadId: string;
|
|
15
|
-
|
|
16
|
-
lastSeenEventId: string | null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function stampCanonicalConnectEvent(
|
|
20
|
-
event: BaseEvent,
|
|
21
|
-
threadId: string,
|
|
22
|
-
runId: string,
|
|
23
|
-
): BaseEvent {
|
|
24
|
-
const {
|
|
25
|
-
thread_id: _threadId,
|
|
26
|
-
run_id: _runId,
|
|
27
|
-
...eventRecord
|
|
28
|
-
} = event as BaseEvent & {
|
|
29
|
-
thread_id?: unknown;
|
|
30
|
-
run_id?: unknown;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
if (event.type === EventType.RUN_STARTED) {
|
|
34
|
-
const runStarted = eventRecord as RunStartedEvent;
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
...runStarted,
|
|
38
|
-
threadId,
|
|
39
|
-
runId,
|
|
40
|
-
input: {
|
|
41
|
-
...(runStarted.input ?? {}),
|
|
42
|
-
threadId,
|
|
43
|
-
runId,
|
|
44
|
-
},
|
|
45
|
-
} as RunStartedEvent;
|
|
46
|
-
}
|
|
47
|
-
|
|
12
|
+
}): { clientUrl: string; topic: string } {
|
|
48
13
|
return {
|
|
49
|
-
|
|
50
|
-
threadId
|
|
51
|
-
|
|
52
|
-
} as BaseEvent;
|
|
14
|
+
clientUrl: params.clientUrl,
|
|
15
|
+
topic: `thread:${params.threadId}`,
|
|
16
|
+
};
|
|
53
17
|
}
|
|
54
18
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
...result,
|
|
62
|
-
events: result.events.map((event) =>
|
|
63
|
-
stampCanonicalConnectEvent(event, threadId, runId),
|
|
64
|
-
),
|
|
65
|
-
};
|
|
19
|
+
interface HandleIntelligenceConnectParams {
|
|
20
|
+
runtime: CopilotIntelligenceRuntimeLike;
|
|
21
|
+
request: Request;
|
|
22
|
+
agentId: string;
|
|
23
|
+
threadId: string;
|
|
66
24
|
}
|
|
67
25
|
|
|
68
26
|
export async function handleIntelligenceConnect({
|
|
69
27
|
runtime,
|
|
70
28
|
request,
|
|
29
|
+
agentId,
|
|
71
30
|
threadId,
|
|
72
|
-
runId,
|
|
73
|
-
lastSeenEventId,
|
|
74
31
|
}: HandleIntelligenceConnectParams): Promise<Response> {
|
|
75
32
|
if (!runtime.intelligence) {
|
|
76
33
|
return Response.json(
|
|
@@ -91,8 +48,7 @@ export async function handleIntelligenceConnect({
|
|
|
91
48
|
const result = await runtime.intelligence.ɵconnectThread({
|
|
92
49
|
threadId,
|
|
93
50
|
userId: user.id,
|
|
94
|
-
|
|
95
|
-
lastSeenEventId,
|
|
51
|
+
agentId,
|
|
96
52
|
});
|
|
97
53
|
|
|
98
54
|
if (result === null) {
|
|
@@ -101,14 +57,38 @@ export async function handleIntelligenceConnect({
|
|
|
101
57
|
});
|
|
102
58
|
}
|
|
103
59
|
|
|
104
|
-
return Response.json(
|
|
105
|
-
|
|
106
|
-
|
|
60
|
+
return Response.json(
|
|
61
|
+
{
|
|
62
|
+
threadId: result.threadId,
|
|
63
|
+
joinToken: result.joinToken,
|
|
64
|
+
realtime: buildRealtimeConnectionInfo({
|
|
65
|
+
clientUrl: runtime.intelligence.ɵgetClientWsUrl(),
|
|
66
|
+
threadId: result.threadId,
|
|
67
|
+
}),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
headers: { "Cache-Control": "no-cache", Connection: "keep-alive" },
|
|
71
|
+
},
|
|
72
|
+
);
|
|
107
73
|
} catch (error) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
74
|
+
const status = getPlatformErrorStatus(error);
|
|
75
|
+
if (
|
|
76
|
+
status === 400 ||
|
|
77
|
+
status === 401 ||
|
|
78
|
+
status === 403 ||
|
|
79
|
+
status === 404 ||
|
|
80
|
+
status === 409
|
|
81
|
+
) {
|
|
82
|
+
return Response.json(
|
|
83
|
+
{
|
|
84
|
+
error: "Connect request rejected",
|
|
85
|
+
message:
|
|
86
|
+
error instanceof Error
|
|
87
|
+
? error.message
|
|
88
|
+
: "Intelligence platform rejected the connect request",
|
|
89
|
+
},
|
|
90
|
+
{ status },
|
|
91
|
+
);
|
|
112
92
|
}
|
|
113
93
|
|
|
114
94
|
console.error("Connect plan not available:", error);
|
|
@@ -1,10 +1,53 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
AbstractAgent,
|
|
3
|
+
BaseEvent,
|
|
4
|
+
EventType,
|
|
5
|
+
Message,
|
|
6
|
+
RunAgentInput,
|
|
7
|
+
} from "@ag-ui/client";
|
|
2
8
|
import { CopilotIntelligenceRuntimeLike } from "../../core/runtime";
|
|
3
9
|
import { generateThreadNameForNewThread } from "./thread-names";
|
|
4
10
|
import { logger } from "@copilotkit/shared";
|
|
5
11
|
import { telemetry } from "../../telemetry";
|
|
6
12
|
import { resolveIntelligenceUser } from "../shared/resolve-intelligence-user";
|
|
7
13
|
import { isHandlerResponse } from "../shared/json-response";
|
|
14
|
+
import { AgentRunnerRunRequest } from "../../runner/agent-runner";
|
|
15
|
+
import { Observable } from "rxjs";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Builds browser-facing realtime connection metadata owned by the runtime.
|
|
19
|
+
*/
|
|
20
|
+
function buildRealtimeConnectionInfo(params: {
|
|
21
|
+
clientUrl: string;
|
|
22
|
+
threadId: string;
|
|
23
|
+
}): { clientUrl: string; topic: string } {
|
|
24
|
+
return {
|
|
25
|
+
clientUrl: params.clientUrl,
|
|
26
|
+
topic: `thread:${params.threadId}`,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface RunnerStartupBoundary {
|
|
31
|
+
events: Observable<BaseEvent>;
|
|
32
|
+
startup: Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface RunnerWithStartupBoundary {
|
|
36
|
+
runWithStartupBoundary(request: AgentRunnerRunRequest): RunnerStartupBoundary;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function hasRunnerStartupBoundary(
|
|
40
|
+
runner: CopilotIntelligenceRuntimeLike["runner"],
|
|
41
|
+
): runner is CopilotIntelligenceRuntimeLike["runner"] &
|
|
42
|
+
RunnerWithStartupBoundary {
|
|
43
|
+
const candidate = runner as { runWithStartupBoundary?: unknown };
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
typeof candidate.runWithStartupBoundary === "function" &&
|
|
47
|
+
(Object.prototype.hasOwnProperty.call(runner, "runWithStartupBoundary") ||
|
|
48
|
+
Object.prototype.hasOwnProperty.call(runner, "threads"))
|
|
49
|
+
);
|
|
50
|
+
}
|
|
8
51
|
|
|
9
52
|
interface HandleIntelligenceRunParams {
|
|
10
53
|
runtime: CopilotIntelligenceRuntimeLike;
|
|
@@ -66,20 +109,23 @@ export async function handleIntelligenceRun({
|
|
|
66
109
|
);
|
|
67
110
|
}
|
|
68
111
|
|
|
69
|
-
let
|
|
112
|
+
let canonicalThreadId = input.threadId;
|
|
113
|
+
let canonicalRunId = input.runId;
|
|
70
114
|
let joinToken: string | undefined;
|
|
71
115
|
try {
|
|
72
116
|
const lockResult = await runtime.intelligence.ɵacquireThreadLock({
|
|
73
117
|
threadId: input.threadId,
|
|
74
118
|
runId: input.runId,
|
|
75
119
|
userId,
|
|
120
|
+
agentId,
|
|
76
121
|
...(runtime.lockKeyPrefix !== undefined
|
|
77
122
|
? { lockKeyPrefix: runtime.lockKeyPrefix }
|
|
78
123
|
: {}),
|
|
79
124
|
ttlSeconds: runtime.lockTtlSeconds,
|
|
80
125
|
});
|
|
126
|
+
canonicalThreadId = lockResult.threadId;
|
|
127
|
+
canonicalRunId = lockResult.runId;
|
|
81
128
|
joinToken = lockResult.joinToken;
|
|
82
|
-
joinCode = lockResult.joinCode;
|
|
83
129
|
} catch (error) {
|
|
84
130
|
logger.error("Thread lock denied:", error);
|
|
85
131
|
return Response.json(
|
|
@@ -90,21 +136,42 @@ export async function handleIntelligenceRun({
|
|
|
90
136
|
);
|
|
91
137
|
}
|
|
92
138
|
|
|
93
|
-
|
|
139
|
+
const cleanupLock = (reason: string): Promise<void> =>
|
|
140
|
+
runtime.intelligence
|
|
141
|
+
.ɵcleanupThreadLock({
|
|
142
|
+
threadId: canonicalThreadId || input.threadId,
|
|
143
|
+
runId: canonicalRunId || input.runId,
|
|
144
|
+
})
|
|
145
|
+
.catch((cleanupError) => {
|
|
146
|
+
logger.error(
|
|
147
|
+
{ err: cleanupError, reason },
|
|
148
|
+
"Failed to cleanup thread lock",
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if (!canonicalThreadId || !canonicalRunId || !joinToken) {
|
|
153
|
+
await cleanupLock("malformed-lock-response");
|
|
94
154
|
return Response.json(
|
|
95
155
|
{
|
|
96
|
-
error: "
|
|
97
|
-
message:
|
|
156
|
+
error: "Run connection credentials not available",
|
|
157
|
+
message:
|
|
158
|
+
"Intelligence platform did not return canonical threadId, runId, and joinToken",
|
|
98
159
|
},
|
|
99
160
|
{ status: 502 },
|
|
100
161
|
);
|
|
101
162
|
}
|
|
102
163
|
|
|
164
|
+
const canonicalInput: RunAgentInput = {
|
|
165
|
+
...input,
|
|
166
|
+
threadId: canonicalThreadId,
|
|
167
|
+
runId: canonicalRunId,
|
|
168
|
+
};
|
|
169
|
+
|
|
103
170
|
let persistedInputMessages: Message[] | undefined;
|
|
104
171
|
if (Array.isArray(input.messages)) {
|
|
105
172
|
try {
|
|
106
173
|
const history = await runtime.intelligence.getThreadMessages({
|
|
107
|
-
threadId:
|
|
174
|
+
threadId: canonicalThreadId,
|
|
108
175
|
});
|
|
109
176
|
const historicMessageIds = new Set(
|
|
110
177
|
history.messages.map((message) => message.id),
|
|
@@ -114,6 +181,7 @@ export async function handleIntelligenceRun({
|
|
|
114
181
|
);
|
|
115
182
|
} catch (error) {
|
|
116
183
|
logger.error("Thread history lookup failed:", error);
|
|
184
|
+
await cleanupLock("thread-history-lookup-failed");
|
|
117
185
|
return Response.json(
|
|
118
186
|
{
|
|
119
187
|
error: "Thread history lookup failed",
|
|
@@ -130,8 +198,8 @@ export async function handleIntelligenceRun({
|
|
|
130
198
|
heartbeatTimer = setInterval(() => {
|
|
131
199
|
runtime.intelligence
|
|
132
200
|
.ɵrenewThreadLock({
|
|
133
|
-
threadId:
|
|
134
|
-
runId:
|
|
201
|
+
threadId: canonicalThreadId,
|
|
202
|
+
runId: canonicalRunId,
|
|
135
203
|
ttlSeconds: runtime.lockTtlSeconds,
|
|
136
204
|
...(runtime.lockKeyPrefix !== undefined
|
|
137
205
|
? { lockKeyPrefix: runtime.lockKeyPrefix }
|
|
@@ -139,6 +207,15 @@ export async function handleIntelligenceRun({
|
|
|
139
207
|
})
|
|
140
208
|
.catch((err) => {
|
|
141
209
|
logger.error("Failed to renew thread lock:", err);
|
|
210
|
+
clearHeartbeat();
|
|
211
|
+
try {
|
|
212
|
+
agent.abortRun();
|
|
213
|
+
} catch (abortError) {
|
|
214
|
+
logger.error(
|
|
215
|
+
"Failed to abort agent after lock renewal failure:",
|
|
216
|
+
abortError,
|
|
217
|
+
);
|
|
218
|
+
}
|
|
142
219
|
});
|
|
143
220
|
}, runtime.lockHeartbeatIntervalSeconds * 1_000);
|
|
144
221
|
|
|
@@ -149,19 +226,48 @@ export async function handleIntelligenceRun({
|
|
|
149
226
|
}
|
|
150
227
|
};
|
|
151
228
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
229
|
+
const runStarted = { current: false };
|
|
230
|
+
let immediateStartupErrorMessage: string | undefined;
|
|
231
|
+
let immediateStartupCleanup: Promise<void> | undefined;
|
|
232
|
+
|
|
233
|
+
const runRequest: AgentRunnerRunRequest = {
|
|
234
|
+
threadId: canonicalThreadId,
|
|
235
|
+
agent,
|
|
236
|
+
input: canonicalInput,
|
|
237
|
+
...(persistedInputMessages !== undefined ? { persistedInputMessages } : {}),
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
try {
|
|
241
|
+
const runStart = hasRunnerStartupBoundary(runtime.runner)
|
|
242
|
+
? runtime.runner.runWithStartupBoundary(runRequest)
|
|
243
|
+
: {
|
|
244
|
+
events: runtime.runner.run(runRequest),
|
|
245
|
+
startup: Promise.resolve(),
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
runStart.events.subscribe({
|
|
249
|
+
next: (event: BaseEvent) => {
|
|
250
|
+
if (event.type === EventType.RUN_STARTED) {
|
|
251
|
+
runStarted.current = true;
|
|
252
|
+
}
|
|
253
|
+
if (event.type === EventType.RUN_ERROR && !runStarted.current) {
|
|
254
|
+
clearHeartbeat();
|
|
255
|
+
immediateStartupErrorMessage =
|
|
256
|
+
"message" in event && typeof event.message === "string"
|
|
257
|
+
? event.message
|
|
258
|
+
: "Runner failed before the run started";
|
|
259
|
+
immediateStartupCleanup = cleanupLock("runner-start-failed");
|
|
260
|
+
}
|
|
261
|
+
},
|
|
163
262
|
error: (error) => {
|
|
164
263
|
clearHeartbeat();
|
|
264
|
+
if (!runStarted.current) {
|
|
265
|
+
immediateStartupErrorMessage =
|
|
266
|
+
error instanceof Error ? error.message : String(error);
|
|
267
|
+
immediateStartupCleanup = cleanupLock("runner-start-error");
|
|
268
|
+
} else {
|
|
269
|
+
cleanupLock("runner-error");
|
|
270
|
+
}
|
|
165
271
|
telemetry.capture("oss.runtime.agent_execution_stream_errored", {
|
|
166
272
|
error: error instanceof Error ? error.message : String(error),
|
|
167
273
|
});
|
|
@@ -173,8 +279,43 @@ export async function handleIntelligenceRun({
|
|
|
173
279
|
},
|
|
174
280
|
});
|
|
175
281
|
|
|
282
|
+
await runStart.startup;
|
|
283
|
+
} catch (error) {
|
|
284
|
+
clearHeartbeat();
|
|
285
|
+
await (immediateStartupCleanup ?? cleanupLock("runner-start-threw"));
|
|
286
|
+
logger.error("Error starting agent runner:", error);
|
|
287
|
+
return Response.json(
|
|
288
|
+
{
|
|
289
|
+
error: "Failed to start runner",
|
|
290
|
+
message: error instanceof Error ? error.message : String(error),
|
|
291
|
+
},
|
|
292
|
+
{ status: 502 },
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (immediateStartupErrorMessage) {
|
|
297
|
+
await immediateStartupCleanup;
|
|
298
|
+
return Response.json(
|
|
299
|
+
{
|
|
300
|
+
error: "Failed to start runner",
|
|
301
|
+
message: immediateStartupErrorMessage,
|
|
302
|
+
},
|
|
303
|
+
{ status: 502 },
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// IntelligenceAgentRunner resolves this boundary after Phoenix channel join.
|
|
308
|
+
// Other runner implementations fall back to construction/subscription errors.
|
|
176
309
|
return Response.json(
|
|
177
|
-
{
|
|
310
|
+
{
|
|
311
|
+
threadId: canonicalThreadId,
|
|
312
|
+
runId: canonicalRunId,
|
|
313
|
+
joinToken,
|
|
314
|
+
realtime: buildRealtimeConnectionInfo({
|
|
315
|
+
clientUrl: runtime.intelligence.ɵgetClientWsUrl(),
|
|
316
|
+
threadId: canonicalThreadId,
|
|
317
|
+
}),
|
|
318
|
+
},
|
|
178
319
|
{
|
|
179
320
|
headers: { "Cache-Control": "no-cache" },
|
|
180
321
|
},
|
|
@@ -1,7 +1,27 @@
|
|
|
1
1
|
import { PlatformRequestError } from "../../intelligence-platform/client";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Returns the HTTP status carried by platform request errors.
|
|
5
|
+
*/
|
|
6
|
+
export function getPlatformErrorStatus(error: unknown): number | undefined {
|
|
7
|
+
if (error instanceof PlatformRequestError) {
|
|
8
|
+
return error.status;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (
|
|
12
|
+
error !== null &&
|
|
13
|
+
typeof error === "object" &&
|
|
14
|
+
"status" in error &&
|
|
15
|
+
typeof error.status === "number"
|
|
16
|
+
) {
|
|
17
|
+
return error.status;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
3
23
|
export function isPlatformNotFoundError(error: unknown): boolean {
|
|
4
|
-
return error
|
|
24
|
+
return getPlatformErrorStatus(error) === 404;
|
|
5
25
|
}
|
|
6
26
|
|
|
7
27
|
const MAX_ID_LENGTH = 128;
|
|
@@ -385,24 +385,34 @@ describe("CopilotKitIntelligence", () => {
|
|
|
385
385
|
});
|
|
386
386
|
|
|
387
387
|
describe("acquireThreadLock", () => {
|
|
388
|
-
it("sends POST to lock endpoint and returns
|
|
388
|
+
it("sends POST to lock endpoint and returns canonical run credentials", async () => {
|
|
389
389
|
fetchMock.mockReturnValue(
|
|
390
|
-
jsonResponse({
|
|
390
|
+
jsonResponse({
|
|
391
|
+
threadId: "t-1",
|
|
392
|
+
runId: "r-1",
|
|
393
|
+
joinToken: "jt-lock",
|
|
394
|
+
}),
|
|
391
395
|
);
|
|
392
396
|
|
|
393
397
|
const result = await client.ɵacquireThreadLock({
|
|
394
398
|
threadId: "t-1",
|
|
395
399
|
runId: "r-1",
|
|
396
400
|
userId: "user-1",
|
|
401
|
+
agentId: "agent-1",
|
|
397
402
|
});
|
|
398
403
|
|
|
399
|
-
expect(result).toEqual({
|
|
404
|
+
expect(result).toEqual({
|
|
405
|
+
threadId: "t-1",
|
|
406
|
+
runId: "r-1",
|
|
407
|
+
joinToken: "jt-lock",
|
|
408
|
+
});
|
|
400
409
|
const [url, opts] = fetchMock.mock.calls[0];
|
|
401
410
|
expect(url).toBe("https://api.example.com/api/threads/t-1/lock");
|
|
402
411
|
expect(opts.method).toBe("POST");
|
|
403
412
|
expect(JSON.parse(opts.body)).toEqual({
|
|
404
413
|
runId: "r-1",
|
|
405
414
|
userId: "user-1",
|
|
415
|
+
agentId: "agent-1",
|
|
406
416
|
});
|
|
407
417
|
});
|
|
408
418
|
|
|
@@ -413,9 +423,24 @@ describe("CopilotKitIntelligence", () => {
|
|
|
413
423
|
threadId: "t-1",
|
|
414
424
|
runId: "r-1",
|
|
415
425
|
userId: "user-1",
|
|
426
|
+
agentId: "agent-1",
|
|
416
427
|
}),
|
|
417
428
|
).rejects.toThrow(/409/);
|
|
418
429
|
});
|
|
430
|
+
|
|
431
|
+
it("sends compare-delete cleanup to the lock endpoint", async () => {
|
|
432
|
+
fetchMock.mockReturnValue(emptyResponse());
|
|
433
|
+
|
|
434
|
+
await client.ɵcleanupThreadLock({
|
|
435
|
+
threadId: "t-1",
|
|
436
|
+
runId: "r-1",
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
const [url, opts] = fetchMock.mock.calls[0];
|
|
440
|
+
expect(url).toBe("https://api.example.com/api/threads/t-1/lock");
|
|
441
|
+
expect(opts.method).toBe("DELETE");
|
|
442
|
+
expect(JSON.parse(opts.body)).toEqual({ runId: "r-1" });
|
|
443
|
+
});
|
|
419
444
|
});
|
|
420
445
|
|
|
421
446
|
describe("getActiveJoinCode", () => {
|
|
@@ -544,8 +569,7 @@ describe("CopilotKitIntelligence", () => {
|
|
|
544
569
|
const result = await client.ɵconnectThread({
|
|
545
570
|
threadId: "t-1",
|
|
546
571
|
userId: "user-1",
|
|
547
|
-
|
|
548
|
-
lastSeenEventId: "event-1",
|
|
572
|
+
agentId: "agent-1",
|
|
549
573
|
});
|
|
550
574
|
|
|
551
575
|
expect(result).toBeNull();
|
|
@@ -554,51 +578,21 @@ describe("CopilotKitIntelligence", () => {
|
|
|
554
578
|
expect(opts.method).toBe("POST");
|
|
555
579
|
expect(JSON.parse(opts.body)).toEqual({
|
|
556
580
|
userId: "user-1",
|
|
557
|
-
|
|
558
|
-
lastSeenEventId: "event-1",
|
|
581
|
+
agentId: "agent-1",
|
|
559
582
|
});
|
|
560
583
|
});
|
|
561
584
|
|
|
562
|
-
it("returns
|
|
585
|
+
it("returns credentials-only connect response", async () => {
|
|
563
586
|
const payload = {
|
|
564
|
-
mode: "bootstrap",
|
|
565
|
-
latestEventId: "event-2",
|
|
566
|
-
events: [
|
|
567
|
-
{
|
|
568
|
-
type: "RUN_STARTED",
|
|
569
|
-
threadId: "t-1",
|
|
570
|
-
run_id: "backend-run-1",
|
|
571
|
-
input: { messages: [] },
|
|
572
|
-
},
|
|
573
|
-
{ type: "RUN_FINISHED" },
|
|
574
|
-
],
|
|
575
|
-
};
|
|
576
|
-
fetchMock.mockReturnValue(jsonResponse(payload));
|
|
577
|
-
|
|
578
|
-
const result = await client.ɵconnectThread({
|
|
579
587
|
threadId: "t-1",
|
|
580
|
-
|
|
581
|
-
runId: "run-1",
|
|
582
|
-
lastSeenEventId: "event-1",
|
|
583
|
-
});
|
|
584
|
-
|
|
585
|
-
expect(result).toEqual(payload);
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
it("returns a live connect plan", async () => {
|
|
589
|
-
const payload = {
|
|
590
|
-
mode: "live",
|
|
591
|
-
joinToken: "jt-live",
|
|
592
|
-
joinFromEventId: "event-2",
|
|
593
|
-
events: [],
|
|
588
|
+
joinToken: "jt-connect",
|
|
594
589
|
};
|
|
595
590
|
fetchMock.mockReturnValue(jsonResponse(payload));
|
|
596
591
|
|
|
597
592
|
const result = await client.ɵconnectThread({
|
|
598
593
|
threadId: "t-1",
|
|
599
594
|
userId: "user-1",
|
|
600
|
-
|
|
601
|
-
lastSeenEventId: "event-2",
|
|
595
|
+
agentId: "agent-1",
|
|
602
596
|
});
|
|
603
597
|
|
|
604
598
|
expect(result).toEqual(payload);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { logger } from "@copilotkit/shared";
|
|
2
|
-
import type { BaseEvent } from "@ag-ui/client";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Error thrown when an Intelligence platform HTTP request returns a non-2xx
|
|
@@ -150,10 +149,12 @@ export interface CreateThreadRequest {
|
|
|
150
149
|
|
|
151
150
|
/** Credentials returned when locking or joining a thread's realtime channel. */
|
|
152
151
|
export interface ThreadConnectionResponse {
|
|
152
|
+
/** Canonical platform thread identifier for the run or connection. */
|
|
153
|
+
threadId: string;
|
|
154
|
+
/** Canonical platform run identifier for an active run lock. */
|
|
155
|
+
runId?: string;
|
|
153
156
|
/** Short-lived token for authenticating the Phoenix channel join. */
|
|
154
157
|
joinToken: string;
|
|
155
|
-
/** Optional join code that can be shared with other clients to join the same channel. */
|
|
156
|
-
joinCode?: string;
|
|
157
158
|
/** Lock metadata echoed back by the platform. */
|
|
158
159
|
lock?: ThreadLockInfo;
|
|
159
160
|
}
|
|
@@ -166,24 +167,13 @@ export interface SubscribeToThreadsResponse {
|
|
|
166
167
|
joinToken: string;
|
|
167
168
|
}
|
|
168
169
|
|
|
169
|
-
export
|
|
170
|
-
mode: "bootstrap";
|
|
171
|
-
latestEventId: string | null;
|
|
172
|
-
events: BaseEvent[];
|
|
173
|
-
}
|
|
170
|
+
export type ConnectThreadResponse = ThreadConnectionResponse | null;
|
|
174
171
|
|
|
175
|
-
export interface
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
joinFromEventId: string | null;
|
|
179
|
-
events: BaseEvent[];
|
|
172
|
+
export interface AcquireThreadLockResponse extends ThreadConnectionResponse {
|
|
173
|
+
/** Canonical platform run identifier for the acquired lock. */
|
|
174
|
+
runId: string;
|
|
180
175
|
}
|
|
181
176
|
|
|
182
|
-
export type ConnectThreadResponse =
|
|
183
|
-
| ConnectThreadBootstrapResponse
|
|
184
|
-
| ConnectThreadLiveResponse
|
|
185
|
-
| null;
|
|
186
|
-
|
|
187
177
|
/** A single message within a thread's persisted history. */
|
|
188
178
|
export interface ThreadMessage {
|
|
189
179
|
/** Unique identifier for this message. */
|
|
@@ -212,6 +202,7 @@ export interface AcquireThreadLockRequest {
|
|
|
212
202
|
threadId: string;
|
|
213
203
|
runId: string;
|
|
214
204
|
userId: string;
|
|
205
|
+
agentId: string;
|
|
215
206
|
/** Custom Redis key prefix for the lock (default: "thread"). */
|
|
216
207
|
lockKeyPrefix?: string;
|
|
217
208
|
/** Lock TTL in seconds. When set, the lock auto-expires after this duration. */
|
|
@@ -227,6 +218,11 @@ export interface RenewThreadLockRequest {
|
|
|
227
218
|
lockKeyPrefix?: string;
|
|
228
219
|
}
|
|
229
220
|
|
|
221
|
+
export interface CleanupThreadLockRequest {
|
|
222
|
+
threadId: string;
|
|
223
|
+
runId: string;
|
|
224
|
+
}
|
|
225
|
+
|
|
230
226
|
export interface RenewThreadLockResponse {
|
|
231
227
|
ttlSeconds: number;
|
|
232
228
|
}
|
|
@@ -606,13 +602,14 @@ export class CopilotKitIntelligence {
|
|
|
606
602
|
|
|
607
603
|
async ɵacquireThreadLock(
|
|
608
604
|
params: AcquireThreadLockRequest,
|
|
609
|
-
): Promise<
|
|
610
|
-
return this.#request<
|
|
605
|
+
): Promise<AcquireThreadLockResponse> {
|
|
606
|
+
return this.#request<AcquireThreadLockResponse>(
|
|
611
607
|
"POST",
|
|
612
608
|
`/api/threads/${encodeURIComponent(params.threadId)}/lock`,
|
|
613
609
|
{
|
|
614
610
|
runId: params.runId,
|
|
615
611
|
userId: params.userId,
|
|
612
|
+
agentId: params.agentId,
|
|
616
613
|
...(params.lockKeyPrefix !== undefined
|
|
617
614
|
? { lockKeyPrefix: params.lockKeyPrefix }
|
|
618
615
|
: {}),
|
|
@@ -623,6 +620,16 @@ export class CopilotKitIntelligence {
|
|
|
623
620
|
);
|
|
624
621
|
}
|
|
625
622
|
|
|
623
|
+
async ɵcleanupThreadLock(params: CleanupThreadLockRequest): Promise<void> {
|
|
624
|
+
return this.#request<void>(
|
|
625
|
+
"DELETE",
|
|
626
|
+
`/api/threads/${encodeURIComponent(params.threadId)}/lock`,
|
|
627
|
+
{
|
|
628
|
+
runId: params.runId,
|
|
629
|
+
},
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
|
|
626
633
|
async ɵrenewThreadLock(
|
|
627
634
|
params: RenewThreadLockRequest,
|
|
628
635
|
): Promise<RenewThreadLockResponse> {
|
|
@@ -653,18 +660,16 @@ export class CopilotKitIntelligence {
|
|
|
653
660
|
async ɵconnectThread(params: {
|
|
654
661
|
threadId: string;
|
|
655
662
|
userId: string;
|
|
656
|
-
|
|
657
|
-
lastSeenEventId?: string | null;
|
|
663
|
+
agentId: string;
|
|
658
664
|
}): Promise<ConnectThreadResponse> {
|
|
659
|
-
const result = await this.#request<
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
});
|
|
665
|
+
const result = await this.#request<ThreadConnectionResponse>(
|
|
666
|
+
"POST",
|
|
667
|
+
`/api/threads/${encodeURIComponent(params.threadId)}/connect`,
|
|
668
|
+
{
|
|
669
|
+
userId: params.userId,
|
|
670
|
+
agentId: params.agentId,
|
|
671
|
+
},
|
|
672
|
+
);
|
|
668
673
|
|
|
669
674
|
// request() returns undefined for empty/204 responses
|
|
670
675
|
return result ?? null;
|