@pentoshi/clai 3.9.3 → 3.9.4

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 (94) hide show
  1. package/dist/agent/runner.d.ts +1 -1
  2. package/dist/agent/runner.js +9 -4
  3. package/dist/agent/runner.js.map +1 -1
  4. package/dist/app/adapters/current-interactive-sessions-adapter.d.ts +4 -0
  5. package/dist/app/adapters/current-interactive-sessions-adapter.js +11 -0
  6. package/dist/app/adapters/current-interactive-sessions-adapter.js.map +1 -0
  7. package/dist/app/controllers/cancel-all-result.d.ts +8 -0
  8. package/dist/app/controllers/cancel-all-result.js +32 -0
  9. package/dist/app/controllers/cancel-all-result.js.map +1 -0
  10. package/dist/app/controllers/session-controller.d.ts +8 -0
  11. package/dist/app/controllers/session-controller.js +20 -4
  12. package/dist/app/controllers/session-controller.js.map +1 -1
  13. package/dist/app/ports/interactive-sessions-port.d.ts +21 -0
  14. package/dist/app/ports/interactive-sessions-port.js +9 -0
  15. package/dist/app/ports/interactive-sessions-port.js.map +1 -0
  16. package/dist/index.js +21 -8
  17. package/dist/index.js.map +1 -1
  18. package/dist/interactive-session/artifact-writer.d.ts +54 -0
  19. package/dist/interactive-session/artifact-writer.js +161 -0
  20. package/dist/interactive-session/artifact-writer.js.map +1 -0
  21. package/dist/interactive-session/cleanup.d.ts +36 -0
  22. package/dist/interactive-session/cleanup.js +157 -0
  23. package/dist/interactive-session/cleanup.js.map +1 -0
  24. package/dist/interactive-session/config.d.ts +118 -0
  25. package/dist/interactive-session/config.js +141 -0
  26. package/dist/interactive-session/config.js.map +1 -0
  27. package/dist/interactive-session/input-policy.d.ts +41 -0
  28. package/dist/interactive-session/input-policy.js +163 -0
  29. package/dist/interactive-session/input-policy.js.map +1 -0
  30. package/dist/interactive-session/manager.d.ts +107 -0
  31. package/dist/interactive-session/manager.js +927 -0
  32. package/dist/interactive-session/manager.js.map +1 -0
  33. package/dist/interactive-session/output-store.d.ts +81 -0
  34. package/dist/interactive-session/output-store.js +239 -0
  35. package/dist/interactive-session/output-store.js.map +1 -0
  36. package/dist/interactive-session/output-view.d.ts +20 -0
  37. package/dist/interactive-session/output-view.js +142 -0
  38. package/dist/interactive-session/output-view.js.map +1 -0
  39. package/dist/interactive-session/recovery-journal.d.ts +55 -0
  40. package/dist/interactive-session/recovery-journal.js +115 -0
  41. package/dist/interactive-session/recovery-journal.js.map +1 -0
  42. package/dist/interactive-session/registry.d.ts +59 -0
  43. package/dist/interactive-session/registry.js +170 -0
  44. package/dist/interactive-session/registry.js.map +1 -0
  45. package/dist/interactive-session/runtime.d.ts +107 -0
  46. package/dist/interactive-session/runtime.js +191 -0
  47. package/dist/interactive-session/runtime.js.map +1 -0
  48. package/dist/interactive-session/session-runtime.d.ts +50 -0
  49. package/dist/interactive-session/session-runtime.js +63 -0
  50. package/dist/interactive-session/session-runtime.js.map +1 -0
  51. package/dist/interactive-session/streaming-redactor.d.ts +53 -0
  52. package/dist/interactive-session/streaming-redactor.js +263 -0
  53. package/dist/interactive-session/streaming-redactor.js.map +1 -0
  54. package/dist/interactive-session/telemetry.d.ts +30 -0
  55. package/dist/interactive-session/telemetry.js +47 -0
  56. package/dist/interactive-session/telemetry.js.map +1 -0
  57. package/dist/interactive-session/transport-factory.d.ts +8 -0
  58. package/dist/interactive-session/transport-factory.js +16 -0
  59. package/dist/interactive-session/transport-factory.js.map +1 -0
  60. package/dist/interactive-session/transport-node-pty.d.ts +49 -0
  61. package/dist/interactive-session/transport-node-pty.js +219 -0
  62. package/dist/interactive-session/transport-node-pty.js.map +1 -0
  63. package/dist/interactive-session/transport-pipe.d.ts +9 -0
  64. package/dist/interactive-session/transport-pipe.js +215 -0
  65. package/dist/interactive-session/transport-pipe.js.map +1 -0
  66. package/dist/interactive-session/transport.d.ts +104 -0
  67. package/dist/interactive-session/transport.js +89 -0
  68. package/dist/interactive-session/transport.js.map +1 -0
  69. package/dist/interactive-session/types.d.ts +254 -0
  70. package/dist/interactive-session/types.js +191 -0
  71. package/dist/interactive-session/types.js.map +1 -0
  72. package/dist/os/process-identity.d.ts +46 -0
  73. package/dist/os/process-identity.js +147 -0
  74. package/dist/os/process-identity.js.map +1 -0
  75. package/dist/repl.js +10 -0
  76. package/dist/repl.js.map +1 -1
  77. package/dist/tools/definitions.js +95 -0
  78. package/dist/tools/definitions.js.map +1 -1
  79. package/dist/tools/interactive-session-tools.d.ts +11 -0
  80. package/dist/tools/interactive-session-tools.js +261 -0
  81. package/dist/tools/interactive-session-tools.js.map +1 -0
  82. package/dist/tools/jobs.js +2 -70
  83. package/dist/tools/jobs.js.map +1 -1
  84. package/dist/tools/registry.js +4 -0
  85. package/dist/tools/registry.js.map +1 -1
  86. package/dist/tui-v2/bootstrap/composition-root.d.ts +3 -0
  87. package/dist/tui-v2/bootstrap/composition-root.js +3 -0
  88. package/dist/tui-v2/bootstrap/composition-root.js.map +1 -1
  89. package/dist/tui-v2/bootstrap/start-tui-v2.js +10 -0
  90. package/dist/tui-v2/bootstrap/start-tui-v2.js.map +1 -1
  91. package/dist/types.d.ts +5 -0
  92. package/dist/version.generated.d.ts +2 -2
  93. package/dist/version.generated.js +2 -2
  94. package/package.json +1 -1
