@datename/fastcode-termux 0.1.6 → 0.1.8-beta.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 +4 -0
- package/README.md +67 -16
- package/dist/agent/approval-json.d.ts +43 -0
- package/dist/agent/approval-json.js +69 -0
- package/dist/agent/approval-native.d.ts +3 -0
- package/dist/agent/approval-native.js +165 -0
- package/dist/agent/approval-store.d.ts +16 -0
- package/dist/agent/approval-store.js +21 -0
- package/dist/agent/approval.d.ts +83 -0
- package/dist/agent/approval.js +42 -0
- package/dist/agent/compaction.d.ts +41 -0
- package/dist/agent/compaction.js +270 -0
- package/dist/agent/context.d.ts +29 -0
- package/dist/agent/context.js +62 -0
- package/dist/agent/guards.d.ts +24 -16
- package/dist/agent/guards.js +1167 -18
- package/dist/agent/ignore.d.ts +11 -0
- package/dist/agent/ignore.js +129 -0
- package/dist/agent/policy/defaults.d.ts +16 -0
- package/dist/agent/policy/defaults.js +17 -0
- package/dist/agent/policy/egress.d.ts +32 -0
- package/dist/agent/policy/egress.js +280 -0
- package/dist/agent/process-tree.d.ts +22 -0
- package/dist/agent/process-tree.js +102 -0
- package/dist/agent/protocol-mode.d.ts +6 -0
- package/dist/agent/protocol-mode.js +51 -0
- package/dist/agent/protocol.d.ts +20 -1
- package/dist/agent/protocol.js +169 -39
- package/dist/agent/registry.d.ts +19 -0
- package/dist/agent/registry.js +247 -0
- package/dist/agent/runner.d.ts +127 -0
- package/dist/agent/runner.js +391 -0
- package/dist/agent/shell-ast.d.ts +11 -0
- package/dist/agent/shell-ast.js +204 -0
- package/dist/agent/shell-session.d.ts +51 -0
- package/dist/agent/shell-session.js +611 -0
- package/dist/agent/stream.d.ts +29 -0
- package/dist/agent/stream.js +103 -0
- package/dist/agent/tool-schemas.d.ts +2 -0
- package/dist/agent/tool-schemas.js +82 -0
- package/dist/agent/tools/append_file.d.ts +2 -0
- package/dist/agent/tools/append_file.js +28 -0
- package/dist/agent/tools/apply_patch.d.ts +10 -0
- package/dist/agent/tools/apply_patch.js +92 -0
- package/dist/agent/tools/copy_file.d.ts +2 -0
- package/dist/agent/tools/copy_file.js +53 -0
- package/dist/agent/tools/delete_file.d.ts +2 -0
- package/dist/agent/tools/delete_file.js +43 -0
- package/dist/agent/tools/grep.d.ts +2 -0
- package/dist/agent/tools/grep.js +54 -0
- package/dist/agent/tools/index.d.ts +2 -0
- package/dist/agent/tools/index.js +32 -0
- package/dist/agent/tools/list_files.d.ts +2 -0
- package/dist/agent/tools/list_files.js +17 -0
- package/dist/agent/tools/make_dir.d.ts +2 -0
- package/dist/agent/tools/make_dir.js +26 -0
- package/dist/agent/tools/move_path.d.ts +9 -0
- package/dist/agent/tools/move_path.js +73 -0
- package/dist/agent/tools/paths.d.ts +7 -0
- package/dist/agent/tools/paths.js +84 -0
- package/dist/agent/tools/pwd.d.ts +2 -0
- package/dist/agent/tools/pwd.js +10 -0
- package/dist/agent/tools/read_file.d.ts +2 -0
- package/dist/agent/tools/read_file.js +81 -0
- package/dist/agent/tools/read_many.d.ts +2 -0
- package/dist/agent/tools/read_many.js +43 -0
- package/dist/agent/tools/readers.d.ts +58 -0
- package/dist/agent/tools/readers.js +426 -0
- package/dist/agent/tools/replace_in_file.d.ts +2 -0
- package/dist/agent/tools/replace_in_file.js +63 -0
- package/dist/agent/tools/shell.d.ts +2 -0
- package/dist/agent/tools/shell.js +48 -0
- package/dist/agent/tools/stat_path.d.ts +2 -0
- package/dist/agent/tools/stat_path.js +31 -0
- package/dist/agent/tools/types.d.ts +65 -0
- package/dist/agent/tools/types.js +1 -0
- package/dist/agent/tools/validate.d.ts +19 -0
- package/dist/agent/tools/validate.js +92 -0
- package/dist/agent/tools/write_file.d.ts +2 -0
- package/dist/agent/tools/write_file.js +28 -0
- package/dist/agent/tools.d.ts +3 -1
- package/dist/agent/tools.js +2 -377
- package/dist/app.d.ts +2 -1
- package/dist/app.js +846 -173
- package/dist/cli-main.js +363 -0
- package/dist/cli.js +444 -333
- package/dist/commands/about.js +1 -1
- package/dist/commands/attach.d.ts +9 -0
- package/dist/commands/attach.js +15 -0
- package/dist/commands/bg.d.ts +7 -0
- package/dist/commands/bg.js +9 -0
- package/dist/commands/cancel.d.ts +10 -0
- package/dist/commands/cancel.js +10 -0
- package/dist/commands/clear.d.ts +1 -0
- package/dist/commands/clear.js +1 -1
- package/dist/commands/compact.d.ts +22 -0
- package/dist/commands/compact.js +34 -0
- package/dist/commands/context.d.ts +7 -0
- package/dist/commands/context.js +25 -0
- package/dist/commands/edit.d.ts +10 -0
- package/dist/commands/edit.js +36 -0
- package/dist/commands/effort.d.ts +25 -0
- package/dist/commands/effort.js +48 -0
- package/dist/commands/expand.d.ts +10 -0
- package/dist/commands/expand.js +8 -0
- package/dist/commands/index.js +24 -0
- package/dist/commands/jobs.d.ts +7 -0
- package/dist/commands/jobs.js +17 -0
- package/dist/commands/kill.d.ts +7 -0
- package/dist/commands/kill.js +13 -0
- package/dist/commands/login.d.ts +6 -4
- package/dist/commands/login.js +24 -22
- package/dist/commands/logout.js +8 -16
- package/dist/commands/model.d.ts +1 -0
- package/dist/commands/model.js +10 -22
- package/dist/commands/provider.d.ts +1 -2
- package/dist/commands/provider.js +4 -11
- package/dist/commands/skills.js +34 -4
- package/dist/commands/steer.d.ts +11 -0
- package/dist/commands/steer.js +13 -0
- package/dist/commands/threads.d.ts +9 -0
- package/dist/commands/threads.js +26 -0
- package/dist/commands/whoami.js +6 -5
- package/dist/config/store.d.ts +53 -12
- package/dist/config/store.js +163 -73
- package/dist/config/trust.d.ts +30 -8
- package/dist/config/trust.js +139 -14
- package/dist/headless.d.ts +7 -0
- package/dist/headless.js +213 -0
- package/dist/index.js +19 -3
- package/dist/lib/debug.d.ts +2 -0
- package/dist/lib/debug.js +22 -0
- package/dist/lib/diagnostics.d.ts +48 -4
- package/dist/lib/diagnostics.js +90 -8
- package/dist/lib/highlight-subset.d.ts +21 -0
- package/dist/lib/highlight-subset.js +88 -0
- package/dist/lib/http.d.ts +11 -1
- package/dist/lib/http.js +62 -4
- package/dist/lib/lifecycle.d.ts +5 -0
- package/dist/lib/lifecycle.js +62 -0
- package/dist/lib/markdown.js +22 -4
- package/dist/lib/model-meta.d.ts +19 -0
- package/dist/lib/model-meta.js +113 -0
- package/dist/lib/native-tools.d.ts +12 -0
- package/dist/lib/native-tools.js +21 -0
- package/dist/lib/net.d.ts +33 -0
- package/dist/lib/net.js +168 -0
- package/dist/patch/apply.d.ts +15 -0
- package/dist/patch/apply.js +172 -0
- package/dist/patch/parse.d.ts +29 -0
- package/dist/patch/parse.js +112 -0
- package/dist/patch/plan.d.ts +49 -0
- package/dist/patch/plan.js +273 -0
- package/dist/providers/anthropic.d.ts +1 -0
- package/dist/providers/anthropic.js +12 -0
- package/dist/providers/custom.d.ts +9 -0
- package/dist/providers/deepseek.d.ts +10 -0
- package/dist/providers/deepseek.js +3 -2
- package/dist/providers/google.d.ts +9 -0
- package/dist/providers/groq.d.ts +10 -0
- package/dist/providers/groq.js +2 -1
- package/dist/providers/index.d.ts +10 -201
- package/dist/providers/index.js +30 -0
- package/dist/providers/mistral.d.ts +10 -0
- package/dist/providers/mistral.js +2 -1
- package/dist/providers/moonshot.d.ts +10 -0
- package/dist/providers/moonshot.js +2 -1
- package/dist/providers/ollama.d.ts +9 -0
- package/dist/providers/openai.d.ts +10 -0
- package/dist/providers/openai.js +2 -1
- package/dist/providers/openaiCompatible.d.ts +33 -2
- package/dist/providers/openaiCompatible.js +78 -16
- package/dist/providers/openrouter.d.ts +10 -0
- package/dist/providers/openrouter.js +2 -1
- package/dist/providers/qwen.d.ts +10 -0
- package/dist/providers/qwen.js +2 -1
- package/dist/providers/spec.d.ts +12 -0
- package/dist/providers/spec.js +29 -0
- package/dist/providers/xai.d.ts +10 -0
- package/dist/providers/xai.js +2 -1
- package/dist/providers/zhipu.d.ts +10 -0
- package/dist/providers/zhipu.js +2 -1
- package/dist/session/log.d.ts +49 -14
- package/dist/session/log.js +236 -11
- package/dist/session/projection.d.ts +76 -0
- package/dist/session/projection.js +526 -0
- package/dist/skills/manager.d.ts +15 -0
- package/dist/skills/manager.js +135 -18
- package/dist/types.d.ts +75 -1
- package/dist/ui/Message.d.ts +1 -7
- package/dist/ui/Message.js +13 -2
- package/dist/ui/Picker.d.ts +8 -0
- package/dist/ui/Picker.js +24 -0
- package/dist/ui/Prompt.d.ts +1 -2
- package/dist/ui/Prompt.js +2 -2
- package/dist/ui/Spinner.js +11 -5
- package/dist/ui/StatusBar.d.ts +2 -1
- package/dist/ui/StatusBar.js +6 -5
- package/package.json +3 -2
- package/src/agent/approval-json.ts +103 -0
- package/src/agent/approval-native.ts +168 -0
- package/src/agent/approval-store.ts +38 -0
- package/src/agent/approval.ts +94 -0
- package/src/agent/compaction.ts +321 -0
- package/src/agent/context.ts +97 -0
- package/src/agent/guards.ts +1239 -19
- package/src/agent/ignore.ts +147 -0
- package/src/agent/policy/defaults.ts +34 -0
- package/src/agent/policy/egress.ts +335 -0
- package/src/agent/process-tree.ts +114 -0
- package/src/agent/protocol-mode.ts +59 -0
- package/src/agent/protocol.ts +191 -40
- package/src/agent/registry.ts +270 -0
- package/src/agent/runner.ts +486 -0
- package/src/agent/shell-ast.ts +227 -0
- package/src/agent/shell-session.ts +673 -0
- package/src/agent/stream.ts +143 -0
- package/src/agent/tool-schemas.ts +86 -0
- package/src/agent/tools/append_file.ts +30 -0
- package/src/agent/tools/apply_patch.ts +95 -0
- package/src/agent/tools/copy_file.ts +56 -0
- package/src/agent/tools/delete_file.ts +45 -0
- package/src/agent/tools/grep.ts +57 -0
- package/src/agent/tools/index.ts +34 -0
- package/src/agent/tools/list_files.ts +19 -0
- package/src/agent/tools/make_dir.ts +28 -0
- package/src/agent/tools/move_path.ts +83 -0
- package/src/agent/tools/paths.ts +92 -0
- package/src/agent/tools/pwd.ts +12 -0
- package/src/agent/tools/read_file.ts +84 -0
- package/src/agent/tools/read_many.ts +45 -0
- package/src/agent/tools/readers.ts +491 -0
- package/src/agent/tools/replace_in_file.ts +66 -0
- package/src/agent/tools/shell.ts +53 -0
- package/src/agent/tools/stat_path.ts +33 -0
- package/src/agent/tools/types.ts +84 -0
- package/src/agent/tools/validate.ts +104 -0
- package/src/agent/tools/write_file.ts +30 -0
- package/src/agent/tools.ts +3 -377
- package/src/app.ts +853 -170
- package/src/{cli.ts → cli-main.ts} +64 -9
- package/src/commands/about.ts +1 -1
- package/src/commands/attach.ts +16 -0
- package/src/commands/bg.ts +9 -0
- package/src/commands/cancel.ts +10 -0
- package/src/commands/clear.ts +1 -1
- package/src/commands/compact.ts +35 -0
- package/src/commands/context.ts +26 -0
- package/src/commands/edit.ts +37 -0
- package/src/commands/effort.ts +49 -0
- package/src/commands/expand.ts +8 -0
- package/src/commands/index.ts +24 -0
- package/src/commands/jobs.ts +18 -0
- package/src/commands/kill.ts +14 -0
- package/src/commands/login.ts +24 -22
- package/src/commands/logout.ts +8 -16
- package/src/commands/model.ts +10 -22
- package/src/commands/provider.ts +4 -11
- package/src/commands/skills.ts +34 -4
- package/src/commands/steer.ts +13 -0
- package/src/commands/threads.ts +28 -0
- package/src/commands/whoami.ts +6 -5
- package/src/config/store.ts +168 -77
- package/src/config/trust.ts +181 -22
- package/src/headless.ts +225 -0
- package/src/index.ts +19 -3
- package/src/lib/debug.ts +24 -0
- package/src/lib/diagnostics.ts +90 -8
- package/src/lib/highlight-subset.ts +93 -0
- package/src/lib/http.ts +66 -4
- package/src/lib/lifecycle.ts +69 -0
- package/src/lib/markdown.ts +22 -4
- package/src/lib/model-meta.ts +138 -0
- package/src/lib/native-tools.ts +32 -0
- package/src/lib/net.ts +204 -0
- package/src/patch/apply.ts +194 -0
- package/src/patch/parse.ts +131 -0
- package/src/patch/plan.ts +317 -0
- package/src/providers/anthropic.ts +13 -1
- package/src/providers/deepseek.ts +3 -2
- package/src/providers/groq.ts +2 -1
- package/src/providers/index.ts +32 -1
- package/src/providers/mistral.ts +2 -1
- package/src/providers/moonshot.ts +2 -1
- package/src/providers/openai.ts +2 -1
- package/src/providers/openaiCompatible.ts +104 -19
- package/src/providers/openrouter.ts +2 -1
- package/src/providers/qwen.ts +2 -1
- package/src/providers/spec.ts +42 -0
- package/src/providers/xai.ts +2 -1
- package/src/providers/zhipu.ts +2 -1
- package/src/session/log.ts +311 -29
- package/src/session/projection.ts +621 -0
- package/src/skills/manager.ts +140 -18
- package/src/types/highlight-shims.d.ts +14 -0
- package/src/types.ts +79 -2
- package/src/ui/Message.ts +13 -2
- package/src/ui/Picker.ts +24 -0
- package/src/ui/Prompt.ts +2 -2
- package/src/ui/Spinner.ts +11 -5
- package/src/ui/StatusBar.ts +6 -5
- package/tsconfig.json +1 -0
- /package/dist/{cli.d.ts → cli-main.d.ts} +0 -0
package/LICENSE
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
3
|
Copyright (c) 2026 SimpleLittleDev
|
|
4
|
+
Copyright (c) 2026 FastCode-Termux contributors
|
|
5
|
+
|
|
6
|
+
This project is a fix and Termux adaptation based on SimpleLittleDev/FastCode,
|
|
7
|
+
originally licensed under MIT.
|
|
4
8
|
|
|
5
9
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
10
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -16,16 +16,24 @@
|
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
npm install -g @datename/fastcode-termux
|
|
19
|
+
npm install -g @datename/fastcode-termux@0.1.8-beta.0
|
|
20
20
|
fastcode
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
> Registry check (2026-11-09): npm still reports `latest=0.1.6` / `beta=0.1.6-beta`. If `@0.1.8-beta.0` is not resolvable yet, use the repo/portable build or `@0.1.6-beta`.
|
|
24
|
+
|
|
23
25
|
Short alias included:
|
|
24
26
|
|
|
25
27
|
```bash
|
|
26
28
|
fc
|
|
27
29
|
```
|
|
28
30
|
|
|
31
|
+
This tree is **0.1.8-beta.0**, based on tag `v0.1.7` plus the 2026-11-09
|
|
32
|
+
fixes (shell safety, `/threads`, exit protection, approval feedback,
|
|
33
|
+
compaction/projection, TUI effort, license). It is built and packaged locally
|
|
34
|
+
but not published. The npm registry still reports `latest=0.1.6` /
|
|
35
|
+
`beta=0.1.6-beta` (checked 2026-11-09); `release/` contains 0.1.8-beta.0 assets.
|
|
36
|
+
|
|
29
37
|
---
|
|
30
38
|
|
|
31
39
|
## 🌈 What Is FastCode?
|
|
@@ -80,7 +88,7 @@ npm link
|
|
|
80
88
|
### Global install
|
|
81
89
|
|
|
82
90
|
```bash
|
|
83
|
-
npm install -g @datename/fastcode-termux
|
|
91
|
+
npm install -g @datename/fastcode-termux@0.1.8-beta.0
|
|
84
92
|
fastcode
|
|
85
93
|
```
|
|
86
94
|
|
|
@@ -108,7 +116,7 @@ export OPENAI_API_KEY="sk-..."
|
|
|
108
116
|
export OPENROUTER_API_KEY="sk-or-..."
|
|
109
117
|
```
|
|
110
118
|
|
|
111
|
-
Or bind a key from inside FastCode —
|
|
119
|
+
Or bind a key from inside FastCode — one key per provider, reused by all its models (input is masked):
|
|
112
120
|
|
|
113
121
|
```text
|
|
114
122
|
/login # guided: provider → model → key
|
|
@@ -133,7 +141,7 @@ fastcode
|
|
|
133
141
|
| `google` | `gemini-1.5-flash` | `GEMINI_API_KEY` or `GOOGLE_API_KEY` |
|
|
134
142
|
| `openrouter` | `anthropic/claude-3.5-sonnet` | `OPENROUTER_API_KEY` |
|
|
135
143
|
| `groq` | `llama-3.3-70b-versatile` | `GROQ_API_KEY` |
|
|
136
|
-
| `deepseek` | `deepseek-
|
|
144
|
+
| `deepseek` | `deepseek-flash` | `DEEPSEEK_API_KEY` |
|
|
137
145
|
| `mistral` | `mistral-small-latest` | `MISTRAL_API_KEY` |
|
|
138
146
|
| `ollama` | `llama3.2` | Local, no API key required |
|
|
139
147
|
|
|
@@ -237,8 +245,29 @@ Installed plugins live under:
|
|
|
237
245
|
|
|
238
246
|
---
|
|
239
247
|
|
|
248
|
+
## ⌨️ CLI Commands
|
|
249
|
+
|
|
250
|
+
These subcommands do not start a conversation:
|
|
251
|
+
|
|
252
|
+
| Command | Action |
|
|
253
|
+
| --- | --- |
|
|
254
|
+
| `fastcode` / `fastcode "prompt"` | open TUI / open with an initial prompt |
|
|
255
|
+
| `fastcode --version` / `-v` / `--help` / `-h` | version / all commands |
|
|
256
|
+
| `fastcode provider [id]` / `model [id]` | list or set default provider / model |
|
|
257
|
+
| `fastcode config` / `config path` | config + paths / config.json path only |
|
|
258
|
+
| `fastcode sessions` / `sessions show <id>` / `resume [id\|latest]` | list / inspect / resume sessions |
|
|
259
|
+
| `fastcode trust [dir]` / `untrust [dir]` / `trusted` / `trust --forget <key>` | trust management |
|
|
260
|
+
| `fastcode skills list\|install <id>\|uninstall <id>\|search <q>` | skill management |
|
|
261
|
+
| `fastcode doctor` / `doctor --net` | diagnostics / probe registry |
|
|
262
|
+
| `fastcode update` | check for updates |
|
|
263
|
+
| `--ui tty\|native\|plain\|json` | approval/render backend |
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
240
267
|
## 🎮 Slash Commands
|
|
241
268
|
|
|
269
|
+
These run in the TUI without sending a model request:
|
|
270
|
+
|
|
242
271
|
| Command | Action |
|
|
243
272
|
| --- | --- |
|
|
244
273
|
| `/help` | Show command list |
|
|
@@ -246,26 +275,36 @@ Installed plugins live under:
|
|
|
246
275
|
| `/tools` | Show available coding tools |
|
|
247
276
|
| `/cwd` | Show agent sandbox root |
|
|
248
277
|
| `/plugin ...` | Import and manage always-active plugin packs |
|
|
249
|
-
| `/provider [id]` |
|
|
250
|
-
| `/model [id]` |
|
|
251
|
-
| `/
|
|
252
|
-
| `/
|
|
278
|
+
| `/provider [id]` | Pick or switch provider (interactive when omitted) |
|
|
279
|
+
| `/model [id]` | Pick or switch model (interactive when omitted) |
|
|
280
|
+
| `/effort [level]` | Thinking effort: auto / low / medium / high / max |
|
|
281
|
+
| `/login [provider] [model] [key]` | Bind a provider-wide API key (guided when omitted) |
|
|
282
|
+
| `/logout <provider>` | Remove the provider's key (`--all` accepted for compatibility) |
|
|
253
283
|
| `/skills ...` | Search, install, enable, disable, list skills |
|
|
254
284
|
| `/theme [id]` | Switch theme |
|
|
255
285
|
| `/cost` | Show token and cost usage |
|
|
256
286
|
| `/system [text]` | Show or update system prompt |
|
|
257
287
|
| `/whoami` | Show configured provider keys |
|
|
258
288
|
| `/config` | Print config and paths |
|
|
259
|
-
| `/clear` | Clear conversation |
|
|
289
|
+
| `/clear` (`cls`) | Clear conversation |
|
|
260
290
|
| `/reset` | Reset config |
|
|
261
291
|
| `/about` | Show version and platform |
|
|
262
|
-
| `/
|
|
292
|
+
| `/context` | Token estimate, compaction threshold, heap |
|
|
293
|
+
| `/compact` | Force local context compaction |
|
|
294
|
+
| `/cancel` | Abort the active run (mobile alternative to Esc) |
|
|
295
|
+
| `/steer <text>` | Guide the next request of an active run |
|
|
296
|
+
| `/jobs` / `/attach <id>` / `/kill <id>` | Background shell sessions |
|
|
297
|
+
| `/threads [1-6]` | Show or set resident shell concurrency (default 2, max 6) |
|
|
298
|
+
| `/expand` | Toggle full/collapsed tool output |
|
|
299
|
+
| `/edit` | Edit the prompt in `$EDITOR` (works around Ink IME bugs) |
|
|
300
|
+
| `/bg` | Explain background shell behaviour |
|
|
301
|
+
| `/exit` | Abort the active run, kill resident shells, then quit |
|
|
263
302
|
|
|
264
303
|
Keyboard controls:
|
|
265
304
|
|
|
266
305
|
```text
|
|
267
|
-
Esc cancel streaming
|
|
268
|
-
Ctrl+C press twice to quit
|
|
306
|
+
Esc cancel streaming; partial output is kept
|
|
307
|
+
Ctrl+C during a run: first cancels, second quits; idle: press twice to quit
|
|
269
308
|
```
|
|
270
309
|
|
|
271
310
|
---
|
|
@@ -296,10 +335,12 @@ Install Node.js first:
|
|
|
296
335
|
|
|
297
336
|
```bash
|
|
298
337
|
pkg install nodejs
|
|
299
|
-
npm install -g @datename/fastcode-termux
|
|
338
|
+
npm install -g @datename/fastcode-termux@0.1.8-beta.0
|
|
300
339
|
fastcode
|
|
301
340
|
```
|
|
302
341
|
|
|
342
|
+
> If npm does not resolve `@0.1.8-beta.0` yet, install the portable bundle or use `@0.1.6-beta`.
|
|
343
|
+
|
|
303
344
|
If the command is not found, check your npm global binary path:
|
|
304
345
|
|
|
305
346
|
```bash
|
|
@@ -381,14 +422,22 @@ npm publish --access public
|
|
|
381
422
|
|
|
382
423
|
---
|
|
383
424
|
|
|
384
|
-
## Safety, sessions and limits (0.1.
|
|
425
|
+
## Safety, sessions and limits (0.1.8-beta.0)
|
|
385
426
|
|
|
386
427
|
| Feature | Behaviour | Override |
|
|
387
428
|
| --- | --- | --- |
|
|
388
429
|
| Project trust | First run in a folder asks before agent tools load; the list lives in ~/.fastcode/trusted.json | fastcode trust / untrust / trusted, FASTCODE_TRUST_ALL=1 |
|
|
389
|
-
| Dangerous-command guard |
|
|
430
|
+
| Dangerous-command guard | Hard Block for destructive/system/sensitive-file commands and credential exfil; `.env` writes/deletes/moves, critical credential reads and egress are non-overridable | FASTCODE_ALLOW_DANGEROUS=1 only for Soft Gate |
|
|
431
|
+
| Egress policy | application-layer tool/payload/output policy; default audit-only, `enabled:true + mode:"block"` returns `EGRESS_DENY` | `config.egress`: enabled, mode, denyPaths, denyGlobs, denyContentMarkers, maxScanBytes |
|
|
390
432
|
| Unified tool-output cap | Output is truncated while it is produced, not sliced afterwards (default 30000 characters) | FASTCODE_TOOL_OUTPUT_LIMIT |
|
|
391
433
|
| Session log | Append-only JSONL in ~/.fastcode/sessions, resumable after a crash (cache cap 256MB by default) | fastcode sessions, fastcode resume, FASTCODE_SESSION_LOG=0, FASTCODE_SESSION_CACHE_MB |
|
|
434
|
+
| Checkpoints/projection | `*.projection.json` (~200KB cap) with tool/model/cwd fingerprint; tool payloads folded to edit facts, dropped-message markers, summary markers protected; unpaired tool calls completed on resume | FASTCODE_FSYNC, FASTCODE_SESSION_LOG |
|
|
435
|
+
| Exit save point | `/exit` aborts the run, stops shells, then writes `session_end{reason,compaction}` + projection and best-effort fsyncs both; busy runs require a second `/exit` within 5s; Esc/Ctrl+C keep partial output | `/exit` (`/quit`, `/q`), double Ctrl+C |
|
|
436
|
+
| Patch transaction | parse → plan → apply; CRLF/mode kept; symlinks refused; move never overwrites | `/expand` for the full diff |
|
|
437
|
+
| Process tree | abort or `/kill` kills children/grandchildren; `/exit` uses SIGTERM → wait → SIGKILL for resident shells | POSIX process group, Windows `taskkill /T` |
|
|
438
|
+
| Compaction | token/heap threshold; Notes/Outcomes/Files first; summary written to `summary-*.md`; `/compact` records a `compacted` fact; old summaries cleaned | FASTCODE_COMPACTION=0, FASTCODE_COMPACTION_RATIO, FASTCODE_COMPACTION_KEEP_TURNS, FASTCODE_COMPACTION_SUMMARY_TOKENS, FASTCODE_HEAP_LIMIT_MB |
|
|
439
|
+
| Background shell | Commands over ~2s return a session id; poll incrementally. `/threads` caps the **resident** pool (default 2, max 6); one-shot commands use a separate `FASTCODE_MAX_SPAWN_PER_TURN` budget and are not globally capped | `/jobs`, `/attach`, `/kill`, `/threads`, FASTCODE_MAX_SPAWN_PER_TURN |
|
|
440
|
+
| Approval hook | Sensitive/large patches need approval; without a resolver they are denied (fail closed) | pending UI not implemented yet |
|
|
392
441
|
|
|
393
442
|
## 🙏 Credits
|
|
394
443
|
|
|
@@ -398,4 +447,6 @@ npm publish --access public
|
|
|
398
447
|
|
|
399
448
|
## 📜 License
|
|
400
449
|
|
|
401
|
-
MIT © SimpleLittleDev
|
|
450
|
+
MIT. Original FastCode © 2026 SimpleLittleDev; this fix/Termux adaptation © 2026
|
|
451
|
+
FastCode-Termux contributors. Based on `SimpleLittleDev/FastCode`
|
|
452
|
+
(`@simplelittledev/fastcode` 0.1.3), originally MIT. See `LICENSE`.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable NDJSON gate protocol for GUI/JSON clients.
|
|
3
|
+
*
|
|
4
|
+
* This module is pure: it validates and encodes one request/decision at a
|
|
5
|
+
* time. A headless transport can consume it without leaking TUI output to
|
|
6
|
+
* stdout. Hard Block requests never reach this code.
|
|
7
|
+
*/
|
|
8
|
+
import type { ApprovalDecision, ApprovalRequest } from "./approval.js";
|
|
9
|
+
export declare const GATE_PROTOCOL_VERSION = 1;
|
|
10
|
+
export interface GateAskEvent {
|
|
11
|
+
event: "gate:ask";
|
|
12
|
+
version: number;
|
|
13
|
+
id: string;
|
|
14
|
+
tier: number;
|
|
15
|
+
action: string;
|
|
16
|
+
reason: string;
|
|
17
|
+
prompt: string;
|
|
18
|
+
source: string;
|
|
19
|
+
targets: string[];
|
|
20
|
+
diffstat: {
|
|
21
|
+
files: number;
|
|
22
|
+
lines: number;
|
|
23
|
+
};
|
|
24
|
+
options: Array<{
|
|
25
|
+
id: string;
|
|
26
|
+
label: string;
|
|
27
|
+
kind: string;
|
|
28
|
+
default?: boolean;
|
|
29
|
+
}>;
|
|
30
|
+
default: string;
|
|
31
|
+
require_input?: string;
|
|
32
|
+
}
|
|
33
|
+
export declare function encodeGateAsk(request: ApprovalRequest): GateAskEvent;
|
|
34
|
+
export interface GateDecisionResult {
|
|
35
|
+
decision: ApprovalDecision;
|
|
36
|
+
error?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Accept only a well-formed decision for this exact request id and one of its
|
|
40
|
+
* stable option ids. Malformed JSON, wrong ids, unknown options and missing
|
|
41
|
+
* required input all fail closed.
|
|
42
|
+
*/
|
|
43
|
+
export declare function parseGateDecision(request: ApprovalRequest, line: string): GateDecisionResult;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { decisionForOption } from "./approval.js";
|
|
2
|
+
export const GATE_PROTOCOL_VERSION = 1;
|
|
3
|
+
function optionId(option, index) {
|
|
4
|
+
return option.id ?? `${option.kind}-${index}`;
|
|
5
|
+
}
|
|
6
|
+
function targetsFor(request) {
|
|
7
|
+
const args = request.args ?? {};
|
|
8
|
+
const values = [args.path, args.from, args.to, args.cwd, request.preview?.kind === "diff" ? "diff" : undefined];
|
|
9
|
+
return values.filter((value) => typeof value === "string" && value.length > 0);
|
|
10
|
+
}
|
|
11
|
+
export function encodeGateAsk(request) {
|
|
12
|
+
const options = request.options.map((option, index) => ({
|
|
13
|
+
id: optionId(option, index),
|
|
14
|
+
label: option.label,
|
|
15
|
+
kind: option.kind,
|
|
16
|
+
...(index === (request.defaultIndex ?? 0) ? { default: true } : {}),
|
|
17
|
+
}));
|
|
18
|
+
const defaultIndex = Math.max(0, Math.min(request.defaultIndex ?? 0, options.length - 1));
|
|
19
|
+
const text = request.preview?.kind === "diff" ? request.preview.text : "";
|
|
20
|
+
const feedbackOption = request.options.find((option) => option.kind === "feedback");
|
|
21
|
+
const requireInput = request.freeInput?.prompt ?? feedbackOption?.prompt;
|
|
22
|
+
return {
|
|
23
|
+
event: "gate:ask",
|
|
24
|
+
version: GATE_PROTOCOL_VERSION,
|
|
25
|
+
id: request.id,
|
|
26
|
+
tier: request.tier ?? 2,
|
|
27
|
+
action: request.tool,
|
|
28
|
+
reason: request.reason,
|
|
29
|
+
prompt: request.prompt ?? "Approval required",
|
|
30
|
+
source: request.source ?? "system",
|
|
31
|
+
targets: targetsFor(request),
|
|
32
|
+
diffstat: { files: text ? text.split(/\r?\n/).filter(Boolean).length : 0, lines: text ? text.split(/\r?\n/).length : 0 },
|
|
33
|
+
options,
|
|
34
|
+
default: options[defaultIndex]?.id ?? "",
|
|
35
|
+
...(requireInput ? { require_input: requireInput } : {}),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Accept only a well-formed decision for this exact request id and one of its
|
|
40
|
+
* stable option ids. Malformed JSON, wrong ids, unknown options and missing
|
|
41
|
+
* required input all fail closed.
|
|
42
|
+
*/
|
|
43
|
+
export function parseGateDecision(request, line) {
|
|
44
|
+
let payload;
|
|
45
|
+
try {
|
|
46
|
+
payload = JSON.parse(line);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return { decision: { kind: "deny", reason: "malformed JSON decision" }, error: "malformed JSON" };
|
|
50
|
+
}
|
|
51
|
+
if (!payload || typeof payload !== "object" || payload.version !== GATE_PROTOCOL_VERSION)
|
|
52
|
+
return { decision: { kind: "deny", reason: "unsupported gate protocol version" }, error: "version" };
|
|
53
|
+
if (payload.event !== "gate:decision")
|
|
54
|
+
return { decision: { kind: "deny", reason: "unexpected gate event" }, error: "event" };
|
|
55
|
+
if (payload.id !== request.id)
|
|
56
|
+
return { decision: { kind: "deny", reason: "decision id does not match the pending request" }, error: "id" };
|
|
57
|
+
if (request.freeInput?.enabled && (typeof payload.input !== "string" || payload.input.trim() === ""))
|
|
58
|
+
return { decision: { kind: "deny", reason: "required free input is missing" }, error: "input" };
|
|
59
|
+
const option = request.options.find((entry, index) => optionId(entry, index) === payload.option);
|
|
60
|
+
if (!option)
|
|
61
|
+
return { decision: { kind: "deny", reason: "unknown approval option id" }, error: "option" };
|
|
62
|
+
if (option.kind === "feedback") {
|
|
63
|
+
const text = typeof payload.input === "string" ? payload.input.trim() : "";
|
|
64
|
+
if (!text)
|
|
65
|
+
return { decision: { kind: "deny", reason: "feedback input is required" }, error: "feedback" };
|
|
66
|
+
return { decision: { kind: "feedback", text } };
|
|
67
|
+
}
|
|
68
|
+
return { decision: decisionForOption(option) };
|
|
69
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type ApprovalDecision, type ApprovalRequest } from "./approval.js";
|
|
2
|
+
export declare function nativeDecisionFromPayload(request: ApprovalRequest, payload: any): ApprovalDecision | undefined;
|
|
3
|
+
export declare function nativeApproval(request: ApprovalRequest, signal?: AbortSignal): Promise<ApprovalDecision | undefined>;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Termux:API approval sheet.
|
|
3
|
+
*
|
|
4
|
+
* Returns undefined when termux-dialog is missing/times out/crashes so the
|
|
5
|
+
* caller can fall back to the TUI. A real cancellation returns a deny.
|
|
6
|
+
* The command is built with spawn args (no shell interpolation).
|
|
7
|
+
*/
|
|
8
|
+
import { spawn } from "node:child_process";
|
|
9
|
+
import { decisionForOption } from "./approval.js";
|
|
10
|
+
export function nativeDecisionFromPayload(request, payload) {
|
|
11
|
+
if (payload?.code === -1)
|
|
12
|
+
return { kind: "deny", reason: "termux-dialog cancelled" };
|
|
13
|
+
if (payload?.code !== 0 || !Number.isInteger(payload.index) ||
|
|
14
|
+
payload.index < 0 || payload.index >= request.options.length)
|
|
15
|
+
return { kind: "deny", reason: "termux-dialog returned an invalid selection" };
|
|
16
|
+
return decisionForOption(request.options[payload.index]);
|
|
17
|
+
}
|
|
18
|
+
async function nativeFeedback(prompt, signal, timeoutMs = 60_000) {
|
|
19
|
+
if (signal?.aborted)
|
|
20
|
+
return undefined;
|
|
21
|
+
return await new Promise((resolve) => {
|
|
22
|
+
let settled = false;
|
|
23
|
+
let child;
|
|
24
|
+
let timer;
|
|
25
|
+
const finish = (value) => {
|
|
26
|
+
if (settled)
|
|
27
|
+
return;
|
|
28
|
+
settled = true;
|
|
29
|
+
if (timer)
|
|
30
|
+
clearTimeout(timer);
|
|
31
|
+
signal?.removeEventListener("abort", onAbort);
|
|
32
|
+
resolve(value);
|
|
33
|
+
};
|
|
34
|
+
const onAbort = () => {
|
|
35
|
+
try {
|
|
36
|
+
child?.kill("SIGKILL");
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Already gone.
|
|
40
|
+
}
|
|
41
|
+
finish(undefined);
|
|
42
|
+
};
|
|
43
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
44
|
+
child = spawn("termux-dialog", ["text", "-t", prompt], {
|
|
45
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
46
|
+
windowsHide: true,
|
|
47
|
+
});
|
|
48
|
+
let stdout = "";
|
|
49
|
+
child.stdout?.setEncoding("utf8");
|
|
50
|
+
child.stdout?.on("data", (chunk) => {
|
|
51
|
+
stdout += chunk;
|
|
52
|
+
});
|
|
53
|
+
child.on("error", () => finish(undefined));
|
|
54
|
+
child.on("close", () => {
|
|
55
|
+
let payload;
|
|
56
|
+
try {
|
|
57
|
+
payload = JSON.parse(stdout.trim());
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
finish(undefined);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (payload?.code === 0 && typeof payload.text === "string")
|
|
64
|
+
finish(payload.text);
|
|
65
|
+
else
|
|
66
|
+
finish(undefined);
|
|
67
|
+
});
|
|
68
|
+
timer = setTimeout(() => {
|
|
69
|
+
try {
|
|
70
|
+
child?.kill("SIGKILL");
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Already gone.
|
|
74
|
+
}
|
|
75
|
+
finish(undefined);
|
|
76
|
+
}, Math.max(1000, timeoutMs));
|
|
77
|
+
timer.unref?.();
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
export async function nativeApproval(request, signal) {
|
|
81
|
+
if (signal?.aborted)
|
|
82
|
+
return { kind: "deny", reason: "approval cancelled" };
|
|
83
|
+
const labels = request.options.map((option) => option.label);
|
|
84
|
+
const title = request.prompt ?? `Approval: ${request.tool}`;
|
|
85
|
+
return await new Promise((resolve) => {
|
|
86
|
+
let settled = false;
|
|
87
|
+
let child;
|
|
88
|
+
let timer;
|
|
89
|
+
const finish = (value) => {
|
|
90
|
+
if (settled)
|
|
91
|
+
return;
|
|
92
|
+
settled = true;
|
|
93
|
+
if (timer)
|
|
94
|
+
clearTimeout(timer);
|
|
95
|
+
signal?.removeEventListener("abort", onAbort);
|
|
96
|
+
resolve(value);
|
|
97
|
+
};
|
|
98
|
+
const onAbort = () => {
|
|
99
|
+
try {
|
|
100
|
+
child?.kill("SIGKILL");
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Already gone.
|
|
104
|
+
}
|
|
105
|
+
finish({ kind: "deny", reason: "approval cancelled" });
|
|
106
|
+
};
|
|
107
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
108
|
+
child = spawn("termux-dialog", ["sheet", "-v", labels.join(","), "-t", title], {
|
|
109
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
110
|
+
windowsHide: true,
|
|
111
|
+
});
|
|
112
|
+
let stdout = "";
|
|
113
|
+
child.stdout?.setEncoding("utf8");
|
|
114
|
+
child.stdout?.on("data", (chunk) => {
|
|
115
|
+
stdout += chunk;
|
|
116
|
+
});
|
|
117
|
+
child.on("error", () => finish(undefined));
|
|
118
|
+
child.on("close", async () => {
|
|
119
|
+
let payload;
|
|
120
|
+
try {
|
|
121
|
+
payload = JSON.parse(stdout.trim());
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
finish(undefined);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const selected = payload?.code === 0 && Number.isInteger(payload.index)
|
|
128
|
+
? request.options[payload.index]
|
|
129
|
+
: undefined;
|
|
130
|
+
if (selected?.kind === "feedback") {
|
|
131
|
+
if (timer) {
|
|
132
|
+
clearTimeout(timer);
|
|
133
|
+
timer = undefined;
|
|
134
|
+
}
|
|
135
|
+
const prompt = selected.prompt || "Why should this be denied?";
|
|
136
|
+
const text = await nativeFeedback(prompt, signal, request.timeoutMs);
|
|
137
|
+
if (signal?.aborted) {
|
|
138
|
+
finish({ kind: "deny", reason: "approval cancelled" });
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (typeof text !== "string") {
|
|
142
|
+
// Text dialog unavailable/cancelled: fall back to the TUI prompt.
|
|
143
|
+
finish(undefined);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const feedback = text.trim();
|
|
147
|
+
finish(feedback
|
|
148
|
+
? { kind: "feedback", text: feedback }
|
|
149
|
+
: { kind: "deny", reason: "feedback was empty (fail closed)" });
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
finish(nativeDecisionFromPayload(request, payload));
|
|
153
|
+
});
|
|
154
|
+
timer = setTimeout(() => {
|
|
155
|
+
try {
|
|
156
|
+
child?.kill("SIGKILL");
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
// Already gone.
|
|
160
|
+
}
|
|
161
|
+
finish(undefined);
|
|
162
|
+
}, Math.max(1000, request.timeoutMs ?? 60_000));
|
|
163
|
+
timer.unref?.();
|
|
164
|
+
});
|
|
165
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approval scope storage.
|
|
3
|
+
*
|
|
4
|
+
* - once: no persistence
|
|
5
|
+
* - session: in-memory, implicitly cleared when the process exits
|
|
6
|
+
* - always: ~/.fastcode/trusted.json `tools` entry bound to catalogHash
|
|
7
|
+
*
|
|
8
|
+
* Keys include the normalized tool-call fingerprint so "always" cannot grant
|
|
9
|
+
* every future shell command from one approval.
|
|
10
|
+
*/
|
|
11
|
+
import type { ApprovalScope } from "./approval.js";
|
|
12
|
+
import type { ToolCall } from "./tools/types.js";
|
|
13
|
+
export declare function approvalKey(tool: string, call: ToolCall, cwd: string): string;
|
|
14
|
+
export declare function isPreApproved(key: string, catalogHash: string): boolean;
|
|
15
|
+
export declare function rememberApproval(key: string, catalogHash: string, scope: ApprovalScope): void;
|
|
16
|
+
export declare function clearSessionApprovals(): void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { toolCallFingerprint } from "./guards.js";
|
|
2
|
+
import { isToolApproved, rememberToolApproval } from "../config/trust.js";
|
|
3
|
+
const sessionApprovals = new Set();
|
|
4
|
+
export function approvalKey(tool, call, cwd) {
|
|
5
|
+
return `${tool}:${toolCallFingerprint(call, cwd)}`;
|
|
6
|
+
}
|
|
7
|
+
export function isPreApproved(key, catalogHash) {
|
|
8
|
+
return sessionApprovals.has(`${catalogHash}:${key}`) || isToolApproved(key, catalogHash);
|
|
9
|
+
}
|
|
10
|
+
export function rememberApproval(key, catalogHash, scope) {
|
|
11
|
+
if (scope === "once")
|
|
12
|
+
return;
|
|
13
|
+
if (scope === "session") {
|
|
14
|
+
sessionApprovals.add(`${catalogHash}:${key}`);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
rememberToolApproval(key, catalogHash);
|
|
18
|
+
}
|
|
19
|
+
export function clearSessionApprovals() {
|
|
20
|
+
sessionApprovals.clear();
|
|
21
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export type ApprovalScope = "once" | "session" | "always";
|
|
2
|
+
export type ApprovalReason = "trust" | "sensitive_path" | "ambiguous_match" | "destructive" | "large_diff";
|
|
3
|
+
export interface ChoiceItem {
|
|
4
|
+
label: string;
|
|
5
|
+
value: string;
|
|
6
|
+
}
|
|
7
|
+
export type ApprovalPreview = {
|
|
8
|
+
kind: "diff";
|
|
9
|
+
text: string;
|
|
10
|
+
} | {
|
|
11
|
+
kind: "choices";
|
|
12
|
+
items: ChoiceItem[];
|
|
13
|
+
};
|
|
14
|
+
export type ApprovalOption = {
|
|
15
|
+
kind: "allow";
|
|
16
|
+
id?: string;
|
|
17
|
+
scope: ApprovalScope;
|
|
18
|
+
label: string;
|
|
19
|
+
} | {
|
|
20
|
+
kind: "deny";
|
|
21
|
+
id?: string;
|
|
22
|
+
label: string;
|
|
23
|
+
} | {
|
|
24
|
+
kind: "choose";
|
|
25
|
+
id?: string;
|
|
26
|
+
label: string;
|
|
27
|
+
value: string;
|
|
28
|
+
meta?: {
|
|
29
|
+
recommended?: boolean;
|
|
30
|
+
risk?: string;
|
|
31
|
+
files?: number;
|
|
32
|
+
reason?: string;
|
|
33
|
+
};
|
|
34
|
+
} | {
|
|
35
|
+
kind: "feedback";
|
|
36
|
+
id?: string;
|
|
37
|
+
label: string;
|
|
38
|
+
prompt: string;
|
|
39
|
+
};
|
|
40
|
+
export interface ApprovalRequest {
|
|
41
|
+
id: string;
|
|
42
|
+
tool: string;
|
|
43
|
+
args: unknown;
|
|
44
|
+
reason: ApprovalReason;
|
|
45
|
+
options: ApprovalOption[];
|
|
46
|
+
preview?: ApprovalPreview;
|
|
47
|
+
tier?: 0 | 1 | 2 | 3;
|
|
48
|
+
prompt?: string;
|
|
49
|
+
source?: "system" | "ai" | "free";
|
|
50
|
+
defaultIndex?: number;
|
|
51
|
+
timeoutMs?: number;
|
|
52
|
+
freeInput?: {
|
|
53
|
+
enabled: true;
|
|
54
|
+
prompt: string;
|
|
55
|
+
hint?: string;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export type ApprovalDecision = {
|
|
59
|
+
kind: "allow";
|
|
60
|
+
scope: ApprovalScope;
|
|
61
|
+
} | {
|
|
62
|
+
kind: "deny";
|
|
63
|
+
reason?: string;
|
|
64
|
+
feedback?: string;
|
|
65
|
+
code?: string;
|
|
66
|
+
target?: string;
|
|
67
|
+
} | {
|
|
68
|
+
kind: "pending";
|
|
69
|
+
request: ApprovalRequest;
|
|
70
|
+
} | {
|
|
71
|
+
kind: "choose";
|
|
72
|
+
value: string;
|
|
73
|
+
input?: string;
|
|
74
|
+
} | {
|
|
75
|
+
kind: "feedback";
|
|
76
|
+
text: string;
|
|
77
|
+
};
|
|
78
|
+
export type ApprovalResolver = (request: ApprovalRequest) => ApprovalDecision | Promise<ApprovalDecision>;
|
|
79
|
+
export declare const DEFAULT_ALLOW_OPTIONS: ApprovalOption[];
|
|
80
|
+
export declare function approvalRequestId(): string;
|
|
81
|
+
export declare function approvalOptionForDecision(request: ApprovalRequest, decision: ApprovalDecision): ApprovalOption | undefined;
|
|
82
|
+
export declare function parseApprovalChoice(request: ApprovalRequest, text: string): ApprovalOption | undefined;
|
|
83
|
+
export declare function decisionForOption(option: ApprovalOption, input?: string): ApprovalDecision;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export const DEFAULT_ALLOW_OPTIONS = [
|
|
2
|
+
{ kind: "allow", id: "allow-once", scope: "once", label: "Allow once" },
|
|
3
|
+
{ kind: "allow", id: "allow-session", scope: "session", label: "Allow for this session" },
|
|
4
|
+
{ kind: "allow", id: "allow-always", scope: "always", label: "Always allow" },
|
|
5
|
+
{ kind: "deny", id: "deny", label: "Deny" },
|
|
6
|
+
{ kind: "feedback", id: "feedback", label: "Deny with feedback", prompt: "Why should this be denied?" },
|
|
7
|
+
];
|
|
8
|
+
export function approvalRequestId() {
|
|
9
|
+
return "ap_" + Date.now().toString(36) + "_" + Math.random().toString(36).slice(2, 7);
|
|
10
|
+
}
|
|
11
|
+
export function approvalOptionForDecision(request, decision) {
|
|
12
|
+
if (decision.kind === "allow")
|
|
13
|
+
return request.options.find((option) => option.kind === "allow" && option.scope === decision.scope);
|
|
14
|
+
if (decision.kind === "deny")
|
|
15
|
+
return request.options.find((option) => option.kind === "deny");
|
|
16
|
+
if (decision.kind === "choose")
|
|
17
|
+
return request.options.find((option) => option.kind === "choose" && option.value === decision.value);
|
|
18
|
+
if (decision.kind === "feedback")
|
|
19
|
+
return request.options.find((option) => option.kind === "feedback");
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
export function parseApprovalChoice(request, text) {
|
|
23
|
+
const index = Number(String(text ?? "").trim()) - 1;
|
|
24
|
+
if (!Number.isInteger(index) || index < 0 || index >= request.options.length)
|
|
25
|
+
return undefined;
|
|
26
|
+
return request.options[index];
|
|
27
|
+
}
|
|
28
|
+
export function decisionForOption(option, input) {
|
|
29
|
+
if (option.kind === "allow")
|
|
30
|
+
return { kind: "allow", scope: option.scope };
|
|
31
|
+
if (option.kind === "choose")
|
|
32
|
+
return { kind: "choose", value: option.value };
|
|
33
|
+
if (option.kind === "feedback") {
|
|
34
|
+
const text = typeof input === "string" ? input.trim() : "";
|
|
35
|
+
// The option's prompt is the question, not the user's answer. Without an
|
|
36
|
+
// actual answer, fail closed instead of sending the prompt back to the model.
|
|
37
|
+
return text
|
|
38
|
+
? { kind: "feedback", text }
|
|
39
|
+
: { kind: "deny", reason: "feedback was required but not provided (fail closed)" };
|
|
40
|
+
}
|
|
41
|
+
return { kind: "deny", reason: "denied by user" };
|
|
42
|
+
}
|