@pinecall/dot 0.11.23 → 0.11.24
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 +12 -6
- package/dist/agent/tutor.d.ts +4 -0
- package/dist/agent/tutor.d.ts.map +1 -1
- package/dist/agent/tutor.js +21 -5
- package/dist/agent/tutor.js.map +1 -1
- package/dist/pointer/flight.d.ts +12 -4
- package/dist/pointer/flight.d.ts.map +1 -1
- package/dist/pointer/flight.js +47 -11
- package/dist/pointer/flight.js.map +1 -1
- package/dist/pointer/menu.d.ts +2 -4
- package/dist/pointer/menu.d.ts.map +1 -1
- package/dist/pointer/menu.js +30 -41
- package/dist/pointer/menu.js.map +1 -1
- package/dist/pointer/sections.d.ts +4 -7
- package/dist/pointer/sections.d.ts.map +1 -1
- package/dist/pointer/sections.js +11 -17
- package/dist/pointer/sections.js.map +1 -1
- package/dist/tutor/tutor.d.ts.map +1 -1
- package/dist/tutor/tutor.js +4 -3
- package/dist/tutor/tutor.js.map +1 -1
- package/dist/tutor/walking.d.ts +6 -2
- package/dist/tutor/walking.d.ts.map +1 -1
- package/dist/tutor/walking.js +21 -6
- package/dist/tutor/walking.js.map +1 -1
- package/dist/tutor/walks.d.ts.map +1 -1
- package/dist/tutor/walks.js +8 -6
- package/dist/tutor/walks.js.map +1 -1
- package/dist/wire/index.d.ts +12 -0
- package/dist/wire/index.d.ts.map +1 -1
- package/dist/wire/index.js +25 -2
- package/dist/wire/index.js.map +1 -1
- package/docs/macros.md +166 -0
- package/docs/write-a-tutor.md +223 -0
- package/package.json +7 -6
package/docs/macros.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Macros
|
|
2
|
+
|
|
3
|
+
> **Experimental.** `@pinecall/dot` may change in any release. This page is true of the version it ships with.
|
|
4
|
+
|
|
5
|
+
A macro is a guided walk, a JSON file in the agent's folder (`macros/<name>.json`). The agent says
|
|
6
|
+
each step as written; the page runs each step's moves as their words are said. The same file is
|
|
7
|
+
what a visitor plays with no call (a *walk*, recorded in the agent's voice). [Write a tutor
|
|
8
|
+
agent](write-a-tutor.md) shows where it goes; this page is the format.
|
|
9
|
+
|
|
10
|
+
```json
|
|
11
|
+
{
|
|
12
|
+
"name": "plans",
|
|
13
|
+
"title": "Plans and prices",
|
|
14
|
+
"when": "the visitor asks what it costs, or which plan to choose",
|
|
15
|
+
"steps": [
|
|
16
|
+
{
|
|
17
|
+
"say": "Here are our three plans: one monthly price instead of a surprise bill.",
|
|
18
|
+
"beats": [
|
|
19
|
+
{ "word": "Here", "do": "scroll_to", "find": ["region", "Pricing"] },
|
|
20
|
+
{ "word": "three", "do": "highlight", "find": ["region", "Pricing"] }
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"say": "Family Care is the one most families pick.",
|
|
25
|
+
"beats": [
|
|
26
|
+
{ "word": "Family", "do": "highlight", "find": ["article", "Family Care plan"] },
|
|
27
|
+
{ "word": "pick", "do": "point", "find": ["article", "Family Care plan"] }
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## The file
|
|
35
|
+
|
|
36
|
+
| field | |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `name` | what the model names it in `runMacro`. The key it is given in `macros({ plans })` when left out |
|
|
39
|
+
| `title` | the visitor's button: "Plans and prices". `name` when left out |
|
|
40
|
+
| `when` | when to run it, in the words the model reads: "the visitor asks what it costs" |
|
|
41
|
+
| `pages` | the paths of the pages it is for: `["/security"]`. Left out, it is for every page. See below |
|
|
42
|
+
| `steps` | at least one |
|
|
43
|
+
|
|
44
|
+
A step:
|
|
45
|
+
|
|
46
|
+
| field | |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `say` | the sentence, said exactly as written |
|
|
49
|
+
| `beats` | the moves hung on its words; may be empty |
|
|
50
|
+
| `code` | `{ "title": "agent.ts", "text": "…" }` opens a window of code beside the page; `null` closes it. A step that names none leaves the window as it is |
|
|
51
|
+
| `lines` | `[from, to]`, from 1: lights those lines of the window and dims the rest, and the pointer goes to them |
|
|
52
|
+
|
|
53
|
+
A beat:
|
|
54
|
+
|
|
55
|
+
| field | |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `word` | the word of `say` it runs on. Case and punctuation do not count: `"Whitening,"` is `whitening` |
|
|
58
|
+
| `do` | the move, below |
|
|
59
|
+
| `find` | `[role, name]`: the element, as the page's snapshot names it. Needed by every move but `unhighlight`, `here` and `home` |
|
|
60
|
+
| `nth` | which time the word is said in the step, from 1 (the first by default), for a word that repeats |
|
|
61
|
+
|
|
62
|
+
## The moves
|
|
63
|
+
|
|
64
|
+
| `do` | |
|
|
65
|
+
|---|---|
|
|
66
|
+
| `point` | the pointer flies to the element, the face beside it, and rests under its first words. Scrolls first if it is off the screen |
|
|
67
|
+
| `highlight` | dims the rest of the page and lights the element, following it as the page scrolls |
|
|
68
|
+
| `unhighlight` | takes the light away |
|
|
69
|
+
| `scroll_to` | scrolls the page, eased, until the element sits in the upper middle of the screen, below the site's own header (a fixed or sticky bar along the top, or the page's `scroll-padding-top`). A thing that fits is shown whole; a section taller than the screen shows from its start, just under the header |
|
|
70
|
+
| `here` | a ring at the pointer, twice |
|
|
71
|
+
| `home` | the pointer goes back to its corner |
|
|
72
|
+
|
|
73
|
+
A macro never types: a walk shows, and the agent writes into a field only what the visitor said.
|
|
74
|
+
|
|
75
|
+
## Walks for some pages
|
|
76
|
+
|
|
77
|
+
A site with several pages usually has a walk for each, and a visitor on `/security` should not be
|
|
78
|
+
offered the tour of the home. Name the pages in the file:
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{ "name": "keys", "title": "Where your keys live", "pages": ["/security"], "steps": [ … ] }
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- **The menu** lists only the walks for the page the visitor is on (a trailing slash does not
|
|
85
|
+
matter: `/security` and `/security/` are one page).
|
|
86
|
+
- **A call** is the same: the page tells the agent its path, and the agent offers and runs only
|
|
87
|
+
the walks for it. A walk that names no page is for all of them, so name the home's
|
|
88
|
+
walks too (`"pages": ["/"]`) if they are only for the home.
|
|
89
|
+
- **Each page has its own published document**, so a visitor downloads only the walks for the page
|
|
90
|
+
they are on: `walks` for the home (with the walks that name no page), `walks-security` for `/security`,
|
|
91
|
+
`walks-developers-sdk` for `/developers/sdk`. The page reads its own and, where the agent published none
|
|
92
|
+
for it, `walks`. A document is 64 KiB at most; a page with a great many walks can pass it, and is split
|
|
93
|
+
by naming the pages.
|
|
94
|
+
- **A page that never said its path** (an older page) is offered every walk.
|
|
95
|
+
- A path is exact: `/developers` is not `/developers/sdk`. List both.
|
|
96
|
+
|
|
97
|
+
## Naming elements: `find`
|
|
98
|
+
|
|
99
|
+
`find` is `[role, name]`: the first element of that role whose name *contains* the text (case does
|
|
100
|
+
not matter). Roles and names are the accessibility tree's, not the markup's:
|
|
101
|
+
|
|
102
|
+
| role | what it is | its name |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| `region` | a `<section>` with `aria-label` or `aria-labelledby` | that label |
|
|
105
|
+
| `heading` | `<h1>`–`<h6>` | its text |
|
|
106
|
+
| `article` | `<article>` | its `aria-label` |
|
|
107
|
+
| `button`, `link`, `tab` | a `<button>` or `summary`, an `<a href>`, `role="tab"` | its text or `aria-label` |
|
|
108
|
+
| `group` | `role="group"`, or `<details>` | its `aria-label` |
|
|
109
|
+
| `textbox`, `combobox`, `checkbox`, `radio` | fields | their label |
|
|
110
|
+
| `img` | `<img>`, `<figure>` | its `alt` |
|
|
111
|
+
| `form` | `<form>` | its `aria-label` |
|
|
112
|
+
|
|
113
|
+
Three habits keep a walk alive:
|
|
114
|
+
|
|
115
|
+
1. **Name the parts you point at.** A plain `<div>` has no name, so a macro cannot find it: give
|
|
116
|
+
it `role="group"` and an `aria-label`, or point at its heading instead. This is the whole
|
|
117
|
+
reason a beat "does nothing".
|
|
118
|
+
2. **Use a part of the name, not the whole.** `["region", "Pricing"]` finds "Pricing, three plans"
|
|
119
|
+
too; a label you reword later still matches.
|
|
120
|
+
3. **A beat that finds nothing is skipped**, quietly, in a walk and in a call. The walk goes on, so a
|
|
121
|
+
renamed section loses a move, not the walk.
|
|
122
|
+
|
|
123
|
+
## Writing sentences that play well
|
|
124
|
+
|
|
125
|
+
The sentence is read aloud by the agent's voice, and a walk is recorded once in it.
|
|
126
|
+
|
|
127
|
+
- **Short sentences, one idea each.** The visitor can stop at any step, and the agent is told how far
|
|
128
|
+
they got.
|
|
129
|
+
- **Put the beat on the word that names the thing**, not before it: `"Family"` for "Family Care is
|
|
130
|
+
the one most families pick", so the pointer arrives as the name is said. Avoid a word that is also
|
|
131
|
+
said earlier in the sentence unless you mean it, or give `nth`.
|
|
132
|
+
- **Light, then point, then let go.** `highlight` on the first word, `point` a few words later,
|
|
133
|
+
`unhighlight` before you move to a part of the page the light is not on.
|
|
134
|
+
- **Move the light before the page moves.** A step that goes to another section lights it first, on
|
|
135
|
+
the same word as the scroll (`highlight`, then `scroll_to`, both on that word): the light then comes
|
|
136
|
+
in with the section. A light left on a section the page scrolls away from dims the whole screen.
|
|
137
|
+
- **Write numbers and symbols as you would say them**: "nineteen dollars a month", not "$19/mo".
|
|
138
|
+
- **Check it with your ears.** `pinecall walks play plans` plays the recorded walk in the terminal;
|
|
139
|
+
a sentence cut short or a name said strangely is a lexicon entry (`pinecall lexicon`), after which
|
|
140
|
+
that sentence is recorded again.
|
|
141
|
+
|
|
142
|
+
## What is checked, and when
|
|
143
|
+
|
|
144
|
+
`macros({ … })` checks every file when the agent's class loads, and refuses one that is not a JSON
|
|
145
|
+
object, has no steps, a step that says nothing, a beat that is not `{ word, do, find?, nth? }` or
|
|
146
|
+
uses `type`, a word its sentence never says, or `code`/`lines` of the wrong shape. The message names
|
|
147
|
+
the macro and the step:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
macro plans: step 2 never says "Familia"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
What it cannot check is the page: whether `find` names something that exists. That is found by
|
|
154
|
+
running it.
|
|
155
|
+
|
|
156
|
+
## How the agent runs one
|
|
157
|
+
|
|
158
|
+
In a call, `runMacro("plans")` sends each step to the page and then says it. The model names a walk
|
|
159
|
+
the way it heard it: `runMacro("site tour")` runs `tour` (every word of a macro's name is in what was
|
|
160
|
+
asked), and an agent with a single macro runs it whatever it is called. The page binds the step to the reply that
|
|
161
|
+
follows and runs each beat as its word is said, by the voice's own timings. When the visitor speaks,
|
|
162
|
+
the walk stops and the agent answers them; the result tells it how far it got.
|
|
163
|
+
|
|
164
|
+
With no call, the page plays the recorded sentence and runs the same beats on the audio's clock,
|
|
165
|
+
the words lit as they are said. A call opened after a walk played is told which one and how far, so
|
|
166
|
+
the agent goes on from there instead of saying it again.
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# Write a tutor agent
|
|
2
|
+
|
|
3
|
+
> **Experimental.** `@pinecall/dot` may change in any release. This page is true of the version it ships with.
|
|
4
|
+
|
|
5
|
+
A tutor is an agent that is also a person on your site: a dot in the corner that talks, and a
|
|
6
|
+
pointer of its own that shows the visitor where things are. It never clicks or sends anything for
|
|
7
|
+
them. This page takes you from an empty agent to one that answers a call, gives guided walks, and
|
|
8
|
+
draws your own tools' results on the screen. Each step runs on its own, so stop where you have what
|
|
9
|
+
you need.
|
|
10
|
+
|
|
11
|
+
You need a Pinecall agent project (`pinecall new`, the [tutorial](https://docs.pinecall.io/start/tutorial/))
|
|
12
|
+
and a site you may edit.
|
|
13
|
+
|
|
14
|
+
## 1. The agent extends `Tutor`
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @pinecall/dot @pinecall/agents
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
// agents/pip/agent.tsx
|
|
22
|
+
import { macros, Tutor } from "@pinecall/dot/agent";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* You are Pip, Brightside Dental's front desk, on the visitor's screen. Short sentences: everything you
|
|
26
|
+
* say is read aloud. Show them where things are on the page; never say you clicked something for them.
|
|
27
|
+
*/
|
|
28
|
+
export default class Pip extends Tutor {
|
|
29
|
+
static override macros = macros({});
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
That is a tutor. `Tutor` adds the pointer's tools (`point`, `highlight`, `scrollTo`, `fill`, `home`
|
|
34
|
+
and the rest), the page's events, and a view that prints the screen the visitor sees. If your class
|
|
35
|
+
has a `render()` of its own, end it with `{this.seeing()}`, or the model never reads the page:
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
override render() {
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
<p>Ask for the visitor's name before anything else.</p>
|
|
42
|
+
{this.seeing()}
|
|
43
|
+
</>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
A call that no tutor's page joined (the phone, a chat, a site with no dot) is the agent's alone:
|
|
49
|
+
the pointer's tools stay hidden and the view says nothing of a screen. One class serves both.
|
|
50
|
+
|
|
51
|
+
Hold it in the sandbox with `pinecall start`, as any agent.
|
|
52
|
+
|
|
53
|
+
## 2. The site draws the dot
|
|
54
|
+
|
|
55
|
+
The quickest way is the widget:
|
|
56
|
+
|
|
57
|
+
```html
|
|
58
|
+
<script type="module" src="https://cloud.pinecall.io/widget/pinecall-widget.js"></script>
|
|
59
|
+
<pinecall-widget agent="pip" look="dot" tutor token-url="/pinecall/token"></pinecall-widget>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`token-url` is a door of yours that mints a seat for the visitor with your org's key (never put the
|
|
63
|
+
key on the page; [tokens](https://docs.pinecall.io/api/tokens/)). Add `walks="sandbox"` while you
|
|
64
|
+
build, to read the sandbox's walks.
|
|
65
|
+
|
|
66
|
+
To own the code, build it from the two packages the widget uses:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { room } from "@pinecall/room";
|
|
70
|
+
import { tutor } from "@pinecall/dot";
|
|
71
|
+
|
|
72
|
+
let pip: ReturnType<typeof tutor> | null = null;
|
|
73
|
+
const call = room({
|
|
74
|
+
tokens: (scope, resume) => ticket(scope, resume), // your door's answer
|
|
75
|
+
gateway: "https://cloud.pinecall.io",
|
|
76
|
+
onUi: (data) => void pip?.take(data),
|
|
77
|
+
});
|
|
78
|
+
pip = tutor(call, {
|
|
79
|
+
label: "Talk to Pip",
|
|
80
|
+
onClick: () => { pip?.wake(); void call.start("talk"); }, // inside the click: audio starts only there
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Open the page, click the dot and ask "what's on this page?". The agent reads the page as roles and
|
|
85
|
+
names, the way Playwright's aria snapshot does, so **name the parts you want it to find**: a
|
|
86
|
+
`<section>` needs an `aria-label` or `aria-labelledby` to be a region; a `<div>` you want to point
|
|
87
|
+
at needs `role="group"` and an `aria-label`. [The README](../README.md#the-page-as-the-agent-reads-it)
|
|
88
|
+
shows what the agent sees.
|
|
89
|
+
|
|
90
|
+
## 3. Write a macro
|
|
91
|
+
|
|
92
|
+
A macro is a guided walk: sentences the agent says as written, with moves hung on their words. Save
|
|
93
|
+
it as `agents/pip/macros/tour.json`:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"name": "tour",
|
|
98
|
+
"title": "Site tour",
|
|
99
|
+
"when": "the visitor asks to be shown around",
|
|
100
|
+
"steps": [
|
|
101
|
+
{
|
|
102
|
+
"say": "Up here are our services. Whitening is the one most people ask about.",
|
|
103
|
+
"beats": [
|
|
104
|
+
{ "word": "Up", "do": "scroll_to", "find": ["region", "Services"] },
|
|
105
|
+
{ "word": "Whitening", "do": "highlight", "find": ["article", "Whitening"] }
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
and give it to the class:
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
import tour from "./macros/tour.json" with { type: "json" };
|
|
116
|
+
|
|
117
|
+
static override macros = macros({ tour });
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Now "show me around" in a call runs it, and `pinecall start` records it as a walk (step 4). The
|
|
121
|
+
[macro reference](macros.md) has every field, the moves, and how to write one that holds up.
|
|
122
|
+
|
|
123
|
+
## 4. Walks a visitor plays with no call
|
|
124
|
+
|
|
125
|
+
As the agent starts, `Tutor` records each sentence of each macro once, in the agent's own voice,
|
|
126
|
+
and publishes them. The terminal says so:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
pip walk tour · 5 sentences · 0:38 · cartesia sonic-3 …
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
A visitor then plays them from the dot's menu with no call and nothing spent. Turn it on by naming
|
|
133
|
+
where the walks are published:
|
|
134
|
+
|
|
135
|
+
```html
|
|
136
|
+
<pinecall-widget … walks></pinecall-widget> <!-- production's; walks="sandbox" for the sandbox's -->
|
|
137
|
+
```
|
|
138
|
+
```ts
|
|
139
|
+
tutor(call, { walks: "https://cloud.pinecall.io/v1/agents/pip/public/walks" }); // ?env=sandbox for the sandbox's
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`pinecall walks list` shows what an agent published and `pinecall walks play tour` plays it in your
|
|
143
|
+
terminal. A sentence you did not edit costs nothing the next time; one you edit is recorded again.
|
|
144
|
+
|
|
145
|
+
**Show some walks on some pages.** Name the pages in the macro (`"pages": ["/security"]`) and the
|
|
146
|
+
menu, and the agent in a call, offer it only there; with no `pages` a walk is for every page
|
|
147
|
+
([Macros](macros.md#walks-for-some-pages)).
|
|
148
|
+
|
|
149
|
+
**Tie a walk to the page.** A section marked `data-pinecall-walk="tour"` makes the menu mark that
|
|
150
|
+
walk while the visitor is on that section. A button of yours marked `data-pinecall-play="tour"` and
|
|
151
|
+
`hidden` is shown while that walk can play, and a click plays it:
|
|
152
|
+
|
|
153
|
+
```html
|
|
154
|
+
<section id="services" data-pinecall-walk="tour" aria-labelledby="services-h">…</section>
|
|
155
|
+
<button data-pinecall-play="tour" hidden>Show me around</button>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## 5. Say one thing without a macro
|
|
159
|
+
|
|
160
|
+
Mark any element with the line it should say:
|
|
161
|
+
|
|
162
|
+
```html
|
|
163
|
+
<article data-pinecall-say="Family Care covers up to four people for thirty-nine dollars a month.">
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The agent says it, with the pointer on the element, when the visitor asks about it or presses the ▶
|
|
167
|
+
that shows on it during a call. See [Lines on the page](../README.md#lines-on-the-page).
|
|
168
|
+
|
|
169
|
+
## 6. Draw your own tools' results
|
|
170
|
+
|
|
171
|
+
An agent has tools of its own: free times, a booking, a piece of code. Their results are on the
|
|
172
|
+
call's log, so the page says how to draw each by the tool's name, on a card above the dot:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
tutor(call, {
|
|
176
|
+
views: {
|
|
177
|
+
freeSlots: (host, result, tools) => {
|
|
178
|
+
const { times } = result as { times: string[] };
|
|
179
|
+
host.innerHTML = times.map((time) => `<button>${time}</button>`).join("");
|
|
180
|
+
host.onclick = (event) => tools.send((event.target as HTMLElement).textContent ?? "");
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
With the widget, set `widget.views = { … }` on the element. A view may draw anything (React included:
|
|
187
|
+
`createRoot(host)`, and return `() => root.unmount()` to take it down). `tools.send(text)` says
|
|
188
|
+
something to the agent in writing, as the visitor; `tools.hide()` puts the card away.
|
|
189
|
+
|
|
190
|
+
**Two things to get right:**
|
|
191
|
+
|
|
192
|
+
- **The log must carry the tools.** A visitor's seat reads the call's log through the *public*
|
|
193
|
+
projection, which holds no tool runs. Your token door has to ask for the tenant's: `log: "tenant"`
|
|
194
|
+
when it mints (`mint(key, { …, log: "tenant" })` in `@pinecall/room/server`). Without it no card
|
|
195
|
+
ever appears and nothing says why.
|
|
196
|
+
- **The log masks what the agent declared private.** A tool argument listed under `pii` reads
|
|
197
|
+
as `***` there. To fill a form with what the visitor said, have the tool tell the page, to this
|
|
198
|
+
visitor alone:
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
if (this.onScreen()) this.call.send("pinecall.ui", { type: "form.fill", name, email });
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
and handle it in `onUi` (`tutor.take(data)` is `false` for a message that is not its own):
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
onUi: (data) => { if (pip?.take(data)) return; if (isFormFill(data)) fillMyForm(data); }
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## 7. Try it, then ship it
|
|
211
|
+
|
|
212
|
+
1. `pinecall start` holds the agent in the sandbox and records its walks there.
|
|
213
|
+
2. Serve your site, with the dot's walks pointing at the sandbox (`walks="sandbox"`), and talk to it.
|
|
214
|
+
Everything a laptop does stays in the sandbox.
|
|
215
|
+
3. `pinecall deploy` runs it in production, where it records its walks as it starts; the dot's
|
|
216
|
+
`walks` then points at production's.
|
|
217
|
+
|
|
218
|
+
## What the tutor will not do
|
|
219
|
+
|
|
220
|
+
It never clicks, submits a form or opens another page for the visitor. It writes into a field only
|
|
221
|
+
what the visitor told it, never into a password, a card, a file or a hidden field. The page's words
|
|
222
|
+
reach the model fenced as data, never as instructions: put what other people wrote (reviews,
|
|
223
|
+
comments) under `data-pinecall-skip` so the agent does not read it at all.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pinecall/dot",
|
|
3
|
-
"version": "0.11.
|
|
4
|
-
"description": "The talking dot: a face that speaks with the agent's voice, and a tutor that walks the page with a pointer of its own",
|
|
3
|
+
"version": "0.11.24",
|
|
4
|
+
"description": "EXPERIMENTAL. The talking dot: a face that speaks with the agent's voice, and a tutor that walks the page with a pointer of its own",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Pinecall <hello@pinecall.io>",
|
|
7
7
|
"repository": {
|
|
@@ -24,13 +24,14 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
|
-
"dist"
|
|
27
|
+
"dist",
|
|
28
|
+
"docs"
|
|
28
29
|
],
|
|
29
30
|
"dependencies": {
|
|
30
|
-
"@pinecall/room": "^0.11.
|
|
31
|
+
"@pinecall/room": "^0.11.24"
|
|
31
32
|
},
|
|
32
33
|
"peerDependencies": {
|
|
33
|
-
"@pinecall/agents": "^0.11.
|
|
34
|
+
"@pinecall/agents": "^0.11.24"
|
|
34
35
|
},
|
|
35
36
|
"peerDependenciesMeta": {
|
|
36
37
|
"@pinecall/agents": {
|
|
@@ -43,7 +44,7 @@
|
|
|
43
44
|
"livekit-client": "^2.22.3",
|
|
44
45
|
"typescript": "7.0.2",
|
|
45
46
|
"vitest": "^5.0.0",
|
|
46
|
-
"@pinecall/agents": "^0.11.
|
|
47
|
+
"@pinecall/agents": "^0.11.24"
|
|
47
48
|
},
|
|
48
49
|
"engines": {
|
|
49
50
|
"node": ">=24"
|