@qilin-agent/tool-bash-persistent 0.0.0-stage → 3.1.3

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kkutysllb
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.
@@ -0,0 +1,79 @@
1
+ # Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
2
+ # section, a hash of its English and Chinese blocks outside code blocks and generated regions.
3
+ # After editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/shell/tool-bash-persistent/README.md
5
+ /:
6
+ en: 1516a333e77ca54a
7
+ zh: 53ef80da9cefd884
8
+ /qilin-tool-bash-persistent:
9
+ en: 0b2f94e7a94335b6
10
+ zh: 0b95644899dbcdd7
11
+ /qilin-tool-bash-persistent/summary:
12
+ en: c34db740c58468e2
13
+ zh: 712a34519dd66922
14
+ /qilin-tool-bash-persistent/table-of-contents:
15
+ en: d152484eb41ac6b4
16
+ zh: 09388d293f9be9cb
17
+ /qilin-tool-bash-persistent/use-this-package:
18
+ en: ddec7cc1004498f8
19
+ zh: cd0cba22e570d1e9
20
+ /qilin-tool-bash-persistent/use-this-package/when-to-choose-it:
21
+ en: c44e27f03b5bf48c
22
+ zh: a0bb453d130cf6ae
23
+ /qilin-tool-bash-persistent/use-this-package/minimal-configuration:
24
+ en: 65386955f4a6a3a8
25
+ zh: 9fccd80dc540ab58
26
+ /qilin-tool-bash-persistent/use-this-package/what-the-agent-can-rely-on:
27
+ en: b398f124ea7b77b0
28
+ zh: 50855e601e6c1b88
29
+ /qilin-tool-bash-persistent/use-this-package/what-can-go-wrong:
30
+ en: d0d44ffa08f1a917
31
+ zh: ece228da0c0089aa
32
+ /qilin-tool-bash-persistent/understand-the-implementation:
33
+ en: 8d94a7381bca3fa9
34
+ zh: f4bd2866fff6fa8d
35
+ /qilin-tool-bash-persistent/understand-the-implementation/design-philosophy:
36
+ en: 61c39f52982612e3
37
+ zh: 92bc7e3c8d13a211
38
+ /qilin-tool-bash-persistent/understand-the-implementation/source-map:
39
+ en: 857e036f41c913ac
40
+ zh: e9f383f7ee384c59
41
+ /qilin-tool-bash-persistent/understand-the-implementation/command-flow:
42
+ en: 3b7bf7c20b266c31
43
+ zh: 85a325ef65fa7ac7
44
+ /qilin-tool-bash-persistent/further-exploration:
45
+ en: 2f6547ad7a0e569c
46
+ zh: 2f9b5f19b3ebcc18
47
+ /qilin-tool-bash-persistent/model-experience:
48
+ en: 215e7ba838619b7b
49
+ zh: a311da3843709f90
50
+ /qilin-tool-bash-persistent/model-experience/tool-schema:
51
+ en: e4c7376d4f29dbd5
52
+ zh: 6b7a9eaf69c9ea53
53
+ /qilin-tool-bash-persistent/model-experience/tool-schema/what-the-model-sees:
54
+ en: 17853846e6afc910
55
+ zh: 52805c18361c2650
56
+ /qilin-tool-bash-persistent/model-experience/tool-schema/token-effect:
57
+ en: feee7dec91ae7329
58
+ zh: b62338fa72ad3b87
59
+ /qilin-tool-bash-persistent/model-experience/tool-schema/kv-cache-effect:
60
+ en: 7d9323583a83cb95
61
+ zh: cc5daa6bce8450f8
62
+ /qilin-tool-bash-persistent/model-experience/tool-results:
63
+ en: beaabb79f229692a
64
+ zh: 8573891441963be4
65
+ /qilin-tool-bash-persistent/model-experience/tool-results/what-the-model-sees:
66
+ en: 7c197e9bb86cc044
67
+ zh: d8ffe918aab08250
68
+ /qilin-tool-bash-persistent/model-experience/tool-results/token-effect:
69
+ en: 5dea04ce6a30ad48
70
+ zh: c2434253dc517d3d
71
+ /qilin-tool-bash-persistent/model-experience/tool-results/kv-cache-effect:
72
+ en: ba6a48888c3361e5
73
+ zh: 3a8f648053555dc9
74
+ /qilin-tool-bash-persistent/known-limitations-and-deferred-work:
75
+ en: dc5a908f2b2da040
76
+ zh: 3afceaabcf324771
77
+ /qilin-tool-bash-persistent/known-limitations-and-deferred-work/dev-note:
78
+ en: e67f633dc6fe7773
79
+ zh: 3bb0c20c93f16f98
package/README.md CHANGED
@@ -1,3 +1,158 @@
1
- # Temporary Holding Version
1
+ ---
2
+ description: "The model-facing persistent bash tool for users and maintainers choosing, configuring, or debugging owner-scoped shell state that survives across calls."
3
+ kind: "package-reference"
4
+ ---
2
5
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
6
+ # @qilin-agent/tool-bash-persistent
7
+
8
+ English | [中文](README.zh.md)
9
+
10
+ ## Summary
11
+
12
+ This package gives an agent a `bash` tool whose cwd, exported variables, functions, and background jobs persist across calls. Each agent receives an isolated shell, and its commands run sequentially. Choose it for workflows that depend on cross-call state; use `qilin-tool-bash` when every command should start clean. Configure the PTY backend and per-command timeout; `exit`, timeout, or cancellation resets the shell, while interactive commands that wait for stdin may run until timeout.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ Load this plugin in any composition where the agent should keep shell state between commands — for example long build sessions, activated environments, or scripts that export variables for later steps. It registers the `bash` tool and requires the `ctx.tools` and `ctx.terminals` services plus an owning agent session at execution time.
29
+
30
+ ### When to choose it
31
+
32
+ Choose the persistent tool when work depends on cross-call state: a one-shot `qilin-tool-bash` call cannot remember a `cd` or an exported variable. Choose the one-shot tool when every command should start from a known, clean environment, or when the command is short and self-contained. Commands that need interactive stdin are unsupported here — a foreground child that reads input blocks until the command timeout — so interactive work belongs to the terminal tools.
33
+
34
+ ### Minimal configuration
35
+
36
+ The default `shell` backend starts an interactive bash through `qilin-terminal-bash`; deployments may register another PTY backend and select it by name.
37
+
38
+ ```yaml
39
+ - name: '@qilin-agent/terminal'
40
+ - name: '@qilin-agent/terminal-bash'
41
+ - name: '@qilin-agent/tool-bash-persistent'
42
+ ```
43
+
44
+ | Field | Default | Meaning |
45
+ |---|---|---|
46
+ | `backendType` | `shell` | Registered PTY backend used for each agent's shell |
47
+ | `timeoutMs` | `300,000` | Wall-clock limit for one command; timeout closes the shell |
48
+ | `maxOutputChars` | `16,000` | Maximum retained command-output characters; fixed diagnostics are added afterward |
49
+ | `description` | `Run commands in a persistent bash shell. State, including the current directory and exported environment variables, persists across calls for this agent.` | Model-facing environment contract; deployments may describe their environment |
50
+
51
+ The generated [configuration catalog](../../../docs/config-catalog.md#qilin-agenttool-bash-persistent) is the exhaustive source for every accepted field and its JSDoc.
52
+
53
+ ### What the agent can rely on
54
+
55
+ Commands share one shell per agent, so state persists until an `exit`, a timeout, or a reset — each of which closes the shell and tells the agent the next call starts from the workspace with a fresh directory and environment. Results exclude the private completion markers; every settled command appends `[Command finished with exit code N]`, and a shell that exits before reporting that status instead appends `[shell exited: code N]`, `[shell killed by signal: SIG]`, or `[shell exited]`, then resets. Long output keeps the earliest retained prefix plus a clipping notice; if the terminal has already dropped that prefix, the result says so explicitly rather than presenting a tail as complete output.
56
+
57
+ ### What can go wrong
58
+
59
+ A call without an owning agent session fails with `bash requires an owning agent session`, and a composition without a PTY backend activates the tool but fails its first call with `no PTY backend registered for "shell"`. An interactive foreground child (for example a REPL) returns early with partial output only where the backend proves its stdin wait; elsewhere the call runs to `timeoutMs`, which closes the uncertain shell and reports the reset. Cancellation also resets and discards the result, even when a complete status marker is already observable.
60
+
61
+ -----
62
+
63
+ <a id="understand-the-implementation"></a>
64
+ ## Understand the implementation
65
+
66
+ <details>
67
+ <summary>Implementation internals — click to expand</summary>
68
+
69
+ This section explains the design decisions behind the tool and points at the code that realizes them; the observable behavior is fully covered in [Use this package](#use-this-package).
70
+
71
+ ### Design philosophy
72
+
73
+ - **One shell per owner, nothing shared.** The shell registry keys every session by the calling `Agent`, so concurrent agents never share state, and commands for the same agent are serialized through a per-owner queue.
74
+ - **Marker-anchored extraction.** Each command is wrapped with unique start/end markers carrying the exit status; the tool polls the PTY scrollback and extracts the span between the real markers, so prompts and echoed input never leak into results.
75
+ - **Reset, never repair.** Any uncertain state — an explicit `exit`, a timeout, a send failure, an abort — closes the shell and starts the next call fresh, because a half-known shell is worse than a clean one.
76
+ - **Owner-scoped lifecycle.** Shells are created lazily on first use and killed on plugin disposal or owner teardown; the owner-scoped `ctx.terminals` service fences every operation to the owning agent.
77
+
78
+ ### Source map
79
+
80
+ | File | Role |
81
+ |---|---|
82
+ | [`src/index.ts`](src/index.ts) | Plugin entry: shell registry, command wrapping, scrollback polling, extraction and rendering |
83
+
84
+ ### Command flow
85
+
86
+ A first command spawns the shell through `ctx.terminals.spawn`, disables input echo (`stty -echo`), and waits for readiness. Each command is then wrapped into one physical line — a printf of the start marker, the command body escaped with `$'…'`, and a printf of the end marker plus `$?` — so embedded newlines cannot leak terminal prompts into the result. The tool polls the scrollback in 1,000-line pages until the end marker appears, extracts the span, and renders it with any status marker. A timeout aborts the deadline, captures the partial output, and resets the shell.
87
+
88
+ </details>
89
+
90
+ -----
91
+
92
+ <a id="further-exploration"></a>
93
+ ## Further Exploration
94
+
95
+ Read these pages when the package-level contract is not enough. They move from the terminal family to the seam, the backends, and the design note behind owner-scoped sessions.
96
+
97
+ - [terminal package map](../../terminal/README.md) — the persistent PTY capability family.
98
+ - [terminal seam](../../terminal/terminal/README.md) — the `ctx.terminals` service behind the tool.
99
+ - [terminal-bash backend](../../terminal/terminal-bash/README.md) — the default `shell` backend.
100
+ - [tool-terminal](../../terminal/tool-terminal/README.md) — six model-facing terminal tools for interactive work.
101
+ - [Persistent PTY sessions Agent Note](../../../.agents/notes/implemented/feature/2026-07-16-persistent-pty-sessions.md) — the owner-scoped session design and its rationale.
102
+ - [Generated tool catalog](../../../docs/tool-catalog.md#qilin-agenttool-bash-persistent) — the exact `bash` argument schema.
103
+ - [Generated configuration catalog](../../../docs/config-catalog.md#qilin-agenttool-bash-persistent) — every accepted config field and its source declaration.
104
+
105
+ -----
106
+
107
+ <a id="model-experience"></a>
108
+ ## Model Experience
109
+
110
+ ### Tool schema
111
+
112
+ #### What the model sees
113
+
114
+ The generated [`bash` schema](../../../docs/tool-catalog.md#qilin-agenttool-bash-persistent), including the configured `description`. The plugin contributes no standalone system-prompt section; the deployment owns persona and environment guidance.
115
+
116
+ #### Token effect
117
+
118
+ Fixed schema cost while `bash` is visible.
119
+
120
+ #### KV Cache effect
121
+
122
+ Prefix-stable while the configured description and schema remain unchanged.
123
+
124
+ ### Tool results
125
+
126
+ #### What the model sees
127
+
128
+ Commands share one shell per agent, so cwd, exported variables, activated environments, functions, and background jobs persist across calls. Results exclude private completion markers. When the shell reads stdin again without having printed the completion marker — after `exec`, an interrupt, or an interactive foreground child whose stdin wait the provider proves — the call returns the captured partial output, which can end with the backend's own prompt text. Every settled command appends `[Command finished with exit code N]`; a shell that exits before reporting that status instead appends `[shell exited: code N]`, `[shell killed by signal: SIG]`, or `[shell exited]` when the backend supplies neither, then resets and tells the model that the next call starts fresh. Long output keeps the earliest retained prefix plus a clipping notice. If the PTY has already dropped that prefix, the result says so explicitly instead of presenting a tail as complete output. Timeout returns bounded partial output followed by `[Command timed out or OOM]`, closes the uncertain shell, and reports the reset.
129
+
130
+ #### Token effect
131
+
132
+ Data-dependent. `maxOutputChars` bounds retained command output; fixed clipping, lost-prefix, status, timeout, and reset diagnostics can extend the result.
133
+
134
+ #### KV Cache effect
135
+
136
+ Append-only tool results follow the reusable request prefix.
137
+
138
+ ## Known Limitations and Deferred Work
139
+
140
+ <a id="known-limitations-and-deferred-work"></a>
141
+
142
+
143
+ These limits define when the tool is a poor fit or needs special care. They are current package constraints, not a task backlog.
144
+
145
+ - **The tool requires an owning agent and a real PTY backend** — agent-less calls and backends that cannot start an interactive shell fail.
146
+ - **An interactive foreground child returns early with partial output only where the subprocess provider proves its stdin wait** — elsewhere the call runs to `timeoutMs`.
147
+ - **Explicit `exit` and timeout discard shell state** — cancellation also resets and discards the result, even when a complete status marker is already observable; the next call starts a fresh shell.
148
+ - **Environment facts such as network access and package mirrors belong in the configured `description`** — not this package's default.
149
+
150
+ <a id="dev-note"></a>
151
+ ### Dev Note
152
+
153
+ <details>
154
+ <summary>Working context for maintainers — click to expand</summary>
155
+
156
+ None.
157
+
158
+ </details>
package/README.zh.md ADDED
@@ -0,0 +1,158 @@
1
+ ---
2
+ description: "面向模型的持久 bash 工具,供选择、配置或排查跨调用保留的按所有者隔离 shell 状态的使用者与维护者阅读。"
3
+ kind: "package-reference"
4
+ ---
5
+
6
+ # @qilin-agent/tool-bash-persistent
7
+
8
+ [English](README.md) | 中文
9
+
10
+ ## 概述
11
+
12
+ 本包为 agent(智能体)提供 `bash` 工具,使 cwd、导出的变量、函数与后台任务跨调用保留。每个 agent 都有隔离的 shell,其命令串行执行。需要跨调用状态的工作流应选择本包;每条命令都应从干净环境开始时使用 `qilin-tool-bash`。配置 PTY 后端与单条命令的超时;`exit`、超时或取消会重置 shell,而等待 stdin 的交互式命令可能一直运行到超时。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ 在 agent 需要在命令之间保持 shell 状态的任何组合中加载本插件——例如长时间构建会话、已激活的环境,或为后续步骤导出变量的脚本。它注册 `bash` 工具,需要 `ctx.tools` 与 `ctx.terminals` 服务,并在执行时需要拥有者 agent 会话。
29
+
30
+ ### 何时选择
31
+
32
+ 当工作依赖跨调用状态时选择持久工具:一次性 `qilin-tool-bash` 调用无法记住 `cd` 或导出的变量。当每条命令都应从已知、干净的环境开始,或命令又短又独立时,选择一次性工具。这里不支持需要交互 stdin 的命令——读取输入的前台子进程会一直阻塞到命令超时——因此交互工作属于 terminal 工具。
33
+
34
+ ### 最小配置
35
+
36
+ 默认的 `shell` 后端通过 `qilin-terminal-bash` 启动交互式 bash;部署方可以注册其他 PTY 后端并按名称选择。
37
+
38
+ ```yaml
39
+ - name: '@qilin-agent/terminal'
40
+ - name: '@qilin-agent/terminal-bash'
41
+ - name: '@qilin-agent/tool-bash-persistent'
42
+ ```
43
+
44
+ | 字段 | 默认值 | 含义 |
45
+ |---|---|---|
46
+ | `backendType` | `shell` | 用于每个 agent shell 的已注册 PTY 后端 |
47
+ | `timeoutMs` | `300,000` | 单条命令的墙钟上限;超时关闭 shell |
48
+ | `maxOutputChars` | `16,000` | 保留的命令输出字符上限;固定诊断信息在其后追加 |
49
+ | `description` | `Run commands in a persistent bash shell. State, including the current directory and exported environment variables, persists across calls for this agent.` | 面向模型的环境约定;部署方可描述自己的环境 |
50
+
51
+ 生成的[配置目录](../../../docs/config-catalog.zh.md#qilin-agenttool-bash-persistent)是每个受支持字段及其 JSDoc 的穷尽式真源。
52
+
53
+ ### agent 可以依赖什么
54
+
55
+ 命令共享每个 agent 一个 shell,因此状态一直保留到 `exit`、超时或重置——每一种都会关闭 shell 并告诉 agent 下一次调用从工作区的新目录与环境开始。结果排除私有完成标记;每条完成的命令都追加 `[Command finished with exit code N]`,而在报告该状态前就退出的 shell 改为追加 `[shell exited: code N]`、`[shell killed by signal: SIG]` 或 `[shell exited]`,然后重置。长输出保留最早的已保留前缀并附裁剪通知;若 terminal 已经丢弃该前缀,结果会明确说明,而不是把尾部当作完整输出呈现。
56
+
57
+ ### 可能出什么问题
58
+
59
+ 没有拥有者 agent 会话的调用会以 `bash requires an owning agent session` 失败,没有 PTY 后端的组合会激活该工具,但首次调用以 `no PTY backend registered for "shell"` 失败。交互式前台子进程(例如 REPL)只有在后端证明其 stdin 等待时才提前返回部分输出;否则调用一直运行到 `timeoutMs`,随后关闭不确定的 shell 并报告重置。取消也会重置并丢弃结果,即使完整状态标记已经可观察。
60
+
61
+ -----
62
+
63
+ <a id="understand-the-implementation"></a>
64
+ ## 理解实现
65
+
66
+ <details>
67
+ <summary>实现细节——点击展开</summary>
68
+
69
+ 本节解释工具背后的设计决策,并指出实现它们的代码位置;可观察行为已在[使用本包](#use-this-package)中完整说明。
70
+
71
+ ### 设计理念
72
+
73
+ - **每个 owner 一个 shell,互不共享。** shell 注册表按调用方 `Agent` 为每个会话建键,因此并发 agent 永不共享状态,同一 agent 的命令通过按 owner 的队列串行化。
74
+ - **标记锚定提取。** 每条命令都用携带退出状态的唯一起止标记包装;工具轮询 PTY scrollback 并提取真实标记之间的区间,因此提示符与回显输入永不泄漏进结果。
75
+ - **重置,而非修复。** 任何不确定状态——显式 `exit`、超时、发送失败、中止——都会关闭 shell 并让下一次调用从全新状态开始,因为半知情的 shell 不如干净的 shell。
76
+ - **按 owner 的生命周期。** shell 在首次使用时惰性创建,在插件释放或 owner 拆除时终止;按所有者隔离的 `ctx.terminals` 服务把每个操作都围栏到拥有它的 agent。
77
+
78
+ ### 源码地图
79
+
80
+ | 文件 | 职责 |
81
+ |---|---|
82
+ | [`src/index.ts`](src/index.ts) | 插件入口:shell 注册表、命令包装、scrollback 轮询、提取与渲染 |
83
+
84
+ ### 命令流程
85
+
86
+ 首条命令通过 `ctx.terminals.spawn` 生成 shell,禁用输入回显(`stty -echo`),并等待就绪。随后每条命令都包装成一行物理文本——printf 起始标记、用 `$'…'` 转义的命令体、printf 结束标记加 `$?`——因此内嵌换行无法把终端提示符泄漏进结果。工具以 1,000 行一页轮询 scrollback,直到出现结束标记,提取区间并连同任何状态标记一起渲染。超时会中止截止时间、捕获部分输出并重置 shell。
87
+
88
+ </details>
89
+
90
+ -----
91
+
92
+ <a id="further-exploration"></a>
93
+ ## 进一步探索
94
+
95
+ 当包级约定不够用时阅读以下页面。它们从 terminal 家族逐步进入 seam、后端,以及按所有者会话背后的设计笔记。
96
+
97
+ - [terminal 包映射](../../terminal/README.zh.md)——持久 PTY 能力家族。
98
+ - [terminal seam](../../terminal/terminal/README.zh.md)——工具背后的 `ctx.terminals` 服务。
99
+ - [terminal-bash 后端](../../terminal/terminal-bash/README.zh.md)——默认的 `shell` 后端。
100
+ - [tool-terminal](../../terminal/tool-terminal/README.zh.md)——面向交互工作的六个模型侧 terminal 工具。
101
+ - [持久 PTY 会话 Agent Note](../../../.agents/notes/implemented/feature/2026-07-16-persistent-pty-sessions.zh.md)——按所有者会话的设计及其理由。
102
+ - [生成的工具目录](../../../docs/tool-catalog.zh.md#qilin-agenttool-bash-persistent)——`bash` 参数 schema 的确切内容。
103
+ - [生成的配置目录](../../../docs/config-catalog.zh.md#qilin-agenttool-bash-persistent)——每个受支持配置字段及其源声明。
104
+
105
+ -----
106
+
107
+ <a id="model-experience"></a>
108
+ ## 模型体验
109
+
110
+ ### 工具 schema
111
+
112
+ #### 模型看到什么
113
+
114
+ 生成的 [`bash` schema](../../../docs/tool-catalog.zh.md#qilin-agenttool-bash-persistent),包括配置的 `description`。本插件不贡献独立的系统提示词区段;人设与环境指引由部署方负责。
115
+
116
+ #### Token 影响
117
+
118
+ `bash` 可见期间产生固定 schema 开销。
119
+
120
+ #### KV Cache 影响
121
+
122
+ 只要配置的描述与 schema 不变,前缀就保持稳定。
123
+
124
+ ### 工具结果
125
+
126
+ #### 模型看到什么
127
+
128
+ 命令共享每个 agent 一个 shell,因此 cwd、导出的变量、已激活的环境、函数与后台任务都会跨调用保留。结果排除私有完成标记。当 shell 在没有打印完成标记的情况下再次读取 stdin——`exec`、中断,或提供方证明其 stdin 等待的交互式前台子进程之后——调用返回捕获的部分输出,它可能以后端自己的提示符文本结尾。每条完成的命令都追加 `[Command finished with exit code N]`;在报告该状态前就退出的 shell 改为追加 `[shell exited: code N]`、`[shell killed by signal: SIG]`,或后端两者都未提供时的 `[shell exited]`,然后重置并告诉模型下一次调用从全新状态开始。长输出保留最早的已保留前缀并附裁剪通知。若 PTY 已经丢弃该前缀,结果会明确说明,而不是把尾部当作完整输出呈现。超时返回有界部分输出并追加 `[Command timed out or OOM]`、关闭不确定的 shell 并报告重置。
129
+
130
+ #### Token 影响
131
+
132
+ 依数据而定。`maxOutputChars` 限制保留的命令输出;固定的裁剪、丢失前缀、状态、超时与重置诊断可能延长结果。
133
+
134
+ #### KV Cache 影响
135
+
136
+ 仅追加的工具结果位于可复用请求前缀之后。
137
+
138
+ ## 已知限制与延期工作
139
+
140
+ <a id="known-limitations-and-deferred-work"></a>
141
+
142
+
143
+ 这些限制说明工具何时不合适或需要特别小心。它们是当前包约束,不是任务积压。
144
+
145
+ - **工具需要拥有者 agent 与真实的 PTY 后端**——无 agent 的调用与无法启动交互 shell 的后端都会失败。
146
+ - **交互式前台子进程只在子进程提供方证明其 stdin 等待时才提前返回部分输出**——否则调用一直运行到 `timeoutMs`。
147
+ - **显式 `exit` 与超时会丢弃 shell 状态**——取消同样重置并丢弃结果,即使完整状态标记已经可观察;下一次调用启动全新 shell。
148
+ - **网络访问与包镜像等环境事实属于配置的 `description`**——而不是本包的默认描述。
149
+
150
+ <a id="dev-note"></a>
151
+ ### 开发备注
152
+
153
+ <details>
154
+ <summary>维护者的工作上下文——点击展开</summary>
155
+
156
+ 无。
157
+
158
+ </details>
package/lib/index.js ADDED
@@ -0,0 +1,389 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import z from "@qilin-agent/schemastery";
3
+ import { deadline, timeoutOf } from "@qilin-agent/timeout";
4
+ import { defineTool } from "@qilin-agent/tools";
5
+ //#region lib/types/index.js
6
+ /**
7
+ * Model-facing persistent `bash` tool over the owner-scoped PTY seam.
8
+ * @module @qilin-agent/tool-bash-persistent
9
+ */
10
+ var __addDisposableResource = function(env, value, async) {
11
+ if (value !== null && value !== void 0) {
12
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
13
+ var dispose, inner;
14
+ if (async) {
15
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
16
+ dispose = value[Symbol.asyncDispose];
17
+ }
18
+ if (dispose === void 0) {
19
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
20
+ dispose = value[Symbol.dispose];
21
+ if (async) inner = dispose;
22
+ }
23
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
24
+ if (inner) dispose = function() {
25
+ try {
26
+ inner.call(this);
27
+ } catch (e) {
28
+ return Promise.reject(e);
29
+ }
30
+ };
31
+ env.stack.push({
32
+ value,
33
+ dispose,
34
+ async
35
+ });
36
+ } else if (async) env.stack.push({ async: true });
37
+ return value;
38
+ };
39
+ var __disposeResources = (function(SuppressedError) {
40
+ return function(env) {
41
+ function fail(e) {
42
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
43
+ env.hasError = true;
44
+ }
45
+ var r, s = 0;
46
+ function next() {
47
+ while (r = env.stack.pop()) try {
48
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
49
+ if (r.dispose) {
50
+ var result = r.dispose.call(r.value);
51
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) {
52
+ fail(e);
53
+ return next();
54
+ });
55
+ } else s |= 1;
56
+ } catch (e) {
57
+ fail(e);
58
+ }
59
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
60
+ if (env.hasError) throw env.error;
61
+ }
62
+ return next();
63
+ };
64
+ })(typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
65
+ var e = new Error(message);
66
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
67
+ });
68
+ const TRUNCATED_MESSAGE = "<response clipped><NOTE>To save on context only part of this file has been shown to you. You should retry this tool after you have searched inside the file with `grep -n` in order to find the line numbers of what you are looking for.</NOTE>";
69
+ const LOST_PREFIX_MESSAGE = "<response clipped><NOTE>The beginning of this command output was dropped by the terminal scrollback limit. The following text is the earliest retained output.</NOTE>\n";
70
+ const SHELL_RESET_MESSAGE = "The persistent bash shell was reset; the next bash call starts from the workspace with a fresh current directory and environment.";
71
+ const TIMEOUT_STATUS_MARKER = "[Command timed out or OOM]";
72
+ const TIMEOUT_CODE = "PERSISTENT_BASH_TIMEOUT";
73
+ const SCROLLBACK_PAGE_LINES = 1e3;
74
+ const POLL_INTERVAL_MS = 25;
75
+ const DEFAULT_DESCRIPTION = "Run commands in a persistent bash shell. State, including the current directory and exported environment variables, persists across calls for this agent.";
76
+ function maybeTruncate(content, maxOutputChars, incomplete = false) {
77
+ if (content.length <= maxOutputChars && !incomplete) return content;
78
+ return content.length <= maxOutputChars ? content + TRUNCATED_MESSAGE : content.slice(0, maxOutputChars) + TRUNCATED_MESSAGE;
79
+ }
80
+ function markers() {
81
+ const nonce = randomUUID();
82
+ return {
83
+ start: `__QILIN_PERSISTENT_BASH_START_${nonce}__`,
84
+ end: `__QILIN_PERSISTENT_BASH_END_${nonce}:`
85
+ };
86
+ }
87
+ function quoteForBash(value) {
88
+ return `$'${value.replaceAll("\\", "\\\\").replaceAll("'", "\\'").replaceAll("\r", "\\r").replaceAll("\n", "\\n")}'`;
89
+ }
90
+ function wrapCommand(command, marker) {
91
+ return `printf '%s\\n' ${quoteForBash(marker.start)}; eval -- ${quoteForBash(command)}; __qilin_persistent_bash_status=$?; printf '%s%s\\n' ${quoteForBash(marker.end)} "$__qilin_persistent_bash_status"`;
92
+ }
93
+ function trimTrailingNewline(text) {
94
+ return text.replace(/(?:\r?\n)+$/, "");
95
+ }
96
+ function commandOutput(snapshot, marker) {
97
+ const text = snapshot.text;
98
+ const end = text.lastIndexOf(marker.end);
99
+ const status = /^(\d+)\r?\n/.exec(text.slice(end + marker.end.length))?.[1];
100
+ if (status === void 0) return void 0;
101
+ const startMarker = text.lastIndexOf(marker.start, end);
102
+ const start = startMarker < 0 ? 0 : startMarker + marker.start.length;
103
+ return {
104
+ text: trimTrailingNewline(text.slice(start, end).replace(/^\r?\n/, "")),
105
+ incomplete: startMarker < 0,
106
+ exitCode: Number(status)
107
+ };
108
+ }
109
+ function partialOutput(snapshot, marker, fallback, fallbackTruncated = false) {
110
+ const startMarker = snapshot.text.lastIndexOf(marker.start);
111
+ if (startMarker >= 0) return {
112
+ text: trimTrailingNewline(snapshot.text.slice(startMarker + marker.start.length).replace(/^\r?\n/, "")),
113
+ incomplete: false
114
+ };
115
+ const fallbackStart = fallback.lastIndexOf(marker.start);
116
+ const afterStart = fallbackStart < 0 ? fallback : fallback.slice(fallbackStart + marker.start.length).replace(/^\r?\n/, "");
117
+ const fallbackEnd = afterStart.lastIndexOf(marker.end);
118
+ return {
119
+ text: trimTrailingNewline(fallbackEnd < 0 ? afterStart : afterStart.slice(0, fallbackEnd)),
120
+ incomplete: fallbackTruncated || fallbackStart < 0
121
+ };
122
+ }
123
+ async function pause() {
124
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
125
+ }
126
+ function nextScrollbackOffset(page, offset) {
127
+ if (page.text.length === 0 || page.lineEnd <= offset) return void 0;
128
+ return page.lineEnd;
129
+ }
130
+ function retainedScrollback(ctx, owner, id, latest = ctx.terminals.read(owner, id, {
131
+ offset: 0,
132
+ count: SCROLLBACK_PAGE_LINES
133
+ })) {
134
+ const pages = latest.text.length === 0 ? [] : [latest.text];
135
+ let offset = latest.lineEnd;
136
+ let truncated = latest.truncated;
137
+ while (true) {
138
+ if (offset >= latest.totalLines) break;
139
+ const page = ctx.terminals.read(owner, id, {
140
+ offset,
141
+ count: SCROLLBACK_PAGE_LINES
142
+ });
143
+ truncated ||= page.truncated;
144
+ if (page.text.length > 0) pages.unshift(page.text);
145
+ const next = nextScrollbackOffset(page, offset);
146
+ if (next === void 0 || next >= page.totalLines) break;
147
+ offset = next;
148
+ }
149
+ return {
150
+ text: pages.join("\n"),
151
+ truncated
152
+ };
153
+ }
154
+ function renderCaptured(output, maxOutputChars) {
155
+ const rendered = maybeTruncate(output.text, maxOutputChars, output.incomplete);
156
+ return appendStatusMarker(output.incomplete && output.text.length > 0 ? LOST_PREFIX_MESSAGE + rendered : rendered, output.exitCode !== void 0 ? `[Command finished with exit code ${output.exitCode}]` : void 0);
157
+ }
158
+ function appendStatusMarker(content, marker) {
159
+ if (marker === void 0) return content;
160
+ return content.length === 0 ? marker : `${content}\n${marker}`;
161
+ }
162
+ function renderShellExitStatus(content, exitCode, signal) {
163
+ return appendStatusMarker(content, signal !== null ? `[shell killed by signal: ${signal}]` : exitCode !== null ? `[shell exited: code ${exitCode}]` : "[shell exited]");
164
+ }
165
+ /**
166
+ * Render the exited-session result, reset the owner's shell, and reset the
167
+ * message that tells the model the next call starts fresh.
168
+ * @param shells - the owner-scoped registry to reset.
169
+ * @param status - the exited session status (exit code and signal).
170
+ * @returns the complete model-facing result.
171
+ */
172
+ async function respondToSessionExit(ctx, shells, owner, id, status, marker, fallback, fallbackTruncated, config) {
173
+ const snapshot = retainedScrollback(ctx, owner, id);
174
+ await shells.reset(owner, "persistent bash shell exited");
175
+ return [renderShellExitStatus(renderCaptured(partialOutput(snapshot, marker, fallback, fallbackTruncated), config.maxOutputChars), status.exitCode, status.signal), SHELL_RESET_MESSAGE].filter((part) => part.length > 0).join("\n");
176
+ }
177
+ function persistentShells(ctx, config) {
178
+ const pending = /* @__PURE__ */ new WeakMap();
179
+ const live = /* @__PURE__ */ new Map();
180
+ const creating = /* @__PURE__ */ new Set();
181
+ const ownerCleanupInstalled = /* @__PURE__ */ new WeakSet();
182
+ const lifecycle = new AbortController();
183
+ const close = async (owner, id, reason) => {
184
+ if (!ctx.terminals.list(owner).some((snapshot) => snapshot.sessionId === id)) return;
185
+ await ctx.terminals.kill(owner, id, reason);
186
+ };
187
+ ctx.effect(() => async () => {
188
+ lifecycle.abort(/* @__PURE__ */ new Error("tool-bash-persistent disposed during shell creation"));
189
+ await Promise.allSettled([...creating]);
190
+ const closing = [...live].map(async ([owner, id]) => {
191
+ await close(owner, id, "tool-bash-persistent disposed");
192
+ });
193
+ await Promise.all(closing);
194
+ live.clear();
195
+ }, "tool-bash-persistent shell cleanup");
196
+ const reset = async (owner, reason) => {
197
+ pending.delete(owner);
198
+ const id = live.get(owner);
199
+ live.delete(owner);
200
+ if (id !== void 0) await close(owner, id, reason);
201
+ };
202
+ const get = (owner, signal) => {
203
+ const existing = pending.get(owner);
204
+ if (existing !== void 0) return existing;
205
+ const combinedSignal = AbortSignal.any([signal, lifecycle.signal]);
206
+ const tracked = (async () => {
207
+ try {
208
+ const cwd = owner.session.header.cwd;
209
+ const spawned = await ctx.terminals.spawn(owner, {
210
+ type: config.backendType,
211
+ ...cwd === void 0 ? {} : { cwd }
212
+ }, combinedSignal);
213
+ live.set(owner, spawned.sessionId);
214
+ if (!ownerCleanupInstalled.has(owner)) {
215
+ ownerCleanupInstalled.add(owner);
216
+ owner.ctx.effect(() => () => {
217
+ pending.delete(owner);
218
+ live.delete(owner);
219
+ }, "tool-bash-persistent owner cache cleanup");
220
+ }
221
+ const result = await ctx.terminals.startSend(owner, spawned.sessionId, {
222
+ text: "stty -echo",
223
+ submit: true,
224
+ signal: combinedSignal
225
+ }).done;
226
+ if (result.sessionStatus.kind === "exited" || result.waitReason === "timeout") throw new Error("persistent bash shell did not accept initialization");
227
+ return spawned.sessionId;
228
+ } catch (error) {
229
+ await reset(owner, "persistent bash initialization failed");
230
+ throw error;
231
+ }
232
+ })().finally(() => {
233
+ creating.delete(tracked);
234
+ });
235
+ creating.add(tracked);
236
+ pending.set(owner, tracked);
237
+ return tracked;
238
+ };
239
+ return {
240
+ get,
241
+ reset
242
+ };
243
+ }
244
+ async function executeCommand(ctx, shells, owner, command, config, upstream) {
245
+ const env_1 = {
246
+ stack: [],
247
+ error: void 0,
248
+ hasError: false
249
+ };
250
+ try {
251
+ const commandDeadline = __addDisposableResource(env_1, deadline(upstream, config.timeoutMs, TIMEOUT_CODE), false);
252
+ let id;
253
+ try {
254
+ id = await shells.get(owner, commandDeadline.signal);
255
+ } catch (error) {
256
+ if (upstream.aborted && error === upstream.reason) return "";
257
+ throw error;
258
+ }
259
+ const marker = markers();
260
+ const wrapped = wrapCommand(command, marker);
261
+ let first = true;
262
+ let fallback = "";
263
+ let fallbackTruncated = false;
264
+ while (true) {
265
+ const status = ctx.terminals.list(owner).find((session) => session.sessionId === id)?.status;
266
+ if (status?.kind === "exited") return await respondToSessionExit(ctx, shells, owner, id, status, marker, fallback, fallbackTruncated, config);
267
+ let operation;
268
+ let result;
269
+ try {
270
+ operation = ctx.terminals.startSend(owner, id, {
271
+ text: first ? wrapped : "",
272
+ submit: first,
273
+ signal: commandDeadline.signal
274
+ });
275
+ first = false;
276
+ result = await operation.done;
277
+ } catch (error) {
278
+ await shells.reset(owner, "persistent bash send failed");
279
+ throw error;
280
+ }
281
+ const incremental = operation.readOutput();
282
+ fallback = incremental.delta.length > 0 ? fallback + incremental.delta : result.viewport;
283
+ fallbackTruncated ||= incremental.truncated || result.truncated;
284
+ const latest = ctx.terminals.read(owner, id, {
285
+ offset: 0,
286
+ count: SCROLLBACK_PAGE_LINES
287
+ });
288
+ const timedOut = timeoutOf(commandDeadline.signal, TIMEOUT_CODE);
289
+ if (timedOut !== void 0) {
290
+ const partial = renderCaptured(partialOutput(retainedScrollback(ctx, owner, id, latest), marker, fallback, fallbackTruncated), config.maxOutputChars);
291
+ await shells.reset(owner, "persistent bash command timed out");
292
+ return [
293
+ `Your command timed out after ${Math.round(timedOut.timeoutMs / 1e3)} seconds or experienced an OOM error. Below is partial output:`,
294
+ appendStatusMarker(partial, TIMEOUT_STATUS_MARKER),
295
+ SHELL_RESET_MESSAGE
296
+ ].join("\n");
297
+ }
298
+ if (commandDeadline.signal.aborted) {
299
+ await shells.reset(owner, "persistent bash command aborted");
300
+ return "";
301
+ }
302
+ if (latest.text.includes(marker.end)) {
303
+ const complete = commandOutput(retainedScrollback(ctx, owner, id, latest), marker);
304
+ if (complete !== void 0) return renderCaptured(complete, config.maxOutputChars);
305
+ }
306
+ if (result.sessionStatus.kind === "exited") return await respondToSessionExit(ctx, shells, owner, id, result.sessionStatus, marker, fallback, fallbackTruncated, config);
307
+ if (result.waitReason === "stdin_read") return renderCaptured(partialOutput(retainedScrollback(ctx, owner, id, latest), marker, fallback, fallbackTruncated), config.maxOutputChars);
308
+ await pause();
309
+ }
310
+ } catch (e_1) {
311
+ env_1.error = e_1;
312
+ env_1.hasError = true;
313
+ } finally {
314
+ __disposeResources(env_1);
315
+ }
316
+ }
317
+ /**
318
+ * Register the model-facing persistent `bash` tool.
319
+ * @param ctx - plugin context carrying tools and the owner-scoped PTY service.
320
+ * @param config - selected PTY backend and command deadline.
321
+ */
322
+ function registerPersistentBash(ctx, config) {
323
+ const shells = persistentShells(ctx, config);
324
+ const queues = /* @__PURE__ */ new WeakMap();
325
+ const serialized = async (owner, operation) => {
326
+ const run = (queues.get(owner) ?? Promise.resolve()).then(operation, operation);
327
+ const tail = run.then(() => void 0, () => void 0);
328
+ queues.set(owner, tail);
329
+ try {
330
+ return await run;
331
+ } finally {
332
+ if (queues.get(owner) === tail) queues.delete(owner);
333
+ }
334
+ };
335
+ ctx.tools.register(defineTool({
336
+ name: "bash",
337
+ description: config.description,
338
+ parameters: { command: {
339
+ type: "string",
340
+ required: true,
341
+ description: "The bash command to run. Relative path is preferred in the command."
342
+ } },
343
+ output: {
344
+ schema: { type: "string" },
345
+ render: (_args, value) => [{
346
+ type: "text",
347
+ text: value
348
+ }]
349
+ },
350
+ async execute(args, exec) {
351
+ if (args.command.trim().length === 0) throw new Error("command must be a non-empty string");
352
+ const owner = exec.agent;
353
+ if (owner === void 0) throw new Error("bash requires an owning agent session");
354
+ return serialized(owner, async () => {
355
+ if (exec.signal.aborted) return "";
356
+ return executeCommand(ctx, shells, owner, args.command, config, exec.signal);
357
+ });
358
+ },
359
+ presentCall: (args) => ({
360
+ card: "terminal",
361
+ title: args.command
362
+ })
363
+ }));
364
+ }
365
+ const name = "tool-bash-persistent";
366
+ const inject = ["tools", "terminals"];
367
+ /** Runtime configuration schema for the persistent Bash tool. */
368
+ const Config = z.object({
369
+ backendType: z.string().default("shell"),
370
+ timeoutMs: z.number().default(3e5),
371
+ maxOutputChars: z.number().default(16e3),
372
+ description: z.string().default(DEFAULT_DESCRIPTION)
373
+ });
374
+ /** Register one owner-scoped persistent `bash` tool. */
375
+ function apply(ctx, config) {
376
+ const resolved = {
377
+ backendType: config.backendType ?? "shell",
378
+ timeoutMs: config.timeoutMs ?? 3e5,
379
+ maxOutputChars: config.maxOutputChars ?? 16e3,
380
+ description: config.description ?? DEFAULT_DESCRIPTION
381
+ };
382
+ if (resolved.backendType.trim().length === 0) throw new Error("tool-bash-persistent: backendType must be non-empty");
383
+ if (!Number.isSafeInteger(resolved.timeoutMs) || resolved.timeoutMs <= 0) throw new Error("tool-bash-persistent: timeoutMs must be a positive safe integer");
384
+ if (!Number.isSafeInteger(resolved.maxOutputChars) || resolved.maxOutputChars <= 0) throw new Error("tool-bash-persistent: maxOutputChars must be a positive safe integer");
385
+ if (resolved.description.trim().length === 0) throw new Error("tool-bash-persistent: description must be non-empty");
386
+ registerPersistentBash(ctx, resolved);
387
+ }
388
+ //#endregion
389
+ export { Config, apply, inject, name };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Model-facing persistent `bash` tool over the owner-scoped PTY seam.
3
+ * @module @qilin-agent/tool-bash-persistent
4
+ */
5
+ import type { Context } from '@qilin-agent/kylin';
6
+ import z from '@qilin-agent/schemastery';
7
+ export declare const name = "tool-bash-persistent";
8
+ export declare const inject: string[];
9
+ /** Configuration for the persistent Bash tool. */
10
+ export interface Config {
11
+ /** PTY backend used for each owner-isolated persistent shell (default `shell`). */
12
+ backendType?: string;
13
+ /** Wall-clock limit for one command (default 300000). */
14
+ timeoutMs?: number;
15
+ /** Maximum returned command-output characters before clipping (default 16000). */
16
+ maxOutputChars?: number;
17
+ /** Model-facing tool description; deployments may describe their environment. */
18
+ description?: string;
19
+ }
20
+ /** Runtime configuration schema for the persistent Bash tool. */
21
+ export declare const Config: z<Config>;
22
+ /** Register one owner-scoped persistent `bash` tool. */
23
+ export declare function apply(ctx: Context, config: Config): void;
24
+ //# sourceMappingURL=index.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,59 @@
1
1
  {
2
2
  "name": "@qilin-agent/tool-bash-persistent",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "description": "Model-facing owner-scoped persistent Bash tool backed by the Harness PTY service",
4
+ "version": "3.1.3",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/kkutysllb/QiLin.git",
11
+ "directory": "packages/shell/tool-bash-persistent"
12
+ },
13
+ "engines": {
14
+ "node": "^22.19.0 || >=24.0.0"
15
+ },
16
+ "type": "module",
17
+ "main": "lib/index.js",
18
+ "types": "lib/types/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./lib/types/index.d.ts",
22
+ "default": "./lib/index.js"
23
+ },
24
+ "./package.json": "./package.json"
25
+ },
26
+ "files": [
27
+ "lib/index.js",
28
+ "lib/types/**/*.d.ts"
29
+ ],
30
+ "license": "MIT",
31
+ "peerDependencies": {
32
+ "@qilin-agent/agent": "3.1.3",
33
+ "@qilin-agent/timeout": "3.1.3",
34
+ "@qilin-agent/tools": "3.1.3",
35
+ "@qilin-agent/kylin": "~4.0.3",
36
+ "@qilin-agent/terminal": "3.1.3"
37
+ },
38
+ "dependencies": {
39
+ "@qilin-agent/schemastery": "~3.18.3"
40
+ },
41
+ "devDependencies": {
42
+ "@qilin-agent/kylin-plugin-include": "~1.0.8",
43
+ "@qilin-agent/kylin-plugin-loader": "~1.0.4",
44
+ "@qilin-agent/agent": "3.1.3",
45
+ "@qilin-agent/agent-loop-testkit": "3.1.3",
46
+ "@qilin-agent/llm": "3.1.3",
47
+ "@qilin-agent/terminal": "3.1.3",
48
+ "@qilin-agent/terminal-bash": "3.1.3",
49
+ "@qilin-agent/sandbox": "3.1.3",
50
+ "@qilin-agent/sandbox-policy": "3.1.3",
51
+ "@qilin-agent/session": "3.1.3",
52
+ "@qilin-agent/system-prompt": "3.1.3",
53
+ "@qilin-agent/subprocess-local": "3.1.3",
54
+ "@qilin-agent/kylin": "~4.0.3",
55
+ "@qilin-agent/timeout": "3.1.3",
56
+ "@qilin-agent/session-projection": "3.1.3",
57
+ "@qilin-agent/tools": "3.1.3"
58
+ }
6
59
  }