iosm-cli 0.2.14 → 0.2.16

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 (161) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +18 -1
  3. package/dist/cli/args.d.ts +2 -1
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +16 -3
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/core/agent-profiles.d.ts.map +1 -1
  8. package/dist/core/agent-profiles.js +3 -0
  9. package/dist/core/agent-profiles.js.map +1 -1
  10. package/dist/core/agent-session.d.ts +2 -0
  11. package/dist/core/agent-session.d.ts.map +1 -1
  12. package/dist/core/agent-session.js +26 -10
  13. package/dist/core/agent-session.js.map +1 -1
  14. package/dist/core/agent-teams.d.ts.map +1 -1
  15. package/dist/core/agent-teams.js +12 -9
  16. package/dist/core/agent-teams.js.map +1 -1
  17. package/dist/core/checkpoint/fs-checkpoint.d.ts +14 -0
  18. package/dist/core/checkpoint/fs-checkpoint.d.ts.map +1 -0
  19. package/dist/core/checkpoint/fs-checkpoint.js +211 -0
  20. package/dist/core/checkpoint/fs-checkpoint.js.map +1 -0
  21. package/dist/core/command-dispatcher.d.ts +2 -0
  22. package/dist/core/command-dispatcher.d.ts.map +1 -1
  23. package/dist/core/command-dispatcher.js +78 -13
  24. package/dist/core/command-dispatcher.js.map +1 -1
  25. package/dist/core/mcp/cli.d.ts.map +1 -1
  26. package/dist/core/mcp/cli.js +26 -0
  27. package/dist/core/mcp/cli.js.map +1 -1
  28. package/dist/core/mcp/config.d.ts.map +1 -1
  29. package/dist/core/mcp/config.js +55 -0
  30. package/dist/core/mcp/config.js.map +1 -1
  31. package/dist/core/mcp/index.d.ts +1 -1
  32. package/dist/core/mcp/index.d.ts.map +1 -1
  33. package/dist/core/mcp/index.js.map +1 -1
  34. package/dist/core/mcp/runtime.d.ts +3 -1
  35. package/dist/core/mcp/runtime.d.ts.map +1 -1
  36. package/dist/core/mcp/runtime.js +21 -2
  37. package/dist/core/mcp/runtime.js.map +1 -1
  38. package/dist/core/mcp/types.d.ts +30 -2
  39. package/dist/core/mcp/types.d.ts.map +1 -1
  40. package/dist/core/mcp/types.js.map +1 -1
  41. package/dist/core/package-manager.d.ts +10 -0
  42. package/dist/core/package-manager.d.ts.map +1 -1
  43. package/dist/core/package-manager.js +100 -2
  44. package/dist/core/package-manager.js.map +1 -1
  45. package/dist/core/policy/engine.d.ts +77 -0
  46. package/dist/core/policy/engine.d.ts.map +1 -0
  47. package/dist/core/policy/engine.js +614 -0
  48. package/dist/core/policy/engine.js.map +1 -0
  49. package/dist/core/policy/index.d.ts +2 -0
  50. package/dist/core/policy/index.d.ts.map +1 -0
  51. package/dist/core/policy/index.js +2 -0
  52. package/dist/core/policy/index.js.map +1 -0
  53. package/dist/core/sandbox/executor.d.ts +13 -0
  54. package/dist/core/sandbox/executor.d.ts.map +1 -0
  55. package/dist/core/sandbox/executor.js +64 -0
  56. package/dist/core/sandbox/executor.js.map +1 -0
  57. package/dist/core/sdk.d.ts +2 -2
  58. package/dist/core/sdk.d.ts.map +1 -1
  59. package/dist/core/sdk.js +3 -3
  60. package/dist/core/sdk.js.map +1 -1
  61. package/dist/core/security/index.d.ts +3 -0
  62. package/dist/core/security/index.d.ts.map +1 -0
  63. package/dist/core/security/index.js +3 -0
  64. package/dist/core/security/index.js.map +1 -0
  65. package/dist/core/security/source-security.d.ts +43 -0
  66. package/dist/core/security/source-security.d.ts.map +1 -0
  67. package/dist/core/security/source-security.js +94 -0
  68. package/dist/core/security/source-security.js.map +1 -0
  69. package/dist/core/security/trust-ledger.d.ts +24 -0
  70. package/dist/core/security/trust-ledger.d.ts.map +1 -0
  71. package/dist/core/security/trust-ledger.js +66 -0
  72. package/dist/core/security/trust-ledger.js.map +1 -0
  73. package/dist/core/session-manager.d.ts.map +1 -1
  74. package/dist/core/session-manager.js +128 -15
  75. package/dist/core/session-manager.js.map +1 -1
  76. package/dist/core/settings-manager.d.ts +6 -0
  77. package/dist/core/settings-manager.d.ts.map +1 -1
  78. package/dist/core/settings-manager.js +22 -1
  79. package/dist/core/settings-manager.js.map +1 -1
  80. package/dist/core/system-prompt.d.ts.map +1 -1
  81. package/dist/core/system-prompt.js +9 -2
  82. package/dist/core/system-prompt.js.map +1 -1
  83. package/dist/core/task-plan.d.ts +1 -0
  84. package/dist/core/task-plan.d.ts.map +1 -1
  85. package/dist/core/task-plan.js +103 -0
  86. package/dist/core/task-plan.js.map +1 -1
  87. package/dist/core/tools/apply-patch.d.ts +29 -0
  88. package/dist/core/tools/apply-patch.d.ts.map +1 -0
  89. package/dist/core/tools/apply-patch.js +167 -0
  90. package/dist/core/tools/apply-patch.js.map +1 -0
  91. package/dist/core/tools/bash.d.ts.map +1 -1
  92. package/dist/core/tools/bash.js +15 -1
  93. package/dist/core/tools/bash.js.map +1 -1
  94. package/dist/core/tools/git-common.d.ts.map +1 -1
  95. package/dist/core/tools/git-common.js +15 -1
  96. package/dist/core/tools/git-common.js.map +1 -1
  97. package/dist/core/tools/index.d.ts +20 -2
  98. package/dist/core/tools/index.d.ts.map +1 -1
  99. package/dist/core/tools/index.js +85 -25
  100. package/dist/core/tools/index.js.map +1 -1
  101. package/dist/core/tools/permissions.d.ts +16 -0
  102. package/dist/core/tools/permissions.d.ts.map +1 -1
  103. package/dist/core/tools/permissions.js +34 -1
  104. package/dist/core/tools/permissions.js.map +1 -1
  105. package/dist/core/tools/task.d.ts.map +1 -1
  106. package/dist/core/tools/task.js +68 -24
  107. package/dist/core/tools/task.js.map +1 -1
  108. package/dist/core/tools/tool-search.d.ts +24 -0
  109. package/dist/core/tools/tool-search.d.ts.map +1 -0
  110. package/dist/core/tools/tool-search.js +85 -0
  111. package/dist/core/tools/tool-search.js.map +1 -0
  112. package/dist/core/tools/tool-suggest.d.ts +18 -0
  113. package/dist/core/tools/tool-suggest.d.ts.map +1 -0
  114. package/dist/core/tools/tool-suggest.js +94 -0
  115. package/dist/core/tools/tool-suggest.js.map +1 -0
  116. package/dist/core/tools/verification-runner.d.ts.map +1 -1
  117. package/dist/core/tools/verification-runner.js +15 -1
  118. package/dist/core/tools/verification-runner.js.map +1 -1
  119. package/dist/core/unified-exec.d.ts +39 -0
  120. package/dist/core/unified-exec.d.ts.map +1 -0
  121. package/dist/core/unified-exec.js +286 -0
  122. package/dist/core/unified-exec.js.map +1 -0
  123. package/dist/main.d.ts.map +1 -1
  124. package/dist/main.js +93 -11
  125. package/dist/main.js.map +1 -1
  126. package/dist/modes/acp/acp-mode.d.ts +17 -0
  127. package/dist/modes/acp/acp-mode.d.ts.map +1 -0
  128. package/dist/modes/acp/acp-mode.js +352 -0
  129. package/dist/modes/acp/acp-mode.js.map +1 -0
  130. package/dist/modes/index.d.ts +2 -1
  131. package/dist/modes/index.d.ts.map +1 -1
  132. package/dist/modes/index.js +1 -0
  133. package/dist/modes/index.js.map +1 -1
  134. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  135. package/dist/modes/interactive/components/tool-execution.js +217 -0
  136. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  137. package/dist/modes/interactive/interactive-mode.d.ts +8 -0
  138. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  139. package/dist/modes/interactive/interactive-mode.js +159 -72
  140. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  141. package/dist/modes/rpc/rpc-client.d.ts +25 -1
  142. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  143. package/dist/modes/rpc/rpc-client.js +33 -0
  144. package/dist/modes/rpc/rpc-client.js.map +1 -1
  145. package/dist/modes/rpc/rpc-mode.d.ts +13 -1
  146. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  147. package/dist/modes/rpc/rpc-mode.js +189 -28
  148. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  149. package/dist/modes/rpc/rpc-types.d.ts +54 -1
  150. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  151. package/dist/modes/rpc/rpc-types.js.map +1 -1
  152. package/dist/modes/telegram/telegram-bridge-mode.js +51 -22
  153. package/dist/modes/telegram/telegram-bridge-mode.js.map +1 -1
  154. package/dist/utils/tools-manager.d.ts.map +1 -1
  155. package/dist/utils/tools-manager.js +96 -9
  156. package/dist/utils/tools-manager.js.map +1 -1
  157. package/docs/README.md +2 -0
  158. package/docs/acp-rpc-mapping.md +38 -0
  159. package/docs/configuration.generated.md +81 -0
  160. package/docs/configuration.md +7 -0
  161. package/package.json +4 -1
