@iinm/plain-agent 1.10.0 → 1.10.1

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 CHANGED
@@ -2,62 +2,11 @@
2
2
 
3
3
  A lightweight, capable coding agent for the terminal.
4
4
 
5
- - **Multi-provider** — Use Claude, GPT, Gemini, or any OpenAI-compatible model.
6
- <details>
7
- <summary>Predefined models</summary>
8
-
9
- ```
10
- # Model+Variant (Platform+Variant)
11
- claude-haiku-4-5+thinking-16k (platform: anthropic+default)
12
- claude-haiku-4-5+thinking-32k (platform: anthropic+default)
13
- claude-sonnet-4-6+thinking-high (platform: anthropic+default)
14
- claude-sonnet-4-6+thinking-max (platform: anthropic+default)
15
- claude-opus-4-7+thinking-high (platform: anthropic+default)
16
- claude-opus-4-7+thinking-max (platform: anthropic+default)
17
- claude-haiku-4-5+thinking-16k-bedrock (platform: bedrock+default)
18
- claude-haiku-4-5+thinking-32k-bedrock (platform: bedrock+default)
19
- claude-sonnet-4-6+thinking-high-bedrock (platform: bedrock+default)
20
- claude-sonnet-4-6+thinking-max-bedrock (platform: bedrock+default)
21
- claude-opus-4-7+thinking-high-bedrock (platform: bedrock+default)
22
- claude-opus-4-7+thinking-max-bedrock (platform: bedrock+default)
23
- gemini-3-flash-preview+thinking-medium (platform: gemini+default)
24
- gemini-3-flash-preview+thinking-high (platform: gemini+default)
25
- gemini-3.1-pro-preview+thinking-medium (platform: gemini+default)
26
- gemini-3.1-pro-preview+thinking-high (platform: gemini+default)
27
- gemini-3-flash-preview+thinking-medium-vertex-ai (platform: vertex-ai+default)
28
- gemini-3-flash-preview+thinking-high-vertex-ai (platform: vertex-ai+default)
29
- gemini-3.1-pro-preview+thinking-medium-vertex-ai (platform: vertex-ai+default)
30
- gemini-3.1-pro-preview+thinking-high-vertex-ai (platform: vertex-ai+default)
31
- gpt-5.4-mini+thinking-medium (platform: openai+default)
32
- gpt-5.4-mini+thinking-high (platform: openai+default)
33
- gpt-5.4-mini+thinking-xhigh (platform: openai+default)
34
- gpt-5.5+thinking-medium (platform: openai+default)
35
- gpt-5.5+thinking-high (platform: openai+default)
36
- gpt-5.5+thinking-xhigh (platform: openai+default)
37
- gpt-5.2-chat+thinking-medium-azure (platform: azure+openai)
38
- gpt-oss-120b+fireworks (platform: openai-compatible+fireworks)
39
- glm-5+vertex-ai (platform: vertex-ai+default)
40
- glm-5.1+fireworks (platform: openai-compatible+fireworks)
41
- glm-5.1+novita (platform: openai-compatible+novita)
42
- kimi-k2.6+fireworks (platform: openai-compatible+fireworks)
43
- kimi-k2.6+novita (platform: openai-compatible+novita)
44
- deepseek-v4-pro+novita (platform: openai-compatible+novita)
45
- deepseek-v4-pro+fireworks (platform: openai-compatible+fireworks)
46
- minimax-m2.7+fireworks (platform: openai-compatible+fireworks)
47
- minimax-m2.7+novita (platform: openai-compatible+novita)
48
- qwen3.6-plus+fireworks (platform: openai-compatible+fireworks)
49
- qwen3.6-27b+novita (platform: openai-compatible+novita)
50
- nova-2-lite+bedrock (platform: bedrock+default)
51
- claude-haiku-4-5+thinking-16k-bedrock-converse (platform: bedrock+default)
52
- ```
53
- </details>
54
-
55
-
56
- - **Approval rules & path validation** — Auto-approve tool uses by name and arguments using regex patterns ([config.predefined.json#autoApproval](https://github.com/iinm/plain-agent/blob/main/config/config.predefined.json)); restrict file access to the working directory — git-ignored files require explicit approval ([src/toolInputValidator.mjs](https://github.com/iinm/plain-agent/blob/main/src/toolInputValidator.mjs)).
57
- - **Sandboxed execution** — Run agent commands in a Docker container with a read-only project root and no network; writable mode and allowlisted network destinations can be enabled as needed.
58
- - **Plain-text memory** — Task state is saved as Markdown files under `.plain-agent/memory/` for easy review.
59
- - **Extensible** — Define prompts and subagents in Markdown. Connect MCP servers.
60
- Supports Claude Code plugins and `.claude/` commands, subagents, and skills.
5
+ - **Multi-provider** — Use Claude, GPT, Gemini, or any OpenAI-compatible model via Bedrock, Vertex AI, or direct APIs.
6
+ - **Fine-grained auto-approval** — Auto-approve tool calls by name and arguments using regex patterns.
7
+ - **Sandboxed execution** — Run commands in a Docker container with filesystem and network isolation.
8
+ - **Claude Code compatible** — Use Claude Code plugins, commands, subagents, and skills from `.claude/`.
9
+ - **Zero external dependencies** — Built with Node.js standard libraries only.
61
10
 
62
11
  ## Limitations
63
12
 
@@ -388,8 +337,7 @@ Files are loaded in the following order. Settings in later files override earlie
388
337
  ├── (3) config.json # Project-specific configuration
389
338
  ├── (4) config.local.json # Project-specific local configuration (including secrets)
390
339
  ├── prompts/ # Project-specific prompts
391
- ├── agents/ # Project-specific agent roles
392
- └── sandbox/ # Sandbox runner scripts
340
+ └── agents/ # Project-specific agent roles
393
341
  ```
394
342
 
395
343
  ### Example
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iinm/plain-agent",
3
- "version": "1.10.0",
3
+ "version": "1.10.1",
4
4
  "description": "A lightweight CLI-based coding agent",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -17,6 +17,7 @@ import { createInterruptTransform } from "./cliInterruptTransform.mjs";
17
17
  import { createMuteTransform } from "./cliMuteTransform.mjs";
18
18
  import { createPasteHandler } from "./cliPasteTransform.mjs";
19
19
  import { appendUsageRecord, buildUsageRecord } from "./usageStore.mjs";
20
+ import { createSequentialExecutor } from "./utils/createSequentialExecutor.mjs";
20
21
  import { notify } from "./utils/notify.mjs";
21
22
  import { parseVoiceToggleKey, startVoiceSession } from "./voiceInput.mjs";
22
23
 
@@ -473,12 +474,16 @@ export function startInteractiveSession({
473
474
  }
474
475
  });
475
476
 
477
+ const enqueueOutput = createSequentialExecutor();
478
+
476
479
  agentEventEmitter.on("message", (message) => {
477
- printMessage(message).catch((err) => {
478
- console.error(
479
- styleText("red", `Error rendering message: ${err.message}`),
480
- );
481
- });
480
+ enqueueOutput(() =>
481
+ printMessage(message).catch((err) => {
482
+ console.error(
483
+ styleText("red", `Error rendering message: ${err.message}`),
484
+ );
485
+ }),
486
+ );
482
487
  });
483
488
 
484
489
  agentEventEmitter.on("toolUseRequest", () => {
@@ -500,7 +505,9 @@ export function startInteractiveSession({
500
505
  });
501
506
 
502
507
  agentEventEmitter.on("providerTokenUsage", (usage) => {
503
- console.log(formatProviderTokenUsage(usage));
508
+ enqueueOutput(() => {
509
+ console.log(formatProviderTokenUsage(usage));
510
+ });
504
511
  });
505
512
 
506
513
  agentEventEmitter.on("error", (error) => {
@@ -519,8 +526,12 @@ export function startInteractiveSession({
519
526
  styleText("yellow", `\nNotification error: ${err.message}`),
520
527
  );
521
528
  }
522
- // 暫定対応: token usageのconsole出力を確実にflushするため、次のevent loop tickまで遅延
523
- await new Promise((resolve) => setTimeout(resolve, 0));
529
+
530
+ // Wait for all output operations to complete
531
+ await enqueueOutput(() => {});
532
+
533
+ // Defer prompt rendering to ensure terminal output is visible
534
+ await new Promise((resolve) => setImmediate(resolve));
524
535
 
525
536
  state.turn = true;
526
537
  cli.prompt();
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Create a sequential executor that enqueues async operations
3
+ * and executes them in order.
4
+ *
5
+ * @returns {(fn: () => Promise<void> | void) => Promise<void>}
6
+ * A function that enqueues an operation and returns a promise
7
+ * that resolves when all queued operations (including this one) complete.
8
+ *
9
+ * @example
10
+ * const enqueue = createSequentialExecutor();
11
+ *
12
+ * // These will execute in order, even if called from different event handlers
13
+ * enqueue(() => console.log("first"));
14
+ * enqueue(async () => { await something(); });
15
+ * enqueue(() => console.log("second"));
16
+ */
17
+ export function createSequentialExecutor() {
18
+ /** @type {Promise<void>} */
19
+ let chain = Promise.resolve();
20
+
21
+ return (fn) => {
22
+ chain = chain.then(fn).catch((err) => {
23
+ // Prevent unhandled rejection, but log the error
24
+ console.error("Sequential executor error:", err);
25
+ });
26
+ return chain;
27
+ };
28
+ }