@howaboua/pi-codex-conversion 1.5.1-dev.13.d55bc98 → 1.5.2
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.
package/package.json
CHANGED
|
@@ -110,7 +110,7 @@ const renderExecCommandResultWithOptionalContext: any = (
|
|
|
110
110
|
context: ExecCommandRenderContextLike | undefined,
|
|
111
111
|
tracker: ExecCommandTracker,
|
|
112
112
|
) => {
|
|
113
|
-
if (
|
|
113
|
+
if (!options.expanded) {
|
|
114
114
|
return createEmptyResultComponent();
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -140,12 +140,16 @@ export function registerExecCommandTool(pi: ExtensionAPI, tracker: ExecCommandTr
|
|
|
140
140
|
promptSnippet: "Run a command.",
|
|
141
141
|
parameters: EXEC_COMMAND_PARAMETERS,
|
|
142
142
|
prepareArguments: prepareExecCommandArguments,
|
|
143
|
-
async execute(toolCallId, params, signal,
|
|
143
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
144
144
|
if (signal?.aborted) {
|
|
145
145
|
throw new Error("exec_command aborted");
|
|
146
146
|
}
|
|
147
147
|
const typedParams = parseExecCommandParams(params);
|
|
148
|
-
const
|
|
148
|
+
const toToolResult = (partial: UnifiedExecResult) => ({
|
|
149
|
+
content: [{ type: "text" as const, text: formatUnifiedExecResult(partial, typedParams.cmd) }],
|
|
150
|
+
details: partial,
|
|
151
|
+
});
|
|
152
|
+
const result = await sessions.exec(typedParams, ctx.cwd, signal, onUpdate ? (partial) => onUpdate(toToolResult(partial)) : undefined);
|
|
149
153
|
if (result.session_id !== undefined) {
|
|
150
154
|
tracker.recordPersistentSession(toolCallId, result.session_id);
|
|
151
155
|
}
|
|
@@ -56,9 +56,11 @@ interface PtyExecSession extends BaseExecSession {
|
|
|
56
56
|
|
|
57
57
|
type ExecSession = PipeExecSession | PtyExecSession;
|
|
58
58
|
|
|
59
|
+
export type ExecSessionUpdateCallback = (result: UnifiedExecResult) => void;
|
|
60
|
+
|
|
59
61
|
export interface ExecSessionManager {
|
|
60
|
-
exec(input: ExecCommandInput, cwd: string, signal?: AbortSignal): Promise<UnifiedExecResult>;
|
|
61
|
-
write(input: WriteStdinInput): Promise<UnifiedExecResult>;
|
|
62
|
+
exec(input: ExecCommandInput, cwd: string, signal?: AbortSignal, onUpdate?: ExecSessionUpdateCallback): Promise<UnifiedExecResult>;
|
|
63
|
+
write(input: WriteStdinInput, onUpdate?: ExecSessionUpdateCallback): Promise<UnifiedExecResult>;
|
|
62
64
|
hasSession(sessionId: number): boolean;
|
|
63
65
|
getSessionCommand(sessionId: number): string | undefined;
|
|
64
66
|
onSessionExit(listener: (sessionId: number, command: string) => void): () => void;
|
|
@@ -311,10 +313,7 @@ function generateChunkId(): string {
|
|
|
311
313
|
return randomBytes(3).toString("hex");
|
|
312
314
|
}
|
|
313
315
|
|
|
314
|
-
function
|
|
315
|
-
const text =
|
|
316
|
-
session.kind === "pty" ? computePtyDelta(session.emittedBuffer, session.buffer) : session.buffer.slice(session.emittedBuffer.length);
|
|
317
|
-
session.emittedBuffer = session.buffer;
|
|
316
|
+
function truncateOutput(text: string, maxOutputTokens?: number): { output: string; original_token_count?: number } {
|
|
318
317
|
if (text.length === 0) {
|
|
319
318
|
return { output: "" };
|
|
320
319
|
}
|
|
@@ -331,6 +330,24 @@ function consumeOutput(session: ExecSession, maxOutputTokens?: number): { output
|
|
|
331
330
|
};
|
|
332
331
|
}
|
|
333
332
|
|
|
333
|
+
function consumeOutput(session: ExecSession, maxOutputTokens?: number): { output: string; original_token_count?: number } {
|
|
334
|
+
const text =
|
|
335
|
+
session.kind === "pty" ? computePtyDelta(session.emittedBuffer, session.buffer) : session.buffer.slice(session.emittedBuffer.length);
|
|
336
|
+
session.emittedBuffer = session.buffer;
|
|
337
|
+
return truncateOutput(text, maxOutputTokens);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function peekUnconsumedOutput(session: ExecSession, maxOutputTokens?: number): { output: string; original_token_count?: number } {
|
|
341
|
+
const text =
|
|
342
|
+
session.kind === "pty" ? computePtyDelta(session.emittedBuffer, session.buffer) : session.buffer.slice(session.emittedBuffer.length);
|
|
343
|
+
return truncateOutput(text, maxOutputTokens);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function peekOutputSince(session: ExecSession, baseline: string, maxOutputTokens?: number): { output: string; original_token_count?: number } {
|
|
347
|
+
const text = session.kind === "pty" ? computePtyDelta(baseline, session.buffer) : session.buffer.slice(baseline.length);
|
|
348
|
+
return truncateOutput(text, maxOutputTokens);
|
|
349
|
+
}
|
|
350
|
+
|
|
334
351
|
function registerAbortHandler(signal: AbortSignal | undefined, onAbort: () => void): () => void {
|
|
335
352
|
if (!signal) {
|
|
336
353
|
return () => {};
|
|
@@ -398,17 +415,31 @@ export function createExecSessionManager(options: ExecSessionManagerOptions = {}
|
|
|
398
415
|
notify(session);
|
|
399
416
|
}
|
|
400
417
|
|
|
401
|
-
function waitForExitOrTimeout(
|
|
418
|
+
function waitForExitOrTimeout(
|
|
419
|
+
session: ExecSession,
|
|
420
|
+
yieldTimeMs: number,
|
|
421
|
+
onUpdate?: (elapsedMs: number) => void,
|
|
422
|
+
): Promise<number> {
|
|
402
423
|
if (session.exitCode !== undefined && session.exitCode !== null) {
|
|
403
424
|
return Promise.resolve(0);
|
|
404
425
|
}
|
|
405
426
|
|
|
406
427
|
const startedAt = Date.now();
|
|
428
|
+
let updateTimer: ReturnType<typeof setInterval> | undefined;
|
|
429
|
+
let lastUpdateAt = 0;
|
|
407
430
|
return new Promise((resolvePromise) => {
|
|
431
|
+
const emitUpdate = (force = false) => {
|
|
432
|
+
const now = Date.now();
|
|
433
|
+
if (!force && now - lastUpdateAt < 250) return;
|
|
434
|
+
lastUpdateAt = now;
|
|
435
|
+
onUpdate?.(now - startedAt);
|
|
436
|
+
};
|
|
408
437
|
const onWake = () => {
|
|
409
438
|
if (session.exitCode === undefined || session.exitCode === null) {
|
|
439
|
+
emitUpdate();
|
|
410
440
|
return;
|
|
411
441
|
}
|
|
442
|
+
emitUpdate(true);
|
|
412
443
|
cleanup();
|
|
413
444
|
resolvePromise(Date.now() - startedAt);
|
|
414
445
|
};
|
|
@@ -416,8 +447,12 @@ export function createExecSessionManager(options: ExecSessionManagerOptions = {}
|
|
|
416
447
|
cleanup();
|
|
417
448
|
resolvePromise(Date.now() - startedAt);
|
|
418
449
|
}, yieldTimeMs);
|
|
450
|
+
if (onUpdate) {
|
|
451
|
+
updateTimer = setInterval(emitUpdate, 250);
|
|
452
|
+
}
|
|
419
453
|
const cleanup = () => {
|
|
420
454
|
clearTimeout(timeout);
|
|
455
|
+
if (updateTimer) clearInterval(updateTimer);
|
|
421
456
|
session.listeners.delete(onWake);
|
|
422
457
|
};
|
|
423
458
|
session.listeners.add(onWake);
|
|
@@ -445,6 +480,36 @@ export function createExecSessionManager(options: ExecSessionManagerOptions = {}
|
|
|
445
480
|
return result;
|
|
446
481
|
}
|
|
447
482
|
|
|
483
|
+
function makeSnapshotResult(session: ExecSession, waitMs: number, maxOutputTokens?: number, unconsumedOnly = false): UnifiedExecResult {
|
|
484
|
+
const snapshot = unconsumedOnly ? peekUnconsumedOutput(session, maxOutputTokens) : truncateOutput(session.buffer, maxOutputTokens);
|
|
485
|
+
return makeSnapshotFromOutput(session, waitMs, snapshot);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function makeSnapshotSince(session: ExecSession, waitMs: number, baseline: string, maxOutputTokens?: number): UnifiedExecResult {
|
|
489
|
+
return makeSnapshotFromOutput(session, waitMs, peekOutputSince(session, baseline, maxOutputTokens));
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function makeSnapshotFromOutput(
|
|
493
|
+
session: ExecSession,
|
|
494
|
+
waitMs: number,
|
|
495
|
+
snapshot: { output: string; original_token_count?: number },
|
|
496
|
+
): UnifiedExecResult {
|
|
497
|
+
const result: UnifiedExecResult = {
|
|
498
|
+
chunk_id: generateChunkId(),
|
|
499
|
+
wall_time_seconds: waitMs / 1000,
|
|
500
|
+
output: snapshot.output,
|
|
501
|
+
};
|
|
502
|
+
if (snapshot.original_token_count !== undefined) {
|
|
503
|
+
result.original_token_count = snapshot.original_token_count;
|
|
504
|
+
}
|
|
505
|
+
if (session.exitCode === undefined || session.exitCode === null) {
|
|
506
|
+
result.session_id = session.id;
|
|
507
|
+
} else {
|
|
508
|
+
result.exit_code = session.exitCode;
|
|
509
|
+
}
|
|
510
|
+
return result;
|
|
511
|
+
}
|
|
512
|
+
|
|
448
513
|
function createPipeSession(input: ExecCommandInput, workdir: string, shell: string, signal?: AbortSignal): PipeExecSession {
|
|
449
514
|
const login = input.login ?? true;
|
|
450
515
|
const execution = resolveExecution(input.shell, input.cmd);
|
|
@@ -537,7 +602,7 @@ export function createExecSessionManager(options: ExecSessionManagerOptions = {}
|
|
|
537
602
|
}
|
|
538
603
|
|
|
539
604
|
return {
|
|
540
|
-
exec: async (input, cwd, signal) => {
|
|
605
|
+
exec: async (input, cwd, signal, onUpdate) => {
|
|
541
606
|
const shell = resolveShell(input.shell);
|
|
542
607
|
const workdir = resolveWorkdir(cwd, input.workdir);
|
|
543
608
|
const session = input.tty
|
|
@@ -546,17 +611,20 @@ export function createExecSessionManager(options: ExecSessionManagerOptions = {}
|
|
|
546
611
|
sessions.set(session.id, session);
|
|
547
612
|
rememberCommand(session.id, session.command);
|
|
548
613
|
|
|
614
|
+
onUpdate?.(makeSnapshotResult(session, 0, input.max_output_tokens, true));
|
|
549
615
|
const waitedMs = await waitForExitOrTimeout(
|
|
550
616
|
session,
|
|
551
617
|
clampExecYieldTime(input.yield_time_ms, defaultExecYieldTimeMs, session.interactive, minNonInteractiveExecYieldTimeMs),
|
|
618
|
+
onUpdate ? (elapsedMs) => onUpdate(makeSnapshotResult(session, elapsedMs, input.max_output_tokens)) : undefined,
|
|
552
619
|
);
|
|
553
620
|
return makeResult(session, waitedMs, input.max_output_tokens);
|
|
554
621
|
},
|
|
555
|
-
write: async (input) => {
|
|
622
|
+
write: async (input, onUpdate) => {
|
|
556
623
|
const session = sessions.get(input.session_id);
|
|
557
624
|
if (!session) {
|
|
558
625
|
throw new Error(`Unknown process id ${input.session_id}`);
|
|
559
626
|
}
|
|
627
|
+
const updateBaseline = session.buffer;
|
|
560
628
|
if (input.chars && input.chars.length > 0) {
|
|
561
629
|
if (!session.interactive) {
|
|
562
630
|
throw new Error("stdin is closed for this session; rerun exec_command with tty=true to keep stdin open");
|
|
@@ -565,6 +633,7 @@ export function createExecSessionManager(options: ExecSessionManagerOptions = {}
|
|
|
565
633
|
session.child.write(input.chars);
|
|
566
634
|
}
|
|
567
635
|
}
|
|
636
|
+
onUpdate?.(makeSnapshotSince(session, 0, updateBaseline, input.max_output_tokens));
|
|
568
637
|
const waitedMs =
|
|
569
638
|
session.exitCode === undefined
|
|
570
639
|
? await waitForExitOrTimeout(
|
|
@@ -575,6 +644,7 @@ export function createExecSessionManager(options: ExecSessionManagerOptions = {}
|
|
|
575
644
|
!input.chars || input.chars.length === 0,
|
|
576
645
|
minEmptyWriteYieldTimeMs,
|
|
577
646
|
),
|
|
647
|
+
onUpdate ? (elapsedMs) => onUpdate(makeSnapshotSince(session, elapsedMs, updateBaseline, input.max_output_tokens)) : undefined,
|
|
578
648
|
)
|
|
579
649
|
: 0;
|
|
580
650
|
return makeResult(session, waitedMs, input.max_output_tokens);
|
|
@@ -111,12 +111,16 @@ export function registerWriteStdinTool(pi: ExtensionAPI, sessions: ExecSessionMa
|
|
|
111
111
|
description: "Writes to or polls a running exec session.",
|
|
112
112
|
promptSnippet: "Write to an exec session.",
|
|
113
113
|
parameters: WRITE_STDIN_PARAMETERS,
|
|
114
|
-
async execute(_toolCallId, params) {
|
|
114
|
+
async execute(_toolCallId, params, _signal, onUpdate) {
|
|
115
115
|
const typed = parseWriteStdinParams(params);
|
|
116
116
|
const command = sessions.getSessionCommand(typed.session_id);
|
|
117
117
|
let result: UnifiedExecResult;
|
|
118
118
|
try {
|
|
119
|
-
|
|
119
|
+
const toToolResult = (partial: UnifiedExecResult) => ({
|
|
120
|
+
content: [{ type: "text" as const, text: formatUnifiedExecResult(partial, command) }],
|
|
121
|
+
details: partial,
|
|
122
|
+
});
|
|
123
|
+
result = await sessions.write(typed, onUpdate ? (partial) => onUpdate(toToolResult(partial)) : undefined);
|
|
120
124
|
} catch (error) {
|
|
121
125
|
const message = error instanceof Error ? error.message : String(error);
|
|
122
126
|
throw new Error(`write_stdin failed: ${message}`);
|
|
@@ -133,7 +137,7 @@ export function registerWriteStdinTool(pi: ExtensionAPI, sessions: ExecSessionMa
|
|
|
133
137
|
return new Text(renderWriteStdinCall(sessionId, input, command, theme), 0, 0);
|
|
134
138
|
},
|
|
135
139
|
renderResult(result, { expanded, isPartial }, theme) {
|
|
136
|
-
if (
|
|
140
|
+
if (!expanded) return createEmptyResultComponent();
|
|
137
141
|
const state = getResultState(result);
|
|
138
142
|
const output = renderTerminalText(state.output);
|
|
139
143
|
let text = theme.fg("dim", output || "(no output)");
|
|
Binary file
|
|
Binary file
|