@@ -0,0 +1,261 @@
1
+ /**
2
+ * Tool adapter for the seven additive `terminal.*` operations.
3
+ *
4
+ * Existing shell and job tools are untouched. Every handler requires an owning
5
+ * conversation id, projects a concise ANSI-free `output` for providers and
6
+ * transcripts, and returns the structured result under `interactiveSession`.
7
+ */
8
+ import { isScopeActive, loadScope } from "../store/scope.js";
9
+ import { interactiveSessionManager, } from "../interactive-session/manager.js";
10
+ import { isInteractiveSessionsEnabled, resolveInteractiveSessionConfig, } from "../interactive-session/config.js";
11
+ import { asStableError, } from "../interactive-session/types.js";
12
+ import { CONTROL_INPUTS } from "../interactive-session/types.js";
13
+ export const INTERACTIVE_SESSION_TOOL_NAMES = [
14
+ "terminal.start",
15
+ "terminal.send",
16
+ "terminal.read",
17
+ "terminal.status",
18
+ "terminal.list",
19
+ "terminal.resize",
20
+ "terminal.close",
21
+ ];
22
+ function requireString(args, key) {
23
+ const value = args[key];
24
+ if (typeof value !== "string" || value.length === 0) {
25
+ throw new Error(`${key} must be a non-empty string`);
26
+ }
27
+ return value;
28
+ }
29
+ function optionalString(args, key) {
30
+ const value = args[key];
31
+ return typeof value === "string" ? value : undefined;
32
+ }
33
+ function optionalNumber(args, key) {
34
+ const value = args[key];
35
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
36
+ }
37
+ function optionalView(args) {
38
+ const value = optionalString(args, "view");
39
+ if (value === undefined)
40
+ return undefined;
41
+ if (value !== "plain" && value !== "encoded") {
42
+ throw new Error("view must be plain or encoded");
43
+ }
44
+ return value;
45
+ }
46
+ /** Strict validation of the dependent send fields before the manager sees them. */
47
+ function parseInput(args) {
48
+ const kind = requireString(args, "kind");
49
+ if (kind === "text") {
50
+ const text = args.text;
51
+ if (typeof text !== "string")
52
+ throw new Error("text is required when kind is text");
53
+ if (args.control !== undefined)
54
+ throw new Error("control is not allowed when kind is text");
55
+ const submit = optionalString(args, "submit") ?? "enter";
56
+ if (submit !== "enter" && submit !== "none") {
57
+ throw new Error("submit must be enter or none");
58
+ }
59
+ return { kind: "text", text, submit };
60
+ }
61
+ if (kind === "control") {
62
+ const control = requireString(args, "control");
63
+ if (args.text !== undefined)
64
+ throw new Error("text is not allowed when kind is control");
65
+ if (!CONTROL_INPUTS.includes(control)) {
66
+ throw new Error(`control must be one of: ${CONTROL_INPUTS.join(", ")}`);
67
+ }
68
+ return { kind: "control", control: control };
69
+ }
70
+ if (kind === "eof") {
71
+ if (args.text !== undefined || args.control !== undefined) {
72
+ throw new Error("text and control are not allowed when kind is eof");
73
+ }
74
+ return { kind: "eof" };
75
+ }
76
+ throw new Error("kind must be text, control, or eof");
77
+ }
78
+ /** Active engagement scope, so interactive risk matches the shell boundary. */
79
+ async function activeScope() {
80
+ const scope = await loadScope();
81
+ return isScopeActive(scope) ? scope : undefined;
82
+ }
83
+ function requireOwner(options, operation) {
84
+ const ownerId = options?.sessionId;
85
+ if (typeof ownerId === "string" && ownerId.length > 0)
86
+ return ownerId;
87
+ throw new Error(`terminal.${operation} requires an active clai session that owns the interactive session`);
88
+ }
89
+ /**
90
+ * Interactive confirmation. The runner already prompted when it marked the call
91
+ * confirmed; otherwise the managed secret/confirm path is unavailable and a
92
+ * `confirm`-level action is refused rather than silently delivered.
93
+ */
94
+ function confirmPort(options) {
95
+ return async () => options?.confirmed === true;
96
+ }
97
+ function summarizePage(page) {
98
+ if (!page)
99
+ return [];
100
+ const lines = [];
101
+ const body = page.events.map((event) => event.content).join("");
102
+ if (body.length > 0)
103
+ lines.push(body.replace(/\s+$/, ""));
104
+ lines.push(`[cursor ${page.requestedCursor}→${page.nextCursor}${page.hasMore ? " more" : ""}${page.decodingLoss ? " lossy" : ""}${page.omittedBytes ? ` omitted=${page.omittedBytes}B` : ""}]`);
105
+ return lines;
106
+ }
107
+ function describeError(error) {
108
+ return `[${error.code}] ${error.message} (retryable=${error.retryable})`;
109
+ }
110
+ function project(result) {
111
+ const lines = [];
112
+ let ok = true;
113
+ switch (result.operation) {
114
+ case "start":
115
+ lines.push(`session ${result.sessionId} ${result.state} transport=${result.transport}` +
116
+ (result.dimensions
117
+ ? ` ${result.dimensions.columns}x${result.dimensions.rows}`
118
+ : "") +
119
+ (result.degradedReason ? ` degraded=${result.degradedReason}` : ""), `artifact=${result.artifact.path}`);
120
+ break;
121
+ case "send":
122
+ lines.push(`seq=${result.inputSequence} delivery=${result.delivery} bytes=${result.deliveredBytes} state=${result.state}`, ...summarizePage(result.page));
123
+ if (result.error) {
124
+ ok = false;
125
+ lines.push(describeError(result.error));
126
+ }
127
+ break;
128
+ case "read":
129
+ lines.push(`state=${result.state}`, ...summarizePage(result.page));
130
+ if (result.error) {
131
+ ok = false;
132
+ lines.push(describeError(result.error));
133
+ }
134
+ break;
135
+ case "status": {
136
+ const session = result.session;
137
+ lines.push(`${session.id} ${session.state} transport=${session.transport} cursor=${session.earliestCursor}..${session.latestCursor}` +
138
+ (session.inputClosed ? " input=closed" : "") +
139
+ (session.terminationReason ? ` reason=${session.terminationReason}` : ""));
140
+ break;
141
+ }
142
+ case "list":
143
+ lines.push(result.sessions.length === 0
144
+ ? "No interactive sessions for this conversation."
145
+ : result.sessions
146
+ .map((session) => `${session.id} ${session.state} transport=${session.transport} latestCursor=${session.latestCursor}`)
147
+ .join("\n"));
148
+ break;
149
+ case "resize":
150
+ lines.push(`${result.sessionId} resized to ${result.dimensions.columns}x${result.dimensions.rows} (${result.state})`);
151
+ break;
152
+ case "close":
153
+ lines.push(`${result.sessionId} ${result.state}` +
154
+ (result.terminationReason ? ` reason=${result.terminationReason}` : "") +
155
+ ` cleanupVerified=${result.cleanupVerified}`);
156
+ if (result.error) {
157
+ ok = false;
158
+ lines.push(describeError(result.error));
159
+ }
160
+ break;
161
+ }
162
+ return { ok, output: lines.filter((line) => line.length > 0).join("\n"), interactiveSession: result };
163
+ }
164
+ function failure(error, operation) {
165
+ const stable = asStableError(error, {
166
+ code: "INVALID_REQUEST",
167
+ operation,
168
+ message: error instanceof Error ? error.message : "Invalid interactive session request.",
169
+ });
170
+ return { ok: false, output: describeError(stable), exitCode: 1 };
171
+ }
172
+ async function guarded(operation, run) {
173
+ try {
174
+ if (!isInteractiveSessionsEnabled(resolveInteractiveSessionConfig())) {
175
+ return {
176
+ ok: false,
177
+ output: "Interactive terminal sessions are disabled. Use shell.exec or shell.start instead.",
178
+ exitCode: 1,
179
+ };
180
+ }
181
+ return project(await run());
182
+ }
183
+ catch (error) {
184
+ return failure(error, operation);
185
+ }
186
+ }
187
+ export function createInteractiveSessionHandlers(manager = interactiveSessionManager) {
188
+ return {
189
+ "terminal.start": async (args, options) => guarded("start", async () => {
190
+ const ownerId = requireOwner(options, "start");
191
+ const mode = optionalString(args, "terminalMode");
192
+ return await manager.start({
193
+ ownerId,
194
+ command: requireString(args, "command"),
195
+ cwd: optionalString(args, "cwd"),
196
+ terminalMode: mode,
197
+ columns: optionalNumber(args, "columns"),
198
+ rows: optionalNumber(args, "rows"),
199
+ idleTimeoutMs: optionalNumber(args, "idleTimeoutMs"),
200
+ lifetimeTimeoutMs: optionalNumber(args, "lifetimeTimeoutMs"),
201
+ deadlineMs: optionalNumber(args, "deadlineMs"),
202
+ signal: options?.signal,
203
+ confirm: confirmPort(options),
204
+ scope: await activeScope(),
205
+ });
206
+ }),
207
+ "terminal.send": async (args, options) => guarded("send", async () => {
208
+ const ownerId = requireOwner(options, "send");
209
+ return await manager.send({
210
+ ownerId,
211
+ id: requireString(args, "id"),
212
+ input: parseInput(args),
213
+ cursor: optionalNumber(args, "cursor"),
214
+ quietMs: optionalNumber(args, "quietMs"),
215
+ deadlineMs: optionalNumber(args, "deadlineMs"),
216
+ view: optionalView(args),
217
+ signal: options?.signal,
218
+ confirm: confirmPort(options),
219
+ scope: await activeScope(),
220
+ });
221
+ }),
222
+ "terminal.read": async (args, options) => guarded("read", async () => {
223
+ const ownerId = requireOwner(options, "read");
224
+ const cursor = optionalNumber(args, "cursor");
225
+ if (cursor === undefined)
226
+ throw new Error("cursor is required");
227
+ return await manager.read({
228
+ ownerId,
229
+ id: requireString(args, "id"),
230
+ cursor,
231
+ waitMs: optionalNumber(args, "waitMs"),
232
+ view: optionalView(args),
233
+ signal: options?.signal,
234
+ });
235
+ }),
236
+ "terminal.status": async (args, options) => guarded("status", () => manager.status({
237
+ ownerId: requireOwner(options, "status"),
238
+ id: requireString(args, "id"),
239
+ })),
240
+ "terminal.list": async (_args, options) => guarded("list", () => manager.list({ ownerId: requireOwner(options, "list") })),
241
+ "terminal.resize": async (args, options) => guarded("resize", async () => {
242
+ const columns = optionalNumber(args, "columns");
243
+ const rows = optionalNumber(args, "rows");
244
+ if (columns === undefined || rows === undefined) {
245
+ throw new Error("columns and rows are required");
246
+ }
247
+ return await manager.resize({
248
+ ownerId: requireOwner(options, "resize"),
249
+ id: requireString(args, "id"),
250
+ columns,
251
+ rows,
252
+ });
253
+ }),
254
+ "terminal.close": async (args, options) => guarded("close", async () => await manager.close({
255
+ ownerId: requireOwner(options, "close"),
256
+ id: requireString(args, "id"),
257
+ deadlineMs: optionalNumber(args, "deadlineMs"),
258
+ })),
259
+ };
260
+ }
261
+ //# sourceMappingURL=interactive-session-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive-session-tools.js","sourceRoot":"","sources":["../../src/tools/interactive-session-tools.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { spawn, execFileSync } from "node:child_process";
1
+ import { spawn } from "node:child_process";
2
2
  import { createWriteStream, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
3
3
  import { mkdir, open, stat } from "node:fs/promises";
4
4
  import { basename, join, sep } from "node:path";
@@ -9,6 +9,7 @@ import { safeCwd } from "../os/cwd.js";
9
9
  import { getJobsDir } from "../store/paths.js";
10
10
  import { resolveShell } from "./shell.js";
11
11
  import { terminateProcessTree } from "../os/process-tree.js";
12
+ import { forgetProcessIdentity, processIdentity } from "../os/process-identity.js";
12
13
  export function formatJobElapsed(job, now = Date.now()) {
13
14
  const startedAt = Date.parse(job.startedAt);
14
15
  const endedAt = job.endedAt ? Date.parse(job.endedAt) : now;
@@ -109,75 +110,6 @@ function processAlive(pid) {
109
110
  return error.code === "EPERM";
110
111
  }
111
112
  }
112
- /** Identity is invariant for a pid's lifetime, so it caches safely. */
113
- const PROCESS_IDENTITY_TTL_MS = 15_000;
114
- const PROCESS_IDENTITY_CACHE_MAX = 512;
115
- const processIdentityCache = new Map();
116
- function readLinuxProcessStart(pid) {
117
- try {
118
- const raw = readFileSync(`/proc/${pid}/stat`, "utf8");
119
- const commEnd = raw.lastIndexOf(")");
120
- if (commEnd < 0)
121
- return undefined;
122
- const fields = raw.slice(commEnd + 2).trim().split(/\s+/);
123
- const startTime = fields[19];
124
- return startTime && startTime.length > 0 ? startTime : undefined;
125
- }
126
- catch {
127
- return undefined;
128
- }
129
- }
130
- function readPsProcessStart(pid) {
131
- try {
132
- // Start time only: the command line is deliberately excluded — `sh -c "cmd"`
133
- // execs into `cmd`, mutating `ps command=` mid-run, which made a live job
134
- // fail its OWN identity check.
135
- return execFileSync("ps", ["-p", String(pid), "-o", "lstart="], {
136
- encoding: "utf8",
137
- timeout: 2_000,
138
- stdio: ["ignore", "pipe", "ignore"],
139
- }).trim();
140
- }
141
- catch {
142
- return undefined;
143
- }
144
- }
145
- function computeProcessIdentity(pid) {
146
- const raw = process.platform === "linux"
147
- ? readLinuxProcessStart(pid)
148
- : readPsProcessStart(pid);
149
- return raw ? createHash("sha256").update(raw).digest("hex") : undefined;
150
- }
151
- function pruneProcessIdentityCache(now) {
152
- if (processIdentityCache.size <= PROCESS_IDENTITY_CACHE_MAX)
153
- return;
154
- for (const [pid, entry] of processIdentityCache) {
155
- if (now - entry.at >= PROCESS_IDENTITY_TTL_MS)
156
- processIdentityCache.delete(pid);
157
- }
158
- if (processIdentityCache.size > PROCESS_IDENTITY_CACHE_MAX) {
159
- processIdentityCache.clear();
160
- }
161
- }
162
- function forgetProcessIdentity(pid) {
163
- if (pid)
164
- processIdentityCache.delete(pid);
165
- }
166
- function processIdentity(pid, options = {}) {
167
- if (!pid || process.platform === "win32")
168
- return undefined;
169
- const now = Date.now();
170
- const cached = processIdentityCache.get(pid);
171
- if (cached &&
172
- !options.refresh &&
173
- now - cached.at < PROCESS_IDENTITY_TTL_MS) {
174
- return cached.value;
175
- }
176
- const value = computeProcessIdentity(pid);
177
- processIdentityCache.set(pid, { value, at: now });
178
- pruneProcessIdentityCache(now);
179
- return value;
180
- }
181
113
  /**
182
114
  * Heuristic for legacy registry rows that were actually agent tool-stall
183
115
  * trackers (commandDisplay = "fs.list /path", "shell.jobs {}", …), not real