lsd-pi 1.1.4 → 1.1.6

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 (175) hide show
  1. package/README.md +2 -1
  2. package/dist/headless-ui.js +2 -0
  3. package/dist/onboarding.js +11 -8
  4. package/dist/resources/extensions/async-jobs/async-bash-tool.js +14 -0
  5. package/dist/resources/extensions/async-jobs/await-tool.js +14 -0
  6. package/dist/resources/extensions/async-jobs/cancel-job-tool.js +7 -0
  7. package/dist/resources/extensions/cache-timer/index.js +5 -0
  8. package/dist/resources/extensions/codex-rotate/IMPLEMENTATION.md +18 -13
  9. package/dist/resources/extensions/codex-rotate/README.md +9 -3
  10. package/dist/resources/extensions/codex-rotate/commands.js +15 -8
  11. package/dist/resources/extensions/codex-rotate/index.js +17 -8
  12. package/dist/resources/extensions/memory/auto-extract.js +196 -80
  13. package/dist/resources/extensions/memory/dream.js +86 -19
  14. package/dist/resources/extensions/shared/rtk.js +89 -87
  15. package/dist/resources/extensions/subagent/index.js +33 -7
  16. package/dist/startup-model-validation.js +12 -2
  17. package/dist/update-check.js +2 -2
  18. package/dist/update-cmd.js +3 -3
  19. package/dist/welcome-screen.js +43 -14
  20. package/package.json +3 -2
  21. package/packages/pi-coding-agent/dist/core/agent-session.clear-queue.test.d.ts +2 -0
  22. package/packages/pi-coding-agent/dist/core/agent-session.clear-queue.test.d.ts.map +1 -0
  23. package/packages/pi-coding-agent/dist/core/agent-session.clear-queue.test.js +46 -0
  24. package/packages/pi-coding-agent/dist/core/agent-session.clear-queue.test.js.map +1 -0
  25. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +8 -0
  26. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  27. package/packages/pi-coding-agent/dist/core/agent-session.js +43 -4
  28. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  29. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +3 -1
  30. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  31. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  32. package/packages/pi-coding-agent/dist/core/keybindings.d.ts +1 -1
  33. package/packages/pi-coding-agent/dist/core/keybindings.d.ts.map +1 -1
  34. package/packages/pi-coding-agent/dist/core/keybindings.js +2 -0
  35. package/packages/pi-coding-agent/dist/core/keybindings.js.map +1 -1
  36. package/packages/pi-coding-agent/dist/core/pty-executor.d.ts +48 -0
  37. package/packages/pi-coding-agent/dist/core/pty-executor.d.ts.map +1 -0
  38. package/packages/pi-coding-agent/dist/core/pty-executor.js +173 -0
  39. package/packages/pi-coding-agent/dist/core/pty-executor.js.map +1 -0
  40. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  41. package/packages/pi-coding-agent/dist/core/sdk.js +16 -3
  42. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  43. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +9 -0
  44. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  45. package/packages/pi-coding-agent/dist/core/settings-manager.js +18 -0
  46. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  47. package/packages/pi-coding-agent/dist/core/tool-approval.d.ts.map +1 -1
  48. package/packages/pi-coding-agent/dist/core/tool-approval.js +2 -2
  49. package/packages/pi-coding-agent/dist/core/tool-approval.js.map +1 -1
  50. package/packages/pi-coding-agent/dist/core/tools/index.d.ts +7 -0
  51. package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
  52. package/packages/pi-coding-agent/dist/core/tools/index.js +23 -2
  53. package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
  54. package/packages/pi-coding-agent/dist/core/tools/pty.d.ts +50 -0
  55. package/packages/pi-coding-agent/dist/core/tools/pty.d.ts.map +1 -0
  56. package/packages/pi-coding-agent/dist/core/tools/pty.js +289 -0
  57. package/packages/pi-coding-agent/dist/core/tools/pty.js.map +1 -0
  58. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  59. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  60. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +36 -22
  61. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  62. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts +3 -5
  63. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  64. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +23 -62
  65. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
  66. package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  67. package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.js +1 -4
  68. package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  69. package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  70. package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js +1 -4
  71. package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  72. package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.d.ts +39 -0
  73. package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.d.ts.map +1 -0
  74. package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.js +182 -0
  75. package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.js.map +1 -0
  76. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +6 -0
  77. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  78. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +36 -0
  79. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  80. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  81. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +2 -4
  82. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  83. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -2
  84. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  85. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +106 -77
  86. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  87. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +2 -5
  88. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  89. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +4 -13
  90. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  91. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +11 -0
  92. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  93. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +49 -13
  94. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  95. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts.map +1 -1
  96. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +1 -0
  97. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -1
  98. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  99. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +2 -0
  100. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  101. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +3 -0
  102. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  103. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  104. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +27 -0
  105. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  106. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +251 -39
  107. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  108. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +2 -2
  109. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  110. package/packages/pi-coding-agent/dist/utils/terminal-screen.d.ts +10 -0
  111. package/packages/pi-coding-agent/dist/utils/terminal-screen.d.ts.map +1 -0
  112. package/packages/pi-coding-agent/dist/utils/terminal-screen.js +67 -0
  113. package/packages/pi-coding-agent/dist/utils/terminal-screen.js.map +1 -0
  114. package/packages/pi-coding-agent/dist/utils/terminal-serializer.d.ts +7 -0
  115. package/packages/pi-coding-agent/dist/utils/terminal-serializer.d.ts.map +1 -0
  116. package/packages/pi-coding-agent/dist/utils/terminal-serializer.js +67 -0
  117. package/packages/pi-coding-agent/dist/utils/terminal-serializer.js.map +1 -0
  118. package/packages/pi-coding-agent/package.json +9 -4
  119. package/packages/pi-coding-agent/src/core/agent-session.clear-queue.test.ts +50 -0
  120. package/packages/pi-coding-agent/src/core/agent-session.ts +50 -4
  121. package/packages/pi-coding-agent/src/core/extensions/types.ts +1 -1
  122. package/packages/pi-coding-agent/src/core/keybindings.ts +4 -1
  123. package/packages/pi-coding-agent/src/core/pty-executor.ts +229 -0
  124. package/packages/pi-coding-agent/src/core/sdk.ts +16 -3
  125. package/packages/pi-coding-agent/src/core/settings-manager.ts +27 -0
  126. package/packages/pi-coding-agent/src/core/tool-approval.ts +2 -2
  127. package/packages/pi-coding-agent/src/core/tools/index.ts +35 -2
  128. package/packages/pi-coding-agent/src/core/tools/pty.ts +354 -0
  129. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +37 -24
  130. package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +22 -70
  131. package/packages/pi-coding-agent/src/modes/interactive/components/branch-summary-message.ts +1 -3
  132. package/packages/pi-coding-agent/src/modes/interactive/components/compaction-summary-message.ts +1 -3
  133. package/packages/pi-coding-agent/src/modes/interactive/components/embedded-terminal.ts +224 -0
  134. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +45 -0
  135. package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +2 -3
  136. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +104 -81
  137. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +5 -19
  138. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +55 -13
  139. package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +1 -0
  140. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +2 -0
  141. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +3 -0
  142. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +296 -48
  143. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +2 -2
  144. package/packages/pi-coding-agent/src/utils/terminal-screen.ts +77 -0
  145. package/packages/pi-coding-agent/src/utils/terminal-serializer.ts +72 -0
  146. package/packages/pi-tui/dist/components/__tests__/editor-dropped-image.test.d.ts +2 -0
  147. package/packages/pi-tui/dist/components/__tests__/editor-dropped-image.test.d.ts.map +1 -0
  148. package/packages/pi-tui/dist/components/__tests__/editor-dropped-image.test.js +105 -0
  149. package/packages/pi-tui/dist/components/__tests__/editor-dropped-image.test.js.map +1 -0
  150. package/packages/pi-tui/dist/components/editor.d.ts +4 -0
  151. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  152. package/packages/pi-tui/dist/components/editor.js +57 -3
  153. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  154. package/packages/pi-tui/dist/components/loader.d.ts +26 -6
  155. package/packages/pi-tui/dist/components/loader.d.ts.map +1 -1
  156. package/packages/pi-tui/dist/components/loader.js +178 -18
  157. package/packages/pi-tui/dist/components/loader.js.map +1 -1
  158. package/packages/pi-tui/src/components/editor.ts +65 -3
  159. package/packages/pi-tui/src/components/loader.ts +196 -19
  160. package/pkg/dist/modes/interactive/theme/themes.js +2 -2
  161. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  162. package/pkg/package.json +1 -1
  163. package/src/resources/extensions/async-jobs/async-bash-tool.ts +13 -0
  164. package/src/resources/extensions/async-jobs/await-tool.ts +13 -0
  165. package/src/resources/extensions/async-jobs/cancel-job-tool.ts +8 -0
  166. package/src/resources/extensions/cache-timer/index.ts +102 -96
  167. package/src/resources/extensions/codex-rotate/IMPLEMENTATION.md +18 -13
  168. package/src/resources/extensions/codex-rotate/README.md +9 -3
  169. package/src/resources/extensions/codex-rotate/commands.ts +335 -329
  170. package/src/resources/extensions/codex-rotate/index.ts +85 -75
  171. package/src/resources/extensions/memory/auto-extract.ts +330 -204
  172. package/src/resources/extensions/memory/dream.ts +88 -21
  173. package/src/resources/extensions/memory/tests/auto-extract.test.ts +200 -144
  174. package/src/resources/extensions/shared/rtk.js +112 -0
  175. package/src/resources/extensions/subagent/index.ts +35 -6
