@jini-ai/chat 0.6.15 → 0.6.16
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 +6 -1
- package/dist/core/antigravity/tool-presentation.d.ts +13 -0
- package/dist/core/antigravity/tool-presentation.js +20 -0
- package/dist/core/assistant-client/ag-ui-transport.js +13 -1
- package/dist/core/events.d.ts +5 -1
- package/dist/core/tool-events.d.ts +18 -8
- package/dist/core/tool-events.js +72 -16
- package/dist/core/tool-presentation.d.ts +14 -0
- package/dist/core/tool-presentation.js +8 -0
- package/dist/core/user-text-redaction.d.ts +2 -1
- package/dist/core/user-text-redaction.js +10 -3
- package/dist/embed-widget/browser/frontend-session-bridge.d.ts +99 -0
- package/dist/embed-widget/browser/frontend-session-bridge.js +384 -0
- package/dist/embed-widget/browser/session-store.d.ts +3 -9
- package/dist/embed-widget/browser/session-store.js +6 -38
- package/dist/embed-widget/react/embed.d.ts +3 -10
- package/dist/embed-widget/react/embed.js +8 -52
- package/dist/embed-widget/vanilla/index.d.ts +0 -5
- package/dist/embed-widget/vanilla/index.js +4 -14
- package/dist/embed-widget/vanilla/state.d.ts +3 -11
- package/dist/embed-widget/vanilla/state.js +6 -37
- package/dist/embed-widget/vanilla/style.css +1 -1
- package/dist/react/agent-bridge/frontend-session-bridge.d.ts +2 -56
- package/dist/react/agent-bridge/frontend-session-bridge.js +2 -248
- package/dist/react/components/Icon.d.ts +1 -1
- package/dist/react/components/Icon.js +1 -0
- package/dist/react/components/Markdown.d.ts +1 -0
- package/dist/react/components/Markdown.js +1 -1
- package/dist/react/components/MessageList.d.ts +1 -1
- package/dist/react/components/MessageRow.d.ts +3 -0
- package/dist/react/components/MessageRow.js +10 -8
- package/dist/react/components/ToolCard.d.ts +3 -1
- package/dist/react/components/ToolCard.js +77 -58
- package/dist/react/components/ToolCard.rules.d.ts +20 -0
- package/dist/react/components/ToolCard.rules.js +92 -0
- package/dist/react/features/chat-pane/components/AgentRuntimePicker.js +18 -6
- package/dist/react/features/chat-pane/components/ChatPane.js +3 -3
- package/dist/react/features/chat-pane/hooks/useChatPane.hooks.d.ts +3 -1
- package/dist/react/features/chat-pane/hooks/useChatPane.hooks.js +9 -3
- package/dist/react/features/chat-pane/styles.d.ts +1 -1
- package/dist/react/features/chat-pane/styles.js +76 -9
- package/dist/react/features/chat-pane/types.d.ts +3 -0
- package/dist/react/hooks/useUserTextGuard.d.ts +1 -0
- package/dist/react/hooks/useUserTextGuard.js +3 -1
- package/dist/react/tool-call-display.d.ts +11 -0
- package/dist/react/tool-call-display.js +59 -2
- package/package.json +91 -86
- package/dist/core/browser/page-actions.d.ts +0 -121
- package/dist/core/browser/page-actions.js +0 -115
- package/dist/embed-widget/browser/page-actions.d.ts +0 -2
- package/dist/embed-widget/browser/page-actions.js +0 -3
package/README.md
CHANGED
|
@@ -197,12 +197,17 @@ required argument objects: e.g. `isTerminalRunStatus({ status })`,
|
|
|
197
197
|
`registerExtEventRenderer({ name, renderer }, { slotKey })`. A slot-key callback receives `{ data }`.
|
|
198
198
|
`interleaveMessageBlocks({ events, content, rows }, { slotOf })` retains its reconstruction checks.
|
|
199
199
|
The isolated `core/run-events`, `core/ag-ui`, `server/run-finalizer`, `react/embed`,
|
|
200
|
-
`transports/fetch-sse`, `browser/session-store` and `browser/
|
|
200
|
+
`transports/fetch-sse`, `browser/session-store` and `browser/frontend-session-bridge` subpaths are published. Page control uses the shared frontend bridge and page executor; the retired directive executor and cross-page action queue have been removed.
|
|
201
201
|
`FrontendSessionBridgeArgs` is exported from `react`; hosts pass baseUrl and openStream, optionally request or native fetch.
|
|
202
202
|
The SSE transport takes an injected decoder; browser hosts must supply a browser-safe module.
|
|
203
203
|
|
|
204
204
|
## React integration boundaries
|
|
205
205
|
|
|
206
|
+
`ChatPane` accepts an optional `sendingLabel?: string` for host-localized startup feedback,
|
|
207
|
+
for example `<ChatPane transport={transport} sendingLabel="Connecting to the assistant…" />`.
|
|
208
|
+
The active, sending activity line displays it verbatim until run acceptance. Omitting the prop
|
|
209
|
+
restores the translated `Sending…` label; it has no effect once sending ends.
|
|
210
|
+
|
|
206
211
|
Model/provider selection belongs to the host-injected `ModelAgentPickerSlot`, because choosing
|
|
207
212
|
an LLM or agent is a provider concern rather than a chat-domain concern. The unused internal
|
|
208
213
|
picker was removed after a fresh caller check confirmed that only its own tests reached it.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Antigravity's saved CLI receipts use capitalized argument keys. Display only: never rewrite history. */
|
|
2
|
+
type Call = {
|
|
3
|
+
name: string;
|
|
4
|
+
input: unknown;
|
|
5
|
+
};
|
|
6
|
+
/** Translate agy's MCP envelope and file arguments into the generic chat presentation contract.
|
|
7
|
+
* Returns undefined for other shapes. Pure; O(k) when copying k file argument keys, otherwise O(1).
|
|
8
|
+
*/
|
|
9
|
+
export declare function antigravityToolPresentation({ name, input }: Call, _options?: Record<string, never>): (Call & {
|
|
10
|
+
wrapper?: Call;
|
|
11
|
+
}) | undefined;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=tool-presentation.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** A transport envelope must name both server and tool before its arguments can be unwrapped. */
|
|
2
|
+
function nonEmptyString(value) {
|
|
3
|
+
return typeof value === 'string' && value.length > 0;
|
|
4
|
+
}
|
|
5
|
+
/** Translate agy's MCP envelope and file arguments into the generic chat presentation contract.
|
|
6
|
+
* Returns undefined for other shapes. Pure; O(k) when copying k file argument keys, otherwise O(1).
|
|
7
|
+
*/
|
|
8
|
+
export function antigravityToolPresentation({ name, input }, _options = {}) {
|
|
9
|
+
if (input === null || typeof input !== 'object')
|
|
10
|
+
return undefined;
|
|
11
|
+
const args = input;
|
|
12
|
+
if (name === 'read_file' && typeof args.Path === 'string') {
|
|
13
|
+
return { name, input: { ...args, file_path: args.Path } };
|
|
14
|
+
}
|
|
15
|
+
if (name !== 'call_mcp_tool' || !nonEmptyString(args.ServerName) || !nonEmptyString(args.ToolName))
|
|
16
|
+
return undefined;
|
|
17
|
+
const call = { name: args.ToolName, input: args.Arguments };
|
|
18
|
+
return { ...call, wrapper: call };
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=tool-presentation.js.map
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { EventType } from "@ag-ui/core";
|
|
2
2
|
import { createAgUiToAgentTranslationState, translateAgUiEventToAgentEvent } from "../ag-ui/entry.js";
|
|
3
|
+
/** Only the originating attachment can authorize page execution. Read its token for every turn;
|
|
4
|
+
* caller-supplied tool definitions never authorize or execute a transcript event. */
|
|
5
|
+
function frontendBinding({ input }, _options = {}) {
|
|
6
|
+
const token = input.context?.["frontendBindToken"];
|
|
7
|
+
return typeof token === "string" && token.length > 0 ? { frontendBindToken: token } : {};
|
|
8
|
+
}
|
|
3
9
|
/** Bind the official client without adding its dependency to every chat consumer. */
|
|
4
10
|
export function createAgUiRunClient({ ports }, _options = {}) {
|
|
5
11
|
/** In-flight AG-UI turns' driving `HttpAgent`, keyed by the client-minted runId — the ONLY way
|
|
@@ -54,8 +60,11 @@ export function createAgUiRunClient({ ports }, _options = {}) {
|
|
|
54
60
|
// none yet (e.g. the very first turn).
|
|
55
61
|
const threadId = input.conversationId ?? ports.mintId({}, {});
|
|
56
62
|
const agent = ports.createAgent({ url: ports.endpoint, threadId }, {});
|
|
63
|
+
if (input.signal.aborted)
|
|
64
|
+
throw new DOMException("Run cancelled", "AbortError");
|
|
57
65
|
agUiAgents.set(runId, agent);
|
|
58
|
-
|
|
66
|
+
const abort = () => agent.abortRun();
|
|
67
|
+
input.signal.addEventListener("abort", abort, { once: true });
|
|
59
68
|
const messages = input.history.filter((message) => message.content.trim().length > 0).map(toAgUiMessage);
|
|
60
69
|
const runAgentInput = {
|
|
61
70
|
threadId,
|
|
@@ -65,6 +74,7 @@ export function createAgUiRunClient({ ports }, _options = {}) {
|
|
|
65
74
|
tools: [],
|
|
66
75
|
context: [],
|
|
67
76
|
forwardedProps: {
|
|
77
|
+
...frontendBinding({ input }),
|
|
68
78
|
...(input.agentId ? { agentId: input.agentId } : {}),
|
|
69
79
|
...(input.history.findLast(message => message.role === "user")?.secretRedaction?.secretRedacted ? { secretRedacted: true } : {}),
|
|
70
80
|
},
|
|
@@ -76,6 +86,7 @@ export function createAgUiRunClient({ ports }, _options = {}) {
|
|
|
76
86
|
return;
|
|
77
87
|
settled = true;
|
|
78
88
|
agUiAgents.delete(runId);
|
|
89
|
+
input.signal.removeEventListener("abort", abort);
|
|
79
90
|
handlers.onDone(collected);
|
|
80
91
|
};
|
|
81
92
|
const state = createAgUiToAgentTranslationState({ customEventNames: ports.customEventNames }, {});
|
|
@@ -96,6 +107,7 @@ export function createAgUiRunClient({ ports }, _options = {}) {
|
|
|
96
107
|
},
|
|
97
108
|
error: (error) => {
|
|
98
109
|
agUiAgents.delete(runId);
|
|
110
|
+
input.signal.removeEventListener("abort", abort);
|
|
99
111
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
100
112
|
if (!startSettled) {
|
|
101
113
|
startSettled = true;
|
package/dist/core/events.d.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* not depend on `@jini-ai/protocol` (or, by the same reasoning, on `@jini-ai/daemon`), the same
|
|
18
18
|
* "translate the wire, don't import it" posture `assistant-ag-ui.ts` documents for its own port of
|
|
19
19
|
* this package's vocabulary in the other direction. Keep in sync by hand if the daemon's shape
|
|
20
|
-
* changes —
|
|
20
|
+
* changes — the image arm has two required and two optional fields, so drift is easy to spot in review.
|
|
21
21
|
*/
|
|
22
22
|
export type ToolResultMediaBlock = {
|
|
23
23
|
readonly type: 'text';
|
|
@@ -26,6 +26,10 @@ export type ToolResultMediaBlock = {
|
|
|
26
26
|
readonly type: 'image';
|
|
27
27
|
readonly mimeType: string;
|
|
28
28
|
readonly data: string;
|
|
29
|
+
/** Optional caption and alt text (e.g. the media item's title). */
|
|
30
|
+
readonly title?: string;
|
|
31
|
+
/** Optional link to the full file; the renderer only links a safe (http(s) or same-site) target. */
|
|
32
|
+
readonly href?: string;
|
|
29
33
|
};
|
|
30
34
|
/**
|
|
31
35
|
* A single renderable unit of agent output. Covers every generic variant a
|
|
@@ -29,7 +29,7 @@ export declare function isDelegatedWrapperToolName({ name }: {
|
|
|
29
29
|
name: string;
|
|
30
30
|
}): boolean;
|
|
31
31
|
/**
|
|
32
|
-
* Drop each delegated
|
|
32
|
+
* Drop each delegated or direct MCP wrapper call that the daemon also reported as its own canonical row,
|
|
33
33
|
* so one call shows as one row.
|
|
34
34
|
*
|
|
35
35
|
* WHY. A delegated call arrives twice: the vendor CLI's wrapper `tool_use`
|
|
@@ -39,16 +39,26 @@ export declare function isDelegatedWrapperToolName({ name }: {
|
|
|
39
39
|
* failed call showed two red rows. The canonical row is the one to keep: it carries the tool's own
|
|
40
40
|
* name, input and result.
|
|
41
41
|
*
|
|
42
|
-
* A
|
|
43
|
-
* and
|
|
44
|
-
* such canonical row (refused before the bridge, e.g. a read-only 403) stays, so its error is not
|
|
45
|
-
* hidden.
|
|
42
|
+
* A direct call (`mcp__jini__theme_read_file`) arrives twice the same way: the CLI's `mcp__<server>__X`
|
|
43
|
+
* row and the bridge's `X` row with the same input, so every direct Jini MCP call drew two rows.
|
|
46
44
|
*
|
|
47
|
-
*
|
|
45
|
+
* Antigravity buffers CLI receipts: its adapter-described wrappers match equal inner inputs
|
|
46
|
+
* anywhere in this assistant turn, in appearance order among equal keys. Pass only one turn's events;
|
|
47
|
+
* no state is retained across calls. Wrapper and bridge rows are each claimed at most once.
|
|
48
|
+
* A legacy gateway folds onto the first LATER canonical `tool_use` whose name equals its `input.toolId`.
|
|
49
|
+
* A direct call folds onto the first LATER `X` row with structurally equal JSON input that arrives
|
|
50
|
+
* before the CLI's own result for it. No canonical row is claimed twice, and a folded wrapper's
|
|
51
|
+
* `tool_result` events go with it. A wrapper with no such canonical row (refused before the
|
|
52
|
+
* bridge, e.g. a read-only 403) stays, so its error is not hidden.
|
|
53
|
+
*
|
|
54
|
+
* @param events - One assistant turn's events, normally already passed through {@link dedupeToolUsesById}.
|
|
48
55
|
* @returns A new array without the folded wrappers, or the original reference when none fold.
|
|
49
|
-
* @
|
|
56
|
+
* @throws CanonicalJsonError if a direct call's input contains non-JSON values.
|
|
57
|
+
* @complexity O(n·w·s): n events, w wrappers, s canonical input serialization cost (including key sorting).
|
|
58
|
+
* O(n + s) extra space; inputs and events are not mutated.
|
|
59
|
+
* @example foldDelegatedWrapperCalls({ events }); // canonical rows retain their own results
|
|
50
60
|
*/
|
|
51
61
|
export declare function foldDelegatedWrapperCalls({ events }: {
|
|
52
62
|
events: AgentEvent[] | undefined;
|
|
53
|
-
}): AgentEvent[];
|
|
63
|
+
}, _options?: Record<string, never>): AgentEvent[];
|
|
54
64
|
//# sourceMappingURL=tool-events.d.ts.map
|
package/dist/core/tool-events.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { canonicalJson } from '@jini-ai/core/records';
|
|
2
|
+
import { toolPresentation } from './tool-presentation.js';
|
|
1
3
|
/**
|
|
2
4
|
* Drop repeated `tool_use` events that share an `id`. A retried/duplicated
|
|
3
5
|
* stream can replay the same tool-call id (e.g. reconnect-and-replay landing
|
|
@@ -46,15 +48,31 @@ const DELEGATED_WRAPPER_PATTERN = /^(?:mcp__.+__)?execute_(?:readonly_)?delegate
|
|
|
46
48
|
export function isDelegatedWrapperToolName({ name }) {
|
|
47
49
|
return DELEGATED_WRAPPER_PATTERN.test(name);
|
|
48
50
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
/** The tool `mcp__<server>__X` names (`X`), or undefined for any other name. O(n) in the name length. */
|
|
52
|
+
function directMcpToolName({ name }) {
|
|
53
|
+
if (!name.startsWith('mcp__'))
|
|
51
54
|
return undefined;
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
return
|
|
55
|
+
// The server segment is at least one character, so its closing `__` starts at index 6 or later.
|
|
56
|
+
const separator = name.indexOf('__', 6);
|
|
57
|
+
return separator === -1 ? undefined : name.slice(separator + 2);
|
|
58
|
+
}
|
|
59
|
+
/** Resolve the generic wrapper contract or an MCP gateway/direct name. Pure; O(n) in the name.
|
|
60
|
+
* Legacy gateways retain name-only matching for malformed-input bridge errors;
|
|
61
|
+
* Antigravity's adapter-described wrappers require equal inner inputs but may arrive buffered.
|
|
62
|
+
*/
|
|
63
|
+
function wrapperCall({ event }, _options = {}) {
|
|
64
|
+
const wrapper = toolPresentation(event).wrapper;
|
|
65
|
+
const call = wrapper ?? event;
|
|
66
|
+
if (!isDelegatedWrapperToolName({ name: call.name })) {
|
|
67
|
+
const name = wrapper ? wrapper.name : directMcpToolName({ name: call.name });
|
|
68
|
+
return name ? { name, input: call.input, matchInput: true, buffered: wrapper !== undefined } : undefined;
|
|
69
|
+
}
|
|
70
|
+
const args = call.input !== null && typeof call.input === 'object' ? call.input : {};
|
|
71
|
+
const name = args.toolId;
|
|
72
|
+
return typeof name === 'string' && name ? { name, input: args.input, matchInput: wrapper !== undefined, buffered: wrapper !== undefined } : undefined;
|
|
55
73
|
}
|
|
56
74
|
/**
|
|
57
|
-
* Drop each delegated
|
|
75
|
+
* Drop each delegated or direct MCP wrapper call that the daemon also reported as its own canonical row,
|
|
58
76
|
* so one call shows as one row.
|
|
59
77
|
*
|
|
60
78
|
* WHY. A delegated call arrives twice: the vendor CLI's wrapper `tool_use`
|
|
@@ -64,16 +82,26 @@ function wrapperToolId({ event }) {
|
|
|
64
82
|
* failed call showed two red rows. The canonical row is the one to keep: it carries the tool's own
|
|
65
83
|
* name, input and result.
|
|
66
84
|
*
|
|
67
|
-
* A
|
|
68
|
-
* and
|
|
69
|
-
* such canonical row (refused before the bridge, e.g. a read-only 403) stays, so its error is not
|
|
70
|
-
* hidden.
|
|
85
|
+
* A direct call (`mcp__jini__theme_read_file`) arrives twice the same way: the CLI's `mcp__<server>__X`
|
|
86
|
+
* row and the bridge's `X` row with the same input, so every direct Jini MCP call drew two rows.
|
|
71
87
|
*
|
|
72
|
-
*
|
|
88
|
+
* Antigravity buffers CLI receipts: its adapter-described wrappers match equal inner inputs
|
|
89
|
+
* anywhere in this assistant turn, in appearance order among equal keys. Pass only one turn's events;
|
|
90
|
+
* no state is retained across calls. Wrapper and bridge rows are each claimed at most once.
|
|
91
|
+
* A legacy gateway folds onto the first LATER canonical `tool_use` whose name equals its `input.toolId`.
|
|
92
|
+
* A direct call folds onto the first LATER `X` row with structurally equal JSON input that arrives
|
|
93
|
+
* before the CLI's own result for it. No canonical row is claimed twice, and a folded wrapper's
|
|
94
|
+
* `tool_result` events go with it. A wrapper with no such canonical row (refused before the
|
|
95
|
+
* bridge, e.g. a read-only 403) stays, so its error is not hidden.
|
|
96
|
+
*
|
|
97
|
+
* @param events - One assistant turn's events, normally already passed through {@link dedupeToolUsesById}.
|
|
73
98
|
* @returns A new array without the folded wrappers, or the original reference when none fold.
|
|
74
|
-
* @
|
|
99
|
+
* @throws CanonicalJsonError if a direct call's input contains non-JSON values.
|
|
100
|
+
* @complexity O(n·w·s): n events, w wrappers, s canonical input serialization cost (including key sorting).
|
|
101
|
+
* O(n + s) extra space; inputs and events are not mutated.
|
|
102
|
+
* @example foldDelegatedWrapperCalls({ events }); // canonical rows retain their own results
|
|
75
103
|
*/
|
|
76
|
-
export function foldDelegatedWrapperCalls({ events }) {
|
|
104
|
+
export function foldDelegatedWrapperCalls({ events }, _options = {}) {
|
|
77
105
|
if (!events || events.length === 0)
|
|
78
106
|
return [];
|
|
79
107
|
const claimed = new Set();
|
|
@@ -81,11 +109,11 @@ export function foldDelegatedWrapperCalls({ events }) {
|
|
|
81
109
|
events.forEach((event, i) => {
|
|
82
110
|
if (event.kind !== 'tool_use')
|
|
83
111
|
return;
|
|
84
|
-
const
|
|
112
|
+
const call = wrapperCall({ event });
|
|
85
113
|
// A replayed wrapper (same id) must not claim a second canonical row.
|
|
86
|
-
if (
|
|
114
|
+
if (call === undefined || folded.has(event.id))
|
|
87
115
|
return;
|
|
88
|
-
const match = events
|
|
116
|
+
const match = findCanonicalRow({ events, wrapper: event, wrapperIndex: i, call, claimed });
|
|
89
117
|
if (match === -1)
|
|
90
118
|
return;
|
|
91
119
|
claimed.add(match);
|
|
@@ -96,4 +124,32 @@ export function foldDelegatedWrapperCalls({ events }) {
|
|
|
96
124
|
return events.filter((event) => !((event.kind === 'tool_use' && folded.has(event.id)) ||
|
|
97
125
|
(event.kind === 'tool_result' && folded.has(event.toolUseId))));
|
|
98
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Index of the first unclaimed canonical row in the wrapper's matching window, or -1. Legacy gateways match
|
|
129
|
+
* on name alone; direct calls and adapter-described wrappers also require equal inner input.
|
|
130
|
+
* @throws CanonicalJsonError if a direct call's input contains non-JSON values.
|
|
131
|
+
* @complexity O(n·s): n events, s canonical input serialization cost.
|
|
132
|
+
*/
|
|
133
|
+
function findCanonicalRow({ events, wrapper, wrapperIndex, call, claimed }) {
|
|
134
|
+
const inputKey = call.matchInput ? canonicalJson({ value: { input: call.input } }) : undefined;
|
|
135
|
+
const [start, end] = canonicalSearchWindow({ events, wrapper, wrapperIndex, call });
|
|
136
|
+
for (let j = start; j < end; j += 1) {
|
|
137
|
+
const other = events[j];
|
|
138
|
+
if (claimed.has(j) || other.kind !== 'tool_use' || other.name !== call.name)
|
|
139
|
+
continue;
|
|
140
|
+
if (inputKey === undefined || canonicalJson({ value: { input: other.input } }) === inputKey)
|
|
141
|
+
return j;
|
|
142
|
+
}
|
|
143
|
+
return -1;
|
|
144
|
+
}
|
|
145
|
+
/** Pure matching bounds within one turn; O(n) time, O(1) space for n events.
|
|
146
|
+
* Antigravity's buffered receipts cannot establish arrival/result boundaries. Direct MCP calls
|
|
147
|
+
* retain their later-only window so a completed/refused call cannot consume a separate retry.
|
|
148
|
+
*/
|
|
149
|
+
function canonicalSearchWindow({ events, wrapper, wrapperIndex, call }, _options = {}) {
|
|
150
|
+
if (call.buffered)
|
|
151
|
+
return [0, events.length];
|
|
152
|
+
const resultIndex = !call.matchInput ? -1 : events.findIndex(other => other.kind === 'tool_result' && other.toolUseId === wrapper.id);
|
|
153
|
+
return [wrapperIndex + 1, resultIndex === -1 ? events.length : resultIndex];
|
|
154
|
+
}
|
|
99
155
|
//# sourceMappingURL=tool-events.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type Call = {
|
|
2
|
+
name: string;
|
|
3
|
+
input: unknown;
|
|
4
|
+
};
|
|
5
|
+
/** Display-only call and, when present, the transport's inner call to correlate with the bridge. */
|
|
6
|
+
export type ToolPresentation = Call & {
|
|
7
|
+
wrapper?: Call;
|
|
8
|
+
};
|
|
9
|
+
/** Resolve CLI argument dialects at one boundary. Raw/persisted events remain untouched.
|
|
10
|
+
* Pure; O(k) only when an adapter copies k argument keys, otherwise O(1).
|
|
11
|
+
*/
|
|
12
|
+
export declare function toolPresentation(call: Call, _options?: Record<string, never>): ToolPresentation;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=tool-presentation.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { antigravityToolPresentation } from './antigravity/tool-presentation.js';
|
|
2
|
+
/** Resolve CLI argument dialects at one boundary. Raw/persisted events remain untouched.
|
|
3
|
+
* Pure; O(k) only when an adapter copies k argument keys, otherwise O(1).
|
|
4
|
+
*/
|
|
5
|
+
export function toolPresentation(call, _options = {}) {
|
|
6
|
+
return antigravityToolPresentation(call) ?? call;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=tool-presentation.js.map
|
|
@@ -19,7 +19,8 @@ export interface UserTextRedactionOptions {
|
|
|
19
19
|
export declare const SECRET_REDACTED_NOTICE = "I removed what looked like a key from your message. If it was real, rotate it. Use the secure form to save it.";
|
|
20
20
|
export declare const CREDENTIAL_CARD_GUIDANCE = "Use the secure form to save keys, tokens, passwords, and secrets. Chat answers are sent to the assistant.";
|
|
21
21
|
/** Stricter than error redaction: also detects unlabelled long tokens, while retaining ordinary URLs,
|
|
22
|
-
* hexadecimal hashes, identifiers and prose. Strong credential syntax inside
|
|
22
|
+
* filesystem paths, hexadecimal hashes, identifiers and prose. Strong credential syntax inside
|
|
23
|
+
* URLs or paths is still caught. */
|
|
23
24
|
export declare function redactUserText({ text }: {
|
|
24
25
|
readonly text: string;
|
|
25
26
|
}, { secretField }?: {
|
|
@@ -26,7 +26,8 @@ function entropy(text) {
|
|
|
26
26
|
return bits;
|
|
27
27
|
}
|
|
28
28
|
/** Stricter than error redaction: also detects unlabelled long tokens, while retaining ordinary URLs,
|
|
29
|
-
* hexadecimal hashes, identifiers and prose. Strong credential syntax inside
|
|
29
|
+
* filesystem paths, hexadecimal hashes, identifiers and prose. Strong credential syntax inside
|
|
30
|
+
* URLs or paths is still caught. */
|
|
30
31
|
export function redactUserText({ text }, { secretField = false } = {}) {
|
|
31
32
|
// Existing removal markers must survive a second pass through assignment rules (api_key=…).
|
|
32
33
|
const literalMarkers = new Map();
|
|
@@ -40,10 +41,16 @@ export function redactUserText({ text }, { secretField = false } = {}) {
|
|
|
40
41
|
}).replaceAll(REMOVED, INTERNAL_REMOVED);
|
|
41
42
|
const base = redactSecretShapes({ text: protectedText, policy: { additionalRules: CHAT_RULES } });
|
|
42
43
|
let count = base.redactions;
|
|
43
|
-
|
|
44
|
+
// Exempt locations only AFTER specific secret rules. Only quoted paths may contain whitespace;
|
|
45
|
+
// unquoted paths end there so an adjacent slash-bearing secret cannot inherit the exemption.
|
|
46
|
+
// A quoted path ends at its first matching quote, which must sit on a word boundary, so an
|
|
47
|
+
// apostrophe later in the line ("don't") cannot close an unterminated quote and exempt the gap.
|
|
48
|
+
// Require a root boundary so a slash inside an opaque token cannot exempt that token, and a
|
|
49
|
+
// folder after the root so a bare "/" + token is not mistaken for a path.
|
|
50
|
+
const locations = [...base.text.matchAll(/\b[a-z][a-z0-9+.-]*:\/\/[^\s<>"']+|(?<!\w)(["'`])(?:~\/|[a-z]:[\\/]|\\\\|\/)[^\r\n"'`\\/]+[\\/](?:(?!\1)[^\r\n])*\1(?!\w)|(?<![\w+\/=\\~.-])(?:~\/|[a-z]:[\\/]|\\\\|\/)[^\s<>"'`\\/]+[\\/][^\s<>"'`]*/gi)]
|
|
44
51
|
.map(match => ({ start: match.index, end: match.index + match[0].length }));
|
|
45
52
|
let cleaned = base.text.replace(/(?<![A-Za-z0-9_+\/=-])[A-Za-z0-9_+\/=-]{40,}(?![A-Za-z0-9_+\/=-])/g, (candidate, offset) => {
|
|
46
|
-
if (
|
|
53
|
+
if (locations.some(location => offset >= location.start && offset < location.end))
|
|
47
54
|
return candidate;
|
|
48
55
|
if (/^[a-f0-9]+$/i.test(candidate) || !/[A-Za-z]/.test(candidate))
|
|
49
56
|
return candidate;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type PageDriver } from '@jini-ai/agentic';
|
|
2
|
+
import type { ApprovalAnswer } from '@jini-ai/core';
|
|
3
|
+
import type { ChatPaneAgentToolAction, ChatPaneAgentBridgeAccess } from '../../react/features/chat-pane/types.js';
|
|
4
|
+
export interface FrontendSessionBridgeOptions {
|
|
5
|
+
/** Host validates its server-bound envelope before the shared page executor sees plain args. */
|
|
6
|
+
readonly preparePageInput?: (args: {
|
|
7
|
+
action: ChatPaneAgentToolAction;
|
|
8
|
+
}) => Record<string, unknown>;
|
|
9
|
+
readonly capabilityIds?: readonly string[];
|
|
10
|
+
/** Bounds the HTTP envelope before it reaches a host JSON-body limit. Oversized reads refuse. */
|
|
11
|
+
readonly maxResponseBytes?: number;
|
|
12
|
+
/** Flush host state; a defined return replaces the machine response before acknowledgement. */
|
|
13
|
+
readonly onBeforeResponse?: (args: {
|
|
14
|
+
action: ChatPaneAgentToolAction;
|
|
15
|
+
output: unknown;
|
|
16
|
+
}) => unknown | Promise<unknown>;
|
|
17
|
+
readonly onAfterResponse?: (args: {
|
|
18
|
+
action: ChatPaneAgentToolAction;
|
|
19
|
+
output: unknown;
|
|
20
|
+
}) => void | Promise<void>;
|
|
21
|
+
/** Host policy and the existing app dialog. It never receives an assistant decision. */
|
|
22
|
+
readonly pageApproval?: {
|
|
23
|
+
isDirect(required: {
|
|
24
|
+
capabilityId: string;
|
|
25
|
+
input: Record<string, unknown>;
|
|
26
|
+
}): boolean;
|
|
27
|
+
request(required: {
|
|
28
|
+
capabilityId: string;
|
|
29
|
+
input: Record<string, unknown>;
|
|
30
|
+
signal: AbortSignal;
|
|
31
|
+
}): Promise<ApprovalAnswer>;
|
|
32
|
+
};
|
|
33
|
+
/** Supplying one claims the six `page.*` verbs. Omit for a chat-only surface. */
|
|
34
|
+
readonly pageDriver?: PageDriver;
|
|
35
|
+
/**
|
|
36
|
+
* Product capabilities, keyed by id prefix (`'cms.'`). Each claims every capability under it.
|
|
37
|
+
* This is how a consumer exposes verbs the engine has never heard of.
|
|
38
|
+
*/
|
|
39
|
+
readonly executors?: Readonly<Record<string, (capabilityId: string, input: Record<string, unknown>) => Promise<unknown>>>;
|
|
40
|
+
/** Called for every invocation before it runs — the hook a product uses to show an activity trail. */
|
|
41
|
+
readonly onInvocation?: (action: ChatPaneAgentToolAction) => void;
|
|
42
|
+
/** Reported for failures that are nobody's tool call: stream errors, malformed frames. */
|
|
43
|
+
readonly onError?: (error: unknown) => void;
|
|
44
|
+
}
|
|
45
|
+
/** The host supplies its origin and stream transport; native fetch defaults may be overridden. */
|
|
46
|
+
export interface FrontendSessionBridgeArgs {
|
|
47
|
+
/** Pass an empty string for the same-origin proxy used by a browser host. */
|
|
48
|
+
readonly baseUrl: string;
|
|
49
|
+
readonly endpointPrefix?: string;
|
|
50
|
+
readonly headers?: () => Record<string, string>;
|
|
51
|
+
readonly openStream: (args: {
|
|
52
|
+
url: string;
|
|
53
|
+
headers?: Record<string, string>;
|
|
54
|
+
}) => Pick<EventSource, 'addEventListener' | 'close'>;
|
|
55
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
56
|
+
readonly request?: (args: {
|
|
57
|
+
url: string;
|
|
58
|
+
init: RequestInit;
|
|
59
|
+
timeoutMs: number;
|
|
60
|
+
}) => Promise<Response>;
|
|
61
|
+
}
|
|
62
|
+
export interface FrontendSessionBridge {
|
|
63
|
+
/** Hand this to `ChatPane`'s `agentControl.bridgeAccess`. */
|
|
64
|
+
readonly bridgeAccess: ChatPaneAgentBridgeAccess;
|
|
65
|
+
/**
|
|
66
|
+
* Resolves the *first* time the daemon attaches this surface — an "is it live yet" signal.
|
|
67
|
+
*
|
|
68
|
+
* Its `bindToken` is only correct until the next reconnect, so do not stash it. Call
|
|
69
|
+
* {@link FrontendSessionBridge.bindToken} when building a run request instead.
|
|
70
|
+
*/
|
|
71
|
+
readonly ready: Promise<{
|
|
72
|
+
sessionId: string;
|
|
73
|
+
bindToken: string;
|
|
74
|
+
}>;
|
|
75
|
+
/**
|
|
76
|
+
* The token for the attachment that is live *now*, or `undefined` before the first attach and
|
|
77
|
+
* after {@link FrontendSessionBridge.close}. Read it at run-start; see the module doc.
|
|
78
|
+
*/
|
|
79
|
+
bindToken(): string | undefined;
|
|
80
|
+
close(): void;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Opens the surface's connection and starts routing invocations.
|
|
84
|
+
*
|
|
85
|
+
* @param options - What this page can serve and where the daemon is.
|
|
86
|
+
* @returns The pane's bridge, the attach result, and a teardown.
|
|
87
|
+
*/
|
|
88
|
+
export declare function createFrontendSessionBridge({ baseUrl: base, openStream, request, fetch, endpointPrefix, headers }: FrontendSessionBridgeArgs, options?: FrontendSessionBridgeOptions): FrontendSessionBridge;
|
|
89
|
+
/** Authenticated streaming replacement for EventSource when credentials belong in headers.
|
|
90
|
+
* Uses the shared SSE decoder; reconnect creates a new attachment and clears the old token.
|
|
91
|
+
* O(stream bytes), bounded frame buffer; close aborts fetch/readers and the reconnect timer. */
|
|
92
|
+
export declare function openFetchFrontendStream({ url, headers }: {
|
|
93
|
+
url: string;
|
|
94
|
+
headers: () => Record<string, string>;
|
|
95
|
+
}, { fetch: send, reconnectMs }?: {
|
|
96
|
+
fetch?: typeof globalThis.fetch;
|
|
97
|
+
reconnectMs?: number;
|
|
98
|
+
}): Pick<EventSource, 'addEventListener' | 'close'>;
|
|
99
|
+
//# sourceMappingURL=frontend-session-bridge.d.ts.map
|