@oh-my-pi/pi-coding-agent 6.7.67 → 6.8.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.
- package/CHANGELOG.md +28 -0
- package/package.json +6 -7
- package/src/cli/session-picker.ts +27 -28
- package/src/cli/setup-cli.ts +7 -16
- package/src/cli/update-cli.ts +1 -1
- package/src/config.ts +1 -1
- package/src/core/agent-session.ts +202 -37
- package/src/core/agent-storage.ts +1 -1
- package/src/core/auth-storage.ts +15 -25
- package/src/core/bash-executor.ts +63 -105
- package/src/core/custom-commands/loader.ts +1 -1
- package/src/core/custom-tools/loader.ts +1 -1
- package/src/core/custom-tools/types.ts +1 -2
- package/src/core/exec.ts +16 -100
- package/src/core/extensions/index.ts +1 -7
- package/src/core/extensions/loader.ts +1 -1
- package/src/core/extensions/runner.ts +1 -1
- package/src/core/extensions/types.ts +2 -2
- package/src/core/extensions/wrapper.ts +15 -20
- package/src/core/frontmatter.ts +1 -1
- package/src/core/history-storage.ts +3 -6
- package/src/core/hooks/index.ts +2 -2
- package/src/core/hooks/loader.ts +1 -1
- package/src/core/hooks/tool-wrapper.ts +14 -26
- package/src/core/hooks/types.ts +1 -2
- package/src/core/keybindings.ts +1 -1
- package/src/core/mcp/client.ts +13 -13
- package/src/core/mcp/json-rpc.ts +1 -1
- package/src/core/mcp/loader.ts +1 -1
- package/src/core/mcp/manager.ts +2 -2
- package/src/core/mcp/tool-cache.ts +1 -1
- package/src/core/mcp/transports/http.ts +32 -70
- package/src/core/model-registry.ts +1 -1
- package/src/core/plugins/installer.ts +13 -11
- package/src/core/prompt-templates.ts +4 -9
- package/src/core/python-executor.ts +23 -18
- package/src/core/python-gateway-coordinator.ts +29 -28
- package/src/core/python-kernel.ts +230 -211
- package/src/core/sdk.ts +10 -13
- package/src/core/session-manager.ts +1 -1
- package/src/core/settings-manager.ts +22 -9
- package/src/core/skills.ts +1 -1
- package/src/core/ssh/connection-manager.ts +19 -33
- package/src/core/ssh/ssh-executor.ts +39 -35
- package/src/core/ssh/sshfs-mount.ts +14 -33
- package/src/core/storage-migration.ts +1 -1
- package/src/core/streaming-output.ts +183 -127
- package/src/core/system-prompt.ts +119 -79
- package/src/core/title-generator.ts +1 -1
- package/src/core/tools/ask.ts +2 -2
- package/src/core/tools/bash.ts +3 -3
- package/src/core/tools/calculator.ts +1 -1
- package/src/core/tools/exa/mcp-client.ts +1 -1
- package/src/core/tools/exa/render.ts +1 -1
- package/src/core/tools/find.ts +39 -71
- package/src/core/tools/gemini-image.ts +1 -1
- package/src/core/tools/grep.ts +88 -100
- package/src/core/tools/index.ts +1 -1
- package/src/core/tools/ls.ts +1 -1
- package/src/core/tools/lsp/client.ts +50 -50
- package/src/core/tools/lsp/clients/lsp-linter-client.ts +1 -1
- package/src/core/tools/lsp/config.ts +1 -1
- package/src/core/tools/lsp/index.ts +2 -4
- package/src/core/tools/lsp/lspmux.ts +1 -1
- package/src/core/tools/lsp/rust-analyzer.ts +2 -2
- package/src/core/tools/lsp/utils.ts +0 -14
- package/src/core/tools/notebook.ts +1 -1
- package/src/core/tools/patch/shared.ts +3 -4
- package/src/core/tools/python.ts +3 -3
- package/src/core/tools/read.ts +29 -68
- package/src/core/tools/render-utils.ts +0 -5
- package/src/core/tools/ssh.ts +3 -3
- package/src/core/tools/task/model-resolver.ts +7 -9
- package/src/core/tools/task/worker.ts +144 -139
- package/src/core/tools/todo-write.ts +1 -1
- package/src/core/tools/truncate.ts +2 -2
- package/src/core/tools/web-fetch.ts +13 -15
- package/src/core/tools/web-scrapers/types.ts +1 -3
- package/src/core/tools/web-scrapers/utils.ts +14 -13
- package/src/core/tools/web-scrapers/youtube.ts +39 -12
- package/src/core/tools/web-search/auth.ts +9 -45
- package/src/core/tools/write.ts +1 -1
- package/src/core/ttsr.ts +1 -1
- package/src/core/utils.ts +1 -187
- package/src/core/voice-controller.ts +1 -1
- package/src/core/voice-supervisor.ts +11 -38
- package/src/core/voice.ts +1 -8
- package/src/discovery/codex.ts +1 -1
- package/src/index.ts +4 -4
- package/src/main.ts +5 -10
- package/src/migrations.ts +1 -1
- package/src/modes/index.ts +7 -40
- package/src/modes/interactive/components/extensions/state-manager.ts +1 -1
- package/src/modes/interactive/components/hook-editor.ts +12 -9
- package/src/modes/interactive/components/login-dialog.ts +24 -11
- package/src/modes/interactive/components/settings-defs.ts +9 -0
- package/src/modes/interactive/components/status-line.ts +36 -35
- package/src/modes/interactive/components/todo-display.ts +1 -1
- package/src/modes/interactive/components/tool-execution.ts +1 -1
- package/src/modes/interactive/controllers/command-controller.ts +50 -84
- package/src/modes/interactive/controllers/extension-ui-controller.ts +76 -76
- package/src/modes/interactive/controllers/input-controller.ts +12 -11
- package/src/modes/interactive/interactive-mode.ts +10 -11
- package/src/modes/interactive/theme/theme.ts +1 -1
- package/src/modes/interactive/types.ts +1 -1
- package/src/modes/rpc/rpc-client.ts +91 -121
- package/src/modes/rpc/rpc-mode.ts +71 -79
- package/src/prompts/system/ttsr-interrupt.md +7 -0
- package/src/utils/clipboard.ts +57 -141
- package/src/utils/shell-snapshot.ts +12 -60
- package/src/utils/shell.ts +35 -56
- package/src/utils/tools-manager.ts +42 -71
- package/src/core/logger.ts +0 -111
- package/src/modes/cleanup.ts +0 -23
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { createServer } from "node:net";
|
|
2
2
|
import { delimiter, join } from "node:path";
|
|
3
|
-
import
|
|
3
|
+
import { logger } from "@oh-my-pi/pi-utils";
|
|
4
|
+
import { $, type Subprocess } from "bun";
|
|
4
5
|
import { nanoid } from "nanoid";
|
|
5
6
|
import { getShellConfig, killProcessTree } from "../utils/shell";
|
|
6
7
|
import { getOrCreateSnapshot } from "../utils/shell-snapshot";
|
|
7
|
-
import { logger } from "./logger";
|
|
8
8
|
import { acquireSharedGateway, releaseSharedGateway } from "./python-gateway-coordinator";
|
|
9
9
|
import { loadPythonModules } from "./python-modules";
|
|
10
10
|
import { PYTHON_PRELUDE } from "./python-prelude";
|
|
11
11
|
import { htmlToBasicMarkdown } from "./tools/web-scrapers/types";
|
|
12
|
-
import { ScopeSignal } from "./utils";
|
|
13
12
|
|
|
14
13
|
const TEXT_ENCODER = new TextEncoder();
|
|
15
14
|
const TEXT_DECODER = new TextDecoder();
|
|
@@ -285,14 +284,9 @@ export async function checkPythonKernelAvailability(cwd: string): Promise<Python
|
|
|
285
284
|
const { env } = await getShellConfig();
|
|
286
285
|
const baseEnv = filterEnv(env);
|
|
287
286
|
const runtime = await resolvePythonRuntime(cwd, baseEnv);
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
"-c",
|
|
292
|
-
"import importlib.util,sys;sys.exit(0 if importlib.util.find_spec('kernel_gateway') and importlib.util.find_spec('ipykernel') else 1)",
|
|
293
|
-
],
|
|
294
|
-
{ cwd, env: runtime.env, stdin: "ignore", stdout: "pipe", stderr: "pipe" },
|
|
295
|
-
);
|
|
287
|
+
const checkScript =
|
|
288
|
+
"import importlib.util,sys;sys.exit(0 if importlib.util.find_spec('kernel_gateway') and importlib.util.find_spec('ipykernel') else 1)";
|
|
289
|
+
const result = await $`${runtime.pythonPath} -c ${checkScript}`.quiet().nothrow().cwd(cwd).env(runtime.env);
|
|
296
290
|
if (result.exitCode === 0) {
|
|
297
291
|
return { ok: true, pythonPath: runtime.pythonPath };
|
|
298
292
|
}
|
|
@@ -354,27 +348,28 @@ async function checkExternalGatewayAvailability(config: ExternalGatewayConfig):
|
|
|
354
348
|
}
|
|
355
349
|
|
|
356
350
|
async function allocatePort(): Promise<number> {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
});
|
|
351
|
+
const { promise, resolve, reject } = Promise.withResolvers<number>();
|
|
352
|
+
const server = createServer();
|
|
353
|
+
server.unref();
|
|
354
|
+
server.on("error", reject);
|
|
355
|
+
server.listen(0, "127.0.0.1", () => {
|
|
356
|
+
const address = server.address();
|
|
357
|
+
if (address && typeof address === "object") {
|
|
358
|
+
const port = address.port;
|
|
359
|
+
server.close((err: Error | null | undefined) => {
|
|
360
|
+
if (err) {
|
|
361
|
+
reject(err);
|
|
362
|
+
} else {
|
|
363
|
+
resolve(port);
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
} else {
|
|
367
|
+
server.close();
|
|
368
|
+
reject(new Error("Failed to allocate port"));
|
|
369
|
+
}
|
|
377
370
|
});
|
|
371
|
+
|
|
372
|
+
return promise;
|
|
378
373
|
}
|
|
379
374
|
|
|
380
375
|
function normalizeDisplayText(text: string): string {
|
|
@@ -681,7 +676,7 @@ export class PythonKernel {
|
|
|
681
676
|
|
|
682
677
|
if (gatewayProcess && gatewayUrl) break;
|
|
683
678
|
|
|
684
|
-
killProcessTree(candidateProcess.pid);
|
|
679
|
+
await killProcessTree(candidateProcess.pid);
|
|
685
680
|
lastError = exited ? "Kernel gateway process exited during startup" : "Kernel gateway failed to start";
|
|
686
681
|
}
|
|
687
682
|
|
|
@@ -696,7 +691,7 @@ export class PythonKernel {
|
|
|
696
691
|
});
|
|
697
692
|
|
|
698
693
|
if (!createResponse.ok) {
|
|
699
|
-
killProcessTree(gatewayProcess.pid);
|
|
694
|
+
await killProcessTree(gatewayProcess.pid);
|
|
700
695
|
throw new Error(`Failed to create kernel: ${await createResponse.text()}`);
|
|
701
696
|
}
|
|
702
697
|
|
|
@@ -727,83 +722,84 @@ export class PythonKernel {
|
|
|
727
722
|
wsUrl += `?token=${encodeURIComponent(this.#authToken)}`;
|
|
728
723
|
}
|
|
729
724
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
725
|
+
const { promise, resolve, reject } = Promise.withResolvers<void>();
|
|
726
|
+
const ws = new WebSocket(wsUrl);
|
|
727
|
+
ws.binaryType = "arraybuffer";
|
|
728
|
+
let settled = false;
|
|
734
729
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
730
|
+
const timeout = setTimeout(() => {
|
|
731
|
+
ws.close();
|
|
732
|
+
if (!settled) {
|
|
733
|
+
settled = true;
|
|
734
|
+
reject(new Error("WebSocket connection timeout"));
|
|
735
|
+
}
|
|
736
|
+
}, 10000);
|
|
742
737
|
|
|
743
|
-
|
|
744
|
-
|
|
738
|
+
ws.onopen = () => {
|
|
739
|
+
if (settled) return;
|
|
740
|
+
settled = true;
|
|
741
|
+
clearTimeout(timeout);
|
|
742
|
+
this.#ws = ws;
|
|
743
|
+
resolve();
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
ws.onerror = (event) => {
|
|
747
|
+
const error = new Error(`WebSocket error: ${event}`);
|
|
748
|
+
if (!settled) {
|
|
745
749
|
settled = true;
|
|
746
750
|
clearTimeout(timeout);
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
}
|
|
751
|
+
reject(error);
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
this.#alive = false;
|
|
755
|
+
this.#ws = null;
|
|
756
|
+
this.abortPendingExecutions(error.message);
|
|
757
|
+
};
|
|
750
758
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
};
|
|
759
|
+
ws.onclose = () => {
|
|
760
|
+
this.#alive = false;
|
|
761
|
+
this.#ws = null;
|
|
762
|
+
if (!settled) {
|
|
763
|
+
settled = true;
|
|
764
|
+
clearTimeout(timeout);
|
|
765
|
+
reject(new Error("WebSocket closed before connection"));
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
this.abortPendingExecutions("WebSocket closed");
|
|
769
|
+
};
|
|
763
770
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
+
ws.onmessage = (event) => {
|
|
772
|
+
let msg: JupyterMessage | null = null;
|
|
773
|
+
if (event.data instanceof ArrayBuffer) {
|
|
774
|
+
msg = deserializeWebSocketMessage(event.data);
|
|
775
|
+
} else if (typeof event.data === "string") {
|
|
776
|
+
try {
|
|
777
|
+
msg = JSON.parse(event.data) as JupyterMessage;
|
|
778
|
+
} catch {
|
|
771
779
|
return;
|
|
772
780
|
}
|
|
773
|
-
|
|
774
|
-
|
|
781
|
+
}
|
|
782
|
+
if (!msg) return;
|
|
775
783
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
msg = deserializeWebSocketMessage(event.data);
|
|
780
|
-
} else if (typeof event.data === "string") {
|
|
781
|
-
try {
|
|
782
|
-
msg = JSON.parse(event.data) as JupyterMessage;
|
|
783
|
-
} catch {
|
|
784
|
-
return;
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
if (!msg) return;
|
|
784
|
+
if (TRACE_IPC) {
|
|
785
|
+
logger.debug("Kernel IPC recv", { channel: msg.channel, msgType: msg.header.msg_type });
|
|
786
|
+
}
|
|
788
787
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
788
|
+
const parentId = (msg.parent_header as { msg_id?: string }).msg_id;
|
|
789
|
+
if (parentId) {
|
|
790
|
+
const handler = this.#messageHandlers.get(parentId);
|
|
791
|
+
if (handler) handler(msg);
|
|
792
|
+
}
|
|
792
793
|
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
794
|
+
const channelHandlers = this.#channelHandlers.get(msg.channel);
|
|
795
|
+
if (channelHandlers) {
|
|
796
|
+
for (const handler of channelHandlers) {
|
|
797
|
+
handler(msg);
|
|
797
798
|
}
|
|
799
|
+
}
|
|
800
|
+
};
|
|
798
801
|
|
|
799
|
-
|
|
800
|
-
if (channelHandlers) {
|
|
801
|
-
for (const handler of channelHandlers) {
|
|
802
|
-
handler(msg);
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
};
|
|
806
|
-
});
|
|
802
|
+
return promise;
|
|
807
803
|
}
|
|
808
804
|
|
|
809
805
|
private abortPendingExecutions(reason: string): void {
|
|
@@ -857,140 +853,163 @@ export class PythonKernel {
|
|
|
857
853
|
let cancelled = false;
|
|
858
854
|
let timedOut = false;
|
|
859
855
|
|
|
860
|
-
const
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
856
|
+
const controller = new AbortController();
|
|
857
|
+
const onAbort = () => {
|
|
858
|
+
controller.abort(options?.signal?.reason ?? new Error("Aborted"));
|
|
859
|
+
};
|
|
860
|
+
if (options?.signal) {
|
|
861
|
+
if (options.signal.aborted) {
|
|
862
|
+
onAbort();
|
|
863
|
+
} else {
|
|
864
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
const timeoutId =
|
|
868
|
+
typeof options?.timeoutMs === "number" && options.timeoutMs > 0
|
|
869
|
+
? setTimeout(() => {
|
|
870
|
+
timedOut = true;
|
|
871
|
+
controller.abort(new Error("Timeout"));
|
|
872
|
+
}, options.timeoutMs)
|
|
873
|
+
: undefined;
|
|
874
|
+
|
|
875
|
+
const { promise, resolve } = Promise.withResolvers<KernelExecuteResult>();
|
|
876
|
+
|
|
877
|
+
let resolved = false;
|
|
878
|
+
const finalize = () => {
|
|
879
|
+
if (resolved) return;
|
|
880
|
+
resolved = true;
|
|
881
|
+
this.#messageHandlers.delete(msgId);
|
|
882
|
+
this.#pendingExecutions.delete(msgId);
|
|
883
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
884
|
+
if (options?.signal) {
|
|
885
|
+
options.signal.removeEventListener("abort", onAbort);
|
|
886
|
+
}
|
|
887
|
+
resolve({ status, executionCount, error, cancelled, timedOut, stdinRequested });
|
|
888
|
+
};
|
|
878
889
|
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
cancelled = true;
|
|
882
|
-
timedOut = false;
|
|
883
|
-
if (options?.onChunk) {
|
|
884
|
-
void options.onChunk(`[kernel] ${reason}\n`);
|
|
885
|
-
}
|
|
890
|
+
const checkDone = () => {
|
|
891
|
+
if (replyReceived && idleReceived) {
|
|
886
892
|
finalize();
|
|
887
|
-
}
|
|
893
|
+
}
|
|
894
|
+
};
|
|
888
895
|
|
|
889
|
-
|
|
896
|
+
const cancelFromClose = (reason: string) => {
|
|
897
|
+
if (resolved) return;
|
|
898
|
+
cancelled = true;
|
|
899
|
+
timedOut = false;
|
|
900
|
+
if (options?.onChunk) {
|
|
901
|
+
void options.onChunk(`[kernel] ${reason}\n`);
|
|
902
|
+
}
|
|
903
|
+
finalize();
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
this.#pendingExecutions.set(msgId, cancelFromClose);
|
|
890
907
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
908
|
+
const onExecutionAbort = () => {
|
|
909
|
+
cancelled = true;
|
|
910
|
+
void (async () => {
|
|
894
911
|
try {
|
|
895
912
|
await this.interrupt();
|
|
896
913
|
} finally {
|
|
897
914
|
finalize();
|
|
898
915
|
}
|
|
899
|
-
});
|
|
916
|
+
})();
|
|
917
|
+
};
|
|
918
|
+
controller.signal.addEventListener("abort", onExecutionAbort, { once: true });
|
|
900
919
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
920
|
+
if (controller.signal.aborted) {
|
|
921
|
+
cancelFromClose("Execution aborted");
|
|
922
|
+
return promise;
|
|
923
|
+
}
|
|
905
924
|
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
}
|
|
915
|
-
checkDone();
|
|
916
|
-
break;
|
|
925
|
+
this.#messageHandlers.set(msgId, async (response) => {
|
|
926
|
+
switch (response.header.msg_type) {
|
|
927
|
+
case "execute_reply": {
|
|
928
|
+
replyReceived = true;
|
|
929
|
+
const replyStatus = response.content.status;
|
|
930
|
+
status = replyStatus === "error" ? "error" : "ok";
|
|
931
|
+
if (typeof response.content.execution_count === "number") {
|
|
932
|
+
executionCount = response.content.execution_count;
|
|
917
933
|
}
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
934
|
+
checkDone();
|
|
935
|
+
break;
|
|
936
|
+
}
|
|
937
|
+
case "stream": {
|
|
938
|
+
const text = String(response.content.text ?? "");
|
|
939
|
+
if (text && options?.onChunk) {
|
|
940
|
+
await options.onChunk(text);
|
|
924
941
|
}
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
for (const output of outputs) {
|
|
933
|
-
await options.onDisplay(output);
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
break;
|
|
942
|
+
break;
|
|
943
|
+
}
|
|
944
|
+
case "execute_result":
|
|
945
|
+
case "display_data": {
|
|
946
|
+
const { text, outputs } = this.renderDisplay(response.content);
|
|
947
|
+
if (text && options?.onChunk) {
|
|
948
|
+
await options.onChunk(text);
|
|
937
949
|
}
|
|
938
|
-
|
|
939
|
-
const
|
|
940
|
-
|
|
941
|
-
: [];
|
|
942
|
-
error = {
|
|
943
|
-
name: String(response.content.ename ?? "Error"),
|
|
944
|
-
value: String(response.content.evalue ?? ""),
|
|
945
|
-
traceback,
|
|
946
|
-
};
|
|
947
|
-
const text = traceback.length > 0 ? `${traceback.join("\n")}\n` : `${error.name}: ${error.value}\n`;
|
|
948
|
-
if (options?.onChunk) {
|
|
949
|
-
await options.onChunk(text);
|
|
950
|
+
if (outputs.length > 0 && options?.onDisplay) {
|
|
951
|
+
for (const output of outputs) {
|
|
952
|
+
await options.onDisplay(output);
|
|
950
953
|
}
|
|
951
|
-
break;
|
|
952
954
|
}
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
955
|
+
break;
|
|
956
|
+
}
|
|
957
|
+
case "error": {
|
|
958
|
+
const traceback = Array.isArray(response.content.traceback)
|
|
959
|
+
? response.content.traceback.map((line: unknown) => String(line))
|
|
960
|
+
: [];
|
|
961
|
+
error = {
|
|
962
|
+
name: String(response.content.ename ?? "Error"),
|
|
963
|
+
value: String(response.content.evalue ?? ""),
|
|
964
|
+
traceback,
|
|
965
|
+
};
|
|
966
|
+
const text = traceback.length > 0 ? `${traceback.join("\n")}\n` : `${error.name}: ${error.value}\n`;
|
|
967
|
+
if (options?.onChunk) {
|
|
968
|
+
await options.onChunk(text);
|
|
960
969
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
this.sendMessage({
|
|
969
|
-
channel: "stdin",
|
|
970
|
-
header: {
|
|
971
|
-
msg_id: nanoid(),
|
|
972
|
-
session: this.sessionId,
|
|
973
|
-
username: this.username,
|
|
974
|
-
date: new Date().toISOString(),
|
|
975
|
-
msg_type: "input_reply",
|
|
976
|
-
version: "5.5",
|
|
977
|
-
},
|
|
978
|
-
parent_header: response.header as unknown as Record<string, unknown>,
|
|
979
|
-
metadata: {},
|
|
980
|
-
content: { value: "" },
|
|
981
|
-
});
|
|
982
|
-
break;
|
|
970
|
+
break;
|
|
971
|
+
}
|
|
972
|
+
case "status": {
|
|
973
|
+
const state = response.content.execution_state;
|
|
974
|
+
if (state === "idle") {
|
|
975
|
+
idleReceived = true;
|
|
976
|
+
checkDone();
|
|
983
977
|
}
|
|
978
|
+
break;
|
|
979
|
+
}
|
|
980
|
+
case "input_request": {
|
|
981
|
+
stdinRequested = true;
|
|
982
|
+
if (options?.onChunk) {
|
|
983
|
+
await options.onChunk(
|
|
984
|
+
"[stdin] Kernel requested input. Interactive stdin is not supported; provide input programmatically.\n",
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
this.sendMessage({
|
|
988
|
+
channel: "stdin",
|
|
989
|
+
header: {
|
|
990
|
+
msg_id: nanoid(),
|
|
991
|
+
session: this.sessionId,
|
|
992
|
+
username: this.username,
|
|
993
|
+
date: new Date().toISOString(),
|
|
994
|
+
msg_type: "input_reply",
|
|
995
|
+
version: "5.5",
|
|
996
|
+
},
|
|
997
|
+
parent_header: response.header as unknown as Record<string, unknown>,
|
|
998
|
+
metadata: {},
|
|
999
|
+
content: { value: "" },
|
|
1000
|
+
});
|
|
1001
|
+
break;
|
|
984
1002
|
}
|
|
985
|
-
});
|
|
986
|
-
|
|
987
|
-
try {
|
|
988
|
-
this.sendMessage(msg);
|
|
989
|
-
} catch {
|
|
990
|
-
cancelled = true;
|
|
991
|
-
finalize();
|
|
992
1003
|
}
|
|
993
1004
|
});
|
|
1005
|
+
|
|
1006
|
+
try {
|
|
1007
|
+
this.sendMessage(msg);
|
|
1008
|
+
} catch {
|
|
1009
|
+
cancelled = true;
|
|
1010
|
+
finalize();
|
|
1011
|
+
}
|
|
1012
|
+
return promise;
|
|
994
1013
|
}
|
|
995
1014
|
|
|
996
1015
|
async introspectPrelude(): Promise<PreludeHelper[]> {
|
|
@@ -1079,7 +1098,7 @@ export class PythonKernel {
|
|
|
1079
1098
|
await releaseSharedGateway();
|
|
1080
1099
|
} else if (this.gatewayProcess) {
|
|
1081
1100
|
try {
|
|
1082
|
-
killProcessTree(this.gatewayProcess.pid);
|
|
1101
|
+
await killProcessTree(this.gatewayProcess.pid);
|
|
1083
1102
|
} catch (err: unknown) {
|
|
1084
1103
|
logger.warn("Failed to terminate gateway process", {
|
|
1085
1104
|
error: err instanceof Error ? err.message : String(err),
|
package/src/core/sdk.ts
CHANGED
|
@@ -30,14 +30,14 @@ import { join } from "node:path";
|
|
|
30
30
|
import { Agent, type AgentEvent, type AgentMessage, type AgentTool, type ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
31
31
|
import { type Message, type Model, supportsXhigh } from "@oh-my-pi/pi-ai";
|
|
32
32
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
33
|
-
import chalk from "chalk";
|
|
34
33
|
// Import discovery to register all providers on startup
|
|
35
|
-
import "
|
|
34
|
+
import { logger, postmortem } from "@oh-my-pi/pi-utils";
|
|
35
|
+
import chalk from "chalk";
|
|
36
36
|
import { loadCapability } from "../capability/index";
|
|
37
37
|
import { type Rule, ruleCapability } from "../capability/rule";
|
|
38
38
|
import { getAgentDir, getConfigDirPaths } from "../config";
|
|
39
|
+
import "../discovery";
|
|
39
40
|
import { initializeWithSettings } from "../discovery";
|
|
40
|
-
import { registerAsyncCleanup } from "../modes/cleanup";
|
|
41
41
|
import { AgentSession } from "./agent-session";
|
|
42
42
|
import { AuthStorage } from "./auth-storage";
|
|
43
43
|
import { CursorExecHandlers } from "./cursor/exec-bridge";
|
|
@@ -52,15 +52,14 @@ import {
|
|
|
52
52
|
type ExtensionContext,
|
|
53
53
|
type ExtensionFactory,
|
|
54
54
|
ExtensionRunner,
|
|
55
|
+
ExtensionToolWrapper,
|
|
55
56
|
type ExtensionUIContext,
|
|
56
57
|
type LoadExtensionsResult,
|
|
57
58
|
loadExtensionFromFactory,
|
|
58
59
|
loadExtensions,
|
|
59
60
|
type ToolDefinition,
|
|
60
61
|
wrapRegisteredTools,
|
|
61
|
-
wrapToolWithExtensions,
|
|
62
62
|
} from "./extensions/index";
|
|
63
|
-
import { logger } from "./logger";
|
|
64
63
|
import { discoverAndLoadMCPTools, type MCPManager, type MCPToolsLoadResult } from "./mcp/index";
|
|
65
64
|
import { convertToLlm } from "./messages";
|
|
66
65
|
import { ModelRegistry } from "./model-registry";
|
|
@@ -212,12 +211,11 @@ export type { FileSlashCommand } from "./slash-commands";
|
|
|
212
211
|
export type { Tool } from "./tools/index";
|
|
213
212
|
|
|
214
213
|
export {
|
|
214
|
+
// Individual tool classes (for custom usage)
|
|
215
|
+
BashTool,
|
|
215
216
|
// Tool classes and factories
|
|
216
217
|
BUILTIN_TOOLS,
|
|
217
218
|
createTools,
|
|
218
|
-
type ToolSession,
|
|
219
|
-
// Individual tool classes (for custom usage)
|
|
220
|
-
BashTool,
|
|
221
219
|
EditTool,
|
|
222
220
|
FindTool,
|
|
223
221
|
GitTool,
|
|
@@ -227,6 +225,7 @@ export {
|
|
|
227
225
|
PythonTool,
|
|
228
226
|
ReadTool,
|
|
229
227
|
WriteTool,
|
|
228
|
+
type ToolSession,
|
|
230
229
|
};
|
|
231
230
|
|
|
232
231
|
// Helper Functions
|
|
@@ -441,7 +440,7 @@ async function cleanupSshResources(): Promise<void> {
|
|
|
441
440
|
function registerSshCleanup(): void {
|
|
442
441
|
if (sshCleanupRegistered) return;
|
|
443
442
|
sshCleanupRegistered = true;
|
|
444
|
-
|
|
443
|
+
postmortem.register("ssh-cleanup", cleanupSshResources);
|
|
445
444
|
}
|
|
446
445
|
|
|
447
446
|
let pythonCleanupRegistered = false;
|
|
@@ -449,9 +448,7 @@ let pythonCleanupRegistered = false;
|
|
|
449
448
|
function registerPythonCleanup(): void {
|
|
450
449
|
if (pythonCleanupRegistered) return;
|
|
451
450
|
pythonCleanupRegistered = true;
|
|
452
|
-
|
|
453
|
-
await disposeAllKernelSessions();
|
|
454
|
-
});
|
|
451
|
+
postmortem.register("python-cleanup", disposeAllKernelSessions);
|
|
455
452
|
}
|
|
456
453
|
|
|
457
454
|
function customToolToDefinition(tool: CustomTool): ToolDefinition {
|
|
@@ -872,7 +869,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
872
869
|
}
|
|
873
870
|
if (extensionRunner) {
|
|
874
871
|
for (const tool of toolRegistry.values()) {
|
|
875
|
-
toolRegistry.set(tool.name,
|
|
872
|
+
toolRegistry.set(tool.name, new ExtensionToolWrapper(tool, extensionRunner));
|
|
876
873
|
}
|
|
877
874
|
}
|
|
878
875
|
if (model?.provider === "cursor") {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { basename, join, resolve } from "node:path";
|
|
2
2
|
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
3
3
|
import type { ImageContent, Message, TextContent, Usage } from "@oh-my-pi/pi-ai";
|
|
4
|
+
import { logger } from "@oh-my-pi/pi-utils";
|
|
4
5
|
import { nanoid } from "nanoid";
|
|
5
6
|
import { getAgentDir as getDefaultAgentDir } from "../config";
|
|
6
7
|
import { resizeImage } from "../utils/image-resize";
|
|
7
|
-
import { logger } from "./logger";
|
|
8
8
|
import {
|
|
9
9
|
type BashExecutionMessage,
|
|
10
10
|
type CustomMessage,
|