@@ -1,110 +1,112 @@
1
- import { spawnSync } from "node:child_process";
2
- import { existsSync } from "node:fs";
3
- import { homedir } from "node:os";
4
- import { delimiter, join } from "node:path";
5
- const GSD_RTK_PATH_ENV = "GSD_RTK_PATH";
6
- const GSD_RTK_DISABLED_ENV = "GSD_RTK_DISABLED";
7
- const GSD_RTK_REWRITE_TIMEOUT_MS_ENV = "GSD_RTK_REWRITE_TIMEOUT_MS";
8
- const RTK_TELEMETRY_DISABLED_ENV = "RTK_TELEMETRY_DISABLED";
9
- const RTK_REWRITE_TIMEOUT_MS = 5_000;
1
+ import { spawnSync } from 'node:child_process'
2
+ import { existsSync } from 'node:fs'
3
+ import { homedir } from 'node:os'
4
+ import { delimiter, join } from 'node:path'
5
+
6
+ const GSD_RTK_PATH_ENV = 'GSD_RTK_PATH'
7
+ const GSD_RTK_DISABLED_ENV = 'GSD_RTK_DISABLED'
8
+ const GSD_RTK_REWRITE_TIMEOUT_MS_ENV = 'GSD_RTK_REWRITE_TIMEOUT_MS'
9
+ const RTK_TELEMETRY_DISABLED_ENV = 'RTK_TELEMETRY_DISABLED'
10
+ const RTK_REWRITE_TIMEOUT_MS = 5_000
11
+
10
12
  function isTruthy(value) {
11
- if (!value)
12
- return false;
13
- const normalized = value.trim().toLowerCase();
14
- return normalized === "1" || normalized === "true" || normalized === "yes";
13
+ if (!value) return false
14
+ const normalized = value.trim().toLowerCase()
15
+ return normalized === '1' || normalized === 'true' || normalized === 'yes'
15
16
  }