@@ -0,0 +1,286 @@
1
+ import { spawn } from "node:child_process";
2
+ import { resolve } from "node:path";
3
+ import stripAnsi from "strip-ansi";
4
+ import { getShellConfig, getShellEnv, killProcessTree, sanitizeBinaryOutput } from "../utils/shell.js";
5
+ const DEFAULT_YIELD_TIME_MS = 1000;
6
+ const MAX_YIELD_TIME_MS = 60_000;
7
+ const DEFAULT_MAX_OUTPUT_CHARS = 12_000;
8
+ const MAX_OUTPUT_CHARS = 250_000;
9
+ const MAX_PENDING_OUTPUT_CHARS = 1_000_000;
10
+ const FINISHED_SESSION_TTL_MS = 5 * 60_000;
11
+ const EXIT_GRACE_TIME_MS = 75;
12
+ const DEFAULT_PTY_BRIDGE_BIN = "python3";
13
+ const PYTHON_PTY_BRIDGE_SCRIPT = String.raw `import os
14
+ import pty
15
+ import select
16
+ import subprocess
17
+ import sys
18
+
19
+ if len(sys.argv) < 2:
20
+ print("pty bridge requires shell command arguments", file=sys.stderr)
21
+ sys.exit(2)
22
+
23
+ command = sys.argv[1:]
24
+ master_fd, slave_fd = pty.openpty()
25
+ child = subprocess.Popen(command, stdin=slave_fd, stdout=slave_fd, stderr=slave_fd)
26
+ os.close(slave_fd)
27
+
28
+ stdin_fd = sys.stdin.fileno()
29
+ stdout_fd = sys.stdout.fileno()
30
+ stdin_open = True
31
+
32
+ while True:
33
+ readers = [master_fd]
34
+ if stdin_open:
35
+ readers.append(stdin_fd)
36
+ readable, _, _ = select.select(readers, [], [], 0.05)
37
+
38
+ if master_fd in readable:
39
+ try:
40
+ chunk = os.read(master_fd, 4096)
41
+ except OSError:
42
+ chunk = b""
43
+ if chunk:
44
+ os.write(stdout_fd, chunk)
45
+
46
+ if stdin_open and stdin_fd in readable:
47
+ input_chunk = os.read(stdin_fd, 4096)
48
+ if input_chunk:
49
+ os.write(master_fd, input_chunk)
50
+ else:
51
+ stdin_open = False
52
+
53
+ if child.poll() is not None:
54
+ while True:
55
+ try:
56
+ chunk = os.read(master_fd, 4096)
57
+ except OSError:
58
+ chunk = b""
59
+ if not chunk:
60
+ break
61
+ os.write(stdout_fd, chunk)
62
+ break
63
+
64
+ sys.exit(child.returncode if child.returncode is not None else 0)`;
65
+ function normalizeYieldTimeMs(value) {
66
+ if (typeof value !== "number" || Number.isNaN(value))
67
+ return DEFAULT_YIELD_TIME_MS;
68
+ return Math.max(0, Math.min(MAX_YIELD_TIME_MS, Math.floor(value)));
69
+ }
70
+ function normalizeMaxOutputChars(value) {
71
+ if (typeof value !== "number" || Number.isNaN(value))
72
+ return DEFAULT_MAX_OUTPUT_CHARS;
73
+ return Math.max(1, Math.min(MAX_OUTPUT_CHARS, Math.floor(value)));
74
+ }
75
+ export class UnifiedExecManager {
76
+ constructor() {
77
+ this.sessions = new Map();
78
+ this.nextSessionId = 1;
79
+ }
80
+ async execCommand(input) {
81
+ const command = input.command?.trim();
82
+ if (!command) {
83
+ throw new Error("exec_command requires a non-empty command.");
84
+ }
85
+ const session = this.createSession(command, input);
86
+ await this.waitForUpdate(session, normalizeYieldTimeMs(input.yieldTimeMs));
87
+ await this.waitForExitGrace(session);
88
+ return this.buildPollResult(session, normalizeMaxOutputChars(input.maxOutputChars));
89
+ }
90
+ async writeStdin(input) {
91
+ const session = this.sessions.get(input.sessionId);
92
+ if (!session) {
93
+ throw new Error(`exec session not found: ${input.sessionId}`);
94
+ }
95
+ if (input.chars && session.running) {
96
+ try {
97
+ session.child.stdin?.write(input.chars);
98
+ }
99
+ catch (error) {
100
+ throw new Error(`Failed to write to exec session ${input.sessionId}: ${error instanceof Error ? error.message : String(error)}`);
101
+ }
102
+ }
103
+ await this.waitForUpdate(session, normalizeYieldTimeMs(input.yieldTimeMs));
104
+ await this.waitForExitGrace(session);
105
+ return this.buildPollResult(session, normalizeMaxOutputChars(input.maxOutputChars));
106
+ }
107
+ dispose() {
108
+ for (const session of this.sessions.values()) {
109
+ if (session.cleanupTimer) {
110
+ clearTimeout(session.cleanupTimer);
111
+ session.cleanupTimer = undefined;
112
+ }
113
+ if (session.running && session.child.pid) {
114
+ killProcessTree(session.child.pid);
115
+ }
116
+ session.waiters.clear();
117
+ }
118
+ this.sessions.clear();
119
+ }
120
+ createSession(command, input) {
121
+ const invocation = this.resolveInvocation(command, input);
122
+ const child = spawn(invocation.command, invocation.args, {
123
+ cwd: input.cwd ? resolve(input.cwd) : undefined,
124
+ env: getShellEnv(),
125
+ detached: true,
126
+ stdio: ["pipe", "pipe", "pipe"],
127
+ });
128
+ const session = {
129
+ id: this.nextSessionId++,
130
+ child,
131
+ usesPty: invocation.usesPty,
132
+ pendingOutput: [],
133
+ pendingChars: 0,
134
+ running: true,
135
+ exitCode: null,
136
+ waiters: new Set(),
137
+ };
138
+ this.sessions.set(session.id, session);
139
+ const stdoutDecoder = new TextDecoder();
140
+ const stderrDecoder = new TextDecoder();
141
+ const pushOutput = (raw, decoder) => {
142
+ let text = sanitizeBinaryOutput(stripAnsi(decoder.decode(raw, { stream: true }))).replace(/\r/g, "");
143
+ if (session.usesPty) {
144
+ text = text.replace(/\^D\x08\x08/g, "");
145
+ }
146
+ if (!text)
147
+ return;
148
+ this.appendOutput(session, text);
149
+ };
150
+ child.stdout?.on("data", (raw) => pushOutput(raw, stdoutDecoder));
151
+ child.stderr?.on("data", (raw) => pushOutput(raw, stderrDecoder));
152
+ child.on("error", (error) => {
153
+ this.appendOutput(session, `\n[exec error] ${error.message}\n`);
154
+ });
155
+ child.on("close", (code) => {
156
+ session.running = false;
157
+ session.exitCode = code ?? null;
158
+ this.notify(session);
159
+ session.cleanupTimer = setTimeout(() => {
160
+ this.sessions.delete(session.id);
161
+ }, FINISHED_SESSION_TTL_MS);
162
+ session.cleanupTimer.unref();
163
+ });
164
+ return session;
165
+ }
166
+ appendOutput(session, chunk) {
167
+ session.pendingOutput.push(chunk);
168
+ session.pendingChars += chunk.length;
169
+ while (session.pendingChars > MAX_PENDING_OUTPUT_CHARS && session.pendingOutput.length > 1) {
170
+ const removed = session.pendingOutput.shift();
171
+ if (!removed)
172
+ break;
173
+ session.pendingChars -= removed.length;
174
+ }
175
+ this.notify(session);
176
+ }
177
+ notify(session) {
178
+ if (session.waiters.size === 0)
179
+ return;
180
+ const waiters = Array.from(session.waiters);
181
+ session.waiters.clear();
182
+ for (const resolveWaiter of waiters) {
183
+ resolveWaiter();
184
+ }
185
+ }
186
+ async waitForUpdate(session, timeoutMs) {
187
+ if (!session.running || session.pendingChars > 0 || timeoutMs <= 0) {
188
+ return;
189
+ }
190
+ await new Promise((resolveWait) => {
191
+ const onUpdate = () => {
192
+ if (timer)
193
+ clearTimeout(timer);
194
+ resolveWait();
195
+ };
196
+ const timer = setTimeout(() => {
197
+ session.waiters.delete(onUpdate);
198
+ resolveWait();
199
+ }, timeoutMs);
200
+ session.waiters.add(onUpdate);
201
+ });
202
+ }
203
+ async waitForExitGrace(session) {
204
+ if (!session.running)
205
+ return;
206
+ await this.waitForClose(session, EXIT_GRACE_TIME_MS);
207
+ }
208
+ async waitForClose(session, timeoutMs) {
209
+ if (!session.running || timeoutMs <= 0)
210
+ return;
211
+ await new Promise((resolveWait) => {
212
+ const onUpdate = () => {
213
+ if (!session.running) {
214
+ if (timer)
215
+ clearTimeout(timer);
216
+ session.waiters.delete(onUpdate);
217
+ resolveWait();
218
+ }
219
+ };
220
+ const timer = setTimeout(() => {
221
+ session.waiters.delete(onUpdate);
222
+ resolveWait();
223
+ }, timeoutMs);
224
+ session.waiters.add(onUpdate);
225
+ });
226
+ }
227
+ drainOutput(session, maxOutputChars) {
228
+ const combined = session.pendingOutput.join("");
229
+ session.pendingOutput = [];
230
+ session.pendingChars = 0;
231
+ if (combined.length <= maxOutputChars)
232
+ return combined;
233
+ return combined.slice(combined.length - maxOutputChars);
234
+ }
235
+ buildPollResult(session, maxOutputChars) {
236
+ const output = this.drainOutput(session, maxOutputChars);
237
+ if (session.running) {
238
+ return {
239
+ output,
240
+ running: true,
241
+ sessionId: session.id,
242
+ };
243
+ }
244
+ if (session.cleanupTimer) {
245
+ clearTimeout(session.cleanupTimer);
246
+ session.cleanupTimer = undefined;
247
+ }
248
+ this.sessions.delete(session.id);
249
+ return {
250
+ output,
251
+ running: false,
252
+ exitCode: session.exitCode,
253
+ };
254
+ }
255
+ resolveInvocation(command, input) {
256
+ const shellConfig = getShellConfig();
257
+ const shell = typeof input.shell === "string" && input.shell.trim().length > 0 ? input.shell.trim() : shellConfig.shell;
258
+ const shellArgs = this.buildShellArgs(command, input.login, shellConfig.args);
259
+ if (!input.tty) {
260
+ return {
261
+ command: shell,
262
+ args: shellArgs,
263
+ usesPty: false,
264
+ };
265
+ }
266
+ if (process.platform === "win32") {
267
+ throw new Error("exec_command tty=true is not supported on Windows.");
268
+ }
269
+ const ptyBridgeBin = process.env.IOSM_EXEC_PTY_BRIDGE_BIN?.trim() || DEFAULT_PTY_BRIDGE_BIN;
270
+ return {
271
+ command: ptyBridgeBin,
272
+ args: ["-u", "-c", PYTHON_PTY_BRIDGE_SCRIPT, shell, ...shellArgs],
273
+ usesPty: true,
274
+ };
275
+ }
276
+ buildShellArgs(command, login, defaultArgs) {
277
+ if (login === true) {
278
+ return ["-l", "-c", command];
279
+ }
280
+ if (login === false) {
281
+ return ["-c", command];
282
+ }
283
+ return [...defaultArgs, command];
284
+ }
285
+ }
286
+ //# sourceMappingURL=unified-exec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unified-exec.js","sourceRoot":"","sources":["../../src/core/unified-exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEvG,MAAM,qBAAqB,GAAG,IAAI,CAAC;AACnC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AACjC,MAAM,wBAAwB,GAAG,MAAM,CAAC;AACxC,MAAM,gBAAgB,GAAG,OAAO,CAAC;AACjC,MAAM,wBAAwB,GAAG,SAAS,CAAC;AAC3C,MAAM,uBAAuB,GAAG,CAAC,GAAG,MAAM,CAAC;AAC3C,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAC9B,MAAM,sBAAsB,GAAG,SAAS,CAAC;AAEzC,MAAM,wBAAwB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kEAmDuB,CAAC;AAsCnE,SAAS,oBAAoB,CAAC,KAAyB;IACtD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,qBAAqB,CAAC;IACnF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAyB;IACzD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,wBAAwB,CAAC;IACtF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,OAAO,kBAAkB;IAA/B;QACS,aAAQ,GAAG,IAAI,GAAG,EAA8B,CAAC;QACjD,kBAAa,GAAG,CAAC,CAAC;IA+N3B,CAAC;IA7NA,KAAK,CAAC,WAAW,CAAC,KAA0B;QAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;QACtC,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACnD,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,oBAAoB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;QAC3E,MAAM,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACrC,OAAO,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,uBAAuB,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;IACrF,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,KAA4B;QAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACnD,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,2BAA2B,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;QAC/D,CAAC;QAED,IAAI,KAAK,CAAC,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpC,IAAI,CAAC;gBACJ,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACzC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,MAAM,IAAI,KAAK,CAAC,mCAAmC,KAAK,CAAC,SAAS,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAClI,CAAC;QACF,CAAC;QAED,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,oBAAoB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;QAC3E,MAAM,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACrC,OAAO,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,uBAAuB,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;IACrF,CAAC;IAED,OAAO;QACN,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC9C,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;gBAC1B,YAAY,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;gBACnC,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;YAClC,CAAC;YACD,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;gBAC1C,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACpC,CAAC;YACD,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACzB,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAEO,aAAa,CAAC,OAAe,EAAE,KAA0B;QAChE,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC1D,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,EAAE;YACxD,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS;YAC/C,GAAG,EAAE,WAAW,EAAE;YAClB,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAC/B,CAAC,CAAC;QAEH,MAAM,OAAO,GAAuB;YACnC,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE;YACxB,KAAK;YACL,OAAO,EAAE,UAAU,CAAC,OAAO;YAC3B,aAAa,EAAE,EAAE;YACjB,YAAY,EAAE,CAAC;YACf,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI;YACd,OAAO,EAAE,IAAI,GAAG,EAAE;SAClB,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAEvC,MAAM,aAAa,GAAG,IAAI,WAAW,EAAE,CAAC;QACxC,MAAM,aAAa,GAAG,IAAI,WAAW,EAAE,CAAC;QACxC,MAAM,UAAU,GAAG,CAAC,GAAW,EAAE,OAAyC,EAAQ,EAAE;YACnF,IAAI,IAAI,GAAG,oBAAoB,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACrG,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;gBACrB,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;YACzC,CAAC;YACD,IAAI,CAAC,IAAI;gBAAE,OAAO;YAClB,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAClC,CAAC,CAAC;QAEF,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC;QAC1E,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC;QAC1E,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAC3B,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,kBAAkB,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;QACjE,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YAC1B,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC;YACxB,OAAO,CAAC,QAAQ,GAAG,IAAI,IAAI,IAAI,CAAC;YAChC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACrB,OAAO,CAAC,YAAY,GAAG,UAAU,CAAC,GAAG,EAAE;gBACtC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAClC,CAAC,EAAE,uBAAuB,CAAC,CAAC;YAC5B,OAAO,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QAC9B,CAAC,CAAC,CAAC;QAEH,OAAO,OAAO,CAAC;IAChB,CAAC;IAEO,YAAY,CAAC,OAA2B,EAAE,KAAa;QAC9D,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClC,OAAO,CAAC,YAAY,IAAI,KAAK,CAAC,MAAM,CAAC;QACrC,OAAO,OAAO,CAAC,YAAY,GAAG,wBAAwB,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5F,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;YAC9C,IAAI,CAAC,OAAO;gBAAE,MAAM;YACpB,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;QACxC,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACtB,CAAC;IAEO,MAAM,CAAC,OAA2B;QACzC,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO;QACvC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5C,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,KAAK,MAAM,aAAa,IAAI,OAAO,EAAE,CAAC;YACrC,aAAa,EAAE,CAAC;QACjB,CAAC;IACF,CAAC;IAEO,KAAK,CAAC,aAAa,CAAC,OAA2B,EAAE,SAAiB;QACzE,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,YAAY,GAAG,CAAC,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;YACpE,OAAO;QACR,CAAC;QACD,MAAM,IAAI,OAAO,CAAO,CAAC,WAAW,EAAE,EAAE;YACvC,MAAM,QAAQ,GAAG,GAAG,EAAE;gBACrB,IAAI,KAAK;oBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;gBAC/B,WAAW,EAAE,CAAC;YACf,CAAC,CAAC;YACF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC7B,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACjC,WAAW,EAAE,CAAC;YACf,CAAC,EAAE,SAAS,CAAC,CAAC;YACd,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAAC,OAA2B;QACzD,IAAI,CAAC,OAAO,CAAC,OAAO;YAAE,OAAO;QAC7B,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;IACtD,CAAC;IAEO,KAAK,CAAC,YAAY,CAAC,OAA2B,EAAE,SAAiB;QACxE,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,SAAS,IAAI,CAAC;YAAE,OAAO;QAC/C,MAAM,IAAI,OAAO,CAAO,CAAC,WAAW,EAAE,EAAE;YACvC,MAAM,QAAQ,GAAG,GAAG,EAAE;gBACrB,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;oBACtB,IAAI,KAAK;wBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;oBAC/B,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;oBACjC,WAAW,EAAE,CAAC;gBACf,CAAC;YACF,CAAC,CAAC;YACF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC7B,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACjC,WAAW,EAAE,CAAC;YACf,CAAC,EAAE,SAAS,CAAC,CAAC;YACd,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACJ,CAAC;IAEO,WAAW,CAAC,OAA2B,EAAE,cAAsB;QACtE,MAAM,QAAQ,GAAG,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChD,OAAO,CAAC,aAAa,GAAG,EAAE,CAAC;QAC3B,OAAO,CAAC,YAAY,GAAG,CAAC,CAAC;QACzB,IAAI,QAAQ,CAAC,MAAM,IAAI,cAAc;YAAE,OAAO,QAAQ,CAAC;QACvD,OAAO,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,cAAc,CAAC,CAAC;IACzD,CAAC;IAEO,eAAe,CAAC,OAA2B,EAAE,cAAsB;QAC1E,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;QACzD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACrB,OAAO;gBACN,MAAM;gBACN,OAAO,EAAE,IAAI;gBACb,SAAS,EAAE,OAAO,CAAC,EAAE;aACrB,CAAC;QACH,CAAC;QACD,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YAC1B,YAAY,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YACnC,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;QAClC,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACjC,OAAO;YACN,MAAM;YACN,OAAO,EAAE,KAAK;YACd,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC1B,CAAC;IACH,CAAC;IAEO,iBAAiB,CACxB,OAAe,EACf,KAA0B;QAE1B,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC;QACxH,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;QAE9E,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;YAChB,OAAO;gBACN,OAAO,EAAE,KAAK;gBACd,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,KAAK;aACd,CAAC;QACH,CAAC;QAED,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;QACvE,CAAC;QAED,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,IAAI,EAAE,IAAI,sBAAsB,CAAC;QAC5F,OAAO;YACN,OAAO,EAAE,YAAY;YACrB,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,wBAAwB,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;YACjE,OAAO,EAAE,IAAI;SACb,CAAC;IACH,CAAC;IAEO,cAAc,CAAC,OAAe,EAAE,KAA0B,EAAE,WAAqB;QACxF,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAC9B,CAAC;QACD,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;YACrB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACxB,CAAC;QACD,OAAO,CAAC,GAAG,WAAW,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC;CACD","sourcesContent":["import { spawn, type ChildProcess } from \"node:child_process\";\nimport { resolve } from \"node:path\";\nimport stripAnsi from \"strip-ansi\";\nimport { getShellConfig, getShellEnv, killProcessTree, sanitizeBinaryOutput } from \"../utils/shell.js\";\n\nconst DEFAULT_YIELD_TIME_MS = 1000;\nconst MAX_YIELD_TIME_MS = 60_000;\nconst DEFAULT_MAX_OUTPUT_CHARS = 12_000;\nconst MAX_OUTPUT_CHARS = 250_000;\nconst MAX_PENDING_OUTPUT_CHARS = 1_000_000;\nconst FINISHED_SESSION_TTL_MS = 5 * 60_000;\nconst EXIT_GRACE_TIME_MS = 75;\nconst DEFAULT_PTY_BRIDGE_BIN = \"python3\";\n\nconst PYTHON_PTY_BRIDGE_SCRIPT = String.raw`import os\nimport pty\nimport select\nimport subprocess\nimport sys\n\nif len(sys.argv) < 2:\n\tprint(\"pty bridge requires shell command arguments\", file=sys.stderr)\n\tsys.exit(2)\n\ncommand = sys.argv[1:]\nmaster_fd, slave_fd = pty.openpty()\nchild = subprocess.Popen(command, stdin=slave_fd, stdout=slave_fd, stderr=slave_fd)\nos.close(slave_fd)\n\nstdin_fd = sys.stdin.fileno()\nstdout_fd = sys.stdout.fileno()\nstdin_open = True\n\nwhile True:\n\treaders = [master_fd]\n\tif stdin_open:\n\t\treaders.append(stdin_fd)\n\treadable, _, _ = select.select(readers, [], [], 0.05)\n\n\tif master_fd in readable:\n\t\ttry:\n\t\t\tchunk = os.read(master_fd, 4096)\n\t\texcept OSError:\n\t\t\tchunk = b\"\"\n\t\tif chunk:\n\t\t\tos.write(stdout_fd, chunk)\n\n\tif stdin_open and stdin_fd in readable:\n\t\tinput_chunk = os.read(stdin_fd, 4096)\n\t\tif input_chunk:\n\t\t\tos.write(master_fd, input_chunk)\n\t\telse:\n\t\t\tstdin_open = False\n\n\tif child.poll() is not None:\n\t\twhile True:\n\t\t\ttry:\n\t\t\t\tchunk = os.read(master_fd, 4096)\n\t\t\texcept OSError:\n\t\t\t\tchunk = b\"\"\n\t\t\tif not chunk:\n\t\t\t\tbreak\n\t\t\tos.write(stdout_fd, chunk)\n\t\tbreak\n\nsys.exit(child.returncode if child.returncode is not None else 0)`;\n\nexport interface UnifiedExecRunInput {\n\tcommand: string;\n\tcwd?: string;\n\ttty?: boolean;\n\tshell?: string;\n\tlogin?: boolean;\n\tyieldTimeMs?: number;\n\tmaxOutputChars?: number;\n}\n\nexport interface UnifiedExecWriteInput {\n\tsessionId: number;\n\tchars?: string;\n\tyieldTimeMs?: number;\n\tmaxOutputChars?: number;\n}\n\nexport interface UnifiedExecPollResult {\n\toutput: string;\n\trunning: boolean;\n\tsessionId?: number;\n\texitCode?: number | null;\n}\n\ninterface UnifiedExecSession {\n\tid: number;\n\tchild: ChildProcess;\n\tusesPty: boolean;\n\tpendingOutput: string[];\n\tpendingChars: number;\n\trunning: boolean;\n\texitCode: number | null;\n\twaiters: Set<() => void>;\n\tcleanupTimer?: NodeJS.Timeout;\n}\n\nfunction normalizeYieldTimeMs(value: number | undefined): number {\n\tif (typeof value !== \"number\" || Number.isNaN(value)) return DEFAULT_YIELD_TIME_MS;\n\treturn Math.max(0, Math.min(MAX_YIELD_TIME_MS, Math.floor(value)));\n}\n\nfunction normalizeMaxOutputChars(value: number | undefined): number {\n\tif (typeof value !== \"number\" || Number.isNaN(value)) return DEFAULT_MAX_OUTPUT_CHARS;\n\treturn Math.max(1, Math.min(MAX_OUTPUT_CHARS, Math.floor(value)));\n}\n\nexport class UnifiedExecManager {\n\tprivate sessions = new Map<number, UnifiedExecSession>();\n\tprivate nextSessionId = 1;\n\n\tasync execCommand(input: UnifiedExecRunInput): Promise<UnifiedExecPollResult> {\n\t\tconst command = input.command?.trim();\n\t\tif (!command) {\n\t\t\tthrow new Error(\"exec_command requires a non-empty command.\");\n\t\t}\n\n\t\tconst session = this.createSession(command, input);\n\t\tawait this.waitForUpdate(session, normalizeYieldTimeMs(input.yieldTimeMs));\n\t\tawait this.waitForExitGrace(session);\n\t\treturn this.buildPollResult(session, normalizeMaxOutputChars(input.maxOutputChars));\n\t}\n\n\tasync writeStdin(input: UnifiedExecWriteInput): Promise<UnifiedExecPollResult> {\n\t\tconst session = this.sessions.get(input.sessionId);\n\t\tif (!session) {\n\t\t\tthrow new Error(`exec session not found: ${input.sessionId}`);\n\t\t}\n\n\t\tif (input.chars && session.running) {\n\t\t\ttry {\n\t\t\t\tsession.child.stdin?.write(input.chars);\n\t\t\t} catch (error) {\n\t\t\t\tthrow new Error(`Failed to write to exec session ${input.sessionId}: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\t}\n\t\t}\n\n\t\tawait this.waitForUpdate(session, normalizeYieldTimeMs(input.yieldTimeMs));\n\t\tawait this.waitForExitGrace(session);\n\t\treturn this.buildPollResult(session, normalizeMaxOutputChars(input.maxOutputChars));\n\t}\n\n\tdispose(): void {\n\t\tfor (const session of this.sessions.values()) {\n\t\t\tif (session.cleanupTimer) {\n\t\t\t\tclearTimeout(session.cleanupTimer);\n\t\t\t\tsession.cleanupTimer = undefined;\n\t\t\t}\n\t\t\tif (session.running && session.child.pid) {\n\t\t\t\tkillProcessTree(session.child.pid);\n\t\t\t}\n\t\t\tsession.waiters.clear();\n\t\t}\n\t\tthis.sessions.clear();\n\t}\n\n\tprivate createSession(command: string, input: UnifiedExecRunInput): UnifiedExecSession {\n\t\tconst invocation = this.resolveInvocation(command, input);\n\t\tconst child = spawn(invocation.command, invocation.args, {\n\t\t\tcwd: input.cwd ? resolve(input.cwd) : undefined,\n\t\t\tenv: getShellEnv(),\n\t\t\tdetached: true,\n\t\t\tstdio: [\"pipe\", \"pipe\", \"pipe\"],\n\t\t});\n\n\t\tconst session: UnifiedExecSession = {\n\t\t\tid: this.nextSessionId++,\n\t\t\tchild,\n\t\t\tusesPty: invocation.usesPty,\n\t\t\tpendingOutput: [],\n\t\t\tpendingChars: 0,\n\t\t\trunning: true,\n\t\t\texitCode: null,\n\t\t\twaiters: new Set(),\n\t\t};\n\t\tthis.sessions.set(session.id, session);\n\n\t\tconst stdoutDecoder = new TextDecoder();\n\t\tconst stderrDecoder = new TextDecoder();\n\t\tconst pushOutput = (raw: Buffer, decoder: InstanceType<typeof TextDecoder>): void => {\n\t\t\tlet text = sanitizeBinaryOutput(stripAnsi(decoder.decode(raw, { stream: true }))).replace(/\\r/g, \"\");\n\t\t\tif (session.usesPty) {\n\t\t\t\ttext = text.replace(/\\^D\\x08\\x08/g, \"\");\n\t\t\t}\n\t\t\tif (!text) return;\n\t\t\tthis.appendOutput(session, text);\n\t\t};\n\n\t\tchild.stdout?.on(\"data\", (raw: Buffer) => pushOutput(raw, stdoutDecoder));\n\t\tchild.stderr?.on(\"data\", (raw: Buffer) => pushOutput(raw, stderrDecoder));\n\t\tchild.on(\"error\", (error) => {\n\t\t\tthis.appendOutput(session, `\\n[exec error] ${error.message}\\n`);\n\t\t});\n\t\tchild.on(\"close\", (code) => {\n\t\t\tsession.running = false;\n\t\t\tsession.exitCode = code ?? null;\n\t\t\tthis.notify(session);\n\t\t\tsession.cleanupTimer = setTimeout(() => {\n\t\t\t\tthis.sessions.delete(session.id);\n\t\t\t}, FINISHED_SESSION_TTL_MS);\n\t\t\tsession.cleanupTimer.unref();\n\t\t});\n\n\t\treturn session;\n\t}\n\n\tprivate appendOutput(session: UnifiedExecSession, chunk: string): void {\n\t\tsession.pendingOutput.push(chunk);\n\t\tsession.pendingChars += chunk.length;\n\t\twhile (session.pendingChars > MAX_PENDING_OUTPUT_CHARS && session.pendingOutput.length > 1) {\n\t\t\tconst removed = session.pendingOutput.shift();\n\t\t\tif (!removed) break;\n\t\t\tsession.pendingChars -= removed.length;\n\t\t}\n\t\tthis.notify(session);\n\t}\n\n\tprivate notify(session: UnifiedExecSession): void {\n\t\tif (session.waiters.size === 0) return;\n\t\tconst waiters = Array.from(session.waiters);\n\t\tsession.waiters.clear();\n\t\tfor (const resolveWaiter of waiters) {\n\t\t\tresolveWaiter();\n\t\t}\n\t}\n\n\tprivate async waitForUpdate(session: UnifiedExecSession, timeoutMs: number): Promise<void> {\n\t\tif (!session.running || session.pendingChars > 0 || timeoutMs <= 0) {\n\t\t\treturn;\n\t\t}\n\t\tawait new Promise<void>((resolveWait) => {\n\t\t\tconst onUpdate = () => {\n\t\t\t\tif (timer) clearTimeout(timer);\n\t\t\t\tresolveWait();\n\t\t\t};\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tsession.waiters.delete(onUpdate);\n\t\t\t\tresolveWait();\n\t\t\t}, timeoutMs);\n\t\t\tsession.waiters.add(onUpdate);\n\t\t});\n\t}\n\n\tprivate async waitForExitGrace(session: UnifiedExecSession): Promise<void> {\n\t\tif (!session.running) return;\n\t\tawait this.waitForClose(session, EXIT_GRACE_TIME_MS);\n\t}\n\n\tprivate async waitForClose(session: UnifiedExecSession, timeoutMs: number): Promise<void> {\n\t\tif (!session.running || timeoutMs <= 0) return;\n\t\tawait new Promise<void>((resolveWait) => {\n\t\t\tconst onUpdate = () => {\n\t\t\t\tif (!session.running) {\n\t\t\t\t\tif (timer) clearTimeout(timer);\n\t\t\t\t\tsession.waiters.delete(onUpdate);\n\t\t\t\t\tresolveWait();\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tsession.waiters.delete(onUpdate);\n\t\t\t\tresolveWait();\n\t\t\t}, timeoutMs);\n\t\t\tsession.waiters.add(onUpdate);\n\t\t});\n\t}\n\n\tprivate drainOutput(session: UnifiedExecSession, maxOutputChars: number): string {\n\t\tconst combined = session.pendingOutput.join(\"\");\n\t\tsession.pendingOutput = [];\n\t\tsession.pendingChars = 0;\n\t\tif (combined.length <= maxOutputChars) return combined;\n\t\treturn combined.slice(combined.length - maxOutputChars);\n\t}\n\n\tprivate buildPollResult(session: UnifiedExecSession, maxOutputChars: number): UnifiedExecPollResult {\n\t\tconst output = this.drainOutput(session, maxOutputChars);\n\t\tif (session.running) {\n\t\t\treturn {\n\t\t\t\toutput,\n\t\t\t\trunning: true,\n\t\t\t\tsessionId: session.id,\n\t\t\t};\n\t\t}\n\t\tif (session.cleanupTimer) {\n\t\t\tclearTimeout(session.cleanupTimer);\n\t\t\tsession.cleanupTimer = undefined;\n\t\t}\n\t\tthis.sessions.delete(session.id);\n\t\treturn {\n\t\t\toutput,\n\t\t\trunning: false,\n\t\t\texitCode: session.exitCode,\n\t\t};\n\t}\n\n\tprivate resolveInvocation(\n\t\tcommand: string,\n\t\tinput: UnifiedExecRunInput,\n\t): { command: string; args: string[]; usesPty: boolean } {\n\t\tconst shellConfig = getShellConfig();\n\t\tconst shell = typeof input.shell === \"string\" && input.shell.trim().length > 0 ? input.shell.trim() : shellConfig.shell;\n\t\tconst shellArgs = this.buildShellArgs(command, input.login, shellConfig.args);\n\n\t\tif (!input.tty) {\n\t\t\treturn {\n\t\t\t\tcommand: shell,\n\t\t\t\targs: shellArgs,\n\t\t\t\tusesPty: false,\n\t\t\t};\n\t\t}\n\n\t\tif (process.platform === \"win32\") {\n\t\t\tthrow new Error(\"exec_command tty=true is not supported on Windows.\");\n\t\t}\n\n\t\tconst ptyBridgeBin = process.env.IOSM_EXEC_PTY_BRIDGE_BIN?.trim() || DEFAULT_PTY_BRIDGE_BIN;\n\t\treturn {\n\t\t\tcommand: ptyBridgeBin,\n\t\t\targs: [\"-u\", \"-c\", PYTHON_PTY_BRIDGE_SCRIPT, shell, ...shellArgs],\n\t\t\tusesPty: true,\n\t\t};\n\t}\n\n\tprivate buildShellArgs(command: string, login: boolean | undefined, defaultArgs: string[]): string[] {\n\t\tif (login === true) {\n\t\t\treturn [\"-l\", \"-c\", command];\n\t\t}\n\t\tif (login === false) {\n\t\t\treturn [\"-c\", command];\n\t\t}\n\t\treturn [...defaultArgs, command];\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAyqDH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,iBA6SxC"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA2uDH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,iBAmUxC"}
package/dist/main.js CHANGED
@@ -24,6 +24,7 @@ import { loadModelsDevProviderCatalog } from "./core/models-dev-provider-catalog
24
24
  import { getMcpCommandHelp, getMergedServerByName, loadMergedMcpConfig, McpRuntime, parseMcpAddCommand, parseMcpTargetCommand, } from "./core/mcp/index.js";
