chivgent 0.9.0 → 0.11.0

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 (84) hide show
  1. package/README.md +144 -10
  2. package/README.zh-CN.md +123 -8
  3. package/dist/agent.d.ts.map +1 -1
  4. package/dist/agent.js +9 -0
  5. package/dist/agent.js.map +1 -1
  6. package/dist/cli-options.d.ts +13 -2
  7. package/dist/cli-options.d.ts.map +1 -1
  8. package/dist/cli-options.js +39 -5
  9. package/dist/cli-options.js.map +1 -1
  10. package/dist/cli.js +166 -6
  11. package/dist/cli.js.map +1 -1
  12. package/dist/events.d.ts +17 -1
  13. package/dist/events.d.ts.map +1 -1
  14. package/dist/events.js.map +1 -1
  15. package/dist/extensions/api.d.ts +38 -0
  16. package/dist/extensions/api.d.ts.map +1 -0
  17. package/dist/extensions/api.js +2 -0
  18. package/dist/extensions/api.js.map +1 -0
  19. package/dist/extensions/decide-trust.d.ts +28 -0
  20. package/dist/extensions/decide-trust.d.ts.map +1 -0
  21. package/dist/extensions/decide-trust.js +96 -0
  22. package/dist/extensions/decide-trust.js.map +1 -0
  23. package/dist/extensions/discover.d.ts +19 -0
  24. package/dist/extensions/discover.d.ts.map +1 -0
  25. package/dist/extensions/discover.js +61 -0
  26. package/dist/extensions/discover.js.map +1 -0
  27. package/dist/extensions/loader.d.ts +28 -0
  28. package/dist/extensions/loader.d.ts.map +1 -0
  29. package/dist/extensions/loader.js +60 -0
  30. package/dist/extensions/loader.js.map +1 -0
  31. package/dist/extensions/registry.d.ts +36 -0
  32. package/dist/extensions/registry.d.ts.map +1 -0
  33. package/dist/extensions/registry.js +115 -0
  34. package/dist/extensions/registry.js.map +1 -0
  35. package/dist/extensions/trust.d.ts +44 -0
  36. package/dist/extensions/trust.d.ts.map +1 -0
  37. package/dist/extensions/trust.js +129 -0
  38. package/dist/extensions/trust.js.map +1 -0
  39. package/dist/render.d.ts +5 -0
  40. package/dist/render.d.ts.map +1 -1
  41. package/dist/render.js +36 -0
  42. package/dist/render.js.map +1 -1
  43. package/dist/repl.d.ts +12 -1
  44. package/dist/repl.d.ts.map +1 -1
  45. package/dist/repl.js +47 -2
  46. package/dist/repl.js.map +1 -1
  47. package/dist/session.d.ts.map +1 -1
  48. package/dist/session.js +2 -1
  49. package/dist/session.js.map +1 -1
  50. package/dist/shell/config.d.ts +14 -0
  51. package/dist/shell/config.d.ts.map +1 -0
  52. package/dist/shell/config.js +43 -0
  53. package/dist/shell/config.js.map +1 -0
  54. package/dist/shell/local.d.ts +10 -0
  55. package/dist/shell/local.d.ts.map +1 -0
  56. package/dist/shell/local.js +90 -0
  57. package/dist/shell/local.js.map +1 -0
  58. package/dist/shell/output.d.ts +54 -0
  59. package/dist/shell/output.d.ts.map +1 -0
  60. package/dist/shell/output.js +168 -0
  61. package/dist/shell/output.js.map +1 -0
  62. package/dist/shell/process.d.ts +21 -0
  63. package/dist/shell/process.d.ts.map +1 -0
  64. package/dist/shell/process.js +134 -0
  65. package/dist/shell/process.js.map +1 -0
  66. package/dist/shell/sanitize.d.ts +10 -0
  67. package/dist/shell/sanitize.d.ts.map +1 -0
  68. package/dist/shell/sanitize.js +34 -0
  69. package/dist/shell/sanitize.js.map +1 -0
  70. package/dist/shell/truncate.d.ts +31 -0
  71. package/dist/shell/truncate.d.ts.map +1 -0
  72. package/dist/shell/truncate.js +121 -0
  73. package/dist/shell/truncate.js.map +1 -0
  74. package/dist/shell/types.d.ts +33 -0
  75. package/dist/shell/types.d.ts.map +1 -0
  76. package/dist/shell/types.js +26 -0
  77. package/dist/shell/types.js.map +1 -0
  78. package/dist/tools/bash.d.ts +42 -0
  79. package/dist/tools/bash.d.ts.map +1 -0
  80. package/dist/tools/bash.js +225 -0
  81. package/dist/tools/bash.js.map +1 -0
  82. package/dist/tools/tool.d.ts +5 -0
  83. package/dist/tools/tool.d.ts.map +1 -1
  84. package/package.json +1 -1
