@agent-native/core 0.56.1 → 0.58.0

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 (50) hide show
  1. package/README.md +9 -7
  2. package/dist/cli/plan-local.d.ts.map +1 -1
  3. package/dist/cli/plan-local.js +66 -10
  4. package/dist/cli/plan-local.js.map +1 -1
  5. package/dist/cli/skills.d.ts +2 -2
  6. package/dist/cli/skills.d.ts.map +1 -1
  7. package/dist/cli/skills.js +13 -5
  8. package/dist/cli/skills.js.map +1 -1
  9. package/dist/client/AssistantChat.d.ts +8 -0
  10. package/dist/client/AssistantChat.d.ts.map +1 -1
  11. package/dist/client/AssistantChat.js +24 -4
  12. package/dist/client/AssistantChat.js.map +1 -1
  13. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  14. package/dist/client/agent-chat-adapter.js +39 -4
  15. package/dist/client/agent-chat-adapter.js.map +1 -1
  16. package/dist/client/chat/connectors.d.ts +19 -0
  17. package/dist/client/chat/connectors.d.ts.map +1 -0
  18. package/dist/client/chat/connectors.js +992 -0
  19. package/dist/client/chat/connectors.js.map +1 -0
  20. package/dist/client/chat/index.d.ts +2 -1
  21. package/dist/client/chat/index.d.ts.map +1 -1
  22. package/dist/client/chat/index.js +2 -0
  23. package/dist/client/chat/index.js.map +1 -1
  24. package/dist/client/chat/runtime.d.ts +93 -0
  25. package/dist/client/chat/runtime.d.ts.map +1 -1
  26. package/dist/client/chat/runtime.js +934 -1
  27. package/dist/client/chat/runtime.js.map +1 -1
  28. package/dist/client/index.d.ts +2 -1
  29. package/dist/client/index.d.ts.map +1 -1
  30. package/dist/client/index.js +2 -0
  31. package/dist/client/index.js.map +1 -1
  32. package/dist/mcp/build-server.d.ts.map +1 -1
  33. package/dist/mcp/build-server.js +48 -3
  34. package/dist/mcp/build-server.js.map +1 -1
  35. package/docs/content/actions.md +5 -1
  36. package/docs/content/agent-surfaces.md +273 -0
  37. package/docs/content/components.md +46 -17
  38. package/docs/content/drop-in-agent.md +10 -5
  39. package/docs/content/embedding-sdk.md +4 -0
  40. package/docs/content/external-agents.md +1 -0
  41. package/docs/content/getting-started.md +2 -0
  42. package/docs/content/harness-agents.md +232 -0
  43. package/docs/content/key-concepts.md +24 -22
  44. package/docs/content/mcp-apps.md +1 -1
  45. package/docs/content/native-chat-ui.md +101 -22
  46. package/docs/content/plan-plugin.md +27 -1
  47. package/docs/content/pure-agent-apps.md +2 -1
  48. package/docs/content/using-your-agent.md +1 -0
  49. package/docs/content/what-is-agent-native.md +3 -2
  50. package/package.json +1 -1