17
+
16
18
  function getRewriteTimeoutMs(env = process.env) {
17
- const configured = Number.parseInt(env[GSD_RTK_REWRITE_TIMEOUT_MS_ENV] ?? "", 10);
18
- if (Number.isFinite(configured) && configured > 0) {
19
- return configured;
20
- }
21
- return RTK_REWRITE_TIMEOUT_MS;
19
+ const configured = Number.parseInt(env[GSD_RTK_REWRITE_TIMEOUT_MS_ENV] ?? '', 10)
20
+ if (Number.isFinite(configured) && configured > 0) return configured
21
+ return RTK_REWRITE_TIMEOUT_MS
22
22
  }
23
+
23
24
  export function isRtkEnabled(env = process.env) {
24
- return !isTruthy(env[GSD_RTK_DISABLED_ENV]);
25
+ return !isTruthy(env[GSD_RTK_DISABLED_ENV])
25
26
  }
27
+
26
28
  export function buildRtkEnv(env = process.env) {
27
- return {
28
- ...env,
29
- [RTK_TELEMETRY_DISABLED_ENV]: "1",
30
- };
29
+ return {
30
+ ...env,
31
+ [RTK_TELEMETRY_DISABLED_ENV]: '1',
32
+ }
31
33
  }
34
+
32
35
  function getManagedRtkDir(env = process.env) {
33
- return join(env.GSD_HOME || join(homedir(), ".lsd"), "agent", "bin");
36
+ return join(env.GSD_HOME || join(homedir(), '.lsd'), 'agent', 'bin')
34
37
  }
38
+
35
39
  function getRtkBinaryName(platform = process.platform) {
36
- return platform === "win32" ? "rtk.exe" : "rtk";
40
+ return platform === 'win32' ? 'rtk.exe' : 'rtk'
37
41
  }
42
+
38
43
  function getPathValue(env) {
39
- const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path");
40
- return pathKey ? env[pathKey] : env.PATH;
44
+ const pathKey = Object.keys(env).find((key) => key.toLowerCase() === 'path')
45
+ return pathKey ? env[pathKey] : env.PATH
41
46
  }
47
+
42
48
  function resolvePathCandidates(pathValue) {
43
- if (!pathValue)
44
- return [];
45
- return pathValue
46
- .split(delimiter)
47
- .map((part) => part.trim())
48
- .filter(Boolean);
49
+ if (!pathValue) return []
50
+ return pathValue
51
+ .split(delimiter)
52
+ .map((part) => part.trim())
53
+ .filter(Boolean)
49
54
  }
55
+
50
56
  function resolveSystemRtkPath(pathValue, platform = process.platform) {
51
- const candidates = platform === "win32"
52
- ? ["rtk.exe", "rtk.cmd", "rtk.bat", "rtk"]
53
- : ["rtk"];
54
- for (const dir of resolvePathCandidates(pathValue)) {
55
- for (const candidate of candidates) {
56
- const fullPath = join(dir, candidate);
57
- if (existsSync(fullPath)) {
58
- return fullPath;
59
- }
60
- }
57
+ const candidates = platform === 'win32'
58
+ ? ['rtk.exe', 'rtk.cmd', 'rtk.bat', 'rtk']
59
+ : ['rtk']
60
+
61
+ for (const dir of resolvePathCandidates(pathValue)) {
62
+ for (const candidate of candidates) {
63
+ const fullPath = join(dir, candidate)
64
+ if (existsSync(fullPath)) return fullPath
61
65
  }
62
- return null;
66
+ }
67
+
68
+ return null
63
69
  }
70
+
64
71
  export function resolveRtkBinaryPath(options = {}) {
65
- const env = options.env ?? process.env;
66
- const platform = options.platform ?? process.platform;
67
- const explicitPath = options.binaryPath ?? env[GSD_RTK_PATH_ENV];
68
- if (explicitPath && existsSync(explicitPath)) {
69
- return explicitPath;
70
- }
71
- const managedDir = getManagedRtkDir(env);
72
- const managedPath = join(managedDir, getRtkBinaryName(platform));
73
- if (existsSync(managedPath)) {
74
- return managedPath;
75
- }
76
- // On Windows, also check for rtk.cmd in the managed dir (used by test fake RTK
77
- // and any wrapper-style installs where a .cmd launcher accompanies the binary).
78
- if (platform === "win32") {
79
- const managedCmd = join(managedDir, "rtk.cmd");
80
- if (existsSync(managedCmd)) {
81
- return managedCmd;
82
- }
83
- }
84
- return resolveSystemRtkPath(options.pathValue ?? getPathValue(env), platform);
72
+ const env = options.env ?? process.env
73
+ const platform = options.platform ?? process.platform
74
+
75
+ const explicitPath = options.binaryPath ?? env[GSD_RTK_PATH_ENV]
76
+ if (explicitPath && existsSync(explicitPath)) return explicitPath
77
+
78
+ const managedDir = getManagedRtkDir(env)
79
+ const managedPath = join(managedDir, getRtkBinaryName(platform))
80
+ if (existsSync(managedPath)) return managedPath
81
+ if (platform === 'win32') {
82
+ const managedCmd = join(managedDir, 'rtk.cmd')
83
+ if (existsSync(managedCmd)) return managedCmd
84
+ }
85
+
86
+ return resolveSystemRtkPath(options.pathValue ?? getPathValue(env), platform)
85
87
  }
88
+
86
89
  export function rewriteCommandWithRtk(command, options = {}) {
87
- const env = options.env ?? process.env;
88
- if (!command.trim())
89
- return command;
90
- if (!isRtkEnabled(env))
91
- return command;
92
- const binaryPath = options.binaryPath ?? resolveRtkBinaryPath({ env });
93
- if (!binaryPath)
94
- return command;
95
- const run = options.spawnSyncImpl ?? spawnSync;
96
- const result = run(binaryPath, ["rewrite", command], {
97
- encoding: "utf-8",
98
- env: buildRtkEnv(env),
99
- stdio: ["ignore", "pipe", "ignore"],
100
- timeout: getRewriteTimeoutMs(env),
101
- // .cmd/.bat wrappers (used by fake-rtk in tests) require shell:true on Windows
102
- shell: /\.(cmd|bat)$/i.test(binaryPath),
103
- });
104
- if (result.error)
105
- return command;
106
- if (result.status !== 0 && result.status !== 3)
107
- return command;
108
- const rewritten = (result.stdout ?? "").trimEnd();
109
- return rewritten || command;
90
+ const env = options.env ?? process.env
91
+
92
+ if (!command.trim()) return command
93
+ if (!isRtkEnabled(env)) return command
94
+
95
+ const binaryPath = options.binaryPath ?? resolveRtkBinaryPath({ env })
96
+ if (!binaryPath) return command
97
+
98
+ const run = options.spawnSyncImpl ?? spawnSync
99
+ const result = run(binaryPath, ['rewrite', command], {
100
+ encoding: 'utf-8',
101
+ env: buildRtkEnv(env),
102
+ stdio: ['ignore', 'pipe', 'ignore'],
103
+ timeout: getRewriteTimeoutMs(env),
104
+ shell: /\.(cmd|bat)$/i.test(binaryPath),
105
+ })
106
+
107
+ if (result.error) return command
108
+ if (result.status !== 0 && result.status !== 3) return command
109
+
110
+ const rewritten = (result.stdout ?? '').trimEnd()
111
+ return rewritten || command
110
112
  }
@@ -96,7 +96,7 @@ function formatBackgroundSubagentResults(jobs) {
96
96
  }
97
97
  return parts.join("\n\n---\n\n");
98
98
  }
