Files
hapi/cli/src/pi
905d89e03a feat(pi): auto-title Pi sessions via bundled hapi_change_title extension (#1719)
* feat(pi): auto-title Pi sessions via bundled hapi_change_title extension

Pi sessions never got automatic titles: HAPI set PI_RPC_EMIT_TITLE=1 but
Pi does not implement it, and unlike the Claude/Codex/OpenCode launchers
the Pi bridge neither registers a change_title tool nor injects a title
instruction. This materializes a bundled Pi extension at launch and
passes it via --extension, giving Pi sessions the same titling flow:

- hapi_change_title tool (namespaced to avoid collisions with user
  extensions, mirroring the OpenCode launcher naming)
- first-turn system-prompt instruction matching the Claude/Codex wording
- title lands via ctx.ui.setTitle(), which the existing extension UI
  bridge already syncs into session metadata

Removes the dead PI_RPC_EMIT_TITLE env var. Requires pi >= 0.35.0
(when --extension landed, 2026-01).

Closes #1669 by giving sessions titles from the session model itself,
with zero extra title-provider calls.

* fix(pi): publish title extension atomically and keep retitle rule

Review findings from the HAPI PR bot:

- [Major] Publish the generated extension atomically. All Pi sessions of a
  HAPI version share the same versioned path and Pi treats extension-load
  errors as fatal, so a plain writeFile could break a concurrent launch with
  a half-written file. The extension is now written to a unique temp file and
  moved into place with rename; if rename loses a race against another
  launcher that already published the file, the existing copy is kept and the
  temp file is cleaned up.
- [Minor] Inject the title instruction on every before_agent_start instead of
  stopping after the first title, preserving the objective-change retitle
  rule from the persistent Claude/Codex instruction.

Adds a concurrent-materialization test and a handler test that executes the
title tool and asserts a later turn still carries the instruction.

* test(pi): fix handler return type in title extension test

* fix(pi): keep bundled title extension compatible with older Pi releases

Follow-up review finding: the embedded extension assumed three behaviors
that only exist in recent Pi releases, while HAPI has no Pi minimum-version
floor, so accepted older installations either failed to load the extension
or reached the tool without setting a title:

- import TypeBox via '@sinclair/typebox', the specifier both the legacy
  extension loaders and the current one (which aliases it to the bundled
  'typebox') resolve;
- normalize the execute() context positionally: current Pi passes
  (toolCallId, params, signal, onUpdate, ctx), legacy releases passed
  (toolCallId, params, onUpdate, ctx, signal);
- call ctx.ui.setTitle() directly instead of gating on ctx.hasUI, which
  older RPC releases report as false even though setTitle emits the
  extension UI event (this extension only runs under HAPI's RPC bridge).

Tests now exercise both call orders, including hasUI: false.

---------

Co-authored-by: HongChenGG <HongChenGG@users.noreply.github.com>
2026-09-09 09:31:08 +08:00
..