Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# pi-btw

A small [pi](https://github.com/badlogic/pi-mono) extension that adds a `/btw` side conversation channel.
A small [pi](https://github.com/earendil-works/pi-mono) extension that adds a `/btw` side conversation channel.

`/btw` opens a real pi sub-session with coding-tool access, and it runs immediately even while the main agent is still busy.

Expand Down
54 changes: 38 additions & 16 deletions extensions/btw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ import {
buildSessionContext,
createAgentSession,
createExtensionRuntime,
codingTools,
SessionManager,
type AgentSession,
type AgentSessionEvent,
type ExtensionAPI,
type ExtensionCommandContext,
type ExtensionContext,
type ResourceLoader,
} from "@mariozechner/pi-coding-agent";
import { type AssistantMessage, type Message, type ThinkingLevel as AiThinkingLevel, type UserMessage } from "@mariozechner/pi-ai";
} from "@earendil-works/pi-coding-agent";
import { type AssistantMessage, type Message, type ThinkingLevel as AiThinkingLevel, type UserMessage } from "@earendil-works/pi-ai";
import {
Box,
Container,
Expand All @@ -26,7 +25,7 @@ import {
type KeybindingsManager,
type OverlayHandle,
type TUI,
} from "@mariozechner/pi-tui";
} from "@earendil-works/pi-tui";

const BTW_MESSAGE_TYPE = "btw-note";
const BTW_ENTRY_TYPE = "btw-thread-entry";
Expand Down Expand Up @@ -56,6 +55,11 @@ const BTW_CONTINUE_THREAD_ASSISTANT_TEXT = "Understood, continuing our side conv
type SessionThinkingLevel = "off" | AiThinkingLevel;
type BtwThreadMode = "contextual" | "tangent";
type SessionModel = NonNullable<ExtensionCommandContext["model"]>;
/**
* Loose model reference parsed from `/btw:model <provider> <id> <api>` and persisted to
* session entries. Resolved to a full SessionModel via ctx.modelRegistry.find(...).
*/
type BtwModelRef = Pick<SessionModel, "provider" | "id" | "api">;

type BtwDetails = {
question: string;
Expand Down Expand Up @@ -216,7 +220,7 @@ function parseBtwArgs(args: string): ParsedBtwArgs {
function parseBtwModelArgs(args: string):
| { action: "show" }
| { action: "clear" }
| { action: "set"; model: SessionModel }
| { action: "set"; model: BtwModelRef }
| { action: "invalid"; message: string } {
const trimmed = args.trim();
if (!trimmed) {
Expand All @@ -233,7 +237,7 @@ function parseBtwModelArgs(args: string):
}

const [provider, id, api] = parts;
return { action: "set", model: { provider, id, api } };
return { action: "set", model: { provider, id, api } as BtwModelRef };
}

function parseBtwThinkingArgs(args: string):
Expand Down Expand Up @@ -1555,7 +1559,8 @@ export default function (pi: ExtensionAPI) {
model: settings.model,
modelRegistry: ctx.modelRegistry as AgentSession["modelRegistry"],
thinkingLevel: settings.thinkingLevel,
tools: codingTools,
// Match pi's default coding-agent toolset (read/bash/edit/write).
tools: ["read", "bash", "edit", "write"],
resourceLoader: createBtwResourceLoader(ctx),
});

Expand Down Expand Up @@ -1760,7 +1765,19 @@ export default function (pi: ExtensionAPI) {
return true;
}

await setBtwModelOverride(ctx, parsed.action === "clear" ? null : parsed.model);
if (parsed.action === "clear") {
await setBtwModelOverride(ctx, null);
return true;
}
const ref = parsed.model;
const resolved = ctx.modelRegistry.find(ref.provider, ref.id);
if (!resolved) {
const message = `Unknown model ${ref.provider}/${ref.id}. Use /login or /models to add it before setting it as the BTW override.`;
setOverlayStatus(message, ctx);
notify(ctx, message, "error");
return true;
}
await setBtwModelOverride(ctx, resolved);
return true;
}

Expand Down Expand Up @@ -1911,17 +1928,22 @@ export default function (pi: ExtensionAPI) {

for (let i = 0; i < branch.length; i++) {
if (isCustomEntry(branch[i], BTW_MODEL_OVERRIDE_TYPE)) {
const details = branch[i].data as BtwModelOverrideDetails | undefined;
btwModelOverride =
details?.action === "set"
? { provider: details.provider, id: details.id, api: details.api }
: details?.action === "clear"
? null
: btwModelOverride;
const details = (branch[i] as unknown as { data?: BtwModelOverrideDetails }).data;
if (details?.action === "set") {
const resolved = ctx.modelRegistry.find(details.provider, details.id);
if (resolved) {
btwModelOverride = resolved;
} else {
// Configured override is no longer in the registry; drop it on restore.
btwModelOverride = null;
}
} else if (details?.action === "clear") {
btwModelOverride = null;
}
}

if (isCustomEntry(branch[i], BTW_THINKING_OVERRIDE_TYPE)) {
const details = branch[i].data as BtwThinkingOverrideDetails | undefined;
const details = (branch[i] as unknown as { data?: BtwThinkingOverrideDetails }).data;
btwThinkingOverride =
details?.action === "set"
? details.thinkingLevel
Expand Down
Loading
Loading