Pi tools in QM expose configuration via PiToolsOptions (feature flags and timeouts) and derive execution-safe subsets (CoreToolOptions) that respect auth and read-only constraints. Tool result handling includes character capping, pagination defaults, and ordering logic — all designed to bound output and present lists consistently across the interface. A miniapp is an interactive UI produced by the QM orchestrator; miniapps are rendered inline in the web thread rather than served as detached links.
PiToolsOptions in src/harness/pi-tools.ts exposes feature flags for the tool set: scratchExec, ownerAuthExec, reachExec, controlTools, surfaceTools, surfaceName, readOnly, and timeout/TTL knobs (execTimeoutMs, execTimeoutCeilingMs, backgroundJobTtlMs, backgroundJobTtlMaxMs).[1] CoreToolOptions is PiToolsOptions with readOnly, surfaceTools, and surfaceName omitted — capturing only the options derived from global Config and shared across all surfaces.[1] coreToolOptions derives a CoreToolOptions from a Config object, enabling controlTools only when both signingSecret and apiBaseUrl are present in config.[1]
READ_ONLY_TOOL_NAMES enumerates the tools permitted even in read-only mode: memory, history, and finish_silently.[1] pauseStampAfterToolCall wraps a turn's afterToolCall hook so that the turn terminates immediately (returning { terminate: true }) whenever ref.pausedOnApproval or ref.silentRequested is set — halting the agent loop after an approval gate or a silent-finish request.[1]
Tool result text is capped at MAX_TOOL_RESULT_CHARS = 100_000 characters; when a result exceeds this, the middle is dropped — the first (100_000 − 10_000 − notice.length) characters and the last 10_000 (TRUNCATED_TAIL_CHARS) characters are kept, with a truncation notice inserted between them.[1] capPayloadStrings recursively walks arrays and plain objects to cap every string leaf via capResultText, but skips objects whose prototype is not Object.prototype or null — leaving class instances intact.[1]
List pagination defaults to LIST_PAGE_SIZE = 25 items per page with a hard ceiling of LIST_PAGE_MAX = 100; task body previews are truncated to LIST_TASK_PREVIEW_CHARS = 200 characters.[1] listOrder sorts items so that enabled (active) items come first, then paused items, then archived items; within each tier, items are sorted by createdAt descending, then by id lexicographically.[1] pageOf returns a note string describing pagination state: null when the entire list fits on one page, an "end of list" message when at the last page past offset 0, a "nothing at offset N" message when the slice is empty, and a "next page: offset: N" hint otherwise.[1]
Miniapp playgrounds are rendered inline in the web thread; the primary rendering surface is plugins/web-ui/src/miniapp.ts, with integration points in plugins/web-ui/src/chat.ts and plugins/portal/src/index.ts. The miniapp delivery contract — covering API routes and skill authoring expectations — is documented in src/api/routes/miniapps.ts and skills-seed/miniapp/SKILL.md.
Sources