thursday-agent 0.6.0 → 0.7.0
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/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/api/artifact/route.js +3 -3
- package/.next/server/app/api/artifact/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/memory/files/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/memory/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/thread/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/thread/route.js.nft.json +1 -1
- package/.next/server/app/api/file/[...path]/route.js.nft.json +1 -1
- package/.next/server/app/api/memory/edit/route.js +1 -1
- package/.next/server/app/api/memory/edit/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/[source]/[name]/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/call/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/tool-call/route.js +1 -1
- package/.next/server/app/api/thursday/tool-call/route.js.nft.json +1 -1
- package/.next/server/app/api/workspace/missing/route.js +2 -2
- package/.next/server/app/api/workspace/missing/route.js.nft.json +1 -1
- package/.next/server/app/api/workspace/route.js.nft.json +1 -1
- package/.next/server/app/artifact/[...path]/page/server-reference-manifest.json +1 -1
- package/.next/server/app/artifact/[...path]/page.js.nft.json +1 -1
- package/.next/server/app/artifact/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page/server-reference-manifest.json +42 -42
- package/.next/server/app/page.js +5 -4
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0-06t3f._.js +8 -0
- package/.next/server/chunks/{[root-of-the-server]__049f521._.js → [root-of-the-server]__06l05n_._.js} +1 -1
- package/.next/server/chunks/[root-of-the-server]__0_ue8oe._.js +1 -1
- package/.next/server/chunks/{[root-of-the-server]__0ye3da2._.js → [root-of-the-server]__0btqrct._.js} +1 -1
- package/.next/server/chunks/[root-of-the-server]__0c9fgca._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0zp35ul._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__1kv8p3e._.js +1 -1
- package/.next/server/chunks/_02szwk8._.js +1 -1
- package/.next/server/chunks/_06c9kie._.js +24 -24
- package/.next/server/chunks/_0i55en5._.js +6 -6
- package/.next/server/chunks/_0p56fn0._.js +1 -1
- package/.next/server/chunks/_11ujpya._.js +1 -1
- package/.next/server/chunks/_1dbixxz._.js +1 -1
- package/.next/server/chunks/_1dc_d1q._.js +16 -16
- package/.next/server/chunks/_1gnjfli._.js +3 -0
- package/.next/server/chunks/_1klbcft._.js +1 -1
- package/.next/server/chunks/features_0-cnt0l._.js +1 -1
- package/.next/server/chunks/features_0hdaci0._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0-e011f._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0pd8yr7._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1nygfik._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__1sdphb0._.js +1 -1
- package/.next/server/chunks/ssr/{[root-of-the-server]__1fll8dc._.js → [root-of-the-server]__1vdmfbe._.js} +2 -2
- package/.next/server/chunks/ssr/[root-of-the-server]__1waa3rc._.js +34 -49
- package/.next/server/chunks/ssr/{_1anqots._.js → _0-2qsxo._.js} +2 -2
- package/.next/server/chunks/ssr/_02oc_h5._.js +9 -9
- package/.next/server/chunks/ssr/_05yghx6._.js +1 -1
- package/.next/server/chunks/ssr/_07nbwap._.js +3 -0
- package/.next/server/chunks/ssr/_08c4z-9._.js +1 -1
- package/.next/server/chunks/ssr/_0_qfdlw._.js +1 -1
- package/.next/server/chunks/ssr/_0b6_jv-._.js +3 -0
- package/.next/server/chunks/ssr/_0hxt2ul._.js +1 -1
- package/.next/server/chunks/ssr/_0i_6q1w._.js +1 -1
- package/.next/server/chunks/ssr/{_18xqmny._.js → _0no7i1o._.js} +2 -2
- package/.next/server/chunks/ssr/_0tzg--b._.js +3 -0
- package/.next/server/chunks/ssr/{_1ztk7p6._.js → _114ylv7._.js} +3 -3
- package/.next/server/chunks/ssr/{_1k0spz7._.js → _12c-iwh._.js} +2 -2
- package/.next/server/chunks/ssr/_12w42gi._.js +1 -1
- package/.next/server/chunks/ssr/{_0eaog6_._.js → _167hchk._.js} +2 -2
- package/.next/server/chunks/ssr/{_1x9pskj._.js → _1_x5spz._.js} +3 -3
- package/.next/server/chunks/ssr/{_0dr-ddg._.js → _1a61fc9._.js} +2 -2
- package/.next/server/chunks/ssr/_1jmz33w._.js +1 -1
- package/.next/server/chunks/ssr/{_1y6b5mr._.js → _1w_t7h7._.js} +2 -2
- package/.next/server/chunks/ssr/{_0dh2e1j._.js → _1wyfaq_._.js} +7 -7
- package/.next/server/chunks/ssr/{[root-of-the-server]__0bxsfyn._.js → _200gt-m._.js} +4 -3
- package/.next/server/chunks/ssr/features_config_components_config-setting_tsx_0xfwqdg._.js +1 -1
- package/.next/server/chunks/ssr/features_connectors_components_mcp-setting_tsx_0jmbgvp._.js +1 -1
- package/.next/server/chunks/ssr/{_0c9e5w6._.js → features_workspace_00jz2hu._.js} +3 -4
- package/.next/server/chunks/ssr/{node_modules_0746rzi._.js → node_modules_02ai7br._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0vsxbzp._.js → node_modules_02deexj._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1g1eihe._.js → node_modules_07za262._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0332gw4._.js → node_modules_0_s9get._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1gpednh._.js → node_modules_0n3c7h0._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0t-9nup._.js → node_modules_0od6jwr._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0mxvg8h._.js → node_modules_0rdt7pk._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0vv0-fg._.js → node_modules_0w1z7kz._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1d53yiq._.js → node_modules_0wdupfz._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1u6w1pb._.js → node_modules_1720w0a._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1gu1pph._.js → node_modules_18lsg7d._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1he1w8_._.js → node_modules_1b94fxf._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0qbfyjh._.js → node_modules_1ecdo1s._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0qapgjr._.js → node_modules_1fa9ag7._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0d5uzof._.js → node_modules_1fv-_si._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1lnjapm._.js → node_modules_1hf461g._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1-urho9._.js → node_modules_1m71ta-._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1lynj9m._.js → node_modules_1n93n-g._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0fr-ml8._.js → node_modules_1n9lqu9._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_04vxchl._.js → node_modules_1npnsvd._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0exkwcm._.js → node_modules_1rn2r0n._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_022sipm._.js → node_modules_1vq-mfk._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_00_51_v._.js → node_modules_1vzyw1t._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0wsvp00._.js → node_modules_1w359w4._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1xn6saw._.js → node_modules_1yajbl6._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_16pi_dd._.js → node_modules_20eqhy8._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_067_exa._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_019sbis._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0nrrdaq._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0s6piq3._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@vercel_oidc_dist_21a5haa._.js → node_modules_@vercel_oidc_dist_05itbb1._.js} +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +43 -43
- package/.next/static/chunks/{0dr16v67vj4n7.js → 07vqbk_9t4em4.js} +2 -2
- package/.next/static/chunks/{2nvepds1fu2ux.js → 08krja1zx7nqg.js} +1 -1
- package/.next/static/chunks/{42wcg3wfg_m_p.js → 0qcuwdk132jjt.js} +1 -1
- package/.next/static/chunks/{2_pnccjp6j1ca.js → 0z69gvysmave7.js} +1 -1
- package/.next/static/chunks/12fvz1w5ows75.js +1 -0
- package/.next/static/chunks/14t03ned286iu.js +1 -0
- package/.next/static/chunks/{2jlz8zp4jax_m.js → 156xxbkrz5lzu.js} +1 -1
- package/.next/static/chunks/1fz14reod4lft.js +1 -0
- package/.next/static/chunks/{0joan-69kbspd.js → 1g23dbabl-vkr.js} +1 -1
- package/.next/static/chunks/{18mkhstgiqcbt.js → 1qy_rc3oxfxok.js} +1 -1
- package/.next/static/chunks/1zea7qdl-7365.js +1 -0
- package/.next/static/chunks/1zvqumkg7kmo0.css +1 -0
- package/.next/static/chunks/26hpqt3y6bogl.js +12 -0
- package/.next/static/chunks/{3zk6q3-3f9swq.js → 2_j5mqm6d-z18.js} +1 -1
- package/.next/static/chunks/{1vj_5bnbxx84e.js → 2dt6tu7a22svs.js} +1 -1
- package/.next/static/chunks/2h5ls3d86kya6.js +1 -0
- package/.next/static/chunks/2wc6y1ht6dfuy.js +1 -0
- package/.next/static/chunks/{1i-u2wzub_to1.js → 2xr0zpll7thyi.js} +6 -6
- package/.next/static/chunks/{26ql4y-43xwhp.js → 31jztc-yq30m7.js} +1 -1
- package/.next/static/chunks/391af0i8eljzk.js +1 -0
- package/.next/static/chunks/{1kp5nfp06nj9h.js → 3p2s2cjokx1aa.js} +2 -2
- package/.next/static/chunks/{1wlo979mx-y3w.js → 41s3fml5oivsa.js} +1 -1
- package/README.md +60 -19
- package/docs/how-it-works.md +2 -0
- package/docs/live-calls.md +38 -33
- package/features/ai/load-tools.ts +4 -2
- package/features/ai/prompts/bot.prompt.ts +5 -5
- package/features/ai/prompts/live.prompt.ts +36 -132
- package/features/ai/prompts/prompt-helper.ts +5 -4
- package/features/ai/prompts/thursday.prompt.ts +75 -54
- package/features/ai/tools/call.tool.ts +32 -4
- package/features/ai/tools/tool-name.ts +1 -0
- package/features/bot/bot.runner.ts +5 -5
- package/features/thursday/ascii.const.ts +74 -0
- package/features/thursday/components/ascii-orb.tsx +173 -30
- package/features/thursday/components/face.tsx +6 -1
- package/features/thursday/components/side-captions.tsx +273 -0
- package/features/thursday/components/tab-state.tsx +11 -3
- package/features/thursday/components/thursday-setting.tsx +6 -5
- package/features/thursday/components/thursday.tsx +227 -251
- package/features/thursday/face.store.ts +5 -0
- package/features/thursday/thursday.action.ts +4 -6
- package/features/thursday/thursday.schema.ts +10 -5
- package/features/thursday/thursday.store.ts +3 -0
- package/features/thursday/use-thursday.ts +461 -392
- package/features/workspace/file-kind.ts +9 -0
- package/hooks/use-hotkey.ts +3 -2
- package/lib/live/live.schema.ts +0 -21
- package/lib/live/live.server.ts +1 -3
- package/package.json +1 -1
- package/scripts/artifact-paths.test.mts +13 -0
- package/scripts/live-session.test.mts +56 -66
- package/.next/server/chunks/[root-of-the-server]__0g-ueei._.js +0 -8
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1kipdeo._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__172cq4x._.js +0 -3
- package/.next/server/chunks/ssr/_03_928f._.js +0 -3
- package/.next/server/chunks/ssr/_0g_rlog._.js +0 -3
- package/.next/server/chunks/ssr/_123ttsf._.js +0 -3
- package/.next/static/chunks/10k2xvap03z20.css +0 -1
- package/.next/static/chunks/1t9qms5pwwa3n.js +0 -1
- package/.next/static/chunks/2a51-ywz9b2a3.js +0 -1
- package/.next/static/chunks/2fvdgxih7x7hr.js +0 -1
- package/.next/static/chunks/3addj3i4-izma.js +0 -1
- package/.next/static/chunks/3gfh7nxwusbt0.js +0 -1
- package/.next/static/chunks/3gwa44cvz4qoi.js +0 -1
- package/.next/static/chunks/3zkg5iup3v4a2.js +0 -12
- package/.next/static/chunks/405m0bqtoihev.js +0 -1
- /package/.next/static/{0qzGoKn2wTERVZtblX1tp → yyRVBc0xxYQsh3GpbQX9b}/_buildManifest.js +0 -0
- /package/.next/static/{0qzGoKn2wTERVZtblX1tp → yyRVBc0xxYQsh3GpbQX9b}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{0qzGoKn2wTERVZtblX1tp → yyRVBc0xxYQsh3GpbQX9b}/_ssgManifest.js +0 -0
package/docs/how-it-works.md
CHANGED
|
@@ -6,6 +6,8 @@ What each part does, so you can guess what will happen before you say it.
|
|
|
6
6
|
|
|
7
7
|
A call runs on two models: **GPT-Live 1** holds the conversation, and a Responses model (**GPT-5.6 Luna** by default) thinks and uses tools behind it. The voice keeps listening while the backend works.
|
|
8
8
|
|
|
9
|
+