@@ -0,0 +1,134 @@
1
+ /**
2
+ * How long to keep reading after a child exits.
3
+ *
4
+ * A short-lived command can exit while a descendant it detached still holds the
5
+ * stdout pipe and keeps writing. Finalising on `exit` would destroy the stream
6
+ * mid-write and silently drop that tail, so the timer is re-armed on every
7
+ * chunk: a descendant that is still writing keeps us reading, while a quiet
8
+ * inherited handle that never lets `close` fire still releases us.
9
+ */
10
+ const POST_EXIT_IDLE_MS = 100;
11
+ /**
12
+ * Kills a process and everything it spawned.
13
+ *
14
+ * Children are spawned detached, which makes each one a process group leader,
15
+ * so a negative pid signals the whole group. Killing only the direct child
16
+ * would leave its grandchildren running: stopping `npm test` would kill the npm
17
+ * process and leave the test workers holding CPU and ports.
18
+ */
19
+ export function killProcessTree(pid) {
20
+ try {
21
+ process.kill(-pid, "SIGKILL");
22
+ }
23
+ catch {
24
+ try {
25
+ process.kill(pid, "SIGKILL");
26
+ }
27
+ catch {
28
+ // Already gone.
29
+ }
30
+ }
31
+ }
32
+ const trackedPids = new Set();
33
+ /** Detached children outlive the parent, so every pid is tracked until it exits. */
34
+ export function trackChildPid(pid) {
35
+ trackedPids.add(pid);
36
+ }
37
+ export function untrackChildPid(pid) {
38
+ trackedPids.delete(pid);
39
+ }
40
+ /** Kills every command still running. Called when chivgent itself is shutting down. */
41
+ export function killTrackedChildren() {
42
+ for (const pid of trackedPids) {
43
+ killProcessTree(pid);
44
+ }
45
+ trackedPids.clear();
46
+ }
47
+ /**
48
+ * Resolves with the exit code once the child has exited and its pipes have
49
+ * fallen idle. Rejects only when the process could not be spawned.
50
+ */
51
+ export function waitForChildProcess(child) {
52
+ return new Promise((resolve, reject) => {
53
+ let settled = false;
54
+ let exited = false;
55
+ let exitCode = null;
56
+ let idleTimer;
57
+ let stdoutEnded = child.stdout === null;
58
+ let stderrEnded = child.stderr === null;
59
+ const cleanup = () => {
60
+ if (idleTimer !== undefined) {
61
+ clearTimeout(idleTimer);
62
+ idleTimer = undefined;
63
+ }
64
+ child.removeListener("error", onError);
65
+ child.removeListener("exit", onExit);
66
+ child.removeListener("close", onClose);
67
+ child.stdout?.removeListener("end", onStdoutEnd);
68
+ child.stderr?.removeListener("end", onStderrEnd);
69
+ child.stdout?.removeListener("data", onData);
70
+ child.stderr?.removeListener("data", onData);
71
+ };
72
+ const finalize = (code) => {
73
+ if (settled) {
74
+ return;
75
+ }
76
+ settled = true;
77
+ cleanup();
78
+ child.stdout?.destroy();
79
+ child.stderr?.destroy();
80
+ resolve(code);
81
+ };
82
+ const finalizeIfPipesClosed = () => {
83
+ if (exited && !settled && stdoutEnded && stderrEnded) {
84
+ finalize(exitCode);
85
+ }
86
+ };
87
+ const armIdleTimer = () => {
88
+ if (idleTimer !== undefined) {
89
+ clearTimeout(idleTimer);
90
+ }
91
+ idleTimer = setTimeout(() => finalize(exitCode), POST_EXIT_IDLE_MS);
92
+ };
93
+ const onData = () => {
94
+ if (exited && !settled) {
95
+ armIdleTimer();
96
+ }
97
+ };
98
+ const onStdoutEnd = () => {
99
+ stdoutEnded = true;
100
+ finalizeIfPipesClosed();
101
+ };
102
+ const onStderrEnd = () => {
103
+ stderrEnded = true;
104
+ finalizeIfPipesClosed();
105
+ };
106
+ const onError = (error) => {
107
+ if (settled) {
108
+ return;
109
+ }
110
+ settled = true;
111
+ cleanup();
112
+ reject(error);
113
+ };
114
+ const onExit = (code) => {
115
+ exited = true;
116
+ exitCode = code;
117
+ finalizeIfPipesClosed();
118
+ if (!settled) {
119
+ armIdleTimer();
120
+ }
121
+ };
122
+ const onClose = (code) => {
123
+ finalize(code);
124
+ };
125
+ child.stdout?.once("end", onStdoutEnd);
126
+ child.stderr?.once("end", onStderrEnd);
127
+ child.stdout?.on("data", onData);
128
+ child.stderr?.on("data", onData);
129
+ child.once("error", onError);
130
+ child.once("exit", onExit);
131
+ child.once("close", onClose);
132
+ });
133
+ }
134
+ //# sourceMappingURL=process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/shell/process.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Strips characters that must not reach the transcript or the terminal.
3
+ *
4
+ * Commands print ANSI sequences, progress-bar control codes, and occasionally
5
+ * raw binary. Left alone those corrupt the session JSONL and scramble the
6
+ * terminal, so everything below 0x20 except tab, newline and carriage return is
7
+ * dropped, along with lone surrogates and Unicode format characters.
8
+ */
9
+ export declare function sanitizeShellOutput(value: string): string;
10
+ //# sourceMappingURL=sanitize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/shell/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAwBzD"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Strips characters that must not reach the transcript or the terminal.
3
+ *
4
+ * Commands print ANSI sequences, progress-bar control codes, and occasionally
5
+ * raw binary. Left alone those corrupt the session JSONL and scramble the
6
+ * terminal, so everything below 0x20 except tab, newline and carriage return is
7
+ * dropped, along with lone surrogates and Unicode format characters.
8
+ */
9
+ export function sanitizeShellOutput(value) {
10
+ let result = "";
11
+ for (const character of value) {
12
+ const code = character.codePointAt(0);
13
+ if (code === undefined) {
14
+ continue;
15
+ }
16
+ if (code === 0x09 || code === 0x0a || code === 0x0d) {
17
+ result += character;
18
+ continue;
19
+ }
20
+ if (code <= 0x1f || code === 0x7f) {
21
+ continue;
22
+ }
23
+ // Lone surrogates survive iteration only when unpaired.
24
+ if (code >= 0xd800 && code <= 0xdfff) {
25
+ continue;
26
+ }
27
+ if (code >= 0xfff9 && code <= 0xfffb) {
28
+ continue;
29
+ }
30
+ result += character;
31
+ }
32
+ return result;
33
+ }
34
+ //# sourceMappingURL=sanitize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../../src/shell/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,SAAS,IAAI,KAAK,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;QACtC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,SAAS;QACX,CAAC;QACD,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YACpD,MAAM,IAAI,SAAS,CAAC;YACpB,SAAS;QACX,CAAC;QACD,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClC,SAAS;QACX,CAAC;QACD,wDAAwD;QACxD,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,EAAE,CAAC;YACrC,SAAS;QACX,CAAC;QACD,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,EAAE,CAAC;YACrC,SAAS;QACX,CAAC;QACD,MAAM,IAAI,SAAS,CAAC;IACtB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,31 @@
1
+ export declare const DEFAULT_MAX_LINES = 2000;
2
+ export declare const DEFAULT_MAX_BYTES: number;
3
+ export interface TruncationOptions {
4
+ readonly maxLines?: number;
5
+ readonly maxBytes?: number;
6
+ }
7
+ export interface TruncationResult {
8
+ readonly content: string;
9
+ readonly truncated: boolean;
10
+ readonly truncatedBy: "lines" | "bytes" | null;
11
+ readonly totalLines: number;
12
+ readonly totalBytes: number;
13
+ readonly outputLines: number;
14
+ readonly outputBytes: number;
15
+ /** True when the kept text starts mid-line because one line blew the budget. */
16
+ readonly lastLinePartial: boolean;
17
+ readonly maxLines: number;
18
+ readonly maxBytes: number;
19
+ }
20
+ export declare function countLines(content: string): number;
21
+ /**
22
+ * Keeps the end of the content.
23
+ *
24
+ * Shell output is truncated from the tail because that is where the answer is:
25
+ * the failing assertion, the compiler's last error, the summary line. Reading a
26
+ * file wants the opposite, which is why `read_file` keeps its own line-range
27
+ * logic rather than sharing this one.
28
+ */
29
+ export declare function truncateTail(content: string, options?: TruncationOptions): TruncationResult;
30
+ export declare function formatSize(bytes: number): string;
31
+ //# sourceMappingURL=truncate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"truncate.d.ts","sourceRoot":"","sources":["../../src/shell/truncate.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,iBAAiB,OAAQ,CAAC;AACvC,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAE3C,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAAC;IAC/C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,gFAAgF;IAChF,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAoCD,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,iBAAsB,GAC9B,gBAAgB,CAsElB;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQhD"}
@@ -0,0 +1,121 @@
1
+ // Output truncation shared by the shell tool and anything else that has to fit
2
+ // unbounded output into a bounded result.
3
+ export const DEFAULT_MAX_LINES = 2_000;
4
+ export const DEFAULT_MAX_BYTES = 50 * 1024;
5
+ function byteLength(value) {
6
+ return Buffer.byteLength(value, "utf8");
7
+ }
8
+ /** Splits on newlines, keeping the terminator on the line it belongs to. */
9
+ function splitLines(content) {
10
+ if (content.length === 0) {
11
+ return [];
12
+ }
13
+ const lines = content.split("\n").map((line, index, all) => index === all.length - 1 ? line : `${line}\n`);
14
+ if (lines.at(-1) === "") {
15
+ lines.pop();
16
+ }
17
+ return lines;
18
+ }
19
+ /** Keeps whole characters, never splitting a multi-byte sequence. */
20
+ function takeLastBytes(value, maxBytes) {
21
+ const characters = [...value];
22
+ let bytes = 0;
23
+ let index = characters.length;
24
+ while (index > 0) {
25
+ const size = byteLength(characters[index - 1] ?? "");
26
+ if (bytes + size > maxBytes) {
27
+ break;
28
+ }
29
+ bytes += size;
30
+ index -= 1;
31
+ }
32
+ return characters.slice(index).join("");
33
+ }
34
+ export function countLines(content) {
35
+ return splitLines(content).length;
36
+ }
37
+ /**
38
+ * Keeps the end of the content.
39
+ *
40
+ * Shell output is truncated from the tail because that is where the answer is:
41
+ * the failing assertion, the compiler's last error, the summary line. Reading a
42
+ * file wants the opposite, which is why `read_file` keeps its own line-range
43
+ * logic rather than sharing this one.
44
+ */
45
+ export function truncateTail(content, options = {}) {
46
+ const maxLines = options.maxLines ?? DEFAULT_MAX_LINES;
47
+ const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
48
+ const totalBytes = byteLength(content);
49
+ const lines = splitLines(content);
50
+ const totalLines = lines.length;
51
+ if (totalLines <= maxLines && totalBytes <= maxBytes) {
52
+ return {
53
+ content,
54
+ truncated: false,
55
+ truncatedBy: null,
56
+ totalLines,
57
+ totalBytes,
58
+ outputLines: totalLines,
59
+ outputBytes: totalBytes,
60
+ lastLinePartial: false,
61
+ maxLines,
62
+ maxBytes,
63
+ };
64
+ }
65
+ let bytes = 0;
66
+ let index = lines.length;
67
+ let stoppedBy = "lines";
68
+ while (index > 0) {
69
+ if (lines.length - index >= maxLines) {
70
+ stoppedBy = "lines";
71
+ break;
72
+ }
73
+ const size = byteLength(lines[index - 1] ?? "");
74
+ if (bytes + size > maxBytes) {
75
+ stoppedBy = "bytes";
76
+ break;
77
+ }
78
+ bytes += size;
79
+ index -= 1;
80
+ }
81
+ // Not even one line fits: keep the tail of the last line so the result is
82
+ // never empty just because a command printed one enormous line.
83
+ if (index === lines.length) {
84
+ const partial = takeLastBytes(lines.at(-1) ?? "", maxBytes);
85
+ return {
86
+ content: partial,
87
+ truncated: true,
88
+ truncatedBy: "bytes",
89
+ totalLines,
90
+ totalBytes,
91
+ outputLines: partial.length === 0 ? 0 : 1,
92
+ outputBytes: byteLength(partial),
93
+ lastLinePartial: true,
94
+ maxLines,
95
+ maxBytes,
96
+ };
97
+ }
98
+ const kept = lines.slice(index);
99
+ return {
100
+ content: kept.join(""),
101
+ truncated: true,
102
+ truncatedBy: stoppedBy,
103
+ totalLines,
104
+ totalBytes,
105
+ outputLines: kept.length,
106
+ outputBytes: bytes,
107
+ lastLinePartial: false,
108
+ maxLines,
109
+ maxBytes,
110
+ };
111
+ }
112
+ export function formatSize(bytes) {
113
+ if (bytes < 1024) {
114
+ return `${bytes}B`;
115
+ }
116
+ if (bytes < 1024 * 1024) {
117
+ return `${(bytes / 1024).toFixed(1)}KB`;
118
+ }
119
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
120
+ }
121
+ //# sourceMappingURL=truncate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"truncate.js","sourceRoot":"","sources":["../../src/shell/truncate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /** Raised when a command was killed because its AbortSignal fired. */
2
+ export declare class ShellAbortError extends Error {
3
+ constructor();
4
+ }
5
+ /** Raised when a command outlived the timeout it was given. */
6
+ export declare class ShellTimeoutError extends Error {
7
+ readonly timeoutSeconds: number;
8
+ constructor(timeoutSeconds: number);
9
+ }
10
+ export declare class ShellUnavailableError extends Error {
11
+ constructor(message: string);
12
+ }
13
+ export interface ShellExecOptions {
14
+ /** Receives raw stdout and stderr chunks, interleaved in arrival order. */
15
+ readonly onData: (chunk: Buffer) => void;
16
+ readonly signal?: AbortSignal;
17
+ readonly timeoutSeconds?: number;
18
+ readonly env?: NodeJS.ProcessEnv;
19
+ }
20
+ export interface ShellExecResult {
21
+ /** null when the process was killed by a signal rather than exiting. */
22
+ readonly exitCode: number | null;
23
+ }
24
+ /**
25
+ * Pluggable command execution.
26
+ *
27
+ * Local `spawn` is one implementation. A container or an SSH backend is
28
+ * another, and the shell tool never needs to know which it has.
29
+ */
30
+ export interface ShellOperations {
31
+ exec(command: string, cwd: string, options: ShellExecOptions): Promise<ShellExecResult>;
32
+ }
33
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shell/types.ts"],"names":[],"mappings":"AAEA,sEAAsE;AACtE,qBAAa,eAAgB,SAAQ,KAAK;IACxC,cAKC;CACF;AAED,+DAA+D;AAC/D,qBAAa,iBAAkB,SAAQ,KAAK;IAC9B,QAAQ,CAAC,cAAc,EAAE,MAAM;IAA3C,YAAqB,cAAc,EAAE,MAAM,EAG1C;CACF;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,MAAM,WAAW,gBAAgB;IAC/B,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CACF,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,CAAC;CAC7B"}
@@ -0,0 +1,26 @@
1
+ // Shared contracts for shell execution.
2
+ /** Raised when a command was killed because its AbortSignal fired. */
3
+ export class ShellAbortError extends Error {
4
+ constructor() {
5
+ super("Command aborted");
6
+ // The Agent recognises this name and ends the run as aborted, exactly as
7
+ // it does for a cancelled Provider call.
8
+ this.name = "AbortError";
9
+ }
10
+ }
11
+ /** Raised when a command outlived the timeout it was given. */
12
+ export class ShellTimeoutError extends Error {
13
+ timeoutSeconds;
14
+ constructor(timeoutSeconds) {
15
+ super(`Command timed out after ${timeoutSeconds} seconds`);
16
+ this.timeoutSeconds = timeoutSeconds;
17
+ this.name = "ShellTimeoutError";
18
+ }
19
+ }
20
+ export class ShellUnavailableError extends Error {
21
+ constructor(message) {
22
+ super(message);
23
+ this.name = "ShellUnavailableError";
24
+ }
25
+ }
26
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/shell/types.ts"],"names":[],"mappings":"AAAA,wCAAwC;AAExC,sEAAsE;AACtE,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC;QACE,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACzB,yEAAyE;QACzE,yCAAyC;QACzC,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF;AAED,+DAA+D;AAC/D,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IACrB,cAAc;IAAnC,YAAqB,cAAsB;QACzC,KAAK,CAAC,2BAA2B,cAAc,UAAU,CAAC,CAAC;8BADxC,cAAc;QAEjC,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF"}
@@ -0,0 +1,42 @@
1
+ import type { Tool, ToolContext, ToolOutput } from "./tool.js";
2
+ import { type ShellOperations } from "../shell/types.js";
3
+ /** How often a running command may push an output snapshot. */
4
+ export declare const OUTPUT_UPDATE_THROTTLE_MS = 100;
5
+ export interface BashToolOptions {
6
+ readonly cwd: string;
7
+ /** Swap in a container or SSH backend without touching the tool. */
8
+ readonly operations?: ShellOperations;
9
+ readonly maxLines?: number;
10
+ readonly maxBytes?: number;
11
+ readonly tempDirectory?: string;
12
+ readonly throttleMs?: number;
13
+ }
14
+ export declare class BashTool implements Tool {
15
+ readonly name = "bash";
16
+ readonly description = "Run a shell command in the workspace root and return its combined stdout and stderr. Output is truncated to the last part of the run; the full output is written to a temp file when that happens. Not interactive: commands get no stdin.";
17
+ readonly inputSchema: {
18
+ readonly type: "object";
19
+ readonly properties: {
20
+ readonly command: {
21
+ readonly type: "string";
22
+ readonly description: "Shell command to run in the workspace root. Not interactive: it has no stdin.";
23
+ };
24
+ readonly timeout: {
25
+ readonly type: "number";
26
+ readonly description: "Optional timeout in seconds. There is no default; set one for commands that may hang.";
27
+ };
28
+ };
29
+ readonly required: readonly ["command"];
30
+ readonly additionalProperties: false;
31
+ };
32
+ private readonly cwd;
33
+ private readonly operations;
34
+ private readonly maxLines?;
35
+ private readonly maxBytes?;
36
+ private readonly tempDirectory?;
37
+ private readonly throttleMs;
38
+ constructor(options: BashToolOptions);
39
+ execute(argumentsValue: unknown, context: ToolContext): Promise<ToolOutput>;
40
+ private finish;
41
+ }
42
+ //# sourceMappingURL=bash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../src/tools/bash.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAI/D,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,mBAAmB,CAAC;AAE3B,+DAA+D;AAC/D,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAyB7C,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,qBAAa,QAAS,YAAW,IAAI;IACnC,QAAQ,CAAC,IAAI,UAAU;IACvB,QAAQ,CAAC,WAAW,gPAC2N;IAC/O,QAAQ,CAAC,WAAW;uBApCd,QAAQ;;qBAEZ,OAAO;yBACL,IAAI,EAAE,QAAQ;yBACd,WAAW,EACT,+EAA+E;;qBAEnF,OAAO;yBACL,IAAI,EAAE,QAAQ;yBACd,WAAW,EACT,uFAAuF;;;qCAGlF,SAAS;;MAuBe;IAEnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkB;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,YAAY,OAAO,EAAE,eAAe,EAanC;IAEK,OAAO,CACX,cAAc,EAAE,OAAO,EACvB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC,CAmFrB;YAEa,MAAM;CAWrB"}