@iinm/plain-agent 1.10.24 → 1.10.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -43
- package/config/config.predefined.json +48 -10
- package/package.json +1 -1
- package/src/cli/interactive.mjs +2 -0
- package/src/main.mjs +1 -0
package/README.md
CHANGED
|
@@ -50,14 +50,9 @@ Configure what the agent can do automatically using a small DSL with regex match
|
|
|
50
50
|
// Require --method to be explicit, so GET calls can be safely auto-approved
|
|
51
51
|
{
|
|
52
52
|
"toolName": "exec_command",
|
|
53
|
-
"input": { "command": "gh", "args": ["api", "
|
|
54
|
-
"action": "ask"
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
"toolName": "exec_command",
|
|
58
|
-
"input": { "command": "gh", "args": ["api"] },
|
|
53
|
+
"input": { "command": "gh", "args": ["api", { "$not": { "$regex": "^--(method|help)" } }] },
|
|
59
54
|
"action": "deny",
|
|
60
|
-
"reason": "--method must be specified"
|
|
55
|
+
"reason": "--method must be specified right after 'api'"
|
|
61
56
|
}
|
|
62
57
|
]
|
|
63
58
|
}
|
|
@@ -72,7 +67,7 @@ String values in tool inputs are treated as file paths and validated against the
|
|
|
72
67
|
- No directory traversal (`..` is not allowed)
|
|
73
68
|
- The file must be tracked by Git (not ignored)
|
|
74
69
|
|
|
75
|
-
**Note**: validation only applies when the agent explicitly passes file paths to tools. It cannot catch file access inside scripts the agent writes — something like `bash -c "rm -rf /"` is beyond its reach. Always use a sandbox when
|
|
70
|
+
**Note**: validation only applies when the agent explicitly passes file paths to tools. It cannot catch file access inside scripts the agent writes — something like `bash -c "rm -rf /"` is beyond its reach. Always use a sandbox when auto-approving script execution.
|
|
76
71
|
|
|
77
72
|
### Sandbox
|
|
78
73
|
|
|
@@ -81,11 +76,12 @@ A Docker-based wrapper called `plain-sandbox` is included, but the interface is
|
|
|
81
76
|
|
|
82
77
|
```js
|
|
83
78
|
{
|
|
84
|
-
// Sandbox environment for the exec_command and tmux_command tools
|
|
85
79
|
"sandbox": {
|
|
86
80
|
// Commands are wrapped and executed with this command
|
|
81
|
+
// Build the image before first use: plain-sandbox --verbose echo done
|
|
87
82
|
"command": "plain-sandbox",
|
|
88
|
-
"args": ["--allow-write", "--skip-build", "--keep-alive", "30"],
|
|
83
|
+
"args": ["--allow-write", "--mount-readonly", ".plain-agent/config.json", "--skip-build", "--keep-alive", "30"],
|
|
84
|
+
// ↑ --mount-readonly: prevents the agent from overwriting its own config
|
|
89
85
|
// separator is inserted between sandbox flags and the user command to prevent bypasses
|
|
90
86
|
"separator": "--",
|
|
91
87
|
|
|
@@ -117,7 +113,6 @@ A few design choices keep token usage low:
|
|
|
117
113
|
|
|
118
114
|
- Minimal system prompt — the [system prompt](https://github.com/iinm/plain-agent/blob/main/src/prompt.mjs) contains only what the agent needs to function.
|
|
119
115
|
- Output truncation — when a command or MCP tool produces large output, it is truncated and saved to a file. The agent can then read only the relevant parts.
|
|
120
|
-
- (Experimental) [Hashline-based](https://blog.can.ac/2026/02/12/the-harness-problem/) patch_file tool.
|
|
121
116
|
|
|
122
117
|
### Claude Code Compatibility
|
|
123
118
|
|
|
@@ -457,6 +452,31 @@ Files are loaded in the following order. Settings in later files override earlie
|
|
|
457
452
|
└── agents/ # Project-specific agent roles
|
|
458
453
|
```
|
|
459
454
|
|
|
455
|
+
|
|
456
|
+
<details>
|
|
457
|
+
<summary><b>Minimal example (file editing and web search only — no script execution, no sandbox required)</b></summary>
|
|
458
|
+
|
|
459
|
+
```js
|
|
460
|
+
{
|
|
461
|
+
"autoApproval": {
|
|
462
|
+
"defaultAction": "ask",
|
|
463
|
+
"maxApprovals": 100,
|
|
464
|
+
"patterns": [
|
|
465
|
+
{
|
|
466
|
+
"toolName": { "$regex": "^(write_file|patch_file)$" },
|
|
467
|
+
"action": "allow"
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
"toolName": { "$regex": "^(web_search|web_fetch)$" },
|
|
471
|
+
"action": "allow"
|
|
472
|
+
}
|
|
473
|
+
]
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
</details>
|
|
479
|
+
|
|
460
480
|
<details>
|
|
461
481
|
<summary><b>YOLO mode example (requires a sandbox for safety)</b></summary>
|
|
462
482
|
|
|
@@ -472,11 +492,11 @@ Files are loaded in the following order. Settings in later files override earlie
|
|
|
472
492
|
"action": "allow"
|
|
473
493
|
},
|
|
474
494
|
{
|
|
475
|
-
"toolName": "
|
|
495
|
+
"toolName": { "$regex": "^(web_search|web_fetch)$" },
|
|
476
496
|
"action": "allow"
|
|
477
497
|
},
|
|
478
498
|
{
|
|
479
|
-
"toolName":
|
|
499
|
+
"toolName": "exec_command",
|
|
480
500
|
"action": "allow"
|
|
481
501
|
}
|
|
482
502
|
// ⚠️ Never do this. MCP runs outside the sandbox, so it can send anything externally.
|
|
@@ -487,8 +507,10 @@ Files are loaded in the following order. Settings in later files override earlie
|
|
|
487
507
|
]
|
|
488
508
|
},
|
|
489
509
|
"sandbox": {
|
|
510
|
+
// ⚠️ Build the image before first use: plain-sandbox --verbose echo done
|
|
490
511
|
"command": "plain-sandbox",
|
|
491
|
-
"args": ["--allow-write", "--skip-build", "--keep-alive", "30"],
|
|
512
|
+
"args": ["--allow-write", "--mount-readonly", ".plain-agent/config.json", "--skip-build", "--keep-alive", "30"],
|
|
513
|
+
// ↑ --mount-readonly: prevents the agent from overwriting its own config
|
|
492
514
|
"separator": "--"
|
|
493
515
|
}
|
|
494
516
|
}
|
|
@@ -518,7 +540,7 @@ Files are loaded in the following order. Settings in later files override earlie
|
|
|
518
540
|
"action": "allow"
|
|
519
541
|
},
|
|
520
542
|
|
|
521
|
-
// ⚠️
|
|
543
|
+
// ⚠️ When auto-approving execution, scripts can access unauthorized files and networks. Always use a sandbox.
|
|
522
544
|
{
|
|
523
545
|
"toolName": "exec_command",
|
|
524
546
|
"input": { "command": "npm", "args": ["run", { "$regex": "^(lint|test)$" }] },
|
|
@@ -549,8 +571,10 @@ Files are loaded in the following order. Settings in later files override earlie
|
|
|
549
571
|
// Sandbox environment for the exec_command and tmux_command tools
|
|
550
572
|
"sandbox": {
|
|
551
573
|
// Commands are wrapped and executed with this command
|
|
574
|
+
// Build the image before first use: plain-sandbox --verbose echo done
|
|
552
575
|
"command": "plain-sandbox",
|
|
553
|
-
"args": ["--allow-write", "--skip-build", "--keep-alive", "30"],
|
|
576
|
+
"args": ["--allow-write", "--mount-readonly", ".plain-agent/config.json", "--skip-build", "--keep-alive", "30"],
|
|
577
|
+
// ↑ --mount-readonly: prevents the agent from overwriting its own config
|
|
554
578
|
// separator is inserted between sandbox flags and the user command to prevent bypasses
|
|
555
579
|
"separator": "--",
|
|
556
580
|
|
|
@@ -749,33 +773,6 @@ Press **Ctrl-O** to start recording, then press it again to stop. Partial transc
|
|
|
749
773
|
is a letter (a-z) or one of `[ \ ] ^ _`. Defaults to `"ctrl-o"`.
|
|
750
774
|
- `recorder` — Override automatic recorder detection, e.g. `{ "command": "sox", "args": ["-q", "-d", "-b", "16", "-c", "1", "-r", "24000", "-e", "signed-integer", "-t", "raw", "-"] }`. It must write raw 16-bit little-endian mono PCM to stdout at 24 kHz (OpenAI) or 16 kHz (Gemini).
|
|
751
775
|
|
|
752
|
-
## Development
|
|
753
|
-
|
|
754
|
-
```sh
|
|
755
|
-
# Run lint, typecheck, and tests
|
|
756
|
-
npm run check
|
|
757
|
-
|
|
758
|
-
# Fix lint issues
|
|
759
|
-
npm run fix
|
|
760
|
-
# or
|
|
761
|
-
npm run fix -- --unsafe
|
|
762
|
-
|
|
763
|
-
# Update dependencies
|
|
764
|
-
npx npm-check-updates -t minor -c 3
|
|
765
|
-
npx npm-check-updates -t minor -c 3 -u
|
|
766
|
-
```
|
|
767
|
-
|
|
768
|
-
## Release
|
|
769
|
-
|
|
770
|
-
```sh
|
|
771
|
-
npm version <major|minor|patch>
|
|
772
|
-
|
|
773
|
-
git push --follow-tags
|
|
774
|
-
gh release create $(git describe --tags) --generate-notes
|
|
775
|
-
|
|
776
|
-
npm publish --access public
|
|
777
|
-
```
|
|
778
|
-
|
|
779
776
|
## Appendix: Creating Least-Privilege Users for Cloud Providers
|
|
780
777
|
|
|
781
778
|
<details>
|
|
@@ -921,3 +918,19 @@ gcloud iam service-accounts add-iam-policy-binding "$service_account_email" \
|
|
|
921
918
|
gcloud auth print-access-token --impersonate-service-account "$service_account_email"
|
|
922
919
|
```
|
|
923
920
|
</details>
|
|
921
|
+
|
|
922
|
+
## Developer Notes
|
|
923
|
+
|
|
924
|
+
<details>
|
|
925
|
+
<summary>Release</summary>
|
|
926
|
+
|
|
927
|
+
```sh
|
|
928
|
+
npm version <major|minor|patch>
|
|
929
|
+
|
|
930
|
+
git push --follow-tags
|
|
931
|
+
gh release create $(git describe --tags) --generate-notes
|
|
932
|
+
|
|
933
|
+
npm publish --access public
|
|
934
|
+
```
|
|
935
|
+
</details>
|
|
936
|
+
|
|
@@ -139,18 +139,10 @@
|
|
|
139
139
|
"toolName": "exec_command",
|
|
140
140
|
"input": {
|
|
141
141
|
"command": "gh",
|
|
142
|
-
"args": ["api", "
|
|
143
|
-
},
|
|
144
|
-
"action": "ask"
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
"toolName": "exec_command",
|
|
148
|
-
"input": {
|
|
149
|
-
"command": "gh",
|
|
150
|
-
"args": ["api"]
|
|
142
|
+
"args": ["api", { "$not": { "$regex": "^--(method|help)" } }]
|
|
151
143
|
},
|
|
152
144
|
"action": "deny",
|
|
153
|
-
"reason": "--method must be specified"
|
|
145
|
+
"reason": "--method must be specified right after 'api'"
|
|
154
146
|
}
|
|
155
147
|
]
|
|
156
148
|
},
|
|
@@ -1369,6 +1361,29 @@
|
|
|
1369
1361
|
}
|
|
1370
1362
|
}
|
|
1371
1363
|
},
|
|
1364
|
+
{
|
|
1365
|
+
"name": "minimax-m3",
|
|
1366
|
+
"variant": "novita",
|
|
1367
|
+
"platform": {
|
|
1368
|
+
"name": "openai-compatible",
|
|
1369
|
+
"variant": "novita"
|
|
1370
|
+
},
|
|
1371
|
+
"model": {
|
|
1372
|
+
"format": "openai-messages",
|
|
1373
|
+
"config": {
|
|
1374
|
+
"model": "minimax/minimax-m3"
|
|
1375
|
+
}
|
|
1376
|
+
},
|
|
1377
|
+
"cost": {
|
|
1378
|
+
"currency": "USD",
|
|
1379
|
+
"unit": "1M",
|
|
1380
|
+
"costs": {
|
|
1381
|
+
"prompt_tokens": 0.3,
|
|
1382
|
+
"prompt_tokens_details.cached_tokens": -0.24,
|
|
1383
|
+
"completion_tokens": 1.2
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
},
|
|
1372
1387
|
|
|
1373
1388
|
{
|
|
1374
1389
|
"name": "qwen3.6-plus",
|
|
@@ -1415,6 +1430,29 @@
|
|
|
1415
1430
|
}
|
|
1416
1431
|
}
|
|
1417
1432
|
},
|
|
1433
|
+
{
|
|
1434
|
+
"name": "qwen3.7-max",
|
|
1435
|
+
"variant": "novita",
|
|
1436
|
+
"platform": {
|
|
1437
|
+
"name": "openai-compatible",
|
|
1438
|
+
"variant": "novita"
|
|
1439
|
+
},
|
|
1440
|
+
"model": {
|
|
1441
|
+
"format": "openai-messages",
|
|
1442
|
+
"config": {
|
|
1443
|
+
"model": "qwen/qwen3.7-max"
|
|
1444
|
+
}
|
|
1445
|
+
},
|
|
1446
|
+
"cost": {
|
|
1447
|
+
"currency": "USD",
|
|
1448
|
+
"unit": "1M",
|
|
1449
|
+
"costs": {
|
|
1450
|
+
"prompt_tokens": 1.5625,
|
|
1451
|
+
"prompt_tokens_details.cached_tokens": -1.4375,
|
|
1452
|
+
"completion_tokens": 3.75
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
},
|
|
1418
1456
|
|
|
1419
1457
|
{
|
|
1420
1458
|
"name": "nova-2-lite",
|
package/package.json
CHANGED
package/src/cli/interactive.mjs
CHANGED
|
@@ -391,6 +391,8 @@ export function startInteractiveSession({
|
|
|
391
391
|
|
|
392
392
|
// Handle readline close (e.g., stdin closed externally)
|
|
393
393
|
cli.on("close", handleExit);
|
|
394
|
+
process.on("SIGTERM", handleExit);
|
|
395
|
+
process.on("SIGHUP", handleExit);
|
|
394
396
|
|
|
395
397
|
const handleCommand = createCommandHandler({
|
|
396
398
|
agentCommands,
|
package/src/main.mjs
CHANGED
|
@@ -165,6 +165,7 @@ export async function main(argv = process.argv) {
|
|
|
165
165
|
const sandboxStr = [
|
|
166
166
|
appConfig.sandbox.command,
|
|
167
167
|
...(appConfig.sandbox.args || []),
|
|
168
|
+
...(appConfig.sandbox.separator ? [appConfig.sandbox.separator] : []),
|
|
168
169
|
].join(" ");
|
|
169
170
|
console.log(styleText("green", "\n📦 Sandbox: on"));
|
|
170
171
|
console.log(` ⤷ ${sandboxStr}`);
|