25
25
  import { getSemanticCommandHelp, parseSemanticCliCommand, SemanticConfigMissingError, SemanticIndexRequiredError, SemanticRebuildRequiredError, SemanticSearchRuntime, } from "./core/semantic/index.js";
26
26
  import { DefaultPackageManager } from "./core/package-manager.js";
27
+ import { PolicyEngineV2 } from "./core/policy/index.js";
27
28
  import { DefaultResourceLoader } from "./core/resource-loader.js";
28
29
  import { createAgentSession } from "./core/sdk.js";
29
30
  import { getProfileNames, isValidProfileName } from "./core/agent-profiles.js";
@@ -32,7 +33,7 @@ import { SettingsManager } from "./core/settings-manager.js";
32
33
  import { printTimings, time } from "./core/timings.js";
33
34
  import { allTools } from "./core/tools/index.js";
34
35
  import { runMigrations, showDeprecationWarnings } from "./migrations.js";
35
- import { InteractiveMode, runPrintMode, runRpcMode, runTelegramBridgeMode } from "./modes/index.js";
36
+ import { InteractiveMode, runAcpMode, runPrintMode, runRpcMode, runTelegramBridgeMode } from "./modes/index.js";
36
37
  import { initTheme, stopThemeWatcher } from "./modes/interactive/theme/theme.js";
37
38
  import { buildIosmAgentVerificationPrompt, buildIosmGuideAuthoringPrompt, buildIosmPriorityChecklist, createMetricSnapshot, extractAssistantText, formatMetricSnapshot, getIosmGuidePath, initIosmWorkspace, inspectIosmCycle, listIosmCycles, planIosmCycle, readIosmCycleReport, recordIosmCycleHistory, normalizeIosmGuideMarkdown, writeIosmGuideDocument, } from "./iosm/index.js";
38
39
  /**
@@ -199,6 +200,7 @@ Install a package and add it to settings.
199
200
 
200
201
  Options:
201
202
  -l, --local Install project-locally (${CONFIG_DIR_NAME}/settings.json)
203
+ --yes-trust Non-interactive trust override for source/integrity checks
202
204
 
203
205
  Examples:
204
206
  ${APP_NAME} install npm:@foo/bar
@@ -228,6 +230,9 @@ Example:
228
230
 
229
231
  Update installed packages.
230
232
  If <source> is provided, only that package is updated.
233
+
234
+ Options:
235
+ --yes-trust Non-interactive trust override for source/integrity checks
231
236
  `);
232
237
  return;
233
238
  case "list":
@@ -246,6 +251,7 @@ function parsePackageCommand(args) {
246
251
  }
247
252
  let local = false;
248
253
  let help = false;
254
+ let yesTrust = false;
249
255
  let invalidOption;
250
256
  let source;
251
257
  for (const arg of rest) {
@@ -262,6 +268,10 @@ function parsePackageCommand(args) {
262
268
  }
263
269
  continue;
264
270
  }
271
+ if (arg === "--yes-trust") {
272
+ yesTrust = true;
273
+ continue;
274
+ }
265
275
  if (arg.startsWith("-")) {
266
276
  invalidOption = invalidOption ?? arg;
267
277
  continue;
@@ -270,7 +280,26 @@ function parsePackageCommand(args) {
270
280
  source = arg;
271
281
  }
272
282
  }
273
- return { command, source, local, help, invalidOption };
283
+ return { command, source, local, help, yesTrust, invalidOption };
284
+ }
285
+ async function requestTrustConsentFromTerminal(question) {
286
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
287
+ return false;
288
+ }
289
+ const rl = createInterface({
290
+ input: process.stdin,
291
+ output: process.stdout,
292
+ });
293
+ try {
294
+ const answer = await new Promise((resolve) => {
295
+ rl.question(`${question} [y/N] `, resolve);
296
+ });
297
+ const normalized = answer.trim().toLowerCase();
298
+ return normalized === "y" || normalized === "yes";
299
+ }
300
+ finally {
301
+ rl.close();
302
+ }
274
303
  }
275
304
  async function handlePackageCommand(args) {
276
305
  const options = parsePackageCommand(args);
@@ -298,7 +327,25 @@ async function handlePackageCommand(args) {
298
327
  const agentDir = getAgentDir();
299
328
  const settingsManager = SettingsManager.create(cwd, agentDir);
300
329
  reportSettingsErrors(settingsManager, "package command");
301
- const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
330
+ const policyEngine = new PolicyEngineV2({ cwd, agentDir, settingsManager });
331
+ const packageManager = new DefaultPackageManager({
332
+ cwd,
333
+ agentDir,
334
+ settingsManager,
335
+ allowedSourceHosts: policyEngine.getAllowedSourceHosts(),
336
+ allowNonInteractiveTrustOverride: options.yesTrust,
337
+ trustConsentProvider: async (request) => {
338
+ if (options.yesTrust)
339
+ return true;
340
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
341
+ return false;
342
+ }
343
+ const reasonLabel = request.reason === "fingerprint-change"
344
+ ? `fingerprint changed (${request.previousFingerprint ?? "unknown"} -> ${request.fingerprint})`
345
+ : `new source fingerprint ${request.fingerprint}`;
346
+ return requestTrustConsentFromTerminal(`Trust ${request.sourceType} source "${request.source}" on host ${request.host} (${reasonLabel})?`);
347
+ },
348
+ });
302
349
  packageManager.setProgressCallback((event) => {
303
350
  if (event.type === "start") {
304
351
  process.stdout.write(chalk.dim(`${event.message}\n`));
@@ -1113,7 +1160,19 @@ async function handleConfigCommand(args) {
1113
1160
  const agentDir = getAgentDir();
1114
1161
  const settingsManager = SettingsManager.create(cwd, agentDir);
1115
1162
  reportSettingsErrors(settingsManager, "config command");
1116
- const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
1163
+ const policyEngine = new PolicyEngineV2({ cwd, agentDir, settingsManager });
1164
+ const packageManager = new DefaultPackageManager({
1165
+ cwd,
1166
+ agentDir,
1167
+ settingsManager,
1168
+ allowedSourceHosts: policyEngine.getAllowedSourceHosts(),
1169
+ trustConsentProvider: async (request) => {
1170
+ const reasonLabel = request.reason === "fingerprint-change"
1171
+ ? `fingerprint changed (${request.previousFingerprint ?? "unknown"} -> ${request.fingerprint})`
1172
+ : `new source fingerprint ${request.fingerprint}`;
1173
+ return requestTrustConsentFromTerminal(`Trust ${request.sourceType} source "${request.source}" on host ${request.host} (${reasonLabel})?`);
1174
+ },
1175
+ });
1117
1176
  const resolvedPaths = await packageManager.resolve();
1118
1177
  await selectConfig({
1119
1178
  resolvedPaths,
@@ -1487,6 +1546,7 @@ export async function main(args) {
1487
1546
  const agentDir = getAgentDir();
1488
1547
  const settingsManager = SettingsManager.create(cwd, agentDir);
1489
1548
  reportSettingsErrors(settingsManager, "startup");
1549
+ const policyEngine = new PolicyEngineV2({ cwd, agentDir, settingsManager });
1490
1550
  const authStorage = AuthStorage.create();
1491
1551
  const modelRegistry = new ModelRegistry(authStorage, getModelsPath());
1492
1552
  await hydrateAuthenticatedProviderModelsOnStartup(modelRegistry);
@@ -1552,8 +1612,8 @@ export async function main(args) {
1552
1612
  });
1553
1613
  return;
1554
1614
  }
1555
- // Read piped stdin content (if any) - skip for RPC mode which uses stdin for JSON-RPC
1556
- if (parsed.mode !== "rpc") {
1615
+ // Read piped stdin content (if any) - skip for RPC/ACP modes which use stdin for protocol transport
1616
+ if (parsed.mode !== "rpc" && parsed.mode !== "acp") {
1557
1617
  const stdinContent = await readPipedStdin();
1558
1618
  if (stdinContent !== undefined) {
1559
1619
  // Force print mode since interactive mode requires a TTY for keyboard input
@@ -1576,13 +1636,15 @@ export async function main(args) {
1576
1636
  console.log(`Exported to: ${result}`);
1577
1637
  process.exit(0);
1578
1638
  }
1579
- if (parsed.mode === "rpc" && parsed.fileArgs.length > 0) {
1580
- console.error(chalk.red("Error: @file arguments are not supported in RPC mode"));
1639
+ if ((parsed.mode === "rpc" || parsed.mode === "acp") && parsed.fileArgs.length > 0) {
1640
+ console.error(chalk.red("Error: @file arguments are not supported in RPC/ACP mode"));
1581
1641
  process.exit(1);
1582
1642
  }
1583
1643
  const { initialMessage, initialImages } = await prepareInitialMessage(parsed, settingsManager.getImageAutoResize());
1584
1644
  const isInteractive = !parsed.print && parsed.mode === undefined;
1585
1645
  const mode = parsed.mode || "text";
1646
+ const sandboxEnabled = parsed.sandbox ?? settingsManager.getSandboxEnabled();
1647
+ process.env.IOSM_SANDBOX_ENABLED = sandboxEnabled ? "1" : "0";
1586
1648
  initTheme(settingsManager.getTheme(), isInteractive);
1587
1649
  // Show deprecation warnings in interactive mode
1588
1650
  if (isInteractive && deprecationWarnings.length > 0) {
@@ -1611,7 +1673,7 @@ export async function main(args) {
1611
1673
  sessionOptions.authStorage = authStorage;
1612
1674
  sessionOptions.modelRegistry = modelRegistry;
1613
1675
  sessionOptions.resourceLoader = resourceLoader;
1614
- sessionOptions.enableAskUserTool = isInteractive || mode === "rpc";
1676
+ sessionOptions.enableAskUserTool = isInteractive || mode === "rpc" || mode === "acp";
1615
1677
  // Handle CLI --api-key as runtime override (not persisted)
1616
1678
  if (parsed.apiKey) {
1617
1679
  if (!sessionOptions.model) {
@@ -1621,12 +1683,19 @@ export async function main(args) {
1621
1683
  authStorage.setRuntimeApiKey(sessionOptions.model.provider, parsed.apiKey);
1622
1684
  }
1623
1685
  let mcpRuntime;
1686
+ let sessionTraceSink;
1624
1687
  try {
1625
1688
  mcpRuntime = new McpRuntime({
1626
1689
  cwd,
1627
1690
  agentDir,
1628
1691
  clientName: APP_NAME,
1629
1692
  clientVersion: VERSION,
1693
+ onPolicyDecision: (event) => {
1694
+ sessionTraceSink?.appendRuntimeTrace("policy_decision", {
1695
+ scope: "mcp",
1696
+ ...event,
1697
+ });
1698
+ },
1630
1699
  });
1631
1700
  await mcpRuntime.refresh();
1632
1701
  printMcpConfigWarnings(mcpRuntime.getErrors());
@@ -1635,9 +1704,10 @@ export async function main(args) {
1635
1704
  sessionOptions.customTools = [...(sessionOptions.customTools ?? []), ...mcpTools];
1636
1705
  }
1637
1706
  const { session, modelFallbackMessage } = await createAgentSession(sessionOptions);
1707
+ sessionTraceSink = session;
1638
1708
  // RPC mode must start even without a preselected model so remote clients
1639
1709
  // (for example Telegram bridge) can choose one later via /model.
1640
- if (!isInteractive && mode !== "rpc" && !session.model) {
1710
+ if (!isInteractive && mode !== "rpc" && mode !== "acp" && !session.model) {
1641
1711
  console.error(chalk.red("No model selected."));
1642
1712
  console.error(chalk.yellow("\nSelect one explicitly:"));
1643
1713
  console.error(` ${APP_NAME} --provider <provider> --model <model-id> ...`);
@@ -1662,7 +1732,18 @@ export async function main(args) {
1662
1732
  }
1663
1733
  }
1664
1734
  if (mode === "rpc") {
1665
- await runRpcMode(session);
1735
+ await runRpcMode(session, {
1736
+ mcpRuntime,
1737
+ policyEngine,
1738
+ profileName: sessionOptions.profile,
1739
+ });
1740
+ }
1741
+ else if (mode === "acp") {
1742
+ await runAcpMode(session, {
1743
+ mcpRuntime,
1744
+ policyEngine,
1745
+ profileName: sessionOptions.profile,
1746
+ });
1666
1747
  }
1667
1748
  else if (isInteractive) {
1668
1749
  if (scopedModels.length > 0 && (parsed.verbose || !settingsManager.getQuietStartup())) {
@@ -1685,6 +1766,7 @@ export async function main(args) {
1685
1766
  planMode: parsed.plan,
1686
1767
  profile: sessionOptions.profile,
1687
1768
  mcpRuntime,
1769
+ policyEngine,
1688
1770
  });
1689
1771
  await mode.run();
1690
1772
  }