Commit Graph
16 Commits
Author SHA1 Message Date
cd99cfbc25 fix(hub): preserve session metadata across archive transitions (#825)
* fix(hub): preserve flavor session ids in metadata across archive transitions

When a session ends (terminate, crash, local-launch failure, handoff),
the runner's archive write replaces sessions.metadata wholesale. If the
CLI's locally cached Metadata is null (e.g. Zod parse failed at bootstrap
and api.ts nulled it out) or stale, the spread in archiveAndClose ships
a sparse blob and the resume token (cursorSessionId, codexSessionId,
claudeSessionId, etc.) gets cleared from the row even though the
on-disk chat data is still intact.

Fix at the hub layer because update-metadata is the single chokepoint
for every metadata write surface (CLI, web, future): in the store-level
updateSessionMetadata, read the prior row's metadata inside a
transaction and carry forward a small allowlist of flavor resume tokens
when the incoming write omits them. Explicit overwrites still win.

The allowlist mirrors pickExistingSessionMetadata in sessionFactory.ts
which already preserves the same fields on bootstrap.

Closes tiann/hapi#820

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(hub): address cold-review findings on metadata merge

Three bot findings on the initial patch:

1. (P1) Sparse archive payloads still resulted in metadata blobs that
   failed MetadataSchema parse downstream — required `path`/`host` were
   not in the carry-forward set, so even though the resume token
   survived, hub session cache and CLI getSession nulled-out the row
   and resume_unavailable came back. Add PARSE_IDENTITY_FIELDS = `path`,
   `host` to the carry-forward.

2. (P2) Preserving `cursorSessionProtocol` whenever it was omitted
   carried a stale protocol over to a freshly written `cursorSessionId`,
   misrouting a future remote resume. Pair-aware logic: drop the prior
   protocol when next sets a new id; preserve the protocol only when
   next is silent on both id and protocol.

3. (P2) The successful update-metadata broadcast emitted the pre-merge
   payload to other CLIs in the session room, so even though the DB row
   was preserved, peer caches diverged. Switch the broadcast value to
   `result.value` (the persisted merged value) so live caches stay in
   sync with the truth.

Refactor preserveProtocolResumeFields into mergeSessionMetadata with
two tiers (PARSE_IDENTITY_FIELDS + SIMPLE_RESUME_TOKENS) plus the
cursor pair handler. 6 new tests cover the regressions; existing 16
still pass plus 1 new socket-level test for the broadcast.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(hub): preserve flavor + machineId across sparse metadata merges

Bot P2 on the prior fix: PARSE_IDENTITY_FIELDS (path, host) made the
blob parseable and SIMPLE_RESUME_TOKENS preserved the chat-id, but
flavor and machineId were still being dropped by sparse archive
payloads. Consequences:

- flavor: hub/src/web/routes/sessions.ts and sync/syncEngine.ts read
  `metadata?.flavor ?? 'claude'` to pick which session id field to
  resume. With flavor missing, a Cursor/Codex/Gemini session was
  routed as Claude and the preserved cursorSessionId was ignored.

- machineId: telegram/bot.ts and the CLI's resumable listing read
  `metadata?.machineId` to scope sessions to the current host. With
  machineId missing, the row dropped out of the resume picker.

Add a third carry-forward tier ROUTING_FIELDS = [flavor, machineId]
between PARSE_IDENTITY_FIELDS and SIMPLE_RESUME_TOKENS in
mergeSessionMetadata. 3 new tests cover preservation, no-invention,
and explicit override.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(hub,cli): support explicit-clear sentinel for carry-forward fields

Upstream cold-review (Major): the carry-forward semantics introduced
in the prior commits ("omit field → preserve from prior") collide
with cli/src/codex/session.ts resetCodexThread(), which intentionally
clears codexSessionId by deleting it from the metadata blob before
calling updateMetadata. With omit-as-preserve, the cleared id was
restored from the prior row and /clear on a Codex session no longer
dropped the persisted thread.

