@kibitzsh/kibitz 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,441 +1,453 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/core/session-dispatch.ts
31
+ var session_dispatch_exports = {};
32
+ __export(session_dispatch_exports, {
33
+ SessionDispatchService: () => SessionDispatchService,
34
+ buildExistingDispatchCommand: () => buildExistingDispatchCommand,
35
+ buildInteractiveDispatchCommand: () => buildInteractiveDispatchCommand,
36
+ resolveDispatchCommand: () => resolveDispatchCommand
17
37
  });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.SessionDispatchService = void 0;
37
- exports.buildExistingDispatchCommand = buildExistingDispatchCommand;
38
- exports.buildInteractiveDispatchCommand = buildInteractiveDispatchCommand;
39
- exports.resolveDispatchCommand = resolveDispatchCommand;
40
- const events_1 = require("events");
41
- const child_process_1 = require("child_process");
42
- const fs = __importStar(require("fs"));
43
- const path = __importStar(require("path"));
44
- const platform_support_1 = require("./platform-support");
45
- class SessionDispatchService extends events_1.EventEmitter {
46
- options;
47
- constructor(options) {
48
- super();
49
- this.options = options;
38
+ module.exports = __toCommonJS(session_dispatch_exports);
39
+ var import_events = require("events");
40
+ var import_child_process2 = require("child_process");
41
+ var fs2 = __toESM(require("fs"));
42
+ var path2 = __toESM(require("path"));
43
+
44
+ // src/core/platform-support.ts
45
+ var import_child_process = require("child_process");
46
+ var fs = __toESM(require("fs"));
47
+ var path = __toESM(require("path"));
48
+ function getProviderCliCommand(provider, platform = process.platform) {
49
+ return platform === "win32" ? `${provider}.cmd` : provider;
50
+ }
51
+ function resolveCmdNodeScript(cmdPath) {
52
+ if (!String(cmdPath || "").toLowerCase().endsWith(".cmd")) return null;
53
+ try {
54
+ const content = fs.readFileSync(cmdPath, "utf8");
55
+ const match = content.match(/%dp0%\\(.+?\.js)/i);
56
+ if (!match) return null;
57
+ const scriptPath = path.join(path.dirname(cmdPath), match[1]);
58
+ return fs.existsSync(scriptPath) ? scriptPath : null;
59
+ } catch {
60
+ return null;
61
+ }
62
+ }
63
+ function findCommandPath(command, platform = process.platform) {
64
+ try {
65
+ if (platform === "win32") {
66
+ const out2 = (0, import_child_process.execSync)(`where ${command}`, {
67
+ encoding: "utf8",
68
+ timeout: 5e3,
69
+ stdio: ["ignore", "pipe", "ignore"]
70
+ }).trim();
71
+ const first = out2.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
72
+ return first || void 0;
50
73
  }
51
- async dispatch(request) {
52
- const prompt = String(request.prompt || '').trim();
53
- if (!prompt) {
54
- this.emitStatus('failed', request.target, 'Prompt cannot be empty');
55
- return;
56
- }
57
- this.emitStatus('queued', request.target, `Queued for ${describeTarget(request.target)}`);
58
- if (request.target.kind === 'existing') {
59
- await this.dispatchExistingSession(request.target, prompt);
60
- return;
61
- }
62
- const provider = request.target.kind === 'new-codex' ? 'codex' : 'claude';
63
- this.emitStatus('started', request.target, `Starting new ${provider} session`);
64
- try {
65
- if (request.origin === 'vscode') {
66
- if (!this.options.launchInteractiveSession) {
67
- throw new Error('Interactive launcher is not available in VS Code mode');
68
- }
69
- await this.options.launchInteractiveSession(provider, prompt);
70
- }
71
- else {
72
- await runInteractiveInTerminal(provider, prompt);
73
- }
74
- this.emitStatus('sent', request.target, `Started new ${provider} session`);
75
- }
76
- catch (error) {
77
- this.emitStatus('failed', request.target, normalizeError(error));
78
- }
74
+ const out = (0, import_child_process.execFileSync)("which", [command], {
75
+ encoding: "utf8",
76
+ timeout: 5e3,
77
+ stdio: ["ignore", "pipe", "ignore"]
78
+ }).trim();
79
+ return out || void 0;
80
+ } catch {
81
+ return void 0;
82
+ }
83
+ }
84
+
85
+ // src/core/session-dispatch.ts
86
+ var SessionDispatchService = class extends import_events.EventEmitter {
87
+ constructor(options) {
88
+ super();
89
+ this.options = options;
90
+ }
91
+ async dispatch(request) {
92
+ const prompt = String(request.prompt || "").trim();
93
+ if (!prompt) {
94
+ this.emitStatus("failed", request.target, "Prompt cannot be empty");
95
+ return;
79
96
  }
80
- async dispatchExistingSession(target, prompt) {
81
- const targetSessionId = String(target.sessionId || '').trim().toLowerCase();
82
- const targetAgent = target.agent;
83
- if (!targetSessionId || (targetAgent !== 'claude' && targetAgent !== 'codex')) {
84
- this.emitStatus('failed', target, 'Missing target session or provider');
85
- return;
86
- }
87
- const active = this.options.getActiveSessions();
88
- const activeMatch = active.find((session) => {
89
- return session.agent === targetAgent && session.id.toLowerCase() === targetSessionId;
90
- });
91
- if (!activeMatch) {
92
- this.emitStatus('failed', target, `Selected ${describeProvider(targetAgent)} session is not active`);
93
- return;
94
- }
95
- const targetLabel = describeTarget(target);
96
- this.emitStatus('started', target, `Dispatching to ${targetLabel}`);
97
- try {
98
- const command = buildExistingDispatchCommand(target, prompt);
99
- const dispatchCwd = deriveDispatchCwdForSession(activeMatch);
100
- const beforeDispatch = captureSessionFileSnapshot(activeMatch.filePath);
101
- const handle = await startBackgroundCommand(command, { cwd: dispatchCwd });
102
- const dispatchOutcome = await runWithTimeout(waitForDispatchAcknowledgement(activeMatch.filePath, prompt, beforeDispatch, handle.completion), 20_000, 'Dispatch timed out waiting for target session update');
103
- // If process exited before prompt was observed, enforce full verification.
104
- if (dispatchOutcome === 'process-complete') {
105
- verifyExistingDispatchDelivery(activeMatch.filePath, prompt, beforeDispatch);
106
- }
107
- // Keep completion promise observed to avoid unhandled rejections after early ack.
108
- void handle.completion.catch(() => undefined);
109
- this.emitStatus('sent', target, `Prompt sent to ${targetLabel}`);
110
- }
111
- catch (error) {
112
- this.emitStatus('failed', target, normalizeError(error));
113
- }
97
+ this.emitStatus("queued", request.target, `Queued for ${describeTarget(request.target)}`);
98
+ if (request.target.kind === "existing") {
99
+ await this.dispatchExistingSession(request.target, prompt);
100
+ return;
114
101
  }
115
- emitStatus(state, target, message) {
116
- const status = {
117
- state,
118
- message,
119
- target,
120
- timestamp: Date.now(),
121
- };
122
- this.emit('status', status);
102
+ const provider = request.target.kind === "new-codex" ? "codex" : "claude";
103
+ this.emitStatus("started", request.target, `Starting new ${provider} session`);
104
+ try {
105
+ if (request.origin === "vscode") {
106
+ if (!this.options.launchInteractiveSession) {
107
+ throw new Error("Interactive launcher is not available in VS Code mode");
108
+ }
109
+ await this.options.launchInteractiveSession(provider, prompt);
110
+ } else {
111
+ await runInteractiveInTerminal(provider, prompt);
112
+ }
113
+ this.emitStatus("sent", request.target, `Started new ${provider} session`);
114
+ } catch (error) {
115
+ this.emitStatus("failed", request.target, normalizeError(error));
123
116
  }
124
- }
125
- exports.SessionDispatchService = SessionDispatchService;
126
- function buildExistingDispatchCommand(target, prompt, platform = process.platform) {
127
- if (target.kind !== 'existing') {
128
- throw new Error(`Expected existing target, got "${target.kind}"`);
117
+ }
118
+ async dispatchExistingSession(target, prompt) {
119
+ const targetSessionId = String(target.sessionId || "").trim().toLowerCase();
120
+ const targetAgent = target.agent;
121
+ if (!targetSessionId || targetAgent !== "claude" && targetAgent !== "codex") {
122
+ this.emitStatus("failed", target, "Missing target session or provider");
123
+ return;
129
124
  }
130
- const sessionId = String(target.sessionId || '').trim();
131
- const agent = target.agent;
132
- if (!sessionId || (agent !== 'claude' && agent !== 'codex')) {
133
- throw new Error('Missing existing-session target details');
125
+ const active = this.options.getActiveSessions();
126
+ const activeMatch = active.find((session) => {
127
+ return session.agent === targetAgent && session.id.toLowerCase() === targetSessionId;
128
+ });
129
+ if (!activeMatch) {
130
+ this.emitStatus("failed", target, `Selected ${describeProvider(targetAgent)} session is not active`);
131
+ return;
134
132
  }
135
- if (agent === 'codex') {
136
- return {
137
- provider: 'codex',
138
- command: (0, platform_support_1.getProviderCliCommand)('codex', platform),
139
- args: ['exec', 'resume', '--json', '--skip-git-repo-check', sessionId, prompt],
140
- };
133
+ const targetLabel = describeTarget(target);
134
+ this.emitStatus("started", target, `Dispatching to ${targetLabel}`);
135
+ try {
136
+ const command = buildExistingDispatchCommand(target, prompt);
137
+ const dispatchCwd = deriveDispatchCwdForSession(activeMatch);
138
+ const beforeDispatch = captureSessionFileSnapshot(activeMatch.filePath);
139
+ const handle = await startBackgroundCommand(command, { cwd: dispatchCwd });
140
+ const dispatchOutcome = await runWithTimeout(
141
+ waitForDispatchAcknowledgement(
142
+ activeMatch.filePath,
143
+ prompt,
144
+ beforeDispatch,
145
+ handle.completion
146
+ ),
147
+ 2e4,
148
+ "Dispatch timed out waiting for target session update"
149
+ );
150
+ if (dispatchOutcome === "process-complete") {
151
+ verifyExistingDispatchDelivery(activeMatch.filePath, prompt, beforeDispatch);
152
+ }
153
+ void handle.completion.catch(() => void 0);
154
+ this.emitStatus("sent", target, `Prompt sent to ${targetLabel}`);
155
+ } catch (error) {
156
+ this.emitStatus("failed", target, normalizeError(error));
141
157
  }
158
+ }
159
+ emitStatus(state, target, message) {
160
+ const status = {
161
+ state,
162
+ message,
163
+ target,
164
+ timestamp: Date.now()
165
+ };
166
+ this.emit("status", status);
167
+ }
168
+ };
169
+ function buildExistingDispatchCommand(target, prompt, platform = process.platform) {
170
+ if (target.kind !== "existing") {
171
+ throw new Error(`Expected existing target, got "${target.kind}"`);
172
+ }
173
+ const sessionId = String(target.sessionId || "").trim();
174
+ const agent = target.agent;
175
+ if (!sessionId || agent !== "claude" && agent !== "codex") {
176
+ throw new Error("Missing existing-session target details");
177
+ }
178
+ if (agent === "codex") {
142
179
  return {
143
- provider: 'claude',
144
- command: (0, platform_support_1.getProviderCliCommand)('claude', platform),
145
- args: ['-p', prompt, '--verbose', '--output-format', 'stream-json', '--resume', sessionId],
180
+ provider: "codex",
181
+ command: getProviderCliCommand("codex", platform),
182
+ args: ["exec", "resume", "--json", "--skip-git-repo-check", sessionId, prompt]
146
183
  };
184
+ }
185
+ return {
186
+ provider: "claude",
187
+ command: getProviderCliCommand("claude", platform),
188
+ args: ["-p", prompt, "--verbose", "--output-format", "stream-json", "--resume", sessionId]
189
+ };
147
190
  }
148
191
  function buildInteractiveDispatchCommand(provider, prompt, platform = process.platform) {
149
- return {
150
- provider,
151
- command: (0, platform_support_1.getProviderCliCommand)(provider, platform),
152
- args: [prompt],
153
- };
192
+ return {
193
+ provider,
194
+ command: getProviderCliCommand(provider, platform),
195
+ args: [prompt]
196
+ };
154
197
  }
155
198
  function resolveDispatchCommand(command, platform = process.platform) {
156
- const pathHint = (0, platform_support_1.findCommandPath)(command.command, platform);
157
- if (!pathHint) {
158
- throw new Error(`CLI not found: ${command.command}`);
159
- }
160
- if (platform === 'win32') {
161
- const nodeScript = (0, platform_support_1.resolveCmdNodeScript)(pathHint);
162
- if (nodeScript) {
163
- return {
164
- command: process.execPath,
165
- args: [nodeScript, ...command.args],
166
- shell: false,
167
- };
168
- }
169
- if (pathHint.toLowerCase().endsWith('.cmd')) {
170
- return {
171
- command: pathHint,
172
- args: command.args,
173
- shell: true,
174
- };
175
- }
199
+ const pathHint = findCommandPath(command.command, platform);
200
+ if (!pathHint) {
201
+ throw new Error(`CLI not found: ${command.command}`);
202
+ }
203
+ if (platform === "win32") {
204
+ const nodeScript = resolveCmdNodeScript(pathHint);
205
+ if (nodeScript) {
206
+ return {
207
+ command: process.execPath,
208
+ args: [nodeScript, ...command.args],
209
+ shell: false
210
+ };
176
211
  }
177
- return {
212
+ if (pathHint.toLowerCase().endsWith(".cmd")) {
213
+ return {
178
214
  command: pathHint,
179
215
  args: command.args,
180
- shell: false,
181
- };
216
+ shell: true
217
+ };
218
+ }
219
+ }
220
+ return {
221
+ command: pathHint,
222
+ args: command.args,
223
+ shell: false
224
+ };
182
225
  }
183
226
  async function waitForDispatchAcknowledgement(filePath, prompt, before, completion) {
184
- let completionState = 0; // 0: running, 1: success, 2: failed
185
- let completionError = null;
186
- completion.then(() => {
187
- completionState = 1;
188
- }).catch((error) => {
189
- completionState = 2;
190
- completionError = error instanceof Error ? error : new Error(String(error || 'Dispatch failed'));
227
+ let completionState = 0;
228
+ let completionError = null;
229
+ completion.then(() => {
230
+ completionState = 1;
231
+ }).catch((error) => {
232
+ completionState = 2;
233
+ completionError = error instanceof Error ? error : new Error(String(error || "Dispatch failed"));
234
+ });
235
+ while (true) {
236
+ if (completionState === 2) {
237
+ throw completionError || new Error("Dispatch failed");
238
+ }
239
+ if (hasSessionUpdateWithPrompt(filePath, prompt, before)) {
240
+ return "prompt-observed";
241
+ }
242
+ if (completionState === 1) {
243
+ return "process-complete";
244
+ }
245
+ await sleepMs(120);
246
+ }
247
+ }
248
+ async function startBackgroundCommand(command, options = {}) {
249
+ const resolved = resolveDispatchCommand(command);
250
+ return await new Promise((resolve, reject) => {
251
+ const child = (0, import_child_process2.spawn)(resolved.command, resolved.args, {
252
+ stdio: ["ignore", "ignore", "pipe"],
253
+ shell: resolved.shell,
254
+ windowsHide: true,
255
+ ...options.cwd ? { cwd: options.cwd } : {}
191
256
  });
192
- while (true) {
193
- if (completionState === 2) {
194
- throw completionError || new Error('Dispatch failed');
257
+ let stderr = "";
258
+ let spawned = false;
259
+ let launchSettled = false;
260
+ const failLaunch = (error) => {
261
+ if (launchSettled) return;
262
+ launchSettled = true;
263
+ reject(error);
264
+ };
265
+ const completion = new Promise((resolveCompletion, rejectCompletion) => {
266
+ child.on("error", (error) => {
267
+ if (!spawned) {
268
+ failLaunch(error);
269
+ return;
195
270
  }
196
- if (hasSessionUpdateWithPrompt(filePath, prompt, before)) {
197
- return 'prompt-observed';
271
+ rejectCompletion(error);
272
+ });
273
+ child.on("close", (code) => {
274
+ if (code === 0) {
275
+ resolveCompletion();
276
+ return;
198
277
  }
199
- if (completionState === 1) {
200
- return 'process-complete';
278
+ const normalized = String(stderr || "").trim();
279
+ if (looksLikeUnsupportedFlags(normalized)) {
280
+ rejectCompletion(new Error("Provider CLI does not support required resume flags. Update the CLI version."));
281
+ return;
201
282
  }
202
- await sleepMs(120);
203
- }
204
- }
205
- async function startBackgroundCommand(command, options = {}) {
206
- const resolved = resolveDispatchCommand(command);
207
- return await new Promise((resolve, reject) => {
208
- const child = (0, child_process_1.spawn)(resolved.command, resolved.args, {
209
- stdio: ['ignore', 'ignore', 'pipe'],
210
- shell: resolved.shell,
211
- windowsHide: true,
212
- ...(options.cwd ? { cwd: options.cwd } : {}),
213
- });
214
- let stderr = '';
215
- let spawned = false;
216
- let launchSettled = false;
217
- const failLaunch = (error) => {
218
- if (launchSettled)
219
- return;
220
- launchSettled = true;
221
- reject(error);
222
- };
223
- const completion = new Promise((resolveCompletion, rejectCompletion) => {
224
- child.on('error', (error) => {
225
- if (!spawned) {
226
- failLaunch(error);
227
- return;
228
- }
229
- rejectCompletion(error);
230
- });
231
- child.on('close', (code) => {
232
- if (code === 0) {
233
- resolveCompletion();
234
- return;
235
- }
236
- const normalized = String(stderr || '').trim();
237
- if (looksLikeUnsupportedFlags(normalized)) {
238
- rejectCompletion(new Error('Provider CLI does not support required resume flags. Update the CLI version.'));
239
- return;
240
- }
241
- rejectCompletion(new Error(normalized || `Dispatch exited with code ${code}`));
242
- });
243
- });
244
- child.stderr?.on('data', (data) => {
245
- stderr += data.toString();
246
- });
247
- child.on('spawn', () => {
248
- spawned = true;
249
- if (launchSettled)
250
- return;
251
- launchSettled = true;
252
- resolve({ completion });
253
- });
283
+ rejectCompletion(new Error(normalized || `Dispatch exited with code ${code}`));
284
+ });
285
+ });
286
+ child.stderr?.on("data", (data) => {
287
+ stderr += data.toString();
288
+ });
289
+ child.on("spawn", () => {
290
+ spawned = true;
291
+ if (launchSettled) return;
292
+ launchSettled = true;
293
+ resolve({ completion });
254
294
  });
295
+ });
255
296
  }
256
297
  async function runInteractiveInTerminal(provider, prompt) {
257
- const command = buildInteractiveDispatchCommand(provider, prompt);
258
- const resolved = resolveDispatchCommand(command);
259
- await new Promise((resolve, reject) => {
260
- const child = (0, child_process_1.spawn)(resolved.command, resolved.args, {
261
- stdio: 'inherit',
262
- shell: resolved.shell,
263
- windowsHide: false,
264
- });
265
- child.on('error', (error) => reject(error));
266
- child.on('close', (code) => {
267
- if (code === 0)
268
- resolve();
269
- else
270
- reject(new Error(`Interactive session exited with code ${code}`));
271
- });
298
+ const command = buildInteractiveDispatchCommand(provider, prompt);
299
+ const resolved = resolveDispatchCommand(command);
300
+ await new Promise((resolve, reject) => {
301
+ const child = (0, import_child_process2.spawn)(resolved.command, resolved.args, {
302
+ stdio: "inherit",
303
+ shell: resolved.shell,
304
+ windowsHide: false
272
305
  });
306
+ child.on("error", (error) => reject(error));
307
+ child.on("close", (code) => {
308
+ if (code === 0) resolve();
309
+ else reject(new Error(`Interactive session exited with code ${code}`));
310
+ });
311
+ });
273
312
  }
274
313
  function describeTarget(target) {
275
- if (target.kind === 'new-codex')
276
- return 'new codex session';
277
- if (target.kind === 'new-claude')
278
- return 'new claude session';
279
- const provider = describeProvider(target.agent);
280
- const project = cleanTargetLabel(target.projectName, 24);
281
- const sessionTitle = cleanTargetLabel(target.sessionTitle, 44);
282
- if (project && sessionTitle)
283
- return `${provider} session (${project} › ${sessionTitle})`;
284
- if (sessionTitle)
285
- return `${provider} session (${sessionTitle})`;
286
- if (project)
287
- return `${provider} session (${project})`;
288
- return `${provider} session`;
314
+ if (target.kind === "new-codex") return "new codex session";
315
+ if (target.kind === "new-claude") return "new claude session";
316
+ const provider = describeProvider(target.agent);
317
+ const project = cleanTargetLabel(target.projectName, 24);
318
+ const sessionTitle = cleanTargetLabel(target.sessionTitle, 44);
319
+ if (project && sessionTitle) return `${provider} session (${project} \u203A ${sessionTitle})`;
320
+ if (sessionTitle) return `${provider} session (${sessionTitle})`;
321
+ if (project) return `${provider} session (${project})`;
322
+ return `${provider} session`;
289
323
  }
290
324
  function describeProvider(agent) {
291
- return String(agent || '').toLowerCase() === 'claude' ? 'Claude' : 'Codex';
325
+ return String(agent || "").toLowerCase() === "claude" ? "Claude" : "Codex";
292
326
  }
293
327
  function cleanTargetLabel(value, max) {
294
- const text = String(value || '').replace(/\s+/g, ' ').trim();
295
- if (!text)
296
- return '';
297
- if (/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(text))
298
- return '';
299
- if (text.length <= max)
300
- return text;
301
- if (max <= 3)
302
- return text.slice(0, max);
303
- return `${text.slice(0, max - 3).trimEnd()}...`;
328
+ const text = String(value || "").replace(/\s+/g, " ").trim();
329
+ if (!text) return "";
330
+ if (/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(text)) return "";
331
+ if (text.length <= max) return text;
332
+ if (max <= 3) return text.slice(0, max);
333
+ return `${text.slice(0, max - 3).trimEnd()}...`;
304
334
  }
305
335
  function looksLikeUnsupportedFlags(stderr) {
306
- const normalized = String(stderr || '').toLowerCase();
307
- if (!normalized)
308
- return false;
309
- return normalized.includes('unknown option')
310
- || normalized.includes('unknown flag')
311
- || normalized.includes('unrecognized option')
312
- || normalized.includes('did you mean');
336
+ const normalized = String(stderr || "").toLowerCase();
337
+ if (!normalized) return false;
338
+ return normalized.includes("unknown option") || normalized.includes("unknown flag") || normalized.includes("unrecognized option") || normalized.includes("did you mean");
313
339
  }
314
340
  function normalizeError(error) {
315
- if (error instanceof Error && error.message)
316
- return error.message;
317
- return String(error || 'Dispatch failed');
341
+ if (error instanceof Error && error.message) return error.message;
342
+ return String(error || "Dispatch failed");
318
343
  }
319
344
  function captureSessionFileSnapshot(filePath) {
320
- try {
321
- const stat = fs.statSync(filePath);
322
- return {
323
- exists: true,
324
- size: stat.size,
325
- mtimeMs: stat.mtimeMs,
326
- };
327
- }
328
- catch {
329
- return {
330
- exists: false,
331
- size: 0,
332
- mtimeMs: 0,
333
- };
334
- }
345
+ try {
346
+ const stat = fs2.statSync(filePath);
347
+ return {
348
+ exists: true,
349
+ size: stat.size,
350
+ mtimeMs: stat.mtimeMs
351
+ };
352
+ } catch {
353
+ return {
354
+ exists: false,
355
+ size: 0,
356
+ mtimeMs: 0
357
+ };
358
+ }
335
359
  }
336
360
  function verifyExistingDispatchDelivery(filePath, prompt, before) {
337
- if (!hasSessionFileChanged(filePath, before)) {
338
- throw new Error('Target session did not update after dispatch');
339
- }
340
- const promptSignature = firstPromptSignature(prompt);
341
- if (promptSignature.length < 4)
342
- return;
343
- const tail = readSessionTailSinceOffset(filePath, before.size);
344
- if (!tail) {
345
- throw new Error('Target session updated but prompt text was not found');
346
- }
347
- if (!tail.toLowerCase().includes(promptSignature.toLowerCase())) {
348
- throw new Error('Prompt text was not found in target session update');
349
- }
361
+ if (!hasSessionFileChanged(filePath, before)) {
362
+ throw new Error("Target session did not update after dispatch");
363
+ }
364
+ const promptSignature = firstPromptSignature(prompt);
365
+ if (promptSignature.length < 4) return;
366
+ const tail = readSessionTailSinceOffset(filePath, before.size);
367
+ if (!tail) {
368
+ throw new Error("Target session updated but prompt text was not found");
369
+ }
370
+ if (!tail.toLowerCase().includes(promptSignature.toLowerCase())) {
371
+ throw new Error("Prompt text was not found in target session update");
372
+ }
350
373
  }
351
374
  function hasSessionUpdateWithPrompt(filePath, prompt, before) {
352
- if (!hasSessionFileChanged(filePath, before))
353
- return false;
354
- const promptSignature = firstPromptSignature(prompt);
355
- if (promptSignature.length < 4)
356
- return true;
357
- const tail = readSessionTailSinceOffset(filePath, before.size);
358
- if (!tail)
359
- return false;
360
- return tail.toLowerCase().includes(promptSignature.toLowerCase());
375
+ if (!hasSessionFileChanged(filePath, before)) return false;
376
+ const promptSignature = firstPromptSignature(prompt);
377
+ if (promptSignature.length < 4) return true;
378
+ const tail = readSessionTailSinceOffset(filePath, before.size);
379
+ if (!tail) return false;
380
+ return tail.toLowerCase().includes(promptSignature.toLowerCase());
361
381
  }
362
382
  function hasSessionFileChanged(filePath, before) {
363
- const after = captureSessionFileSnapshot(filePath);
364
- if (!after.exists) {
365
- throw new Error('Target session file is not accessible after dispatch');
366
- }
367
- return after.size > before.size || after.mtimeMs > before.mtimeMs;
383
+ const after = captureSessionFileSnapshot(filePath);
384
+ if (!after.exists) {
385
+ throw new Error("Target session file is not accessible after dispatch");
386
+ }
387
+ return after.size > before.size || after.mtimeMs > before.mtimeMs;
368
388
  }
369
389
  function firstPromptSignature(prompt) {
370
- const lines = String(prompt || '')
371
- .split(/\r?\n/g)
372
- .map((line) => line.trim())
373
- .filter(Boolean);
374
- const first = lines[0] || String(prompt || '').trim();
375
- return first.slice(0, 160);
390
+ const lines = String(prompt || "").split(/\r?\n/g).map((line) => line.trim()).filter(Boolean);
391
+ const first = lines[0] || String(prompt || "").trim();
392
+ return first.slice(0, 160);
376
393
  }
377
394
  function readSessionTailSinceOffset(filePath, previousSize) {
395
+ try {
396
+ const stat = fs2.statSync(filePath);
397
+ const maxBytes = 1024 * 512;
398
+ const start = Math.max(0, previousSize - 2048);
399
+ const desiredStart = stat.size - start > maxBytes ? Math.max(0, stat.size - maxBytes) : start;
400
+ const length = Math.max(0, stat.size - desiredStart);
401
+ if (length === 0) return "";
402
+ const fd = fs2.openSync(filePath, "r");
378
403
  try {
379
- const stat = fs.statSync(filePath);
380
- const maxBytes = 1024 * 512;
381
- const start = Math.max(0, previousSize - 2048);
382
- const desiredStart = stat.size - start > maxBytes
383
- ? Math.max(0, stat.size - maxBytes)
384
- : start;
385
- const length = Math.max(0, stat.size - desiredStart);
386
- if (length === 0)
387
- return '';
388
- const fd = fs.openSync(filePath, 'r');
389
- try {
390
- const buf = Buffer.alloc(length);
391
- fs.readSync(fd, buf, 0, length, desiredStart);
392
- return buf.toString('utf8');
393
- }
394
- finally {
395
- fs.closeSync(fd);
396
- }
397
- }
398
- catch {
399
- return '';
404
+ const buf = Buffer.alloc(length);
405
+ fs2.readSync(fd, buf, 0, length, desiredStart);
406
+ return buf.toString("utf8");
407
+ } finally {
408
+ fs2.closeSync(fd);
400
409
  }
410
+ } catch {
411
+ return "";
412
+ }
401
413
  }
402
414
  async function runWithTimeout(promise, timeoutMs, message) {
403
- return await new Promise((resolve, reject) => {
404
- const timer = setTimeout(() => reject(new Error(message)), timeoutMs);
405
- promise.then((value) => {
406
- clearTimeout(timer);
407
- resolve(value);
408
- }).catch((error) => {
409
- clearTimeout(timer);
410
- reject(error);
411
- });
415
+ return await new Promise((resolve, reject) => {
416
+ const timer = setTimeout(() => reject(new Error(message)), timeoutMs);
417
+ promise.then((value) => {
418
+ clearTimeout(timer);
419
+ resolve(value);
420
+ }).catch((error) => {
421
+ clearTimeout(timer);
422
+ reject(error);
412
423
  });
424
+ });
413
425
  }
414
426
  async function sleepMs(ms) {
415
- await new Promise((resolve) => setTimeout(resolve, ms));
427
+ await new Promise((resolve) => setTimeout(resolve, ms));
416
428
  }
417
429
  function deriveDispatchCwdForSession(session) {
418
- if (session.agent !== 'claude')
419
- return undefined;
420
- return decodeClaudeProjectPathFromSessionFile(session.filePath);
430
+ if (session.agent !== "claude") return void 0;
431
+ return decodeClaudeProjectPathFromSessionFile(session.filePath);
421
432
  }
422
433
  function decodeClaudeProjectPathFromSessionFile(filePath) {
423
- if (!filePath)
424
- return undefined;
425
- const projectDir = path.basename(path.dirname(filePath));
426
- if (!projectDir)
427
- return undefined;
428
- const parts = projectDir.split('-').filter(Boolean);
429
- if (parts.length === 0)
430
- return undefined;
431
- if (parts.length >= 2 && /^[A-Za-z]$/.test(parts[0])) {
432
- const windowsPath = `${parts[0]}:\\${parts.slice(1).join('\\')}`;
433
- if (fs.existsSync(windowsPath))
434
- return windowsPath;
435
- }
436
- const unixPath = `/${parts.join('/')}`;
437
- if (fs.existsSync(unixPath))
438
- return unixPath;
439
- return undefined;
434
+ if (!filePath) return void 0;
435
+ const projectDir = path2.basename(path2.dirname(filePath));
436
+ if (!projectDir) return void 0;
437
+ const parts = projectDir.split("-").filter(Boolean);
438
+ if (parts.length === 0) return void 0;
439
+ if (parts.length >= 2 && /^[A-Za-z]$/.test(parts[0])) {
440
+ const windowsPath = `${parts[0]}:\\${parts.slice(1).join("\\")}`;
441
+ if (fs2.existsSync(windowsPath)) return windowsPath;
442
+ }
443
+ const unixPath = `/${parts.join("/")}`;
444
+ if (fs2.existsSync(unixPath)) return unixPath;
445
+ return void 0;
440
446
  }
441
- //# sourceMappingURL=session-dispatch.js.map
447
+ // Annotate the CommonJS export names for ESM import in node:
448
+ 0 && (module.exports = {
449
+ SessionDispatchService,
450
+ buildExistingDispatchCommand,
451
+ buildInteractiveDispatchCommand,
452
+ resolveDispatchCommand
453
+ });