@oyehello/chat 0.5.1 → 0.5.2
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 +19 -9
- package/dist/index.cjs +26 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.global.js +26 -2
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +26 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,23 +74,33 @@ await chat.sendMessage('Hello');
|
|
|
74
74
|
|
|
75
75
|
## Client tools (browser actions)
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
The SDK supports two browser-side tools: `navigate` (go to a path on the current site) and `openUrl`
|
|
78
|
+
(open a URL, by default in a new tab). Once a tool is enabled for the agent in the console **and** the
|
|
79
|
+
target passes the agent's navigate allowlist (also set in the console), the SDK executes it itself —
|
|
80
|
+
**no host integration required**:
|
|
81
|
+
```html
|
|
82
|
+
<script type="module">import '@oyehello/chat';</script>
|
|
83
|
+
<oyehello-chat api-key="ohk_pub_…" agent-id="agt_…" base-url="https://api.oyehello.ai"></oyehello-chat>
|
|
84
|
+
```
|
|
85
|
+
That's it — a plain embed with no JS wiring can navigate the visitor or open a link when the agent calls
|
|
86
|
+
the tool. `navigate` calls `window.location.assign(path)`; `openUrl` calls `window.open(url, '_blank')`
|
|
87
|
+
(pass `newTab: false` from the agent to open in the same tab instead).
|
|
88
|
+
|
|
89
|
+
A host page MAY still register its own `navigate`/`openUrl` handler to override the default behavior
|
|
90
|
+
(e.g. push through a client-side router instead of a full navigation):
|
|
79
91
|
```js
|
|
80
92
|
const chat = createChatClient({ apiKey, agentId, baseUrl });
|
|
81
93
|
chat.registerTools({
|
|
82
94
|
navigate: ({ path }) => router.push(path),
|
|
83
|
-
fillField: ({ selector, value }) => { /* your DOM logic */ },
|
|
84
95
|
});
|
|
85
96
|
// or on the web component: document.querySelector('oyehello-chat').registerTools({ … })
|
|
86
97
|
```
|
|
87
|
-
Register handlers BEFORE `start()
|
|
98
|
+
Register handlers BEFORE `start()`. A host-registered handler always takes priority over the SDK's
|
|
99
|
+
built-in; any other tool name has no handler and fails cleanly.
|
|
88
100
|
|
|
89
|
-
> **Security.**
|
|
90
|
-
>
|
|
91
|
-
>
|
|
92
|
-
> Because a visitor can influence the conversation, treat these as capable of arbitrary DOM action —
|
|
93
|
-
> validate selectors/values and scope what each handler can do before enabling them.
|
|
101
|
+
> **Security.** Navigation targets (both the SDK's built-in execution and a host-overridden handler)
|
|
102
|
+
> are validated against the agent's navigate allowlist **server-side** before the tool call ever
|
|
103
|
+
> reaches the browser — the SDK/host is not the trust boundary for where a visitor can be sent.
|
|
94
104
|
|
|
95
105
|
## Requirements
|
|
96
106
|
- `api-key` is a **publishable** key (`ohk_pub_…`) created in the oyehello console.
|
package/dist/index.cjs
CHANGED
|
@@ -314,6 +314,29 @@ function installSpaHooks(onNav) {
|
|
|
314
314
|
// src/client.ts
|
|
315
315
|
var seq = 0;
|
|
316
316
|
var uid = (p) => `${p}_${Date.now().toString(36)}_${(seq++).toString(36)}`;
|
|
317
|
+
var isSafeRelativePath = (p) => p.startsWith("/") && !p.startsWith("//");
|
|
318
|
+
var isSafeHttpUrl = (u) => {
|
|
319
|
+
try {
|
|
320
|
+
const parsed = new URL(u, window.location.origin);
|
|
321
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
322
|
+
} catch {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
var BUILTIN_TOOL_HANDLERS = {
|
|
327
|
+
navigate: (args) => {
|
|
328
|
+
const path = String(args.path ?? "");
|
|
329
|
+
if (!isSafeRelativePath(path)) return { ok: false, error: "unsafe navigate path" };
|
|
330
|
+
window.location.assign(path);
|
|
331
|
+
return { ok: true };
|
|
332
|
+
},
|
|
333
|
+
openUrl: (args) => {
|
|
334
|
+
const url = String(args.url ?? "");
|
|
335
|
+
if (!isSafeHttpUrl(url)) return { ok: false, error: "unsafe url" };
|
|
336
|
+
window.open(url, args.newTab === false ? "_self" : "_blank", "noopener,noreferrer");
|
|
337
|
+
return { ok: true };
|
|
338
|
+
}
|
|
339
|
+
};
|
|
317
340
|
function createChatClient(options) {
|
|
318
341
|
const fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
319
342
|
const ES = options.eventSource ?? globalThis.EventSource;
|
|
@@ -472,7 +495,7 @@ function createChatClient(options) {
|
|
|
472
495
|
} else if (ev.type === "tool_call") {
|
|
473
496
|
if (seenCallIds.has(ev.callId)) return;
|
|
474
497
|
seenCallIds.add(ev.callId);
|
|
475
|
-
const handler = toolHandlers.get(ev.name);
|
|
498
|
+
const handler = toolHandlers.get(ev.name) ?? BUILTIN_TOOL_HANDLERS[ev.name];
|
|
476
499
|
void (async () => {
|
|
477
500
|
if (!session) return;
|
|
478
501
|
const base = { baseUrl: options.baseUrl, sessionId: session.sessionId, sessionToken: session.sessionToken, callId: ev.callId, fetchImpl };
|
|
@@ -570,7 +593,8 @@ function createChatClient(options) {
|
|
|
570
593
|
return;
|
|
571
594
|
}
|
|
572
595
|
if (deviceFingerprint === void 0) deviceFingerprint = await computeDeviceFingerprint().catch(() => null);
|
|
573
|
-
|
|
596
|
+
const advertisedTools = /* @__PURE__ */ new Set([...toolHandlers.keys(), ...Object.keys(BUILTIN_TOOL_HANDLERS)]);
|
|
597
|
+
session = await openSession({ baseUrl: options.baseUrl, apiKey: options.apiKey, agentId: options.agentId, user: options.user, clientTools: Array.from(advertisedTools), fingerprint: deviceFingerprint ?? void 0, fetchImpl });
|
|
574
598
|
persistSession(session);
|
|
575
599
|
if (!warnedProtocol && typeof session.protocol === "number" && session.protocol !== CHAT_PROTOCOL_VERSION) {
|
|
576
600
|
warnedProtocol = true;
|