@pinecall/dot 0.11.23 → 0.11.25

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 (124) hide show
  1. package/README.md +145 -63
  2. package/dist/agent/index.d.ts +3 -2
  3. package/dist/agent/index.d.ts.map +1 -1
  4. package/dist/agent/index.js +2 -1
  5. package/dist/agent/index.js.map +1 -1
  6. package/dist/agent/tutor.d.ts +12 -10
  7. package/dist/agent/tutor.d.ts.map +1 -1
  8. package/dist/agent/tutor.js +81 -68
  9. package/dist/agent/tutor.js.map +1 -1
  10. package/dist/agent/walks.d.ts +19 -0
  11. package/dist/agent/walks.d.ts.map +1 -0
  12. package/dist/agent/walks.js +89 -0
  13. package/dist/agent/walks.js.map +1 -0
  14. package/dist/page/page.d.ts +2 -2
  15. package/dist/page/page.d.ts.map +1 -1
  16. package/dist/page/page.js +5 -5
  17. package/dist/page/page.js.map +1 -1
  18. package/dist/page/roles.d.ts +2 -0
  19. package/dist/page/roles.d.ts.map +1 -1
  20. package/dist/page/roles.js +21 -2
  21. package/dist/page/roles.js.map +1 -1
  22. package/dist/pointer/asking.d.ts +25 -0
  23. package/dist/pointer/asking.d.ts.map +1 -0
  24. package/dist/pointer/asking.js +82 -0
  25. package/dist/pointer/asking.js.map +1 -0
  26. package/dist/pointer/flight.d.ts +40 -4
  27. package/dist/pointer/flight.d.ts.map +1 -1
  28. package/dist/pointer/flight.js +139 -17
  29. package/dist/pointer/flight.js.map +1 -1
  30. package/dist/pointer/frames.d.ts +28 -0
  31. package/dist/pointer/frames.d.ts.map +1 -0
  32. package/dist/pointer/frames.js +66 -0
  33. package/dist/pointer/frames.js.map +1 -0
  34. package/dist/pointer/icons.d.ts +8 -0
  35. package/dist/pointer/icons.d.ts.map +1 -0
  36. package/dist/pointer/icons.js +15 -0
  37. package/dist/pointer/icons.js.map +1 -0
  38. package/dist/pointer/menu.d.ts +5 -4
  39. package/dist/pointer/menu.d.ts.map +1 -1
  40. package/dist/pointer/menu.js +56 -47
  41. package/dist/pointer/menu.js.map +1 -1
  42. package/dist/pointer/person.d.ts +13 -2
  43. package/dist/pointer/person.d.ts.map +1 -1
  44. package/dist/pointer/person.js +47 -41
  45. package/dist/pointer/person.js.map +1 -1
  46. package/dist/pointer/pressing.d.ts +6 -0
  47. package/dist/pointer/pressing.d.ts.map +1 -0
  48. package/dist/pointer/pressing.js +32 -0
  49. package/dist/pointer/pressing.js.map +1 -0
  50. package/dist/pointer/sections.d.ts +4 -7
  51. package/dist/pointer/sections.d.ts.map +1 -1
  52. package/dist/pointer/sections.js +11 -17
  53. package/dist/pointer/sections.js.map +1 -1
  54. package/dist/pointer/typing.d.ts +5 -3
  55. package/dist/pointer/typing.d.ts.map +1 -1
  56. package/dist/pointer/typing.js +46 -15
  57. package/dist/pointer/typing.js.map +1 -1
  58. package/dist/server/index.d.ts +34 -0
  59. package/dist/server/index.d.ts.map +1 -0
  60. package/dist/server/index.js +108 -0
  61. package/dist/server/index.js.map +1 -0
  62. package/dist/server/questions.d.ts +30 -0
  63. package/dist/server/questions.d.ts.map +1 -0
  64. package/dist/server/questions.js +136 -0
  65. package/dist/server/questions.js.map +1 -0
  66. package/dist/server/saying.d.ts +31 -0
  67. package/dist/server/saying.d.ts.map +1 -0
  68. package/dist/server/saying.js +124 -0
  69. package/dist/server/saying.js.map +1 -0
  70. package/dist/tutor/acting.d.ts +42 -0
  71. package/dist/tutor/acting.d.ts.map +1 -0
  72. package/dist/tutor/acting.js +180 -0
  73. package/dist/tutor/acting.js.map +1 -0
  74. package/dist/tutor/aloud.d.ts +50 -0
  75. package/dist/tutor/aloud.d.ts.map +1 -0
  76. package/dist/tutor/aloud.js +288 -0
  77. package/dist/tutor/aloud.js.map +1 -0
  78. package/dist/tutor/ask.d.ts +49 -0
  79. package/dist/tutor/ask.d.ts.map +1 -0
  80. package/dist/tutor/ask.js +47 -0
  81. package/dist/tutor/ask.js.map +1 -0
  82. package/dist/tutor/listening.d.ts +17 -0
  83. package/dist/tutor/listening.d.ts.map +1 -0
  84. package/dist/tutor/listening.js +66 -0
  85. package/dist/tutor/listening.js.map +1 -0
  86. package/dist/tutor/options.d.ts +37 -1
  87. package/dist/tutor/options.d.ts.map +1 -1
  88. package/dist/tutor/options.js +1 -1
  89. package/dist/tutor/options.js.map +1 -1
  90. package/dist/tutor/reading.d.ts +58 -0
  91. package/dist/tutor/reading.d.ts.map +1 -0
  92. package/dist/tutor/reading.js +193 -0
  93. package/dist/tutor/reading.js.map +1 -0
  94. package/dist/tutor/text.d.ts +16 -0
  95. package/dist/tutor/text.d.ts.map +1 -0
  96. package/dist/tutor/text.js +119 -0
  97. package/dist/tutor/text.js.map +1 -0
  98. package/dist/tutor/tutor.d.ts +2 -9
  99. package/dist/tutor/tutor.d.ts.map +1 -1
  100. package/dist/tutor/tutor.js +110 -87
  101. package/dist/tutor/tutor.js.map +1 -1
  102. package/dist/tutor/walking.d.ts +26 -8
  103. package/dist/tutor/walking.d.ts.map +1 -1
  104. package/dist/tutor/walking.js +145 -30
  105. package/dist/tutor/walking.js.map +1 -1
  106. package/dist/tutor/walks.d.ts +33 -0
  107. package/dist/tutor/walks.d.ts.map +1 -1
  108. package/dist/tutor/walks.js +121 -13
  109. package/dist/tutor/walks.js.map +1 -1
  110. package/dist/wire/acts.d.ts +60 -0
  111. package/dist/wire/acts.d.ts.map +1 -0
  112. package/dist/wire/acts.js +120 -0
  113. package/dist/wire/acts.js.map +1 -0
  114. package/dist/wire/ask.d.ts +132 -0
  115. package/dist/wire/ask.d.ts.map +1 -0
  116. package/dist/wire/ask.js +102 -0
  117. package/dist/wire/ask.js.map +1 -0
  118. package/dist/wire/index.d.ts +38 -8
  119. package/dist/wire/index.d.ts.map +1 -1
  120. package/dist/wire/index.js +86 -12
  121. package/dist/wire/index.js.map +1 -1
  122. package/docs/macros.md +216 -0
  123. package/docs/write-a-tutor.md +247 -0
  124. package/package.json +11 -6
package/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # @pinecall/dot
2
2
 
3
- > **An experiment.** The dot and its tutor are an add-on of Pinecall, not part of its core: their
4
- > API may change in any release.
3
+ > **Experimental.** The dot and its tutor are an add-on of Pinecall, not part of its core, and they
4
+ > are still being found: any of their API, attributes and messages may change or go in any release, a
5
+ > patch one included. These pages are their only documentation, and they describe what is true today.
5
6
 
6
7
  The talking dot. A small face, a dot with eyes and a mouth, speaks with your agent's voice, moving
7
8
  its mouth with the sound frame by frame. As a tutor it becomes one more person on the page, with a
@@ -11,6 +12,9 @@ about, and the agent's prompt reads the page as it is now.
11
12
  It stands on [`@pinecall/room`](../room/README.md), which joins the call. The pinecall widget
12
13
  draws it with `<pinecall-widget look="dot">`, and with `look="dot" tutor` the tutor too.
13
14
 
15
+ **To build a tutor, start with [Write a tutor agent](docs/write-a-tutor.md)**, step by step, and
16
+ [Macros](docs/macros.md), the format of a guided walk. This page is the reference.
17
+
14
18
  | import | where it runs | what it gives you |
15
19
  |---|---|---|
16
20
  | `@pinecall/dot` | the page | `face()`, `tutor()`, and `Page`, the page as the agent reads it |