99
- async function awaitBackgroundSubagents(manager, jobIds, timeoutSeconds = DEFAULT_AWAIT_SUBAGENT_TIMEOUT_SECONDS) {
99
+ async function awaitBackgroundSubagents(manager, jobIds, timeoutSeconds = DEFAULT_AWAIT_SUBAGENT_TIMEOUT_SECONDS, signal) {
100
100
  const timeoutMs = timeoutSeconds * 1000;
101
101
  let watched;
102
102
  if (jobIds && jobIds.length > 0) {
@@ -128,23 +128,40 @@ async function awaitBackgroundSubagents(manager, jobIds, timeoutSeconds = DEFAUL
128
128
  return formatBackgroundSubagentResults(watched);
129
129
  }
130
130
  const TIMEOUT_SENTINEL = Symbol("timeout");
131
+ const ABORT_SENTINEL = Symbol("abort");
131
132
  const timeoutPromise = new Promise((resolve) => {
132
133
  const timer = setTimeout(() => resolve(TIMEOUT_SENTINEL), timeoutMs);
133
134
  if (typeof timer === "object" && "unref" in timer)
134
135
  timer.unref();
135
136
  });
137
+ const abortPromise = signal
138
+ ? new Promise((resolve) => {
139
+ if (signal.aborted) {
140
+ resolve(ABORT_SENTINEL);
141
+ }
142
+ else {
143
+ signal.addEventListener("abort", () => resolve(ABORT_SENTINEL), { once: true });
144
+ }
145
+ })
146
+ : null;
136
147
  const raceResult = await Promise.race([
137
148
  Promise.race(running.map((job) => job.promise)).then(() => "completed"),
138
149
  timeoutPromise,
150
+ ...(abortPromise ? [abortPromise] : []),
139
151
  ]);
140
152
  const timedOut = raceResult === TIMEOUT_SENTINEL;
153
+ const wasAborted = raceResult === ABORT_SENTINEL;
141
154
  const completed = watched.filter((job) => job.status !== "running");
142
155
  const stillRunning = watched.filter((job) => job.status === "running");
143
156
  let result = formatBackgroundSubagentResults(completed);
144
157
  if (stillRunning.length > 0) {
145
158
  result += `\n\n**Still running:** ${stillRunning.map((job) => `${job.id} (${job.agentName})`).join(", ")}`;
146
159
  }
147
- if (timedOut) {
160
+ if (wasAborted) {
161
+ result += `\n\n⎋ **Cancelled** — subagents are still running in the background. ` +
162
+ `Use \`await_subagent\` or \`/subagents wait\` again later.`;
163
+ }
164
+ else if (timedOut) {
148
165
  result += `\n\n⏱ **Timed out** after ${timeoutSeconds}s waiting for subagents to finish. ` +
149
166
  `Subagents are still running in the background. ` +
150
167
  `Use \`await_subagent\` or \`/subagents wait\` again later.`;
@@ -658,10 +675,11 @@ export default function (pi) {
658
675
  const output = job.status === "completed"
659
676
  ? (job.resultSummary ?? "(no output)")
660
677
  : `Error: ${job.stderr ?? "unknown error"}`;
678
+ const modelInfo = job.model ? ` · ${job.model}` : "";
661
679
  pi.sendMessage({
662
680
  customType: "background_subagent_result",
663
681
  content: [
664
- `**Background subagent ${statusEmoji}: ${job.id}** (${job.agentName}, ${elapsed}s)`,
682
+ `**Background subagent ${statusEmoji}: ${job.id}** (${job.agentName}, ${elapsed}s${modelInfo})`,
665
683
  `> ${taskPreview}`,
666
684
  "",
667
685
  output,
@@ -776,7 +794,8 @@ export default function (pi) {
776
794
  for (const job of running) {
777
795
  const elapsed = ((Date.now() - job.startedAt) / 1000).toFixed(0);
778
796
  const preview = job.task.length > 50 ? `${job.task.slice(0, 50)}…` : job.task;
779
- lines.push(`- **${job.id}** [${job.agentName}] ${elapsed}s — ${preview}`);
797
+ const modelSuffix = job.model ? ` · ${job.model}` : "";
798
+ lines.push(`- **${job.id}** [${job.agentName}${modelSuffix}] ${elapsed}s — ${preview}`);
780
799
  }
781
800
  }
782
801
  if (done.length > 0) {
@@ -815,9 +834,9 @@ export default function (pi) {
815
834
  "Use a shorter timeout when polling and a longer timeout when the user explicitly wants to wait here.",
816
835
  ],
817
836
  parameters: AwaitSubagentParams,
818
- async execute(_toolCallId, params) {
837
+ async execute(_toolCallId, params, signal) {
819
838
  const manager = getBgManager();
820
- const output = await awaitBackgroundSubagents(manager, params.jobs, params.timeout ?? DEFAULT_AWAIT_SUBAGENT_TIMEOUT_SECONDS);
839
+ const output = await awaitBackgroundSubagents(manager, params.jobs, params.timeout ?? DEFAULT_AWAIT_SUBAGENT_TIMEOUT_SECONDS, signal);
821
840
  return {
822
841
  content: [{ type: "text", text: output }],
823
842
  details: undefined,
@@ -1047,6 +1066,11 @@ export default function (pi) {
1047
1066
  isError: true,
1048
1067
  };
1049
1068
  }
1069
+ // Pre-resolve model so we can show it in the launch message
1070
+ const bgPreferences = loadEffectivePreferences()?.preferences;
1071
+ const bgSettingsBudgetModel = readBudgetSubagentModelFromSettings();
1072
+ const bgResolvedModelCfg = resolveConfiguredSubagentModel(agentForBg, bgPreferences, bgSettingsBudgetModel);
1073
+ const bgInferredModel = resolveSubagentModel({ name: agentForBg.name, model: bgResolvedModelCfg }, { overrideModel: params.model, parentModel: ctx.model ? { provider: ctx.model.provider, id: ctx.model.id } : undefined });
1050
1074
  let jobId;
1051
1075
  try {
1052
1076
  jobId = runSubagentInBackground(manager, agents, params.agent, params.task, params.cwd, params.model, { defaultCwd: ctx.cwd, model: ctx.model ? { provider: ctx.model.provider, id: ctx.model.id } : undefined }, async (bgSignal) => {
@@ -1067,8 +1091,9 @@ export default function (pi) {
1067
1091
  isError: true,
1068
1092
  };
1069
1093
  }
1094
+ const bgModelLine = bgInferredModel ? `\nModel: ${bgInferredModel}` : "";
1070
1095
  return {
1071
- content: [{ type: "text", text: `Background subagent started. Job ID: **${jobId}**\nAgent: ${params.agent}\nUse \`await_subagent\` to wait, \`/subagents wait ${jobId}\` to block in the TUI, or \`/subagents cancel ${jobId}\` to stop it.` }],
1096
+ content: [{ type: "text", text: `Background subagent started. Job ID: **${jobId}**\nAgent: ${params.agent}${bgModelLine}\nUse \`await_subagent\` to wait, \`/subagents wait ${jobId}\` to block in the TUI, or \`/subagents cancel ${jobId}\` to stop it.` }],
1072
1097
  details: makeDetails("single")([]),
1073
1098
  };
1074
1099
  }
@@ -1180,6 +1205,7 @@ export default function (pi) {
1180
1205
  theme.fg("accent", agentName) +
1181
1206
  theme.fg("muted", ` [${scope}]`);
1182
1207
  text += `\n ${theme.fg("dim", preview)}`;
1208
+ text += `\n ${theme.fg("muted", "(Ctrl+B to background)")}`;
1183
1209
  return new Text(text, 0, 0);
1184
1210
  },
1185
1211
  renderResult(result, { expanded }, theme) {
@@ -31,9 +31,19 @@ export function validateConfiguredModel(modelRegistry, settingsManager) {
31
31
  // Model not configured at all, or removed from registry — pick a fallback.
32
32
  // Only fires when the model is genuinely unknown (not just temporarily unavailable).
33
33
  const piDefault = getPiDefaultModelAndProvider();
34
- const preferred = (piDefault
35
- ? availableModels.find((m) => m.provider === piDefault.provider && m.id === piDefault.model)
34
+ const preferred = (configuredProvider === 'openai'
35
+ ? availableModels.find((m) => m.provider === 'openai' && m.id === 'gpt-5.4') ||
36
+ availableModels.find((m) => m.provider === 'openai' && m.id === 'gpt-5.4-mini') ||
37
+ availableModels.find((m) => m.provider === 'openai')
36
38
  : undefined) ||
39
+ (configuredProvider === 'anthropic'
40
+ ? availableModels.find((m) => m.provider === 'anthropic' && m.id === 'claude-opus-4-6') ||
41
+ availableModels.find((m) => m.provider === 'anthropic' && m.id.includes('opus')) ||
42
+ availableModels.find((m) => m.provider === 'anthropic')
43
+ : undefined) ||
44
+ (piDefault
45
+ ? availableModels.find((m) => m.provider === piDefault.provider && m.id === piDefault.model)
46
+ : undefined) ||
37
47
  availableModels.find((m) => m.provider === 'openai' && m.id === 'gpt-5.4') ||
38
48
  availableModels.find((m) => m.provider === 'openai' && m.id === 'gpt-5.4-mini') ||
39
49
  availableModels.find((m) => m.provider === 'openai') ||
@@ -2,7 +2,7 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync, rmSync } from 'node
2
2
  import { dirname, join } from 'node:path';
3
3
  import chalk from 'chalk';
4
4
  import { appRoot } from './app-paths.js';
5
- import { execSync } from 'node:child_process';
5
+ import { execFileSync } from 'node:child_process';
6
6
  const CACHE_FILE = join(appRoot, '.update-check');
7
7
  const NPM_PACKAGE_NAME = 'lsd-pi';
8
8
  const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
@@ -186,7 +186,7 @@ export async function checkAndPromptForUpdates(options = {}) {
186
186
  if (choice === '1') {
187
187
  process.stderr.write(`\n ${chalk.dim('Running:')} npm install -g ${NPM_PACKAGE_NAME}@latest\n\n`);
188
188
  try {
189
- execSync(`npm install -g ${NPM_PACKAGE_NAME}@latest`, { stdio: 'inherit' });
189
+ execFileSync('npm', ['install', '-g', `${NPM_PACKAGE_NAME}@latest`], { stdio: 'inherit' });
190
190
  process.stderr.write(`\n ${chalk.green.bold(`✓ Updated to v${latestVersion}`)}\n\n`);
191
191
  return true;
192
192
  }
@@ -1,4 +1,4 @@
1
- import { execSync } from 'node:child_process';
1
+ import { execFileSync } from 'node:child_process';
2
2
  import { compareSemver } from './update-check.js';
3
3
  const NPM_PACKAGE = 'lsd-pi';
4
4
  export async function runUpdate() {
@@ -13,7 +13,7 @@ export async function runUpdate() {
13
13
  // Fetch latest version
14
14
  let latest;
15
15
  try {
16
- latest = execSync(`npm view ${NPM_PACKAGE} version`, {
16
+ latest = execFileSync('npm', ['view', NPM_PACKAGE, 'version'], {
17
17
  encoding: 'utf-8',
18
18
  stdio: ['ignore', 'pipe', 'ignore'],
19
19
  }).trim();
@@ -28,7 +28,7 @@ export async function runUpdate() {
28
28
  }
29
29
  process.stdout.write(`${dim}Updating:${reset} v${current} → ${bold}v${latest}${reset}\n`);
30
30
  try {
31
- execSync(`npm install -g ${NPM_PACKAGE}@latest`, {
31
+ execFileSync('npm', ['install', '-g', `${NPM_PACKAGE}@latest`], {
32
32
  stdio: 'inherit',
33
33
  });
34
34
  process.stdout.write(`\n${green}${bold}Updated to v${latest}${reset}\n`);
@@ -8,7 +8,6 @@
8
8
  import os from 'node:os';
9
9
  import chalk from 'chalk';
10
10
  import { GSD_LOGO_SEGMENTS } from './logo.js';
11
- import { brandNameChalk, LSD_BLUE, LSD_PINK, LSD_YELLOW } from './lsd-brand.js';
12
11
  import { accentHex } from './cli-theme.js';
13
12
  function getShortCwd() {
14
13
  const cwd = process.cwd();
@@ -23,6 +22,28 @@ function visLen(s) {
23
22
  function rpad(s, w) {
24
23
  return s + ' '.repeat(Math.max(0, w - visLen(s)));
25
24
  }
25
+ function parseHex(hex) {
26
+ const raw = hex.trim().replace(/^#/, '');
27
+ const full = raw.length === 3 ? raw.split('').map((c) => c + c).join('') : raw;
28
+ if (!/^[0-9a-fA-F]{6}$/.test(full))
29
+ return null;
30
+ return [
31
+ parseInt(full.slice(0, 2), 16),
32
+ parseInt(full.slice(2, 4), 16),
33
+ parseInt(full.slice(4, 6), 16),
34
+ ];
35
+ }
36
+ function mixHex(baseHex, tintHex, tintWeight = 0.5) {
37
+ const base = parseHex(baseHex);
38
+ const tint = parseHex(tintHex);
39
+ if (!base || !tint)
40
+ return baseHex;
41
+ const w = Math.min(1, Math.max(0, tintWeight));
42
+ const mix = (a, b) => Math.round(a * (1 - w) + b * w);
43
+ const [r, g, b] = [mix(base[0], tint[0]), mix(base[1], tint[1]), mix(base[2], tint[2])];
44
+ const toHex = (n) => n.toString(16).padStart(2, '0');
45
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
46
+ }
26
47
  export function printWelcomeScreen(opts) {
27
48
  if (!process.stderr.isTTY)
28
49
  return;
@@ -31,26 +52,34 @@ export function printWelcomeScreen(opts) {
31
52
  const termWidth = Math.min((process.stderr.columns || 80) - 1, 200);
32
53
  // Narrow terminal fallback
33
54
  if (termWidth < 70) {
34
- process.stderr.write(`\n Lucent Software Developer v${version}\n ${shortCwd}\n\n`);
55
+ process.stderr.write(`\n Looks Sort of Done v${version}\n ${shortCwd}\n\n`);
35
56
  return;
36
57
  }
37
- // ── LSD vibrant colors ───────────────────────────────────────────────────
38
- const YELLOW = LSD_YELLOW;
39
- const BLUE = LSD_BLUE;
40
- const PINK = LSD_PINK;
58
+ // ── Theme-adaptive palette ───────────────────────────────────────────────
59
+ // Keep welcome colors anchored to the active CLI theme accent so the banner
60
+ // feels native regardless of custom themes.
41
61
  const ACCENT = accentHex();
62
+ const LOGO_EDGE = chalk.hex(mixHex(ACCENT, '#111111', 0.55));
63
+ const LOGO_CENTER = chalk.hex(mixHex(ACCENT, '#ffffff', 0.3));
64
+ const TITLE_BASE = chalk.bold;
65
+ const TITLE_MARK = chalk.hex(mixHex(ACCENT, '#ffffff', 0.35)).bold;
66
+ const VERSION = chalk.dim;
67
+ const META = chalk.dim;
68
+ const TOOLS = chalk.dim;
42
69
  // ── Panel widths ────────────────────────────────────────────────────────────
43
70
  // Layout: 1 leading space + LEFT_INNER logo content + 1 inner divider + RIGHT_INNER info
44
71
  // Total: 1 + LEFT_INNER + 1 + RIGHT_INNER = termWidth
45
72
  const LEFT_INNER = 34;
46
73
  const RIGHT_INNER = termWidth - LEFT_INNER - 2; // 2 = leading space + inner divider
47
74
  // ── Bar/divider chars (matching GLYPH.separator + widget ui.bar() style) ────
48
- const H = '─', DV = '│', DS = '├';
75
+ const H = '─';
76
+ const DV = '│';
77
+ const DS = '├';
49
78
  // ── Left rows: blank + 6 logo lines + blank (8 total) ───────────────────────
50
79
  const leftRows = [null, ...GSD_LOGO_SEGMENTS, null];
51
80
  // ── Right rows (8 total, null = divider) ────────────────────────────────────
52
- const titleLeft = ` ${brandNameChalk()}`;
53
- const titleRight = chalk.hex(YELLOW)(`v${version}`);
81
+ const titleLeft = ` ${TITLE_MARK('L')}${TITLE_BASE('ooks Sort of ')}${TITLE_MARK('D')}${TITLE_BASE('one')}`;
82
+ const titleRight = VERSION(`v${version}`);
54
83
  const titleFill = RIGHT_INNER - visLen(titleLeft) - visLen(titleRight);
55
84
  const titleRow = titleLeft + ' '.repeat(Math.max(1, titleFill)) + titleRight;
56
85
  const toolParts = [];
@@ -65,15 +94,15 @@ export function printWelcomeScreen(opts) {
65
94
  if (process.env.CONTEXT7_API_KEY)
66
95
  toolParts.push('Context7 ✓');
67
96
  // Tools summary row
68
- const toolsLeft = toolParts.length > 0 ? chalk.hex(PINK)(' ' + toolParts.join(' · ')) : '';
97
+ const toolsLeft = toolParts.length > 0 ? TOOLS(' ' + toolParts.join(' · ')) : '';
69
98
  const footerRow = rpad(toolsLeft, RIGHT_INNER);
70
99
  const DIVIDER = null;
71
100
  const rightRows = [
72
101
  titleRow,
73
102
  DIVIDER,
74
- modelName ? ` Model ${chalk.hex(ACCENT)(modelName)}` : '',
75
- provider ? ` Provider ${chalk.hex(ACCENT)(provider)}` : '',
76
- ` Directory ${chalk.hex(ACCENT)(shortCwd)}`,
103
+ modelName ? ` Model ${META(modelName)}` : '',
104
+ provider ? ` Provider ${META(provider)}` : '',
105
+ ` Directory ${META(shortCwd)}`,
77
106
  DIVIDER,
78
107
  footerRow,
79
108
  '',
@@ -85,7 +114,7 @@ export function printWelcomeScreen(opts) {
85
114
  for (let i = 0; i < 8; i++) {
86
115
  const row = leftRows[i];
87
116
  const lContent = row
88
- ? rpad(chalk.hex(YELLOW)(row[0]) + chalk.hex(BLUE)(row[1]) + chalk.hex(PINK)(row[2]), LEFT_INNER)
117
+ ? rpad(LOGO_EDGE(row[0]) + LOGO_CENTER(row[1]) + LOGO_EDGE(row[2]), LEFT_INNER)
89
118
  : ' '.repeat(LEFT_INNER);
90
119
  const rRow = rightRows[i];
91
120
  if (rRow === null) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lsd-pi",
3
- "version": "1.1.4",
4
- "description": "LSD — let stuff develop coding agent",
3
+ "version": "1.1.6",
4
+ "description": "LSD — Looks Sort of Done coding agent",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -140,6 +140,7 @@
140
140
  "@gsd-build/engine-linux-arm64-gnu": ">=2.10.2",
141
141
  "@gsd-build/engine-linux-x64-gnu": ">=2.10.2",
142
142
  "@gsd-build/engine-win32-x64-msvc": ">=2.10.2",
143
+ "@lydell/node-pty": "^1.2.0-beta.3",
143
144
  "fsevents": "~2.3.3",
144
145
  "koffi": "^2.9.0"
145
146
  },
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=agent-session.clear-queue.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-session.clear-queue.test.d.ts","sourceRoot":"","sources":["../../src/core/agent-session.clear-queue.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,46 @@
1
+ import { describe, it, mock } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { AgentSession } from "./agent-session.js";
4
+ describe("AgentSession.clearQueue", () => {
5
+ it("avoids duplicate queued prompts when session and agent queues mirror the same user message", () => {
6
+ const clearAllQueues = mock.fn(() => { });
7
+ const fakeSession = {
8
+ _steeringMessages: ["make it in the main accent color"],
9
+ _followUpMessages: [],
10
+ agent: {
11
+ drainUserMessages: () => ({
12
+ steering: [{ role: "user", content: [{ type: "text", text: "make it in the main accent color" }] }],
13
+ followUp: [],
14
+ }),
15
+ clearAllQueues,
16
+ },
17
+ };
18
+ const result = AgentSession.prototype.clearQueue.call(fakeSession);
19
+ assert.deepEqual(result, {
20
+ steering: ["make it in the main accent color"],
21
+ followUp: [],
22
+ });
23
+ assert.deepEqual(fakeSession._steeringMessages, []);
24
+ assert.deepEqual(fakeSession._followUpMessages, []);
25
+ assert.equal(clearAllQueues.mock.callCount(), 1);
26
+ });
27
+ it("keeps extra preserved messages that are not present in session-tracked arrays", () => {
28
+ const fakeSession = {
29
+ _steeringMessages: ["first"],
30
+ _followUpMessages: [],
31
+ agent: {
32
+ drainUserMessages: () => ({
33
+ steering: [
34
+ { role: "user", content: [{ type: "text", text: "first" }] },
35
+ { role: "user", content: [{ type: "text", text: "second" }] },
36
+ ],
37
+ followUp: [],
38
+ }),
39
+ clearAllQueues: () => { },
40
+ },
41
+ };
42
+ const result = AgentSession.prototype.clearQueue.call(fakeSession);
43
+ assert.deepEqual(result.steering, ["first", "second"]);
44
+ });
45
+ });
46
+ //# sourceMappingURL=agent-session.clear-queue.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-session.clear-queue.test.js","sourceRoot":"","sources":["../../src/core/agent-session.clear-queue.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,4FAA4F,EAAE,GAAG,EAAE;QACrG,MAAM,cAAc,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACzC,MAAM,WAAW,GAAG;YACnB,iBAAiB,EAAE,CAAC,kCAAkC,CAAC;YACvD,iBAAiB,EAAE,EAAE;YACrB,KAAK,EAAE;gBACN,iBAAiB,EAAE,GAAG,EAAE,CAAC,CAAC;oBACzB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,kCAAkC,EAAE,CAAC,EAAE,CAAC;oBACnG,QAAQ,EAAE,EAAE;iBACZ,CAAC;gBACF,cAAc;aACd;SACM,CAAC;QAET,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAEnE,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE;YACxB,QAAQ,EAAE,CAAC,kCAAkC,CAAC;YAC9C,QAAQ,EAAE,EAAE;SACZ,CAAC,CAAC;QACH,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;QACpD,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;QACpD,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+EAA+E,EAAE,GAAG,EAAE;QACxF,MAAM,WAAW,GAAG;YACnB,iBAAiB,EAAE,CAAC,OAAO,CAAC;YAC5B,iBAAiB,EAAE,EAAE;YACrB,KAAK,EAAE;gBACN,iBAAiB,EAAE,GAAG,EAAE,CAAC,CAAC;oBACzB,QAAQ,EAAE;wBACT,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE;wBAC5D,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE;qBAC7D;oBACD,QAAQ,EAAE,EAAE;iBACZ,CAAC;gBACF,cAAc,EAAE,GAAG,EAAE,GAAE,CAAC;aACxB;SACM,CAAC;QAET,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACnE,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["import { describe, it, mock } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport { AgentSession } from \"./agent-session.js\";\n\ndescribe(\"AgentSession.clearQueue\", () => {\n\tit(\"avoids duplicate queued prompts when session and agent queues mirror the same user message\", () => {\n\t\tconst clearAllQueues = mock.fn(() => {});\n\t\tconst fakeSession = {\n\t\t\t_steeringMessages: [\"make it in the main accent color\"],\n\t\t\t_followUpMessages: [],\n\t\t\tagent: {\n\t\t\t\tdrainUserMessages: () => ({\n\t\t\t\t\tsteering: [{ role: \"user\", content: [{ type: \"text\", text: \"make it in the main accent color\" }] }],\n\t\t\t\t\tfollowUp: [],\n\t\t\t\t}),\n\t\t\t\tclearAllQueues,\n\t\t\t},\n\t\t} as any;\n\n\t\tconst result = AgentSession.prototype.clearQueue.call(fakeSession);\n\n\t\tassert.deepEqual(result, {\n\t\t\tsteering: [\"make it in the main accent color\"],\n\t\t\tfollowUp: [],\n\t\t});\n\t\tassert.deepEqual(fakeSession._steeringMessages, []);\n\t\tassert.deepEqual(fakeSession._followUpMessages, []);\n\t\tassert.equal(clearAllQueues.mock.callCount(), 1);\n\t});\n\n\tit(\"keeps extra preserved messages that are not present in session-tracked arrays\", () => {\n\t\tconst fakeSession = {\n\t\t\t_steeringMessages: [\"first\"],\n\t\t\t_followUpMessages: [],\n\t\t\tagent: {\n\t\t\t\tdrainUserMessages: () => ({\n\t\t\t\t\tsteering: [\n\t\t\t\t\t\t{ role: \"user\", content: [{ type: \"text\", text: \"first\" }] },\n\t\t\t\t\t\t{ role: \"user\", content: [{ type: \"text\", text: \"second\" }] },\n\t\t\t\t\t],\n\t\t\t\t\tfollowUp: [],\n\t\t\t\t}),\n\t\t\t\tclearAllQueues: () => {},\n\t\t\t},\n\t\t} as any;\n\n\t\tconst result = AgentSession.prototype.clearQueue.call(fakeSession);\n\t\tassert.deepEqual(result.steering, [\"first\", \"second\"]);\n\t});\n});\n"]}
@@ -15,6 +15,7 @@
15
15
  import type { Agent, AgentEvent, AgentMessage, AgentState, AgentTool, ThinkingLevel } from "@gsd/pi-agent-core";
16
16
  import type { ImageContent, Model, TextContent } from "@gsd/pi-ai";
17
17
  import { type BashResult } from "./bash-executor.js";
18
+ import { type PtyExecutionSession } from "./pty-executor.js";
18
19
  import { type CompactionResult } from "./compaction/index.js";
19
20
  import { type ContextUsage, type ExtensionCommandContextActions, type ExtensionErrorListener, ExtensionRunner, type ExtensionUIContext, type InputSource, type ShutdownHandler, type ToolDefinition, type ToolInfo } from "./extensions/index.js";
20
21
  import type { CustomMessage } from "./messages.js";
@@ -548,6 +549,13 @@ export declare class AgentSession {
548
549
  operations?: BashOperations;
549
550
  loginShell?: boolean;
550
551
  }): Promise<BashResult>;
552
+ executeBashInteractive(command: string, options?: {
553
+ onChunk?: (chunk: string) => void;
554
+ cols?: number;
555
+ rows?: number;
556
+ loginShell?: boolean;
557
+ }): Promise<PtyExecutionSession>;
558
+ clearBashAbortController(): void;
551
559
  /**
552
560
  * Record a bash execution result in session history.
553
561
  * Used by executeBash and by extensions that handle bash execution themselves.