@@ -1,2 +1,935 @@
1
- export {};
1
+ import { agentNativePath } from "../api-path.js";
2
+ import { settleInterruptedToolCalls, } from "../sse-event-processor.js";
3
+ const DEFAULT_RUNTIME_CAPABILITIES = {
4
+ messages: {
5
+ streaming: true,
6
+ history: true,
7
+ structuredContent: true,
8
+ attachments: true,
9
+ },
10
+ tools: {
11
+ events: true,
12
+ hostTools: true,
13
+ resultStreaming: true,
14
+ mcpApps: true,
15
+ },
16
+ sessions: {
17
+ create: true,
18
+ restore: true,
19
+ persistent: true,
20
+ },
21
+ cancellation: {
22
+ abortSignal: true,
23
+ explicitCancel: true,
24
+ },
25
+ models: {
26
+ selectable: true,
27
+ reasoningEffort: true,
28
+ },
29
+ artifacts: {
30
+ files: true,
31
+ links: true,
32
+ progress: true,
33
+ },
34
+ };
35
+ function mergeCapabilities(overrides) {
36
+ return {
37
+ ...DEFAULT_RUNTIME_CAPABILITIES,
38
+ ...overrides,
39
+ messages: {
40
+ ...DEFAULT_RUNTIME_CAPABILITIES.messages,
41
+ ...overrides?.messages,
42
+ },
43
+ tools: {
44
+ ...DEFAULT_RUNTIME_CAPABILITIES.tools,
45
+ ...overrides?.tools,
46
+ events: overrides?.tools?.events ?? DEFAULT_RUNTIME_CAPABILITIES.tools.events,
47
+ },
48
+ sessions: {
49
+ ...DEFAULT_RUNTIME_CAPABILITIES.sessions,
50
+ ...overrides?.sessions,
51
+ create: overrides?.sessions?.create ??
52
+ DEFAULT_RUNTIME_CAPABILITIES.sessions.create,
53
+ },
54
+ cancellation: {
55
+ ...DEFAULT_RUNTIME_CAPABILITIES.cancellation,
56
+ ...overrides?.cancellation,
57
+ },
58
+ models: { ...DEFAULT_RUNTIME_CAPABILITIES.models, ...overrides?.models },
59
+ artifacts: {
60
+ ...DEFAULT_RUNTIME_CAPABILITIES.artifacts,
61
+ ...overrides?.artifacts,
62
+ },
63
+ };
64
+ }
65
+ function createRuntimeId(prefix) {
66
+ if (typeof crypto !== "undefined" &&
67
+ typeof crypto.randomUUID === "function") {
68
+ return `${prefix}-${crypto.randomUUID()}`;
69
+ }
70
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
71
+ }
72
+ function createAbortController(signal) {
73
+ const controller = new AbortController();
74
+ if (!signal)
75
+ return { controller, cleanup: () => { } };
76
+ if (signal.aborted)
77
+ controller.abort();
78
+ const abort = () => controller.abort();
79
+ signal.addEventListener("abort", abort, { once: true });
80
+ return {
81
+ controller,
82
+ cleanup: () => signal.removeEventListener("abort", abort),
83
+ };
84
+ }
85
+ async function resolveHeaders(headers, input) {
86
+ const resolved = typeof headers === "function" ? await headers(input) : headers;
87
+ return new Headers(resolved);
88
+ }
89
+ function normalizeEndpoint(value) {
90
+ return typeof value === "string" ? value : value.toString();
91
+ }
92
+ function isRuntimeEvent(value) {
93
+ return (!!value &&
94
+ typeof value === "object" &&
95
+ typeof value.type === "string");
96
+ }
97
+ function parseJsonEvent(raw) {
98
+ const trimmed = raw.trim();
99
+ if (!trimmed || trimmed === "[DONE]")
100
+ return null;
101
+ try {
102
+ return JSON.parse(trimmed);
103
+ }
104
+ catch {
105
+ return null;
106
+ }
107
+ }
108
+ async function* readJsonEventStream(body) {
109
+ const reader = body.getReader();
110
+ const decoder = new TextDecoder();
111
+ let buffer = "";
112
+ let pendingSseData = [];
113
+ const flushSseData = function* () {
114
+ if (pendingSseData.length === 0)
115
+ return;
116
+ const parsed = parseJsonEvent(pendingSseData.join("\n"));
117
+ pendingSseData = [];
118
+ if (parsed)
119
+ yield parsed;
120
+ };
121
+ try {
122
+ while (true) {
123
+ const { done, value } = await reader.read();
124
+ if (done)
125
+ break;
126
+ buffer += decoder.decode(value, { stream: true });
127
+ const lines = buffer.split(/\r?\n/);
128
+ buffer = lines.pop() ?? "";
129
+ for (const line of lines) {
130
+ if (line.startsWith("data:")) {
131
+ pendingSseData.push(line.slice(5).trimStart());
132
+ continue;
133
+ }
134
+ if (line.trim() === "") {
135
+ yield* flushSseData();
136
+ continue;
137
+ }
138
+ const parsed = parseJsonEvent(line);
139
+ if (parsed)
140
+ yield parsed;
141
+ }
142
+ }
143
+ if (buffer.trim()) {
144
+ const parsed = parseJsonEvent(buffer);
145
+ if (parsed)
146
+ yield parsed;
147
+ }
148
+ yield* flushSseData();
149
+ }
150
+ finally {
151
+ try {
152
+ reader.releaseLock();
153
+ }
154
+ catch {
155
+ // Some browser runtimes consider a cancelled stream locked for a tick.
156
+ }
157
+ }
158
+ }
159
+ function textResponseEvents(text, sessionId, turnId) {
160
+ const message = {
161
+ id: createRuntimeId("message"),
162
+ role: "assistant",
163
+ content: [],
164
+ };
165
+ return [
166
+ { type: "message-start", sessionId, turnId, message },
167
+ ...(text
168
+ ? [
169
+ {
170
+ type: "message-delta",
171
+ sessionId,
172
+ turnId,
173
+ messageId: message.id,
174
+ delta: { type: "text", text },
175
+ },
176
+ ]
177
+ : []),
178
+ { type: "message-done", sessionId, turnId, message },
179
+ { type: "done", sessionId, turnId, reason: "complete" },
180
+ ];
181
+ }
182
+ async function* eventsFromJsonResponse(value, sessionId, turnId) {
183
+ if (Array.isArray(value)) {
184
+ for (const item of value) {
185
+ if (isRuntimeEvent(item))
186
+ yield item;
187
+ }
188
+ return;
189
+ }
190
+ if (!value || typeof value !== "object")
191
+ return;
192
+ const record = value;
193
+ if (Array.isArray(record.events)) {
194
+ for (const item of record.events) {
195
+ if (isRuntimeEvent(item))
196
+ yield item;
197
+ }
198
+ return;
199
+ }
200
+ const text = typeof record.message === "string"
201
+ ? record.message
202
+ : typeof record.text === "string"
203
+ ? record.text
204
+ : "";
205
+ if (text) {
206
+ for (const event of textResponseEvents(text, sessionId, turnId)) {
207
+ yield event;
208
+ }
209
+ }
210
+ }
211
+ function defaultMapHttpRuntimeEvent(event, _context) {
212
+ if (!isRuntimeEvent(event))
213
+ return null;
214
+ return event;
215
+ }
216
+ function normalizeMappedEvents(mapped) {
217
+ if (!mapped)
218
+ return [];
219
+ return Array.isArray(mapped)
220
+ ? mapped
221
+ : [mapped];
222
+ }
223
+ async function* streamResponseEvents(response, input) {
224
+ const context = {
225
+ sessionId: input.sessionId,
226
+ turnId: input.turnId,
227
+ runId: input.runId,
228
+ };
229
+ const contentType = response.headers.get("content-type") ?? "";
230
+ if (response.body && !contentType.includes("application/json")) {
231
+ for await (const raw of readJsonEventStream(response.body)) {
232
+ for (const event of normalizeMappedEvents(input.mapEvent(raw, context))) {
233
+ yield event;
234
+ }
235
+ }
236
+ return;
237
+ }
238
+ const json = await response.json().catch(() => null);
239
+ for await (const event of eventsFromJsonResponse(json, input.sessionId, input.turnId)) {
240
+ for (const mapped of normalizeMappedEvents(input.mapEvent(event, context))) {
241
+ yield mapped;
242
+ }
243
+ }
244
+ }
245
+ function defaultHttpRuntimeRequest(input) {
246
+ return {
247
+ sessionId: input.session.id,
248
+ threadId: input.session.threadId,
249
+ turnId: input.turnId,
250
+ prompt: input.turn.prompt,
251
+ messages: input.turn.messages,
252
+ attachments: input.turn.attachments,
253
+ tools: input.turn.tools,
254
+ model: input.turn.model,
255
+ reasoningEffort: input.turn.reasoningEffort,
256
+ temperature: input.turn.temperature,
257
+ providerOptions: input.turn.providerOptions,
258
+ metadata: input.turn.metadata,
259
+ };
260
+ }
261
+ async function readErrorText(response) {
262
+ const text = await response.text().catch(() => "");
263
+ if (!text)
264
+ return `HTTP ${response.status}`;
265
+ try {
266
+ const parsed = JSON.parse(text);
267
+ if (typeof parsed.error === "string")
268
+ return parsed.error;
269
+ if (typeof parsed.message === "string")
270
+ return parsed.message;
271
+ }
272
+ catch {
273
+ // Keep raw text.
274
+ }
275
+ return text.slice(0, 500);
276
+ }
277
+ export function createHttpAgentChatRuntime(options) {
278
+ const fetchImpl = options.fetch ?? fetch;
279
+ const capabilities = mergeCapabilities(options.capabilities);
280
+ const runtimeId = options.id ?? "external:http";
281
+ const mapEvent = options.mapEvent ??
282
+ defaultMapHttpRuntimeEvent;
283
+ const createSessionObject = (input) => {
284
+ const sessionId = input?.id ?? input?.threadId ?? createRuntimeId("session");
285
+ const summary = {
286
+ id: sessionId,
287
+ runtimeId,
288
+ threadId: input?.threadId,
289
+ title: input?.title,
290
+ status: "idle",
291
+ createdAt: new Date().toISOString(),
292
+ updatedAt: new Date().toISOString(),
293
+ metadata: input?.metadata,
294
+ };
295
+ const startTurn = async (turn) => {
296
+ const turnId = createRuntimeId("turn");
297
+ const { controller, cleanup } = createAbortController(turn.abortSignal);
298
+ const endpoint = typeof options.endpoint === "function"
299
+ ? options.endpoint({ session: summary, turn })
300
+ : options.endpoint;
301
+ const headers = await resolveHeaders(options.headers, {
302
+ sessionId,
303
+ turnId,
304
+ });
305
+ if (!headers.has("Content-Type"))
306
+ headers.set("Content-Type", "application/json");
307
+ const response = await fetchImpl(normalizeEndpoint(endpoint), {
308
+ method: options.method ?? "POST",
309
+ headers,
310
+ credentials: options.credentials,
311
+ body: JSON.stringify(options.mapRequest
312
+ ? options.mapRequest({ session: summary, turn, turnId })
313
+ : defaultHttpRuntimeRequest({ session: summary, turn, turnId })),
314
+ signal: controller.signal,
315
+ });
316
+ if (!response.ok) {
317
+ cleanup();
318
+ throw new Error(await readErrorText(response));
319
+ }
320
+ const runId = response.headers.get("X-Run-Id") ?? undefined;
321
+ const events = (async function* () {
322
+ try {
323
+ yield* streamResponseEvents(response, {
324
+ sessionId,
325
+ turnId,
326
+ runId,
327
+ mapEvent,
328
+ });
329
+ }
330
+ finally {
331
+ cleanup();
332
+ }
333
+ })();
334
+ return {
335
+ id: turnId,
336
+ sessionId,
337
+ runId,
338
+ events,
339
+ cancel: async (cancelInput) => {
340
+ controller.abort();
341
+ if (!options.cancelEndpoint)
342
+ return { status: "cancelled" };
343
+ const endpoint = typeof options.cancelEndpoint === "function"
344
+ ? options.cancelEndpoint({
345
+ ...cancelInput,
346
+ sessionId,
347
+ turnId,
348
+ runId,
349
+ })
350
+ : options.cancelEndpoint;
351
+ if (!endpoint)
352
+ return { status: "unsupported" };
353
+ const cancelHeaders = await resolveHeaders(options.headers, {
354
+ sessionId,
355
+ turnId,
356
+ });
357
+ if (!cancelHeaders.has("Content-Type")) {
358
+ cancelHeaders.set("Content-Type", "application/json");
359
+ }
360
+ const cancelResponse = await fetchImpl(normalizeEndpoint(endpoint), {
361
+ method: "POST",
362
+ headers: cancelHeaders,
363
+ credentials: options.credentials,
364
+ body: JSON.stringify({
365
+ sessionId,
366
+ turnId,
367
+ runId,
368
+ reason: cancelInput?.reason ?? "user",
369
+ metadata: cancelInput?.metadata,
370
+ }),
371
+ signal: cancelInput?.abortSignal,
372
+ });
373
+ return cancelResponse.ok
374
+ ? { status: "cancelled" }
375
+ : {
376
+ status: "unsupported",
377
+ message: await readErrorText(cancelResponse),
378
+ };
379
+ },
380
+ };
381
+ };
382
+ return {
383
+ id: sessionId,
384
+ runtimeId,
385
+ threadId: input?.threadId,
386
+ capabilities,
387
+ sendMessage: startTurn,
388
+ startTurn,
389
+ snapshot: () => ({
390
+ ...summary,
391
+ status: "idle",
392
+ updatedAt: new Date().toISOString(),
393
+ messages: input?.messages,
394
+ resumeState: input?.resumeState,
395
+ }),
396
+ dispose: () => undefined,
397
+ };
398
+ };
399
+ const runtime = {
400
+ id: runtimeId,
401
+ kind: options.kind ?? "external-agent",
402
+ label: options.label ?? "External agent",
403
+ description: options.description,
404
+ capabilities,
405
+ createSession: createSessionObject,
406
+ restoreSession: (snapshot) => createSessionObject({
407
+ id: snapshot.id,
408
+ threadId: snapshot.threadId,
409
+ title: snapshot.title,
410
+ messages: snapshot.messages,
411
+ resumeState: snapshot.resumeState,
412
+ metadata: snapshot.metadata,
413
+ }),
414
+ sendMessage: async (input) => {
415
+ const session = createSessionObject({
416
+ id: input.sessionId,
417
+ threadId: input.sessionId,
418
+ metadata: input.metadata,
419
+ });
420
+ return session.startTurn(input);
421
+ },
422
+ subscribe: async (input) => {
423
+ if (!options.resumeEndpoint)
424
+ return (async function* () { })();
425
+ const endpoint = typeof options.resumeEndpoint === "function"
426
+ ? options.resumeEndpoint(input)
427
+ : options.resumeEndpoint;
428
+ if (!endpoint)
429
+ return (async function* () { })();
430
+ const headers = await resolveHeaders(options.headers, input);
431
+ const response = await fetchImpl(normalizeEndpoint(endpoint), {
432
+ headers,
433
+ credentials: options.credentials,
434
+ signal: input.abortSignal,
435
+ });
436
+ if (!response.ok)
437
+ throw new Error(await readErrorText(response));
438
+ return streamResponseEvents(response, {
439
+ sessionId: input.sessionId ?? "session",
440
+ turnId: input.turnId,
441
+ runId: input.runId,
442
+ mapEvent,
443
+ });
444
+ },
445
+ resume: async (input) => {
446
+ const sessionId = input.sessionId ?? createRuntimeId("session");
447
+ const events = runtime.subscribe
448
+ ? await runtime.subscribe(input)
449
+ : (async function* () { })();
450
+ return {
451
+ id: input.turnId,
452
+ sessionId,
453
+ runId: input.runId,
454
+ events,
455
+ };
456
+ },
457
+ cancel: async (input) => {
458
+ if (!options.cancelEndpoint)
459
+ return { status: "unsupported" };
460
+ const endpoint = typeof options.cancelEndpoint === "function"
461
+ ? options.cancelEndpoint(input)
462
+ : options.cancelEndpoint;
463
+ if (!endpoint)
464
+ return { status: "unsupported" };
465
+ const headers = await resolveHeaders(options.headers, input);
466
+ if (!headers.has("Content-Type"))
467
+ headers.set("Content-Type", "application/json");
468
+ const response = await fetchImpl(normalizeEndpoint(endpoint), {
469
+ method: "POST",
470
+ headers,
471
+ credentials: options.credentials,
472
+ body: JSON.stringify(input),
473
+ signal: input.abortSignal,
474
+ });
475
+ return response.ok
476
+ ? { status: "cancelled" }
477
+ : { status: "unsupported", message: await readErrorText(response) };
478
+ },
479
+ };
480
+ return runtime;
481
+ }
482
+ function runtimeMessageText(message) {
483
+ return message.content
484
+ .map((part) => part.type === "text" || part.type === "reasoning" ? part.text : "")
485
+ .filter(Boolean)
486
+ .join("\n");
487
+ }
488
+ function nativeHistoryFromMessages(messages, currentPrompt) {
489
+ const history = (messages ?? [])
490
+ .filter((message) => message.role === "user" || message.role === "assistant")
491
+ .map((message) => ({
492
+ role: message.role,
493
+ content: runtimeMessageText(message),
494
+ }))
495
+ .filter((message) => message.content.trim());
496
+ if (!currentPrompt.trim())
497
+ return history;
498
+ const last = history[history.length - 1];
499
+ return last?.role === "user" && last.content === currentPrompt
500
+ ? history.slice(0, -1)
501
+ : history;
502
+ }
503
+ function mapAgentNativeEvent(raw, input) {
504
+ if (!raw || typeof raw !== "object")
505
+ return [];
506
+ const ev = raw;
507
+ const base = {
508
+ sessionId: input.sessionId,
509
+ turnId: input.turnId,
510
+ };
511
+ if (ev.seq !== undefined) {
512
+ base.metadata = {
513
+ seq: ev.seq,
514
+ };
515
+ }
516
+ if (ev.type === "text") {
517
+ const text = ev.text ?? "";
518
+ const events = [];
519
+ if (!input.text.started) {
520
+ input.text.started = true;
521
+ events.push({
522
+ type: "message-start",
523
+ ...base,
524
+ message: {
525
+ id: input.messageId,
526
+ role: "assistant",
527
+ content: [],
528
+ },
529
+ });
530
+ }
531
+ input.text.value += text;
532
+ events.push({
533
+ type: "message-delta",
534
+ ...base,
535
+ messageId: input.messageId,
536
+ delta: { type: "text", text },
537
+ });
538
+ return events;
539
+ }
540
+ if (ev.type === "activity") {
541
+ return [
542
+ {
543
+ type: "status",
544
+ ...base,
545
+ message: ev.label ?? ev.tool ?? "Working",
546
+ metadata: ev.tool ? { tool: ev.tool } : undefined,
547
+ },
548
+ ];
549
+ }
550
+ if (ev.type === "tool_start") {
551
+ return [
552
+ {
553
+ type: "tool-start",
554
+ ...base,
555
+ toolCall: {
556
+ id: ev.id ?? createRuntimeId("tool"),
557
+ name: ev.tool ?? "unknown",
558
+ input: ev.input,
559
+ inputText: ev.input ? JSON.stringify(ev.input) : undefined,
560
+ },
561
+ },
562
+ ];
563
+ }
564
+ if (ev.type === "tool_done") {
565
+ return [
566
+ {
567
+ type: "tool-done",
568
+ ...base,
569
+ toolCallId: ev.id ?? "",
570
+ toolName: ev.tool ?? "unknown",
571
+ status: ev.error ? "failed" : "completed",
572
+ result: ev.result,
573
+ resultText: typeof ev.result === "string"
574
+ ? ev.result
575
+ : ev.result !== undefined
576
+ ? JSON.stringify(ev.result)
577
+ : undefined,
578
+ error: ev.error,
579
+ mcpApp: ev.mcpApp,
580
+ chatUI: ev.chatUI,
581
+ },
582
+ ];
583
+ }
584
+ if (ev.type === "approval_required") {
585
+ return [
586
+ {
587
+ type: "approval-request",
588
+ ...base,
589
+ approvalId: ev.approvalKey ?? ev.id ?? createRuntimeId("approval"),
590
+ toolCallId: ev.id,
591
+ toolName: ev.tool,
592
+ message: ev.label ?? "Approve this tool call?",
593
+ input: ev.input,
594
+ },
595
+ ];
596
+ }
597
+ if (ev.type === "error" || ev.type === "missing_api_key") {
598
+ return [
599
+ {
600
+ type: "error",
601
+ ...base,
602
+ error: ev.error ?? "Agent chat failed.",
603
+ code: ev.errorCode,
604
+ recoverable: ev.recoverable,
605
+ },
606
+ { type: "done", ...base, reason: "error" },
607
+ ];
608
+ }
609
+ if (ev.type === "done") {
610
+ const message = {
611
+ id: input.messageId,
612
+ role: "assistant",
613
+ content: input.text.value
614
+ ? [{ type: "text", text: input.text.value }]
615
+ : [],
616
+ };
617
+ return [
618
+ ...(input.text.started
619
+ ? [{ type: "message-done", ...base, message }]
620
+ : []),
621
+ { type: "done", ...base, reason: "complete" },
622
+ ];
623
+ }
624
+ return [];
625
+ }
626
+ export function createAgentNativeChatRuntime(options = {}) {
627
+ const apiUrl = options.apiUrl ?? agentNativePath("/_agent-native/agent-chat");
628
+ const runtimeId = options.id ?? "agent-native";
629
+ const fetchImpl = options.fetch ?? fetch;
630
+ return createHttpAgentChatRuntime({
631
+ id: runtimeId,
632
+ kind: "agent-native",
633
+ label: options.label ?? "Agent Native",
634
+ description: options.description ?? "Agent-Native's built-in chat transport.",
635
+ endpoint: apiUrl,
636
+ fetch: fetchImpl,
637
+ headers: async (input) => {
638
+ const headers = await resolveHeaders(options.headers, input);
639
+ headers.set("x-agent-native-surface", options.surface ?? "app");
640
+ return headers;
641
+ },
642
+ capabilities: DEFAULT_RUNTIME_CAPABILITIES,
643
+ mapRequest: ({ session, turn, turnId }) => {
644
+ const prompt = turn.prompt ??
645
+ [...(turn.messages ?? [])]
646
+ .reverse()
647
+ .find((message) => message.role === "user")
648
+ ?.content.map((part) => (part.type === "text" ? part.text : ""))
649
+ .join("\n") ??
650
+ "";
651
+ return {
652
+ message: prompt,
653
+ displayMessage: prompt,
654
+ history: nativeHistoryFromMessages(turn.messages, prompt),
655
+ turnId,
656
+ threadId: session.threadId ?? options.threadId,
657
+ ...(options.mode ? { mode: options.mode } : {}),
658
+ ...((turn.model ?? options.model)
659
+ ? { model: turn.model ?? options.model }
660
+ : {}),
661
+ ...(options.engine ? { engine: options.engine } : {}),
662
+ ...((turn.reasoningEffort ?? options.effort)
663
+ ? { effort: turn.reasoningEffort ?? options.effort }
664
+ : {}),
665
+ ...(options.browserTabId ? { browserTabId: options.browserTabId } : {}),
666
+ ...(options.scope ? { scope: options.scope } : {}),
667
+ ...(turn.attachments?.length ? { attachments: turn.attachments } : {}),
668
+ ...(turn.metadata ? { metadata: turn.metadata } : {}),
669
+ };
670
+ },
671
+ mapEvent: (() => {
672
+ const states = new Map();
673
+ return (event, context) => {
674
+ const stateKey = context.turnId ?? context.sessionId;
675
+ let state = states.get(stateKey);
676
+ if (!state) {
677
+ state = {
678
+ messageId: createRuntimeId("message"),
679
+ text: { value: "", started: false },
680
+ };
681
+ states.set(stateKey, state);
682
+ }
683
+ const mapped = mapAgentNativeEvent(event, {
684
+ sessionId: context.sessionId,
685
+ turnId: context.turnId,
686
+ messageId: state.messageId,
687
+ text: state.text,
688
+ });
689
+ const type = event && typeof event === "object"
690
+ ? event.type
691
+ : undefined;
692
+ if (type === "done" || type === "error" || type === "missing_api_key") {
693
+ states.delete(stateKey);
694
+ }
695
+ return mapped;
696
+ };
697
+ })(),
698
+ cancelEndpoint: (input) => input.runId
699
+ ? `${apiUrl}/runs/${encodeURIComponent(input.runId)}/abort`
700
+ : null,
701
+ resumeEndpoint: (input) => input.runId
702
+ ? `${apiUrl}/runs/${encodeURIComponent(input.runId)}/events?after=${input.after ?? 0}`
703
+ : null,
704
+ });
705
+ }
706
+ function toContentPartInput(value) {
707
+ if (!value || typeof value !== "object" || Array.isArray(value))
708
+ return {};
709
+ const out = {};
710
+ for (const [key, item] of Object.entries(value)) {
711
+ out[key] = typeof item === "string" ? item : JSON.stringify(item);
712
+ }
713
+ return out;
714
+ }
715
+ function toolResultText(value) {
716
+ if (typeof value === "string")
717
+ return value;
718
+ if (value === undefined)
719
+ return "";
720
+ try {
721
+ return JSON.stringify(value);
722
+ }
723
+ catch {
724
+ return String(value);
725
+ }
726
+ }
727
+ function applyRuntimeEventToContent(event, content) {
728
+ const typed = event;
729
+ if (typed.type === "message-start") {
730
+ for (const part of typed.message.content) {
731
+ if (part.type === "text" && part.text) {
732
+ content.push({ type: "text", text: part.text });
733
+ }
734
+ }
735
+ return { content: [...content] };
736
+ }
737
+ if (typed.type === "message-delta") {
738
+ if (typed.delta.type === "text") {
739
+ const last = content[content.length - 1];
740
+ if (last?.type === "text") {
741
+ last.text += typed.delta.text;
742
+ }
743
+ else {
744
+ content.push({ type: "text", text: typed.delta.text });
745
+ }
746
+ return { content: [...content] };
747
+ }
748
+ return null;
749
+ }
750
+ if (typed.type === "message-done") {
751
+ return { content: [...content] };
752
+ }
753
+ if (typed.type === "tool-start") {
754
+ content.push({
755
+ type: "tool-call",
756
+ toolCallId: typed.toolCall.id,
757
+ toolName: typed.toolCall.name,
758
+ argsText: typed.toolCall.inputText ?? JSON.stringify(typed.toolCall.input ?? {}),
759
+ args: toContentPartInput(typed.toolCall.input),
760
+ });
761
+ return { content: [...content] };
762
+ }
763
+ if (typed.type === "tool-delta") {
764
+ const part = [...content]
765
+ .reverse()
766
+ .find((candidate) => candidate.type === "tool-call" &&
767
+ candidate.toolCallId === typed.toolCallId);
768
+ if (!part)
769
+ return null;
770
+ if (typed.inputTextDelta) {
771
+ part.argsText += typed.inputTextDelta;
772
+ }
773
+ if (typed.resultTextDelta) {
774
+ part.result = `${part.result ?? ""}${typed.resultTextDelta}`;
775
+ }
776
+ return { content: [...content] };
777
+ }
778
+ if (typed.type === "tool-done") {
779
+ const part = [...content]
780
+ .reverse()
781
+ .find((candidate) => candidate.type === "tool-call" &&
782
+ candidate.toolCallId === typed.toolCallId);
783
+ if (part) {
784
+ part.result =
785
+ typed.error ?? typed.resultText ?? toolResultText(typed.result);
786
+ if (typed.mcpApp)
787
+ part.mcpApp = typed.mcpApp;
788
+ if (typed.chatUI)
789
+ part.chatUI = typed.chatUI;
790
+ }
791
+ return { content: [...content] };
792
+ }
793
+ if (typed.type === "approval-request") {
794
+ const part = [...content]
795
+ .reverse()
796
+ .find((candidate) => candidate.type === "tool-call" &&
797
+ (candidate.toolCallId === typed.toolCallId ||
798
+ candidate.toolName === typed.toolName));
799
+ if (part) {
800
+ part.approval = { approvalKey: typed.approvalId };
801
+ }
802
+ else {
803
+ content.push({
804
+ type: "tool-call",
805
+ toolCallId: typed.toolCallId ?? typed.approvalId,
806
+ toolName: typed.toolName ?? "approval",
807
+ argsText: typed.input ? JSON.stringify(typed.input) : "",
808
+ args: toContentPartInput(typed.input),
809
+ approval: { approvalKey: typed.approvalId },
810
+ });
811
+ }
812
+ return { content: [...content] };
813
+ }
814
+ if (typed.type === "error") {
815
+ settleInterruptedToolCalls(content);
816
+ content.push({
817
+ type: "text",
818
+ text: `Something went wrong: ${typed.error}`,
819
+ });
820
+ return {
821
+ content: [...content],
822
+ status: { type: "incomplete", reason: "error" },
823
+ metadata: {
824
+ custom: {
825
+ runError: {
826
+ message: typed.error,
827
+ ...(typed.code ? { errorCode: typed.code } : {}),
828
+ ...(typed.recoverable ? { recoverable: typed.recoverable } : {}),
829
+ },
830
+ },
831
+ },
832
+ };
833
+ }
834
+ if (typed.type === "done") {
835
+ return {
836
+ content: [...content],
837
+ status: typed.reason === "error"
838
+ ? { type: "incomplete", reason: "error" }
839
+ : { type: "complete", reason: "stop" },
840
+ };
841
+ }
842
+ return null;
843
+ }
844
+ function assistantMessageText(message) {
845
+ return (message.content ?? [])
846
+ .filter((part) => part.type === "text")
847
+ .map((part) => part.text)
848
+ .join("\n");
849
+ }
850
+ function assistantMessagesToRuntimeMessages(messages) {
851
+ return messages
852
+ .filter((message) => message.role === "user" || message.role === "assistant")
853
+ .map((message, index) => {
854
+ const content = [];
855
+ for (const part of message.content ?? []) {
856
+ if (part.type === "text" && typeof part.text === "string") {
857
+ content.push({ type: "text", text: part.text });
858
+ }
859
+ else if (part.type === "image" && typeof part.image === "string") {
860
+ content.push({ type: "image", data: part.image });
861
+ }
862
+ }
863
+ return {
864
+ id: `assistant-ui-${index}`,
865
+ role: message.role,
866
+ content,
867
+ };
868
+ });
869
+ }
870
+ function latestUserPrompt(messages) {
871
+ for (let i = messages.length - 1; i >= 0; i--) {
872
+ const message = messages[i];
873
+ if (message.role !== "user")
874
+ continue;
875
+ return assistantMessageText(message);
876
+ }
877
+ return "";
878
+ }
879
+ export function createAgentChatRuntimeAdapter(runtime, options = {}) {
880
+ let sessionPromise = null;
881
+ const getSession = () => {
882
+ sessionPromise ??= Promise.resolve(runtime.createSession({
883
+ id: options.sessionId ?? options.threadId,
884
+ threadId: options.threadId,
885
+ metadata: options.metadata,
886
+ }));
887
+ return sessionPromise;
888
+ };
889
+ return {
890
+ async *run({ messages, abortSignal }) {
891
+ const adapterMessages = messages;
892
+ const session = await getSession();
893
+ const prompt = latestUserPrompt(adapterMessages);
894
+ const turn = await session.startTurn({
895
+ prompt,
896
+ messages: assistantMessagesToRuntimeMessages(adapterMessages),
897
+ model: options.modelRef?.current,
898
+ reasoningEffort: options.effortRef?.current,
899
+ abortSignal,
900
+ metadata: options.metadata,
901
+ });
902
+ const content = [];
903
+ try {
904
+ for await (const event of turn.events) {
905
+ const result = applyRuntimeEventToContent(event, content);
906
+ if (result) {
907
+ const metadata = (result.metadata ?? {});
908
+ const custom = metadata.custom && typeof metadata.custom === "object"
909
+ ? metadata.custom
910
+ : {};
911
+ yield {
912
+ ...result,
913
+ metadata: {
914
+ ...metadata,
915
+ custom: {
916
+ ...custom,
917
+ runtimeId: runtime.id,
918
+ ...(turn.runId ? { runId: turn.runId } : {}),
919
+ },
920
+ },
921
+ };
922
+ }
923
+ }
924
+ }
925
+ catch (error) {
926
+ if (error instanceof Error && error.name === "AbortError") {
927
+ await turn.cancel?.({ reason: "abort" });
928
+ return;
929
+ }
930
+ throw error;
931
+ }
932
+ },
933
+ };
934
+ }
2
935
  //# sourceMappingURL=runtime.js.map