@@ -37,19 +41,16 @@ talkButton.onclick = () => {
37
41
  // pip.close() takes it off the page.
38
42
  ```
39
43
 
40
- The face fills its element with one SVG: 64 px is a launcher, 240 px a hero. Two faces on one page
41
- are fine.
44
+ The face fills its element with one SVG: 64 px is a launcher, 240 px a hero. Two faces on one page are fine.
42
45
 
43
46
  **Its shape and colour.** `face(host, call, { shape, color })`: `shape` is `circle` (the default),
44
- `squircle` or `blob`, and `color` any CSS colour (else the page's `--pc-face`). `reshape(shape)`
45
- changes it later, the body easing from one into the other. The body moves whole with the mood, its
46
- outline never bent: it bobs and leans in while it listens, more slowly while it thinks, and squashes
47
- with the voice while it speaks; still under `prefers-reduced-motion`. Nothing is drawn outside the
48
- body, no halo and no shadow, so it sits on any background. `tutor()` takes the same `shape` and `color`, which
49
- tints its pointer too.
47
+ `squircle` or `blob`, and `color` any CSS colour (else the page's `--pc-face`). `reshape(shape)` changes it
48
+ later, the body easing from one into the other. The body moves whole with the mood, its outline never bent: it
49
+ bobs and leans in while it listens, more slowly while it thinks, and squashes with the voice while it speaks;
50
+ still under `prefers-reduced-motion`. Nothing is drawn outside the body, no halo and no shadow, so it sits on
51
+ any background. `tutor()` takes the same `shape` and `color`, which tints its pointer too.
50
52
 
51
- **How the mouth is read.** Everything happens in the browser, from the element the agent's voice
52
- plays in (`state.voice`):
53
+ **How the mouth is read.** Everything happens in the browser, from the element the agent's voice plays in (`state.voice`):
53
54
 
54
55
  | step | what |
55
56
  |---|---|
@@ -90,33 +91,35 @@ pip = tutor(call, { label: "Talk to Pip", onClick: () => { pip.wake(); void call
90
91
  for the other), 58 px across (`--pc-dot-size`) and 14 px from the edges; touring, a pointer in its colour with the face beside the tip and what it says
91
92
  beside the face. It moves the way a hand moves a mouse:
92
93
 
93
- - **It aims at an element the way a person does.** It lands on the middle of the element's first
94
- words, not of its box, and flies there on a minimum-jerk path with a slight arc, timed by the
95
- distance.
94
+ - **It aims at an element the way a person does.** It lands on the middle of the element's first words, not
95
+ of its box, and flies there on a minimum-jerk path with a slight arc, timed by the distance. Its words stay
96
+ on the screen on the way: out of its corner, the face and the bubble take the side that fits until the destination's side does.
96
97
  - **The pointer looks like the one the element asks for**: a hand on a link, an I-beam in a field.
97
98
  - **Hover looks real.** Every `:hover` rule of the site's own style sheets is mirrored onto
98
- `[data-pc-hover]`, set on the element under the tip and its ancestors, and `pointerenter` and
99
- `mouseenter` are sent for the site's code. A style sheet from another origin cannot be read and
100
- keeps no hover.
101
- - **It scrolls the page eased**, until the element sits a little above the middle of the screen.
102
- - **A highlight dims the rest of the page** and follows the element as the page scrolls. "Here" is a
103
- ring at the tip.
104
-
105
- **It never clicks, sends a form or opens another page for the visitor.** It shows where, and they do
106
- it. A form's fields it fills with what the visitor told it, typed letter by letter with the `input`
107
- and `change` events the site's code listens for, a list by the option that says it; the visitor
108
- presses its button. It never touches a password, a card number, a file or a hidden field.
99
+ `[data-pc-hover]`, set on the element under the tip and its ancestors, and `pointerenter` and `mouseenter` are
100
+ sent for the site's code. A style sheet from another origin cannot be read and keeps no hover.
101
+ - **It scrolls the page eased**, until the element sits a little above the middle of the screen, below the
102
+ site's header (a fixed or sticky bar along the top, or the page's `scroll-padding-top`). A box that scrolls
103
+ inside the page is scrolled too, its own `scroll-padding-top` kept clear; anything not shown whole where
104
+ the visitor can see it is scrolled to before the pointer lands on it.
105
+ - **A highlight dims the rest of the page** and follows the element as the page scrolls. "Here" rings the tip.
106
+
107
+ **It never sends a form or opens another page for the visitor.** It shows where, and they do it. A
108
+ form's fields it fills with what the visitor told it, typed letter by letter with the `input` and
109
+ `change` events the site's code listens for, a list by the option that says it; the visitor presses
110
+ its button. It never touches a password, a card number, a file or a hidden field. The one button it
111
+ presses is one a walk the site wrote names (`press`): outside any form, or in a search form
112
+ (`<search>`, `role="search"`); never a link, never a form that books, buys or signs up.
109
113
 
110
114
  **The conversation in writing, and the call's buttons.** The agent's words show beside the face,
111
- lit as its voice says them; the visitor's, what the agent heard, show in the corner as they speak
112
- and for `linger` ms after (2600 by default), one side at a time. `captions` chooses whose may show:
113
- `both` (default), `agent`, `caller` or `none`. During a call two buttons sit beside the corner:
114
- **CC**, which turns the words on and off for this visitor: off until they turn them on (kept in
115
- their browser's `localStorage`, and absent with `captions: "none"`), and **✕**, which hangs up (`call.leave()`). While a walk plays,
116
- CC sits there with **■**, which stops it. The buttons slide into the face's corner while it tours, and
117
- aside as it comes home. No words show while a
118
- window of code or a card is open: they would cover it. `words: { hangUp, captions, stop }` names them for a
119
- screen reader.
115
+ lit as its voice says them; the visitor's, what the agent heard, show in the corner as they speak and for
116
+ `linger` ms after (2600 by default), one side at a time. `captions` chooses whose may show: `both` (default),
117
+ `agent`, `caller` or `none`. During a call two buttons sit beside the corner: **CC**, which turns the words on
118
+ and off for this visitor: off until they turn them on (kept in their browser's `localStorage`, and absent with
119
+ `captions: "none"`), and **✕**, which hangs up (`call.leave()`). While a walk plays, CC sits there with **■**,
120
+ which stops it. The buttons slide into the face's corner while it tours, and aside as it comes home. No words
121
+ show while a window of code or a card is open: they would cover it. `words: { hangUp, captions, stop }` names
122
+ them for a screen reader.
120
123
 
121
124
  `take(data)` returns `true` for a message that was the tutor's (a cursor move, a macro step) and
122
125
  `false` for anything else, which stays the page's to draw.
@@ -141,16 +144,15 @@ screen reader.
141
144
  - region "Book your first visit" [ref=e18] (below)
142
145
  ```
143
146
 
144
- - **What is on the screen is there in full. A section out of view is one line**, its name and
145
- `above` or `below`.
146
- - **Words in a box that is only layout are a line of text**, as a price in a `<div>`, as Playwright
147
- says them.
147
+ - **What is on the screen is there in full. A section out of view is one line**, its name and `above` or `below`.
148
+ - **Words in a box that is only layout are a line of text**, as a price in a `<div>`, as Playwright says them.
149
+ - **A name is the element's words with a space between its parts**, as a person reads them across:
150
+ `link "L la-espiga not held right now"`, never the parts run together.
148
151
  - **A ref is stable for the page's life**, and still names its element after a scroll took it off
149
152
  the screen.
150
153
  - **A field is its label and what it holds now**: `[value="Jordan Lee"]` or `[empty]`, a list's
151
- choice and its `(options …)`, `[checked]` or `[unchecked]`, so the agent sees what the visitor
152
- typed. Passwords, card numbers, and anything under `[data-pinecall-skip]` or `aria-hidden` are
153
- left out.
154
+ choice and its `(options …)`, `[checked]` or `[unchecked]`, so the agent sees what the visitor typed. Passwords,
155
+ card numbers, and anything under `[data-pinecall-skip]` or `aria-hidden` are left out.
154
156
  - **What the site wrote for the agent to say is marked `[has a line]`** ([below](#lines-on-the-page)).
155
157
 
156
158
  The tutor sends it as `page.seen` when the call goes live, when a scroll stops, on a resize, when
@@ -183,9 +185,8 @@ A class that extends `Tutor` gets these:
183
185
  model calls them, while the words that name them play, and the model answers one only when it made
184
186
  it without a word; `runMacro` too, which starts on its own first words and ends on its own last ones.
185
187
  - **A move onto a thing waits for the page to say it ended** (`point`, `highlight`, `scrollTo`, three
186
- seconds at most), and its result is that thing as the visitor now sees it, fenced as data: a
187
- section that was one line below the fold comes back in full, its prices with it, for the model to
188
- talk about.
188
+ seconds at most), and its result is that thing as the visitor now sees it, fenced as data: a section that was
189
+ one line below the fold comes back in full, its prices with it, for the model to talk about.
189
190
  - **`fill(field, text)` writes what the visitor said into a field**, after the agent asked for it,
190
191
  one field at a time; its result is the field as it now shows, and a field the page refused (a
191
192
  password, a list with no such option) is a tool error naming why. It never sends the form.
@@ -229,6 +230,10 @@ sentence the agent says as written, and each beat is a move run as its word is s
229
230
  never says.
230
231
  - **A model names a walk the way it heard it**: `runMacro("site tour")` runs `tour`, and an agent with one
231
232
  walk runs it whatever it is called (`walkNamed`).
233
+ - **A macro can be a function** ([docs/macros.md](docs/macros.md#a-macro-as-a-function)): `params` it is
234
+ called with fill the `{slots}` of its sentences, and steps that act fill a field, press a search, wait
235
+ for its answer and read the table it drew (`{hit.Time}`). In a call the model passes the params; typed
236
+ to the dot, they are picked out of what the visitor wrote. A sentence with slots is recorded live.
232
237
 
233
238
  How a macro runs. `runMacro("tour")` sends each step on `pinecall.ui`, then says it. The page binds
234
239
  the step to the reply that follows and counts its words as the voice says them, the same word
@@ -249,9 +254,9 @@ widget's `walks` attribute) puts a small button beside the resting face. A click
249
254
  round it, four at a time (a small ⌄ under them shows the next ones), each a pill with its title and length; a pick plays it: the voice, the mouth moving with
250
255
  it, the words lit as said, each beat on its word, a ring round the face for how far it is. While it
251
256
  plays, the corner holds captions, pause and stop; a click on the face stops it too. No microphone is
252
- asked for. A section can offer its own walk: with `data-pinecall-walk="<macro>"` on it, as the page is
253
- scrolled to it that walk's pill shows alone beside the face for a few seconds, then goes; not during
254
- a call or a walk. The site can also draw its own button, `hidden`, and mark it
257
+ asked for. A section can name its walk: with `data-pinecall-walk="<macro>"` on it, the menu, when it
258
+ opens, marks the walk of the section the visitor is on (the one across the screen's middle) and opens on
259
+ the page it stands on. The site can also draw its own button, `hidden`, and mark it
255
260
  `data-pinecall-play="<macro>"`: the tutor shows it while that walk can play, and a click plays it. A call opened afterwards tells the agent which walk
256
261
  was played and how far (`page.walked`), so it goes on from there rather than give it again.
257
262
  `onWalk` hears each start and end, for the page's own analytics.
@@ -281,26 +286,104 @@ chose. The card holds the last tool that finished with a view, until it is repla
281
286
  visitor's way to say something, never a way round the agent: nothing on it books or sends for them.
282
287
  With the widget, set `widget.views = { … }` on the element. Colours: `--pc-card` and `--pc-card-ink`.
283
288
 
289
+ ### Typing to the dot: one thing done, never a conversation
290
+
291
+ `tutor(call, { ask })` puts a field in the walks' menu ("Ask me to show you…"). `ask(request)` is the
292
+ site's door, on its own server. What the visitor types goes there with the page's path, its named
293
+ things by ref and the walks of the page (`{ decide }`); the answer is one thing to do, and the dot does
294
+ it with no call: it plays a walk, called with the params picked out of what was typed, or lights and
295
+ points at a thing with its name beside the face. A question a person answers, or something not on the
296
+ page, is never answered in the dot's words: the field says so, with a button that hands it to
297
+ `onTalk(message)` (the site starts a call with it written in). The same door picks the row of a table a
298
+ walk read (`{ row }`) and records a walk's sentence once its slots are filled (`{ say }`).
299
+
300
+ ```ts
301
+ // the page
302
+ tutor(call, { ask: (request) => fetch("/api/ask", { method: "POST", body: JSON.stringify(request) }).then((r) => r.json()) });
303
+
304
+ // the site's server
305
+ import { answer } from "@pinecall/dot/server";
306
+ const answered = await answer(body, {
307
+ key: process.env.OPENROUTER_API_KEY,
308
+ voicing: { key: process.env.PINECALL_KEY, agent: "pip" }, // records sentences with slots in the agent's voice
309
+ });
310
+ ```
311
+
312
+ `answer()` asks Jev, TypeSafe's System One model, through OpenRouter's Decisions API: one request, its
313
+ questions answered at once (what they want, which walk, which thing of the page, each param of each walk),
314
+ each a choice among the options given, with probabilities. A param in the visitor's words is one of the
315
+ parts of what they typed (one to five words in a row): what fills a field is what they wrote, never a word
316
+ of the model's. A sentence with slots is read from the walks the agent published, never from the page, and
317
+ recorded once per set of values (a line of 80 characters each). Jev cannot write a sentence or name a thing
318
+ not on the page, and under its threshold (0.5) decides nothing rather than guess. The page's words and what
319
+ the visitor typed go to OpenRouter and TypeSafe: a site says so in its privacy policy.
320
+
321
+ ### A page read aloud
322
+
323
+ `tutor(call, { ask, reading: "Leer el capítulo" })` puts a first pill in the walks' menu that reads the page
324
+ aloud in the agent's voice, with no call: what the site marks `data-pinecall-read` (a `<main>`, a section), its
325
+ headings, paragraphs, items and captions in the page's order, as written, the part read lit and pointed at and
326
+ the rest dimmed; a long one is read a few sentences at a time. Every part is asked of the door as the tutor
327
+ mounts, three at a time, so the reading starts with no wait. A button marked `data-pinecall-press` is an
328
+ example: reached, the pointer presses it (its value said first: `data-pinecall-press="Lo corro."`), the reading
329
+ waits for what it controls (`aria-controls`, its output) to answer and not be `aria-busy`, then says its
330
+ `data-pinecall-then` pointing at the output and reads the output line by line with notes: `data-pinecall-notes`
331
+ names a list whose items each find a line of it (`data-pinecall-find`, a text in any case or `/a regex/`) and
332
+ say what it means; the line is lit and pointed at, the output marked `data-pinecall-reading` meanwhile so the
333
+ site can widen a box that scrolls. A note whose line is not there is left out. A field marked
334
+ `data-pinecall-type` is typed with its value, the page's own example (a chat's question), and the
335
+ `data-pinecall-press` after it sends it, in any form; an element that is no field (an editor) marked so is told
336
+ each letter by `pinecall-typing` events the site takes with `preventDefault()`. A frame of the site's own (a
337
+ chat in an iframe) is read in its place. A send button marked `data-pinecall-goal` is a chat the dot talks to:
338
+ it asks the door for its line (`{ next }`: Pinecall's caller, with `voicing.next`), opens the chat by focusing
339
+ its field (its greeting waited on while busy), says each line as it types it, sends it, reads aloud what the
340
+ agent said and the tools it used (turns marked `data-pinecall-said`; a tool's said by its `data-pinecall-say`)
341
+ until it has what it came for, the chat ends (a turn marked `end`) or forty turns pass, and the page goes on.
342
+ Such a button in no form is a spoken call: the dot presses it, hears the greeting (`data-pinecall-heard` on the
343
+ output: the agent and its tools are heard, not read; only the end is said) and says its lines into the call,
344
+ each told to the output by a `pinecall-saying` event with its recording; `tutor.stop()` ends a reading. A page whose top was scrolled away is read from the first part still in view. A button
345
+ marked `data-pinecall-explain` with no notes has its output explained by the door (`{ explain }`, with
346
+ `voicing.explain`), the notes read in the place of its `data-pinecall-then`. `data-pinecall-quiet` is not read.
347
+ An element with `data-pinecall-say` is said as that line instead, nothing inside it read again. `tutor.read()`
348
+ starts it from a button of the site's own; while it plays, the corner holds back, pause, next and stop.
349
+
350
+ Each part is recorded by the site's door when the reading reaches it, the next asked for while one plays (`{
351
+ read: { text } }`), and the gateway keeps it: a page read twice costs nothing the second time. A door lets its
352
+ pages be read only when it says so (`answer(body, { voicing: { …, read: true } })`): a page may then have any
353
+ text of 480 characters said in the agent's voice, so a site gives it to its own pages alone.
354
+
355
+ ```html
356
+ <main data-pinecall-read>
357
+ <h2>Cómo leer esto</h2>
358
+ <p>Cada comando está en un bloque con ▶ Run.</p>
359
+ <div class="block" data-pinecall-quiet>
360
+ <pre>pinecall whoami</pre>
361
+ <button aria-label="Run pinecall whoami" aria-controls="out-1" data-pinecall-press="Lo corro." data-pinecall-notes="notes-1">▶ Run</button>
362
+ <output id="out-1"></output>
363
+ <ol id="notes-1" hidden data-pinecall-skip>
364
+ <li data-pinecall-find="org lesson">Ahí está tu org, lesson.</li>
365
+ <li data-pinecall-find="/person's key/">Y es una key de persona.</li>
366
+ </ol>
367
+ </div>
368
+ </main>
369
+ ```
370
+
284
371
  ### Lines on the page
285
372
 
286
- A walk can also live in the site's own markup, beside what it is about, so the HTML and what is
287
- said never drift apart:
373
+ A walk can also live in the site's own markup, beside what it is about:
288
374
 
289
375
  ```html
290
376
  <section id="pricing" data-pinecall-say="Pricing is simple: three plans, and most families pick Family Care." data-pinecall-do="highlight">
291
377
  <article data-pinecall-say="Family Care covers up to four people for thirty-nine dollars a month." data-pinecall-order="2">
292
378
  ```
293
379
 
294
- - **`data-pinecall-say` is said as written**, with the move `data-pinecall-do` names (`point`
295
- unless `highlight` or `scroll_to`), in `data-pinecall-order` and then in the page's order.
296
- - **One at a time**: `explain(what)` says the line of a thing marked `[has a line]`, the pointer on
297
- it, when the visitor asks what it is; and during a call a ▶ shows at the top right of a marked
298
- element under the visitor's mouse, which says it at once (`page.explain`).
299
- - **All of them**: the walk `page`, listed with the agent's macros, says every line in order.
300
- - **Said once.** The agent says a line through `say()`, so it is the turn's answer, in the history:
301
- the model is not asked to answer it again, and the tool's result tells it the line is said.
302
-
303
- Only the site's markup is read: a line under `[data-pinecall-skip]` or `aria-hidden` is not.
380
+ - **`data-pinecall-say` is said as written**, with the move `data-pinecall-do` names (`point` unless
381
+ `highlight` or `scroll_to`), in `data-pinecall-order` and then in the page's order; a line under
382
+ `[data-pinecall-skip]` or `aria-hidden` is not read.
383
+ - **One at a time, or all**: `explain(what)` says the line of a thing marked `[has a line]`, the pointer
384
+ on it; during a call a ▶ at the top right of a marked element under the mouse says it at once
385
+ (`page.explain`); the walk `page`, listed with the agent's macros, says every line in order.
386
+ - **Said once.** The agent says a line through `say()`: it is the turn's answer, in the history, never asked again.
304
387
 
305
388
  ## What the page and the agent say to each other
306
389
 
@@ -312,6 +395,5 @@ Only the site's markup is read: a line under `[data-pinecall-skip]` or `aria-hid
312
395
  | agent → page | `pinecall.ui` `{ type: "macro.step", macro, step, say, beats }` |
313
396
  | agent → page | `pinecall.ui` `{ type: "look" }`: what the page shows now, answered with `page.seen` |
314
397
 
315
- **Anybody can send what a browser sends.** The agent reads the page as the visitor's own words, and
316
- a site that shows other people's words (reviews, comments) puts them under `data-pinecall-skip`
317
- when the agent should not read them.
398
+ **Anybody can send what a browser sends.** The agent reads the page as the visitor's own words: a site
399
+ that shows other people's words (reviews, comments) puts them under `data-pinecall-skip`.
@@ -1,4 +1,5 @@
1
1
  /** @pinecall/dot/agent: the agent's side of the dot tutor, for a class written with @pinecall/agents. */
2
- export { Tutor, macros, walkNamed } from "./tutor.js";
3
- export type { Action, Beat, Macro, MacroStep } from "../wire/index.js";
2
+ export { Tutor } from "./tutor.js";
3
+ export { macros, walkNamed } from "./walks.js";
4
+ export type { Act, ActStep, Action, Beat, Macro, MacroStep, Param } from "../wire/index.js";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA,yGAAyG;AAEzG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACtD,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA,yGAAyG;AAEzG,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC/C,YAAY,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC"}
@@ -1,3 +1,4 @@
1
1
  /** @pinecall/dot/agent: the agent's side of the dot tutor, for a class written with @pinecall/agents. */
2
- export { Tutor, macros, walkNamed } from "./tutor.js";
2
+ export { Tutor } from "./tutor.js";
3
+ export { macros, walkNamed } from "./walks.js";
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA,yGAAyG;AAEzG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA,yGAAyG;AAEzG,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC"}
@@ -1,13 +1,6 @@
1
1
  /** The agent's side of the dot tutor: the visitor's page in its prompt, the pointer's tools, and the macros it was given. */
2
2
  import { Agent, type Call, type Child, type EventDeclarations, type EventMeta, type Serve } from "@pinecall/agents";
3
3
  import { type Line, type Macro } from "../wire/index.js";
4
- /**
5
- * The walk a model means by `asked`: the one so named, else the one whose name it says among its words
6
- * ("site tour" for `tour`), else the only one there is. A model names a walk the way it heard it.
7
- */
8
- export declare function walkNamed(walks: Record<string, Macro>, asked: string): Macro | undefined;
9
- /** Each macro file, checked: `macros({ tour })` with `import tour from "./macros/tour.json" with { type: "json" }`. */
10
- export declare function macros(files: Record<string, unknown>): Record<string, Macro>;
11
4
  export declare class Tutor extends Agent {
12
5
  static events: EventDeclarations;
13
6
  /** The guided walks this agent can give, by name. */
@@ -22,6 +15,8 @@ export declare class Tutor extends Agent {
22
15
  lines: Line[];
23
16
  /** The walk the visitor played with no call before this one, and how far: `"tour", all 5 sentences`. */
24
17
  walked: string;
18
+ /** The path of the visitor's page, as the page last said it; none, every walk is for it. */
19
+ path: string;
25
20
  /**
26
21
  * Before any call: each macro recorded in the agent's voice and published as `walks`, for the
27
22
  * page to play with no call. A sentence recorded before costs nothing, so this runs as it is again.
@@ -50,8 +45,15 @@ export declare class Tutor extends Agent {
50
45
  here(): string;
51
46
  /** Take your pointer back to your corner, when you are done showing. */
52
47
  home(): string;
53
- /** Give one of your walks, by its name: each sentence said as written, your pointer moving on its words. */
54
- runMacro(name: string): Promise<string>;
48
+ /** The walks for the page the visitor is on: those that name it, and those that name none. */
49
+ private macrosHere;
50
+ /** Give one of your walks, by its name, with the params it takes: each sentence said as written, your pointer moving on its words. */
51
+ runMacro(name: string, params?: Record<string, string>): Promise<string>;
52
+ /**
53
+ * A walk's act on the page: a field filled, a button pressed, the page waited on. A table read ends the
54
+ * walk's own words: what it shows is handed to the model, to answer the visitor from.
55
+ */
56
+ private acted;
55
57
  /** The site's lines, one by one: the pointer onto each element, then its line said as written. */
56
58
  private walkThePage;
57
59
  /** The pointer onto a line's element, then the line said: the turn's answer, kept in the history. */
@@ -60,7 +62,7 @@ export declare class Tutor extends Agent {
60
62
  private shown;
61
63
  /** Move onto `what`, wait for the page to say the move ended, and say what the visitor sees there. */
62
64
  private show;
63
- /** Send a move and wait for the page to say it ended, three seconds at most; a move never answered ends with nothing known. */
65
+ /** Send a move and wait for the page to say it ended, three seconds at most (`within`); a move never answered ends with nothing known. */
64
66
  private moved;
65
67
  private move;
66
68
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tutor.d.ts","sourceRoot":"","sources":["../../src/agent/tutor.ts"],"names":[],"mappings":"AAAA,6HAA6H;AAgB7H,OAAO,EAAE,KAAK,EAAiC,KAAK,IAAI,EAAE,KAAK,KAAK,EAAE,KAAK,iBAAiB,EAAE,KAAK,SAAS,EAAE,KAAK,KAAK,EAAoB,MAAM,kBAAkB,CAAC;AAErK,OAAO,EAAmH,KAAK,IAAI,EAAE,KAAK,KAAK,EAAiC,MAAM,kBAAkB,CAAC;AAgDzM;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAKxF;AAcD,uHAAuH;AACvH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAE5E;AAED,qBAAa,KAAM,SAAQ,KAAK;IAC9B,OAAgB,MAAM,EAAE,iBAAiB,CAMvC;IAEF,qDAAqD;IACrD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAM;IAE1C,uEAAuE;IACvE,IAAI,SAAM;IACV,0DAA0D;IAC1D,OAAO,SAAM;IACb,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,4GAA4G;IAC5G,KAAK,EAAE,IAAI,EAAE,CAAM;IACnB,wGAAwG;IACxG,MAAM,SAAM;IAEZ;;;OAGG;IACH,OAAsB,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBzD;IAED,qHAAqH;IAC5G,MAAM,CAAC,KAAK,EAAE,IAAI,GAAG,OAAO,CAGpC;IAEQ,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAavF;IAED,+GAA+G;IACtG,MAAM,IAAI,KAAK,CAEvB;IAED,6EAA6E;IAC7E,MAAM,IAAI,MAAM,CAuBf;IAED,8FAA8F;IAC9F,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEnC;IAED,2FAA2F;IAC3F,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEvC;IAED,8BAA8B;IAC9B,WAAW,IAAI,MAAM,CAEpB;IAED,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEtC;IAED,oIAAoI;IAC9H,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAO3C;IAED,kJAAkJ;IAC5I,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CASvD;IAED,6EAA6E;IAC7E,IAAI,IAAI,MAAM,CAEb;IAED,wEAAwE;IACxE,IAAI,IAAI,MAAM,CAEb;IAED,4GAA4G;IACtG,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoB5C;IAED,kGAAkG;YACpF,WAAW;IAgBzB,qGAAqG;YACvF,OAAO;IAQrB,wGAAwG;IACxG,OAAO,CAAC,KAAK;IAYb,sGAAsG;YACxF,IAAI;IAUlB,+HAA+H;IAC/H,OAAO,CAAC,KAAK;IAkBb,OAAO,CAAC,IAAI;CAIb"}
1
+ {"version":3,"file":"tutor.d.ts","sourceRoot":"","sources":["../../src/agent/tutor.ts"],"names":[],"mappings":"AAAA,6HAA6H;AAkB7H,OAAO,EAAE,KAAK,EAAiC,KAAK,IAAI,EAAE,KAAK,KAAK,EAAE,KAAK,iBAAiB,EAAE,KAAK,SAAS,EAAE,KAAK,KAAK,EAAoB,MAAM,kBAAkB,CAAC;AAErK,OAAO,EAA8J,KAAK,IAAI,EAAE,KAAK,KAAK,EAAe,MAAM,kBAAkB,CAAC;AAiDlO,qBAAa,KAAM,SAAQ,KAAK;IAC9B,OAAgB,MAAM,EAAE,iBAAiB,CAMvC;IAEF,qDAAqD;IACrD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAM;IAE1C,uEAAuE;IACvE,IAAI,SAAM;IACV,0DAA0D;IAC1D,OAAO,SAAM;IACb,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,4GAA4G;IAC5G,KAAK,EAAE,IAAI,EAAE,CAAM;IACnB,wGAAwG;IACxG,MAAM,SAAM;IACZ,4FAA4F;IAC5F,IAAI,SAAM;IAEV;;;OAGG;IACH,OAAsB,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzD;IAED,qHAAqH;IAC5G,MAAM,CAAC,KAAK,EAAE,IAAI,GAAG,OAAO,CAGpC;IAEQ,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAcvF;IAED,+GAA+G;IACtG,MAAM,IAAI,KAAK,CAEvB;IAED,6EAA6E;IAC7E,MAAM,IAAI,MAAM,CAsBf;IAED,8FAA8F;IAC9F,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEnC;IAED,2FAA2F;IAC3F,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEvC;IAED,8BAA8B;IAC9B,WAAW,IAAI,MAAM,CAEpB;IAED,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEtC;IAED,oIAAoI;IAC9H,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAO3C;IAED,kJAAkJ;IAC5I,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CASvD;IAED,6EAA6E;IAC7E,IAAI,IAAI,MAAM,CAEb;IAED,wEAAwE;IACxE,IAAI,IAAI,MAAM,CAEb;IAED,8FAA8F;IAC9F,OAAO,CAAC,UAAU;IAKlB,sIAAsI;IAChI,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAmC7E;IAED;;;OAGG;YACW,KAAK;IAuBnB,kGAAkG;YACpF,WAAW;IAgBzB,qGAAqG;YACvF,OAAO;IAQrB,wGAAwG;IACxG,OAAO,CAAC,KAAK;IAYb,sGAAsG;YACxF,IAAI;IAUlB,0IAA0I;IAC1I,OAAO,CAAC,KAAK;IAkBb,OAAO,CAAC,IAAI;CAIb"}