@oh-my-pi/pi-coding-agent 17.0.9 → 17.1.1
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 +78 -0
- package/README.md +1 -1
- package/dist/cli.js +4685 -4614
- package/dist/types/async/job-manager.d.ts +37 -1
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/bench-cli.d.ts +41 -0
- package/dist/types/commands/bench.d.ts +15 -3
- package/dist/types/commands/launch.d.ts +4 -0
- package/dist/types/config/model-registry.d.ts +3 -1
- package/dist/types/config/models-config-schema.d.ts +18 -6
- package/dist/types/config/models-config.d.ts +14 -5
- package/dist/types/config/provider-globals.d.ts +2 -2
- package/dist/types/config/service-tier.d.ts +5 -1
- package/dist/types/config/settings-schema.d.ts +374 -47
- package/dist/types/config/settings.d.ts +7 -0
- package/dist/types/dap/client.d.ts +20 -0
- package/dist/types/exec/bash-executor.d.ts +38 -0
- package/dist/types/exec/direnv.d.ts +33 -0
- package/dist/types/export/html/args.d.ts +15 -0
- package/dist/types/export/html/index.d.ts +11 -26
- package/dist/types/export/html/web-palette.d.ts +55 -116
- package/dist/types/extensibility/extensions/loader.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +17 -2
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +3 -0
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +13 -0
- package/dist/types/live/attestation.d.ts +2 -0
- package/dist/types/live/controller.d.ts +48 -0
- package/dist/types/live/protocol.d.ts +85 -0
- package/dist/types/live/protocol.test.d.ts +1 -0
- package/dist/types/live/transport.d.ts +35 -0
- package/dist/types/live/visualizer.d.ts +30 -0
- package/dist/types/lsp/client.d.ts +16 -1
- package/dist/types/mcp/config-writer.test.d.ts +1 -0
- package/dist/types/mcp/oauth-discovery.d.ts +2 -0
- package/dist/types/mcp/smithery-auth.d.ts +1 -1
- package/dist/types/mcp/smithery-auth.test.d.ts +1 -0
- package/dist/types/memory-backend/tool-names.d.ts +2 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -0
- package/dist/types/modes/components/custom-message.d.ts +1 -1
- package/dist/types/modes/components/login-dialog.d.ts +2 -2
- package/dist/types/modes/components/message-frame.d.ts +8 -4
- package/dist/types/modes/components/plan-review-overlay.d.ts +26 -0
- package/dist/types/modes/components/session-account-selector.d.ts +11 -0
- package/dist/types/modes/components/settings-defs.d.ts +7 -1
- package/dist/types/modes/components/status-line/component.jj-cache.test.d.ts +1 -0
- package/dist/types/modes/components/tool-execution.d.ts +9 -1
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/event-controller.d.ts +6 -1
- package/dist/types/modes/controllers/live-command-controller.d.ts +14 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +6 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +4 -1
- package/dist/types/modes/utils/context-usage.d.ts +12 -2
- package/dist/types/plan-mode/plan-files.d.ts +10 -0
- package/dist/types/registry/agent-lifecycle.d.ts +21 -9
- package/dist/types/registry/agent-registry.d.ts +11 -4
- package/dist/types/sdk.d.ts +15 -1
- package/dist/types/secrets/index.d.ts +15 -2
- package/dist/types/secrets/obfuscator.d.ts +113 -15
- package/dist/types/session/acp-permission-gate.d.ts +19 -0
- package/dist/types/session/agent-session-events.d.ts +84 -0
- package/dist/types/session/agent-session-types.d.ts +321 -0
- package/dist/types/session/agent-session.d.ts +135 -753
- package/dist/types/session/async-job-delivery.d.ts +28 -0
- package/dist/types/session/auth-storage.d.ts +1 -1
- package/dist/types/session/bash-runner.d.ts +75 -0
- package/dist/types/session/blob-store.d.ts +11 -1
- package/dist/types/session/blob-store.test.d.ts +1 -0
- package/dist/types/session/checkpoint-entries.d.ts +28 -0
- package/dist/types/session/eval-runner.d.ts +51 -0
- package/dist/types/session/irc-bridge.d.ts +46 -0
- package/dist/types/session/messages.d.ts +24 -0
- package/dist/types/session/model-controls.d.ts +180 -0
- package/dist/types/session/prewalk.d.ts +46 -0
- package/dist/types/session/queued-messages.d.ts +22 -0
- package/dist/types/session/redis-session-storage.d.ts +1 -0
- package/dist/types/session/retry-fallback-chains.d.ts +74 -0
- package/dist/types/session/role-models.d.ts +15 -0
- package/dist/types/session/session-advisors.d.ts +228 -0
- package/dist/types/session/session-entries.d.ts +8 -0
- package/dist/types/session/session-handoff.d.ts +70 -0
- package/dist/types/session/session-history-format.d.ts +6 -1
- package/dist/types/session/session-maintenance.d.ts +246 -0
- package/dist/types/session/session-manager.d.ts +41 -0
- package/dist/types/session/session-memory.d.ts +56 -0
- package/dist/types/session/session-provider-boundary.d.ts +62 -0
- package/dist/types/session/session-stats.d.ts +48 -0
- package/dist/types/session/session-tools.d.ts +158 -0
- package/dist/types/session/session-workspace.d.ts +32 -0
- package/dist/types/session/stream-guards.d.ts +44 -0
- package/dist/types/session/streaming-output.d.ts +8 -0
- package/dist/types/session/todo-tracker.d.ts +56 -0
- package/dist/types/session/ttsr-coordinator.d.ts +51 -0
- package/dist/types/session/turn-recovery.d.ts +225 -0
- package/dist/types/slash-commands/helpers/session-pin.d.ts +9 -0
- package/dist/types/stt/index.d.ts +0 -2
- package/dist/types/stt/stt-controller.d.ts +7 -0
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +4 -1
- package/dist/types/task/output-manager.d.ts +3 -1
- package/dist/types/tiny/title-client.d.ts +10 -0
- package/dist/types/tools/approval.d.ts +1 -0
- package/dist/types/tools/browser/launch.d.ts +5 -0
- package/dist/types/tools/builtin-names.d.ts +1 -1
- package/dist/types/tools/computer/protocol.d.ts +43 -0
- package/dist/types/tools/computer/supervisor.d.ts +32 -0
- package/dist/types/tools/computer/worker-entry.d.ts +1 -0
- package/dist/types/tools/computer/worker.d.ts +15 -0
- package/dist/types/tools/computer-renderer.d.ts +22 -0
- package/dist/types/tools/computer.d.ts +71 -0
- package/dist/types/tools/context.d.ts +2 -0
- package/dist/types/tools/default-renderer.d.ts +21 -0
- package/dist/types/tools/image-gen.d.ts +4 -4
- package/dist/types/tools/image-providers.d.ts +38 -0
- package/dist/types/tools/index.d.ts +7 -0
- package/dist/types/tools/path-utils.d.ts +8 -0
- package/dist/types/tools/todo.d.ts +10 -4
- package/dist/types/tools/xdev.d.ts +5 -1
- package/dist/types/tts/streaming-player.d.ts +10 -29
- package/dist/types/tts/vocalizer.d.ts +5 -0
- package/dist/types/utils/jj.d.ts +29 -0
- package/dist/types/utils/lang-from-path.d.ts +1 -0
- package/dist/types/utils/title-generator.d.ts +30 -0
- package/dist/types/utils/tools-manager.d.ts +1 -2
- package/dist/types/vibe/runtime.d.ts +36 -8
- package/dist/types/web/search/index.d.ts +1 -1
- package/dist/types/web/search/provider.d.ts +15 -5
- package/dist/types/web/search/types.d.ts +94 -0
- package/package.json +12 -12
- package/scripts/generate-share-viewer.ts +4 -6
- package/src/advisor/__tests__/advisor.test.ts +643 -0
- package/src/advisor/runtime.ts +144 -43
- package/src/async/job-manager.ts +90 -2
- package/src/cli/args.ts +3 -0
- package/src/cli/auth-gateway-cli.ts +18 -3
- package/src/cli/bench-cli.ts +395 -29
- package/src/cli/flag-tables.ts +3 -0
- package/src/cli/gc-cli.ts +2 -2
- package/src/cli/setup-cli.ts +2 -14
- package/src/cli/usage-cli.ts +10 -1
- package/src/cli.ts +8 -0
- package/src/collab/host.ts +3 -2
- package/src/commands/bench.ts +18 -4
- package/src/commands/launch.ts +4 -0
- package/src/config/model-registry.ts +16 -2
- package/src/config/models-config-schema.ts +15 -4
- package/src/config/provider-globals.ts +9 -9
- package/src/config/service-tier.ts +26 -1
- package/src/config/settings-schema.ts +270 -51
- package/src/config/settings.ts +81 -2
- package/src/dap/client.ts +37 -4
- package/src/edit/index.ts +49 -12
- package/src/eval/executor-base.ts +1 -0
- package/src/eval/js/executor.ts +2 -0
- package/src/eval/py/executor.ts +103 -33
- package/src/exec/bash-executor.ts +90 -6
- package/src/exec/direnv.ts +145 -0
- package/src/export/html/args.ts +20 -0
- package/src/export/html/index.ts +45 -47
- package/src/export/html/template.css +19 -1
- package/src/export/html/template.html +6 -0
- package/src/export/html/template.js +21 -0
- package/src/export/html/tool-views.generated.js +31 -31
- package/src/export/html/web-palette.ts +116 -132
- package/src/export/share.ts +249 -49
- package/src/extensibility/extensions/loader.ts +29 -1
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +33 -2
- package/src/extensibility/extensions/wrapper.ts +68 -12
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +7 -1
- package/src/extensibility/plugins/legacy-pi-compat.ts +19 -1
- package/src/hindsight/state.ts +64 -6
- package/src/internal-urls/local-protocol.ts +2 -1
- package/src/internal-urls/memory-protocol.ts +2 -2
- package/src/internal-urls/skill-protocol.ts +2 -2
- package/src/internal-urls/ssh-protocol.ts +2 -1
- package/src/internal-urls/vault-protocol.ts +2 -1
- package/src/live/attestation.ts +91 -0
- package/src/live/controller.ts +517 -0
- package/src/live/prompts/agent-final-message.md +3 -0
- package/src/live/prompts/live-instructions.md +23 -0
- package/src/live/protocol.test.ts +140 -0
- package/src/live/protocol.ts +233 -0
- package/src/live/transport.ts +422 -0
- package/src/live/visualizer.ts +214 -0
- package/src/lsp/client.ts +29 -9
- package/src/lsp/index.ts +19 -4
- package/src/main.ts +8 -1
- package/src/mcp/config-writer.test.ts +43 -0
- package/src/mcp/config-writer.ts +109 -82
- package/src/mcp/oauth-discovery.ts +10 -2
- package/src/mcp/smithery-auth.test.ts +29 -0
- package/src/mcp/smithery-auth.ts +3 -2
- package/src/memories/index.ts +40 -20
- package/src/memory-backend/tool-names.ts +2 -0
- package/src/mnemopi/backend.ts +24 -7
- package/src/modes/acp/acp-agent.ts +56 -10
- package/src/modes/acp/acp-event-mapper.ts +8 -1
- package/src/modes/components/agent-hub.ts +1 -1
- package/src/modes/components/assistant-message.ts +7 -2
- package/src/modes/components/custom-message.ts +4 -1
- package/src/modes/components/login-dialog.ts +14 -4
- package/src/modes/components/message-frame.ts +14 -8
- package/src/modes/components/plan-review-overlay.ts +350 -35
- package/src/modes/components/session-account-selector.ts +62 -0
- package/src/modes/components/settings-defs.ts +23 -1
- package/src/modes/components/settings-selector.ts +169 -1
- package/src/modes/components/status-line/component.jj-cache.test.ts +229 -0
- package/src/modes/components/status-line/component.ts +123 -2
- package/src/modes/components/tool-execution.ts +28 -112
- package/src/modes/controllers/command-controller.ts +56 -15
- package/src/modes/controllers/event-controller.ts +111 -16
- package/src/modes/controllers/extension-ui-controller.test.ts +1 -0
- package/src/modes/controllers/extension-ui-controller.ts +20 -4
- package/src/modes/controllers/input-controller.ts +47 -39
- package/src/modes/controllers/live-command-controller.ts +255 -0
- package/src/modes/controllers/mcp-command-controller.ts +10 -1
- package/src/modes/controllers/omfg-controller.ts +44 -40
- package/src/modes/controllers/selector-controller.ts +80 -10
- package/src/modes/controllers/session-focus-controller.ts +6 -1
- package/src/modes/interactive-mode.ts +176 -38
- package/src/modes/runtime-init.ts +2 -0
- package/src/modes/setup-wizard/scenes/web-search.ts +17 -9
- package/src/modes/theme/theme.ts +1 -1
- package/src/modes/types.ts +4 -1
- package/src/modes/utils/context-usage.ts +23 -7
- package/src/modes/utils/ui-helpers.ts +10 -5
- package/src/plan-mode/plan-files.ts +40 -0
- package/src/prompts/bench/cache-prefix-chunk.md +1 -0
- package/src/prompts/bench/cache-prefix.md +3 -0
- package/src/prompts/bench/cache-suffix.md +1 -0
- package/src/prompts/system/computer-safety.md +14 -0
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/project-prompt.md +8 -1
- package/src/prompts/system/subagent-async-pending.md +6 -0
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/system/workflow-notice.md +48 -40
- package/src/prompts/system/xdev-mount-notice.md +4 -0
- package/src/prompts/tools/computer.md +26 -0
- package/src/prompts/tools/todo.md +3 -1
- package/src/registry/agent-lifecycle.ts +116 -46
- package/src/registry/agent-registry.ts +35 -8
- package/src/registry/persisted-agents.ts +26 -2
- package/src/sdk.ts +215 -136
- package/src/secrets/index.ts +127 -4
- package/src/secrets/obfuscator.ts +2328 -105
- package/src/session/acp-permission-gate.ts +165 -0
- package/src/session/agent-session-error-log.test.ts +1 -1
- package/src/session/agent-session-events.ts +66 -0
- package/src/session/agent-session-types.ts +343 -0
- package/src/session/agent-session.ts +1940 -12218
- package/src/session/artifacts.ts +6 -5
- package/src/session/async-job-delivery.ts +74 -0
- package/src/session/auth-storage.ts +1 -0
- package/src/session/bash-runner.ts +326 -0
- package/src/session/blob-store.test.ts +56 -0
- package/src/session/blob-store.ts +18 -2
- package/src/session/checkpoint-entries.ts +81 -0
- package/src/session/eval-runner.ts +217 -0
- package/src/session/indexed-session-storage.ts +9 -2
- package/src/session/irc-bridge.ts +203 -0
- package/src/session/messages.ts +266 -1
- package/src/session/model-controls.ts +714 -0
- package/src/session/prewalk.ts +252 -0
- package/src/session/queued-messages.ts +93 -0
- package/src/session/redis-session-storage.ts +51 -11
- package/src/session/retry-fallback-chains.ts +455 -0
- package/src/session/role-models.ts +85 -0
- package/src/session/session-advisors.ts +1679 -0
- package/src/session/session-context.ts +6 -3
- package/src/session/session-entries.ts +8 -0
- package/src/session/session-handoff.ts +308 -0
- package/src/session/session-history-format.ts +20 -9
- package/src/session/session-loader.ts +0 -46
- package/src/session/session-maintenance.ts +2983 -0
- package/src/session/session-manager.ts +390 -30
- package/src/session/session-memory.ts +222 -0
- package/src/session/session-provider-boundary.ts +307 -0
- package/src/session/session-stats.ts +293 -0
- package/src/session/session-tools.ts +980 -0
- package/src/session/session-workspace.ts +53 -0
- package/src/session/stream-guards.ts +417 -0
- package/src/session/streaming-output.ts +52 -5
- package/src/session/todo-tracker.ts +380 -0
- package/src/session/ttsr-coordinator.ts +496 -0
- package/src/session/turn-recovery.ts +1629 -0
- package/src/slash-commands/builtin-registry.ts +257 -20
- package/src/slash-commands/helpers/session-pin.ts +44 -0
- package/src/slash-commands/helpers/usage-report.ts +22 -3
- package/src/stt/downloader.ts +0 -2
- package/src/stt/index.ts +0 -2
- package/src/stt/stt-controller.ts +57 -146
- package/src/system-prompt.ts +45 -17
- package/src/task/executor.ts +285 -70
- package/src/task/index.ts +98 -105
- package/src/task/output-manager.ts +25 -3
- package/src/task/persisted-revive.ts +4 -3
- package/src/task/structured-subagent.ts +1 -0
- package/src/tiny/title-client.ts +22 -0
- package/src/tools/approval.ts +55 -10
- package/src/tools/bash-interactive.ts +116 -86
- package/src/tools/bash-skill-urls.ts +28 -2
- package/src/tools/bash.ts +339 -123
- package/src/tools/browser/launch.ts +9 -1
- package/src/tools/browser/tab-supervisor.ts +8 -5
- package/src/tools/browser.ts +94 -47
- package/src/tools/builtin-names.ts +1 -0
- package/src/tools/computer/protocol.ts +28 -0
- package/src/tools/computer/supervisor.ts +258 -0
- package/src/tools/computer/worker-entry.ts +25 -0
- package/src/tools/computer/worker.ts +135 -0
- package/src/tools/computer-renderer.ts +108 -0
- package/src/tools/computer.ts +433 -0
- package/src/tools/context.ts +2 -0
- package/src/tools/default-renderer.ts +139 -0
- package/src/tools/essential-tools.ts +1 -0
- package/src/tools/image-gen.ts +20 -28
- package/src/tools/image-providers.ts +50 -0
- package/src/tools/index.ts +14 -0
- package/src/tools/path-utils.ts +49 -2
- package/src/tools/read.ts +156 -88
- package/src/tools/renderers.ts +9 -1
- package/src/tools/todo.ts +101 -11
- package/src/tools/vibe.ts +1 -2
- package/src/tools/write.ts +51 -1
- package/src/tools/xdev.ts +121 -29
- package/src/tts/streaming-player.ts +82 -283
- package/src/tts/vocalizer.ts +18 -1
- package/src/utils/clipboard.ts +1 -30
- package/src/utils/jj.ts +125 -4
- package/src/utils/lang-from-path.ts +7 -0
- package/src/utils/mac-file-urls.applescript +37 -0
- package/src/utils/title-generator.ts +137 -1
- package/src/utils/tool-choice.ts +14 -0
- package/src/utils/tools-manager.ts +1 -19
- package/src/vibe/runtime.ts +852 -72
- package/src/web/search/index.ts +3 -6
- package/src/web/search/provider.ts +31 -16
- package/src/web/search/providers/xai.ts +7 -1
- package/src/web/search/types.ts +3 -0
- package/dist/types/stt/recorder.d.ts +0 -30
- package/dist/types/stt/transcriber.d.ts +0 -14
- package/dist/types/stt/wav.d.ts +0 -29
- package/dist/types/tts/player.d.ts +0 -32
- package/src/stt/recorder.ts +0 -551
- package/src/stt/transcriber.ts +0 -60
- package/src/stt/wav.ts +0 -173
- package/src/tts/player.ts +0 -137
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import type { Agent } from "@oh-my-pi/pi-agent-core";
|
|
2
|
+
import { logger } from "@oh-my-pi/pi-utils";
|
|
3
|
+
import type { Settings } from "../config/settings";
|
|
4
|
+
import { disposeJuliaKernelSessionsByOwner } from "../eval/jl/executor";
|
|
5
|
+
import { namespaceSessionId as namespacePythonSessionId } from "../eval/py";
|
|
6
|
+
import {
|
|
7
|
+
disposeKernelSessionsByOwner,
|
|
8
|
+
executePython as executePythonCommand,
|
|
9
|
+
type PythonResult,
|
|
10
|
+
} from "../eval/py/executor";
|
|
11
|
+
import { disposeRubyKernelSessionsByOwner } from "../eval/rb/executor";
|
|
12
|
+
import { defaultEvalSessionId } from "../eval/session-id";
|
|
13
|
+
import type { ExtensionRunner } from "../extensibility/extensions";
|
|
14
|
+
import { outputMeta } from "../tools/output-meta";
|
|
15
|
+
import type { PythonExecutionMessage } from "./messages";
|
|
16
|
+
import type { SessionManager } from "./session-manager";
|
|
17
|
+
|
|
18
|
+
/** Capabilities the eval runner borrows from its owning session. */
|
|
19
|
+
export interface EvalRunnerHost {
|
|
20
|
+
agent: Agent;
|
|
21
|
+
sessionManager: SessionManager;
|
|
22
|
+
settings: Settings;
|
|
23
|
+
extensionRunner(): ExtensionRunner | undefined;
|
|
24
|
+
isStreaming(): boolean;
|
|
25
|
+
appendSessionMessage(message: PythonExecutionMessage): void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Owns user-initiated Python execution and retained eval-kernel lifecycle. */
|
|
29
|
+
export class EvalRunner {
|
|
30
|
+
readonly #host: EvalRunnerHost;
|
|
31
|
+
readonly #kernelOwnerId: string;
|
|
32
|
+
readonly #parentSessionId: string | undefined;
|
|
33
|
+
#abortControllers = new Set<AbortController>();
|
|
34
|
+
#pendingMessages: PythonExecutionMessage[] = [];
|
|
35
|
+
#activeExecutions = new Set<Promise<unknown>>();
|
|
36
|
+
#disposing = false;
|
|
37
|
+
|
|
38
|
+
constructor(host: EvalRunnerHost, options: { kernelOwnerId: string; parentSessionId: string | undefined }) {
|
|
39
|
+
this.#host = host;
|
|
40
|
+
this.#kernelOwnerId = options.kernelOwnerId;
|
|
41
|
+
this.#parentSessionId = options.parentSessionId;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Executes Python in the session's shared kernel. */
|
|
45
|
+
async executePython(
|
|
46
|
+
code: string,
|
|
47
|
+
onChunk?: (chunk: string) => void,
|
|
48
|
+
options?: { excludeFromContext?: boolean },
|
|
49
|
+
): Promise<PythonResult> {
|
|
50
|
+
const excludeFromContext = options?.excludeFromContext === true;
|
|
51
|
+
const cwd = this.#host.sessionManager.getCwd();
|
|
52
|
+
this.assertExecutionAllowed();
|
|
53
|
+
const abortController = new AbortController();
|
|
54
|
+
const execution = (async (): Promise<PythonResult> => {
|
|
55
|
+
const extensionRunner = this.#host.extensionRunner();
|
|
56
|
+
if (extensionRunner?.hasHandlers("user_python")) {
|
|
57
|
+
const hookResult = await extensionRunner.emitUserPython({
|
|
58
|
+
type: "user_python",
|
|
59
|
+
code,
|
|
60
|
+
excludeFromContext,
|
|
61
|
+
cwd,
|
|
62
|
+
});
|
|
63
|
+
this.assertExecutionAllowed();
|
|
64
|
+
if (hookResult?.result) {
|
|
65
|
+
this.recordPythonResult(code, hookResult.result, options);
|
|
66
|
+
return hookResult.result;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const sessionId =
|
|
70
|
+
this.getSessionId() ??
|
|
71
|
+
defaultEvalSessionId({
|
|
72
|
+
cwd,
|
|
73
|
+
getSessionFile: () => this.#host.sessionManager.getSessionFile() ?? null,
|
|
74
|
+
});
|
|
75
|
+
const result = await executePythonCommand(code, {
|
|
76
|
+
cwd,
|
|
77
|
+
sessionId: namespacePythonSessionId(sessionId),
|
|
78
|
+
kernelOwnerId: this.#kernelOwnerId,
|
|
79
|
+
kernelMode: this.#host.settings.get("python.kernelMode"),
|
|
80
|
+
interpreter: this.#host.settings.get("python.interpreter")?.trim() || undefined,
|
|
81
|
+
onChunk,
|
|
82
|
+
signal: abortController.signal,
|
|
83
|
+
});
|
|
84
|
+
this.recordPythonResult(code, result, options);
|
|
85
|
+
return result;
|
|
86
|
+
})();
|
|
87
|
+
return await this.trackExecution(execution, abortController);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Rejects new eval work once session disposal begins. */
|
|
91
|
+
assertExecutionAllowed(): void {
|
|
92
|
+
if (this.#disposing) throw new Error("Python execution is unavailable while session disposal is in progress");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Tracks externally started Python work so disposal can await and abort it. */
|
|
96
|
+
trackExecution<T>(execution: Promise<T>, abortController: AbortController): Promise<T> {
|
|
97
|
+
this.#abortControllers.add(abortController);
|
|
98
|
+
this.#activeExecutions.add(execution);
|
|
99
|
+
void execution.then(
|
|
100
|
+
() => {
|
|
101
|
+
this.#abortControllers.delete(abortController);
|
|
102
|
+
this.#activeExecutions.delete(execution);
|
|
103
|
+
},
|
|
104
|
+
() => {
|
|
105
|
+
this.#abortControllers.delete(abortController);
|
|
106
|
+
this.#activeExecutions.delete(execution);
|
|
107
|
+
},
|
|
108
|
+
);
|
|
109
|
+
return execution;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Records a Python execution result in session history. */
|
|
113
|
+
recordPythonResult(code: string, result: PythonResult, options?: { excludeFromContext?: boolean }): void {
|
|
114
|
+
const meta = outputMeta().truncationFromSummary(result, { direction: "tail" }).get();
|
|
115
|
+
const message: PythonExecutionMessage = {
|
|
116
|
+
role: "pythonExecution",
|
|
117
|
+
code,
|
|
118
|
+
output: result.output,
|
|
119
|
+
exitCode: result.exitCode,
|
|
120
|
+
cancelled: result.cancelled,
|
|
121
|
+
truncated: result.truncated,
|
|
122
|
+
meta,
|
|
123
|
+
timestamp: Date.now(),
|
|
124
|
+
excludeFromContext: options?.excludeFromContext,
|
|
125
|
+
};
|
|
126
|
+
if (this.#host.isStreaming()) {
|
|
127
|
+
this.#pendingMessages.push(message);
|
|
128
|
+
} else {
|
|
129
|
+
this.#host.appendSessionMessage(message);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Cancels every running Python execution. */
|
|
134
|
+
abort(): void {
|
|
135
|
+
for (const abortController of this.#abortControllers) abortController.abort();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Whether a Python execution is currently running. */
|
|
139
|
+
get isRunning(): boolean {
|
|
140
|
+
return this.#abortControllers.size > 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Whether Python results are waiting for a safe persistence boundary. */
|
|
144
|
+
get hasPendingMessages(): boolean {
|
|
145
|
+
return this.#pendingMessages.length > 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Returns the stable owner shared by eval and session-owned tools. */
|
|
149
|
+
getKernelOwnerId(): string {
|
|
150
|
+
return this.#kernelOwnerId;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Returns the eval session shared with the Python backend. */
|
|
154
|
+
getSessionId(): string | null {
|
|
155
|
+
if (this.#parentSessionId !== undefined) return this.#parentSessionId;
|
|
156
|
+
return defaultEvalSessionId({
|
|
157
|
+
cwd: this.#host.sessionManager.getCwd(),
|
|
158
|
+
getSessionFile: () => this.#host.sessionManager.getSessionFile() ?? null,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Flushes deferred Python results into agent state and persistence. */
|
|
163
|
+
flushPending(): void {
|
|
164
|
+
if (this.#pendingMessages.length === 0) return;
|
|
165
|
+
for (const message of this.#pendingMessages) this.#host.appendSessionMessage(message);
|
|
166
|
+
this.#pendingMessages = [];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Prevents new Python executions before asynchronous disposal starts. */
|
|
170
|
+
beginDispose(): void {
|
|
171
|
+
this.#disposing = true;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Waits for active work and disposes every retained eval kernel owned by the session. */
|
|
175
|
+
async disposeKernels(): Promise<void> {
|
|
176
|
+
const settled = await this.#prepareExecutionsForDispose();
|
|
177
|
+
if (!settled) {
|
|
178
|
+
logger.warn("Detaching retained eval-kernel ownership during dispose while eval execution is still active");
|
|
179
|
+
}
|
|
180
|
+
const results = await Promise.allSettled([
|
|
181
|
+
disposeKernelSessionsByOwner(this.#kernelOwnerId),
|
|
182
|
+
disposeRubyKernelSessionsByOwner(this.#kernelOwnerId),
|
|
183
|
+
disposeJuliaKernelSessionsByOwner(this.#kernelOwnerId),
|
|
184
|
+
]);
|
|
185
|
+
const errors: unknown[] = [];
|
|
186
|
+
for (const result of results) if (result.status === "rejected") errors.push(result.reason);
|
|
187
|
+
if (errors.length > 0) throw new AggregateError(errors, "Failed to dispose one or more eval kernels");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async #waitForExecutionsToSettle(timeoutMs: number): Promise<boolean> {
|
|
191
|
+
const deadline = Date.now() + timeoutMs;
|
|
192
|
+
while (this.#activeExecutions.size > 0) {
|
|
193
|
+
const remainingMs = deadline - Date.now();
|
|
194
|
+
if (remainingMs <= 0) return false;
|
|
195
|
+
const settled = await Promise.race([
|
|
196
|
+
Promise.allSettled(Array.from(this.#activeExecutions)).then(() => true),
|
|
197
|
+
Bun.sleep(remainingMs).then(() => false),
|
|
198
|
+
]);
|
|
199
|
+
if (!settled && this.#activeExecutions.size > 0) return false;
|
|
200
|
+
}
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async #prepareExecutionsForDispose(): Promise<boolean> {
|
|
205
|
+
if (!(await this.#waitForExecutionsToSettle(3_000))) {
|
|
206
|
+
logger.warn("Aborting active Python execution during dispose before retained kernel cleanup");
|
|
207
|
+
this.abort();
|
|
208
|
+
if (!(await this.#waitForExecutionsToSettle(1_000))) {
|
|
209
|
+
logger.warn(
|
|
210
|
+
"Python execution is still active after dispose aborted all active runs; retained kernel ownership will still be detached",
|
|
211
|
+
);
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -270,8 +270,15 @@ export class IndexedSessionStorage implements SessionStorage {
|
|
|
270
270
|
{ trackDrain: false },
|
|
271
271
|
);
|
|
272
272
|
} catch (err) {
|
|
273
|
-
|
|
274
|
-
|
|
273
|
+
const error = toError(err);
|
|
274
|
+
try {
|
|
275
|
+
if ((await this.#backend.readFull(path)) === content) return;
|
|
276
|
+
} catch {
|
|
277
|
+
// Preserve the original write failure; verification was unavailable.
|
|
278
|
+
}
|
|
279
|
+
const current = this.#index.get(path);
|
|
280
|
+
if (current?.mtimeMs === mtimeMs) this.#restoreIndex(path, previous);
|
|
281
|
+
throw error;
|
|
275
282
|
}
|
|
276
283
|
}
|
|
277
284
|
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import type { Agent } from "@oh-my-pi/pi-agent-core";
|
|
2
|
+
import { logger, prompt } from "@oh-my-pi/pi-utils";
|
|
3
|
+
import type { Settings } from "../config/settings";
|
|
4
|
+
import { IrcBus, type IrcMessage } from "../irc/bus";
|
|
5
|
+
import parentIrcSteerTemplate from "../prompts/steering/parent-irc.md" with { type: "text" };
|
|
6
|
+
import ircAutoReplyTemplate from "../prompts/system/irc-autoreply.md" with { type: "text" };
|
|
7
|
+
import ircIncomingTemplate from "../prompts/system/irc-incoming.md" with { type: "text" };
|
|
8
|
+
import { AgentRegistry } from "../registry/agent-registry";
|
|
9
|
+
import type { AgentSessionEvent } from "./agent-session-events";
|
|
10
|
+
import type { CustomMessage } from "./messages";
|
|
11
|
+
import type { SessionManager } from "./session-manager";
|
|
12
|
+
|
|
13
|
+
/** Capabilities the IRC bridge borrows from its owning session. */
|
|
14
|
+
export interface IrcBridgeHost {
|
|
15
|
+
agent: Agent;
|
|
16
|
+
sessionManager: SessionManager;
|
|
17
|
+
settings: Settings;
|
|
18
|
+
isDisposed(): boolean;
|
|
19
|
+
isStreaming(): boolean;
|
|
20
|
+
planModeEnabled(): boolean;
|
|
21
|
+
emitSessionEvent(event: AgentSessionEvent): Promise<void>;
|
|
22
|
+
wakeForIrc(records: CustomMessage[]): void;
|
|
23
|
+
runEphemeralTurn(args: { promptText: string }): Promise<{ replyText: string }>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Owns incoming IRC queues, injection, and side-channel auto-replies. */
|
|
27
|
+
export class IrcBridge {
|
|
28
|
+
readonly #host: IrcBridgeHost;
|
|
29
|
+
#interrupts: CustomMessage[] = [];
|
|
30
|
+
#asides: CustomMessage[] = [];
|
|
31
|
+
|
|
32
|
+
constructor(host: IrcBridgeHost) {
|
|
33
|
+
this.#host = host;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Whether an incoming peer message can interrupt a wait. */
|
|
37
|
+
hasInterrupts(): boolean {
|
|
38
|
+
return this.#interrupts.length > 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Whether any undelivered IRC record remains queued. */
|
|
42
|
+
hasPending(): boolean {
|
|
43
|
+
return this.#interrupts.length > 0 || this.#asides.length > 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Takes every queued IRC record in interrupt-before-aside order. */
|
|
47
|
+
drainPending(): CustomMessage[] {
|
|
48
|
+
const records = [...this.#interrupts, ...this.#asides];
|
|
49
|
+
this.#interrupts = [];
|
|
50
|
+
this.#asides = [];
|
|
51
|
+
return records;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Surfaces and consumes queued incoming records before automatic injection. */
|
|
55
|
+
drainInboxMessages(agentId: string, opts?: { from?: string; limit?: number }): IrcMessage[] {
|
|
56
|
+
const messages: IrcMessage[] = [];
|
|
57
|
+
const remainingInterrupts: CustomMessage[] = [];
|
|
58
|
+
const remainingAsides: CustomMessage[] = [];
|
|
59
|
+
const queues = [
|
|
60
|
+
{ records: this.#interrupts, remaining: remainingInterrupts },
|
|
61
|
+
{ records: this.#asides, remaining: remainingAsides },
|
|
62
|
+
];
|
|
63
|
+
for (const queue of queues) {
|
|
64
|
+
for (const record of queue.records) {
|
|
65
|
+
if (record.customType !== "irc:incoming") {
|
|
66
|
+
queue.remaining.push(record);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const details = record.details;
|
|
70
|
+
if (!details || typeof details !== "object") {
|
|
71
|
+
queue.remaining.push(record);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const id = Reflect.get(details, "id");
|
|
75
|
+
const from = Reflect.get(details, "from");
|
|
76
|
+
const body = Reflect.get(details, "message");
|
|
77
|
+
const replyTo = Reflect.get(details, "replyTo");
|
|
78
|
+
if (typeof id !== "string" || typeof from !== "string" || typeof body !== "string") {
|
|
79
|
+
queue.remaining.push(record);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (opts?.from !== undefined && from !== opts.from) {
|
|
83
|
+
queue.remaining.push(record);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (opts?.limit !== undefined && messages.length >= opts.limit) {
|
|
87
|
+
queue.remaining.push(record);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
messages.push({
|
|
91
|
+
id,
|
|
92
|
+
from,
|
|
93
|
+
to: agentId,
|
|
94
|
+
body,
|
|
95
|
+
ts: record.timestamp,
|
|
96
|
+
...(typeof replyTo === "string" ? { replyTo } : {}),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
this.#interrupts = remainingInterrupts;
|
|
101
|
+
this.#asides = remainingAsides;
|
|
102
|
+
return messages;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Delivers an IRC message into the recipient session without awaiting any wake turn. */
|
|
106
|
+
async deliver(msg: IrcMessage, opts?: { expectsReply?: boolean }): Promise<"injected" | "woken"> {
|
|
107
|
+
if (this.#host.isDisposed()) throw new Error("Recipient session is disposed.");
|
|
108
|
+
const streaming = this.#host.isStreaming();
|
|
109
|
+
const planModeIdle = !streaming && this.#host.planModeEnabled();
|
|
110
|
+
const autoReply =
|
|
111
|
+
(opts?.expectsReply ?? false) && ((streaming && !this.#host.settings.get("async.enabled")) || planModeIdle);
|
|
112
|
+
const record: CustomMessage = {
|
|
113
|
+
role: "custom",
|
|
114
|
+
customType: "irc:incoming",
|
|
115
|
+
content: prompt.render(ircIncomingTemplate, {
|
|
116
|
+
from: msg.from,
|
|
117
|
+
message: msg.body,
|
|
118
|
+
replyTo: msg.replyTo ?? "",
|
|
119
|
+
autoReplied: autoReply,
|
|
120
|
+
interrupting: streaming,
|
|
121
|
+
}),
|
|
122
|
+
display: true,
|
|
123
|
+
details: { id: msg.id, from: msg.from, message: msg.body, ...(msg.replyTo ? { replyTo: msg.replyTo } : {}) },
|
|
124
|
+
attribution: "agent",
|
|
125
|
+
timestamp: msg.ts,
|
|
126
|
+
};
|
|
127
|
+
void this.#host.emitSessionEvent({ type: "irc_message", message: record });
|
|
128
|
+
if (streaming) {
|
|
129
|
+
const recipientParentId = AgentRegistry.global().get(msg.to)?.parentId;
|
|
130
|
+
if (recipientParentId === msg.from) {
|
|
131
|
+
this.#host.agent.steer({
|
|
132
|
+
role: "user",
|
|
133
|
+
content: prompt.render(parentIrcSteerTemplate, { from: msg.from, message: msg.body }),
|
|
134
|
+
attribution: "agent",
|
|
135
|
+
timestamp: msg.ts,
|
|
136
|
+
steering: true,
|
|
137
|
+
});
|
|
138
|
+
} else {
|
|
139
|
+
this.#interrupts.push(record);
|
|
140
|
+
}
|
|
141
|
+
if (autoReply) void this.#runAutoReply(msg);
|
|
142
|
+
return "injected";
|
|
143
|
+
}
|
|
144
|
+
if (this.#host.planModeEnabled()) {
|
|
145
|
+
this.#host.agent.appendMessage(record);
|
|
146
|
+
this.#host.sessionManager.appendCustomMessageEntry(
|
|
147
|
+
record.customType,
|
|
148
|
+
record.content,
|
|
149
|
+
record.display,
|
|
150
|
+
record.details,
|
|
151
|
+
record.attribution ?? "agent",
|
|
152
|
+
);
|
|
153
|
+
if (autoReply) void this.#runAutoReply(msg);
|
|
154
|
+
return "injected";
|
|
155
|
+
}
|
|
156
|
+
this.#host.wakeForIrc([record]);
|
|
157
|
+
return "woken";
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Emits an IRC relay observation for rendering without persisting it. */
|
|
161
|
+
emitRelayObservation(record: CustomMessage): void {
|
|
162
|
+
void this.#host.emitSessionEvent({ type: "irc_message", message: record });
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Persists queued IRC records that missed their step-boundary injection. */
|
|
166
|
+
flushPending(): void {
|
|
167
|
+
for (const record of this.drainPending()) {
|
|
168
|
+
this.#host.agent.emitExternalEvent({ type: "message_start", message: record });
|
|
169
|
+
this.#host.agent.emitExternalEvent({ type: "message_end", message: record });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async #runAutoReply(msg: IrcMessage): Promise<void> {
|
|
174
|
+
try {
|
|
175
|
+
const { replyText } = await this.#host.runEphemeralTurn({
|
|
176
|
+
promptText: prompt.render(ircAutoReplyTemplate, {
|
|
177
|
+
from: msg.from,
|
|
178
|
+
message: msg.body,
|
|
179
|
+
replyTo: msg.replyTo ?? "",
|
|
180
|
+
}),
|
|
181
|
+
});
|
|
182
|
+
const body = replyText.trim();
|
|
183
|
+
if (!body || this.#host.isDisposed()) return;
|
|
184
|
+
const record: CustomMessage = {
|
|
185
|
+
role: "custom",
|
|
186
|
+
customType: "irc:autoreply",
|
|
187
|
+
content: `[IRC you → \`${msg.from}\` (auto)]\n\n${body}`,
|
|
188
|
+
display: true,
|
|
189
|
+
details: { to: msg.from, body, replyTo: msg.id },
|
|
190
|
+
attribution: "agent",
|
|
191
|
+
timestamp: Date.now(),
|
|
192
|
+
};
|
|
193
|
+
void this.#host.emitSessionEvent({ type: "irc_message", message: record });
|
|
194
|
+
this.#asides.push(record);
|
|
195
|
+
const receipt = await IrcBus.global().send({ from: msg.to, to: msg.from, body, replyTo: msg.id });
|
|
196
|
+
if (receipt.outcome === "failed") {
|
|
197
|
+
logger.warn("IRC auto-reply delivery failed", { to: msg.from, error: receipt.error });
|
|
198
|
+
}
|
|
199
|
+
} catch (error) {
|
|
200
|
+
logger.warn("IRC auto-reply turn failed", { from: msg.from, error: String(error) });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|