@buyi1net/pi-toolkit 0.0.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.
Files changed (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -0
  3. package/assembler.ts +127 -0
  4. package/config.ts +201 -0
  5. package/i18n.ts +693 -0
  6. package/index.ts +99 -0
  7. package/menu/items.ts +264 -0
  8. package/menu/panels.ts +91 -0
  9. package/menu/settings-list.ts +84 -0
  10. package/menu/theme.ts +31 -0
  11. package/menu/toolkit-menu.ts +139 -0
  12. package/module.ts +156 -0
  13. package/modules/eyes/chain.ts +173 -0
  14. package/modules/eyes/config.ts +319 -0
  15. package/modules/eyes/index.ts +161 -0
  16. package/modules/eyes/menu.ts +828 -0
  17. package/modules/eyes/pi-model-backend.ts +245 -0
  18. package/modules/eyes/resilience.ts +161 -0
  19. package/modules/eyes/vision-bridge.ts +232 -0
  20. package/modules/eyes/vision-cache.ts +86 -0
  21. package/modules/eyes/vision-json.ts +19 -0
  22. package/modules/eyes/vision-preprocess.ts +192 -0
  23. package/modules/eyes/vision-probe.ts +21 -0
  24. package/modules/eyes/vision-prompt.ts +90 -0
  25. package/modules/eyes/vision-tool.ts +59 -0
  26. package/modules/eyes/vision-types.ts +33 -0
  27. package/modules/index.ts +10 -0
  28. package/modules/subagents/agents/researcher.md +51 -0
  29. package/modules/subagents/agents/scout.md +40 -0
  30. package/modules/subagents/agents/worker.md +79 -0
  31. package/modules/subagents/config.json.example +6 -0
  32. package/modules/subagents/config.ts +144 -0
  33. package/modules/subagents/index.ts +91 -0
  34. package/modules/subagents/menu.ts +388 -0
  35. package/modules/subagents/src/activity.ts +511 -0
  36. package/modules/subagents/src/agents.ts +126 -0
  37. package/modules/subagents/src/command.ts +37 -0
  38. package/modules/subagents/src/dependencies.ts +246 -0
  39. package/modules/subagents/src/diagnostics.ts +13 -0
  40. package/modules/subagents/src/display.ts +94 -0
  41. package/modules/subagents/src/headless.ts +342 -0
  42. package/modules/subagents/src/herdr.ts +203 -0
  43. package/modules/subagents/src/index.ts +2798 -0
  44. package/modules/subagents/src/inspect-tool.ts +839 -0
  45. package/modules/subagents/src/launch-config.ts +196 -0
  46. package/modules/subagents/src/layout-budget.ts +26 -0
  47. package/modules/subagents/src/list-tool.ts +292 -0
  48. package/modules/subagents/src/message-tool.ts +808 -0
  49. package/modules/subagents/src/names.ts +17 -0
  50. package/modules/subagents/src/pane-layout.ts +49 -0
  51. package/modules/subagents/src/params.ts +145 -0
  52. package/modules/subagents/src/renderers.ts +181 -0
  53. package/modules/subagents/src/result.ts +43 -0
  54. package/modules/subagents/src/retention.ts +114 -0
  55. package/modules/subagents/src/route-error.ts +212 -0
  56. package/modules/subagents/src/routing.ts +235 -0
  57. package/modules/subagents/src/runtime-registry.ts +159 -0
  58. package/modules/subagents/src/session.ts +801 -0
  59. package/modules/subagents/src/status.ts +513 -0
  60. package/modules/subagents/src/stop-tool.ts +235 -0
  61. package/modules/subagents/src/subagent-done.ts +590 -0
  62. package/modules/subagents/src/subagent-tool.ts +1155 -0
  63. package/modules/subagents/src/surface.ts +355 -0
  64. package/modules/subagents/src/team-dispatch-tool.ts +219 -0
  65. package/modules/subagents/src/team.ts +232 -0
  66. package/modules/subagents/src/tmux.ts +210 -0
  67. package/modules/subagents/src/tools/safe-bash.ts +72 -0
  68. package/modules/subagents/src/types.ts +131 -0
  69. package/modules/tui/adapter/provider-usage.ts +143 -0
  70. package/modules/tui/config.ts +50 -0
  71. package/modules/tui/index.ts +120 -0
  72. package/modules/tui/kernel/pkg/shared/grok-subscription.ts +169 -0
  73. package/modules/tui/kernel/pkg/shared/official-subscription.ts +237 -0
  74. package/modules/tui/kernel/pkg/shared/provider-catalog.ts +367 -0
  75. package/modules/tui/kernel/pkg/shared/provider-contracts.ts +150 -0
  76. package/modules/tui/kernel/pkg/shared/provider-display.ts +55 -0
  77. package/modules/tui/kernel/pkg/shared/provider-parsers.ts +171 -0
  78. package/modules/tui/kernel/pkg/shared/volcengine.ts +191 -0
  79. package/modules/tui/kernel/pkg/shared/zhipu.ts +149 -0
  80. package/modules/tui/kernel/pkg/usage-core/index.ts +335 -0
  81. package/modules/tui/kernel/pkg/usage-core/provider-routes.ts +187 -0
  82. package/modules/tui/kernel/pkg/usage-node/index.ts +635 -0
  83. package/modules/tui/kernel/pkg/usage-node/provider-usage.ts +388 -0
  84. package/modules/tui/kernel/usage-core.ts +2 -0
  85. package/modules/tui/kernel/usage-node.ts +2 -0
  86. package/modules/tui/menu.ts +418 -0
  87. package/modules/tui/plugin/editor.ts +396 -0
  88. package/modules/tui/plugin/footer.ts +161 -0
  89. package/modules/tui/plugin/index.ts +30 -0
  90. package/modules/tui/plugin/lifecycle.ts +637 -0
  91. package/modules/tui/plugin/package-order.ts +169 -0
  92. package/modules/tui/plugin/screen-transition.ts +203 -0
  93. package/modules/tui/plugin/settings-config.ts +297 -0
  94. package/modules/tui/plugin/status-sources.ts +49 -0
  95. package/modules/tui/plugin/transition-gate.ts +261 -0
  96. package/modules/tui/renderer/custom-header.ts +157 -0
  97. package/modules/tui/renderer/editor.ts +70 -0
  98. package/modules/tui/renderer/header.ts +72 -0
  99. package/modules/tui/renderer/icons.ts +149 -0
  100. package/modules/tui/renderer/pi-installer-logo.ts +194 -0
  101. package/modules/tui/status/auto-compaction.ts +67 -0
  102. package/modules/tui/status/project-status.ts +655 -0
  103. package/modules/tui/status/provider-status.ts +120 -0
  104. package/modules/tui/status/runtime-status.ts +307 -0
  105. package/modules/tui/status/session-status.ts +325 -0
  106. package/modules/tui/status/status-config.ts +95 -0
  107. package/modules/tui/status/status-segments.ts +263 -0
  108. package/modules/tui/status/turn-telemetry.ts +466 -0
  109. package/modules/tui/themes/LICENSE.pi-themes-bundle +21 -0
  110. package/modules/tui/themes/UPSTREAM.md +7 -0
  111. package/modules/tui/themes/catppuccin-latte.json +80 -0
  112. package/modules/tui/themes/catppuccin-mocha.json +79 -0
  113. package/modules/tui/themes/crimson-noir.json +85 -0
  114. package/modules/tui/themes/dracula.json +79 -0
  115. package/modules/tui/themes/everforest-dark.json +85 -0
  116. package/modules/tui/themes/gruvbox-dark.json +85 -0
  117. package/modules/tui/themes/gruvbox-light.json +85 -0
  118. package/modules/tui/themes/matrix.json +85 -0
  119. package/modules/tui/themes/nord.json +85 -0
  120. package/modules/tui/themes/one-dark.json +85 -0
  121. package/modules/tui/themes/rose-pine-dawn.json +85 -0
  122. package/modules/tui/themes/rose-pine.json +85 -0
  123. package/modules/tui/themes/solarized-dark.json +85 -0
  124. package/modules/tui/themes/solarized-light.json +85 -0
  125. package/modules/tui/themes/tokyo-night-storm.json +79 -0
  126. package/modules/tui/themes/tokyo-night.json +79 -0
  127. package/package.json +28 -0
  128. package/services.ts +38 -0
  129. package/toolkit.ts +147 -0
@@ -0,0 +1,235 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { Type } from "@sinclair/typebox";
3
+ import { Text } from "@earendil-works/pi-tui";
4
+ import { debugLog } from "./diagnostics.ts";
5
+ import type { RunningSubagent, SubagentResult } from "./types.ts";
6
+
7
+ export interface SubagentStopToolDeps {
8
+ /** 按名字解析本会话运行中的子代理(含空名/未知名/重名的明确错误)。 */
9
+ resolveRunningByName: (name: string) => { running: RunningSubagent } | { error: string };
10
+ /** 关闭 pane(内部 assertOwned 白名单护栏:只关本扩展创建/接管的 pane)。 */
11
+ closeSurface: (surface: string) => void;
12
+ updateWidget: () => void;
13
+ /** 等待落定上限(毫秒),缺省 STOP_SETTLE_TIMEOUT_MS;测试可注入短时限。 */
14
+ settleTimeoutMs?: number;
15
+ /** 持久成员终止(成员 watcher 不兑终态,由这里直接 kill + roster offline)。 */
16
+ stopMember?: (running: RunningSubagent) => void;
17
+ }
18
+
19
+ /**
20
+ * 等待停止落定的上限:watcher 轮询 tick 为 1s,10s 足够宽裕;超时不伪造
21
+ * "已停止",如实报告 stop-requested(停止信号已发出,落定稍后自行完成)。
22
+ */
23
+ const STOP_SETTLE_TIMEOUT_MS = 10_000;
24
+
25
+ /**
26
+ * `subagent_stop`:唯一的显式停止入口。
27
+ *
28
+ * 生命周期契约(与 Escape/detached 语义严格分离):
29
+ * - 主工具等待被 Escape 中止只会让工具调用返回 detached——子代理继续运行,
30
+ * 不会被杀、不会被记成 cancelled;真正终止子代理只发生在本工具。
31
+ * - 普通的 subagent_message 文本不是停止命令,绝不解析为隐式指令。
32
+ * - 只允许操作本会话内由本插件登记、且仍在运行的子代理;已结束的子代理
33
+ * 没有可停止的进程,明确报错引导(subagents_list 查看)。
34
+ *
35
+ * 执行路径:置 stopRequested 标志(让 watcher 把停止后的 pane 消失按显式
36
+ * cancelled 落定,不误分类 user_closed)→ 交互式 pane 在此直接关闭(自动
37
+ * pane/headless 由 watcher 的 cancelled 分支统一终止)→ abort watcher →
38
+ * 等 watcher 兑出终态(幂等:watcher promise 单次 resolve,结果回注仍由
39
+ * 原有路径——阻塞中的工具调用或 detached 迟到回注——恰好各一次)。
40
+ */
41
+ export function registerSubagentStopTool(pi: ExtensionAPI, deps: SubagentStopToolDeps): void {
42
+ pi.registerTool({
43
+ name: "subagent_stop",
44
+ label: "Stop Subagent",
45
+ description:
46
+ "Explicitly stop a RUNNING subagent by name — this is the ONLY way to terminate one. " +
47
+ "Aborting the spawning tool call (Escape) only detaches the wait: the sub-agent keeps running and is NOT cancelled. " +
48
+ "subagent_message text is never interpreted as a stop command. " +
49
+ "Only subagents registered by THIS session's pi-subagents extension can be stopped; finished sub-agents have no live process to stop. " +
50
+ "Stopping kills the headless process / closes the pane, cleans up its runtime registration, settles its dependency record as cancelled, " +
51
+ "and delivers a single terminal result through the existing path (the blocked tool call, or a late steer message if the wait was detached).",
52
+ promptSnippet:
53
+ "Stop a running subagent by name — the only explicit stop entry point. Escape-detached sub-agents keep running; use this to really terminate one.",
54
+ parameters: Type.Object({
55
+ name: Type.String({
56
+ description:
57
+ "Exact display name of the RUNNING subagent to stop (see subagents_list scope:session). " +
58
+ "Names are unique per session; finished sub-agents cannot be stopped.",
59
+ }),
60
+ }),
61
+
62
+ renderCall(args, theme) {
63
+ const target = typeof (args as any).name === "string" ? (args as any).name : "(unknown)";
64
+ return new Text(
65
+ "○ " + theme.fg("toolTitle", theme.bold(target)) + theme.fg("dim", " — stop"),
66
+ 0,
67
+ 0,
68
+ );
69
+ },
70
+
71
+ renderResult(result, _opts, theme) {
72
+ const details = result.details as any;
73
+ const name = details?.name ?? "subagent";
74
+ if (details?.status === "stopped") {
75
+ return new Text(
76
+ theme.fg("error", "■") + " " + theme.fg("toolTitle", theme.bold(name)) +
77
+ theme.fg("warning", " — stopped"),
78
+ 0,
79
+ 0,
80
+ );
81
+ }
82
+ if (details?.status === "stop-requested") {
83
+ return new Text(
84
+ theme.fg("warning", "■") + " " + theme.fg("toolTitle", theme.bold(name)) +
85
+ theme.fg("dim", " — stop requested (settling)"),
86
+ 0,
87
+ 0,
88
+ );
89
+ }
90
+ const content = result.content[0];
91
+ const text = content && content.type === "text" ? content.text : "";
92
+ return new Text(theme.fg("dim", text), 0, 0);
93
+ },
94
+
95
+ async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
96
+ const rawName = typeof params?.name === "string" ? params.name.trim() : "";
97
+ if (!rawName) {
98
+ const error = "Provide the exact `name` of the running subagent to stop.";
99
+ return { content: [{ type: "text", text: error }], details: { error } };
100
+ }
101
+ const resolved = deps.resolveRunningByName(rawName);
102
+ if ("error" in resolved) {
103
+ return {
104
+ content: [{
105
+ type: "text",
106
+ text:
107
+ `${resolved.error} ` +
108
+ `Only subagents still running in this session can be stopped — finished ones have no live process. ` +
109
+ `Use subagents_list with scope "session" to see what is running.`,
110
+ }],
111
+ details: { error: resolved.error, name: rawName },
112
+ };
113
+ }
114
+
115
+ const running = resolved.running;
116
+ // 持久成员:watcher 是常驻轮询(不兑终态 promise),停止由专用入口
117
+ // 直接终止进程 + roster offline,同步返回 stopped(不等待 watcher 轮次)。
118
+ if (running.member) {
119
+ running.stopRequested = true;
120
+ deps.stopMember?.(running);
121
+ deps.updateWidget();
122
+ return {
123
+ content: [{
124
+ type: "text",
125
+ text:
126
+ `Team member "${running.name}" was stopped: its process was terminated and the roster marks it offline. ` +
127
+ `Its session/loadout are preserved at ${running.sessionFile} — resume it as a one-shot task with ` +
128
+ `subagent_message, or respawn a fresh member with subagent({ member: true, … }) if the team still needs it.`,
129
+ }],
130
+ details: {
131
+ id: running.id,
132
+ name: running.name,
133
+ sessionFile: running.sessionFile,
134
+ status: "stopped",
135
+ member: true,
136
+ },
137
+ };
138
+ }
139
+ // 标记先于 abort:watcher 观察到停止后的 pane 消失时按显式 cancelled
140
+ // 落定,不会误分类为 user_closed(用户直接关闭)。
141
+ running.stopRequested = true;
142
+ // 交互式 pane 归用户驱动,watcher 的取消分支不会关它——显式停止时在
143
+ // 此直接关闭(closeSurface 内 assertOwned 白名单护栏只放行本插件
144
+ // 创建/接管的 pane)。已死 pane 报错忽略,watcher 会按停止终态落定。
145
+ if (running.interactive && running.kind !== "headless") {
146
+ try {
147
+ deps.closeSurface(running.surface);
148
+ } catch (error) {
149
+ debugLog(`Could not close interactive pane ${running.surface} during stop`, error);
150
+ }
151
+ }
152
+ // 中止 watcher 等待循环:watcher 判定 cancelled(显式 stop)后终止
153
+ // headless 进程/关闭自动 pane、移除 runtime record、兑 cancelled 终态。
154
+ running.abortController?.abort();
155
+ deps.updateWidget();
156
+
157
+ if (running.watchPromise) {
158
+ const timeoutMs = deps.settleTimeoutMs ?? STOP_SETTLE_TIMEOUT_MS;
159
+ type StopSettle =
160
+ | { ok: true; result: SubagentResult | null; error?: string }
161
+ | { ok: false };
162
+ const settled: StopSettle = await Promise.race([
163
+ running.watchPromise.then(
164
+ (result): StopSettle => ({ ok: true, result }),
165
+ (error: any): StopSettle => ({
166
+ ok: true,
167
+ result: null,
168
+ error: error?.message ?? String(error),
169
+ }),
170
+ ),
171
+ new Promise<StopSettle>((resolve) =>
172
+ setTimeout(() => resolve({ ok: false }), timeoutMs),
173
+ ),
174
+ ]);
175
+ if (settled.ok && settled.result) {
176
+ return {
177
+ content: [{
178
+ type: "text",
179
+ text:
180
+ `Sub-agent "${running.name}" was stopped. Its process/pane was terminated, its runtime ` +
181
+ `registration cleaned up, and its dependency record settled as cancelled. ` +
182
+ (settled.result.sessionFile
183
+ ? `Its session file is preserved at ${settled.result.sessionFile} and can be resumed with subagent_message.`
184
+ : `A terminal result is being delivered through the existing path.`),
185
+ }],
186
+ details: {
187
+ id: running.id,
188
+ name: running.name,
189
+ sessionFile: settled.result.sessionFile ?? running.sessionFile,
190
+ status: "stopped",
191
+ exitCode: settled.result.exitCode,
192
+ elapsed: settled.result.elapsed,
193
+ },
194
+ };
195
+ }
196
+ if (settled.ok && settled.result === null) {
197
+ // watcher reject(正常不发生):停止已发出,如实报告错误。
198
+ const errorText = settled.error ?? "unknown watcher error";
199
+ return {
200
+ content: [{
201
+ type: "text",
202
+ text:
203
+ `Stop signal was delivered to "${running.name}", but its watcher ended with an error: ${errorText}. ` +
204
+ `Treat the sub-agent as stopped and check its session if needed.`,
205
+ }],
206
+ details: { id: running.id, name: running.name, status: "stopped", error: errorText },
207
+ };
208
+ }
209
+ if (!settled.ok) {
210
+ return {
211
+ content: [{
212
+ type: "text",
213
+ text:
214
+ `Stop signal was delivered to "${running.name}"; it did not settle within ` +
215
+ `${timeoutMs / 1000}s (the watcher may still be tearing down). ` +
216
+ `The terminal result will arrive through the existing delivery path — do not respawn.`,
217
+ }],
218
+ details: { id: running.id, name: running.name, status: "stop-requested" },
219
+ };
220
+ }
221
+ }
222
+
223
+ // 无 watchPromise(旧 mock/极端竞态):停止信号已发出,不伪造完成。
224
+ return {
225
+ content: [{
226
+ type: "text",
227
+ text:
228
+ `Stop signal was delivered to "${running.name}". The sub-agent is being terminated; ` +
229
+ `its terminal result will arrive through the existing delivery path.`,
230
+ }],
231
+ details: { id: running.id, name: running.name, status: "stop-requested" },
232
+ };
233
+ },
234
+ });
235
+ }