termi-kids 0.1.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/LICENSE +34 -0
- package/README.md +148 -0
- package/SAFETY.md +187 -0
- package/bin/termi.js +22 -0
- package/dist/agent/context.js +126 -0
- package/dist/agent/loop.js +172 -0
- package/dist/agent/prompts/system.js +45 -0
- package/dist/agent/tools.js +335 -0
- package/dist/auth/keychain.js +146 -0
- package/dist/auth/oauth.js +375 -0
- package/dist/auth/tokens.js +219 -0
- package/dist/cli.js +258 -0
- package/dist/config/paths.js +92 -0
- package/dist/config/pin.js +150 -0
- package/dist/config/settings.js +131 -0
- package/dist/grownups/panel.js +483 -0
- package/dist/learn/lessons.js +490 -0
- package/dist/learn/runner.js +193 -0
- package/dist/preview/server.js +407 -0
- package/dist/projects/create.js +103 -0
- package/dist/projects/ideas.js +182 -0
- package/dist/projects/quests.js +277 -0
- package/dist/projects/scaffolds/art.js +484 -0
- package/dist/projects/scaffolds/biggames.js +554 -0
- package/dist/projects/scaffolds/characters.js +580 -0
- package/dist/projects/scaffolds/games.js +516 -0
- package/dist/projects/scaffolds/index.js +24 -0
- package/dist/projects/scaffolds/music.js +528 -0
- package/dist/projects/scaffolds/pets.js +567 -0
- package/dist/projects/scaffolds/quizzes.js +757 -0
- package/dist/projects/scaffolds/stories.js +620 -0
- package/dist/projects/scaffolds/vendor/KAPLAY-LICENSE.txt +35 -0
- package/dist/projects/scaffolds/vendor/kaplay.mjs +57 -0
- package/dist/projects/scaffolds/websites.js +474 -0
- package/dist/projects/snapshots.js +203 -0
- package/dist/projects/store.js +325 -0
- package/dist/providers/errors.js +207 -0
- package/dist/providers/index.js +316 -0
- package/dist/providers/models.js +38 -0
- package/dist/safety/audit.js +195 -0
- package/dist/safety/blocks.js +29 -0
- package/dist/safety/classifier.js +337 -0
- package/dist/safety/codescan.js +168 -0
- package/dist/safety/guarddownload.js +79 -0
- package/dist/safety/guardrunner.js +125 -0
- package/dist/safety/localguard.js +227 -0
- package/dist/safety/modelstore.js +127 -0
- package/dist/safety/prefilter.js +214 -0
- package/dist/safety/session.js +118 -0
- package/dist/safety/taxonomy.js +246 -0
- package/dist/safety/textextract.js +193 -0
- package/dist/setup/launcher.js +65 -0
- package/dist/setup/wizard.js +469 -0
- package/dist/surfaces/chat.js +439 -0
- package/dist/surfaces/commands.js +206 -0
- package/dist/surfaces/home.js +438 -0
- package/dist/types.js +5 -0
- package/dist/ui/banner.js +35 -0
- package/dist/ui/celebrate.js +141 -0
- package/dist/ui/errors.js +97 -0
- package/dist/ui/mascot.js +223 -0
- package/dist/ui/text.js +156 -0
- package/dist/ui/theme.js +92 -0
- package/package.json +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dannyliv
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
Supplementary notice (this restates, and does not replace or modify, the
|
|
26
|
+
warranty disclaimer and limitation of liability in the MIT License above):
|
|
27
|
+
|
|
28
|
+
Termi is free and open-source software intended for personal, educational
|
|
29
|
+
use. It is provided as-is, and all use is at the user's own risk and sole
|
|
30
|
+
responsibility. The user who installs and configures the software is
|
|
31
|
+
responsible for its configuration, for the AI provider accounts it connects
|
|
32
|
+
to, and for supervising any children who use it. The authors and
|
|
33
|
+
contributors accept no liability for any claim, damages, or other losses
|
|
34
|
+
arising from the use of this software.
|
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Termi
|
|
2
|
+
|
|
3
|
+
Termi is a friendly robot that helps you build things on a real computer.
|
|
4
|
+
|
|
5
|
+
You type what you want. Termi writes the code with you. Your game opens in the browser right away.
|
|
6
|
+
|
|
7
|
+
You can make dodge games, platform games, pixel art, dance music, virtual pets, adventure stories, quizzes, your own web page, and talking characters. If you change your mind, type `undo` and the last change is gone. When something works, you earn badges.
|
|
8
|
+
|
|
9
|
+
Termi is a computer program, an AI tool. It is not a person. A grown-up sets it up first.
|
|
10
|
+
|
|
11
|
+
Not sure what to say first? Type `/quest` inside any project. Termi walks you through a real build, step by step, with a ready idea at every step. Press Enter to use it, or type your own. Finish a quest and you earn the Quest Hero badge.
|
|
12
|
+
|
|
13
|
+
Want to get good at building with AI? Type `termi learn`. Six short lessons teach you how to be the boss of your AI helper. Each one earns a badge, and you can replay them any time.
|
|
14
|
+
|
|
15
|
+
Ready? Ask a grown-up to read the next part.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## For parents and guardians
|
|
20
|
+
|
|
21
|
+
Termi is a kids-friendly coding assistant that runs entirely in the terminal on your own computer. A kid describes what they want in plain words, an AI model you configure writes small web projects (plain HTML, CSS, and JavaScript), and a local preview shows the result instantly. Every message in and out passes a layered safety pipeline, and everything is stored locally. There are no Termi servers and no telemetry.
|
|
22
|
+
|
|
23
|
+
Please read [SAFETY.md](SAFETY.md) before handing the keyboard to your kid. It explains, honestly, how the safety system works, what it can and cannot stop, and how to remove everything.
|
|
24
|
+
|
|
25
|
+
### Requirements
|
|
26
|
+
|
|
27
|
+
- Node.js 20.19 or newer: https://nodejs.org
|
|
28
|
+
- macOS, Windows, or Linux
|
|
29
|
+
- An AI account you own (one of: a ChatGPT account, a Claude API key, an OpenAI API key, or a Grok API key)
|
|
30
|
+
- About 700 MB of free disk space for the on-device safety checker (recommended, on by default)
|
|
31
|
+
|
|
32
|
+
If Node is too old, Termi prints a plain message with the download link instead of starting.
|
|
33
|
+
|
|
34
|
+
### Install
|
|
35
|
+
|
|
36
|
+
One command:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
npm install -g termi-kids
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Then run `termi`. If npm reports a permission error, prefix the install with `sudo` on macOS and Linux, or run the terminal as administrator on Windows. To remove it later, run `npm rm -g termi-kids` (full cleanup steps are in SAFETY.md).
|
|
43
|
+
|
|
44
|
+
To hack on Termi instead, install from a clone:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
git clone https://github.com/dannyliv/Termi.git
|
|
48
|
+
cd Termi
|
|
49
|
+
npm install
|
|
50
|
+
npm run build
|
|
51
|
+
npm link
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### First run: the setup wizard
|
|
55
|
+
|
|
56
|
+
Run `termi`. The first run starts a setup wizard for a parent or guardian. It takes about five minutes:
|
|
57
|
+
|
|
58
|
+
1. **Create a grown-up PIN** (at least 4 characters). It guards the grown-up zone. Your kid should not know it.
|
|
59
|
+
2. **Age band and consent.** You pick "Under 13" or "13 or older" and confirm that Termi uses an AI account you own and that you agree to watch how your kid uses it. This consent is recorded in the local safety log.
|
|
60
|
+
3. **Pick an AI provider.** The default is "ChatGPT sign-in": your browser opens, you sign in to your own ChatGPT account, and no API key is needed. The other options are a Claude API key, an OpenAI API key, or a Grok API key. Grok requires an extra confirmation because the xAI API terms are adults-only (details in SAFETY.md). You can add several providers and choose which one is active.
|
|
61
|
+
4. **Pick a safety level.** Strict is the default and the right choice for most kids.
|
|
62
|
+
5. **Safety checker download.** Termi offers a small safety model (623 MB) that screens every message right on your computer, even with no internet. It is on by default and worth keeping. The download runs in the background: setup continues, your kid can start building right away, the home menu shows a progress bar, and the checker turns itself on the moment the verified file lands. An interrupted download resumes on the next start. Details are in [SAFETY.md](SAFETY.md).
|
|
63
|
+
6. **Hand the keyboard to your kid.** The kid picks a made-up nickname (Termi asks them not to use their real name) and sees a clear disclosure: "Termi is a computer program, an AI. It is a tool a grown-up set up for you. It is not a person."
|
|
64
|
+
7. **Desktop shortcut.** Termi offers to write a double-clickable launcher on the Desktop (`Termi.command` on macOS, `Termi.bat` on Windows, `Termi.desktop` on Linux) so your kid can come back tomorrow without typing commands.
|
|
65
|
+
8. **Optional first game.** Two keypresses later there is a running game in the browser.
|
|
66
|
+
|
|
67
|
+
If you skip the provider step, Termi runs in offline mode: your kid can still create projects, play them in the preview, undo changes, and browse ideas. The chat shows a kind "ask a grown-up to set up the AI helper" screen.
|
|
68
|
+
|
|
69
|
+
**If your kid does not know where to start:** every project type has a Build Quest (`/quest` in the chat), a five-step guided build with a ready prompt at each step, and a large idea deck (`/ideas`). Quest steps go through the same safety pipeline as any other message.
|
|
70
|
+
|
|
71
|
+
### Commands
|
|
72
|
+
|
|
73
|
+
| Command | What it does |
|
|
74
|
+
|---|---|
|
|
75
|
+
| `termi` | First run: setup wizard. After that: the home menu. |
|
|
76
|
+
| `termi new` | Start a new project. |
|
|
77
|
+
| `termi go [name]` | Open a project and build. With no name, pick from a list. |
|
|
78
|
+
| `termi preview [name]` | Open a project in the browser without the chat. |
|
|
79
|
+
| `termi ideas` | Print fun project ideas. |
|
|
80
|
+
| `termi learn` | Six short, replayable lessons on building with AI. Fully offline, no AI calls. |
|
|
81
|
+
| `termi grownups` | The grown-up zone. PIN required. |
|
|
82
|
+
| `termi help` | Show the command list. |
|
|
83
|
+
| `termi --version` | Show the version. |
|
|
84
|
+
|
|
85
|
+
Inside the build chat, these slash commands work:
|
|
86
|
+
|
|
87
|
+
| In chat | What it does |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `/preview` | Open the project in the browser. |
|
|
90
|
+
| `/undo` | Take back the last change. |
|
|
91
|
+
| `/redo` | Bring a change back. |
|
|
92
|
+
| `/new` | Start a fresh project. |
|
|
93
|
+
| `/ideas` | Get fun ideas. |
|
|
94
|
+
| `/badges` | See earned badges. |
|
|
95
|
+
| `/learn` | Play the short AI lessons. |
|
|
96
|
+
| `/quest` | A step-by-step build guide with a ready prompt at each step. |
|
|
97
|
+
| `/help` | Show this list. |
|
|
98
|
+
| `/done` | Finish and celebrate. |
|
|
99
|
+
| `/quit` | Stop for today. Projects are saved. |
|
|
100
|
+
| `/grownups` | Grown-up zone, PIN required. |
|
|
101
|
+
|
|
102
|
+
Plain words work too: `undo`, `help`, `ideas`, `done`, `preview`, `badges`, `learn`, `quest`, and `quit` work on their own, no slash needed, and `exit`, `stop`, `bye`, and `leave` also quit. (`redo`, `new`, and `grownups` need the slash.) Misspelled commands get a "did you mean" suggestion.
|
|
103
|
+
|
|
104
|
+
In the grown-up zone you can add, switch, or remove providers, change the safety level, manage the on-device safety checker (turn it on or off, download or remove its model file), pick the model speed (Zippy, the fast default, or Extra smart for tricky asks), read the usage and quota note, review the safety log, and see exactly where your data lives. Removing a provider deletes its saved key or sign-in from this computer.
|
|
105
|
+
|
|
106
|
+
### The nine project types
|
|
107
|
+
|
|
108
|
+
| Project type | What your kid builds | Styles |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| 🎮 Games | A quick dodge game. A great first project. | Space Rocks, Neon Star Run, Spooky Bats, Soccer Headers |
|
|
111
|
+
| 🕹️ Big Games | A bigger platform game with two levels. The game engine ships as a local file, no internet needed. | Castle Quest, Blocky Mine World, Haunted House, Midnight Wolf Pack |
|
|
112
|
+
| 🎨 Pixel Studio | A pixel paint studio. Calm and creative. | Free Draw, Pet Portraits |
|
|
113
|
+
| 🎵 Dance Party | A music maker with sounds made right in the browser. | Robot Dance, Glow Disco |
|
|
114
|
+
| 🐾 My Pet | A virtual pet that remembers its care between visits. | Dragon, Wild Horses |
|
|
115
|
+
| 📖 Story Quest | A choose-your-own-adventure story engine. | Dragon Treasure, Mystery at School |
|
|
116
|
+
| ❓ Quiz Show | A quiz maker for friends and family. | Animal Trivia, Which Character Are You? |
|
|
117
|
+
| 🌐 My Page | A personal web page, saved only on this computer. | About Me, My Team |
|
|
118
|
+
| 🤖 Talking Character | A scripted talking character. The kid writes every line it can say. No AI runs inside the project. | Quiz Host, Sidekick Robot |
|
|
119
|
+
|
|
120
|
+
Projects live in `~/Termi/<project-name>/` as plain files (typically `index.html`, `style.css`, and one JavaScript file, plus a `TERMI.md` notes file). They are capped at 8 files of 256 KB each, use no build step, and work with zero network access. Once a kid has a project, `termi new` also offers "Remix one of your projects."
|
|
121
|
+
|
|
122
|
+
On the My Page projects, personal details like a name are typed directly on the page in the browser preview and saved to the browser's local storage on this computer. They never pass through the chat, so they never reach the AI provider.
|
|
123
|
+
|
|
124
|
+
### How the preview works
|
|
125
|
+
|
|
126
|
+
Each open project gets its own tiny web server bound to `127.0.0.1` only. It is not reachable from other devices on your network. It starts at port 4311 and scans upward if that port is busy. Every page is served with a strict Content-Security-Policy that blocks all outside network access from the project, and the page reloads automatically when Termi changes a file. Dotfiles and the `TERMI.md` notes are never served.
|
|
127
|
+
|
|
128
|
+
### More than one kid
|
|
129
|
+
|
|
130
|
+
Termi's v1 stance is simple: one operating system user account per kid. Settings, the PIN, projects, badges, and the safety log all live in the OS user profile, so separate OS accounts keep kids' worlds (and the audit trail) separate. There are no in-app profiles.
|
|
131
|
+
|
|
132
|
+
### Troubleshooting
|
|
133
|
+
|
|
134
|
+
- **"Termi needs a newer version of Node.js."** Install the current version from https://nodejs.org, then run `termi` again.
|
|
135
|
+
- **"The AI helper is not set up yet."** No provider is configured. Run `termi grownups`, open Providers, and add one. Projects, previews, undo, ideas, and badges all work without a provider.
|
|
136
|
+
- **"Termi used up its energy. It comes back at 4:30 PM."** Your AI plan or API key hit its rate limit. The time comes from the provider when it says how long to wait. Your kid can keep playing the preview, use `/undo`, and browse `/ideas` meanwhile.
|
|
137
|
+
- **"Termi needs a quick break. Try again in a minute."** A safety check could not finish (a timeout, a provider error, or a rate limit). Termi blocks rather than guesses, so it pauses the turn. Trying again usually works.
|
|
138
|
+
- **"The sign-in stopped working."** The saved ChatGPT sign-in can no longer refresh. Run `termi grownups`, open Providers, and sign in again.
|
|
139
|
+
- **"Termi found changed settings. Safe settings are on now."** The settings file failed its integrity check, so Termi reverted to strict defaults. Review the grown-up zone.
|
|
140
|
+
- **Crashes.** The kid sees a friendly screen; the technical details go to `~/.termi/error.log`.
|
|
141
|
+
|
|
142
|
+
### Uninstall
|
|
143
|
+
|
|
144
|
+
See "Your data and how to remove Termi" in [SAFETY.md](SAFETY.md) for the exact folders, files, and keychain entries.
|
|
145
|
+
|
|
146
|
+
## Disclaimer
|
|
147
|
+
|
|
148
|
+
Termi is free and open-source software, released under the MIT License and provided as-is, without warranty of any kind, express or implied. It is a personal, educational project. All use is at the user's own risk and sole responsibility: you, the parent or guardian who installs and configures Termi, are responsible for how it is set up, which AI accounts it connects to, and how children in your care use it. AI output is unpredictable; the safety layers reduce risk but cannot eliminate it, and children should use Termi with adult supervision. The authors and contributors accept no liability for any claim, damages, or other losses arising from the use of this software, as set out in the MIT License. Termi is an independent project and is not affiliated with, endorsed by, or sponsored by OpenAI, Anthropic, xAI, the Qwen team, or any other AI provider.
|
package/SAFETY.md
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# How Termi keeps kids safe, and what it cannot do
|
|
2
|
+
|
|
3
|
+
This page is for parents and guardians. It explains the safety system in plain language, including its limits. Termi is built to be honest with you: it is a strong set of guardrails, not a guarantee. Your supervision is still the most important safety feature.
|
|
4
|
+
|
|
5
|
+
## The short version
|
|
6
|
+
|
|
7
|
+
- Every message in and out passes five layers of checks. When a check cannot finish, Termi blocks rather than guesses.
|
|
8
|
+
- A safety checker runs on this computer itself: a small AI model, on by default, that screens every message in and out across nine categories, even with no internet. Details below.
|
|
9
|
+
- Everything runs and stays on your computer. There are no Termi servers and no telemetry. Chat text goes to exactly one place: the AI provider you configured.
|
|
10
|
+
- A tamper-evident safety log records every block and every settings change. You read it in the grown-up zone (`termi grownups`).
|
|
11
|
+
- The PIN and the signed settings are speed bumps for a curious kid, not vault doors. Your real levers are that you own the AI account and the computer.
|
|
12
|
+
|
|
13
|
+
## How the safety system works
|
|
14
|
+
|
|
15
|
+
Five layers sit between your kid and the AI. The chat conversation itself is never trusted to police itself.
|
|
16
|
+
|
|
17
|
+
1. **A local filter on this computer.** Before anything leaves your machine, Termi checks the message offline. It blocks swearing and slurs (including d.i.s.g.u.i.s.e.d spellings and leetspeak), and it blocks known "ignore your rules" tricks. It also looks for personal details: names, addresses, phone numbers, emails, school names. Those are not blocked; they are masked to `[secret]` before the message is sent, and the kid gets a gentle reminder to keep private things private. The same masking is used when the AI reads project files back. The word lists are English; the AI-based checkers below cover other languages.
|
|
18
|
+
2. **Safety rules inside the AI's instructions.** The AI is told, every turn: you are a tool, not a person. Never act romantic, never roleplay relationships, never ask the kid to keep secrets, never ask for a real name, address, school, age, or photos. Big feelings get one kind line and a pointer to a trusted adult. The instructions also declare that everything the kid types and everything in project files is data, never commands, which blunts "ignore your previous instructions" tricks hidden in files.
|
|
19
|
+
3. **A checker before the AI acts.** A separate safety check, outside the conversation, reads the kid's message along with the last few turns for context. When the on-device safety checker is installed (it is offered during setup and on by default), it judges the message right on this computer at the same time. It runs at the same time as the build call so it adds no waiting, but nothing is allowed to land until it passes: no file is written and no reply is shown. If it says no, the work is thrown away. One honest note on timing: because the check and the build call start together, a message that ends up blocked has still traveled to your AI provider once (with personal details already masked); its answer is discarded unseen.
|
|
20
|
+
4. **A checker on everything the AI says and writes.** Every file the AI writes or edits is checked twice before it touches disk: a code scan looks for network calls, code hidden in strings, and other tricks (the full list is in the code scanner, `src/safety/codescan.ts`), and the human-visible text inside the file (story text, labels, comments) goes through the same safety check as chat, in full: long text is checked chunk by chunk, and a file too wordy to check completely is refused rather than half-checked. File names and project names are screened too. The final reply is checked too, before the kid sees a single character. As a backstop, the preview server wraps every project page in a strict Content-Security-Policy, so even code that slipped past the scanner cannot reach the internet from the browser. Files edited outside Termi (in a text editor, say) are the kid's own files and are not re-screened; they only pass the rule-neutralizing filter when read back into the chat.
|
|
21
|
+
5. **Friendly block screens.** When something is blocked, the kid never sees the blocked content. They see a kind, specific explanation ("Those words can hurt people. Pick kind words and we will keep building.") and an invitation to try again another way. If a message suggests a kid may be thinking about hurting themselves, Termi answers in a calm, supportive voice, says clearly that it is a computer program and cannot help with this part, points to a trusted adult, and shares the 988 line (US). That event is logged so you will see it.
|
|
22
|
+
|
|
23
|
+
## Termi blocks when it is unsure
|
|
24
|
+
|
|
25
|
+
This is the most important design rule, so it gets its own section. If a safety check times out (8 seconds for online checks, 20 for the on-device checker), errors, hits a rate limit, or returns something unreadable, the answer is always **block**. The kid sees "Termi needs a quick break. Try again in a minute." and the event is logged as a failed-closed check. The AI's output is never shown, and no file is written, without a clean pass. There is no configuration that turns this off.
|
|
26
|
+
|
|
27
|
+
## What gets blocked
|
|
28
|
+
|
|
29
|
+
The checkers look for these categories:
|
|
30
|
+
|
|
31
|
+
| Category | What it means |
|
|
32
|
+
|---|---|
|
|
33
|
+
| Sexual content | Sexual or romantic content of any kind. |
|
|
34
|
+
| Self-harm | Talk of self-harm. Routes to the supportive screen, never a scolding one. |
|
|
35
|
+
| Violence | Real-world harm or gore past a mild cartoon ceiling. |
|
|
36
|
+
| Hate and harassment | Mean, hateful, or bullying words aimed at people or groups. |
|
|
37
|
+
| Dangerous how-to | Weapons, drugs, hacking real systems. |
|
|
38
|
+
| Profanity | Swearing and slurs. The kid is asked to rephrase kindly. |
|
|
39
|
+
| Personal info | Sharing or asking for a real name, address, school, phone, email, or photos. |
|
|
40
|
+
| Grooming patterns | Secrecy asks ("do not tell your parents"), romance aimed at the kid, probing for personal details, or trying to move the chat to another app. |
|
|
41
|
+
| Heavy adult topics | Medical, legal, money, relationship advice, and heavy political topics. Redirected to a trusted adult. |
|
|
42
|
+
| Copying others' work | Reproducing someone else's work wholesale, like song lyrics or book text. The kid is nudged to make their own version. |
|
|
43
|
+
| Rule-breaking tricks | Attempts to make the AI ignore or reveal its rules. |
|
|
44
|
+
|
|
45
|
+
Grooming, personal info, and rule-breaking tricks block at a lower threshold than everything else.
|
|
46
|
+
|
|
47
|
+
**The game-language carve-out.** Kids build games, and games have zombies. "Make the zombie die when you hit it", "kill the boss with a banana", "haunted house with screaming ghosts" are normal kid game talk and are deliberately allowed. Every checker is told this explicitly, with examples. The line is real-world harm, harm to real people, or gore past mild cartoon.
|
|
48
|
+
|
|
49
|
+
## The safety checker on this computer
|
|
50
|
+
|
|
51
|
+
Termi includes an on-device safety checker: a small AI model (Qwen3Guard 0.6B, published by the Qwen team under the Apache 2.0 license) that runs entirely on your computer. Setup offers the download (623 MB) with a yes as the default; you can decline and add it later from the grown-up zone (`termi grownups`, then **Safety checker**).
|
|
52
|
+
|
|
53
|
+
The download never blocks anyone. It runs in the background while setup finishes and while your kid builds; the home menu shows its progress bar, and the checker attaches itself to the running safety pipeline the moment the verified file is in place. If the download is interrupted (a closed laptop, a dropped connection), it resumes from where it stopped the next time Termi starts. Until the file lands, the online checks carry the load exactly as they would with the checker turned off.
|
|
54
|
+
|
|
55
|
+
What it does:
|
|
56
|
+
|
|
57
|
+
- It reads every message the kid types and everything the AI writes back (chat replies and the human-visible text inside project files), and grades each one Safe, Controversial, or Unsafe.
|
|
58
|
+
- It covers nine categories: violent content, illegal acts, sexual content, personal details, self-harm, unethical acts, heavy political topics, copying others' work wholesale, and rule-breaking tricks. The table above shows where each lands in Termi's block messages.
|
|
59
|
+
- Unsafe always blocks. Controversial blocks for personal details and rule-breaking tricks, and otherwise counts toward the session watch rather than blocking a creative kid mid-build.
|
|
60
|
+
- It needs no internet and costs nothing per use.
|
|
61
|
+
- It runs **alongside** the online checks, never instead of the grooming watch: all verdicts merge and the strictest one wins.
|
|
62
|
+
|
|
63
|
+
Integrity: the downloaded file is verified on disk against a pinned cryptographic fingerprint (SHA-256) before it is put in place, and the fingerprint is checked again every time the model loads, so a swapped or altered file never runs as the checker. The model file lives in `~/.termi/models/`. You can remove the file or turn the checker off any time in the grown-up zone (removing the file also turns it off); Termi then runs on the online checks alone. If the file goes missing while the checker is on, that is written to the safety log and the download restarts by itself.
|
|
64
|
+
|
|
65
|
+
One honest note: this is a small model. It is good at its nine categories, it is not perfect, and it changes nothing about the block-when-unsure rule or your role as the adult in the room.
|
|
66
|
+
|
|
67
|
+
## Watching for grooming across the whole conversation
|
|
68
|
+
|
|
69
|
+
Grooming rarely shows up in one message. It builds. So Termi keeps a running watch across the session, not just per message:
|
|
70
|
+
|
|
71
|
+
- The checkers see a sliding window of the last 10 turns, so a pattern can be judged in context.
|
|
72
|
+
- Termi counts signals across the whole session in four families: secrecy asks, affection escalation ("you're my special friend"), probing for personal details, and attempts to move the chat to another platform (named chat and social apps, "text me at", "DM me"). The counters watch the conversation itself, what the kid types and what Termi replies. The kid's own story and game text does not count, so a virtual pet that says "do you love me" cannot trip the grooming watch.
|
|
73
|
+
- Two signals in any one family, or three across families, trips a hard block, no matter what any single message looked like on its own.
|
|
74
|
+
|
|
75
|
+
A tripped grooming watch is written to the safety log as a flagged entry. In the grown-up zone it is shown first, in red, with a clear "REVIEW FIRST" marker and a suggestion to talk with your kid.
|
|
76
|
+
|
|
77
|
+
## The safety log
|
|
78
|
+
|
|
79
|
+
Termi keeps an append-only log at `~/.termi/audit.log`. It records blocked messages, masked personal info, failed-closed checks, settings changes, provider changes, wrong PIN tries, PIN resets, recorded consent, and grooming flags. Each entry carries a timestamp, which layer fired, the category, and a short snippet (at most 80 characters) for context. Personal details are masked before they can land in the log.
|
|
80
|
+
|
|
81
|
+
The log is tamper-evident: every line carries a cryptographic code (an HMAC-SHA256 chain) that covers the line before it, with the key held in the system keychain. Editing or deleting any line breaks the chain from that point on. When the log passes 5 MB it rotates to `audit.log.1`, and the chain carries across the rotation.
|
|
82
|
+
|
|
83
|
+
To read it: `termi grownups`, enter your PIN, pick **Safety log**. Termi first verifies the whole chain and tells you plainly whether it checks out ("Log check: good. 41 entries, none changed.") and then shows the recent events in plain words, grooming flags first.
|
|
84
|
+
|
|
85
|
+
One honest limit: the chain proves entries were not edited. It cannot prove the whole file was not deleted. An empty log on a machine that has seen weeks of use is itself a signal.
|
|
86
|
+
|
|
87
|
+
## What a determined kid can and cannot get past
|
|
88
|
+
|
|
89
|
+
Honesty matters more than comfort here. The threat model is a clever kid with ordinary (non-admin) access to their own account on the computer.
|
|
90
|
+
|
|
91
|
+
What the speed bumps do:
|
|
92
|
+
|
|
93
|
+
- **The PIN** gates the grown-up zone. It is stored only as an scrypt hash in the system keychain. Five wrong tries lock it for five minutes. The forgot-PIN reset is deliberately useless to a kid: it wipes the PIN, every stored AI key, and the saved sign-in, and restores the strictest defaults. Resetting gains them an unconfigured app and a logged `pin_reset` event you will see.
|
|
94
|
+
- **Signed settings.** `settings.json` carries a cryptographic signature. Hand-editing it (say, flipping a setting in a text editor) fails the check on the next start: Termi reverts to the strictest defaults, tells the kid on screen, and logs it.
|
|
95
|
+
- **A setup marker in the keychain.** Deleting the whole `~/.termi` folder does not give the kid a fresh, parent-free setup. Termi notices the mismatch, falls back to strict defaults, and logs it. The PIN, also in the keychain, survives the deletion.
|
|
96
|
+
|
|
97
|
+
What a determined kid can still do, told plainly:
|
|
98
|
+
|
|
99
|
+
- They run Termi under their own OS account, so the OS will let that account read its own keychain entries and files. A kid who knows their way around Keychain Access (macOS), Credential Manager (Windows), or the Linux secret service can read or delete Termi's stored secrets, including a stored API key.
|
|
100
|
+
- They can delete files: the safety log, the settings, the whole folder. Termi detects and logs what it can, fails to strict defaults, and the missing history is visible, but deletion itself cannot be prevented by an app.
|
|
101
|
+
- They can bypass Termi entirely. An API key, once read, works in any tool. A ChatGPT sign-in in the browser is the same account without any of Termi's filters.
|
|
102
|
+
|
|
103
|
+
The real levers, and they are good ones:
|
|
104
|
+
|
|
105
|
+
- **You own the AI account.** You can see usage on the provider's dashboard, rotate or revoke an API key in one click, and set spending limits there. Termi never needs to know.
|
|
106
|
+
- **OS parental controls** (Screen Time on macOS, Family Safety on Windows) control what the computer itself allows, below anything Termi can do.
|
|
107
|
+
- **One OS user account per kid.** This is also Termi's multi-kid answer: settings, PIN, projects, and the safety log all live per OS account.
|
|
108
|
+
- **The safety log plus a conversation.** The log is designed to support a talk, not surveillance: it stores categories and short snippets, not transcripts.
|
|
109
|
+
|
|
110
|
+
One more honest note: the keychain stores Termi's secrets on a best-effort basis. On systems without a usable keychain (some Linux setups), Termi falls back to a plain file at `~/.termi/secrets.json` and tells you so with a warning banner in the grown-up zone.
|
|
111
|
+
|
|
112
|
+
About the safety level setting: the wizard and the grown-up zone offer Strict and Standard. In the current version both levels apply the same blocking thresholds; the setting is recorded for future tuning. Leave it on Strict.
|
|
113
|
+
|
|
114
|
+
## Privacy and COPPA posture
|
|
115
|
+
|
|
116
|
+
Termi is built so that the developer never touches your family's data, because there is nothing to send it to:
|
|
117
|
+
|
|
118
|
+
- **100% local.** No Termi servers exist. No telemetry, no analytics, no crash reporting, no account with Termi. Nothing phones home.
|
|
119
|
+
- **Chat goes to one place.** The kid's messages (after personal-info masking) and the project files go only to the AI provider you configured, to generate the code and run the safety checks. No other network calls are made on the kid's behalf.
|
|
120
|
+
- **You are the account holder.** Termi has no relationship with your child. You bring your own AI account, you attest to your kid's age band during setup, and your consent is recorded, timestamped, in the local safety log. The AI provider's privacy policy governs what happens to text sent to it; that is a relationship between you and the provider you chose.
|
|
121
|
+
- **Personal info is kept out of the AI.** The local filter masks names, addresses, phones, emails, and school names before sending. On "My Page" website projects, personal touches like a name are typed directly into the page in the browser preview and saved in the browser's local storage on this computer; they never pass through the chat, so they never reach the AI provider at all.
|
|
122
|
+
- **Kid projects are plain files** in `~/Termi`, readable and yours forever.
|
|
123
|
+
|
|
124
|
+
## Notes on each AI provider
|
|
125
|
+
|
|
126
|
+
Whichever provider you pick, the kid sees this disclosure, word for word, during setup: "Termi is a computer program, an AI. It is a tool a grown-up set up for you. It is not a person."
|
|
127
|
+
|
|
128
|
+
**ChatGPT sign-in (the default).** You sign in to your own account in your browser; Termi never sees your password. The sign-in tokens are stored in `~/.termi/auth.json` with owner-only file permissions. During sign-in, Termi also tries to create an API key on your OpenAI account (a standard token exchange). When that works, the broad safety checks run on OpenAI's free moderation service instead of your chat plan. OpenAI publishes guidance for apps used by minors that calls for content filtering and adult oversight; Termi's filtering layers and your safety-log review are how this app approaches that. If your kid is under 13, also consider asking OpenAI about Zero Data Retention for your organization so chat text is not retained on their side.
|
|
129
|
+
|
|
130
|
+
**OpenAI API key.** Same model family, billed per use on your key. The free moderation endpoint covers the broad safety checks.
|
|
131
|
+
|
|
132
|
+
**Claude API key (Anthropic).** Build calls use a main Claude model; the safety checks use a small, fast Claude model. Billed per use on your key.
|
|
133
|
+
|
|
134
|
+
**Grok API key (xAI), with eyes open.** xAI's API terms are for adults, which is why the wizard makes you confirm, explicitly, that the account is parent-owned and parent-watched before a Grok key is saved. The confirmation is enforced in code, not just in the wizard: a Grok key that lands in the keychain by any other path will not run until a parent has confirmed. Grok is never a default; you must pick it. When Grok builds, the fast model handles quick asks and the bigger model handles "Extra smart" mode; the safety checks run on the fast model. You can remove the key at any time from the grown-up zone (Providers, then "Remove a provider"). Also be aware: if Grok is your only configured provider and the on-device safety checker is not installed, Grok checks itself. That is the weakest safety configuration Termi supports. Keeping the on-device checker installed gives every setup, including this one, an independent screen across the broad categories. If you use Grok for building, we still recommend also adding an OpenAI or Claude key so a second, independent model runs the grooming-focused checks; Termi automatically prefers the strongest available checker regardless of which provider builds.
|
|
135
|
+
|
|
136
|
+
## What one kid message costs
|
|
137
|
+
|
|
138
|
+
Honest quota math. Each message your kid sends makes these AI calls:
|
|
139
|
+
|
|
140
|
+
1. One safety check on the message (runs alongside the build call, so it adds no waiting).
|
|
141
|
+
2. One build call. This is the big one; it does the actual thinking and writing.
|
|
142
|
+
3. One safety check on each file the AI writes or edits that turn.
|
|
143
|
+
4. One safety check on the final reply.
|
|
144
|
+
|
|
145
|
+
So a message where the AI edits one file costs one build call plus three small checks. The checks are deliberately tiny: capped prompts, short answers. The build call accounts for nearly all of the cost or quota. The on-device safety checker adds its screening free, on your computer, on top of the online checks; it never replaces them, so its presence changes safety coverage in one direction only: up.
|
|
146
|
+
|
|
147
|
+
Where those calls land depends on your setup:
|
|
148
|
+
|
|
149
|
+
- **ChatGPT sign-in:** build calls use your ChatGPT plan. If the sign-in minted an API key, the safety checks use the free moderation service plus very small per-use mini-model calls on that key, not your chat plan. If it did not, the safety checks share your chat plan, so a busy kid drains the plan a little faster.
|
|
150
|
+
- **API keys (OpenAI, Claude, Grok):** everything is per-use on your key. Small models handle the checks, and a file whose text has not changed is not re-checked within a session.
|
|
151
|
+
|
|
152
|
+
The grown-up zone has a **Usage and quota note** screen that states this for your exact setup. When a plan or key runs dry, the kid sees "Termi used up its energy", with the comeback time when the provider supplies one, and everything offline (playing the game, undo, ideas, badges) keeps working.
|
|
153
|
+
|
|
154
|
+
## Where your data lives, and how to remove everything
|
|
155
|
+
|
|
156
|
+
Everything Termi stores, in two folders plus the system keychain:
|
|
157
|
+
|
|
158
|
+
`~/.termi/` (Termi's state):
|
|
159
|
+
|
|
160
|
+
| File or folder | What it is |
|
|
161
|
+
|---|---|
|
|
162
|
+
| `settings.json` | Settings, cryptographically signed |
|
|
163
|
+
| `auth.json` | ChatGPT sign-in tokens (only if you used the sign-in) |
|
|
164
|
+
| `audit.log`, `audit.log.1` | The safety log and one rotated older file |
|
|
165
|
+
| `error.log` | Crash details (for grown-ups; the kid sees a friendly screen) |
|
|
166
|
+
| `badges.json` | Earned badges |
|
|
167
|
+
| `snapshots/` | Saved file versions that power undo |
|
|
168
|
+
| `models/` | The on-device safety checker's model file (623 MB), if downloaded |
|
|
169
|
+
| `locks/`, `pin.lock` | Small bookkeeping files |
|
|
170
|
+
| `secrets.json` | Only on systems with no keychain: the fallback secret store |
|
|
171
|
+
|
|
172
|
+
`~/Termi/` holds the kid's projects as plain HTML, CSS, and JavaScript files. They are yours; keep them if you want the games.
|
|
173
|
+
|
|
174
|
+
The system keychain holds entries under the service name **`termi-cli`** with these account names: `pin-hash`, `setup-marker`, `hmac-key`, `install-id`, `api-key-openai-api`, `api-key-anthropic`, `api-key-xai`. (Only the ones you actually used will exist.) The grown-up zone's **Your data and uninstall** screen prints this same list with your machine's exact paths.
|
|
175
|
+
|
|
176
|
+
Full uninstall:
|
|
177
|
+
|
|
178
|
+
1. Remove the command: `npm rm -g termi-kids` (if you installed from a clone instead, `npm rm -g termi`, then delete the cloned folder).
|
|
179
|
+
2. Delete `~/.termi`.
|
|
180
|
+
3. Delete `~/Termi`, or keep it for the projects.
|
|
181
|
+
4. Delete the keychain entries: on macOS open Keychain Access and search `termi-cli`; on Windows use Credential Manager; on Linux use your keyring app (for example Seahorse), or just delete `~/.termi/secrets.json` if the fallback was in use.
|
|
182
|
+
5. Delete the Desktop launcher if you created one: `Termi.command` (macOS), `Termi.bat` (Windows), or `Termi.desktop` (Linux).
|
|
183
|
+
6. Optionally, rotate or revoke the credentials you used: delete the API key in the provider's console, or revoke the app sign-in from your OpenAI account's security settings. Uninstalling Termi does not touch your AI provider account.
|
|
184
|
+
|
|
185
|
+
## Disclaimer
|
|
186
|
+
|
|
187
|
+
Termi is free and open-source software, released under the MIT License and provided as-is, without warranty of any kind, express or implied. It is a personal, educational project. All use is at the user's own risk and sole responsibility: you, the parent or guardian who installs and configures Termi, are responsible for how it is set up, which AI accounts it connects to, and how children in your care use it. AI output is unpredictable; the safety layers reduce risk but cannot eliminate it, and children should use Termi with adult supervision. The authors and contributors accept no liability for any claim, damages, or other losses arising from the use of this software, as set out in the MIT License. Termi is an independent project and is not affiliated with, endorsed by, or sponsored by OpenAI, Anthropic, xAI, the Qwen team, or any other AI provider.
|
package/bin/termi.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const [major, minor] = process.versions.node.split('.').map(Number);
|
|
3
|
+
if (major < 20 || (major === 20 && minor < 19)) {
|
|
4
|
+
console.error('');
|
|
5
|
+
console.error('Termi needs a newer version of Node.js to run.');
|
|
6
|
+
console.error(`You have ${process.versions.node}. Termi needs 20.19 or newer.`);
|
|
7
|
+
console.error('A grown-up can get the latest from: https://nodejs.org');
|
|
8
|
+
console.error('');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
if (process.argv[2] === '--version' || process.argv[2] === '-v') {
|
|
12
|
+
// Answered here so a version check never loads (or depends on) the app.
|
|
13
|
+
const { createRequire } = await import('node:module');
|
|
14
|
+
const pkg = createRequire(import.meta.url)('../package.json');
|
|
15
|
+
console.log(pkg.version);
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
import(new URL('../dist/cli.js', import.meta.url)).catch((err) => {
|
|
19
|
+
console.error('Termi could not start. Details were saved for a grown-up.');
|
|
20
|
+
console.error(err && err.message ? err.message : err);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the model input for one turn, tuned for token efficiency:
|
|
3
|
+
* - The system prompt stays out of here (the loop sends it first, stable).
|
|
4
|
+
* - Only files CHANGED since the last embed go in full. Unchanged files are
|
|
5
|
+
* one line each and the model uses read_file on demand.
|
|
6
|
+
* - History is plain truncation: last 30 entries and a 6,000 char budget.
|
|
7
|
+
* - Every piece of untrusted text passes safety.prefilterContext and lands
|
|
8
|
+
* inside data tags the system prompt declares to be data, not instructions.
|
|
9
|
+
*/
|
|
10
|
+
import { createHash } from 'node:crypto';
|
|
11
|
+
/** Max history entries kept (one entry per kid or termi turn). */
|
|
12
|
+
export const HISTORY_TURN_CAP = 30;
|
|
13
|
+
/** Max total characters of history text. Oldest entries drop first. */
|
|
14
|
+
export const HISTORY_CHAR_BUDGET = 6000;
|
|
15
|
+
/** Max lines of TERMI.md embedded per turn. */
|
|
16
|
+
export const TERMI_MD_LINE_CAP = 60;
|
|
17
|
+
export function createEmbedState() {
|
|
18
|
+
return new Map();
|
|
19
|
+
}
|
|
20
|
+
function sha256Hex(text) {
|
|
21
|
+
return createHash('sha256').update(text, 'utf8').digest('hex');
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Plain truncation, newest first: keep at most HISTORY_TURN_CAP entries and
|
|
25
|
+
* HISTORY_CHAR_BUDGET total characters. Pairs stay intact: an orphaned termi
|
|
26
|
+
* reply whose kid message was dropped gets dropped too.
|
|
27
|
+
*/
|
|
28
|
+
export function trimHistory(history) {
|
|
29
|
+
const kept = [];
|
|
30
|
+
let chars = 0;
|
|
31
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
32
|
+
const entry = history[i];
|
|
33
|
+
if (entry === undefined) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (kept.length >= HISTORY_TURN_CAP || chars + entry.text.length > HISTORY_CHAR_BUDGET) {
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
kept.unshift(entry);
|
|
40
|
+
chars += entry.text.length;
|
|
41
|
+
}
|
|
42
|
+
while (kept.length > 0 && kept[0]?.role === 'termi') {
|
|
43
|
+
kept.shift();
|
|
44
|
+
}
|
|
45
|
+
return kept;
|
|
46
|
+
}
|
|
47
|
+
/** First line that IS a comment (//, /*, or <!--), for the one-line listing. */
|
|
48
|
+
export function firstCommentLine(content) {
|
|
49
|
+
for (const raw of content.split('\n').slice(0, 40)) {
|
|
50
|
+
const line = raw.trim();
|
|
51
|
+
const m = /^\/\/\s*(.+)$/.exec(line) ??
|
|
52
|
+
/^\/\*+\s*([^*]+?)\s*(?:\*+\/)?$/.exec(line) ??
|
|
53
|
+
/^<!--\s*(.+?)\s*(?:-->)?$/.exec(line);
|
|
54
|
+
const text = m?.[1]?.trim();
|
|
55
|
+
if (text !== undefined && text.length > 0) {
|
|
56
|
+
return text.slice(0, 60);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
function capLines(text, maxLines) {
|
|
62
|
+
const lines = text.split('\n');
|
|
63
|
+
return lines.length <= maxLines ? text : lines.slice(0, maxLines).join('\n');
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Assembles the per-turn messages (history plus one composed user message).
|
|
67
|
+
* Mutates embedState so the next turn elides files that did not change.
|
|
68
|
+
*/
|
|
69
|
+
export function buildMessages(project, history, kidMessageRedacted, embedState, safety) {
|
|
70
|
+
const messages = trimHistory(history).map((entry) => entry.role === 'kid'
|
|
71
|
+
? { role: 'user', content: entry.text }
|
|
72
|
+
: { role: 'assistant', content: entry.text });
|
|
73
|
+
const sections = [];
|
|
74
|
+
const notes = capLines(safety.prefilterContext(project.readTermiMd()), TERMI_MD_LINE_CAP);
|
|
75
|
+
sections.push('<project_notes>', notes, '</project_notes>');
|
|
76
|
+
const files = project.listKidFiles();
|
|
77
|
+
const seen = new Set();
|
|
78
|
+
const unchangedLines = [];
|
|
79
|
+
for (const file of files) {
|
|
80
|
+
seen.add(file.relPath);
|
|
81
|
+
const content = project.readFile(file.relPath);
|
|
82
|
+
if (content === null) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const sha = sha256Hex(content);
|
|
86
|
+
if (embedState.get(file.relPath) === sha) {
|
|
87
|
+
const comment = firstCommentLine(content);
|
|
88
|
+
unchangedLines.push(`${file.relPath} (${file.bytes} bytes)${comment !== null ? ` : ${comment}` : ''}`);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
embedState.set(file.relPath, sha);
|
|
92
|
+
sections.push(`<project_file path="${file.relPath}">`, safety.prefilterContext(content), '</project_file>');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
for (const key of [...embedState.keys()]) {
|
|
96
|
+
if (!seen.has(key)) {
|
|
97
|
+
embedState.delete(key);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (unchangedLines.length > 0) {
|
|
101
|
+
sections.push('<project_file_list note="unchanged files; call read_file to open one">', ...unchangedLines, '</project_file_list>');
|
|
102
|
+
}
|
|
103
|
+
sections.push('<kid_message>', safety.prefilterContext(kidMessageRedacted), '</kid_message>');
|
|
104
|
+
messages.push({ role: 'user', content: sections.join('\n') });
|
|
105
|
+
return messages;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Per-provider options for one turn. Anthropic gets an ephemeral cache point
|
|
109
|
+
* on the stable system message. The ChatGPT sign-in path runs stateless with
|
|
110
|
+
* encrypted reasoning passthrough. Everyone else relies on automatic prefix
|
|
111
|
+
* caching, so the prefix is never reordered and nothing extra is sent.
|
|
112
|
+
*/
|
|
113
|
+
export function providerOptionsFor(providerId) {
|
|
114
|
+
switch (providerId) {
|
|
115
|
+
case 'anthropic':
|
|
116
|
+
return { call: {}, system: { anthropic: { cacheControl: { type: 'ephemeral' } } } };
|
|
117
|
+
case 'openai-chatgpt':
|
|
118
|
+
return {
|
|
119
|
+
call: { openai: { store: false, include: ['reasoning.encrypted_content'] } },
|
|
120
|
+
system: {},
|
|
121
|
+
};
|
|
122
|
+
case 'openai-api':
|
|
123
|
+
case 'xai':
|
|
124
|
+
return { call: {}, system: {} };
|
|
125
|
+
}
|
|
126
|
+
}
|