Skip to content

feat(listener): start a new session in a project from the phone - #35

Merged
tak-bro merged 1 commit into
mainfrom
feat/mobile-session-start
Nov 2, 2026
Merged

tak-bro merged 1 commit into
mainfrom
feat/mobile-session-start

Conversation

@tak-bro

@tak-bro tak-bro commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

Summary

폰에서 한 프로젝트의 세션을 새로 켤 수 있게 listener 를 넓힌다. 기존 다시 시작 요청(agent.session.resume.request)에 선택 필드 fresh: true 가 오면, 기록된 폴더·에이전트로 대화를 잇지 않는 새 tmux 세션을 띄운다. 이름이 실행 중이면 이름 가족의 다음 빈 이름을 쓴다. presence capabilities 에 session-start-v1 을 실어 앱이 이 기능을 아는 컴퓨터에만 버튼을 보이게 한다. 끄기는 이미 있는 agent.session.exit.request 를 앱이 보내므로 cli 변경이 없다.

앱 쪽: hwipit/hwip#672(시작) · hwipit/hwip#673(끄기, 스택). 설계 = hwip docs/session-runs.md Q47 · Q48.

Changes

  • src/listener.ts
    • handleSessionResumeRequest 에 fresh 분기를 넣었다. resumableConversation 을 건너뛰고(대화 id 없음), conversationRunning 거절도 건너뛴다. 실행 중 이름은 runId 가 없어도 familyNames(familyBase(name)) 의 다음 빈 이름으로 띄운다.
    • 가족 버킷 start:<base>(분당 3개)는 거절 검사를 다 통과하고 실제로 띄우기 직전에만 차감한다.
    • 로그는 + start 로 남긴다.
  • src/device-presence.ts — SESSION_START_CAPABILITY = 'session-start-v1' 을 presence 에 싣는다.
  • src/listener-session-resume.test.ts — fresh start describe 9개를 추가했다(가족 예산, 거절이 예산을 쓰지 않음 포함).
  • src/device-presence.test.ts — capability 목록 기대값을 갱신하고 케이스 1개를 추가했다.
  • README.md — 폰에서 켜기·끄기 한 문단.

How to Test

npx vitest run src/listener-session-resume.test.ts src/device-presence.test.ts
npx vitest run --exclude src/http-proxy.test.ts

Evidence

  • red → green:
    • fresh start: a refused start does not spend the family budget 는 차감 위치를 옮기기 전 코드에서 missing_directory 대신 resumed: true 를 받아 실패했고, 옮긴 뒤 통과한다.
    • fresh 케이스 전체는 구현 전 red 였다(- red: 행).
  • 로컬 검증:
    • cli-test = Tests 5650 passed | 1 skipped (5651)
    • s01-ac1 = Tests 10 passed | 46 skipped (56)
    • 둘 다 verify-check.sh --plan 기준이다.
  • src/http-proxy.test.ts 는 main(b2f6903)에서도 8건 실패한다(로컬 프록시 소켓 문제, 이 변경과 무관). 그래서 제외했다.

Risks

  • fresh 는 프로세스를 하나 더 남기므로 남용 상한이 두 겹이다.
    • 세션 이름 버킷 SUBMIT_COST 와 가족 버킷(분당 3개)이 걸린다.
    • 가족 이름은 최대 20개(MAX_SUFFIX_ATTEMPTS)이고, 다 차면 already_running 으로 거절한다.
  • 폰은 폴더·바이너리·인자를 정하지 못한다. 기록(recallRun/recallSession)에서만 고르고, tmux 는 argv 배열로 띄운다.
  • fixloop: 리뷰 fix 루프가 1회 돌았다(가족 버스트 Medium 은 가족 버킷으로 고침).

Merge danger

Door: two-way — 새 선택 필드와 capability 만 더한다. 옛 앱은 fresh 를 보내지 않고, 옛 cli 는 capability 가 없어 새 앱이 버튼을 숨긴다. Blast radius: listener.

Alternatives considered

  • 새 agent.session.start.* 와이어 — 사용자 결정으로 resume + fresh 를 골랐다. 중계·권한·서브에이전트 거절·rate limit 을 그대로 쓰므로 서버 코드가 0이다.

Auto-decided

  • 끄기 capability 게이트 없음 — exit 핸들러는 cli 첫 릴리스부터 있다.
  • fresh 이름 = 끝난 이름이면 그 이름, 실행 중이면 가족의 다음 빈 이름(Q41 와 같은 규칙).
  • 남용 상한: 처음엔 기존 상한만 두기로 했으나 보안 스캔 Medium 으로 가족 버킷을 더했다.

Acceptance

  • AC-1 · PASS · verify-check.sh --plan @hwip 08f951f5 → s01-ac1 green
  • AC-7 · PASS · cli-test green (5650 passed)
  • audit: ok (5 checked rows at 08f951f5)

사람 할 일

  • 실제 listener 로 켜기·끄기 E2E(hwip PR 의 AC-3 · AC-5). launchd listener 교체가 권한 분류기에 막혀 돌리지 못했다. 절차는 hwip 시작 PR 에 있다.
  • 배포 순서: 이 cli 를 먼저 배포한다. 앱은 session-start-v1 을 안 보면 '새 세션' 버튼을 숨기므로 순서가 바뀌어도 깨지지는 않는다.

Checklist

  • 테스트 추가·갱신
  • README 갱신
  • 보안 스캔(브랜치 변경분) Critical·High·Medium 0

🤖 Generated with Claude Code

A resume request with `fresh: true` starts the agent recorded for that
session in its directory without picking up any conversation. When the
name is live, it starts beside it under the next free family name
instead of being refused. Each family pays on its own bucket, three new
sessions a minute, so one recorded name cannot fill all 20 family names
in a burst.

The listener advertises `session-start-v1` in its presence so the apps
offer the action only to a build that reads `fresh`.
@tak-bro
tak-bro marked this pull request as ready for review November 2, 2026 06:50
@tak-bro
tak-bro merged commit 8e2daa1 into main Nov 2, 2026
4 checks passed
@tak-bro
tak-bro deleted the feat/mobile-session-start branch November 2, 2026 06:54
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 0.1.17 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant