@agent-native/core 0.137.6 → 0.137.7
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/client/AssistantChat.js +1 -0
- package/dist/client/chat/message-components.d.ts +10 -1
- package/dist/client/chat/message-components.js +13 -4
- package/dist/client/chat/runtime.js +20 -9
- package/dist/client/chat/tool-call-display.d.ts +7 -0
- package/dist/client/chat/tool-call-display.js +9 -1
- package/dist/client/sse-event-processor.d.ts +2 -0
- package/dist/client/sse-event-processor.js +39 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/package.json +1 -1
|
@@ -211,6 +211,7 @@ function cloneContentParts(content) {
|
|
|
211
211
|
args: { ...part.args },
|
|
212
212
|
...(part.mcpApp ? { mcpApp: { ...part.mcpApp } } : {}),
|
|
213
213
|
...(part.chatUI ? { chatUI: { ...part.chatUI } } : {}),
|
|
214
|
+
...(part.approval ? { approval: { ...part.approval } } : {}),
|
|
214
215
|
});
|
|
215
216
|
}
|
|
216
217
|
export function settleInterruptedAssistantToolCallsInRepo(repo) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { ContentPart } from "../sse-event-processor.js";
|
|
3
3
|
import { type RunErrorInfo } from "./run-recovery.js";
|
|
4
|
+
import { toolCallHasPendingApproval } from "./tool-call-display.js";
|
|
5
|
+
export { toolCallHasPendingApproval };
|
|
4
6
|
export declare function displayableUserMessageText(text: string): string;
|
|
5
7
|
export declare function isHiddenUserMessage(message: unknown): boolean;
|
|
6
8
|
interface FormattedMessageTimestamp {
|
|
@@ -98,6 +100,10 @@ export declare function isCollapsibleAssistantWorkPart(part: {
|
|
|
98
100
|
toolName?: string;
|
|
99
101
|
chatUI?: unknown;
|
|
100
102
|
mcpApp?: unknown;
|
|
103
|
+
approval?: {
|
|
104
|
+
approvalKey?: string;
|
|
105
|
+
dismissed?: boolean;
|
|
106
|
+
};
|
|
101
107
|
}): boolean;
|
|
102
108
|
export declare function getAssistantToolSummaryInfo(parts: readonly {
|
|
103
109
|
type?: string;
|
|
@@ -106,6 +112,10 @@ export declare function getAssistantToolSummaryInfo(parts: readonly {
|
|
|
106
112
|
args?: Record<string, unknown>;
|
|
107
113
|
chatUI?: unknown;
|
|
108
114
|
mcpApp?: unknown;
|
|
115
|
+
approval?: {
|
|
116
|
+
approvalKey?: string;
|
|
117
|
+
dismissed?: boolean;
|
|
118
|
+
};
|
|
109
119
|
}[]): {
|
|
110
120
|
startIndex: number;
|
|
111
121
|
hiddenToolCount: number;
|
|
@@ -126,5 +136,4 @@ export declare function RunningActivityStatus({ label }: {
|
|
|
126
136
|
export declare function ThinkingIndicator({ label, }?: {
|
|
127
137
|
label?: string;
|
|
128
138
|
}): React.JSX.Element;
|
|
129
|
-
export {};
|
|
130
139
|
//# sourceMappingURL=message-components.d.ts.map
|
|
@@ -19,7 +19,8 @@ import { cn } from "../utils.js";
|
|
|
19
19
|
import { MarkdownText, renderMarkdownToClipboardHtml, } from "./markdown-renderer.js";
|
|
20
20
|
import { getAssistantRunDurationMs } from "./repo-helpers.js";
|
|
21
21
|
import { getRunErrorMetadata, runErrorHeadline, runErrorKey, } from "./run-recovery.js";
|
|
22
|
-
import { ToolCallFallback, ToolActivityPresentation, FilesChangedSummary, ASSISTANT_VISIBLE_TOOL_CALL_LIMIT, ChatRunningContext, ChatRunDurationContext, formatWorkedDuration, RanToolsSummary, ReasoningCell, WorkedForSummary, } from "./tool-call-display.js";
|
|
22
|
+
import { ToolCallFallback, ToolActivityPresentation, FilesChangedSummary, ASSISTANT_VISIBLE_TOOL_CALL_LIMIT, ChatRunningContext, ChatRunDurationContext, formatWorkedDuration, RanToolsSummary, ReasoningCell, WorkedForSummary, toolCallHasPendingApproval, } from "./tool-call-display.js";
|
|
23
|
+
export { toolCallHasPendingApproval };
|
|
23
24
|
// ─── Pending selection context key ───────────────────────────────────────────
|
|
24
25
|
// Mirrored from AssistantChat to avoid a cross-import on a private constant.
|
|
25
26
|
const PENDING_SELECTION_KEY = "pending-selection-context";
|
|
@@ -506,7 +507,9 @@ export function assistantMessageHasCompletedCustomUi(content) {
|
|
|
506
507
|
}
|
|
507
508
|
hasCompletedTool = true;
|
|
508
509
|
lastCompletedToolIsCustomUi =
|
|
509
|
-
record.chatUI !== undefined ||
|
|
510
|
+
record.chatUI !== undefined ||
|
|
511
|
+
record.mcpApp !== undefined ||
|
|
512
|
+
toolCallHasPendingApproval(record);
|
|
510
513
|
}
|
|
511
514
|
return hasCompletedTool && lastCompletedToolIsCustomUi;
|
|
512
515
|
}
|
|
@@ -518,7 +521,9 @@ export function assistantMessageHasCustomUi(content) {
|
|
|
518
521
|
return false;
|
|
519
522
|
const record = part;
|
|
520
523
|
return (record.type === "tool-call" &&
|
|
521
|
-
(record.chatUI !== undefined ||
|
|
524
|
+
(record.chatUI !== undefined ||
|
|
525
|
+
record.mcpApp !== undefined ||
|
|
526
|
+
toolCallHasPendingApproval(record)));
|
|
522
527
|
});
|
|
523
528
|
}
|
|
524
529
|
// Only the last assistant message may shimmer as "the currently running
|
|
@@ -628,7 +633,11 @@ export function isCollapsibleAssistantWorkPart(part) {
|
|
|
628
633
|
return (part.type === "tool-call" &&
|
|
629
634
|
!ALWAYS_VISIBLE_ASSISTANT_TOOLS.has(part.toolName ?? "") &&
|
|
630
635
|
part.chatUI === undefined &&
|
|
631
|
-
part.mcpApp === undefined
|
|
636
|
+
part.mcpApp === undefined &&
|
|
637
|
+
// Keep the Approve/Deny affordance outside "Worked for…" — needsApproval
|
|
638
|
+
// tools finish with a result string, so without this they collapse and the
|
|
639
|
+
// human gate disappears from the viewport.
|
|
640
|
+
!toolCallHasPendingApproval(part));
|
|
632
641
|
}
|
|
633
642
|
export function getAssistantToolSummaryInfo(parts) {
|
|
634
643
|
const toolCallIndices = parts.reduce((indices, part, index) => {
|
|
@@ -622,7 +622,10 @@ function mapAgentNativeEvent(raw, input) {
|
|
|
622
622
|
type: "approval-request",
|
|
623
623
|
...base,
|
|
624
624
|
approvalId: ev.approvalKey ?? ev.id ?? createRuntimeId("approval"),
|
|
625
|
-
|
|
625
|
+
// `approval_required` carries the model-side call id as `toolCallId`,
|
|
626
|
+
// not `id`. Without this the request falls back to matching by tool
|
|
627
|
+
// name, which picks the wrong call when two are pending at once.
|
|
628
|
+
toolCallId: ev.toolCallId ?? ev.id,
|
|
626
629
|
toolName: ev.tool,
|
|
627
630
|
message: ev.label ?? "Approve this tool call?",
|
|
628
631
|
input: ev.input,
|
|
@@ -849,18 +852,26 @@ function applyRuntimeEventToContent(event, projection) {
|
|
|
849
852
|
return { content: [...content] };
|
|
850
853
|
}
|
|
851
854
|
if (typed.type === "approval-request") {
|
|
852
|
-
const
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
855
|
+
const reversed = [...content].reverse();
|
|
856
|
+
const isToolCall = (candidate) => candidate.type === "tool-call";
|
|
857
|
+
// Match on the exact call id whenever the server supplied one. Falling back
|
|
858
|
+
// to "newest call with this name" would hand this call's approvalKey to a
|
|
859
|
+
// different parallel call of the same action, so name matching is reserved
|
|
860
|
+
// for events that carry no id at all.
|
|
861
|
+
const part = typed.toolCallId
|
|
862
|
+
? reversed.find((candidate) => isToolCall(candidate) && candidate.toolCallId === typed.toolCallId)
|
|
863
|
+
: reversed.find((candidate) => isToolCall(candidate) && candidate.toolName === typed.toolName);
|
|
864
|
+
if (part && part.type === "tool-call") {
|
|
858
865
|
part.approval = { approvalKey: typed.approvalId };
|
|
859
866
|
}
|
|
860
|
-
else {
|
|
867
|
+
else if (!typed.toolCallId) {
|
|
868
|
+
// Only runtimes that never announced the call (no id) get a synthesized
|
|
869
|
+
// card. An id that matches nothing means the call was never observed or
|
|
870
|
+
// is already resolved, and inventing an Approve/Deny card for it would
|
|
871
|
+
// gate something the user cannot see.
|
|
861
872
|
content.push({
|
|
862
873
|
type: "tool-call",
|
|
863
|
-
toolCallId: typed.
|
|
874
|
+
toolCallId: typed.approvalId,
|
|
864
875
|
toolName: typed.toolName ?? "approval",
|
|
865
876
|
argsText: typed.input ? JSON.stringify(typed.input) : "",
|
|
866
877
|
args: toContentPartInput(typed.input),
|
|
@@ -30,6 +30,13 @@ export type ApprovalContextValue = {
|
|
|
30
30
|
onAlwaysAllow?: (approvalKey: string) => void;
|
|
31
31
|
};
|
|
32
32
|
export declare const ApprovalContext: React.Context<ApprovalContextValue | null>;
|
|
33
|
+
/** Pending human-in-the-loop gate still waiting for Approve/Deny. */
|
|
34
|
+
export declare function toolCallHasPendingApproval(part: {
|
|
35
|
+
approval?: {
|
|
36
|
+
approvalKey?: string;
|
|
37
|
+
dismissed?: boolean;
|
|
38
|
+
} | null;
|
|
39
|
+
}): boolean;
|
|
33
40
|
export declare const TOOL_LONG_RUNNING_HINT_DELAY_MS = 45000;
|
|
34
41
|
export declare function ToolActivityPresentation({ toolName, isRunning, isActiveTail, suppressLongRunningHint, children, }: {
|
|
35
42
|
toolName: string;
|
|
@@ -20,6 +20,13 @@ export const ChatRunningContext = React.createContext(false);
|
|
|
20
20
|
export const ChatRunDurationContext = React.createContext(null);
|
|
21
21
|
export const ASSISTANT_VISIBLE_TOOL_CALL_LIMIT = 3;
|
|
22
22
|
export const ApprovalContext = React.createContext(null);
|
|
23
|
+
/** Pending human-in-the-loop gate still waiting for Approve/Deny. */
|
|
24
|
+
export function toolCallHasPendingApproval(part) {
|
|
25
|
+
const approval = part.approval;
|
|
26
|
+
return (typeof approval?.approvalKey === "string" &&
|
|
27
|
+
approval.approvalKey.length > 0 &&
|
|
28
|
+
approval.dismissed !== true);
|
|
29
|
+
}
|
|
23
30
|
export const TOOL_LONG_RUNNING_HINT_DELAY_MS = 45_000;
|
|
24
31
|
export function ToolActivityPresentation({ toolName, isRunning, isActiveTail, suppressLongRunningHint = false, children, }) {
|
|
25
32
|
const [showLongRunningHint, setShowLongRunningHint] = useState(false);
|
|
@@ -514,7 +521,8 @@ function isReconnectSummarizablePart(part) {
|
|
|
514
521
|
(part.type === "tool-call" &&
|
|
515
522
|
part.toolName !== "connect-builder" &&
|
|
516
523
|
part.chatUI === undefined &&
|
|
517
|
-
part.mcpApp === undefined
|
|
524
|
+
part.mcpApp === undefined &&
|
|
525
|
+
!toolCallHasPendingApproval(part)));
|
|
518
526
|
}
|
|
519
527
|
function isReconnectToolSummaryPart(content, index, startIndex) {
|
|
520
528
|
if (startIndex < 0 || index >= startIndex)
|
|
@@ -68,6 +68,8 @@ export interface SSEEvent {
|
|
|
68
68
|
/** Stable key the client echoes back in `approvedToolCalls` to approve a
|
|
69
69
|
* paused `needsApproval` tool call. Present on `approval_required` events. */
|
|
70
70
|
approvalKey?: string;
|
|
71
|
+
/** Model-side tool-call id for `approval_required` (mirrors AgentChatEvent). */
|
|
72
|
+
toolCallId?: string;
|
|
71
73
|
error?: string;
|
|
72
74
|
seq?: number;
|
|
73
75
|
agent?: string;
|
|
@@ -201,6 +201,39 @@ function findPendingToolCallIndexById(content, toolCallId) {
|
|
|
201
201
|
}
|
|
202
202
|
return -1;
|
|
203
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* Locate the tool call an `approval_required` event refers to.
|
|
206
|
+
*
|
|
207
|
+
* Stricter than `findPendingToolCallIndex`: when the server supplies a call id
|
|
208
|
+
* we never fall back to "newest pending call with this name". A paused
|
|
209
|
+
* `tool_done` resolves the call right after the gate fires, so a replayed or
|
|
210
|
+
* reordered approval would otherwise miss on id and silently attach this call's
|
|
211
|
+
* approvalKey to a *different* in-flight call of the same action — putting the
|
|
212
|
+
* wrong key behind a visible Approve button.
|
|
213
|
+
*
|
|
214
|
+
* The only tolerated fallback mirrors `findPendingActivityToolCallIndex`: a
|
|
215
|
+
* single unambiguous reader-local (`tc_N`) placeholder, which is how a call
|
|
216
|
+
* looks when an older server omitted the id on `tool_start`.
|
|
217
|
+
*/
|
|
218
|
+
function findApprovalToolCallIndex(content, toolName, toolCallId) {
|
|
219
|
+
if (!toolCallId) {
|
|
220
|
+
return findPendingToolCallIndex(content, toolName);
|
|
221
|
+
}
|
|
222
|
+
const exactIndex = findPendingToolCallIndexById(content, toolCallId);
|
|
223
|
+
if (exactIndex >= 0)
|
|
224
|
+
return exactIndex;
|
|
225
|
+
const readerLocalCandidates = [];
|
|
226
|
+
for (let i = 0; i < content.length; i += 1) {
|
|
227
|
+
const part = content[i];
|
|
228
|
+
if (part.type === "tool-call" &&
|
|
229
|
+
part.toolName === toolName &&
|
|
230
|
+
part.result === undefined &&
|
|
231
|
+
/^tc_\d+$/.test(part.toolCallId)) {
|
|
232
|
+
readerLocalCandidates.push(i);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return readerLocalCandidates.length === 1 ? readerLocalCandidates[0] : -1;
|
|
236
|
+
}
|
|
204
237
|
function findOldestPendingActivityToolCallIndex(content, toolName) {
|
|
205
238
|
for (let i = 0; i < content.length; i += 1) {
|
|
206
239
|
const part = content[i];
|
|
@@ -656,6 +689,8 @@ function coalesceJournalRecoveredTool(content, completedIndex) {
|
|
|
656
689
|
prior.mcpApp = current.mcpApp;
|
|
657
690
|
if (current.chatUI)
|
|
658
691
|
prior.chatUI = current.chatUI;
|
|
692
|
+
if (current.approval)
|
|
693
|
+
prior.approval = { ...current.approval };
|
|
659
694
|
}
|
|
660
695
|
content.splice(completedIndex, 1);
|
|
661
696
|
return true;
|
|
@@ -1044,7 +1079,10 @@ export function processEvent(ev, content, toolCallCounter, tabId, state) {
|
|
|
1044
1079
|
const approvalTool = ev.tool ?? "unknown";
|
|
1045
1080
|
const approvalKey = ev.approvalKey;
|
|
1046
1081
|
if (approvalKey) {
|
|
1047
|
-
|
|
1082
|
+
// `toolCallId` is the model-side id in the `approval_required` contract;
|
|
1083
|
+
// `id` is only carried by older frames. Same precedence as the runtime
|
|
1084
|
+
// path so both processors resolve an event to the same call.
|
|
1085
|
+
const idx = findApprovalToolCallIndex(content, approvalTool, ev.toolCallId ?? ev.id);
|
|
1048
1086
|
if (idx >= 0) {
|
|
1049
1087
|
const part = content[idx];
|
|
1050
1088
|
if (part.type === "tool-call") {
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
|
|
14
14
|
*/
|
|
15
15
|
export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
16
|
-
error: string;
|
|
17
16
|
ok?: undefined;
|
|
17
|
+
error: string;
|
|
18
18
|
} | {
|
|
19
19
|
error?: undefined;
|
|
20
20
|
ok: boolean;
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
|
|
14
14
|
count: number;
|
|
15
15
|
updated?: undefined;
|
|
16
|
-
ok?: undefined;
|
|
17
16
|
error?: undefined;
|
|
17
|
+
ok?: undefined;
|
|
18
18
|
} | {
|
|
19
19
|
count?: undefined;
|
|
20
20
|
updated: number;
|
|
21
|
-
ok?: undefined;
|
|
22
21
|
error?: undefined;
|
|
22
|
+
ok?: undefined;
|
|
23
23
|
} | {
|
|
24
24
|
count?: undefined;
|
|
25
25
|
updated?: undefined;
|
|
@@ -28,7 +28,7 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
|
|
|
28
28
|
} | {
|
|
29
29
|
count?: undefined;
|
|
30
30
|
updated?: undefined;
|
|
31
|
-
ok: boolean;
|
|
32
31
|
error?: undefined;
|
|
32
|
+
ok: boolean;
|
|
33
33
|
}>>;
|
|
34
34
|
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -27,11 +27,11 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
|
|
|
27
27
|
export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
28
28
|
error: any;
|
|
29
29
|
} | {
|
|
30
|
+
error?: undefined;
|
|
30
31
|
ok: boolean;
|
|
31
32
|
key: string;
|
|
32
33
|
baseUrlKey?: string;
|
|
33
34
|
scope: AgentEngineApiKeyScope;
|
|
34
|
-
error?: undefined;
|
|
35
35
|
}>>;
|
|
36
36
|
export {};
|
|
37
37
|
//# sourceMappingURL=agent-engine-api-key-route.d.ts.map
|
|
@@ -26,9 +26,9 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
|
|
|
26
26
|
expiresAt?: undefined;
|
|
27
27
|
ttlSeconds?: undefined;
|
|
28
28
|
} | {
|
|
29
|
+
error?: undefined;
|
|
29
30
|
token: string;
|
|
30
31
|
expiresAt: string;
|
|
31
32
|
ttlSeconds: number;
|
|
32
|
-
error?: undefined;
|
|
33
33
|
}>>;
|
|
34
34
|
//# sourceMappingURL=realtime-token.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.137.
|
|
3
|
+
"version": "0.137.7",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|