mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
feat(p2): the terminal tool needs a turn the owner started
This commit is contained in:
parent
5c6ce2b32f
commit
2b78884e3a
3 changed files with 16 additions and 9 deletions
|
|
@ -888,8 +888,9 @@ translated phone drawer and intercept chat taps.
|
|||
viewer or the agent); a panel showing a terminal keeps it, one showing none attaches the newest new one. Archiving a
|
||||
session closes its terminals (`SessionArchived`, a host-lifetime contribution; no host is started for it).
|
||||
**Agent terminal tool** (owner decisions): the agent drives the same terminals the owner sees (shared: the owner
|
||||
watches and types; the agent may read, type into and close any terminal of its session, the owner's too). Guest
|
||||
turns may use it, like bash. Code mode only, like MCP tools (`deferred`, `callers: ["tools"]`; scripts find it with
|
||||
watches and types; the agent may read, type into and close any terminal of its session, the owner's too). Only
|
||||
turns the owner started may use it (`origin: "owner"`, owner decision): a terminal may be connected beyond the
|
||||
session's environment (ssh, a waiting sudo prompt), where a guest's bash would not reach. Code mode only, like MCP tools (`deferred`, `callers: ["tools"]`; scripts find it with
|
||||
`searchTools()`); while code mode is off `tool_search` loads it (installed once by `durableRegistry`, not by each
|
||||
plugin: two plugins contributing `tool-search` would be a registry conflict). The description says to use it only
|
||||
when the user asks for a terminal or for driving an interactive program in one, never instead of bash; no prompt
|
||||
|
|
@ -1065,7 +1066,8 @@ translated phone drawer and intercept chat taps.
|
|||
`origin` (`"any"` default, or `"owner"`: only a turn the owner started) and `scope` (`"any"` default, `"main"`: only
|
||||
top-level conversations, or `"subagent"`: given to subagents only). The worker's Durable registry wraps every tool
|
||||
with requirements (`guardTool`, in `durableRegistry`), so no tool checks them itself. `origin: "owner"`: the location
|
||||
tools, `reload_plugins`, `conversations`, `ports`, MCP tools (the owner's accounts are behind them). A turn's origin
|
||||
tools, `reload_plugins`, `conversations`, `ports`, MCP tools (the owner's accounts are behind them), `terminal`
|
||||
(the owner's terminals, possibly connected beyond the session's environment). A turn's origin
|
||||
(`originOf`, `src/runtime/origin.ts`) is the conversation's current run (`pi.live.run.inputs`, each submission's
|
||||
entry through `tx.submission`): the owner's only if every input the run placed is (`entryOrigin`): a person's input
|
||||
by its author in the entry data, an agent-written one by its `origin`. Agent-written inputs record the writing
|
||||
|
|
|
|||
|
|
@ -1,17 +1,19 @@
|
|||
// The agent's `terminal` tool (worker): tmux commands as argv on the session's terminals, the ones the owner sees in
|
||||
// the terminal panel. Code mode scripts call it (`tools.terminal({ command, args })`); without code mode `tool_search`
|
||||
// loads it, as MCP tools. Replay-safe: the host does a call with the tool call's key once, and reads start where the
|
||||
// first attempt's did (memoized), so a call a restart interrupted runs again without typing or opening twice.
|
||||
// loads it, as MCP tools. Only turns the owner started may call it (`origin: "owner"`): the terminals are the owner's
|
||||
// too, possibly connected where the session's `bash` would not reach (an ssh session, a waiting sudo prompt).
|
||||
// Replay-safe: the host does a call with the tool call's key once, and reads start where the first attempt's did
|
||||
// (memoized), so a call a restart interrupted runs again without typing or opening twice.
|
||||
|
||||
import {
|
||||
defineExtension,
|
||||
defineTool,
|
||||
type Extension,
|
||||
type ToolExecutionApi,
|
||||
type ToolExecutionResult,
|
||||
} from "@earendil-works/pi-durable";
|
||||
import { arr, bool, int, obj, opt, type Static, str, union } from "@earendil-works/pi-schema";
|
||||
import { type Context, isRooted, joinPath, type Location, literal } from "@p2/app/v1";
|
||||
import { defineP2Tool, type P2Tool } from "@p2/app/v1/worker";
|
||||
import {
|
||||
COMMANDS,
|
||||
CommandError,
|
||||
|
|
@ -99,11 +101,11 @@ export function createTerminalExtension(
|
|||
reach: TerminalReach,
|
||||
backend: TerminalBackend,
|
||||
locate: (api: ToolExecutionApi, context: Context) => Promise<Location>,
|
||||
): Extension {
|
||||
return defineExtension({
|
||||
): Extension<P2Tool> {
|
||||
return defineExtension<P2Tool>({
|
||||
name: TERMINAL_EXTENSION,
|
||||
tools: [
|
||||
defineTool({
|
||||
defineP2Tool({
|
||||
name: "terminal",
|
||||
description: DESCRIPTION,
|
||||
parameters: obj({
|
||||
|
|
@ -116,6 +118,7 @@ export function createTerminalExtension(
|
|||
...(reach === "codemode" ? { callers: ["tools" as const] } : {}),
|
||||
outputLimits: { maxBytes: OUTPUT_CHARS + 1024, retain: "tail" },
|
||||
replay: "safe",
|
||||
origin: "owner",
|
||||
execute: async (args, api, context) => {
|
||||
let command: TerminalCommand;
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -108,6 +108,8 @@ test("code mode only while code mode is on; tool_search reaches it otherwise", (
|
|||
assert.deepEqual(terminal(backend).callers, ["tools"]);
|
||||
assert.equal(terminal(backend).deferred, true);
|
||||
assert.equal(terminal(backend).replay, "safe");
|
||||
// The owner's terminals: only turns the owner started (checked by the worker's registry).
|
||||
assert.equal(terminal(backend).origin, "owner");
|
||||
assert.equal(terminal(backend, "tool_search").callers, undefined);
|
||||
assert.equal(terminal(backend, "tool_search").deferred, true);
|
||||
assert.match(terminal(backend).description, /Only when the user asks for a terminal/);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue