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
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ anchor: class Channel
created: 2026-10-14T17:33:22Z
norm: '1'
sig: c905677fa4be33c1
body_hash: edc1045f02d80405
raw_hash: 95969dbe14ea6781
vouched: 2026-10-22T21:41:26Z
body_hash: a3c1f1281c5b3997
raw_hash: 10f2879f7a794315
vouched: 2026-10-22T22:28:50Z
lines: 114-792
---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ anchor: class Channel
created: 2026-10-15T00:30:42Z
norm: '1'
sig: c905677fa4be33c1
body_hash: edc1045f02d80405
raw_hash: 95969dbe14ea6781
vouched: 2026-10-22T21:41:26Z
body_hash: a3c1f1281c5b3997
raw_hash: 10f2879f7a794315
vouched: 2026-10-22T22:28:50Z
lines: 114-792
---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ anchor: class Channel
created: 2026-10-15T02:34:37Z
norm: '1'
sig: c905677fa4be33c1
body_hash: edc1045f02d80405
raw_hash: 95969dbe14ea6781
vouched: 2026-10-22T21:41:26Z
body_hash: a3c1f1281c5b3997
raw_hash: 10f2879f7a794315
vouched: 2026-10-22T22:28:50Z
lines: 114-794
---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ anchor: class Channel
created: 2026-10-15T10:17:55Z
norm: '1'
sig: c905677fa4be33c1
body_hash: edc1045f02d80405
raw_hash: 95969dbe14ea6781
vouched: 2026-10-22T21:41:26Z
body_hash: a3c1f1281c5b3997
raw_hash: 10f2879f7a794315
vouched: 2026-10-22T22:28:50Z
lines: 121-817
---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ anchor: class Channel
created: 2026-10-16T22:11:35Z
norm: '1'
sig: c905677fa4be33c1
body_hash: edc1045f02d80405
raw_hash: 95969dbe14ea6781
vouched: 2026-10-22T21:41:26Z
body_hash: a3c1f1281c5b3997
raw_hash: 10f2879f7a794315
vouched: 2026-10-22T22:28:50Z
lines: 127-873
---

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
id: 01M5JVP06WMT6GF0BNWADVRA43
anchor: function events
created: 2026-10-22T22:28:36Z
norm: '1'
sig: 9791b5aa2005ab6b
body_hash: 6d8754eb6e1f376b
raw_hash: 150cd53f05e61cea
lines: 1201-1270
---

tool calls in an aborted or failed assistant message never ran: pi runs calls only from a finished reply, so they carry unrun
7 changes: 6 additions & 1 deletion apps/app/src/timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ function text(value: string): Event.Message.Content.Text {
return parts.length ? { type: "text", text: value, parts } : { type: "text", text: value };
}

const NOT_RUN = "Not run";

function tool(
tool: Tool,
busy: boolean,
Expand All @@ -110,8 +112,11 @@ function tool(
name: tool.name,
arguments: (tool.args ?? {}) as Record<string, unknown>,
status,
...(caption ? { caption } : {}),
...(tool.unrun ? { caption: NOT_RUN } : caption ? { caption } : {}),
...(recap ? { recap } : {}),
...(tool.unrun
? { result: { content: `${NOT_RUN}: the run ended while the agent was writing this call.` } }
: {}),
...(tool.result
? {
result: {
Expand Down
9 changes: 8 additions & 1 deletion apps/app/src/transcript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export type Tool = {
};
/** The running call's live output tail, until its result replaces it. */
progress?: { text: string; truncated?: boolean };
/** The reply ended while the agent was still writing this call, so it never ran. */
unrun?: boolean;
};

export type Item =
Expand Down Expand Up @@ -148,7 +150,12 @@ export function apply(state: Transcript, event: Event): Transcript {
return { ...state, draft: "", items: [...state.items, item] };
}
case "tool": {
const tool: Tool = { call: event.call, name: event.name, args: event.args };
const tool: Tool = {
call: event.call,
name: event.name,
args: event.args,
...(event.unrun ? { unrun: true } : {}),
};
const last = state.items.at(-1);
if (last?.kind === "reply" && !last.text) {
return {
Expand Down
1 change: 1 addition & 0 deletions apps/host/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ function render(event: Event, info: ChannelInfo) {
return console.log(`${tag}${bold(event.model)}: ${event.text}${ending}`);
}
case "tool": {
if (event.unrun) return console.log(dim(`${tag} → ${event.name} (not run)`));
if (event.name !== "question") {
return console.log(dim(`${tag} → ${event.name} ${clip(JSON.stringify(event.args), 100)}`));
}
Expand Down
18 changes: 13 additions & 5 deletions packages/channel/src/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ export class Channel {
case "kill":
this.#author(request.author);
await this.#mayInvoke(request.author);
return this.kill();
return this.kill(request.author);
case "share":
return this.#share(request);
case "desktop":
Expand Down Expand Up @@ -637,15 +637,21 @@ export class Channel {
return importSeed(this.#harness, value, shift);
}

async kill(): Promise<void> {
/** `author`'s kill records a Stop in each chat it ended, so their next runs know. */
async kill(author?: string): Promise<void> {
const { chats } = await this.info();
this.#log("warn", "channel.kill", {
chats: chats.filter((chat) => chat.busy).map((c) => c.id),
});
const busy = chats.filter((chat) => chat.busy);
this.#log("warn", "channel.kill", { chats: busy.map((c) => c.id) });
await Promise.all(chats.map(async (chat) => {
const conversation = await this.#harness.conversation(chat.id as ConversationId, context);
await conversation?.abort(context, { background: true });
}));
if (!author) return;
const at = Date.now();
await Promise.all(busy.map(async (chat) => {
const conversation = await this.#conversation(chat.id);
await conversation.submit({ type: "write", entry: room.stop(author, at) }, context);
}));
}

async close(): Promise<void> {
Expand Down Expand Up @@ -1235,6 +1241,8 @@ function events(entry: EntryRecord): Event[] {
call: block.id,
name: block.name,
args: block.arguments,
// pi runs calls only from a finished reply; a cut-off one's calls were never run.
...(error || stopped ? { unrun: true as const } : {}),
});
}
return out;
Expand Down
2 changes: 2 additions & 0 deletions packages/channel/src/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@ export type Event =
call: string;
name: string;
args: unknown;
/** The reply ended while the agent was still writing this call, so it never ran. */
unrun?: true;
}
/**
* The bounded tail of a running tool call's output, sent on attachment and as it grows, at most
Expand Down