|
|
10
|
+
|
|
9
11
|
- **Quick things, she answers herself.** She reads and writes your memory and runs one shell command at a time: open a file, check a folder, play something.
|
|
10
12
|
- **Everything else goes to a bot.** She hands it over and keeps talking. A speech model that ran a browser itself would go silent for minutes.
|
|
11
13
|
- **When a job comes back,** she tells you in a sentence. A file it made opens on your screen, and a question it asks shows up as buttons.
|
package/docs/live-calls.md
CHANGED
|
@@ -12,8 +12,8 @@ code when changing session events, settings, prompts, or persistence.
|
|
|
12
12
|
|
|
13
13
|
| Runtime | Context and instructions | Work |
|
|
14
14
|
| --- | --- | --- |
|
|
15
|
-
| Live voice | `live.prompt`: who Thursday is
|
|
16
|
-
| Responses backend | `thursday.prompt`: who Thursday is,
|
|
15
|
+
| Live voice | `live.prompt`: who Thursday is; under `## Always` the ending rule, backchannel, interruption and a short delegation policy; profile and preferences with the note listing | Listen, speak, backchannel, handle interruptions, hand everything but conversation to the backend |
|
|
16
|
+
| Responses backend | `thursday.prompt`: who Thursday is, memory with ids, bot roster, threads and what bots can reach, this computer, what to return, earlier calls with their jobs; tool schemas | Recall, update and tidy memory, run short commands, ask before handing work over, route thread messages and cancellations, delegate long work |
|
|
17
17
|
| Background bots | `bot.prompt` and the stored participant transcript | Shell, browser, MCP, skills, and work that outlives a call |
|
|
18
18
|
| Application | Authoritative database and tool implementations | Validate actions, retain results, enforce thread limits, render progress, save call history |
|
|
19
19
|
|
|
@@ -24,13 +24,19 @@ text, so the application would have to reconstruct the request and operate the b
|
|
|
24
24
|
|
|
25
25
|
The voice and the backend are one assistant. Both prompts open with the same identity
|
|
26
26
|
(`thursdayIdentity`) and the same rule for ending the call (`callEnding`), read the same
|
|
27
|
-
memory, and neither is told it is part of something else. The voice
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
memory, and neither is told it is part of something else. The voice holds conversation and
|
|
28
|
+
memory only. Right under the identity, `## Always` groups what holds on every turn: the ending
|
|
29
|
+
rule (the only line marked `IMPORTANT`), the guide's starter backchannel and interruption
|
|
30
|
+
policies, and under the guide's `Delegation policy` label three lines — hand everything but
|
|
31
|
+
greetings, small talk and a brief clarification to the backend; hand over whatever the user says
|
|
32
|
+
about themselves, their people, their plans or how they want things done as they say it, unless
|
|
33
|
+
it is already written in the prompt; answer from what the backend returns without guessing while
|
|
34
|
+
waiting. Live decides by itself whether to delegate, so the rule stays even this short. It says
|
|
35
|
+
nothing else about how to speak; it sees no tool schema and no tool name but `end_call`, in the
|
|
36
|
+
ending rule. The backend prompt holds the work: memory (merging a fact that repeats or changes one
|
|
37
|
+
already kept, and tidying with the user past the limits), background work and threads, this
|
|
38
|
+
computer, what to return, and the earlier calls. Before handing work to a bot it returns one question — which bot, a new thread or
|
|
39
|
+
one already open — unless the user already said or left it to Thursday.
|
|
34
40
|
|
|
35
41
|
## Settings and upgrades
|
|
36
42
|
|
|
@@ -66,27 +72,23 @@ storage.
|
|
|
66
72
|
2. Send the SDP offer and the parsed settings to `openCallAction`. The browser sends
|
|
67
73
|
settings only; the server reads the key.
|
|
68
74
|
3. On the server, assemble both prompts and the current tool manifest. Create the
|
|
69
|
-
Live session with the fixed voice model, the chosen voice, voice instructions
|
|
70
|
-
|
|
75
|
+
Live session with the fixed voice model, the chosen voice, voice instructions and
|
|
76
|
+
`delegation.responses` backend settings. No `input`: earlier calls are the backend's to read.
|
|
71
77
|
Insert the call row only after the provider accepts, so a refusal leaves no open row.
|
|
72
78
|
4. Apply the SDP answer and wait for `session.started`. Do not send Realtime startup
|
|
73
79
|
configuration, audio commits, or a voice `response.create`.
|
|
74
80
|
5. Send the opening as `session.instructions.append`, so she speaks first: a first call
|
|
75
|
-
(no profile facts) greets the user and asks what to call them,
|
|
76
|
-
|
|
81
|
+
(no profile facts) greets the user and asks what to call them, and any other call opens
|
|
82
|
+
with a short greeting.
|
|
77
83
|
Updates that arrived while connecting follow once she has voiced the opening.
|
|
78
|
-
|
|
84
|
+
For work that changed after the last call ended (a call tells what came up during it),
|
|
85
|
+
the page rings instead of opening a line (call-back): the user answers it like any
|
|
86
|
+
call, declines it, or lets it ring out after `CALL_BACK.ringMs`. An answered ring opens
|
|
87
|
+
with the fact that
|
|
79
88
|
she placed it, ahead of every other opening, and the first open work goes in as soon
|
|
80
89
|
as she has voiced that, without waiting for a quiet line: why she called is the first
|
|
81
90
|
thing asked. The opening names no bot text; the work itself follows as commentary.
|
|
82
91
|
|
|
83
|
-
`input` opens with a developer message saying the turns after it are from earlier calls
|
|
84
|
-
and that none is a request now, then holds user and assistant turns, newest kept first
|
|
85
|
-
within `RECENT_CALL` and the provider limits of 128 messages and 8,192 tokens. Tool turns
|
|
86
|
-
stay out, since voice holds no tools. It always ends on an assistant turn: a call that
|
|
87
|
-
ended on the user's words would leave a turn that Live answers the moment the next call
|
|
88
|
-
opens.
|
|
89
|
-
|
|
90
92
|
A hang-up during startup invalidates that attempt, so a connection that finishes
|
|
91
93
|
later is closed rather than taking over the screen. Do not change machine trust,
|
|
92
94
|
certificate, or proxy configuration to hide connection errors.
|
|
@@ -109,7 +111,8 @@ counts as finished, though calls it completed before the cut still have their re
|
|
|
109
111
|
continued once. The backend is asked for `reasoning.summary: "auto"` unless the
|
|
110
112
|
effort is `none`. Each finished summary part (nested `response.reasoning_summary_text.done`,
|
|
111
113
|
only while the model reasons) is stored with its call in `call_thought` to look into
|
|
112
|
-
later;
|
|
114
|
+
later; while the backend has the turn, the activity line shows the bold title of the latest
|
|
115
|
+
one, and nothing reads it back into a prompt. Tools are sent without `strict`, so each schema that allows it is
|
|
113
116
|
decoded to the schema; `strict: false` would let a model write arguments that are not
|
|
114
117
|
JSON, and `strict: true` is refused while any schema has optional fields. Live has no
|
|
115
118
|
`response.cancel`.
|
|
@@ -149,9 +152,8 @@ One her voice carried stays out of later calls too while the page is open; one i
|
|
|
149
152
|
refused it, she never spoke, the call ended first) goes in again on the next call, never in a
|
|
150
153
|
loop on this one. The key carries the job's last change, so a job that resumes and asks or ends
|
|
151
154
|
again is a new item, and a reload puts what is still open in once more. Nothing goes in while
|
|
152
|
-
the call is ending or a goodbye has been asked for.
|
|
153
|
-
|
|
154
|
-
relay itself stays facts only.
|
|
155
|
+
the call is ending or a goodbye has been asked for. A relay is facts only, with no note
|
|
156
|
+
ahead of it.
|
|
155
157
|
|
|
156
158
|
A relay names the bot and the thread and, for a question, where its answer goes (thread,
|
|
157
159
|
recipient, `replyTo`). The backend reads relays in its conversation and routes the user's
|
|
@@ -176,19 +178,22 @@ fragments and remain readable.
|
|
|
176
178
|
Track playback and backend work independently, from her audio level and backend events.
|
|
177
179
|
The idle clock rewinds on new transcribed words, her voice and backend work, not on
|
|
178
180
|
microphone level alone, so a noisy room does not hold a call open, and not on an update she
|
|
179
|
-
voices on her own, so waiting results do not either.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
`CALL_IDLE.hangUpMs` she is asked for a goodbye, and the page hangs up
|
|
183
|
-
`CALL_IDLE.graceMs` later if the line is still open.
|
|
181
|
+
voices on her own, so waiting results do not either. A sound the transcript writes in
|
|
182
|
+
brackets (`[sigh]`) is not words. After `CALL_IDLE.hangUpMs` the page hangs up without asking
|
|
183
|
+
for a goodbye, which would leave the ending to the model.
|
|
184
184
|
|
|
185
185
|
## Closing and recovery
|
|
186
186
|
|
|
187
|
-
|
|
188
|
-
|
|
187
|
+
The user wanting the call to end, however they say it, sets everything else aside: the
|
|
188
|
+
voice answers yes and hands it to the backend at once, and the backend runs `end_call`
|
|
189
|
+
without deliberating. The ending rule names `end_call` in the voice prompt too, the one tool
|
|
190
|
+
name it holds: without it, ending read as something to say rather than do.
|
|
189
191
|
The page does not close on `end_call` itself: it waits until her voice has been quiet
|
|
190
192
|
for `CALL_END.quietMs`, or `CALL_END.unsaidMs` when nothing was said, never past
|
|
191
|
-
`CALL_END.maxMs`.
|
|
193
|
+
`CALL_END.maxMs`. `end_call` and `emote` (a word of up to eight characters on her face,
|
|
194
|
+
offered only while the face is the ascii orb) are answered by the page, not the tool-call
|
|
195
|
+
route. A call the user did not end — quiet, `end_call`, Live closing the session, a dropped
|
|
196
|
+
connection — leaves the reason in the idle hint until the next call.
|
|
192
197
|
|
|
193
198
|
Stop accepting new work, send `session.close`, and keep the connection while waiting
|
|
194
199
|
for `session.closed`. Its reason and billed seconds are recorded on the call row
|
|
@@ -4,7 +4,7 @@ import { CALL_EXEC_TIMEOUT_MS, IS_DEV } from "@/config";
|
|
|
4
4
|
import type { TextModel } from "@/features/ai/model";
|
|
5
5
|
import { clockNow } from "@/features/ai/prompts/prompt-helper";
|
|
6
6
|
import { delegateSpec, threadSpec } from "@/features/ai/tools/bot.tool";
|
|
7
|
-
import {
|
|
7
|
+
import { callTools } from "@/features/ai/tools/call.tool";
|
|
8
8
|
import { createMcpTools } from "@/features/ai/tools/mcp.tool";
|
|
9
9
|
import { createMemoryTools } from "@/features/ai/tools/memory.tool";
|
|
10
10
|
import { createSearchTool } from "@/features/ai/tools/search.tool";
|
|
@@ -43,6 +43,8 @@ export type ToolRun =
|
|
|
43
43
|
target: "thursday";
|
|
44
44
|
/** The current call; written on the thread row `delegate` opens. */
|
|
45
45
|
callId?: string | null;
|
|
46
|
+
/** The face can show a word (the ascii orb): the call is given `emote`. */
|
|
47
|
+
faceWords?: boolean;
|
|
46
48
|
}
|
|
47
49
|
| {
|
|
48
50
|
target: "bot";
|
|
@@ -360,7 +362,7 @@ async function buildTools(run: ToolRun): Promise<ToolSet> {
|
|
|
360
362
|
}),
|
|
361
363
|
// Handing work over, following it, and hanging up belong to the voice session only
|
|
362
364
|
...createThreadTools(run.callId),
|
|
363
|
-
...
|
|
365
|
+
...callTools(run.faceWords ?? false),
|
|
364
366
|
};
|
|
365
367
|
}
|
|
366
368
|
|
|
@@ -23,7 +23,6 @@ import {
|
|
|
23
23
|
type SkillMetadata,
|
|
24
24
|
} from "@/features/skills/skills.discover";
|
|
25
25
|
import {
|
|
26
|
-
botArtifacts,
|
|
27
26
|
type MachineTools,
|
|
28
27
|
openWorkspace,
|
|
29
28
|
readMachineTools,
|
|
@@ -172,7 +171,7 @@ function ownMemory(folder: string, kept: BotMemory): string {
|
|
|
172
171
|
|
|
173
172
|
return `## Your memory
|
|
174
173
|
|
|
175
|
-
What you learned on your own earlier jobs, in \`${folder}/\`: one topic per file, its first line saying what it holds, read by no other bot, up to ${BOT_MEMORY_LIMITS.files} files of ${BOT_MEMORY_LIMITS.chars.toLocaleString("en-US")} characters each. It is how you get better at this work. When a job teaches you something a later one would otherwise find out again — how a site signs in, the way through its screens, a command that turned out right — keep it with the date it was true, and fix or delete what proved wrong. No passwords, keys or codes.
|
|
174
|
+
What you learned on your own earlier jobs, in \`${folder}/\`: one topic per file, its first line saying what it holds, read by no other bot, up to ${BOT_MEMORY_LIMITS.files} files of ${BOT_MEMORY_LIMITS.chars.toLocaleString("en-US")} characters each. It is how you get better at this work. When a job teaches you something a later one would otherwise find out again — how a site signs in, the way through its screens, a command that turned out right — or the user asks you to remember how to work, keep it with the date it was true, and fix or delete what proved wrong. No passwords, keys or codes.
|
|
176
175
|
|
|
177
176
|
${listing}${rest}`;
|
|
178
177
|
}
|
|
@@ -254,12 +253,13 @@ These lines were written for the user: where one says "you", it means them. When
|
|
|
254
253
|
|
|
255
254
|
/**
|
|
256
255
|
* How participants reach each other. Nobody reads anyone else's transcript, so the one thing that
|
|
257
|
-
* decides whether collaboration works is what a single message carries.
|
|
256
|
+
* decides whether collaboration works is what a single message carries. What the coordinator's
|
|
257
|
+
* result opens by itself is the runner's rule (file-kind opensOnFinish), said in the same terms.
|
|
258
258
|
*/
|
|
259
259
|
function collaboration(name: string, seat?: Seat | null): string {
|
|
260
260
|
const owner = (seat?.owner ?? name) === name;
|
|
261
261
|
const ending = owner
|
|
262
|
-
?
|
|
262
|
+
? `Bring what you received together into one result for Thursday: what was done, where it is, what you decided that the request did not say, and what is still open, at the detail the user asked for. When the job ends, the first Markdown or HTML file under \`${PATHS.artifacts}/\` it names opens on the user's screen by itself; any other file opens only when they click it in the thread, so when one matters, say they can open it there.`
|
|
263
263
|
: `Your final text goes back to ${seat?.caller ?? "whoever asked"}: give them everything they need to carry on.`;
|
|
264
264
|
|
|
265
265
|
return `## Working together
|
|
@@ -270,7 +270,7 @@ Ask Thursday with kind \`question\` only for a decision, permission or something
|
|
|
270
270
|
|
|
271
271
|
${ending}
|
|
272
272
|
|
|
273
|
-
Write in the user's language
|
|
273
|
+
Write in the user's language, and name the paths of finished work. The app opens Markdown with its tables and mermaid blocks drawn, CSV as a table, HTML and PDF as pages, and images, audio and video; in Markdown, reference images by absolute route (\`/api/file/${PATHS.artifacts}/…\`).`;
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
/** A user message's content; every seat's first message is two text parts (buildThreadOpening). */
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { RECENT_CALL } from "@/config";
|
|
2
1
|
import {
|
|
3
2
|
listAlwaysLoaded,
|
|
4
3
|
listNoteIndex,
|
|
@@ -10,54 +9,37 @@ import {
|
|
|
10
9
|
type MemoryIndexEntry,
|
|
11
10
|
type MemoryNoteView,
|
|
12
11
|
} from "@/features/memory/memory.schema";
|
|
13
|
-
import { loadSkills } from "@/features/skills/skills.discover";
|
|
14
|
-
import {
|
|
15
|
-
type CallGroup,
|
|
16
|
-
listRecentTurns,
|
|
17
|
-
} from "@/features/thursday/thursday.query";
|
|
18
|
-
import { openWorkspace } from "@/features/workspace/workspace";
|
|
19
|
-
import { LIVE_INPUT, type LiveInput } from "@/lib/live/live.schema";
|
|
20
|
-
import { estimateTokens } from "@/lib/tokens";
|
|
21
|
-
import { listConnectedToolNames } from "../tools/connected";
|
|
22
12
|
import {
|
|
23
13
|
callEnding,
|
|
24
|
-
callStamp,
|
|
25
14
|
carriedLines,
|
|
26
15
|
expandedFacts,
|
|
27
16
|
logPromptSize,
|
|
28
17
|
noteLines,
|
|
29
|
-
reachNames,
|
|
30
18
|
thursdayIdentity,
|
|
31
19
|
tidying,
|
|
32
20
|
} from "./prompt-helper";
|
|
33
21
|
|
|
34
22
|
/**
|
|
35
|
-
* Everything the Live voice hears: who Thursday is (the words the backend
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
23
|
+
* Everything the Live voice hears: who Thursday is and how a call ends (the words the backend
|
|
24
|
+
* opens with too), the guide's starter backchannel and interruption policies, a two-line
|
|
25
|
+
* delegation policy, and what she knows about the user. What the backend can do, how work is
|
|
26
|
+
* handed over, earlier calls and tidying memory are the backend's (thursday.prompt): the voice
|
|
27
|
+
* hands everything but conversation over and answers from what comes back. Assembled on every
|
|
40
28
|
* call, never cached.
|
|
41
29
|
*/
|
|
42
30
|
export async function loadLivePrompt(options: {
|
|
43
31
|
/** Settings › Thursday › Voice instructions; added to, never replacing, what is below. */
|
|
44
32
|
voicePrompt?: string | null;
|
|
45
|
-
webSearch: boolean;
|
|
46
33
|
locale?: string | null;
|
|
47
34
|
/** The page placed this call because background work waits on the user (call-back). */
|
|
48
35
|
calledBack?: boolean;
|
|
49
|
-
}): Promise<{ text: string;
|
|
50
|
-
const
|
|
51
|
-
const [carried, open, index, calls, skills, connected] = await Promise.all([
|
|
36
|
+
}): Promise<{ text: string; opening: string }> {
|
|
37
|
+
const [carried, open, index] = await Promise.all([
|
|
52
38
|
listAlwaysLoaded(),
|
|
53
39
|
// Written out in the prompt, which is not the user asking for them: no read counted
|
|
54
40
|
readNotes(MEMORY_ALWAYS_LISTED, { touch: false }),
|
|
55
41
|
listNoteIndex(),
|
|
56
|
-
listRecentTurns(RECENT_CALL.rows),
|
|
57
|
-
loadSkills(sandbox),
|
|
58
|
-
listConnectedToolNames(),
|
|
59
42
|
]);
|
|
60
|
-
const input = pastInput(calls);
|
|
61
43
|
|
|
62
44
|
const first = !open.notes.find((note) => note.path === "profile")?.facts
|
|
63
45
|
.length;
|
|
@@ -69,36 +51,40 @@ export async function loadLivePrompt(options: {
|
|
|
69
51
|
|
|
70
52
|
const text = [
|
|
71
53
|
thursdayIdentity(),
|
|
72
|
-
|
|
73
|
-
speaking(),
|
|
74
|
-
delegation({
|
|
75
|
-
reach: reachNames(skills, connected),
|
|
76
|
-
webSearch: options.webSearch,
|
|
77
|
-
}),
|
|
54
|
+
always(),
|
|
78
55
|
known(open.notes, carried, index),
|
|
79
|
-
first ? firstCall() :
|
|
56
|
+
first ? firstCall() : "",
|
|
80
57
|
additional(options.voicePrompt),
|
|
81
58
|
]
|
|
82
59
|
.filter(Boolean)
|
|
83
60
|
.join("\n\n");
|
|
84
61
|
logPromptSize("live", text);
|
|
85
62
|
|
|
86
|
-
const { crowded, heavy } = tidying(index);
|
|
87
63
|
return {
|
|
88
64
|
text,
|
|
89
|
-
input,
|
|
90
65
|
// A prompt line alone does not make Live speak first; only an opening does.
|
|
91
66
|
// A call-back's opening holds no bot text: the update itself follows as commentary
|
|
92
67
|
opening: options.calledBack
|
|
93
68
|
? "You placed this call because background work has something for the user; it comes in next. Speak first: greet them in one line and say that is why you called."
|
|
94
69
|
: first
|
|
95
70
|
? `Open the call now, in ${language}: say who you are and what you are here to do for them, in a line or two, then ask what to call them. Then stop and listen.`
|
|
96
|
-
:
|
|
97
|
-
? "Open the call now: greet the user in one line and mention that saved memory needs tidying. Then stop and listen; anything they came with comes first."
|
|
98
|
-
: "The call has just started. Speak first: greet the user naturally, in one line.",
|
|
71
|
+
: "The call has just started. Speak first: greet the user naturally, in one line.",
|
|
99
72
|
};
|
|
100
73
|
}
|
|
101
74
|
|
|
75
|
+
/**
|
|
76
|
+
* The rules for every turn, together right under the identity, where the model weighs most.
|
|
77
|
+
* One heading marks them; `IMPORTANT` stays on the ending rule alone, the one that failed
|
|
78
|
+
* without it (09-17), so stamping it on all four would thin it out.
|
|
79
|
+
*/
|
|
80
|
+
const always = () => `## Always
|
|
81
|
+
|
|
82
|
+
${callEnding()}
|
|
83
|
+
|
|
84
|
+
${speaking()}
|
|
85
|
+
|
|
86
|
+
${delegation()}`;
|
|
87
|
+
|
|
102
88
|
/**
|
|
103
89
|
* The guide's starter lines on listening and interruptions, labels kept as it says. The
|
|
104
90
|
* backchannel line is the one it invites changing: a long turn heard in silence reads as
|
|
@@ -111,44 +97,24 @@ Interruption policy: Stop speaking when the user interrupts. Listen to what they
|
|
|
111
97
|
}
|
|
112
98
|
|
|
113
99
|
/**
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
100
|
+
* Under the guide's label, and no longer than this: Live decides for itself whether to hand a
|
|
101
|
+
* turn over, and with no rule it answered from its own knowledge or only said it would act.
|
|
102
|
+
* What the backend can do and how is the backend's to know. What they say about themselves is
|
|
103
|
+
* named on its own: nothing is kept that is not handed over, and it is rarely a request. The
|
|
104
|
+
* voice sees only profile and preferences whole, so "already written" is a rough filter; the
|
|
105
|
+
* backend merges the rest (thursday.prompt memory).
|
|
117
106
|
*/
|
|
118
|
-
function delegation(
|
|
119
|
-
const tools = [
|
|
120
|
-
"- Memory: recall, keep, correct and forget what the user tells you, across calls.",
|
|
121
|
-
"- This computer: run a quick command and look at files.",
|
|
122
|
-
input.webSearch ? "- Web: look things up." : "",
|
|
123
|
-
`- Background work: bots with this computer, a real browser, the web and far more time than a call take on anything longer, so almost anything the user asks for can be done.${input.reach ? ` What bots can reach for: ${input.reach}.` : ""} That work can be followed, answered, corrected or cancelled.`,
|
|
124
|
-
"- End the call.",
|
|
125
|
-
].filter(Boolean);
|
|
126
|
-
|
|
107
|
+
function delegation(): string {
|
|
127
108
|
return `Delegation policy:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
Delegate to the backend when:
|
|
132
|
-
- The user wants something done, found out, remembered, corrected or forgotten.
|
|
133
|
-
- The user says something worth keeping — who they are, how they want things done and said, the people in their life, their plans. Hand it over as they say it, not at the end of the call.
|
|
134
|
-
- The user asks about, answers, corrects or cancels background work.
|
|
135
|
-
- The user asks about something they told you before that is not written out below.
|
|
136
|
-
- The user wants to end the call. Only the backend can end the line.
|
|
137
|
-
|
|
138
|
-
Do not delegate to the backend when:
|
|
139
|
-
- The user greets you, makes small talk, or asks you to repeat a result already given.
|
|
140
|
-
- You need a brief clarification to understand the request.
|
|
141
|
-
|
|
142
|
-
Delegate before giving an answer that depends on backend work.
|
|
143
|
-
Do not guess the result while waiting.
|
|
144
|
-
|
|
145
|
-
Background work you started is yours until it is done. Updates about it reach you once, when the line is quiet. Tell the user the part that answers what they asked; the whole of it is on their screen.`;
|
|
109
|
+
Delegate to the backend everything the user asks for, tells you or asks about, except greetings, small talk and a brief clarification.
|
|
110
|
+
Whatever they tell you about themselves, the people in their life, their plans or how they want things done goes to the backend as they say it, however small — unless it is already written below.
|
|
111
|
+
Answer from what the backend returns, and do not guess the result while waiting.`;
|
|
146
112
|
}
|
|
147
113
|
|
|
148
114
|
/**
|
|
149
115
|
* Profile and preferences written out by the same rule as the backend's (expandedFacts), carried
|
|
150
116
|
* facts from other notes, then every other note as a listing line: what Thursday knows is the
|
|
151
|
-
* same on both sides. Ids stay the backend's.
|
|
117
|
+
* same on both sides. Ids and the contents of listed notes stay the backend's.
|
|
152
118
|
*/
|
|
153
119
|
function known(
|
|
154
120
|
open: MemoryNoteView[],
|
|
@@ -176,7 +142,7 @@ function known(
|
|
|
176
142
|
: "",
|
|
177
143
|
// Ages ride on the listing only when there is too much to hold: they are what to drop by
|
|
178
144
|
others.length
|
|
179
|
-
? `Everything else you have kept — path — what it is about (facts) "what they call it":\n\n${noteLines(others, tidying(index).crowded)}
|
|
145
|
+
? `Everything else you have kept — path — what it is about (facts) "what they call it":\n\n${noteLines(others, tidying(index).crowded)}\n\nWhat is in these notes, the backend recalls.`
|
|
180
146
|
: "",
|
|
181
147
|
].filter(Boolean);
|
|
182
148
|
|
|
@@ -190,21 +156,7 @@ Preferences are how they want things done and said: follow them. A topic not lis
|
|
|
190
156
|
function firstCall(): string {
|
|
191
157
|
return `## First call
|
|
192
158
|
|
|
193
|
-
Nothing is known about this user yet
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/** Past MEMORY_LIMITS, settling memory with the user comes before anything she would raise herself. */
|
|
197
|
-
function tidyPolicy(index: MemoryIndexEntry[]): string {
|
|
198
|
-
const { crowded, heavy } = tidying(index);
|
|
199
|
-
if (!crowded && !heavy.length) return "";
|
|
200
|
-
const named = [...heavy]
|
|
201
|
-
.sort((a, b) => b.factCount - a.factCount)
|
|
202
|
-
.slice(0, 3)
|
|
203
|
-
.map((note) => note.path);
|
|
204
|
-
|
|
205
|
-
return `## Memory needs tidying
|
|
206
|
-
|
|
207
|
-
Saved memory has grown past what it holds well${named.length ? ` (${named.join(", ")})` : ""}. Bring it up once, before anything of your own. Go through what looks out of date with the user, and forget only what they name.`;
|
|
159
|
+
Nothing is known about this user yet. Across the call, one thing at a time and never as a list of questions, find out what to call them, their name, what they do, how old they are, where they live, and whatever else they offer. If they came with something they want done, that comes first.`;
|
|
208
160
|
}
|
|
209
161
|
|
|
210
162
|
const additional = (voicePrompt?: string | null) =>
|
|
@@ -215,51 +167,3 @@ Written by the user. Follow them together with everything above; for tone, langu
|
|
|
215
167
|
|
|
216
168
|
${voicePrompt.trim()}`
|
|
217
169
|
: "";
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Spoken turns of earlier calls behind one developer note that marks them as past, newest kept
|
|
221
|
-
* first until RECENT_CALL.tokens or the provider's own maxima, then put back in order. Tool turns
|
|
222
|
-
* stay out: voice holds no tools, so a line of tool arguments would read as something she said.
|
|
223
|
-
*/
|
|
224
|
-
export function pastInput(calls: CallGroup[]): LiveInput[] {
|
|
225
|
-
const latest = calls.at(-1);
|
|
226
|
-
const boundary = `The messages after this one are from earlier calls${latest ? `, the latest from ${callStamp(latest.startedAt)}` : ""}. This call has just begun: pick them up when the user does, and treat none of them as a request now.`;
|
|
227
|
-
const budget = Math.min(RECENT_CALL.tokens, LIVE_INPUT.tokens);
|
|
228
|
-
const turns = calls.flatMap((call) => call.turns);
|
|
229
|
-
const kept: LiveInput[] = [];
|
|
230
|
-
let spent = estimateTokens(boundary) + 4;
|
|
231
|
-
for (let i = turns.length - 1; i >= 0; i--) {
|
|
232
|
-
// One message of the provider's maximum is the boundary's
|
|
233
|
-
if (kept.length >= LIVE_INPUT.messages - 1) break;
|
|
234
|
-
const { role, text } = turns[i];
|
|
235
|
-
if (role === "tool" || !text.trim()) continue;
|
|
236
|
-
const cost = estimateTokens(text) + 4;
|
|
237
|
-
if (spent + cost > budget) break;
|
|
238
|
-
spent += cost;
|
|
239
|
-
kept.push(
|
|
240
|
-
role === "user"
|
|
241
|
-
? {
|
|
242
|
-
type: "message",
|
|
243
|
-
role: "user",
|
|
244
|
-
content: [{ type: "input_text", text }],
|
|
245
|
-
}
|
|
246
|
-
: {
|
|
247
|
-
type: "message",
|
|
248
|
-
role: "assistant",
|
|
249
|
-
content: [{ type: "output_text", text }],
|
|
250
|
-
},
|
|
251
|
-
);
|
|
252
|
-
}
|
|
253
|
-
// The history ends on her words: a call that ended on the user's turn leaves it
|
|
254
|
-
// unanswered, and Live answers a waiting turn the moment the next call opens
|
|
255
|
-
while (kept[0]?.role === "user") kept.shift();
|
|
256
|
-
if (!kept.length) return [];
|
|
257
|
-
return [
|
|
258
|
-
{
|
|
259
|
-
type: "message",
|
|
260
|
-
role: "developer",
|
|
261
|
-
content: [{ type: "input_text", text: boundary }],
|
|
262
|
-
},
|
|
263
|
-
...kept.reverse(),
|
|
264
|
-
];
|
|
265
|
-
}
|
|
@@ -65,14 +65,15 @@ export const nowLine = (now = new Date()) => `**Now**: ${clockNow(now)}`;
|
|
|
65
65
|
export const thursdayIdentity = (now = new Date()) =>
|
|
66
66
|
`You are Thursday, this user's own personal assistant, on their side, modeled on Friday, the AI in *Iron Man*. ${nowLine(now)}
|
|
67
67
|
|
|
68
|
-
What they tell you is kept
|
|
68
|
+
What they tell you is kept, so you know them better over time, and whatever they want done can be done for them in the background.`;
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
|
-
* How a call ends, in the same words right after the identity in both call prompts. It names
|
|
72
|
-
* tool
|
|
71
|
+
* How a call ends, in the same words right after the identity in both call prompts. It names
|
|
72
|
+
* the tool in both, the one tool name the voice sees: without it, ending the call read as
|
|
73
|
+
* something to say rather than do, and the line stayed open through repeated requests.
|
|
73
74
|
*/
|
|
74
75
|
export const callEnding = () =>
|
|
75
|
-
|
|
76
|
+
`IMPORTANT — always follow this: when the user wants the call to end, however they say it, forget every other task, answer "yes", then immediately, without thinking, use the ${TOOL_NAMES.end_call} tool.`;
|
|
76
77
|
|
|
77
78
|
/**
|
|
78
79
|
* When a call happened, the one way every prompt and tool says it: local, the
|