Add an explicit-clear sentinel: when next sets a carry-forward field
to `null`, the merge drops the key entirely from the persisted blob
(key removed; not stored as null since MetadataSchema fields are
`string().optional()`). `undefined` (key missing from next) keeps its
"carry forward" meaning. The two semantics now compose cleanly:

  - next.field = "x"   → next wins (caller sets a new value)
  - next.field = null  → drop the field (caller intentionally clears)
  - next omits field   → carry forward prior (caller didn't touch it)

Update resetCodexThread() to send `codexSessionId: null` so the
reset actually drops the persisted thread under the new merge.

4 new hub tests cover: explicit clear of a single token, clear-one-
preserve-others independence, no-op clear on a never-set field, and
the success-ack value reflects the cleared blob. cli/src/codex tests
(224/224) and hub suite (301/301) green; bun typecheck clean.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-08 13:28:00 +08:00
f9ef3a4489 feat(codex): import local Codex sessions into Hapi (#796)
* local: add Codex Desktop session sync controls

* feat(codex): import local Codex sessions into Hapi

---------

Co-authored-by: Codex Local <codex-local@example.invalid>
2026-06-04 17:53:12 +08:00
NightWatcher314andGitHub 66e41c90d4 fix(codex): support app-server plan mode (#622)
* fix(codex): support app-server plan mode

* fix(codex): broaden plan mode compatibility checks
2026-05-15 10:56:05 +08:00
NightWatcher314andGitHub a612be50d6 Add Codex clear and compact slash commands (#541)
* Add Codex clear and compact slash commands

* Stabilize queued thinking event test

* Interrupt active Codex turn before slash commands
2026-04-29 14:15:44 +08:00
weishu 8e54bbeb53 feat(codex): use hook to obtain the session id and transcript path 2026-04-24 10:52:38 +08:00
weishu 79a13d26c6 Fix Codex reasoning effort resume and updates 2026-04-10 11:50:03 +08:00
Junmo KimandGitHub d76b1a6ac0 refactor: introduce model-agnostic agent interfaces (#323) 2026-03-20 08:45:32 +08:00
weishu 16829b7c78 Add support for codex plan mode 2026-03-16 20:48:39 +08:00
weishu 0228146b99 refactor: rename daemon to runner throughout codebase 2026-01-19 11:12:48 +08:00
weishu 334cebc2da feat: add real-time permission and model mode synchronization close #21
Implements bidirectional sync of permission/model modes between CLI sessions and web app. Adds Codex-specific permission modes (read-only, safe-yolo, yolo) alongside Claude's modes. Web can now control CLI session state via RPC set-session-config handler, while CLI broadcasts state changes through keep-alive payloads. UI controls are flavor-aware, showing appropriate modes for Claude vs Codex vs Gemini. Type centralization in api/types eliminates circular dependencies.
2025-12-28 20:51:18 +08:00
weishu eee7a249fa feat: implement unified local launch failure handling policy 2025-12-26 16:11:58 +08:00
weishu 2a10ce9675 feat: add support for user message events from Codex
Add sendUserMessage() method to ApiSessionClient and CodexSession to handle
user_message events from Codex. Updates CodexConversionResult type and
convertCodexEvent() to process user_message payloads, with test coverage.
2025-12-24 17:24:16 +08:00
weishu 322c50e5a9 refactor(codex): extract config building and support CLI overrides in remote mode
This refactoring introduces CLI argument overrides for sandbox and approval policy settings in remote mode, allowing users to specify security constraints via `--sandbox` and `--ask-for-approval` flags.

Changes:
- Add `CodexCliOverrides` type and `parseCodexCliOverrides()` utility to parse CLI flags like `--sandbox`, `-s`, `--ask-for-approval`, `-a`, along with convenience flags (`--full-auto`, `--dangerously-bypass-approvals-and-sandbox`)
- Extract complex start config building logic into `buildCodexStartConfig()` function with proper approval policy and sandbox resolution based on permission mode
- Thread `codexCliOverrides` through the session/loop/launcher chain and apply overrides only when permission mode is 'default'
- Update `codexRemoteLauncher` to use the new config builder and display appropriate warnings based on whether overrides are present
- Add comprehensive tests for both parsing and config building functions
2025-12-23 18:43:01 +08:00
weishu f132a7dc79 feat(codex): implement CLI argument passthrough for codex
Collect unknown CLI arguments from index.ts and pass them through the execution chain (runCodex → loop → CodexSession → codexLocalLauncher → codexLocal), similar to how claude already works. This enables users to pass CLI arguments like --model and --sandbox to the underlying codex process.

Filter out the 'resume' subcommand which is managed internally by hapi, while allowing other CLI arguments to pass through. Add warning log in remote mode when CLI args are ignored since remote mode uses message-based configuration instead. Include unit tests for the resume filtering logic.
2025-12-23 09:06:08 +08:00
weishu 360e3a9919 refactor: extract session and loop logic into reusable agent base classes 2025-12-22 23:12:24 +08:00
weishu 4290f539bd refactor(codex): extract session management and launcher logic into separate modules
Reorganized runCodex.ts to improve maintainability by extracting:
- CodexSession class for session lifecycle management
- CodexLocalLauncher and CodexRemoteLauncher for mode-specific initialization
- CodexEventConverter for MCP message handling and UI buffer updates
- CodexSessionScanner for resume file discovery
- emitReadyIfIdle utility for ready event emission

Added codexSessionId field to metadata schema for session tracking.
Updated UI components to work with refactored architecture.
2025-12-22 19:14:47 +08:00