feat(cli): two-pane home screen with session resume#240
Conversation
Scan the agents' own session dirs (~/.claude/projects, ~/.codex/sessions)
into a ranked list of resumable sessions — the same files the harness
tailer streams, so the list stays accurate with no separate store to
keep in sync. Each row resolves to { agent, id, cwd, label, lastActive }.
- Order current-folder-first, then most-recent, so resuming from where
you stand is at the top.
- Cost-bounded: only the newest N files are opened, and only their first
64 KiB parsed (enough for session meta + the first prompt).
- Label = the first human prompt, skipping <...>-wrapped system turns
(codex environment/permissions context, claude command/tool wrappers)
and tool-result turns that carry no text.
Covered by tests: label extraction for both agents, folder-first order,
limit, and single-line truncation.
Bare `tinyplace` now shows a two-pane home: the action list on the left (Start Codex / Start Claude / Connect OpenHuman / Settings / Quit) and a recent-session resume list on the right, the way Claude Code offers a resume picker. Tab and the arrow keys move the cursor between panes; the focused pane takes the cyan border and the live row highlight. Selecting a session relaunches its agent with the resume flag (`claude --resume <id>` / `codex resume <id>`) in the session's original working directory, so its files and OpenHuman folder-thread line up. The resumed bridge pins a stable, per-session OpenHuman scope (`tp-<agent>-resume-<id>`) so re-resuming the same session continues the same thread regardless of folder — fresh launches keep folder scope. Launch cwd is now threaded through the pty/child/tmux paths and the session monitor via effectiveCwd() so a resumed session is located and bridged in the folder it actually runs in. The non-TTY home snapshot gains a recent-sessions section too.
|
@oxoxDev is attempting to deploy a commit to the Vezures Team on Vercel. A member of the Team first needs to authorize it. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughAdds recent-session discovery for Claude and Codex, a two-pane TUI resume flow, provider-specific resumed launches with session locking and cwd preservation, resumed-session tailing support, and tests for history parsing, locking, and tail offsets. ChangesSession resume feature
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d43c33a892
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| return; | ||
| } | ||
| this.setProfile(session.agent, session.id); | ||
| this.launchCwd = session.cwd; |
There was a problem hiding this comment.
When resuming a session that appends to its existing JSONL file (Claude resumes do this, and Codex can as well), this code only carries the session cwd forward and drops session.path. The bridge tailer created later calls HarnessSessionTailer.start(), which snapshots all existing session files into ignoredSessionFiles and then only locates files not in that set, so it never tails the selected resumed file and OpenHuman continuity silently stops unless the agent creates a brand-new file. Please pass the selected path through as config.sessionFile or otherwise bypass the ignored-file filter for resume launches.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Addressed in 5ae0b0c — a resumed file predates the tailer's start snapshot, so it was in ignoredSessionFiles and never streamed. Threaded it through a new config.resumeSessionFile that un-ignores exactly that file (chose un-ignore over pinning sessionFile so a resume that instead spawns a fresh file is still located normally — codex can do that).
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
sdk/typescript/src/cli/tui.ts (1)
1117-1188: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winSplit the longer helpers to satisfy the 20-line function limit.
renderHomePanes,renderHomeActionsContent, andbuildAgentProfilenow exceed the repository limit; extract pane construction, OpenHuman status rendering, and resume-arg building into small helpers. As per coding guidelines, "Limit functions to a maximum of 20 lines of code; extract longer logic into separate helper functions".Also applies to: 1492-1540
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/typescript/src/cli/tui.ts` around lines 1117 - 1188, The home screen rendering helpers are too long and need to be split to satisfy the 20-line limit. Refactor renderHomePanes, renderHomeActionsContent, and buildAgentProfile by extracting the pane construction, OpenHuman status formatting, and resume-argument assembly into small helper methods with clear names, then keep the original methods as thin orchestrators. Use the existing symbols renderHomePanes, renderHomeActionsContent, buildAgentProfile, and the OpenHuman-related state fields to preserve behavior while reducing each function’s length.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@sdk/typescript/src/cli/session-history.ts`:
- Around line 119-136: Add JSDoc comments to the exported functions
claudeSessionsDir and codexSessionsDir in session-history.ts, matching the style
used on listRecentSessions. Briefly describe each function’s purpose and its env
parameter/return value so all public exports in this module are documented.
In `@sdk/typescript/src/cli/tui.ts`:
- Around line 1984-1989: Session labels are rendered directly in the recent
sessions list and can carry unsafe terminal control bytes. Update the rendering
in the session list builder that maps over sessions (and the same logic in the
other resume view referenced by the review) to sanitize `session.label` before
interpolating it, while still preserving Blessed tag escaping. Use the existing
session rendering path around `relativeTime(...)` and `session.label` to strip
control characters at display time.
- Around line 1979-1980: The non-TTY snapshot session ordering is using the
wrong working directory, causing `renderSnapshotSessions` to ignore the
configured cwd when `options.cwd` differs. Update `renderSnapshotSessions` to
use the same cwd source as interactive home mode, specifically
`this.effectiveCwd()` or an equivalent context-derived cwd, instead of
`process.cwd()`, so the “current folder first” ordering matches both TTY and
piped output paths.
In `@sdk/typescript/tests/session-history.test.ts`:
- Around line 86-94: The `writeSession` helper is using a dynamic `import()` for
`utimes`, which should be replaced with a top-level import to match the existing
`node:fs/promises` usage in this test file. Update the imports at the top of the
module to include `utimes` alongside `mkdtemp`, `mkdir`, and `writeFile`, then
remove the ներպ-function import inside `writeSession` and keep the rest of the
helper unchanged.
---
Nitpick comments:
In `@sdk/typescript/src/cli/tui.ts`:
- Around line 1117-1188: The home screen rendering helpers are too long and need
to be split to satisfy the 20-line limit. Refactor renderHomePanes,
renderHomeActionsContent, and buildAgentProfile by extracting the pane
construction, OpenHuman status formatting, and resume-argument assembly into
small helper methods with clear names, then keep the original methods as thin
orchestrators. Use the existing symbols renderHomePanes,
renderHomeActionsContent, buildAgentProfile, and the OpenHuman-related state
fields to preserve behavior while reducing each function’s length.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ab22ea32-9b13-4fd1-a26e-1e940ee2064a
📒 Files selected for processing (3)
sdk/typescript/src/cli/session-history.tssdk/typescript/src/cli/tui.tssdk/typescript/tests/session-history.test.ts
Address review on tinyhumansai#240: - Strip C0/DEL/C1 control bytes from labels at the source (truncateLabel), so a prompt carrying an escape sequence can't move the cursor or recolor the pane — covers every consumer (TTY rows, non-TTY snapshot, tests) in one place instead of at each render site. - Add the missing JSDoc on the exported claudeSessionsDir/codexSessionsDir. - Test file: import utimes at top level instead of a dynamic import(). - New test asserts control bytes are stripped from the label.
Address review on tinyhumansai#240: - A resumed session appends to a file that already existed when the tailer started, so it was swept into ignoredSessionFiles and never streamed — breaking OpenHuman continuity on resume. Thread the resumed file through a new HarnessWrapperConfig.resumeSessionFile that un-ignores just that file (without pinning, so a resume that spawns a fresh file still locates normally). - renderHomeSnapshot now takes the caller's cwd so the non-TTY 'current folder first' ordering matches interactive home mode instead of always using process.cwd().
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@sdk/typescript/src/cli/harness-wrapper.ts`:
- Around line 653-660: The resumed session replay starts from the wrong offset
because `poll()` resets `lineOffset` to 0 when `resumeSessionFile` is found,
causing already-existing transcript lines to be reread and republished. Update
the resume path in `harness-wrapper.ts`, especially the `start()` setup and the
`poll()` logic around `resumeSessionFile` and `lineOffset`, so the resumed
file’s initial `lineOffset` is seeded from its current line count before tailing
continues.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 72393c2a-8425-4614-a02a-a00e1e94bbe2
📒 Files selected for processing (4)
sdk/typescript/src/cli/harness-wrapper.tssdk/typescript/src/cli/session-history.tssdk/typescript/src/cli/tui.tssdk/typescript/tests/session-history.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- sdk/typescript/tests/session-history.test.ts
- sdk/typescript/src/cli/tui.ts
- sdk/typescript/src/cli/session-history.ts
Resuming pins a stable OpenHuman thread id (tp-<agent>-resume-<sessionId>) so re-resuming continues the same conversation. But two tinyplace instances resuming the SAME session at once share one wallet + one thread key: they double-stream into a single OpenHuman thread and two '<agent> --resume <id>' processes scribble into the same session JSONL. Fix is mutual exclusion, not key-uniqueness (uniqueness would defeat the resume continuity): take a per-session lockfile (agent+sessionId, under <config-dir>/locks) before launch and release it on teardown. A second instance sees it held and stays on the home screen with a notice; a dead holder's lock is reclaimed via a PID-liveness probe so a crash never wedges a resume. Lock filesystem errors degrade to 'allowed' rather than blocking. The deeper shared-wallet risks (two instances → one Signal ratchet + one inbox) predate this feature and any concurrent instance; tracked separately.
Address review on tinyhumansai#240: un-ignoring the resumed file made the tailer locate it, but poll() reset lineOffset to 0, so it re-read the whole file and republished every prior turn to OpenHuman on resume. Record the file's line count at tailer start as resumeStartOffset and seed lineOffset from it when the located path is the resumed file, so only turns appended after resume stream. Regression test drives the tailer over a pre-populated file + one appended turn and asserts only the new turn is emitted.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
sdk/typescript/tests/session-lock.test.ts (1)
38-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd coverage for reclaiming a lock whose holder PID is dead.
The suite covers corrupt/unreadable reclamation and a live-holder refusal, but not the primary
isStalepath: a lock stamped with a valid-but-dead numeric pid should be reclaimed. A stamp with an unused high pid (which makesprocess.kill(pid, 0)throwESRCH) exercises that branch directly.🧪 Suggested test
it("reclaims a lock whose holder process is dead", async () => { const dir = await lockDir(); const path = join(dir, "claude-sess-1.lock"); // A pid that is almost certainly not a running process → ESRCH on kill(pid, 0). await writeFile(path, JSON.stringify({ pid: 2 ** 22, at: Date.now() })); const lock = acquireSessionLock(dir, "claude", "sess-1"); expect(lock).toBeDefined(); const stamped = JSON.parse(await readFile(lock!.path, "utf8")); expect(stamped.pid).toBe(process.pid); await rm(dir, { recursive: true, force: true }); });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/typescript/tests/session-lock.test.ts` around lines 38 - 46, Add a test beside the existing corrupt-lock case that writes a valid JSON lock with an unused high numeric pid and current timestamp, then calls acquireSessionLock for the same lock. Assert the lock is reclaimed, its stamped pid equals process.pid, and clean up the temporary directory; import or use readFile as needed to inspect lock.path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@sdk/typescript/src/cli/session-lock.ts`:
- Around line 44-55: Replace the non-atomic unlinkSync() followed by writeLock()
sequence in the stale-lock branch of the session-lock acquisition logic with a
single atomic reclaim/handoff operation, updating the relevant lock helper such
as writeLock() to safely replace only the stale lock. Ensure competing
contenders cannot both successfully reclaim the same session, while preserving
the existing { path } return and failure behavior.
---
Nitpick comments:
In `@sdk/typescript/tests/session-lock.test.ts`:
- Around line 38-46: Add a test beside the existing corrupt-lock case that
writes a valid JSON lock with an unused high numeric pid and current timestamp,
then calls acquireSessionLock for the same lock. Assert the lock is reclaimed,
its stamped pid equals process.pid, and clean up the temporary directory; import
or use readFile as needed to inspect lock.path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2451c02b-1164-4d2b-903c-590cceb88fab
📒 Files selected for processing (3)
sdk/typescript/src/cli/session-lock.tssdk/typescript/src/cli/tui.tssdk/typescript/tests/session-lock.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- sdk/typescript/src/cli/tui.ts
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
sdk/typescript/tests/harness-resume-tail.test.ts (4)
20-48: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winKeep the fixture helper within the 20-line function limit.
resumeConfigspans Lines 20-48. Extract the shared configuration into a constant and leave this helper responsible only for applying the session-file overrides.As per coding guidelines, functions must be limited to 20 lines and longer logic should be extracted into helpers.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/typescript/tests/harness-resume-tail.test.ts` around lines 20 - 48, resumeConfig exceeds the 20-line function limit. Extract its shared HarnessWrapperConfig fields into a module-level constant, then have resumeConfig return that configuration with only sessionFile and resumeSessionFile overrides applied, preserving the existing values and behavior.Source: Coding guidelines
84-122: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAlign the async test callback with the repository rules.
The async Vitest callback has no explicit
Promise<void>return type and notry/catcherror handling. Add both, or explicitly document an exemption for test callbacks.As per coding guidelines, all TypeScript functions require explicit return types and async functions require try/catch handling.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/typescript/tests/harness-resume-tail.test.ts` around lines 84 - 122, Update the async test callback in “skips the existing transcript and emits only the post-resume turn” to explicitly return Promise<void> and wrap its body in try/catch error handling, preserving the existing assertions and cleanup behavior.Source: Coding guidelines
20-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse compliant TypeScript identifier names.
This file introduces non-camelCase or abbreviated identifiers:
EXISTING,resumeConfig,dirs,dir,out,_enc, andcb. Rename them to descriptive camelCase names such asexistingTranscript,createResumeConfiguration,directories,temporaryDirectory,outputStream,encoding, andcallback.As per coding guidelines, TypeScript variables, functions, and parameters must use camelCase and abbreviations must be avoided unless explicitly excepted.
Also applies to: 50-50, 76-76, 85-87, 90-100
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/typescript/tests/harness-resume-tail.test.ts` at line 20, The identifiers in the resume test harness violate the project’s naming conventions. Rename EXISTING, resumeConfig, dirs, dir, out, _enc, and cb to descriptive camelCase names such as existingTranscript, createResumeConfiguration, directories, temporaryDirectory, outputStream, encoding, and callback, updating all references across the affected functions and test cases.Source: Coding guidelines
35-39: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winAlso test the non-pinned resume-discovery path.
Because
sessionFileis set,HarnessSessionTailer.locateSession()returns through its explicit-file branch and does not consultignoredSessionFiles. This test therefore passes even ifresumeSessionFileis never un-ignored. Add a separate case withoutsessionFilethat discovers the resumed file fromsessionsDir.Also applies to: 97-104
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/typescript/tests/harness-resume-tail.test.ts` around lines 35 - 39, Add a separate test case in the harness resume-tail tests that omits `sessionFile` while setting `resumeSessionFile`, allowing `HarnessSessionTailer.locateSession()` to discover the file from `sessionsDir` and verify it is removed from `ignoredSessionFiles`; retain the existing pinned-file case for explicit-file behavior.sdk/typescript/src/cli/harness-wrapper.ts (1)
659-666: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winKeep
start()within the 20-line limit.Extract the resume-offset initialization or polling setup into helpers. As per coding guidelines, functions are limited to a maximum of 20 lines;
start()exceeds that limit.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/typescript/src/cli/harness-wrapper.ts` around lines 659 - 666, Reduce the length of start() to 20 lines or fewer by extracting the resume-session offset initialization and/or polling setup into focused private helper methods. Preserve the existing behavior involving ignoredSessionFiles, resumeStartOffset, and transcript streaming, and invoke the helpers from start().Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@sdk/typescript/src/cli/harness-wrapper.ts`:
- Around line 661-665: Canonicalize the configured resume session path in the
resume handling around resumeStartOffset, using the same canonical path for
ignoredSessionFiles deletion and the located.path comparison in the session-file
tailing logic. Ensure equivalent relative, absolute, and symlink paths resolve
identically, and add tests covering path aliases plus a competing fresh file so
resumed history is not replayed.
In `@sdk/typescript/tests/harness-resume-tail.test.ts`:
- Around line 118-121: In the resume-tail test, replace the containment
assertions for v1Texts(chunks) with an exact sequence assertion, verifying that
the emitted texts equal only the expected “fresh turn after resume” message in
the correct order and contain no additional or duplicate messages.
---
Nitpick comments:
In `@sdk/typescript/src/cli/harness-wrapper.ts`:
- Around line 659-666: Reduce the length of start() to 20 lines or fewer by
extracting the resume-session offset initialization and/or polling setup into
focused private helper methods. Preserve the existing behavior involving
ignoredSessionFiles, resumeStartOffset, and transcript streaming, and invoke the
helpers from start().
In `@sdk/typescript/tests/harness-resume-tail.test.ts`:
- Around line 20-48: resumeConfig exceeds the 20-line function limit. Extract
its shared HarnessWrapperConfig fields into a module-level constant, then have
resumeConfig return that configuration with only sessionFile and
resumeSessionFile overrides applied, preserving the existing values and
behavior.
- Around line 84-122: Update the async test callback in “skips the existing
transcript and emits only the post-resume turn” to explicitly return
Promise<void> and wrap its body in try/catch error handling, preserving the
existing assertions and cleanup behavior.
- Line 20: The identifiers in the resume test harness violate the project’s
naming conventions. Rename EXISTING, resumeConfig, dirs, dir, out, _enc, and cb
to descriptive camelCase names such as existingTranscript,
createResumeConfiguration, directories, temporaryDirectory, outputStream,
encoding, and callback, updating all references across the affected functions
and test cases.
- Around line 35-39: Add a separate test case in the harness resume-tail tests
that omits `sessionFile` while setting `resumeSessionFile`, allowing
`HarnessSessionTailer.locateSession()` to discover the file from `sessionsDir`
and verify it is removed from `ignoredSessionFiles`; retain the existing
pinned-file case for explicit-file behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e1b6f763-0b6b-4bed-bc7c-a64d2f7e3b08
📒 Files selected for processing (2)
sdk/typescript/src/cli/harness-wrapper.tssdk/typescript/tests/harness-resume-tail.test.ts
Delete-then-create let two contenders both reclaim a stale lock, and the second unlink could wipe the first winner's fresh lock, leaving two holders. Steal the stale file via an atomic renameSync (only one racer wins; the loser gets ENOENT) and publish with the existing exclusive wx create, so at most one holder survives. Adds a dead-pid reclaim test covering the isStale liveness path.
The resume path comes from session discovery, a different origin than listSessionFiles, so a symlinked/relative/absolute alias of the same file was compared by raw string equality. A mismatch left the file in the ignore set (never tailed) or failed the offset-seed check (replaying the whole transcript to OpenHuman). Resolve both the ignore-set removal and the located-path check through a canonical (realpath) identity. Tightens the resume-tail assertion to an exact sequence and adds symlink-alias + competing-fresh coverage.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (4)
sdk/typescript/src/cli/harness-wrapper.ts (2)
663-681: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract resume initialization from
start().The added canonicalization, ignore-set reconciliation, and offset setup make this public method exceed the repository’s 20-line limit. Move that block into a private helper so
start()remains focused on lifecycle setup.As per coding guidelines, TypeScript/JavaScript functions must be limited to a maximum of 20 lines; extract longer logic into separate helper functions.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/typescript/src/cli/harness-wrapper.ts` around lines 663 - 681, Extract the resume-session canonicalization, ignored-file reconciliation, and offset calculation from start() into a private helper method, such as initializeResumeSession(). Invoke it from start() at the same lifecycle point, preserving resumeSessionPath, ignoredSessionFiles, and resumeStartOffset behavior while keeping start() within the 20-line limit.Source: Coding guidelines
676-680: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winAvoid loading the entire transcript just to count lines.
readAllLines(...).lengthperforms synchronous full-file parsing and retains all existing lines during resume startup. Use a streaming newline counter or a helper that returns only the count to bound memory usage for large session transcripts.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/typescript/src/cli/harness-wrapper.ts` around lines 676 - 680, Replace the full-array `readAllLines(...).length` call in the resume initialization with a memory-bounded line-counting helper or streaming newline counter that returns only the transcript line count, and assign that result to `this.resumeStartOffset`.sdk/typescript/tests/harness-resume-tail.test.ts (2)
136-137: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReplace abbreviations with descriptive identifiers.
Names such as
dir,dirs,out,config,_enc, andcbviolate the repository’s abbreviation rule. Prefer names such astemporaryDirectory,output,tailerConfig,encoding, andcallback.As per coding guidelines: “Avoid abbreviations (unicorn/prevent-abbreviations) — exceptions:
db,arg,args,env,fn,prop,props,ref,refs.”Also applies to: 179-192
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/typescript/tests/harness-resume-tail.test.ts` around lines 136 - 137, Replace abbreviated identifiers throughout the test, including dir/dirs, out, config, _enc, and cb, with descriptive names such as temporaryDirectory, directories, output, tailerConfig, encoding, and callback; update all declarations and references consistently in the affected test helpers and callbacks.Source: Coding guidelines
192-192: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRemove the unnecessary double type assertion.
TinyPlaceCliOptionsonly has optional properties, so this can preserve type checking:- const options = { env: {} } as unknown as TinyPlaceCliOptions; + const options: TinyPlaceCliOptions = { env: {} };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/typescript/tests/harness-resume-tail.test.ts` at line 192, In the test setup, remove the unnecessary `unknown` intermediary from the `options` declaration and use a directly typed empty object, preserving TypeScript’s checking for the optional properties of `TinyPlaceCliOptions`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@sdk/typescript/tests/harness-resume-tail.test.ts`:
- Around line 201-214: Make transcript selection deterministic in the test
before invoking HarnessSessionTailer.start(): set fixed, distinct mtimes for
both transcript files using timestamps after the fixed startedAt, with real as
the newest. Remove the post-start utimesSync(real, new Date(), new Date()) call
and retain the append after start.
- Around line 135-219: Refactor the async test callback in “resolves a symlinked
resume path and ignores a competing fresh file” to stay under 20 lines: extract
fixture/configuration setup and tailer execution/assertions into named helpers.
Add explicit return types, including Promise<void> for every async helper and
the test callback, and wrap each async operation in try-catch handling
consistent with the test suite.
In `@sdk/typescript/tests/session-lock.test.ts`:
- Around line 51-52: Update the “reclaims a lock whose holder process is dead”
test callback to explicitly return Promise<void>, rename the dir variable to
directory, wrap the test body in the required try/catch handling, and move
cleanup into a finally block so it always runs.
- Around line 54-56: Replace the probabilistic PID fixture in the session-lock
test with a deterministic stale-process setup: spawn a child process, wait for
it to exit, and write its PID into the lock data, or mock the liveness probe
used by isStale. Keep the test focused on the primary liveness branch rather
than the corrupt-data path.
---
Nitpick comments:
In `@sdk/typescript/src/cli/harness-wrapper.ts`:
- Around line 663-681: Extract the resume-session canonicalization, ignored-file
reconciliation, and offset calculation from start() into a private helper
method, such as initializeResumeSession(). Invoke it from start() at the same
lifecycle point, preserving resumeSessionPath, ignoredSessionFiles, and
resumeStartOffset behavior while keeping start() within the 20-line limit.
- Around line 676-680: Replace the full-array `readAllLines(...).length` call in
the resume initialization with a memory-bounded line-counting helper or
streaming newline counter that returns only the transcript line count, and
assign that result to `this.resumeStartOffset`.
In `@sdk/typescript/tests/harness-resume-tail.test.ts`:
- Around line 136-137: Replace abbreviated identifiers throughout the test,
including dir/dirs, out, config, _enc, and cb, with descriptive names such as
temporaryDirectory, directories, output, tailerConfig, encoding, and callback;
update all declarations and references consistently in the affected test helpers
and callbacks.
- Line 192: In the test setup, remove the unnecessary `unknown` intermediary
from the `options` declaration and use a directly typed empty object, preserving
TypeScript’s checking for the optional properties of `TinyPlaceCliOptions`.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 711c4a41-b186-4452-9106-8741cd545764
📒 Files selected for processing (4)
sdk/typescript/src/cli/harness-wrapper.tssdk/typescript/src/cli/session-lock.tssdk/typescript/tests/harness-resume-tail.test.tssdk/typescript/tests/session-lock.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- sdk/typescript/src/cli/session-lock.ts
Pin the resume-tail transcript mtimes before start() (which polls immediately) so session selection never depends on the wall clock, and replace the probabilistic high-pid stale-lock fixture with a reaped child pid that reliably reports dead.
…story # Conflicts: # sdk/typescript/src/cli/tui.ts
…not the agent prompt The agents run inline (no alternate screen) inside the native tmux relay, so with tmux mouse capture off the scroll wheel bypassed tmux and reached the agent, scrolling its own prompt up and down instead of the scrollback. Enable `mouse on` so the wheel drives tmux copy-mode scrollback and the prompt stays anchored. Trade-off: drag-select now yields a tmux selection (still copies) rather than the host terminal's native selection.
|
@coderabbitai can you review once more and approve. |
|
✅ Action performedComments resolved and changes approved. Review finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
sdk/typescript/src/cli/harness-wrapper.ts (1)
1189-1206: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd JSDoc for the exported envelope helpers.
envelopeRoleandenvelopeTextare public exports without JSDoc contracts.As per coding guidelines, public functions must always have JSDoc comments.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/typescript/src/cli/harness-wrapper.ts` around lines 1189 - 1206, The exported envelopeRole and envelopeText helpers lack required JSDoc contracts. Add concise JSDoc comments directly above both functions describing their purpose, input envelope, and returned role or text value, preserving the existing implementations.Source: Coding guidelines
sdk/typescript/tests/session-lock.test.ts (1)
64-77: 🩺 Stability & Availability | 🔵 Trivial | 🏗️ Heavy liftAdd a concurrent stale-lock contention test.
This verifies single-process reclamation, but not the atomic rename and exclusive-create guarantee introduced to prevent two contenders from acquiring the same stale lock. Run two contenders against one stale lock and assert exactly one succeeds.
Based on the PR objectives, concurrent stale-lock reclamation is a core behavior of this change.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/typescript/tests/session-lock.test.ts` around lines 64 - 77, Add a test alongside the existing “reclaims a lock whose holder process is dead” case that creates one stale lock, starts two concurrent acquireSessionLock contenders for the same directory and session, and asserts exactly one returns a defined lock while the other fails. Verify the winning lock is stamped with the current process and clean up the lock directory afterward, exercising atomic stale-lock reclamation and exclusive creation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@sdk/typescript/src/cli/harness-wrapper.ts`:
- Around line 1005-1053: The echo suppression currently stores only one expiry
per text, so multiple envelopes from one injection or overlapping identical
injections are not tracked correctly. Update SessionEnvelopePublisher’s
injectedEchoes and markInjected to maintain multiple tokens per echoKey (such as
a count or expiry queue), and change isInjectedEcho to consume exactly one
unexpired token per matching envelope while pruning expired tokens.
---
Nitpick comments:
In `@sdk/typescript/src/cli/harness-wrapper.ts`:
- Around line 1189-1206: The exported envelopeRole and envelopeText helpers lack
required JSDoc contracts. Add concise JSDoc comments directly above both
functions describing their purpose, input envelope, and returned role or text
value, preserving the existing implementations.
In `@sdk/typescript/tests/session-lock.test.ts`:
- Around line 64-77: Add a test alongside the existing “reclaims a lock whose
holder process is dead” case that creates one stale lock, starts two concurrent
acquireSessionLock contenders for the same directory and session, and asserts
exactly one returns a defined lock while the other fails. Verify the winning
lock is stamped with the current process and clean up the lock directory
afterward, exercising atomic stale-lock reclamation and exclusive creation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ba185fb6-9d37-4241-b5ea-f629b79a901b
📒 Files selected for processing (5)
sdk/typescript/src/cli/harness-wrapper.tssdk/typescript/src/cli/session-lock.tssdk/typescript/src/cli/tui.tssdk/typescript/tests/harness-resume-tail.test.tssdk/typescript/tests/session-lock.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- sdk/typescript/tests/harness-resume-tail.test.ts
- sdk/typescript/src/cli/session-lock.ts
- sdk/typescript/src/cli/tui.ts
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 1
🧹 Nitpick comments (2)
sdk/typescript/src/cli/harness-wrapper.ts (1)
1189-1206: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd JSDoc for the exported envelope helpers.
envelopeRoleandenvelopeTextare public exports without JSDoc contracts.As per coding guidelines, public functions must always have JSDoc comments.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/typescript/src/cli/harness-wrapper.ts` around lines 1189 - 1206, The exported envelopeRole and envelopeText helpers lack required JSDoc contracts. Add concise JSDoc comments directly above both functions describing their purpose, input envelope, and returned role or text value, preserving the existing implementations.Source: Coding guidelines
sdk/typescript/tests/session-lock.test.ts (1)
64-77: 🩺 Stability & Availability | 🔵 Trivial | 🏗️ Heavy liftAdd a concurrent stale-lock contention test.
This verifies single-process reclamation, but not the atomic rename and exclusive-create guarantee introduced to prevent two contenders from acquiring the same stale lock. Run two contenders against one stale lock and assert exactly one succeeds.
Based on the PR objectives, concurrent stale-lock reclamation is a core behavior of this change.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/typescript/tests/session-lock.test.ts` around lines 64 - 77, Add a test alongside the existing “reclaims a lock whose holder process is dead” case that creates one stale lock, starts two concurrent acquireSessionLock contenders for the same directory and session, and asserts exactly one returns a defined lock while the other fails. Verify the winning lock is stamped with the current process and clean up the lock directory afterward, exercising atomic stale-lock reclamation and exclusive creation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@sdk/typescript/src/cli/harness-wrapper.ts`:
- Around line 1005-1053: The echo suppression currently stores only one expiry
per text, so multiple envelopes from one injection or overlapping identical
injections are not tracked correctly. Update SessionEnvelopePublisher’s
injectedEchoes and markInjected to maintain multiple tokens per echoKey (such as
a count or expiry queue), and change isInjectedEcho to consume exactly one
unexpired token per matching envelope while pruning expired tokens.
---
Nitpick comments:
In `@sdk/typescript/src/cli/harness-wrapper.ts`:
- Around line 1189-1206: The exported envelopeRole and envelopeText helpers lack
required JSDoc contracts. Add concise JSDoc comments directly above both
functions describing their purpose, input envelope, and returned role or text
value, preserving the existing implementations.
In `@sdk/typescript/tests/session-lock.test.ts`:
- Around line 64-77: Add a test alongside the existing “reclaims a lock whose
holder process is dead” case that creates one stale lock, starts two concurrent
acquireSessionLock contenders for the same directory and session, and asserts
exactly one returns a defined lock while the other fails. Verify the winning
lock is stamped with the current process and clean up the lock directory
afterward, exercising atomic stale-lock reclamation and exclusive creation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ba185fb6-9d37-4241-b5ea-f629b79a901b
📒 Files selected for processing (5)
sdk/typescript/src/cli/harness-wrapper.tssdk/typescript/src/cli/session-lock.tssdk/typescript/src/cli/tui.tssdk/typescript/tests/harness-resume-tail.test.tssdk/typescript/tests/session-lock.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- sdk/typescript/tests/harness-resume-tail.test.ts
- sdk/typescript/src/cli/session-lock.ts
- sdk/typescript/src/cli/tui.ts
🛑 Comments failed to post (1)
sdk/typescript/src/cli/harness-wrapper.ts (1)
1005-1053: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Track one suppression token per emitted echo.
injectedEchoesstores one expiry per text. WithemitV2, one injected session line can produce both v1 and v2 envelopes; two identical inbound prompts can also overlap. The first matching envelope consumes the only token, so the next is forwarded and may re-enter the auto-reply loop. Store a count or expiry queue and consume one token per matching envelope.Based on the supplied tailer/publisher flow, one injected session line can produce multiple outbound envelopes.
Also applies to: 1065-1068
🧰 Tools
🪛 ast-grep (0.44.1)
[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn as spawnChild } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').(detect-child-process-typescript)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/typescript/src/cli/harness-wrapper.ts` around lines 1005 - 1053, The echo suppression currently stores only one expiry per text, so multiple envelopes from one injection or overlapping identical injections are not tracked correctly. Update SessionEnvelopePublisher’s injectedEchoes and markInjected to maintain multiple tokens per echoKey (such as a count or expiry queue), and change isInjectedEcho to consume exactly one unexpired token per matching envelope while pruning expired tokens.
Summary
Bare
tinyplacenow opens a two-pane home screen: the action list on the left and a recent-session resume list on the right — the same picker experience Claude Code gives you, but spanning both agents. Pick a past session and it relaunches with the right resume flag, in its original folder, on the same OpenHuman thread.Motivation
The home menu could only start a fresh Codex/Claude session. To continue yesterday's work you had to remember the agent's own resume incantation and run it outside
tinyplace, which also broke the OpenHuman bridge's thread continuity. This puts "resume where I left off" one keystroke away and keeps the bridge attached.What changed
New read model —
session-history.tslistRecentSessions(env, cwd)scans the agents' own session dirs (~/.claude/projects,~/.codex/sessions) — the very files the harness tailer streams — so the list is always accurate with no separate store to keep in sync.<…>-wrapped system turns (codex environment/permissions context, claude command/tool wrappers) and text-less tool-result turns.Home UI —
tui.tsTaband←/→move the cursor between them; the focused pane takes the cyan border + live row highlight.↑↓move within it,Enteractivates.claude --resume <id>/codex resume <id>in the session's original cwd.tp-<agent>-resume-<id>) so re-resuming the same session continues the same thread regardless of folder; fresh launches keep the default folder scope.effectiveCwd(), so a resumed session is located and bridged in the folder it actually runs in.Testing
tests/session-history.test.ts(5 cases): label extraction for both agents, folder-first ordering, limit, single-line truncation, empty-dirs.pnpm --filter @tinyhumansai/tinyplace build— clean.Home mode ignores a provider-specific recipientreads the real~/.tinyplace/config.json; fails identically on cleanmain, passes in CI's isolated env).Notes
tui.ts.Summary by CodeRabbit