diff --git a/.semgrep/architecture.yaml b/.semgrep/architecture.yaml index ac47f466..adb6141d 100644 --- a/.semgrep/architecture.yaml +++ b/.semgrep/architecture.yaml @@ -1001,3 +1001,67 @@ rules: metadata: category: architecture violation: socket-churn + + # --------------------------------------------------------------------------- + # 11. PR cache enforcement: daemon must use cached/batched PR lookups + # + # The daemon MUST NOT call uncached PR lookup functions directly. + # These functions shell out to `gh` CLI on every call with no caching, + # which exhausts the GitHub API rate limit (5000/hr) with just ~4 active runs. + # + # Allowed: pr.PopulateRunInfo() / pr.PopulateRunInfoWithClient() — batched + cached + # Banned: pr.LookupInfo() / pr.LookupInfoByURL() / pr.LookupInfoWithClient() + # + # Tracked by: orch-450 + # --------------------------------------------------------------------------- + + - id: daemon-no-uncached-pr-lookup + patterns: + - pattern-either: + - pattern: pr.LookupInfo(...) + - pattern: pr.LookupInfoWithClient(...) + paths: + include: + - /internal/daemon/ + exclude: + - /internal/daemon/*_test.go + # Known debt (orch-450) — remove exclusions as each file is fixed + - /internal/daemon/monitor.go + - /internal/daemon/types.go + - /internal/daemon/proto_handler.go + message: > + Daemon must not call pr.LookupInfo() or pr.LookupInfoWithClient() directly. + These bypass the PR cache and shell out to `gh pr list` on every call, + exhausting the GitHub API rate limit (5000/hr). + Fix: Use pr.PopulateRunInfo() or pr.PopulateRunInfoWithClient() which + batch lookups and respect cache TTLs (cacheHitTTL, cacheMissTTL, cacheMaxFetches). + See: orch-450 + severity: ERROR + languages: [go] + metadata: + category: architecture + violation: rate-limit-bypass + + - id: daemon-no-uncached-pr-lookup-by-url + patterns: + - pattern: pr.LookupInfoByURL(...) + paths: + include: + - /internal/daemon/ + exclude: + - /internal/daemon/*_test.go + # Known debt (orch-450) — remove exclusions as each file is fixed + - /internal/daemon/monitor.go + - /internal/daemon/types.go + - /internal/daemon/proto_handler.go + message: > + Daemon must not call pr.LookupInfoByURL() directly. + This bypasses the PR cache and shells out to `gh pr view` on every call, + exhausting the GitHub API rate limit (5000/hr). + Fix: Use a cache-aware lookup that respects TTLs, or batch via PopulateRunInfo. + See: orch-450 + severity: ERROR + languages: [go] + metadata: + category: architecture + violation: rate-limit-bypass diff --git a/CLAUDE.md b/CLAUDE.md index dcd11367..81dfeaf0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,14 +28,14 @@ When your PR has conflicts with main after other PRs are merged: ### Preferred Approach: Rebase via Feedback -1. **DO**: Use `orch send "rebase message"` to send feedback to the blocked run +1. **DO**: Use `orch send "rebase message"` to send feedback to the waiting run 2. **DO**: Let the agent resolve conflicts in its existing session context 3. **DO**: Wait for the agent to force push the rebased branch ### Avoid 1. **DON'T**: Close the PR and restart the run from scratch -2. **DON'T**: Cancel the run just because it's blocked +2. **DON'T**: Cancel the run just because it's waiting 3. **DON'T**: Manually resolve conflicts when the agent can do it ### Why This Matters @@ -48,9 +48,9 @@ When your PR has conflicts with main after other PRs are merged: ### Example ```bash -# Run is blocked with conflicts +# Run is waiting with conflicts orch ps -# Shows: 75731b orch-383 block conflict +# Shows: 75731b orch-383 wait conflict # Send feedback to resume orch send 75731b "Your PR has conflicts with main. Please run: git fetch origin main && git rebase origin/main - then resolve conflicts and force push." @@ -66,13 +66,13 @@ orch send 75731b "Your PR has conflicts with main. Please run: git fetch origin ## Run Lifecycle ``` -queued → booting → running ⟷ blocked → done +queued → booting → running ⟷ waiting → done ↓ ↓ fail cancel ``` -- `blocked`: Run is waiting for user input (use `orch send`) -- `blocked_api`: Run is waiting for API response +- `waiting`: Run is waiting for user input (use `orch send`) +- `rate_limited`: Run is waiting for API response - `done`: Run completed successfully - `fail`: Run encountered an error - `cancel`: Run was manually cancelled @@ -119,7 +119,7 @@ Existing violations are tracked; do not add new ones. ### "Connection refused" when using `orch send` -The opencode server may have stopped while the run shows as "blocked". +The opencode server may have stopped while the run shows as "waiting". - Check if the run is actually alive: look at the ALIVE column in `orch ps` - If server stopped, use `orch continue ` to restart diff --git a/README.md b/README.md index 4273c184..d42522e3 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ orch attach my-task - **Issue**: A task specification (markdown file or external ticket) - **Run**: One execution attempt for an issue (isolated worktree + branch) - **Event**: Append-only log entry tracking run progress -- **Status**: Current state derived from events (running, blocked, done, etc.) +- **Status**: Current state derived from events (running, waiting, done, etc.) ``` User runs: orch run my-issue @@ -99,7 +99,7 @@ User interacts: orch attach my-issue | Status | Meaning | User Action | |--------|---------|-------------| | `running` | Agent is working | Wait, or attach to watch | -| `blocked` | Agent needs input | `orch attach` to help | +| `waiting` | Agent needs input | `orch attach` to help | | `pr_open` | PR created | Review the PR | | `done` | Completed | Celebrate! | | `failed` | Error occurred | Check logs, retry | diff --git a/api/orch.pb.go b/api/orch.pb.go index 280b16c9..17ef5fff 100644 --- a/api/orch.pb.go +++ b/api/orch.pb.go @@ -24,16 +24,16 @@ const ( type RunStatus int32 const ( - RunStatus_RUN_STATUS_UNSPECIFIED RunStatus = 0 - RunStatus_RUN_STATUS_QUEUED RunStatus = 1 - RunStatus_RUN_STATUS_BOOTING RunStatus = 2 - RunStatus_RUN_STATUS_RUNNING RunStatus = 3 - RunStatus_RUN_STATUS_BLOCKED RunStatus = 4 - RunStatus_RUN_STATUS_BLOCKED_API RunStatus = 5 - RunStatus_RUN_STATUS_PR_OPEN RunStatus = 6 - RunStatus_RUN_STATUS_DONE RunStatus = 7 - RunStatus_RUN_STATUS_FAILED RunStatus = 8 - RunStatus_RUN_STATUS_CANCELED RunStatus = 9 + RunStatus_RUN_STATUS_UNSPECIFIED RunStatus = 0 + RunStatus_RUN_STATUS_QUEUED RunStatus = 1 + RunStatus_RUN_STATUS_BOOTING RunStatus = 2 + RunStatus_RUN_STATUS_RUNNING RunStatus = 3 + RunStatus_RUN_STATUS_WAITING RunStatus = 4 + RunStatus_RUN_STATUS_RATE_LIMITED RunStatus = 5 + RunStatus_RUN_STATUS_PR_OPEN RunStatus = 6 + RunStatus_RUN_STATUS_DONE RunStatus = 7 + RunStatus_RUN_STATUS_FAILED RunStatus = 8 + RunStatus_RUN_STATUS_CANCELED RunStatus = 9 ) // Enum value maps for RunStatus. @@ -43,24 +43,24 @@ var ( 1: "RUN_STATUS_QUEUED", 2: "RUN_STATUS_BOOTING", 3: "RUN_STATUS_RUNNING", - 4: "RUN_STATUS_BLOCKED", - 5: "RUN_STATUS_BLOCKED_API", + 4: "RUN_STATUS_WAITING", + 5: "RUN_STATUS_RATE_LIMITED", 6: "RUN_STATUS_PR_OPEN", 7: "RUN_STATUS_DONE", 8: "RUN_STATUS_FAILED", 9: "RUN_STATUS_CANCELED", } RunStatus_value = map[string]int32{ - "RUN_STATUS_UNSPECIFIED": 0, - "RUN_STATUS_QUEUED": 1, - "RUN_STATUS_BOOTING": 2, - "RUN_STATUS_RUNNING": 3, - "RUN_STATUS_BLOCKED": 4, - "RUN_STATUS_BLOCKED_API": 5, - "RUN_STATUS_PR_OPEN": 6, - "RUN_STATUS_DONE": 7, - "RUN_STATUS_FAILED": 8, - "RUN_STATUS_CANCELED": 9, + "RUN_STATUS_UNSPECIFIED": 0, + "RUN_STATUS_QUEUED": 1, + "RUN_STATUS_BOOTING": 2, + "RUN_STATUS_RUNNING": 3, + "RUN_STATUS_WAITING": 4, + "RUN_STATUS_RATE_LIMITED": 5, + "RUN_STATUS_PR_OPEN": 6, + "RUN_STATUS_DONE": 7, + "RUN_STATUS_FAILED": 8, + "RUN_STATUS_CANCELED": 9, } ) @@ -350,6 +350,11 @@ type Run struct { ContinuedFrom string `protobuf:"bytes,19,opt,name=continued_from,json=continuedFrom,proto3" json:"continued_from,omitempty"` PrNumber int32 `protobuf:"varint,20,opt,name=pr_number,json=prNumber,proto3" json:"pr_number,omitempty"` PrState string `protobuf:"bytes,21,opt,name=pr_state,json=prState,proto3" json:"pr_state,omitempty"` + IssueStatus string `protobuf:"bytes,22,opt,name=issue_status,json=issueStatus,proto3" json:"issue_status,omitempty"` + IssueTopic string `protobuf:"bytes,23,opt,name=issue_topic,json=issueTopic,proto3" json:"issue_topic,omitempty"` + Alive bool `protobuf:"varint,24,opt,name=alive,proto3" json:"alive,omitempty"` + AliveKnown bool `protobuf:"varint,25,opt,name=alive_known,json=aliveKnown,proto3" json:"alive_known,omitempty"` + WorktreeExists bool `protobuf:"varint,26,opt,name=worktree_exists,json=worktreeExists,proto3" json:"worktree_exists,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -531,6 +536,41 @@ func (x *Run) GetPrState() string { return "" } +func (x *Run) GetIssueStatus() string { + if x != nil { + return x.IssueStatus + } + return "" +} + +func (x *Run) GetIssueTopic() string { + if x != nil { + return x.IssueTopic + } + return "" +} + +func (x *Run) GetAlive() bool { + if x != nil { + return x.Alive + } + return false +} + +func (x *Run) GetAliveKnown() bool { + if x != nil { + return x.AliveKnown + } + return false +} + +func (x *Run) GetWorktreeExists() bool { + if x != nil { + return x.WorktreeExists + } + return false +} + type Issue struct { state protoimpl.MessageState `protogen:"open.v1"` Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` @@ -1913,6 +1953,7 @@ type CreateIssueRequest struct { IssueId string `protobuf:"bytes,2,opt,name=issue_id,json=issueId,proto3" json:"issue_id,omitempty"` Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"` Body string `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"` + Tags []string `protobuf:"bytes,5,rep,name=tags,proto3" json:"tags,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1975,6 +2016,13 @@ func (x *CreateIssueRequest) GetBody() string { return "" } +func (x *CreateIssueRequest) GetTags() []string { + if x != nil { + return x.Tags + } + return nil +} + type CreateIssueResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` @@ -8947,7 +8995,7 @@ const file_orch_proto_rawDesc = "" + "\tadditions\x18\x01 \x01(\x05R\tadditions\x12\x1c\n" + "\tdeletions\x18\x02 \x01(\x05R\tdeletions\x12#\n" + "\rfiles_changed\x18\x03 \x01(\x05R\ffilesChanged\x12\x14\n" + - "\x05files\x18\x04 \x03(\tR\x05files\"\xfc\x05\n" + + "\x05files\x18\x04 \x03(\tR\x05files\"\xa0\a\n" + "\x03Run\x12\x19\n" + "\bissue_id\x18\x01 \x01(\tR\aissueId\x12\x15\n" + "\x06run_id\x18\x02 \x01(\tR\x05runId\x12*\n" + @@ -8972,7 +9020,14 @@ const file_orch_proto_rawDesc = "" + "\x13opencode_session_id\x18\x12 \x01(\tR\x11opencodeSessionId\x12%\n" + "\x0econtinued_from\x18\x13 \x01(\tR\rcontinuedFrom\x12\x1b\n" + "\tpr_number\x18\x14 \x01(\x05R\bprNumber\x12\x19\n" + - "\bpr_state\x18\x15 \x01(\tR\aprState\"\xf1\x01\n" + + "\bpr_state\x18\x15 \x01(\tR\aprState\x12!\n" + + "\fissue_status\x18\x16 \x01(\tR\vissueStatus\x12\x1f\n" + + "\vissue_topic\x18\x17 \x01(\tR\n" + + "issueTopic\x12\x14\n" + + "\x05alive\x18\x18 \x01(\bR\x05alive\x12\x1f\n" + + "\valive_known\x18\x19 \x01(\bR\n" + + "aliveKnown\x12'\n" + + "\x0fworktree_exists\x18\x1a \x01(\bR\x0eworktreeExists\"\xf1\x01\n" + "\x05Issue\x12\x0e\n" + "\x02id\x18\x01 \x01(\tR\x02id\x12\x14\n" + "\x05title\x18\x02 \x01(\tR\x05title\x12\x18\n" + @@ -9097,13 +9152,14 @@ const file_orch_proto_rawDesc = "" + "issuesRoot\x12\x19\n" + "\bissue_id\x18\x02 \x01(\tR\aissueId\"8\n" + "\x10GetIssueResponse\x12$\n" + - "\x05issue\x18\x01 \x01(\v2\x0e.orch.v1.IssueR\x05issue\"z\n" + + "\x05issue\x18\x01 \x01(\v2\x0e.orch.v1.IssueR\x05issue\"\x8e\x01\n" + "\x12CreateIssueRequest\x12\x1f\n" + "\vissues_root\x18\x01 \x01(\tR\n" + "issuesRoot\x12\x19\n" + "\bissue_id\x18\x02 \x01(\tR\aissueId\x12\x14\n" + "\x05title\x18\x03 \x01(\tR\x05title\x12\x12\n" + - "\x04body\x18\x04 \x01(\tR\x04body\")\n" + + "\x04body\x18\x04 \x01(\tR\x04body\x12\x12\n" + + "\x04tags\x18\x05 \x03(\tR\x04tags\")\n" + "\x13CreateIssueResponse\x12\x12\n" + "\x04path\x18\x01 \x01(\tR\x04path\"O\n" + "\x11CloseIssueRequest\x12\x1f\n" + @@ -9635,14 +9691,14 @@ const file_orch_proto_rawDesc = "" + "get_config\x18/ \x01(\v2\x1a.orch.v1.GetConfigResponseH\x00R\tgetConfig\x12N\n" + "\x11get_daemon_status\x180 \x01(\v2 .orch.v1.GetDaemonStatusResponseH\x00R\x0fgetDaemonStatusB\n" + "\n" + - "\bresponse*\xff\x01\n" + + "\bresponse*\x80\x02\n" + "\tRunStatus\x12\x1a\n" + "\x16RUN_STATUS_UNSPECIFIED\x10\x00\x12\x15\n" + "\x11RUN_STATUS_QUEUED\x10\x01\x12\x16\n" + "\x12RUN_STATUS_BOOTING\x10\x02\x12\x16\n" + "\x12RUN_STATUS_RUNNING\x10\x03\x12\x16\n" + - "\x12RUN_STATUS_BLOCKED\x10\x04\x12\x1a\n" + - "\x16RUN_STATUS_BLOCKED_API\x10\x05\x12\x16\n" + + "\x12RUN_STATUS_WAITING\x10\x04\x12\x1b\n" + + "\x17RUN_STATUS_RATE_LIMITED\x10\x05\x12\x16\n" + "\x12RUN_STATUS_PR_OPEN\x10\x06\x12\x13\n" + "\x0fRUN_STATUS_DONE\x10\a\x12\x15\n" + "\x11RUN_STATUS_FAILED\x10\b\x12\x17\n" + diff --git a/api/orch.proto b/api/orch.proto index ee02c6e7..d50b0773 100644 --- a/api/orch.proto +++ b/api/orch.proto @@ -13,8 +13,8 @@ enum RunStatus { RUN_STATUS_QUEUED = 1; RUN_STATUS_BOOTING = 2; RUN_STATUS_RUNNING = 3; - RUN_STATUS_BLOCKED = 4; - RUN_STATUS_BLOCKED_API = 5; + RUN_STATUS_WAITING = 4; + RUN_STATUS_RATE_LIMITED = 5; RUN_STATUS_PR_OPEN = 6; RUN_STATUS_DONE = 7; RUN_STATUS_FAILED = 8; diff --git a/api/orchpb/orch.pb.go b/api/orchpb/orch.pb.go index 1ce56a2a..17ef5fff 100644 --- a/api/orchpb/orch.pb.go +++ b/api/orchpb/orch.pb.go @@ -24,16 +24,16 @@ const ( type RunStatus int32 const ( - RunStatus_RUN_STATUS_UNSPECIFIED RunStatus = 0 - RunStatus_RUN_STATUS_QUEUED RunStatus = 1 - RunStatus_RUN_STATUS_BOOTING RunStatus = 2 - RunStatus_RUN_STATUS_RUNNING RunStatus = 3 - RunStatus_RUN_STATUS_BLOCKED RunStatus = 4 - RunStatus_RUN_STATUS_BLOCKED_API RunStatus = 5 - RunStatus_RUN_STATUS_PR_OPEN RunStatus = 6 - RunStatus_RUN_STATUS_DONE RunStatus = 7 - RunStatus_RUN_STATUS_FAILED RunStatus = 8 - RunStatus_RUN_STATUS_CANCELED RunStatus = 9 + RunStatus_RUN_STATUS_UNSPECIFIED RunStatus = 0 + RunStatus_RUN_STATUS_QUEUED RunStatus = 1 + RunStatus_RUN_STATUS_BOOTING RunStatus = 2 + RunStatus_RUN_STATUS_RUNNING RunStatus = 3 + RunStatus_RUN_STATUS_WAITING RunStatus = 4 + RunStatus_RUN_STATUS_RATE_LIMITED RunStatus = 5 + RunStatus_RUN_STATUS_PR_OPEN RunStatus = 6 + RunStatus_RUN_STATUS_DONE RunStatus = 7 + RunStatus_RUN_STATUS_FAILED RunStatus = 8 + RunStatus_RUN_STATUS_CANCELED RunStatus = 9 ) // Enum value maps for RunStatus. @@ -43,24 +43,24 @@ var ( 1: "RUN_STATUS_QUEUED", 2: "RUN_STATUS_BOOTING", 3: "RUN_STATUS_RUNNING", - 4: "RUN_STATUS_BLOCKED", - 5: "RUN_STATUS_BLOCKED_API", + 4: "RUN_STATUS_WAITING", + 5: "RUN_STATUS_RATE_LIMITED", 6: "RUN_STATUS_PR_OPEN", 7: "RUN_STATUS_DONE", 8: "RUN_STATUS_FAILED", 9: "RUN_STATUS_CANCELED", } RunStatus_value = map[string]int32{ - "RUN_STATUS_UNSPECIFIED": 0, - "RUN_STATUS_QUEUED": 1, - "RUN_STATUS_BOOTING": 2, - "RUN_STATUS_RUNNING": 3, - "RUN_STATUS_BLOCKED": 4, - "RUN_STATUS_BLOCKED_API": 5, - "RUN_STATUS_PR_OPEN": 6, - "RUN_STATUS_DONE": 7, - "RUN_STATUS_FAILED": 8, - "RUN_STATUS_CANCELED": 9, + "RUN_STATUS_UNSPECIFIED": 0, + "RUN_STATUS_QUEUED": 1, + "RUN_STATUS_BOOTING": 2, + "RUN_STATUS_RUNNING": 3, + "RUN_STATUS_WAITING": 4, + "RUN_STATUS_RATE_LIMITED": 5, + "RUN_STATUS_PR_OPEN": 6, + "RUN_STATUS_DONE": 7, + "RUN_STATUS_FAILED": 8, + "RUN_STATUS_CANCELED": 9, } ) @@ -9691,14 +9691,14 @@ const file_orch_proto_rawDesc = "" + "get_config\x18/ \x01(\v2\x1a.orch.v1.GetConfigResponseH\x00R\tgetConfig\x12N\n" + "\x11get_daemon_status\x180 \x01(\v2 .orch.v1.GetDaemonStatusResponseH\x00R\x0fgetDaemonStatusB\n" + "\n" + - "\bresponse*\xff\x01\n" + + "\bresponse*\x80\x02\n" + "\tRunStatus\x12\x1a\n" + "\x16RUN_STATUS_UNSPECIFIED\x10\x00\x12\x15\n" + "\x11RUN_STATUS_QUEUED\x10\x01\x12\x16\n" + "\x12RUN_STATUS_BOOTING\x10\x02\x12\x16\n" + "\x12RUN_STATUS_RUNNING\x10\x03\x12\x16\n" + - "\x12RUN_STATUS_BLOCKED\x10\x04\x12\x1a\n" + - "\x16RUN_STATUS_BLOCKED_API\x10\x05\x12\x16\n" + + "\x12RUN_STATUS_WAITING\x10\x04\x12\x1b\n" + + "\x17RUN_STATUS_RATE_LIMITED\x10\x05\x12\x16\n" + "\x12RUN_STATUS_PR_OPEN\x10\x06\x12\x13\n" + "\x0fRUN_STATUS_DONE\x10\a\x12\x15\n" + "\x11RUN_STATUS_FAILED\x10\b\x12\x17\n" + diff --git a/claude-plugins/orch-toolset/README.md b/claude-plugins/orch-toolset/README.md index b96f44b4..62e12431 100644 --- a/claude-plugins/orch-toolset/README.md +++ b/claude-plugins/orch-toolset/README.md @@ -56,7 +56,7 @@ Skills are model-invoked - Claude will automatically use this skill when you ask - "Show me how to monitor multiple runs and send guidance." - "What is the control agent workflow for orch?" - "How do I run tests in an agent's worktree?" -- "What commands can I use to check on blocked runs?" +- "What commands can I use to check on waiting runs?" ## Skill Contents @@ -101,10 +101,10 @@ orch issue create my-task --title "Implement feature X" orch run my-task # Monitor progress -orch ps --status running,blocked +orch ps --status running,waiting orch capture my-task --lines 200 -# Send guidance to blocked agent +# Send guidance to waiting agent orch send my-task "Focus on the auth module" # Run tests in isolation diff --git a/claude-plugins/orch-toolset/skills/orch-toolset/SKILL.md b/claude-plugins/orch-toolset/skills/orch-toolset/SKILL.md index 5db56a70..43c751eb 100644 --- a/claude-plugins/orch-toolset/skills/orch-toolset/SKILL.md +++ b/claude-plugins/orch-toolset/skills/orch-toolset/SKILL.md @@ -64,12 +64,12 @@ Orch is a non-interactive orchestrator for managing multiple LLM CLI agents (Cla ### Maintenance - `orch repair`: Fix system state corruption -- `orch tick `: Resume blocked runs +- `orch tick `: Resume waiting runs ## Run Lifecycle States ``` -queued -> booting -> running -> blocked -> pr_open -> done +queued -> booting -> running -> waiting -> pr_open -> done \-> failed \-> canceled \-> unknown @@ -79,8 +79,8 @@ State meanings: - `queued`: Run created, waiting to start - `booting`: Agent starting up - `running`: Agent actively working -- `blocked`: Agent needs input (waiting on question) -- `blocked_api`: API rate limit hit +- `waiting`: Agent needs input (waiting on question) +- `rate_limited`: API rate limit hit - `pr_open`: PR created, awaiting review - `done`: Work completed successfully - `failed`: Run failed with error @@ -95,7 +95,7 @@ State meanings: - Use issue status (open/resolved) separately from run status ### Monitoring Strategy -- Use `orch ps --status running,blocked` for active attention +- Use `orch ps --status running,waiting` for active attention - Use `orch monitor` for multi-agent coordination and rapid context-switching - Set up background daemon (auto-runs) and check periodically @@ -116,7 +116,7 @@ State meanings: When acting as a control agent managing other runs: 1. **Survey**: `orch issue list` to see all work items -2. **Monitor**: `orch ps --status running,blocked` to prioritize attention +2. **Monitor**: `orch ps --status running,waiting` to prioritize attention 3. **Investigate**: `orch capture ` to fetch output without attaching 4. **Inspect**: `orch open ` to read full run doc with context 5. **Guide**: `orch send "focused guidance"` to steer work @@ -174,7 +174,7 @@ Benefits of external worktrees: | Create issue | `orch issue create --title "..." --tag ` | | Start run | `orch run ` | | Continue failed run | `orch continue --agent codex` | -| List active | `orch ps --status running,blocked` | +| List active | `orch ps --status running,waiting` | | Inspect run | `orch show ` | | Watch agent | `orch attach ` | | Get output | `orch capture ` | diff --git a/claude-plugins/orch-toolset/skills/orch-toolset/reference.md b/claude-plugins/orch-toolset/skills/orch-toolset/reference.md index f3b7b1d8..a014578e 100644 --- a/claude-plugins/orch-toolset/skills/orch-toolset/reference.md +++ b/claude-plugins/orch-toolset/skills/orch-toolset/reference.md @@ -236,7 +236,7 @@ orch ps # Filter by status orch ps --status running -orch ps --status running,blocked +orch ps --status running,waiting orch ps --status pr_open,done # Filter by issue @@ -257,7 +257,7 @@ orch ps --tsv # for fzf **Flags:** | Flag | Description | |------|-------------| -| `--status` | Filter: queued,booting,running,blocked,blocked_api,pr_open,done,resolved,failed,canceled,unknown | +| `--status` | Filter: queued,booting,running,waiting,rate_limited,pr_open,done,resolved,failed,canceled,unknown | | `--issue-status` | Filter by issue status (open/closed) | | `--issue` | Filter by issue ID | | `--limit N` | Max runs (default: 50) | @@ -366,7 +366,7 @@ orch monitor orch monitor --issue orch-055 # Filter by status -orch monitor --status running,blocked +orch monitor --status running,waiting # Start and immediately attach orch monitor --attach @@ -564,13 +564,13 @@ orch repair --force ### orch tick RUN_REF|--all -Resume blocked runs when questions are answered. +Resume waiting runs when questions are answered. ```bash # Tick specific run orch tick orch-055#20251223-165605 -# Tick all blocked runs +# Tick all waiting runs orch tick --all # Limit number processed @@ -583,8 +583,8 @@ orch tick orch-055 --agent claude **Flags:** | Flag | Description | |------|-------------| -| `--all` | Process all blocked runs | -| `--only-blocked` | Only blocked (default with --all) | +| `--all` | Process all waiting runs | +| `--only-waiting` | Only waiting (default with --all) | | `--agent` | Agent for restart | | `--max N` | Max runs to process | @@ -606,7 +606,7 @@ orch ps --status running ### Monitoring Active Runs ```bash -orch ps --status running,blocked +orch ps --status running,waiting orch capture a3b4c5 --lines 200 orch monitor ``` @@ -634,8 +634,8 @@ orch repair --dry-run # Check for problems ### Control Agent Loop ```bash orch issue list --status open -orch ps --status running,blocked -orch capture -orch send "guidance" +orch ps --status running,waiting +orch capture +orch send "guidance" orch resolve ``` diff --git a/docs/agents/claude.md b/docs/agents/claude.md index 67d77819..275b499f 100644 --- a/docs/agents/claude.md +++ b/docs/agents/claude.md @@ -252,7 +252,7 @@ Claude has various UI modes and features. In tmux sessions: |--------------|-------------| | Initializing | `booting` | | Actively working | `running` | -| Waiting for input | `blocked` | +| Waiting for input | `waiting` | | Completed task | `done` | | Error message | `failed` | | Exited to shell | `unknown` | diff --git a/docs/backends/github.md b/docs/backends/github.md index 8511b402..03ec415e 100644 --- a/docs/backends/github.md +++ b/docs/backends/github.md @@ -132,7 +132,7 @@ github: comments: enabled: true on_start: true # Comment when run starts - on_blocked: true # Comment when input needed + on_waiting: true # Comment when input needed on_complete: true # Comment with results ``` @@ -265,7 +265,7 @@ slack: enabled: true webhook_url: ${SLACK_WEBHOOK} notify_on: - - blocked + - waiting ``` ## Limitations diff --git a/docs/backends/linear.md b/docs/backends/linear.md index 5fd7b35d..c75b65ad 100644 --- a/docs/backends/linear.md +++ b/docs/backends/linear.md @@ -137,7 +137,7 @@ linear: comments: enabled: true on_start: true - on_blocked: true + on_waiting: true on_complete: true include_run_link: true ``` diff --git a/docs/concepts.md b/docs/concepts.md index 9bef92a0..4c6412b3 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -55,7 +55,7 @@ A **single execution attempt** for an issue. **Key properties:** - `issue_id` - The parent issue - `run_id` - Timestamp identifier -- `status` - Current state (running, blocked, done, etc.) +- `status` - Current state (running, waiting, done, etc.) - `agent` - Which LLM CLI is being used - `worktree` - Isolated git working directory - `branch` - Git branch for this run @@ -85,13 +85,13 @@ stateDiagram-v2 [*] --> queued: orch run queued --> booting: agent starting booting --> running: agent ready - running --> blocked: needs input + running --> waiting: needs input running --> pr_open: PR created running --> done: complete running --> failed: error running --> canceled: stopped - blocked --> running: input provided - blocked --> canceled: stopped + waiting --> running: input provided + waiting --> canceled: stopped pr_open --> done: PR merged done --> [*] failed --> [*] @@ -103,8 +103,8 @@ stateDiagram-v2 | `queued` | Run created, waiting to start | Wait | | `booting` | Agent is launching | Wait | | `running` | Agent actively working | Wait, or attach to watch | -| `blocked` | Agent needs human input | Attach and provide input | -| `blocked_api` | API/rate limit issue | Wait or check credentials | +| `waiting` | Agent needs human input | Attach and provide input | +| `rate_limited` | API/rate limit issue | Wait or check credentials | | `pr_open` | Pull request created | Review the PR | | `done` | Work completed successfully | Celebrate! | | `failed` | Run encountered an error | Check logs, maybe retry | @@ -236,7 +236,7 @@ A key design principle of orch: - Agents run in the background without blocking your terminal - Use `orch ps` to check status anytime - Use `orch attach` when you need to interact -- Human input is handled through the `blocked` status +- Human input is handled through the `waiting` status This allows you to: - Start multiple agents working on different issues diff --git a/docs/configuration.md b/docs/configuration.md index 8fcb65c3..8a5caf88 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -143,8 +143,8 @@ slack: # Events that trigger notifications notify_on: - - blocked - - blocked_api + - waiting + - rate_limited # - done # - failed @@ -158,8 +158,8 @@ monitor: - queued - booting - running - - blocked - - blocked_api + - waiting + - rate_limited - pr_open # Default issue statuses to show @@ -292,7 +292,7 @@ slack: enabled: true webhook_url: https://hooks.slack.com/services/XXX/YYY/ZZZ notify_on: - - blocked + - waiting - failed ``` @@ -307,8 +307,8 @@ slack: bot_token: xoxb-your-token channel: "#orch-notifications" notify_on: - - blocked - - blocked_api + - waiting + - rate_limited - done ``` @@ -342,7 +342,7 @@ slack: enabled: true webhook_url: ${SLACK_WEBHOOK_URL} notify_on: - - blocked + - waiting ``` ### Multiple models diff --git a/docs/daily-workflow.md b/docs/daily-workflow.md index 7f1c6a4b..61fcb50f 100644 --- a/docs/daily-workflow.md +++ b/docs/daily-workflow.md @@ -11,7 +11,7 @@ Start your day by checking what happened overnight: orch ps # See runs that completed or need attention -orch ps --status done,pr_open,blocked,failed +orch ps --status done,pr_open,waiting,failed ``` Review any PRs that agents created: @@ -89,7 +89,7 @@ The agent starts working in the background. You can continue with other tasks. orch ps # Filter by status -orch ps --status running,blocked +orch ps --status running,waiting # Detailed view of a specific run orch show fix-login-timeout @@ -100,7 +100,7 @@ orch show fix-login-timeout | Status | What it means | Your action | |--------|---------------|-------------| | `running` | Agent is actively working | Wait, or watch with `attach` | -| `blocked` | Agent needs input or is stuck | Use `attach` or `send` to help | +| `waiting` | Agent needs input or is stuck | Use `attach` or `send` to help | | `pr_open` | Agent created a PR | Review the PR | | `done` | Work completed successfully | Celebrate! | | `failed` | Something went wrong | Check logs, possibly restart | @@ -216,7 +216,7 @@ orch ps 1. **Use descriptive issue IDs** - Easy to tell runs apart in `ps` output 2. **Check status regularly** - Some may finish quickly, others may block -3. **Prioritize blocked runs** - Agents waiting for input aren't making progress +3. **Prioritize waiting runs** - Agents waiting for input aren't making progress 4. **Review PRs as they come in** - Don't let them pile up ## End of Day @@ -275,9 +275,9 @@ orch run update-deps orch ps # ISSUE STATUS RUN AGENT UPDATED # optimize-db-queries running 20260115-093012 claude 5m ago -# update-deps blocked 20260115-093045 claude 2m ago +# update-deps waiting 20260115-093045 claude 2m ago -# Help the blocked agent +# Help the waiting agent orch attach update-deps # Agent: "Should I update React to v19 or stay on v18?" # You: "Stay on v18 for now, we're not ready for the migration" diff --git a/docs/getting-started.md b/docs/getting-started.md index bee1a35f..d460f270 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -174,7 +174,7 @@ my-first-issue running 20260120-163045 claude 2m ago | `queued` | Run created, starting soon | | `booting` | Agent is launching | | `running` | Agent is actively working | -| `blocked` | Agent needs your input | +| `waiting` | Agent needs your input | | `pr_open` | Agent created a PR | | `done` | Task completed | | `failed` | Something went wrong | @@ -271,7 +271,7 @@ Ask it to create issues, start runs, check status—all through natural language Learn efficient patterns for working with orch day-to-day: - Morning routines for checking overnight progress -- How to handle blocked agents +- How to handle waiting agents - Running multiple agents in parallel - Reviewing and merging agent PRs @@ -306,7 +306,7 @@ ORCH_DEBUG=1 orch run my-issue Check if it's waiting for input: ```bash -orch ps # Look for "blocked" status +orch ps # Look for "waiting" status orch attach my-issue # Connect and provide input ``` diff --git a/docs/orch-monitor.md b/docs/orch-monitor.md index ef335e1e..2b3c5e9c 100644 --- a/docs/orch-monitor.md +++ b/docs/orch-monitor.md @@ -134,7 +134,7 @@ The TUI uses colors to indicate run status at a glance: | Color | Status | Meaning | |-------|--------|---------| | 🟢 Green | `running` | Agent is actively working | -| 🟡 Yellow | `blocked` | Agent needs your input | +| 🟡 Yellow | `waiting` | Agent needs your input | | 🔵 Blue | `pr_open` | PR created, ready for review | | ⚪ White | `done` | Completed successfully | | 🔴 Red | `failed` | Error occurred | @@ -167,7 +167,7 @@ Agent: Starting run for fix-login-timeout with claude agent... You: What's running right now? Agent: You have 2 active runs: - fix-login-timeout: running (started 10m ago) - - update-deps: blocked (waiting for input about React version) + - update-deps: waiting (needs input about React version) ``` ### Getting Help @@ -199,8 +199,8 @@ Agent: You can press 'd' while a run is selected in the Runs panel, - Watch the status change in the Runs panel - Status updates automatically -5. **Help if blocked** - - If status turns yellow (blocked), press `a` to attach +5. **Help if waiting** + - If status turns yellow (waiting), press `a` to attach - Provide the needed input - Press `Ctrl+B D` to detach @@ -226,7 +226,7 @@ Agent: You can press 'd' while a run is selected in the Runs panel, If the TUI seems unresponsive: 1. Press `r` to refresh the current panel -2. Check if a run is actually blocked (`orch ps` in another terminal) +2. Check if a run is actually waiting (`orch ps` in another terminal) 3. Try `q` and restart if needed If control agent isn't responding: diff --git a/docs/reference/commands.md b/docs/reference/commands.md index b0dcd3ae..3310c2de 100644 --- a/docs/reference/commands.md +++ b/docs/reference/commands.md @@ -139,7 +139,7 @@ orch run ISSUE_ID [flags] | `--profile ` | Agent profile | | `--prompt-template ` | Custom prompt template file | | `--repo-root ` | Git repository root | -| `--reuse` | Reuse latest run if blocked | +| `--reuse` | Reuse latest run if waiting | | `--run-id ` | Manually specify run ID | | `--tmux` | Run in tmux session (default: true) | | `--tmux-session ` | Session name | @@ -231,7 +231,7 @@ orch ps [flags] orch ps # Filter by status -orch ps --status running,blocked +orch ps --status running,waiting # Show runs for specific issue orch ps --issue my-issue @@ -455,7 +455,7 @@ orch resolve ISSUE_ID [flags] ## orch tick -Resume blocked runs. +Resume waiting runs. ```bash orch tick RUN_REF|--all [flags] @@ -465,8 +465,8 @@ orch tick RUN_REF|--all [flags] | Flag | Description | |------|-------------| -| `--all` | Process all blocked runs | -| `--only-blocked` | Only process blocked runs | +| `--all` | Process all waiting runs | +| `--only-waiting` | Only process waiting runs | | `--agent ` | Agent for resumption | | `--max ` | Max runs to process | diff --git a/docs/reference/events.md b/docs/reference/events.md index 47947e36..a583511d 100644 --- a/docs/reference/events.md +++ b/docs/reference/events.md @@ -30,8 +30,8 @@ Tracks run state changes. These events determine the current status of a run. | `queued` | Run created, waiting to start | `status \| queued \|` | | `booting` | Agent is starting | `status \| booting \| agent=claude` | | `running` | Agent is working | `status \| running \|` | -| `blocked` | Needs human input | `status \| blocked \| reason=question` | -| `blocked_api` | API/rate limit issue | `status \| blocked_api \| error=rate_limit` | +| `waiting` | Needs human input | `status \| waiting \| reason=question` | +| `rate_limited` | API/rate limit issue | `status \| rate_limited \| error=rate_limit` | | `pr_open` | PR created | `status \| pr_open \|` | | `done` | Completed successfully | `status \| done \|` | | `failed` | Error occurred | `status \| failed \| error=...` | diff --git a/docs/reference/query.md b/docs/reference/query.md index a2cdf812..0e7edb70 100644 --- a/docs/reference/query.md +++ b/docs/reference/query.md @@ -113,7 +113,7 @@ WHERE r.run_id IS NULL; SELECT DISTINCT i.* FROM issues i JOIN runs r ON i.id = r.issue_id -WHERE r.status IN ('running', 'blocked', 'booting'); +WHERE r.status IN ('running', 'waiting', 'booting'); -- Issue summary with run counts SELECT @@ -133,14 +133,14 @@ SELECT issue_id, run_id, agent, status FROM runs WHERE status = 'running'; --- Blocked runs needing attention +-- Waiting runs needing attention SELECT issue_id, run_id, hex_id, updated FROM runs -WHERE status IN ('blocked', 'blocked_api') +WHERE status IN ('waiting', 'rate_limited') ORDER BY updated DESC; -- Runs with PRs @@ -303,9 +303,9 @@ other-task closed # Get running run IDs RUNNING=$(orch q "SELECT hex_id FROM runs WHERE status='running'" --format tsv) -# Iterate over blocked runs -orch q "SELECT hex_id FROM runs WHERE status='blocked'" --format tsv | while read id; do - echo "Run $id is blocked" +# Iterate over waiting runs +orch q "SELECT hex_id FROM runs WHERE status='waiting'" --format tsv | while read id; do + echo "Run $id is waiting" done ``` diff --git a/docs/reference/statuses.md b/docs/reference/statuses.md index 9c37b9c5..3b5c2466 100644 --- a/docs/reference/statuses.md +++ b/docs/reference/statuses.md @@ -11,19 +11,19 @@ stateDiagram-v2 queued --> booting: agent starting booting --> running: agent ready - running --> blocked: needs human input - running --> blocked_api: API/rate limit issue + running --> waiting: needs human input + running --> rate_limited: API/rate limit issue running --> pr_open: PR created running --> done: task complete running --> failed: error occurred running --> canceled: orch stop running --> unknown: agent exited unexpectedly - blocked --> running: input provided (orch attach) - blocked --> canceled: orch stop + waiting --> running: input provided (orch attach) + waiting --> canceled: orch stop - blocked_api --> running: issue resolved - blocked_api --> canceled: orch stop + rate_limited --> running: issue resolved + rate_limited --> canceled: orch stop pr_open --> done: PR merged/closed @@ -68,9 +68,9 @@ Agent is actively working on the task. |--------|---------| | What's happening | Agent analyzing, coding, testing | | User action | Wait, or attach to watch | -| Next status | `blocked`, `pr_open`, `done`, `failed` | +| Next status | `waiting`, `pr_open`, `done`, `failed` | -#### `blocked` +#### `waiting` Agent needs human input to continue. @@ -80,13 +80,13 @@ Agent needs human input to continue. | User action | `orch attach` to provide input | | Next status | `running` (after input) | -Common reasons for blocked: +Common reasons for waiting: - Agent asking clarifying question - Permission confirmation needed - Design decision required - Error needs human judgment -#### `blocked_api` +#### `rate_limited` API or rate limit issue preventing progress. @@ -162,10 +162,10 @@ The daemon detects this by: ```bash # Active runs -orch ps --status running,blocked +orch ps --status running,waiting -# All blocked runs -orch ps --status blocked,blocked_api +# All waiting runs +orch ps --status waiting,rate_limited # Completed runs orch ps --status done @@ -191,9 +191,9 @@ SELECT FROM runs WHERE status = 'running'; --- Recently blocked +-- Recently waiting SELECT * FROM runs -WHERE status = 'blocked' +WHERE status = 'waiting' ORDER BY updated_at DESC LIMIT 10; ``` @@ -209,7 +209,7 @@ queued → booting → running → pr_open → done ### With human interaction ``` -queued → booting → running → blocked → running → pr_open → done +queued → booting → running → waiting → running → pr_open → done ``` ### Failure scenarios @@ -224,7 +224,7 @@ running → unknown (agent crashed) ``` running → canceled (user stopped) -blocked → canceled (user stopped) +waiting → canceled (user stopped) ``` ## Monitoring Status @@ -239,7 +239,7 @@ orch ps watch -n 5 orch ps # JSON for scripting -orch ps --json | jq '.runs[] | select(.status == "blocked")' +orch ps --json | jq '.runs[] | select(.status == "waiting")' ``` ### Notifications @@ -251,17 +251,17 @@ slack: enabled: true webhook_url: ${SLACK_WEBHOOK} notify_on: - - blocked - - blocked_api + - waiting + - rate_limited - failed ``` ## Best Practices -### Handling blocked runs +### Handling waiting runs 1. Check `orch ps` regularly -2. Set up Slack notifications for `blocked` +2. Set up Slack notifications for `waiting` 3. Use `orch attach` to provide input 4. Document common questions in issue templates @@ -278,7 +278,7 @@ slack: # Simple loop while true; do clear - orch ps --status running,blocked,booting + orch ps --status running,waiting,booting sleep 10 done ``` diff --git a/docs/specs/daemon-state-api.md b/docs/specs/daemon-state-api.md index f87d74dd..1d13b337 100644 --- a/docs/specs/daemon-state-api.md +++ b/docs/specs/daemon-state-api.md @@ -23,7 +23,7 @@ EventSourceDaemon - Lowest authority (daemon inferences) | From State | To State | User | Agent | Daemon | |------------|----------|------|-------|--------| -| running | blocked | ✓ | ✓ | ✓ | +| running | waiting | ✓ | ✓ | ✓ | | running | pr_open | ✓ | ✓ | ✓ | | running | done | ✓ | ✓ | ✓ | | running | canceled | ✓ | ✓ | ✓ | diff --git a/internal/agent/manager.go b/internal/agent/manager.go index 5af8c801..4502fc8b 100644 --- a/internal/agent/manager.go +++ b/internal/agent/manager.go @@ -188,13 +188,13 @@ func (m *MuxManager) GetStatus(run *model.Run, output string, state *RunState, o return model.StatusDone } if IsAPILimited(output) { - return model.StatusBlockedAPI + return model.StatusRateLimited } if IsFailed(output) { return model.StatusFailed } if hasPrompt { - return model.StatusBlocked + return model.StatusWaiting } if outputChanged { return model.StatusRunning @@ -339,9 +339,9 @@ func (m *OpenCodeManager) GetStatus(run *model.Run, output string, state *RunSta case SessionStatusBusy: return model.StatusRunning case SessionStatusIdle: - return model.StatusBlocked + return model.StatusWaiting case SessionStatusRetry: - return model.StatusBlockedAPI + return model.StatusRateLimited default: return "" } @@ -356,7 +356,7 @@ func (m *OpenCodeManager) GetStatus(run *model.Run, output string, state *RunSta } if m.sessionExists(ctx, client) { - return model.StatusBlocked + return model.StatusWaiting } return model.StatusUnknown diff --git a/internal/agent/manager_test.go b/internal/agent/manager_test.go index f21bcb5f..4f61d15b 100644 --- a/internal/agent/manager_test.go +++ b/internal/agent/manager_test.go @@ -180,7 +180,7 @@ func TestIsWaitingForInput(t *testing.T) { want: true, }, { - name: "codex blocked prompt from pane", + name: "codex waiting prompt from pane", output: strings.Join([]string{ "› Use /skills to list available skills", " ? for shortcuts", @@ -188,17 +188,17 @@ func TestIsWaitingForInput(t *testing.T) { want: true, }, { - name: "codex blocked prompt tab to queue message", + name: "codex waiting prompt tab to queue message", output: "tab to queue message", want: true, }, { - name: "codex blocked prompt context left", + name: "codex waiting prompt context left", output: "100% context left", want: true, }, { - name: "codex active work should not be treated as blocked", + name: "codex active work should not be treated as waiting", output: strings.Join([]string{ "› Run this command", "• Working (7s • esc to interrupt)", @@ -208,7 +208,7 @@ func TestIsWaitingForInput(t *testing.T) { want: false, }, { - name: "codex active work with queue hint should not be treated as blocked", + name: "codex active work with queue hint should not be treated as waiting", output: strings.Join([]string{ "• Planning follow-up checks (2m 53s • esc to interrupt)", "tab to queue message", @@ -217,7 +217,7 @@ func TestIsWaitingForInput(t *testing.T) { want: false, }, { - name: "codex background task should not be treated as blocked", + name: "codex background task should not be treated as waiting", output: strings.Join([]string{ "• Sending initial progress update (19s • esc to interrupt)", " 1 background terminal running · /ps to view", @@ -495,7 +495,7 @@ func TestMuxManagerGetStatus(t *testing.T) { output: "Rate limit exceeded", outputChanged: false, hasPrompt: false, - want: model.StatusBlockedAPI, + want: model.StatusRateLimited, }, { name: "failed", @@ -516,14 +516,14 @@ func TestMuxManagerGetStatus(t *testing.T) { output: "Working...", outputChanged: true, hasPrompt: true, - want: model.StatusBlocked, + want: model.StatusWaiting, }, { - name: "has prompt = blocked", + name: "has prompt = waiting", output: "Regular output", outputChanged: false, hasPrompt: true, - want: model.StatusBlocked, + want: model.StatusWaiting, }, { name: "no change no prompt = empty", @@ -589,14 +589,14 @@ func TestOpenCodeManagerGetStatusFromAPI(t *testing.T) { wantStatus: model.StatusRunning, }, { - name: "idle session returns blocked", + name: "idle session returns waiting", sessionStatus: SessionStatusIdle, - wantStatus: model.StatusBlocked, + wantStatus: model.StatusWaiting, }, { - name: "retry session returns blocked_api", + name: "retry session returns rate_limited", sessionStatus: SessionStatusRetry, - wantStatus: model.StatusBlockedAPI, + wantStatus: model.StatusRateLimited, }, } @@ -643,8 +643,8 @@ func TestOpenCodeManagerGetStatusSessionIdleInStatusMap(t *testing.T) { run := &model.Run{Status: model.StatusRunning} state := &RunState{} got := manager.GetStatus(run, "", state, false, false) - if got != model.StatusBlocked { - t.Errorf("GetStatus() for idle session = %v, want %v", got, model.StatusBlocked) + if got != model.StatusWaiting { + t.Errorf("GetStatus() for idle session = %v, want %v", got, model.StatusWaiting) } } @@ -1060,9 +1060,9 @@ func TestOpenCodeManagerGetStatusUsesDirectory(t *testing.T) { wantStatus model.Status }{ { - name: "detects idle status (blocked) with directory scoping", + name: "detects idle status (waiting) with directory scoping", sessionStatus: SessionStatusIdle, - wantStatus: model.StatusBlocked, + wantStatus: model.StatusWaiting, }, { name: "detects busy status (running) with directory scoping", @@ -1070,9 +1070,9 @@ func TestOpenCodeManagerGetStatusUsesDirectory(t *testing.T) { wantStatus: model.StatusRunning, }, { - name: "detects retry status (blocked_api) with directory scoping", + name: "detects retry status (rate_limited) with directory scoping", sessionStatus: SessionStatusRetry, - wantStatus: model.StatusBlockedAPI, + wantStatus: model.StatusRateLimited, }, } @@ -1224,8 +1224,8 @@ func TestOpenCodeManagerDirectoryScoping(t *testing.T) { state := &RunState{} status := manager.GetStatus(run, "", state, false, false) - if status != model.StatusBlocked { - t.Errorf("GetStatus() = %v, want %v (blocked when agent is idle)", status, model.StatusBlocked) + if status != model.StatusWaiting { + t.Errorf("GetStatus() = %v, want %v (waiting when agent is idle)", status, model.StatusWaiting) } } diff --git a/internal/cli/capture_all.go b/internal/cli/capture_all.go index 9bbdff8d..48515737 100644 --- a/internal/cli/capture_all.go +++ b/internal/cli/capture_all.go @@ -121,8 +121,8 @@ func captureAllStatusesAPI() []orchapi.RunStatus { return []orchapi.RunStatus{ orchapi.RunStatusRunning, orchapi.RunStatusBooting, - orchapi.RunStatusBlocked, - orchapi.RunStatusBlockedAPI, + orchapi.RunStatusWaiting, + orchapi.RunStatusRateLimited, orchapi.RunStatusPROpen, } } diff --git a/internal/cli/capture_all_test.go b/internal/cli/capture_all_test.go index 2b1c5c4a..752445d2 100644 --- a/internal/cli/capture_all_test.go +++ b/internal/cli/capture_all_test.go @@ -97,7 +97,7 @@ func TestRunCaptureAllJSON(t *testing.T) { mockAPI := &mockCaptureAllAPI{ runs: []*orchapi.Run{ {IssueID: "issue-1", RunID: "run-1", Status: orchapi.RunStatusRunning}, - {IssueID: "issue-2", RunID: "run-2", Status: orchapi.RunStatusBlocked}, + {IssueID: "issue-2", RunID: "run-2", Status: orchapi.RunStatusWaiting}, }, captureResults: map[string]*orchapi.CaptureResult{ "issue-1#run-1": {Content: "run-1 output\n", Timestamp: time.Now(), Source: "tmux"}, @@ -170,8 +170,8 @@ func TestRunCaptureAllJSON(t *testing.T) { if item2.RunID != "run-2" { t.Fatalf("issue-2 run_id = %q, want run-2", item2.RunID) } - if item2.Status != string(orchapi.RunStatusBlocked) { - t.Fatalf("issue-2 status = %q, want %q", item2.Status, orchapi.RunStatusBlocked) + if item2.Status != string(orchapi.RunStatusWaiting) { + t.Fatalf("issue-2 status = %q, want %q", item2.Status, orchapi.RunStatusWaiting) } if !strings.Contains(item2.Error, "session not found") { t.Fatalf("issue-2 error = %q", item2.Error) @@ -184,7 +184,7 @@ func TestRunCaptureAllPlain(t *testing.T) { mockAPI := &mockCaptureAllAPI{ runs: []*orchapi.Run{ {IssueID: "issue-1", RunID: "run-1", Status: orchapi.RunStatusRunning}, - {IssueID: "issue-2", RunID: "run-2", Status: orchapi.RunStatusBlocked}, + {IssueID: "issue-2", RunID: "run-2", Status: orchapi.RunStatusWaiting}, }, captureResults: map[string]*orchapi.CaptureResult{ "issue-1#run-1": {Content: "run-1 output\n", Timestamp: time.Now(), Source: "tmux"}, @@ -207,7 +207,7 @@ func TestRunCaptureAllPlain(t *testing.T) { if !strings.Contains(out, "run-1 output") { t.Fatalf("missing issue-1 output: %q", out) } - if !strings.Contains(out, "=== issue-2#run-2 [blocked] ===") { + if !strings.Contains(out, "=== issue-2#run-2 [waiting] ===") { t.Fatalf("missing issue-2 header: %q", out) } if !strings.Contains(out, "error:") && !strings.Contains(out, "session not found") { diff --git a/internal/cli/delete.go b/internal/cli/delete.go index 15af55d8..d016f09a 100644 --- a/internal/cli/delete.go +++ b/internal/cli/delete.go @@ -131,11 +131,11 @@ func parseStatus(s string) ([]model.Status, error) { return nil, nil } - status := model.Status(s) + status := model.NormalizeStatus(s) switch status { case model.StatusDone, model.StatusFailed, model.StatusCanceled: return []model.Status{status}, nil - case model.StatusRunning, model.StatusBooting, model.StatusBlocked, model.StatusBlockedAPI, model.StatusQueued: + case model.StatusRunning, model.StatusBooting, model.StatusWaiting, model.StatusRateLimited, model.StatusQueued: return nil, fmt.Errorf("cannot delete %s runs (use 'orch stop' first)", status) default: return nil, fmt.Errorf("unknown status: %s", s) @@ -261,11 +261,11 @@ func parseStatusAPI(s string) ([]orchapi.RunStatus, error) { return nil, nil } - status := orchapi.RunStatus(s) + status := orchapi.NormalizeRunStatus(s) switch status { case orchapi.RunStatusDone, orchapi.RunStatusFailed, orchapi.RunStatusCanceled: return []orchapi.RunStatus{status}, nil - case orchapi.RunStatusRunning, orchapi.RunStatusBooting, orchapi.RunStatusBlocked, orchapi.RunStatusBlockedAPI, orchapi.RunStatusQueued: + case orchapi.RunStatusRunning, orchapi.RunStatusBooting, orchapi.RunStatusWaiting, orchapi.RunStatusRateLimited, orchapi.RunStatusQueued: return nil, fmt.Errorf("cannot delete %s runs (use 'orch stop' first)", status) default: return nil, fmt.Errorf("unknown status: %s", s) diff --git a/internal/cli/issue.go b/internal/cli/issue.go index cce5c369..f49d198b 100644 --- a/internal/cli/issue.go +++ b/internal/cli/issue.go @@ -417,8 +417,8 @@ func runIssueListViaAPI(ctx context.Context, api orchapi.OrchAPI, opts *issueLis runsResp, err := api.ListRuns(ctx, &orchapi.ListRunsFilter{ Status: []orchapi.RunStatus{ orchapi.RunStatusRunning, - orchapi.RunStatusBlocked, - orchapi.RunStatusBlockedAPI, + orchapi.RunStatusWaiting, + orchapi.RunStatusRateLimited, orchapi.RunStatusBooting, orchapi.RunStatusQueued, }, diff --git a/internal/cli/monitor.go b/internal/cli/monitor.go index 0b51db25..26af45b3 100644 --- a/internal/cli/monitor.go +++ b/internal/cli/monitor.go @@ -271,7 +271,7 @@ func runMonitor(opts *monitorOptions) error { if s == "" { continue } - statuses = append(statuses, model.Status(s)) + statuses = append(statuses, model.NormalizeStatus(s)) } runSortFallback := settings.RunSort diff --git a/internal/cli/ps.go b/internal/cli/ps.go index de3db807..0c52bba7 100644 --- a/internal/cli/ps.go +++ b/internal/cli/ps.go @@ -60,7 +60,7 @@ func newPsCmd() *cobra.Command { }, } - cmd.Flags().StringSliceVar(&opts.Status, "status", nil, "Filter by run status (queued,booting,running,blocked,blocked_api,pr_open,done,failed,canceled,unknown)") + cmd.Flags().StringSliceVar(&opts.Status, "status", nil, "Filter by run status (queued,booting,running,waiting,rate_limited,pr_open,done,failed,canceled,unknown)") cmd.Flags().StringSliceVar(&opts.IssueStatus, "issue-status", nil, "Filter by issue status (open,resolved,closed)") cmd.Flags().StringVar(&opts.Issue, "issue", "", "Filter by issue ID") cmd.Flags().IntVar(&opts.Limit, "limit", 50, "Maximum number of runs to show") @@ -89,7 +89,7 @@ func runPsWithDeps(ctx context.Context, opts *psOptions, deps *psDeps) error { statusFilter := make([]orchapi.RunStatus, len(opts.Status)) for i, s := range opts.Status { - statusFilter[i] = orchapi.RunStatus(s) + statusFilter[i] = orchapi.NormalizeRunStatus(s) } limit := opts.Limit @@ -541,10 +541,10 @@ func shortAgentStatus(status model.Status) string { return "boot" case model.StatusRunning: return "run" - case model.StatusBlocked: - return "block" - case model.StatusBlockedAPI: - return "block" + case model.StatusWaiting: + return "wait" + case model.StatusRateLimited: + return "rlimit" case model.StatusPROpen: return "pr" case model.StatusDone: @@ -745,16 +745,16 @@ func formatRelativeTime(when time.Time, now time.Time) string { func colorStatus(status model.Status) string { colors := map[model.Status]string{ - model.StatusRunning: "\033[32m", - model.StatusBlocked: "\033[33m", - model.StatusBlockedAPI: "\033[33m", - model.StatusFailed: "\033[31m", - model.StatusDone: "\033[34m", - model.StatusPROpen: "\033[36m", - model.StatusQueued: "\033[37m", - model.StatusBooting: "\033[32m", - model.StatusCanceled: "\033[90m", - model.StatusUnknown: "\033[35m", + model.StatusRunning: "\033[32m", + model.StatusWaiting: "\033[33m", + model.StatusRateLimited: "\033[33m", + model.StatusFailed: "\033[31m", + model.StatusDone: "\033[34m", + model.StatusPROpen: "\033[36m", + model.StatusQueued: "\033[37m", + model.StatusBooting: "\033[32m", + model.StatusCanceled: "\033[90m", + model.StatusUnknown: "\033[35m", } reset := "\033[0m" @@ -766,16 +766,16 @@ func colorStatus(status model.Status) string { func colorShortStatus(status model.Status) string { colors := map[model.Status]string{ - model.StatusRunning: "\033[32m", - model.StatusBlocked: "\033[33m", - model.StatusBlockedAPI: "\033[33m", - model.StatusFailed: "\033[31m", - model.StatusDone: "\033[34m", - model.StatusPROpen: "\033[36m", - model.StatusQueued: "\033[37m", - model.StatusBooting: "\033[32m", - model.StatusCanceled: "\033[90m", - model.StatusUnknown: "\033[35m", + model.StatusRunning: "\033[32m", + model.StatusWaiting: "\033[33m", + model.StatusRateLimited: "\033[33m", + model.StatusFailed: "\033[31m", + model.StatusDone: "\033[34m", + model.StatusPROpen: "\033[36m", + model.StatusQueued: "\033[37m", + model.StatusBooting: "\033[32m", + model.StatusCanceled: "\033[90m", + model.StatusUnknown: "\033[35m", } short := shortAgentStatus(status) @@ -848,7 +848,7 @@ func parseStatusList(s string) []model.Status { parts := strings.Split(s, ",") statuses := make([]model.Status, len(parts)) for i, p := range parts { - statuses[i] = model.Status(strings.TrimSpace(p)) + statuses[i] = model.NormalizeStatus(strings.TrimSpace(p)) } return statuses } diff --git a/internal/cli/ps_test.go b/internal/cli/ps_test.go index 7f3d0020..754517c5 100644 --- a/internal/cli/ps_test.go +++ b/internal/cli/ps_test.go @@ -52,7 +52,7 @@ func setupMockPsDeps(issuesRoot string) *psDeps { func TestParseStatusList(t *testing.T) { statuses := parseStatusList("running, blocked ,done") - want := []model.Status{model.StatusRunning, model.StatusBlocked, model.StatusDone} + want := []model.Status{model.StatusRunning, model.StatusWaiting, model.StatusDone} if len(statuses) != len(want) { t.Fatalf("got %d statuses, want %d", len(statuses), len(want)) } @@ -527,8 +527,8 @@ func TestShortAgentStatus(t *testing.T) { {model.StatusQueued, "queue"}, {model.StatusBooting, "boot"}, {model.StatusRunning, "run"}, - {model.StatusBlocked, "block"}, - {model.StatusBlockedAPI, "block"}, + {model.StatusWaiting, "wait"}, + {model.StatusRateLimited, "rlimit"}, {model.StatusPROpen, "pr"}, {model.StatusDone, "done"}, {model.StatusFailed, "fail"}, @@ -645,7 +645,7 @@ func TestOutputTableAgentColumn(t *testing.T) { wantShort string }{ {model.StatusRunning, "run"}, - {model.StatusBlocked, "block"}, + {model.StatusWaiting, "wait"}, {model.StatusDone, "done"}, {model.StatusFailed, "fail"}, } diff --git a/internal/cli/root.go b/internal/cli/root.go index bd95ac48..bc5857a7 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -203,7 +203,7 @@ func apiRunToModelRun(r *orchapi.Run) *model.Run { return &model.Run{ IssueID: r.IssueID, RunID: r.RunID, - Status: model.Status(r.Status), + Status: model.NormalizeStatus(string(r.Status)), Agent: r.Agent, Model: r.Model, ModelVariant: r.ModelVariant, diff --git a/internal/cli/run.go b/internal/cli/run.go index 313c4abb..4655b29c 100644 --- a/internal/cli/run.go +++ b/internal/cli/run.go @@ -62,7 +62,7 @@ Debug output can be enabled with --verbose, --log-level debug, or ORCH_DEBUG=1.` } cmd.Flags().BoolVar(&opts.New, "new", true, "Always create a new run (default)") - cmd.Flags().BoolVar(&opts.Reuse, "reuse", false, "Reuse the latest run if blocked or blocked_api") + cmd.Flags().BoolVar(&opts.Reuse, "reuse", false, "Reuse the latest run if waiting or rate_limited") cmd.Flags().StringVar(&opts.RunID, "run-id", "", "Manually specify run ID") cmd.Flags().StringVar(&opts.Agent, "agent", "", "Agent type (claude|codex|gemini|opencode|custom)") cmd.Flags().StringVar(&opts.AgentCmd, "agent-cmd", "", "Custom agent command (when --agent=custom)") diff --git a/internal/cli/show.go b/internal/cli/show.go index 338e8a66..3f32ce97 100644 --- a/internal/cli/show.go +++ b/internal/cli/show.go @@ -117,7 +117,7 @@ func showJSON(run *orchapi.Run, opts *showOptions) error { func showHuman(run *orchapi.Run, opts *showOptions) error { // Header fmt.Printf("Run: %s#%s\n", run.IssueID, run.RunID) - fmt.Printf("Status: %s", colorStatus(model.Status(run.Status))) + fmt.Printf("Status: %s", colorStatus(model.NormalizeStatus(string(run.Status)))) fmt.Println() fmt.Println(strings.Repeat("-", 60)) diff --git a/internal/cli/stop.go b/internal/cli/stop.go index 76e10052..d77b5dd6 100644 --- a/internal/cli/stop.go +++ b/internal/cli/stop.go @@ -102,8 +102,8 @@ func stopAllForIssue(ctx context.Context, api orchapi.OrchAPI, issueID string, o Status: []orchapi.RunStatus{ orchapi.RunStatusRunning, orchapi.RunStatusBooting, - orchapi.RunStatusBlocked, - orchapi.RunStatusBlockedAPI, + orchapi.RunStatusWaiting, + orchapi.RunStatusRateLimited, orchapi.RunStatusQueued, }, }) @@ -152,8 +152,8 @@ func runStopAllWithDeps(ctx context.Context, opts *stopOptions, deps *stopDeps) Status: []orchapi.RunStatus{ orchapi.RunStatusRunning, orchapi.RunStatusBooting, - orchapi.RunStatusBlocked, - orchapi.RunStatusBlockedAPI, + orchapi.RunStatusWaiting, + orchapi.RunStatusRateLimited, orchapi.RunStatusQueued, }, }) diff --git a/internal/cli/tick.go b/internal/cli/tick.go index 39ccc0b1..bb47b0c3 100644 --- a/internal/cli/tick.go +++ b/internal/cli/tick.go @@ -12,7 +12,7 @@ import ( type tickOptions struct { All bool - OnlyBlocked bool + OnlyWaiting bool Agent string Max int } @@ -22,10 +22,10 @@ func newTickCmd() *cobra.Command { cmd := &cobra.Command{ Use: "tick [RUN_REF]", - Short: "Resume blocked runs", - Long: `Trigger blocked runs to resume if their questions are answered. + Short: "Resume waiting runs", + Long: `Trigger waiting runs to resume if their questions are answered. -With --all, processes all blocked runs. Otherwise, processes a single run.`, +With --all, processes all waiting runs. Otherwise, processes a single run.`, Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { var refStr string @@ -36,8 +36,8 @@ With --all, processes all blocked runs. Otherwise, processes a single run.`, }, } - cmd.Flags().BoolVar(&opts.All, "all", false, "Process all blocked runs") - cmd.Flags().BoolVar(&opts.OnlyBlocked, "only-blocked", true, "Only process blocked runs (default when --all)") + cmd.Flags().BoolVar(&opts.All, "all", false, "Process all waiting runs") + cmd.Flags().BoolVar(&opts.OnlyWaiting, "only-waiting", true, "Only process waiting runs (default when --all)") cmd.Flags().StringVar(&opts.Agent, "agent", "", "Agent to use for resumption") cmd.Flags().IntVar(&opts.Max, "max", 10, "Maximum runs to process with --all") @@ -74,7 +74,7 @@ func runTick(refStr string, opts *tickOptions) error { if opts.All { filter := &orchapi.ListRunsFilter{ - Status: []orchapi.RunStatus{orchapi.RunStatusBlocked, orchapi.RunStatusBlockedAPI}, + Status: []orchapi.RunStatus{orchapi.RunStatusWaiting, orchapi.RunStatusRateLimited}, Limit: opts.Max, } listResult, err := api.ListRuns(ctx, filter) @@ -103,11 +103,11 @@ func runTick(refStr string, opts *tickOptions) error { for _, run := range runs { // Only check blocked status for single-run case; --all already filters at daemon level - if !opts.All && opts.OnlyBlocked && run.Status != orchapi.RunStatusBlocked && run.Status != orchapi.RunStatusBlockedAPI { + if !opts.All && opts.OnlyWaiting && run.Status != orchapi.RunStatusWaiting && run.Status != orchapi.RunStatusRateLimited { result.Skipped = append(result.Skipped, skippedRun{ IssueID: run.IssueID, RunID: run.RunID, - Reason: fmt.Sprintf("status is %s, not blocked", run.Status), + Reason: fmt.Sprintf("status is %s, not waiting", run.Status), }) continue } @@ -175,10 +175,10 @@ func buildResumePrompt(issue *orchapi.Issue, run *orchapi.Run) string { if issue.Title != "" { prompt += fmt.Sprintf("Title: %s\n\n", issue.Title) } - if run.Status == orchapi.RunStatusBlockedAPI { - prompt += "The previous session was blocked by API usage limits.\n" + if run.Status == orchapi.RunStatusRateLimited { + prompt += "The previous session was rate limited by API usage limits.\n" } else { - prompt += "The previous session was blocked.\n" + prompt += "The previous session was waiting for input.\n" } prompt += "Please continue from where you left off.\n" return prompt diff --git a/internal/cli/tutorial.go b/internal/cli/tutorial.go index 628a6e39..6b2244d8 100644 --- a/internal/cli/tutorial.go +++ b/internal/cli/tutorial.go @@ -118,7 +118,7 @@ Interact with the agent: orch send "msg" Send message to running agent orch monitor TUI dashboard for all runs orch show Show issue details - orch continue Resume a paused or blocked run + orch continue Resume a paused or waiting run orch capture Capture agent session to markdown -------------------------------------------------------------------------------- @@ -128,12 +128,12 @@ Interact with the agent: Status Meaning Action ------ ------- ------ running Agent is working Wait or 'attach' to watch - blocked Agent needs input 'attach' to help + waiting Agent needs input 'attach' to help done Work complete Review the PR failed Error occurred Check logs, retry resolved Run completed and acked No action needed -To check why a run is blocked: +To check why a run is waiting: orch show @@ -215,7 +215,7 @@ Workflow with Control Agent: 1. Talk to control agent (bottom pane) to create issues 2. Select issue and press 'n' to start a run 3. Monitor status: queued → booting → running → pr_open → done - 4. Attach with 'a' when status is 'blocked' + 4. Attach with 'a' when status is 'waiting' 5. Review PR when status is 'pr_open' Status Legend: @@ -225,7 +225,7 @@ Status Legend: queued Run waiting to start booting Agent starting up running Agent actively working - blocked Agent needs input - attach! + waiting Agent needs input - attach! pr_open PR created - review it done Work completed diff --git a/internal/config/config.go b/internal/config/config.go index 9d6f84da..2e353101 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -130,7 +130,7 @@ func (s *SlackConfig) ShouldNotify(status string) bool { return false } if len(s.NotifyOn) == 0 { - return status == "blocked" || status == "blocked_api" + return status == "waiting" || status == "blocked" || status == "rate_limited" || status == "blocked_api" } for _, n := range s.NotifyOn { if n == status { @@ -795,10 +795,12 @@ var validLogLevels = map[string]bool{ } var validSlackNotifyStatuses = map[string]bool{ - "blocked": true, - "blocked_api": true, - "done": true, - "failed": true, + "waiting": true, + "blocked": true, + "rate_limited": true, + "blocked_api": true, + "done": true, + "failed": true, } // Validate enforces config key/value constraints after loading/merging. diff --git a/internal/daemon/daemon.go b/internal/daemon/daemon.go index 407c31e9..b9a4e20c 100644 --- a/internal/daemon/daemon.go +++ b/internal/daemon/daemon.go @@ -325,7 +325,7 @@ func (d *Daemon) monitorAll() { continue } runs, err := ctx.Store.ListRuns(&store.ListRunsFilter{ - Status: []model.Status{model.StatusRunning, model.StatusBooting, model.StatusBlocked, model.StatusBlockedAPI, model.StatusPROpen, model.StatusUnknown}, + Status: []model.Status{model.StatusRunning, model.StatusBooting, model.StatusWaiting, model.StatusRateLimited, model.StatusPROpen, model.StatusUnknown}, }) if err != nil { d.logger.Printf("error listing runs for %s: %v", ctx.RepoID, err) diff --git a/internal/daemon/monitor.go b/internal/daemon/monitor.go index 118240e7..b6b4ce7d 100644 --- a/internal/daemon/monitor.go +++ b/internal/daemon/monitor.go @@ -24,7 +24,7 @@ const ( captureRefusedBackoffInitial = 10 * time.Second captureRefusedBackoffMax = 5 * time.Minute captureRefusedNegativeCacheTTL = 30 * time.Second - blockedPromptStreakThreshold = 2 + waitingPromptStreakThreshold = 2 ) func (d *Daemon) monitorRun(run *model.Run, st store.Store) error { @@ -264,7 +264,7 @@ func (s *RunState) recordPromptSignal(hasPrompt bool) bool { s.PromptStreak = 0 } - return hasPrompt && s.PromptStreak >= blockedPromptStreakThreshold + return hasPrompt && s.PromptStreak >= waitingPromptStreakThreshold } func captureBackoffDuration(err error, failures int) time.Duration { @@ -461,7 +461,7 @@ func (d *Daemon) notifyStatusChange(run *model.Run, newStatus model.Status, last } var err error - if newStatus == model.StatusBlocked || newStatus == model.StatusBlockedAPI { + if newStatus == model.StatusWaiting || newStatus == model.StatusRateLimited { err = d.slackNotifier.NotifyBlocked(run, issueTitle, lastOutput) } else { err = d.slackNotifier.NotifyStatusChange(run, issueTitle, newStatus) @@ -547,7 +547,7 @@ func (d *Daemon) inferStatusFromGitState(run *model.Run, st store.Store, wasAliv d.logger.Printf("%s#%s: infer: commits ahead=%d, uncommitted=%v", run.IssueID, run.RunID, aheadCount, hasUncommitted) if aheadCount > 0 || hasUncommitted { - return model.StatusBlocked + return model.StatusWaiting } if !wasAlive { diff --git a/internal/daemon/monitor_test.go b/internal/daemon/monitor_test.go index bcc75dfa..395a2da2 100644 --- a/internal/daemon/monitor_test.go +++ b/internal/daemon/monitor_test.go @@ -204,8 +204,8 @@ func TestUpdateStatusAutoResolve(t *testing.T) { wantSetStatusCall: false, }, { - name: "StatusBlocked does not resolve", - status: model.StatusBlocked, + name: "StatusWaiting does not resolve", + status: model.StatusWaiting, issueStatus: model.IssueStatusOpen, wantSetStatusCall: false, }, diff --git a/internal/daemon/proto_client.go b/internal/daemon/proto_client.go index d2364a5d..62308ed9 100644 --- a/internal/daemon/proto_client.go +++ b/internal/daemon/proto_client.go @@ -829,10 +829,10 @@ func stringToProtoRunStatus(s string) orchpb.RunStatus { return orchpb.RunStatus_RUN_STATUS_BOOTING case "running": return orchpb.RunStatus_RUN_STATUS_RUNNING - case "blocked": - return orchpb.RunStatus_RUN_STATUS_BLOCKED - case "blocked_api": - return orchpb.RunStatus_RUN_STATUS_BLOCKED_API + case "blocked", "waiting": + return orchpb.RunStatus_RUN_STATUS_WAITING + case "blocked_api", "rate_limited": + return orchpb.RunStatus_RUN_STATUS_RATE_LIMITED case "pr_open": return orchpb.RunStatus_RUN_STATUS_PR_OPEN case "done": @@ -867,10 +867,10 @@ func protoRunStatusToString(s orchpb.RunStatus) string { return "booting" case orchpb.RunStatus_RUN_STATUS_RUNNING: return "running" - case orchpb.RunStatus_RUN_STATUS_BLOCKED: - return "blocked" - case orchpb.RunStatus_RUN_STATUS_BLOCKED_API: - return "blocked_api" + case orchpb.RunStatus_RUN_STATUS_WAITING: + return "waiting" + case orchpb.RunStatus_RUN_STATUS_RATE_LIMITED: + return "rate_limited" case orchpb.RunStatus_RUN_STATUS_PR_OPEN: return "pr_open" case orchpb.RunStatus_RUN_STATUS_DONE: diff --git a/internal/daemon/proto_convert.go b/internal/daemon/proto_convert.go index 569711d6..d31a2e76 100644 --- a/internal/daemon/proto_convert.go +++ b/internal/daemon/proto_convert.go @@ -15,10 +15,10 @@ func modelStatusToProto(s model.Status) orchpb.RunStatus { return orchpb.RunStatus_RUN_STATUS_BOOTING case model.StatusRunning: return orchpb.RunStatus_RUN_STATUS_RUNNING - case model.StatusBlocked: - return orchpb.RunStatus_RUN_STATUS_BLOCKED - case model.StatusBlockedAPI: - return orchpb.RunStatus_RUN_STATUS_BLOCKED_API + case model.StatusWaiting: + return orchpb.RunStatus_RUN_STATUS_WAITING + case model.StatusRateLimited: + return orchpb.RunStatus_RUN_STATUS_RATE_LIMITED case model.StatusPROpen: return orchpb.RunStatus_RUN_STATUS_PR_OPEN case model.StatusDone: @@ -40,10 +40,10 @@ func protoStatusToModel(s orchpb.RunStatus) model.Status { return model.StatusBooting case orchpb.RunStatus_RUN_STATUS_RUNNING: return model.StatusRunning - case orchpb.RunStatus_RUN_STATUS_BLOCKED: - return model.StatusBlocked - case orchpb.RunStatus_RUN_STATUS_BLOCKED_API: - return model.StatusBlockedAPI + case orchpb.RunStatus_RUN_STATUS_WAITING: + return model.StatusWaiting + case orchpb.RunStatus_RUN_STATUS_RATE_LIMITED: + return model.StatusRateLimited case orchpb.RunStatus_RUN_STATUS_PR_OPEN: return model.StatusPROpen case orchpb.RunStatus_RUN_STATUS_DONE: diff --git a/internal/daemon/proto_handler.go b/internal/daemon/proto_handler.go index 33926870..b261ba27 100644 --- a/internal/daemon/proto_handler.go +++ b/internal/daemon/proto_handler.go @@ -551,7 +551,7 @@ func formatElapsedTime(startedAt, updatedAt time.Time, status model.Status) stri } var elapsed time.Duration - if status == model.StatusRunning || status == model.StatusBlocked || status == model.StatusBlockedAPI { + if status == model.StatusRunning || status == model.StatusWaiting || status == model.StatusRateLimited { elapsed = time.Since(startedAt) } else if !updatedAt.IsZero() { elapsed = updatedAt.Sub(startedAt) diff --git a/internal/daemon/socket.go b/internal/daemon/socket.go index abc94659..5224bfcd 100644 --- a/internal/daemon/socket.go +++ b/internal/daemon/socket.go @@ -1300,8 +1300,8 @@ func (s *SocketServer) buildControlAgentPrompt(st store.Store, projectRoot strin runs, _ := st.ListRuns(&store.ListRunsFilter{ Status: []model.Status{ model.StatusRunning, - model.StatusBlocked, - model.StatusBlockedAPI, + model.StatusWaiting, + model.StatusRateLimited, model.StatusBooting, model.StatusQueued, model.StatusPROpen, @@ -1405,8 +1405,8 @@ Use `+"`--agent `"+` to specify a different agent when starting runs. 2. Start run: `+"`orch run `"+` 3. Monitor: Watch the runs table or use `+"`orch ps`"+` -### Handling Blocked Runs -When a run shows "blocked" status: +### Handling Waiting Runs +When a run shows "waiting" status: 1. Capture: `+"`orch capture `"+` to see what the agent needs 2. Send feedback: `+"`orch send \"\"`"+` to provide input 3. The agent will resume automatically after receiving input @@ -1427,12 +1427,12 @@ Run these commands directly using bash (do not use any special protocol): ### Run Management - Start a run: `+"`orch run `"+` - Continue from branch: `+"`orch continue --branch `"+` -- List runs: `+"`orch ps`"+` (use `+"`--status running,blocked`"+` to filter) +- List runs: `+"`orch ps`"+` (use `+"`--status running,waiting`"+` to filter) - Stop a run: `+"`orch stop #`"+` - Resolve a run: `+"`orch resolve #`"+` - Show run details: `+"`orch show #`"+` - Capture run state: `+"`orch capture `"+` - see agent's last message -- Send feedback: `+"`orch send \"\"`"+` - provide input to blocked agent +- Send feedback: `+"`orch send \"\"`"+` - provide input to waiting agent ### Interactive Commands (DO NOT USE) The following commands are interactive and will hang if called by an AI agent: @@ -1998,7 +1998,7 @@ func (s *SocketServer) handleListRuns(req SendRequest, encoder *json.Encoder) { Limit: 0, } for _, status := range req.Status { - filter.Status = append(filter.Status, model.Status(status)) + filter.Status = append(filter.Status, model.NormalizeStatus(status)) } runs, err = st.ListRuns(filter) } @@ -2063,7 +2063,7 @@ func (s *SocketServer) listAllRepoRuns(req SendRequest) ([]*model.Run, error) { TimeRange: req.TimeRange, } for _, status := range req.Status { - filter.Status = append(filter.Status, model.Status(status)) + filter.Status = append(filter.Status, model.NormalizeStatus(status)) } runs, err := st.ListRuns(filter) if err != nil { @@ -2347,7 +2347,7 @@ func (s *SocketServer) handleStartRun(req SendRequest, encoder *json.Encoder) { if req.Reuse { runs, _ := st.ListRuns(&store.ListRunsFilter{IssueID: req.IssueID}) for _, r := range runs { - if r.Status == model.StatusBlocked || r.Status == model.StatusBlockedAPI { + if r.Status == model.StatusWaiting || r.Status == model.StatusRateLimited { runID = r.RunID break } @@ -2636,7 +2636,7 @@ func (s *SocketServer) processStartRunCore(st store.Store, projectRoot string, o if opts.Reuse { runs, _ := st.ListRuns(&store.ListRunsFilter{IssueID: opts.IssueID}) for _, r := range runs { - if r.Status == model.StatusBlocked || r.Status == model.StatusBlockedAPI { + if r.Status == model.StatusWaiting || r.Status == model.StatusRateLimited { runID = r.RunID break } @@ -3616,7 +3616,7 @@ func (s *SocketServer) handleContinueRun(req SendRequest, encoder *json.Encoder) func isActiveForContinue(status model.Status) bool { switch status { - case model.StatusRunning, model.StatusBlocked, model.StatusBlockedAPI, model.StatusBooting, model.StatusQueued, model.StatusPROpen: + case model.StatusRunning, model.StatusWaiting, model.StatusRateLimited, model.StatusBooting, model.StatusQueued, model.StatusPROpen: return true default: return false @@ -3699,7 +3699,7 @@ func (s *SocketServer) handleStopRun(req SendRequest, encoder *json.Encoder) { } else { runs, err := st.ListRuns(&store.ListRunsFilter{ IssueID: req.IssueID, - Status: []model.Status{model.StatusRunning, model.StatusBooting, model.StatusBlocked, model.StatusBlockedAPI, model.StatusQueued}, + Status: []model.Status{model.StatusRunning, model.StatusBooting, model.StatusWaiting, model.StatusRateLimited, model.StatusQueued}, }) if err != nil { s.logger.Printf("error listing runs for %s: %v", req.IssueID, err) @@ -4318,7 +4318,7 @@ func (s *SocketServer) handleAppendEvent(req SendRequest, encoder *json.Encoder) source := model.EventSource(req.EventSource) if req.EventType == "status" { - newStatus := model.Status(req.EventName) + newStatus := model.NormalizeStatus(req.EventName) if !model.CanTransitionStatus(run.Status, newStatus, source) { reason := fmt.Sprintf("cannot transition from %s to %s (source=%s)", run.Status, newStatus, source) s.logger.Printf("%s#%s: %s", req.IssueID, req.RunID, reason) diff --git a/internal/daemon/socket_test.go b/internal/daemon/socket_test.go index 146bb37b..c963a11b 100644 --- a/internal/daemon/socket_test.go +++ b/internal/daemon/socket_test.go @@ -735,7 +735,7 @@ func TestListRunsPaginationContract(t *testing.T) { "orch-001#run-1": {IssueID: "orch-001", RunID: "run-1", Status: model.StatusRunning, Agent: "opencode", StartedAt: now.Add(-5 * time.Hour), UpdatedAt: now.Add(-4 * time.Hour)}, "orch-001#run-2": {IssueID: "orch-001", RunID: "run-2", Status: model.StatusDone, Agent: "claude", StartedAt: now.Add(-4 * time.Hour), UpdatedAt: now.Add(-3 * time.Hour)}, "orch-002#run-3": {IssueID: "orch-002", RunID: "run-3", Status: model.StatusRunning, Agent: "opencode", StartedAt: now.Add(-3 * time.Hour), UpdatedAt: now.Add(-2 * time.Hour)}, - "orch-002#run-4": {IssueID: "orch-002", RunID: "run-4", Status: model.StatusBlocked, Agent: "claude", StartedAt: now.Add(-2 * time.Hour), UpdatedAt: now.Add(-1 * time.Hour)}, + "orch-002#run-4": {IssueID: "orch-002", RunID: "run-4", Status: model.StatusWaiting, Agent: "claude", StartedAt: now.Add(-2 * time.Hour), UpdatedAt: now.Add(-1 * time.Hour)}, "orch-003#run-5": {IssueID: "orch-003", RunID: "run-5", Status: model.StatusFailed, Agent: "opencode", StartedAt: now.Add(-1 * time.Hour), UpdatedAt: now}, }, issues: make(map[string]*model.Issue), diff --git a/internal/daemon/types.go b/internal/daemon/types.go index c7ddb4bd..fb0cb801 100644 --- a/internal/daemon/types.go +++ b/internal/daemon/types.go @@ -532,7 +532,7 @@ func SummaryToRun(s *RunSummary) *model.Run { return &model.Run{ IssueID: s.IssueID, RunID: s.RunID, - Status: model.Status(s.Status), + Status: model.NormalizeStatus(s.Status), Phase: model.Phase(s.Phase), Agent: s.Agent, Model: s.Model, diff --git a/internal/daemon/types_test.go b/internal/daemon/types_test.go index b03df48a..dd16c583 100644 --- a/internal/daemon/types_test.go +++ b/internal/daemon/types_test.go @@ -181,7 +181,7 @@ func TestRunToSummaryRoundTrip(t *testing.T) { original := &model.Run{ IssueID: "issue-roundtrip", RunID: "run-roundtrip-123", - Status: model.StatusBlocked, + Status: model.StatusWaiting, Phase: model.PhasePlan, Agent: "opencode", Model: "gpt-4", @@ -263,7 +263,7 @@ func TestListRunsFilterDefaults(t *testing.T) { { name: "partial filter only sets specified fields", filter: &ListRunsFilter{ - Status: []string{"running", "blocked"}, + Status: []string{"running", "waiting"}, }, check: func(t *testing.T, f *ListRunsFilter) { if f.IssueID != "" { @@ -272,8 +272,8 @@ func TestListRunsFilterDefaults(t *testing.T) { if len(f.Status) != 2 { t.Errorf("Status = %v, want 2 elements", f.Status) } - if f.Status[0] != "running" || f.Status[1] != "blocked" { - t.Errorf("Status = %v, want [running, blocked]", f.Status) + if f.Status[0] != "running" || f.Status[1] != "waiting" { + t.Errorf("Status = %v, want [running, waiting]", f.Status) } }, }, diff --git a/internal/github/backend_test.go b/internal/github/backend_test.go index e829ac3e..f3748444 100644 --- a/internal/github/backend_test.go +++ b/internal/github/backend_test.go @@ -190,6 +190,7 @@ func TestBackendMapLabelsToStatus(t *testing.T) { Repo: "testrepo", StatusLabels: map[string]string{ "status:in-progress": "in_progress", + "status:waiting": "waiting", "status:blocked": "blocked", "status:done": "done", }, @@ -205,6 +206,7 @@ func TestBackendMapLabelsToStatus(t *testing.T) { want model.IssueStatus }{ {[]string{"bug", "status:in-progress"}, model.IssueStatus("in_progress")}, + {[]string{"status:waiting", "urgent"}, model.IssueStatus("waiting")}, {[]string{"status:blocked", "urgent"}, model.IssueStatus("blocked")}, {[]string{"enhancement"}, model.IssueStatus("")}, } diff --git a/internal/model/event.go b/internal/model/event.go index 2905ec91..b9bebe3f 100644 --- a/internal/model/event.go +++ b/internal/model/event.go @@ -23,25 +23,36 @@ const ( type Status string const ( - StatusQueued Status = "queued" - StatusBooting Status = "booting" - StatusRunning Status = "running" - StatusBlocked Status = "blocked" - StatusBlockedAPI Status = "blocked_api" - StatusPROpen Status = "pr_open" - StatusDone Status = "done" - StatusFailed Status = "failed" - StatusCanceled Status = "canceled" - StatusUnknown Status = "unknown" // Agent exited unexpectedly, shell prompt showing + StatusQueued Status = "queued" + StatusBooting Status = "booting" + StatusRunning Status = "running" + StatusWaiting Status = "waiting" + StatusRateLimited Status = "rate_limited" + StatusPROpen Status = "pr_open" + StatusDone Status = "done" + StatusFailed Status = "failed" + StatusCanceled Status = "canceled" + StatusUnknown Status = "unknown" // Agent exited unexpectedly, shell prompt showing ) +func NormalizeStatus(s string) Status { + switch s { + case "blocked": + return StatusWaiting + case "blocked_api": + return StatusRateLimited + default: + return Status(s) + } +} + func (s Status) IsTerminal() bool { return s == StatusDone || s == StatusCanceled || s == StatusFailed } func (s Status) IsActive() bool { switch s { - case StatusQueued, StatusBooting, StatusRunning, StatusBlocked, StatusBlockedAPI, StatusPROpen: + case StatusQueued, StatusBooting, StatusRunning, StatusWaiting, StatusRateLimited, StatusPROpen: return true default: return false diff --git a/internal/model/event_test.go b/internal/model/event_test.go index 0906a9e6..d31ac136 100644 --- a/internal/model/event_test.go +++ b/internal/model/event_test.go @@ -130,6 +130,29 @@ func TestNewStatusEvent(t *testing.T) { } } +func TestNormalizeStatus(t *testing.T) { + tests := []struct { + input string + want Status + }{ + {"blocked", StatusWaiting}, + {"blocked_api", StatusRateLimited}, + {"waiting", StatusWaiting}, + {"rate_limited", StatusRateLimited}, + {"running", StatusRunning}, + {"done", StatusDone}, + {"queued", StatusQueued}, + } + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + got := NormalizeStatus(tt.input) + if got != tt.want { + t.Errorf("NormalizeStatus(%q) = %q, want %q", tt.input, got, tt.want) + } + }) + } +} + func TestNewArtifactEvent(t *testing.T) { event := NewArtifactEvent("worktree", map[string]string{"path": "/tmp/test"}) if event.Type != EventTypeArtifact { diff --git a/internal/model/run.go b/internal/model/run.go index 189e81a7..2f23db05 100644 --- a/internal/model/run.go +++ b/internal/model/run.go @@ -134,7 +134,7 @@ func (r *Run) GetStatus() Status { for i := len(r.Events) - 1; i >= 0; i-- { e := r.Events[i] if e.Type == EventTypeStatus { - return Status(e.Name) + return NormalizeStatus(e.Name) } } return StatusQueued diff --git a/internal/monitor/agent_prompt.go b/internal/monitor/agent_prompt.go index 6ff7f616..f53e9c42 100644 --- a/internal/monitor/agent_prompt.go +++ b/internal/monitor/agent_prompt.go @@ -84,8 +84,8 @@ No active runs. 2. Start run: ` + "`orch run `" + ` 3. Monitor: Watch the runs table or use ` + "`orch ps`" + ` -### Handling Blocked Runs -When a run shows "blocked" status: +### Handling Waiting Runs +When a run shows "waiting" status: 1. Capture: ` + "`orch capture `" + ` to see what the agent needs 2. Send feedback: ` + "`orch send \"\"`" + ` to provide input 3. The agent will resume automatically after receiving input @@ -106,12 +106,12 @@ Run these commands directly using bash (do not use any special protocol): ### Run Management - Start a run: ` + "`orch run `" + ` - Continue from branch: ` + "`orch continue --branch `" + ` -- List runs: ` + "`orch ps`" + ` (use ` + "`--status running,blocked`" + ` to filter) +- List runs: ` + "`orch ps`" + ` (use ` + "`--status running,waiting`" + ` to filter) - Stop a run: ` + "`orch stop #`" + ` - Resolve a run: ` + "`orch resolve #`" + ` - Show run details: ` + "`orch show #`" + ` - Capture run state: ` + "`orch capture `" + ` - see agent's last message -- Send feedback: ` + "`orch send \"\"`" + ` - provide input to blocked agent +- Send feedback: ` + "`orch send \"\"`" + ` - provide input to waiting agent ### Interactive Commands (DO NOT USE) The following commands are interactive and will hang if called by an AI agent: @@ -348,8 +348,8 @@ func buildControlAgentPromptViaAPI(ctx context.Context, api orchapi.OrchAPI, iss runsResult, _ := api.ListRuns(ctx, &orchapi.ListRunsFilter{ Status: []orchapi.RunStatus{ orchapi.RunStatusRunning, - orchapi.RunStatusBlocked, - orchapi.RunStatusBlockedAPI, + orchapi.RunStatusWaiting, + orchapi.RunStatusRateLimited, orchapi.RunStatusBooting, orchapi.RunStatusQueued, orchapi.RunStatusPROpen, diff --git a/internal/monitor/agent_prompt_test.go b/internal/monitor/agent_prompt_test.go index 6e20b2ea..69fbc90b 100644 --- a/internal/monitor/agent_prompt_test.go +++ b/internal/monitor/agent_prompt_test.go @@ -198,8 +198,8 @@ func TestControlPromptTemplateContainsNewSections(t *testing.T) { if !contains(controlPromptTemplate, "## Workflows") { t.Error("template should contain Workflows section") } - if !contains(controlPromptTemplate, "### Handling Blocked Runs") { - t.Error("template should contain Handling Blocked Runs subsection") + if !contains(controlPromptTemplate, "### Handling Waiting Runs") { + t.Error("template should contain Handling Waiting Runs subsection") } if !contains(controlPromptTemplate, "### Continuing Work") { t.Error("template should contain Continuing Work subsection") diff --git a/internal/monitor/dashboard.go b/internal/monitor/dashboard.go index 85b61916..6faf0b71 100644 --- a/internal/monitor/dashboard.go +++ b/internal/monitor/dashboard.go @@ -1100,8 +1100,8 @@ func (d *Dashboard) renderStats() string { fmt.Sprintf("booting: %d", counts[model.StatusBooting]), fmt.Sprintf("queued: %d", counts[model.StatusQueued]), fmt.Sprintf("running: %d", counts[model.StatusRunning]), - fmt.Sprintf("blocked: %d", counts[model.StatusBlocked]), - fmt.Sprintf("blocked_api: %d", counts[model.StatusBlockedAPI]), + fmt.Sprintf("waiting: %d", counts[model.StatusWaiting]), + fmt.Sprintf("rate_limited: %d", counts[model.StatusRateLimited]), fmt.Sprintf("pr_open: %d", counts[model.StatusPROpen]), fmt.Sprintf("done: %d", counts[model.StatusDone]), fmt.Sprintf("failed: %d", counts[model.StatusFailed]), diff --git a/internal/monitor/dashboard_filter.go b/internal/monitor/dashboard_filter.go index f61ab30a..7def45b0 100644 --- a/internal/monitor/dashboard_filter.go +++ b/internal/monitor/dashboard_filter.go @@ -61,9 +61,9 @@ var runFilterPresets = []runFilterPreset{ apply: func() RunFilter { filter := DefaultRunFilter() filter.Statuses = map[model.Status]bool{ - model.StatusBlocked: true, - model.StatusBlockedAPI: true, - model.StatusFailed: true, + model.StatusWaiting: true, + model.StatusRateLimited: true, + model.StatusFailed: true, } return filter }, @@ -161,7 +161,7 @@ func (d *Dashboard) activateFilterRow() (tea.Model, tea.Cmd) { d.filter.Statuses = allStatusSet() return d, nil } - status := model.Status(row.value) + status := model.NormalizeStatus(row.value) if d.filter.Statuses == nil { d.filter.Statuses = make(map[model.Status]bool) } diff --git a/internal/monitor/monitor.go b/internal/monitor/monitor.go index 20453dcc..c1ac36f2 100644 --- a/internal/monitor/monitor.go +++ b/internal/monitor/monitor.go @@ -1309,8 +1309,8 @@ func (m *Monitor) getOrCreateControlSession(ctx context.Context, client *agent.O func defaultStatuses() []model.Status { return []model.Status{ model.StatusRunning, - model.StatusBlocked, - model.StatusBlockedAPI, + model.StatusWaiting, + model.StatusRateLimited, model.StatusBooting, model.StatusQueued, model.StatusPROpen, @@ -1804,7 +1804,7 @@ func apiRunToModel(r *orchapi.Run) *model.Run { return &model.Run{ IssueID: r.IssueID, RunID: r.RunID, - Status: model.Status(r.Status), + Status: model.NormalizeStatus(string(r.Status)), Agent: r.Agent, Model: r.Model, ModelVariant: r.ModelVariant, @@ -1848,7 +1848,7 @@ func apiIssueToModel(i *orchapi.Issue) *model.Issue { func statusSliceAPI(set map[model.Status]bool) []orchapi.RunStatus { result := make([]orchapi.RunStatus, 0, len(set)) for s := range set { - result = append(result, orchapi.RunStatus(s)) + result = append(result, orchapi.NormalizeRunStatus(string(s))) } return result } diff --git a/internal/monitor/run_filter.go b/internal/monitor/run_filter.go index 0788e067..d9a818f2 100644 --- a/internal/monitor/run_filter.go +++ b/internal/monitor/run_filter.go @@ -28,8 +28,8 @@ const ( var runStatusOptions = []model.Status{ model.StatusRunning, - model.StatusBlocked, - model.StatusBlockedAPI, + model.StatusWaiting, + model.StatusRateLimited, model.StatusQueued, model.StatusBooting, model.StatusPROpen, diff --git a/internal/monitor/run_filter_test.go b/internal/monitor/run_filter_test.go index 9b266177..3faa6dbe 100644 --- a/internal/monitor/run_filter_test.go +++ b/internal/monitor/run_filter_test.go @@ -68,7 +68,7 @@ func TestRunFilterRows(t *testing.T) { }, { IssueID: "orch-2", - Status: model.StatusBlocked, + Status: model.StatusWaiting, Agent: "claude", PR: "-", Merged: "conflict", @@ -91,7 +91,7 @@ func TestRunFilterRows(t *testing.T) { filter := DefaultRunFilter() filter.Statuses = map[model.Status]bool{ model.StatusRunning: true, - model.StatusBlocked: true, + model.StatusWaiting: true, } filter.Agent = "codex" filter.Merged = mergedFilterClean diff --git a/internal/monitor/sort.go b/internal/monitor/sort.go index 955daf35..7a6eb0af 100644 --- a/internal/monitor/sort.go +++ b/internal/monitor/sort.go @@ -159,8 +159,8 @@ func SortIndicator(dir SortDirection) string { var runStatusOrder = map[model.Status]int{ model.StatusRunning: 0, - model.StatusBlocked: 1, - model.StatusBlockedAPI: 2, + model.StatusWaiting: 1, + model.StatusRateLimited: 2, model.StatusBooting: 3, model.StatusQueued: 4, model.StatusPROpen: 5, diff --git a/internal/monitor/sort_test.go b/internal/monitor/sort_test.go index 48ee3be8..9b9eb1f5 100644 --- a/internal/monitor/sort_test.go +++ b/internal/monitor/sort_test.go @@ -112,7 +112,7 @@ func TestSortRunRows(t *testing.T) { { IssueID: "b", ShortID: "b1", - Status: model.StatusBlocked, + Status: model.StatusWaiting, Updated: updatedMid, Run: &model.Run{IssueID: "b", RunID: "002"}, }, @@ -133,7 +133,7 @@ func TestSortRunRows(t *testing.T) { } sortRunRows(rows, SortByUpdated) - if rows[0].Status != model.StatusRunning || rows[1].Status != model.StatusBlocked || rows[2].Status != model.StatusDone { + if rows[0].Status != model.StatusRunning || rows[1].Status != model.StatusWaiting || rows[2].Status != model.StatusDone { t.Fatalf("SortByUpdated order mismatch: got %v, %v, %v", rows[0].Status, rows[1].Status, rows[2].Status) } if rows[0].Index != 1 || rows[1].Index != 2 || rows[2].Index != 3 { @@ -156,7 +156,7 @@ func TestSortRunRows(t *testing.T) { { IssueID: "b", ShortID: "b1", - Status: model.StatusBlocked, + Status: model.StatusWaiting, Updated: updatedMid, Run: &model.Run{IssueID: "b", RunID: "002"}, }, diff --git a/internal/monitor/styles.go b/internal/monitor/styles.go index d28cc9db..3a250a89 100644 --- a/internal/monitor/styles.go +++ b/internal/monitor/styles.go @@ -29,8 +29,8 @@ func DefaultStyles() Styles { Faint: lipgloss.NewStyle().Foreground(lipgloss.Color("241")), Status: map[model.Status]lipgloss.Style{ model.StatusRunning: lipgloss.NewStyle().Foreground(lipgloss.Color("2")), - model.StatusBlocked: lipgloss.NewStyle().Foreground(lipgloss.Color("3")), - model.StatusBlockedAPI: lipgloss.NewStyle().Foreground(lipgloss.Color("3")), + model.StatusWaiting: lipgloss.NewStyle().Foreground(lipgloss.Color("3")), + model.StatusRateLimited: lipgloss.NewStyle().Foreground(lipgloss.Color("3")), model.StatusBooting: lipgloss.NewStyle().Foreground(lipgloss.Color("2")), model.StatusQueued: lipgloss.NewStyle().Foreground(lipgloss.Color("7")), model.StatusPROpen: lipgloss.NewStyle().Foreground(lipgloss.Color("6")), diff --git a/internal/notify/slack.go b/internal/notify/slack.go index 5492cb75..3621ddc7 100644 --- a/internal/notify/slack.go +++ b/internal/notify/slack.go @@ -50,7 +50,7 @@ func (s *SlackNotifier) NotifyBlocked(run *model.Run, issueTitle string, lastOut emoji := statusEmoji(run.Status) statusText := statusDescription(run.Status) - text := fmt.Sprintf("%s Run blocked: %s#%s", emoji, run.IssueID, run.ShortID()) + text := fmt.Sprintf("%s Run waiting: %s#%s", emoji, run.IssueID, run.ShortID()) attachCmd := fmt.Sprintf("orch attach %s#%s", run.IssueID, run.RunID) @@ -59,7 +59,7 @@ func (s *SlackNotifier) NotifyBlocked(run *model.Run, issueTitle string, lastOut Type: "section", Text: &SlackBlockText{ Type: "mrkdwn", - Text: fmt.Sprintf("*%s Run blocked: %s#%s*", emoji, run.IssueID, run.ShortID()), + Text: fmt.Sprintf("*%s Run waiting: %s#%s*", emoji, run.IssueID, run.ShortID()), }, }, { @@ -185,7 +185,7 @@ func (s *SlackNotifier) sendBotMessage(msg SlackMessage) error { func statusEmoji(status model.Status) string { switch status { - case model.StatusBlocked, model.StatusBlockedAPI: + case model.StatusWaiting, model.StatusRateLimited: return ":no_entry:" case model.StatusDone: return ":white_check_mark:" @@ -270,9 +270,9 @@ func isUIElement(line string) bool { func statusDescription(status model.Status) string { switch status { - case model.StatusBlocked: + case model.StatusWaiting: return "waiting for user input" - case model.StatusBlockedAPI: + case model.StatusRateLimited: return "waiting for API response" case model.StatusDone: return "task completed" diff --git a/internal/notify/slack_test.go b/internal/notify/slack_test.go index 3d2d2896..c325c89d 100644 --- a/internal/notify/slack_test.go +++ b/internal/notify/slack_test.go @@ -35,7 +35,7 @@ func TestSlackNotifier_NotifyBlocked_Webhook(t *testing.T) { run := &model.Run{ IssueID: "test-123", RunID: "20260115-120000", - Status: model.StatusBlocked, + Status: model.StatusWaiting, } err := notifier.NotifyBlocked(run, "Test Issue Title", "Agent is waiting for input") @@ -79,7 +79,7 @@ func TestSlackNotifier_NotifyBlocked_BotToken(t *testing.T) { run := &model.Run{ IssueID: "test-456", RunID: "20260115-130000", - Status: model.StatusBlockedAPI, + Status: model.StatusRateLimited, } err := notifier.NotifyBlocked(run, "Another Issue", "") @@ -144,13 +144,25 @@ func TestSlackConfig_ShouldNotify(t *testing.T) { expected: false, }, { - name: "default notifies on blocked", + name: "default notifies on waiting", + cfg: config.SlackConfig{Enabled: true}, + status: "waiting", + expected: true, + }, + { + name: "default notifies on rate_limited", + cfg: config.SlackConfig{Enabled: true}, + status: "rate_limited", + expected: true, + }, + { + name: "default notifies on blocked (compat)", cfg: config.SlackConfig{Enabled: true}, status: "blocked", expected: true, }, { - name: "default notifies on blocked_api", + name: "default notifies on blocked_api (compat)", cfg: config.SlackConfig{Enabled: true}, status: "blocked_api", expected: true, @@ -189,8 +201,8 @@ func TestStatusEmoji(t *testing.T) { status model.Status expected string }{ - {model.StatusBlocked, ":no_entry:"}, - {model.StatusBlockedAPI, ":no_entry:"}, + {model.StatusWaiting, ":no_entry:"}, + {model.StatusRateLimited, ":no_entry:"}, {model.StatusDone, ":white_check_mark:"}, {model.StatusFailed, ":x:"}, {model.StatusPROpen, ":pull_request:"}, @@ -211,8 +223,8 @@ func TestStatusDescription(t *testing.T) { status model.Status expected string }{ - {model.StatusBlocked, "waiting for user input"}, - {model.StatusBlockedAPI, "waiting for API response"}, + {model.StatusWaiting, "waiting for user input"}, + {model.StatusRateLimited, "waiting for API response"}, {model.StatusDone, "task completed"}, {model.StatusFailed, "run failed"}, } diff --git a/internal/orchapi/types.go b/internal/orchapi/types.go index 1ff99f0a..b5778463 100644 --- a/internal/orchapi/types.go +++ b/internal/orchapi/types.go @@ -18,17 +18,28 @@ const ( type RunStatus string const ( - RunStatusQueued RunStatus = "queued" - RunStatusBooting RunStatus = "booting" - RunStatusRunning RunStatus = "running" - RunStatusBlocked RunStatus = "blocked" - RunStatusBlockedAPI RunStatus = "blocked_api" - RunStatusPROpen RunStatus = "pr_open" - RunStatusDone RunStatus = "done" - RunStatusFailed RunStatus = "failed" - RunStatusCanceled RunStatus = "canceled" + RunStatusQueued RunStatus = "queued" + RunStatusBooting RunStatus = "booting" + RunStatusRunning RunStatus = "running" + RunStatusWaiting RunStatus = "waiting" + RunStatusRateLimited RunStatus = "rate_limited" + RunStatusPROpen RunStatus = "pr_open" + RunStatusDone RunStatus = "done" + RunStatusFailed RunStatus = "failed" + RunStatusCanceled RunStatus = "canceled" ) +func NormalizeRunStatus(s string) RunStatus { + switch s { + case "blocked": + return RunStatusWaiting + case "blocked_api": + return RunStatusRateLimited + default: + return RunStatus(s) + } +} + type BranchState string const ( diff --git a/internal/orchapi/types_test.go b/internal/orchapi/types_test.go new file mode 100644 index 00000000..9be22d12 --- /dev/null +++ b/internal/orchapi/types_test.go @@ -0,0 +1,26 @@ +package orchapi + +import "testing" + +func TestNormalizeRunStatus(t *testing.T) { + tests := []struct { + input string + want RunStatus + }{ + {"blocked", RunStatusWaiting}, + {"blocked_api", RunStatusRateLimited}, + {"waiting", RunStatusWaiting}, + {"rate_limited", RunStatusRateLimited}, + {"running", RunStatusRunning}, + {"done", RunStatusDone}, + {"queued", RunStatusQueued}, + } + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + got := NormalizeRunStatus(tt.input) + if got != tt.want { + t.Errorf("NormalizeRunStatus(%q) = %q, want %q", tt.input, got, tt.want) + } + }) + } +} diff --git a/internal/pr/cache_test.go b/internal/pr/cache_test.go index 14fe6d46..f5dc3aca 100644 --- a/internal/pr/cache_test.go +++ b/internal/pr/cache_test.go @@ -1,8 +1,11 @@ package pr import ( + "fmt" "testing" + "time" + "github.com/s22625/orch/internal/git" "github.com/s22625/orch/internal/model" ) @@ -11,8 +14,9 @@ type fakeGitHubClient struct { output []byte err error - lastDir string - lastArgs []string + lastDir string + lastArgs []string + callCount int } func (f *fakeGitHubClient) IsAvailable() bool { @@ -20,11 +24,13 @@ func (f *fakeGitHubClient) IsAvailable() bool { } func (f *fakeGitHubClient) Run(args ...string) ([]byte, error) { + f.callCount++ f.lastArgs = append([]string(nil), args...) return f.output, f.err } func (f *fakeGitHubClient) RunInDir(dir string, args ...string) ([]byte, error) { + f.callCount++ f.lastDir = dir f.lastArgs = append([]string(nil), args...) return f.output, f.err @@ -85,3 +91,274 @@ func TestPopulateRunInfoWithClient_UsesInjectedClient(t *testing.T) { t.Fatalf("run.PRUrl = %q", runs[0].PRUrl) } } + +// --------------------------------------------------------------------------- +// Regression tests: verify existing PopulateRunInfo caching works (must PASS) +// --------------------------------------------------------------------------- + +func TestPopulateRunInfo_RespectsMaxFetches(t *testing.T) { + t.Setenv("XDG_CACHE_HOME", t.TempDir()) + + client := &fakeGitHubClient{ + available: true, + output: []byte(`[{"url":"https://github.com/acme/repo/pull/1","number":1,"state":"OPEN"}]`), + } + + runs := make([]*model.Run, 6) + for i := range runs { + runs[i] = &model.Run{ + IssueID: "test", + RunID: fmt.Sprintf("run-%d", i), + Branch: fmt.Sprintf("branch-%d", i), + } + } + + PopulateRunInfoWithClient(client, runs) + + if client.callCount > cacheMaxFetches { + t.Fatalf("expected at most %d API calls (cacheMaxFetches), got %d", + cacheMaxFetches, client.callCount) + } +} + +func TestPopulateRunInfo_RespectsMinFetchInterval(t *testing.T) { + t.Setenv("XDG_CACHE_HOME", t.TempDir()) + + client := &fakeGitHubClient{ + available: true, + output: []byte(`[{"url":"https://github.com/acme/repo/pull/1","number":1,"state":"OPEN"}]`), + } + + runs := []*model.Run{ + {IssueID: "test", RunID: "run-1", Branch: "branch-a"}, + } + + PopulateRunInfoWithClient(client, runs) + firstCallCount := client.callCount + + runs2 := []*model.Run{ + {IssueID: "test", RunID: "run-2", Branch: "branch-b"}, + } + PopulateRunInfoWithClient(client, runs2) + + if client.callCount != firstCallCount { + t.Fatalf("expected 0 additional API calls within cacheMinFetchInterval, got %d", + client.callCount-firstCallCount) + } +} + +func TestPopulateRunInfo_CacheHitSkipsAPI(t *testing.T) { + t.Setenv("XDG_CACHE_HOME", t.TempDir()) + + repoRoot, err := git.FindMainRepoRoot("") + if err != nil { + t.Skip("no git repo root available") + } + branch := "feature/pre-cached" + + cachePath, err := getCachePath(repoRoot) + if err != nil { + t.Fatalf("getCachePath: %v", err) + } + preCached := cache{ + Entries: map[string]cacheEntry{ + branch: { + URL: "https://github.com/acme/repo/pull/77", + Number: 77, + State: "OPEN", + CheckedAt: time.Now(), + }, + }, + } + saveCache(cachePath, preCached) + + client := &fakeGitHubClient{available: true} + + run := &model.Run{ + IssueID: "test", + RunID: "run-1", + Branch: branch, + } + + infoMap := PopulateRunInfoWithClient(client, []*model.Run{run}) + info := infoMap[branch] + + if info == nil { + t.Fatal("expected cached info, got nil") + } + if info.URL != "https://github.com/acme/repo/pull/77" { + t.Fatalf("expected cached URL, got %q", info.URL) + } + if client.callCount != 0 { + t.Fatalf("expected 0 API calls (cache hit), got %d", client.callCount) + } +} + +// --------------------------------------------------------------------------- +// TDD RED tests: define desired caching for currently-uncached functions +// These tests FAIL against current code — fix tracked by orch-gh-ratelimit +// --------------------------------------------------------------------------- + +func TestLookupInfoWithClient_MustUseCache(t *testing.T) { + t.Setenv("XDG_CACHE_HOME", t.TempDir()) + + client := &fakeGitHubClient{ + available: true, + output: []byte(`[{"url":"https://github.com/acme/repo/pull/42","number":42,"state":"OPEN"}]`), + } + repoRoot := t.TempDir() + branch := "feature/must-cache" + + info1, err := LookupInfoWithClient(client, repoRoot, branch) + if err != nil { + t.Fatalf("first call error: %v", err) + } + if info1 == nil { + t.Fatal("first call: expected info") + } + if client.callCount != 1 { + t.Fatalf("first call: expected 1 API call, got %d", client.callCount) + } + + info2, err := LookupInfoWithClient(client, repoRoot, branch) + if err != nil { + t.Fatalf("second call error: %v", err) + } + if info2 == nil { + t.Fatal("second call: expected cached info, got nil") + } + if client.callCount != 1 { + t.Fatalf("second call: expected still 1 API call (cache hit), got %d", client.callCount) + } + if info2.URL != info1.URL || info2.Number != info1.Number { + t.Fatal("cached info doesn't match original") + } +} + +func TestLookupInfoWithClient_CacheHitSkipsAPI(t *testing.T) { + t.Setenv("XDG_CACHE_HOME", t.TempDir()) + + repoRoot := t.TempDir() + branch := "feature/pre-populated" + + cachePath, err := getCachePath(repoRoot) + if err != nil { + t.Fatalf("getCachePath: %v", err) + } + preCached := cache{ + LastFetch: time.Now(), + Entries: map[string]cacheEntry{ + branch: { + URL: "https://github.com/acme/repo/pull/99", + Number: 99, + State: "MERGED", + CheckedAt: time.Now(), + }, + }, + } + saveCache(cachePath, preCached) + + client := &fakeGitHubClient{ + available: true, + output: []byte(`[{"url":"https://github.com/acme/repo/pull/999","number":999,"state":"OPEN"}]`), + } + + info, err := LookupInfoWithClient(client, repoRoot, branch) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if info == nil { + t.Fatal("expected cached info, got nil") + } + if client.callCount != 0 { + t.Fatalf("expected 0 API calls (cache hit), got %d", client.callCount) + } + if info.URL != "https://github.com/acme/repo/pull/99" { + t.Fatalf("expected cached URL https://github.com/acme/repo/pull/99, got %q (cache was ignored)", info.URL) + } + if info.Number != 99 { + t.Fatalf("expected cached number 99, got %d", info.Number) + } + if info.State != "MERGED" { + t.Fatalf("expected cached state MERGED, got %q", info.State) + } +} + +func TestLookupInfoByURL_MustUseCache(t *testing.T) { + t.Setenv("XDG_CACHE_HOME", t.TempDir()) + + oldClient := ghClient + defer func() { ghClient = oldClient }() + + client := &fakeGitHubClient{ + available: true, + output: []byte(`{"url":"https://github.com/acme/repo/pull/42","number":42,"state":"OPEN"}`), + } + ghClient = client + + prURL := "https://github.com/acme/repo/pull/42" + + info1, err := LookupInfoByURL(prURL) + if err != nil { + t.Fatalf("first call error: %v", err) + } + if info1 == nil { + t.Fatal("first call: expected info") + } + if client.callCount != 1 { + t.Fatalf("first call: expected 1 API call, got %d", client.callCount) + } + + info2, err := LookupInfoByURL(prURL) + if err != nil { + t.Fatalf("second call error: %v", err) + } + if info2 == nil { + t.Fatal("second call: expected cached info, got nil") + } + if client.callCount != 1 { + t.Fatalf("second call: expected still 1 API call (cache hit), got %d", client.callCount) + } + if info2.URL != info1.URL || info2.Number != info1.Number { + t.Fatal("cached info doesn't match original") + } +} + +func TestLookupInfoByURL_CacheHitSkipsAPI(t *testing.T) { + t.Setenv("XDG_CACHE_HOME", t.TempDir()) + + oldClient := ghClient + defer func() { ghClient = oldClient }() + + client := &fakeGitHubClient{ + available: true, + output: []byte(`{"url":"https://github.com/acme/repo/pull/55","number":55,"state":"MERGED"}`), + } + ghClient = client + + prURL := "https://github.com/acme/repo/pull/55" + + info1, err := LookupInfoByURL(prURL) + if err != nil { + t.Fatalf("seed call error: %v", err) + } + if info1 == nil { + t.Fatal("seed call: expected info") + } + + seedCalls := client.callCount + + info2, err := LookupInfoByURL(prURL) + if err != nil { + t.Fatalf("cached call error: %v", err) + } + if info2 == nil { + t.Fatal("cached call: expected info from cache, got nil") + } + if client.callCount != seedCalls { + t.Fatalf("cached call: expected 0 additional API calls, got %d", client.callCount-seedCalls) + } + if info2.State != "MERGED" { + t.Fatalf("cached call: expected state MERGED, got %q", info2.State) + } +} diff --git a/internal/query/loader.go b/internal/query/loader.go index 47d5f9d4..fcd01286 100644 --- a/internal/query/loader.go +++ b/internal/query/loader.go @@ -192,7 +192,7 @@ func apiRunToModel(r *orchapi.Run) *model.Run { run := &model.Run{ IssueID: r.IssueID, RunID: r.RunID, - Status: model.Status(r.Status), + Status: model.NormalizeStatus(string(r.Status)), Phase: model.Phase(r.Phase), Agent: r.Agent, Model: r.Model, diff --git a/internal/store/file/file.go b/internal/store/file/file.go index d39835f2..6752f9e5 100644 --- a/internal/store/file/file.go +++ b/internal/store/file/file.go @@ -705,7 +705,7 @@ func (s *FileStore) AppendEvent(ref *model.RunRef, event *model.Event) error { } if event.Type == model.EventTypeStatus { - newStatus := model.Status(event.Name) + newStatus := model.NormalizeStatus(event.Name) source := model.EventSource(event.Attrs["source"]) if source == "" { source = model.EventSourceDaemon diff --git a/internal/store/file/file_test.go b/internal/store/file/file_test.go index 3a29e143..9444d6f2 100644 --- a/internal/store/file/file_test.go +++ b/internal/store/file/file_test.go @@ -312,7 +312,7 @@ func TestListRunsWithStatusFilter(t *testing.T) { s.AppendEvent(run1.Ref(), model.NewStatusEvent(model.StatusRunning)) run2, _ := s.CreateRun("test123", "20231220-110000", nil) - s.AppendEvent(run2.Ref(), model.NewStatusEvent(model.StatusBlocked)) + s.AppendEvent(run2.Ref(), model.NewStatusEvent(model.StatusWaiting)) run3, _ := s.CreateRun("test123", "20231220-120000", nil) s.AppendEvent(run3.Ref(), model.NewStatusEvent(model.StatusDone)) @@ -324,7 +324,7 @@ func TestListRunsWithStatusFilter(t *testing.T) { } // Filter by multiple statuses - runs, _ = s.ListRuns(&store.ListRunsFilter{Status: []model.Status{model.StatusRunning, model.StatusBlocked}}) + runs, _ = s.ListRuns(&store.ListRunsFilter{Status: []model.Status{model.StatusRunning, model.StatusWaiting}}) if len(runs) != 2 { t.Errorf("expected 2 runs, got %d", len(runs)) } diff --git a/orch-monitor-tui/orch_monitor/api/orch_pb2.py b/orch-monitor-tui/orch_monitor/api/orch_pb2.py index d70a2dee..d0b32af9 100644 --- a/orch-monitor-tui/orch_monitor/api/orch_pb2.py +++ b/orch-monitor-tui/orch_monitor/api/orch_pb2.py @@ -24,7 +24,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\norch.proto\x12\x07orch.v1\"W\n\tDiffStats\x12\x11\n\tadditions\x18\x01 \x01(\x05\x12\x11\n\tdeletions\x18\x02 \x01(\x05\x12\x15\n\rfiles_changed\x18\x03 \x01(\x05\x12\r\n\x05\x66iles\x18\x04 \x03(\t\"\xf0\x04\n\x03Run\x12\x10\n\x08issue_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t\x12\"\n\x06status\x18\x03 \x01(\x0e\x32\x12.orch.v1.RunStatus\x12\r\n\x05\x61gent\x18\x04 \x01(\t\x12\r\n\x05model\x18\x05 \x01(\t\x12\x0e\n\x06\x62ranch\x18\x06 \x01(\t\x12\x15\n\rworktree_path\x18\x07 \x01(\t\x12\x0e\n\x06pr_url\x18\x08 \x01(\t\x12\x17\n\x0fstarted_at_unix\x18\t \x01(\x03\x12\x17\n\x0fupdated_at_unix\x18\n \x01(\x03\x12\x17\n\x0f\x65lapsed_seconds\x18\x0b \x01(\x05\x12\x17\n\x0f\x65lapsed_display\x18\x0c \x01(\t\x12&\n\ndiff_stats\x18\r \x01(\x0b\x32\x12.orch.v1.DiffStats\x12*\n\x0c\x62ranch_state\x18\x0e \x01(\x0e\x32\x14.orch.v1.BranchState\x12\x14\n\x0csession_name\x18\x0f \x01(\t\x12)\n\x0bmultiplexer\x18\x10 \x01(\x0e\x32\x14.orch.v1.Multiplexer\x12\x13\n\x0bserver_port\x18\x11 \x01(\x05\x12\x1b\n\x13opencode_session_id\x18\x12 \x01(\t\x12\x16\n\x0e\x63ontinued_from\x18\x13 \x01(\t\x12\x11\n\tpr_number\x18\x14 \x01(\x05\x12\x10\n\x08pr_state\x18\x15 \x01(\t\x12\x14\n\x0cissue_status\x18\x16 \x01(\t\x12\x13\n\x0bissue_topic\x18\x17 \x01(\t\x12\r\n\x05\x61live\x18\x18 \x01(\x08\x12\x13\n\x0b\x61live_known\x18\x19 \x01(\x08\x12\x17\n\x0fworktree_exists\x18\x1a \x01(\x08\"\xac\x01\n\x05Issue\x12\n\n\x02id\x18\x01 \x01(\t\x12\r\n\x05title\x18\x02 \x01(\t\x12\x0f\n\x07summary\x18\x03 \x01(\t\x12$\n\x06status\x18\x04 \x01(\x0e\x32\x14.orch.v1.IssueStatus\x12\x0c\n\x04tags\x18\x05 \x03(\t\x12\x0c\n\x04\x62ody\x18\x06 \x01(\t\x12\x0c\n\x04path\x18\x07 \x01(\t\x12\x18\n\x10modified_at_unix\x18\x08 \x01(\x03\x12\r\n\x05topic\x18\t \x01(\t\"\x93\x01\n\x05\x45vent\x12\x16\n\x0etimestamp_unix\x18\x01 \x01(\x03\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12(\n\x05\x61ttrs\x18\x04 \x03(\x0b\x32\x19.orch.v1.Event.AttrsEntry\x1a,\n\nAttrsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\r\n\x0bPingRequest\"+\n\x0cPingResponse\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x0f\n\x07version\x18\x02 \x01(\t\"\xc7\x01\n\x0fListRunsRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\"\n\x06status\x18\x03 \x03(\x0e\x32\x12.orch.v1.RunStatus\x12\r\n\x05\x61gent\x18\x04 \x01(\t\x12\x13\n\x0btext_search\x18\x05 \x01(\t\x12\x12\n\ntime_range\x18\x06 \x01(\t\x12\r\n\x05limit\x18\x07 \x01(\x05\x12\x0e\n\x06\x63ursor\x18\x08 \x01(\t\x12\x12\n\nolder_than\x18\t \x01(\t\"R\n\x10ListRunsResponse\x12\x1a\n\x04runs\x18\x01 \x03(\x0b\x32\x0c.orch.v1.Run\x12\r\n\x05total\x18\x02 \x01(\x05\x12\x13\n\x0bnext_cursor\x18\x03 \x01(\t\"F\n\rGetRunRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\"K\n\x0eGetRunResponse\x12\x19\n\x03run\x18\x01 \x01(\x0b\x32\x0c.orch.v1.Run\x12\x1e\n\x06\x65vents\x18\x02 \x03(\x0b\x32\x0e.orch.v1.Event\"\xff\x02\n\x0fStartRunRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\r\n\x05\x61gent\x18\x03 \x01(\t\x12\r\n\x05model\x18\x04 \x01(\t\x12\x15\n\rmodel_variant\x18\x05 \x01(\t\x12\x13\n\x0b\x62\x61se_branch\x18\x06 \x01(\t\x12\x14\n\x0cproject_root\x18\x07 \x01(\t\x12\x0e\n\x06preset\x18\x08 \x01(\t\x12\x0e\n\x06\x62ranch\x18\t \x01(\t\x12\x14\n\x0cworktree_dir\x18\n \x01(\t\x12\r\n\x05no_pr\x18\x0b \x01(\x08\x12\x17\n\x0fprompt_template\x18\x0c \x01(\t\x12\x18\n\x10pr_target_branch\x18\r \x01(\t\x12\x0f\n\x07\x64ry_run\x18\x0e \x01(\x08\x12\r\n\x05reuse\x18\x0f \x01(\x08\x12\x0e\n\x06run_id\x18\x10 \x01(\t\x12\x11\n\tagent_cmd\x18\x11 \x01(\t\x12\x15\n\ragent_profile\x18\x12 \x01(\t\x12\x13\n\x0bmultiplexer\x18\x13 \x01(\t\"o\n\x10StartRunResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12\x0e\n\x06\x62ranch\x18\x02 \x01(\t\x12\x15\n\rworktree_path\x18\x03 \x01(\t\x12\x14\n\x0csession_name\x18\x04 \x01(\t\x12\x0e\n\x06status\x18\x05 \x01(\t\"\xb5\x01\n\x10\x43reateRunRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x39\n\x08metadata\x18\x04 \x03(\x0b\x32\'.orch.v1.CreateRunRequest.MetadataEntry\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"C\n\x11\x43reateRunResponse\x12\x10\n\x08issue_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t\x12\x0c\n\x04path\x18\x03 \x01(\t\"G\n\x0eStopRunRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\"\x11\n\x0fStopRunResponse\"J\n\x11ResolveRunRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\"\x14\n\x12ResolveRunResponse\"\xa3\x01\n\x11ListIssuesRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12$\n\x06status\x18\x02 \x03(\x0e\x32\x14.orch.v1.IssueStatus\x12\x0c\n\x04tags\x18\x03 \x03(\t\x12\x11\n\ttags_mode\x18\x04 \x01(\t\x12\x13\n\x0btext_search\x18\x05 \x01(\t\x12\r\n\x05limit\x18\x06 \x01(\x05\x12\x0e\n\x06\x63ursor\x18\x07 \x01(\t\"X\n\x12ListIssuesResponse\x12\x1e\n\x06issues\x18\x01 \x03(\x0b\x32\x0e.orch.v1.Issue\x12\r\n\x05total\x18\x02 \x01(\x05\x12\x13\n\x0bnext_cursor\x18\x03 \x01(\t\"8\n\x0fGetIssueRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\"1\n\x10GetIssueResponse\x12\x1d\n\x05issue\x18\x01 \x01(\x0b\x32\x0e.orch.v1.Issue\"f\n\x12\x43reateIssueRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\r\n\x05title\x18\x03 \x01(\t\x12\x0c\n\x04\x62ody\x18\x04 \x01(\t\x12\x0c\n\x04tags\x18\x05 \x03(\t\"#\n\x13\x43reateIssueResponse\x12\x0c\n\x04path\x18\x01 \x01(\t\":\n\x11\x43loseIssueRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\"\x14\n\x12\x43loseIssueResponse\"X\n\x1cGetControlAgentLaunchRequest\x12\x14\n\x0cproject_root\x18\x01 \x01(\t\x12\r\n\x05\x61gent\x18\x02 \x01(\t\x12\x13\n\x0bnew_session\x18\x03 \x01(\x08\"g\n\x1dGetControlAgentLaunchResponse\x12\x0f\n\x07\x63ommand\x18\x01 \x01(\t\x12\x13\n\x0bprompt_file\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\x05\x12\x12\n\nsession_id\x18\x04 \x01(\t\"_\n\x14GetAttachInfoRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08short_id\x18\x04 \x01(\t\"\xe3\x01\n\x15GetAttachInfoResponse\x12\x0f\n\x07\x63ommand\x18\x01 \x03(\t\x12)\n\x0bmultiplexer\x18\x02 \x01(\x0e\x32\x14.orch.v1.Multiplexer\x12\x14\n\x0csession_name\x18\x03 \x01(\t\x12\x15\n\rworktree_path\x18\x04 \x01(\t\x12\r\n\x05\x61gent\x18\x05 \x01(\t\x12\x13\n\x0bserver_port\x18\x06 \x01(\x05\x12\x1b\n\x13opencode_session_id\x18\x07 \x01(\t\x12\x10\n\x08issue_id\x18\x08 \x01(\t\x12\x0e\n\x06run_id\x18\t \x01(\t\"N\n\x15\x43\x61ptureSessionRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\"Q\n\x16\x43\x61ptureSessionResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12\x16\n\x0etimestamp_unix\x18\x02 \x01(\x03\x12\x0e\n\x06source\x18\x03 \x01(\t\"\\\n\x12SendMessageRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x0f\n\x07message\x18\x04 \x01(\t\"\x15\n\x13SendMessageResponse\"L\n\x13GetDiffStatsRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\">\n\x14GetDiffStatsResponse\x12&\n\ndiff_stats\x18\x01 \x01(\x0b\x32\x12.orch.v1.DiffStats\"N\n\x15GetBranchStateRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\"=\n\x16GetBranchStateResponse\x12#\n\x05state\x18\x01 \x01(\x0e\x32\x14.orch.v1.BranchState\"G\n\x0eGetDiffRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\"\x1f\n\x0fGetDiffResponse\x12\x0c\n\x04\x64iff\x18\x01 \x01(\t\"p\n\x16RegisterMonitorRequest\x12\x0b\n\x03pid\x18\x01 \x01(\x05\x12\x14\n\x0cmonitor_type\x18\x02 \x01(\t\x12\x0c\n\x04view\x18\x03 \x01(\t\x12\x0f\n\x07project\x18\x04 \x01(\t\x12\x14\n\x0csession_name\x18\x05 \x01(\t\"-\n\x17RegisterMonitorResponse\x12\x12\n\nmonitor_id\x18\x01 \x01(\t\".\n\x18UnregisterMonitorRequest\x12\x12\n\nmonitor_id\x18\x01 \x01(\t\"\x1b\n\x19UnregisterMonitorResponse\"&\n\x10HeartbeatRequest\x12\x12\n\nmonitor_id\x18\x01 \x01(\t\"\x13\n\x11HeartbeatResponse\"3\n\x13ListMonitorsRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x0b\n\x03\x61ll\x18\x02 \x01(\x08\"\x9f\x01\n\x0bMonitorInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0b\n\x03pid\x18\x02 \x01(\x05\x12\x0c\n\x04type\x18\x03 \x01(\t\x12\x0c\n\x04view\x18\x04 \x01(\t\x12\x0f\n\x07project\x18\x05 \x01(\t\x12\x14\n\x0csession_name\x18\x06 \x01(\t\x12\x17\n\x0fstarted_at_unix\x18\x07 \x01(\x03\x12\x1b\n\x13last_heartbeat_unix\x18\x08 \x01(\x03\">\n\x14ListMonitorsResponse\x12&\n\x08monitors\x18\x01 \x03(\x0b\x32\x14.orch.v1.MonitorInfo\"V\n\x12KillMonitorRequest\x12\x12\n\nmonitor_id\x18\x01 \x01(\t\x12\x0b\n\x03\x61ll\x18\x02 \x01(\x08\x12\x0e\n\x06global\x18\x03 \x01(\x08\x12\x0f\n\x07project\x18\x04 \x01(\t\"+\n\x13KillMonitorResponse\x12\x14\n\x0ckilled_count\x18\x01 \x01(\x05\"?\n\x16GetRunByShortIDRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08short_id\x18\x02 \x01(\t\"T\n\x17GetRunByShortIDResponse\x12\x19\n\x03run\x18\x01 \x01(\x0b\x32\x0c.orch.v1.Run\x12\x1e\n\x06\x65vents\x18\x02 \x03(\x0b\x32\x0e.orch.v1.Event\"K\n\x13ResolveIssueRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\r\n\x05\x66orce\x18\x03 \x01(\x08\"(\n\x14ResolveIssueResponse\x12\x10\n\x08issue_id\x18\x01 \x01(\t\"\xfe\x01\n\x12\x41ppendEventRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x12\n\nevent_type\x18\x04 \x01(\t\x12\x12\n\nevent_name\x18\x05 \x01(\t\x12@\n\x0b\x65vent_attrs\x18\x06 \x03(\x0b\x32+.orch.v1.AppendEventRequest.EventAttrsEntry\x12\x14\n\x0c\x65vent_source\x18\x07 \x01(\t\x1a\x31\n\x0f\x45ventAttrsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"6\n\x13\x41ppendEventResponse\x12\x0f\n\x07skipped\x18\x01 \x01(\x08\x12\x0e\n\x06reason\x18\x02 \x01(\t\"3\n\x1b\x45nsureOpenCodeServerRequest\x12\x14\n\x0cproject_root\x18\x01 \x01(\t\"E\n\x1c\x45nsureOpenCodeServerResponse\x12\x0c\n\x04port\x18\x01 \x01(\x05\x12\x17\n\x0f\x61lready_running\x18\x02 \x01(\x08\"+\n\x13RegisterRepoRequest\x12\x14\n\x0cproject_root\x18\x01 \x01(\t\"\'\n\x14RegisterRepoResponse\x12\x0f\n\x07repo_id\x18\x01 \x01(\t\"\x12\n\x10ListReposRequest\"A\n\x08RepoInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0cproject_root\x18\x02 \x01(\t\x12\x13\n\x0bissues_root\x18\x03 \x01(\t\"5\n\x11ListReposResponse\x12 \n\x05repos\x18\x01 \x03(\x0b\x32\x11.orch.v1.RepoInfo\"\x96\x01\n\x10\x44\x65leteRunRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08short_id\x18\x04 \x01(\t\x12\x15\n\rwith_worktree\x18\x05 \x01(\x08\x12\x13\n\x0bwith_branch\x18\x06 \x01(\x08\x12\r\n\x05\x66orce\x18\x07 \x01(\x08\"\x91\x01\n\x11\x44\x65leteRunResponse\x12\x10\n\x08issue_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t\x12\x10\n\x08short_id\x18\x03 \x01(\t\x12\x18\n\x10worktree_removed\x18\x04 \x01(\x08\x12\x16\n\x0e\x62ranch_removed\x18\x05 \x01(\x08\x12\x16\n\x0esession_killed\x18\x06 \x01(\x08\"y\n\x12UpdateIssueRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\r\n\x05title\x18\x03 \x01(\t\x12\x0f\n\x07summary\x18\x04 \x01(\t\x12\x0c\n\x04\x62ody\x18\x05 \x01(\t\x12\x0e\n\x06status\x18\x06 \x01(\t\"4\n\x13UpdateIssueResponse\x12\x1d\n\x05issue\x18\x01 \x01(\x0b\x32\x0e.orch.v1.Issue\"B\n\x19ValidateIssueFilesRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\"M\n\x0fValidationIssue\x12\x0c\n\x04\x63ode\x18\x01 \x01(\t\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x0c\n\x04line\x18\x03 \x01(\x05\x12\r\n\x05level\x18\x04 \x01(\t\"\x8c\x01\n\x14ValidationResultItem\x12\x0c\n\x04\x66ile\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12(\n\x06\x65rrors\x18\x03 \x03(\x0b\x32\x18.orch.v1.ValidationIssue\x12*\n\x08warnings\x18\x04 \x03(\x0b\x32\x18.orch.v1.ValidationIssue\",\n\x0f\x44uplicateIDItem\x12\n\n\x02id\x18\x01 \x01(\t\x12\r\n\x05\x66iles\x18\x02 \x03(\t\"\xc8\x01\n\x1aValidateIssueFilesResponse\x12\r\n\x05total\x18\x01 \x01(\x05\x12\r\n\x05valid\x18\x02 \x01(\x05\x12-\n\x06\x65rrors\x18\x03 \x03(\x0b\x32\x1d.orch.v1.ValidationResultItem\x12/\n\x08warnings\x18\x04 \x03(\x0b\x32\x1d.orch.v1.ValidationResultItem\x12,\n\nduplicates\x18\x05 \x03(\x0b\x32\x18.orch.v1.DuplicateIDItem\"s\n\x17WriteAgentPromptRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08short_id\x18\x04 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x05 \x01(\t\"\x1a\n\x18WriteAgentPromptResponse\"a\n\x16ReadAgentPromptRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08short_id\x18\x04 \x01(\t\"*\n\x17ReadAgentPromptResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\"4\n\x12RepairStateRequest\x12\x0f\n\x07\x64ry_run\x18\x01 \x01(\x08\x12\r\n\x05\x66orce\x18\x02 \x01(\x08\"V\n\x13RepairStateResponse\x12\x16\n\x0eproblems_found\x18\x01 \x01(\x05\x12\x16\n\x0eproblems_fixed\x18\x02 \x01(\x05\x12\x0f\n\x07\x64\x65tails\x18\x03 \x03(\t\"$\n\x13GetDaemonLogRequest\x12\r\n\x05lines\x18\x01 \x01(\x05\"\'\n\x14GetDaemonLogResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\"\x1f\n\x0fReadFileRequest\x12\x0c\n\x04path\x18\x01 \x01(\t\"#\n\x10ReadFileResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\x0c\"?\n\x10WriteFileRequest\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\x0c\x12\x0c\n\x04perm\x18\x03 \x01(\r\"\x13\n\x11WriteFileResponse\"U\n\x12KillSessionRequest\x12\x14\n\x0csession_name\x18\x01 \x01(\t\x12)\n\x0bmultiplexer\x18\x02 \x01(\x0e\x32\x14.orch.v1.Multiplexer\"%\n\x13KillSessionResponse\x12\x0e\n\x06killed\x18\x01 \x01(\x08\"@\n\x13ListSessionsRequest\x12)\n\x0bmultiplexer\x18\x01 \x01(\x0e\x32\x14.orch.v1.Multiplexer\"(\n\x14ListSessionsResponse\x12\x10\n\x08sessions\x18\x01 \x03(\t\"[\n\x10ResumeRunRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08short_id\x18\x04 \x01(\t\")\n\x11ResumeRunResponse\x12\x14\n\x0csession_name\x18\x01 \x01(\t\"@\n\x1aQueryOpenCodeServerRequest\x12\x0c\n\x04port\x18\x01 \x01(\x05\x12\x14\n\x0cproject_root\x18\x02 \x01(\t\"\\\n\x14OpenCodeProviderInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12*\n\x06models\x18\x03 \x03(\x0b\x32\x1a.orch.v1.OpenCodeModelInfo\"?\n\x11OpenCodeModelInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x10\n\x08variants\x18\x03 \x03(\t\"\xee\x01\n\x1bQueryOpenCodeServerResponse\x12\x16\n\x0eserver_running\x18\x01 \x01(\x08\x12\x30\n\tproviders\x18\x02 \x03(\x0b\x32\x1d.orch.v1.OpenCodeProviderInfo\x12O\n\x0esession_status\x18\x03 \x03(\x0b\x32\x37.orch.v1.QueryOpenCodeServerResponse.SessionStatusEntry\x1a\x34\n\x12SessionStatusEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xa9\x01\n\x1aInjectInitialPromptRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x0e\n\x06prompt\x18\x04 \x01(\t\x12\r\n\x05model\x18\x05 \x01(\t\x12\x15\n\rmodel_variant\x18\x06 \x01(\t\x12\x10\n\x08work_dir\x18\x07 \x01(\t\x12\x0c\n\x04port\x18\x08 \x01(\x05\"?\n\x1bInjectInitialPromptResponse\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12\x0c\n\x04port\x18\x02 \x01(\x05\"\xd2\x02\n\x12\x43ontinueRunRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x14\n\x0cproject_root\x18\x02 \x01(\t\x12\x10\n\x08issue_id\x18\x03 \x01(\t\x12\x0e\n\x06run_id\x18\x04 \x01(\t\x12\x10\n\x08short_id\x18\x05 \x01(\t\x12\x0e\n\x06\x62ranch\x18\x06 \x01(\t\x12\r\n\x05\x61gent\x18\x07 \x01(\t\x12\x11\n\tagent_cmd\x18\x08 \x01(\t\x12\x15\n\ragent_profile\x18\t \x01(\t\x12\x14\n\x0cworktree_dir\x18\n \x01(\t\x12\r\n\x05no_pr\x18\x0b \x01(\x08\x12\x17\n\x0fprompt_template\x18\x0c \x01(\t\x12\x18\n\x10pr_target_branch\x18\r \x01(\t\x12\x13\n\x0bmultiplexer\x18\x0e \x01(\t\x12\x14\n\x0csession_name\x18\x0f \x01(\t\x12\x11\n\trepo_root\x18\x10 \x01(\t\"\x9c\x01\n\x13\x43ontinueRunResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12\x0e\n\x06\x62ranch\x18\x02 \x01(\t\x12\x15\n\rworktree_path\x18\x03 \x01(\t\x12\x14\n\x0csession_name\x18\x04 \x01(\t\x12\x0e\n\x06status\x18\x05 \x01(\t\x12\x16\n\x0e\x63ontinued_from\x18\x06 \x01(\t\x12\x10\n\x08issue_id\x18\x07 \x01(\t\"(\n\x10GetConfigRequest\x12\x14\n\x0cproject_root\x18\x01 \x01(\t\"o\n\x10SlackConfigProto\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x13\n\x0bwebhook_url\x18\x02 \x01(\t\x12\x11\n\tbot_token\x18\x03 \x01(\t\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\x12\x11\n\tnotify_on\x18\x05 \x03(\t\"\x8e\x01\n\x13OpenCodeConfigProto\x12\x15\n\rdefault_model\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65\x66\x61ult_variant\x18\x02 \x01(\t\x12\x17\n\x0fprompt_template\x18\x03 \x01(\t\x12\x12\n\nextra_args\x18\x04 \x03(\t\x12\x1a\n\x12\x63ontrol_extra_args\x18\x05 \x03(\t\"\\\n\x11\x43laudeConfigProto\x12\x17\n\x0fprompt_template\x18\x01 \x01(\t\x12\x12\n\nextra_args\x18\x02 \x03(\t\x12\x1a\n\x12\x63ontrol_extra_args\x18\x03 \x03(\t\"[\n\x10\x43odexConfigProto\x12\x17\n\x0fprompt_template\x18\x01 \x01(\t\x12\x12\n\nextra_args\x18\x02 \x03(\t\x12\x1a\n\x12\x63ontrol_extra_args\x18\x03 \x03(\t\"\\\n\x11GeminiConfigProto\x12\x17\n\x0fprompt_template\x18\x01 \x01(\t\x12\x12\n\nextra_args\x18\x02 \x03(\t\x12\x1a\n\x12\x63ontrol_extra_args\x18\x03 \x03(\t\"]\n\x0bPresetProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x62\x61\x63kend\x18\x02 \x01(\t\x12\r\n\x05model\x18\x03 \x01(\t\x12\x0f\n\x07variant\x18\x04 \x01(\t\x12\x0f\n\x07profile\x18\x05 \x01(\t\"2\n\x11IssuesConfigProto\x12\x0f\n\x07\x62\x61\x63kend\x18\x01 \x01(\t\x12\x0c\n\x04path\x18\x02 \x01(\t\"\xd7\x01\n\x11GitHubConfigProto\x12\r\n\x05owner\x18\x01 \x01(\t\x12\x0c\n\x04repo\x18\x02 \x01(\t\x12\x14\n\x0clabel_filter\x18\x03 \x01(\t\x12\x15\n\rpoll_interval\x18\x04 \x01(\x05\x12\x43\n\rstatus_labels\x18\x05 \x03(\x0b\x32,.orch.v1.GitHubConfigProto.StatusLabelsEntry\x1a\x33\n\x11StatusLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"(\n\x12MonitorConfigProto\x12\x12\n\nps_columns\x18\x01 \x03(\t\"\x96\x06\n\x11GetConfigResponse\x12\r\n\x05\x61gent\x18\x01 \x01(\t\x12\r\n\x05model\x18\x02 \x01(\t\x12\x15\n\rmodel_variant\x18\x03 \x01(\t\x12\x14\n\x0cworktree_dir\x18\x04 \x01(\t\x12\x13\n\x0b\x62\x61se_branch\x18\x05 \x01(\t\x12\x18\n\x10pr_target_branch\x18\x06 \x01(\t\x12\x11\n\tlog_level\x18\x07 \x01(\t\x12\x17\n\x0fprompt_template\x18\x08 \x01(\t\x12\x13\n\x0bmultiplexer\x18\t \x01(\t\x12\x1b\n\x13monitor_multiplexer\x18\n \x01(\t\x12\x19\n\x11\x61gent_multiplexer\x18\x0b \x01(\t\x12\r\n\x05no_pr\x18\x0c \x01(\x08\x12\x16\n\x0e\x64\x65\x66\x61ult_preset\x18\r \x01(\t\x12\x15\n\rcontrol_agent\x18\x0e \x01(\t\x12\x15\n\rcontrol_model\x18\x0f \x01(\t\x12\x1d\n\x15\x63ontrol_model_variant\x18\x10 \x01(\t\x12\x11\n\tdiff_tool\x18\x11 \x01(\t\x12,\n\x07monitor\x18\x12 \x01(\x0b\x32\x1b.orch.v1.MonitorConfigProto\x12%\n\x07presets\x18\x13 \x03(\x0b\x32\x14.orch.v1.PresetProto\x12.\n\x08opencode\x18\x14 \x01(\x0b\x32\x1c.orch.v1.OpenCodeConfigProto\x12*\n\x06\x63laude\x18\x15 \x01(\x0b\x32\x1a.orch.v1.ClaudeConfigProto\x12(\n\x05\x63odex\x18\x16 \x01(\x0b\x32\x19.orch.v1.CodexConfigProto\x12*\n\x06gemini\x18\x17 \x01(\x0b\x32\x1a.orch.v1.GeminiConfigProto\x12(\n\x05slack\x18\x18 \x01(\x0b\x32\x19.orch.v1.SlackConfigProto\x12*\n\x06issues\x18\x19 \x01(\x0b\x32\x1a.orch.v1.IssuesConfigProto\x12*\n\x06github\x18\x1a \x01(\x0b\x32\x1a.orch.v1.GitHubConfigProto\"\x18\n\x16GetDaemonStatusRequest\"Z\n\x17GetDaemonStatusResponse\x12\x0f\n\x07running\x18\x01 \x01(\x08\x12\x0b\n\x03pid\x18\x02 \x01(\x05\x12\x10\n\x08log_path\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\t\"\xec\x13\n\x07Request\x12$\n\x04ping\x18\x01 \x01(\x0b\x32\x14.orch.v1.PingRequestH\x00\x12-\n\tlist_runs\x18\x02 \x01(\x0b\x32\x18.orch.v1.ListRunsRequestH\x00\x12)\n\x07get_run\x18\x03 \x01(\x0b\x32\x16.orch.v1.GetRunRequestH\x00\x12-\n\tstart_run\x18\x04 \x01(\x0b\x32\x18.orch.v1.StartRunRequestH\x00\x12+\n\x08stop_run\x18\x05 \x01(\x0b\x32\x17.orch.v1.StopRunRequestH\x00\x12\x31\n\x0bresolve_run\x18\x06 \x01(\x0b\x32\x1a.orch.v1.ResolveRunRequestH\x00\x12\x31\n\x0blist_issues\x18\x07 \x01(\x0b\x32\x1a.orch.v1.ListIssuesRequestH\x00\x12-\n\tget_issue\x18\x08 \x01(\x0b\x32\x18.orch.v1.GetIssueRequestH\x00\x12\x33\n\x0c\x63reate_issue\x18\t \x01(\x0b\x32\x1b.orch.v1.CreateIssueRequestH\x00\x12\x31\n\x0b\x63lose_issue\x18\n \x01(\x0b\x32\x1a.orch.v1.CloseIssueRequestH\x00\x12I\n\x18get_control_agent_launch\x18\x0b \x01(\x0b\x32%.orch.v1.GetControlAgentLaunchRequestH\x00\x12\x38\n\x0fget_attach_info\x18\x0c \x01(\x0b\x32\x1d.orch.v1.GetAttachInfoRequestH\x00\x12\x39\n\x0f\x63\x61pture_session\x18\r \x01(\x0b\x32\x1e.orch.v1.CaptureSessionRequestH\x00\x12\x33\n\x0csend_message\x18\x0e \x01(\x0b\x32\x1b.orch.v1.SendMessageRequestH\x00\x12\x36\n\x0eget_diff_stats\x18\x0f \x01(\x0b\x32\x1c.orch.v1.GetDiffStatsRequestH\x00\x12:\n\x10get_branch_state\x18\x10 \x01(\x0b\x32\x1e.orch.v1.GetBranchStateRequestH\x00\x12+\n\x08get_diff\x18\x11 \x01(\x0b\x32\x17.orch.v1.GetDiffRequestH\x00\x12;\n\x10register_monitor\x18\x12 \x01(\x0b\x32\x1f.orch.v1.RegisterMonitorRequestH\x00\x12?\n\x12unregister_monitor\x18\x13 \x01(\x0b\x32!.orch.v1.UnregisterMonitorRequestH\x00\x12.\n\theartbeat\x18\x14 \x01(\x0b\x32\x19.orch.v1.HeartbeatRequestH\x00\x12\x35\n\rlist_monitors\x18\x15 \x01(\x0b\x32\x1c.orch.v1.ListMonitorsRequestH\x00\x12\x33\n\x0ckill_monitor\x18\x16 \x01(\x0b\x32\x1b.orch.v1.KillMonitorRequestH\x00\x12>\n\x13get_run_by_short_id\x18\x17 \x01(\x0b\x32\x1f.orch.v1.GetRunByShortIDRequestH\x00\x12\x35\n\rresolve_issue\x18\x18 \x01(\x0b\x32\x1c.orch.v1.ResolveIssueRequestH\x00\x12\x33\n\x0c\x61ppend_event\x18\x19 \x01(\x0b\x32\x1b.orch.v1.AppendEventRequestH\x00\x12\x46\n\x16\x65nsure_opencode_server\x18\x1a \x01(\x0b\x32$.orch.v1.EnsureOpenCodeServerRequestH\x00\x12\x35\n\rregister_repo\x18\x1b \x01(\x0b\x32\x1c.orch.v1.RegisterRepoRequestH\x00\x12/\n\nlist_repos\x18\x1c \x01(\x0b\x32\x19.orch.v1.ListReposRequestH\x00\x12/\n\ndelete_run\x18\x1d \x01(\x0b\x32\x19.orch.v1.DeleteRunRequestH\x00\x12\x33\n\x0cupdate_issue\x18\x1e \x01(\x0b\x32\x1b.orch.v1.UpdateIssueRequestH\x00\x12\x42\n\x14validate_issue_files\x18\x1f \x01(\x0b\x32\".orch.v1.ValidateIssueFilesRequestH\x00\x12>\n\x12write_agent_prompt\x18 \x01(\x0b\x32 .orch.v1.WriteAgentPromptRequestH\x00\x12<\n\x11read_agent_prompt\x18! \x01(\x0b\x32\x1f.orch.v1.ReadAgentPromptRequestH\x00\x12\x33\n\x0crepair_state\x18\" \x01(\x0b\x32\x1b.orch.v1.RepairStateRequestH\x00\x12\x36\n\x0eget_daemon_log\x18# \x01(\x0b\x32\x1c.orch.v1.GetDaemonLogRequestH\x00\x12-\n\tread_file\x18$ \x01(\x0b\x32\x18.orch.v1.ReadFileRequestH\x00\x12/\n\nwrite_file\x18% \x01(\x0b\x32\x19.orch.v1.WriteFileRequestH\x00\x12/\n\ncreate_run\x18& \x01(\x0b\x32\x19.orch.v1.CreateRunRequestH\x00\x12\x33\n\x0ckill_session\x18\' \x01(\x0b\x32\x1b.orch.v1.KillSessionRequestH\x00\x12\x35\n\rlist_sessions\x18( \x01(\x0b\x32\x1c.orch.v1.ListSessionsRequestH\x00\x12/\n\nresume_run\x18) \x01(\x0b\x32\x19.orch.v1.ResumeRunRequestH\x00\x12\x44\n\x15query_opencode_server\x18* \x01(\x0b\x32#.orch.v1.QueryOpenCodeServerRequestH\x00\x12\x44\n\x15inject_initial_prompt\x18+ \x01(\x0b\x32#.orch.v1.InjectInitialPromptRequestH\x00\x12\x33\n\x0c\x63ontinue_run\x18, \x01(\x0b\x32\x1b.orch.v1.ContinueRunRequestH\x00\x12/\n\nget_config\x18- \x01(\x0b\x32\x19.orch.v1.GetConfigRequestH\x00\x12<\n\x11get_daemon_status\x18. \x01(\x0b\x32\x1f.orch.v1.GetDaemonStatusRequestH\x00\x42\t\n\x07request\"\xb7\x14\n\x08Response\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12%\n\x04ping\x18\x03 \x01(\x0b\x32\x15.orch.v1.PingResponseH\x00\x12.\n\tlist_runs\x18\x04 \x01(\x0b\x32\x19.orch.v1.ListRunsResponseH\x00\x12*\n\x07get_run\x18\x05 \x01(\x0b\x32\x17.orch.v1.GetRunResponseH\x00\x12.\n\tstart_run\x18\x06 \x01(\x0b\x32\x19.orch.v1.StartRunResponseH\x00\x12,\n\x08stop_run\x18\x07 \x01(\x0b\x32\x18.orch.v1.StopRunResponseH\x00\x12\x32\n\x0bresolve_run\x18\x08 \x01(\x0b\x32\x1b.orch.v1.ResolveRunResponseH\x00\x12\x32\n\x0blist_issues\x18\t \x01(\x0b\x32\x1b.orch.v1.ListIssuesResponseH\x00\x12.\n\tget_issue\x18\n \x01(\x0b\x32\x19.orch.v1.GetIssueResponseH\x00\x12\x34\n\x0c\x63reate_issue\x18\x0b \x01(\x0b\x32\x1c.orch.v1.CreateIssueResponseH\x00\x12\x32\n\x0b\x63lose_issue\x18\x0c \x01(\x0b\x32\x1b.orch.v1.CloseIssueResponseH\x00\x12J\n\x18get_control_agent_launch\x18\r \x01(\x0b\x32&.orch.v1.GetControlAgentLaunchResponseH\x00\x12\x39\n\x0fget_attach_info\x18\x0e \x01(\x0b\x32\x1e.orch.v1.GetAttachInfoResponseH\x00\x12:\n\x0f\x63\x61pture_session\x18\x0f \x01(\x0b\x32\x1f.orch.v1.CaptureSessionResponseH\x00\x12\x34\n\x0csend_message\x18\x10 \x01(\x0b\x32\x1c.orch.v1.SendMessageResponseH\x00\x12\x37\n\x0eget_diff_stats\x18\x11 \x01(\x0b\x32\x1d.orch.v1.GetDiffStatsResponseH\x00\x12;\n\x10get_branch_state\x18\x12 \x01(\x0b\x32\x1f.orch.v1.GetBranchStateResponseH\x00\x12,\n\x08get_diff\x18\x13 \x01(\x0b\x32\x18.orch.v1.GetDiffResponseH\x00\x12<\n\x10register_monitor\x18\x14 \x01(\x0b\x32 .orch.v1.RegisterMonitorResponseH\x00\x12@\n\x12unregister_monitor\x18\x15 \x01(\x0b\x32\".orch.v1.UnregisterMonitorResponseH\x00\x12/\n\theartbeat\x18\x16 \x01(\x0b\x32\x1a.orch.v1.HeartbeatResponseH\x00\x12\x36\n\rlist_monitors\x18\x17 \x01(\x0b\x32\x1d.orch.v1.ListMonitorsResponseH\x00\x12\x34\n\x0ckill_monitor\x18\x18 \x01(\x0b\x32\x1c.orch.v1.KillMonitorResponseH\x00\x12?\n\x13get_run_by_short_id\x18\x19 \x01(\x0b\x32 .orch.v1.GetRunByShortIDResponseH\x00\x12\x36\n\rresolve_issue\x18\x1a \x01(\x0b\x32\x1d.orch.v1.ResolveIssueResponseH\x00\x12\x34\n\x0c\x61ppend_event\x18\x1b \x01(\x0b\x32\x1c.orch.v1.AppendEventResponseH\x00\x12G\n\x16\x65nsure_opencode_server\x18\x1c \x01(\x0b\x32%.orch.v1.EnsureOpenCodeServerResponseH\x00\x12\x36\n\rregister_repo\x18\x1d \x01(\x0b\x32\x1d.orch.v1.RegisterRepoResponseH\x00\x12\x30\n\nlist_repos\x18\x1e \x01(\x0b\x32\x1a.orch.v1.ListReposResponseH\x00\x12\x30\n\ndelete_run\x18\x1f \x01(\x0b\x32\x1a.orch.v1.DeleteRunResponseH\x00\x12\x34\n\x0cupdate_issue\x18 \x01(\x0b\x32\x1c.orch.v1.UpdateIssueResponseH\x00\x12\x43\n\x14validate_issue_files\x18! \x01(\x0b\x32#.orch.v1.ValidateIssueFilesResponseH\x00\x12?\n\x12write_agent_prompt\x18\" \x01(\x0b\x32!.orch.v1.WriteAgentPromptResponseH\x00\x12=\n\x11read_agent_prompt\x18# \x01(\x0b\x32 .orch.v1.ReadAgentPromptResponseH\x00\x12\x34\n\x0crepair_state\x18$ \x01(\x0b\x32\x1c.orch.v1.RepairStateResponseH\x00\x12\x37\n\x0eget_daemon_log\x18% \x01(\x0b\x32\x1d.orch.v1.GetDaemonLogResponseH\x00\x12.\n\tread_file\x18& \x01(\x0b\x32\x19.orch.v1.ReadFileResponseH\x00\x12\x30\n\nwrite_file\x18\' \x01(\x0b\x32\x1a.orch.v1.WriteFileResponseH\x00\x12\x30\n\ncreate_run\x18( \x01(\x0b\x32\x1a.orch.v1.CreateRunResponseH\x00\x12\x34\n\x0ckill_session\x18) \x01(\x0b\x32\x1c.orch.v1.KillSessionResponseH\x00\x12\x36\n\rlist_sessions\x18* \x01(\x0b\x32\x1d.orch.v1.ListSessionsResponseH\x00\x12\x30\n\nresume_run\x18+ \x01(\x0b\x32\x1a.orch.v1.ResumeRunResponseH\x00\x12\x45\n\x15query_opencode_server\x18, \x01(\x0b\x32$.orch.v1.QueryOpenCodeServerResponseH\x00\x12\x45\n\x15inject_initial_prompt\x18- \x01(\x0b\x32$.orch.v1.InjectInitialPromptResponseH\x00\x12\x34\n\x0c\x63ontinue_run\x18. \x01(\x0b\x32\x1c.orch.v1.ContinueRunResponseH\x00\x12\x30\n\nget_config\x18/ \x01(\x0b\x32\x1a.orch.v1.GetConfigResponseH\x00\x12=\n\x11get_daemon_status\x18\x30 \x01(\x0b\x32 .orch.v1.GetDaemonStatusResponseH\x00\x42\n\n\x08response*\xff\x01\n\tRunStatus\x12\x1a\n\x16RUN_STATUS_UNSPECIFIED\x10\x00\x12\x15\n\x11RUN_STATUS_QUEUED\x10\x01\x12\x16\n\x12RUN_STATUS_BOOTING\x10\x02\x12\x16\n\x12RUN_STATUS_RUNNING\x10\x03\x12\x16\n\x12RUN_STATUS_BLOCKED\x10\x04\x12\x1a\n\x16RUN_STATUS_BLOCKED_API\x10\x05\x12\x16\n\x12RUN_STATUS_PR_OPEN\x10\x06\x12\x13\n\x0fRUN_STATUS_DONE\x10\x07\x12\x15\n\x11RUN_STATUS_FAILED\x10\x08\x12\x17\n\x13RUN_STATUS_CANCELED\x10\t*v\n\x0bIssueStatus\x12\x1c\n\x18ISSUE_STATUS_UNSPECIFIED\x10\x00\x12\x15\n\x11ISSUE_STATUS_OPEN\x10\x01\x12\x19\n\x15ISSUE_STATUS_RESOLVED\x10\x02\x12\x17\n\x13ISSUE_STATUS_CLOSED\x10\x03*\xf4\x01\n\x0b\x42ranchState\x12\x1c\n\x18\x42RANCH_STATE_UNSPECIFIED\x10\x00\x12\x16\n\x12\x42RANCH_STATE_CLEAN\x10\x01\x12\x16\n\x12\x42RANCH_STATE_DIRTY\x10\x02\x12\x17\n\x13\x42RANCH_STATE_MERGED\x10\x03\x12\x19\n\x15\x42RANCH_STATE_CONFLICT\x10\x04\x12\x16\n\x12\x42RANCH_STATE_AHEAD\x10\x05\x12\x17\n\x13\x42RANCH_STATE_BEHIND\x10\x06\x12\x19\n\x15\x42RANCH_STATE_DIVERGED\x10\x07\x12\x17\n\x13\x42RANCH_STATE_SYNCED\x10\x08*X\n\x0bMultiplexer\x12\x1b\n\x17MULTIPLEXER_UNSPECIFIED\x10\x00\x12\x14\n\x10MULTIPLEXER_TMUX\x10\x01\x12\x16\n\x12MULTIPLEXER_ZELLIJ\x10\x02\x42#Z!github.com/s22625/orch/api/orchpbb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\norch.proto\x12\x07orch.v1\"W\n\tDiffStats\x12\x11\n\tadditions\x18\x01 \x01(\x05\x12\x11\n\tdeletions\x18\x02 \x01(\x05\x12\x15\n\rfiles_changed\x18\x03 \x01(\x05\x12\r\n\x05\x66iles\x18\x04 \x03(\t\"\xf0\x04\n\x03Run\x12\x10\n\x08issue_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t\x12\"\n\x06status\x18\x03 \x01(\x0e\x32\x12.orch.v1.RunStatus\x12\r\n\x05\x61gent\x18\x04 \x01(\t\x12\r\n\x05model\x18\x05 \x01(\t\x12\x0e\n\x06\x62ranch\x18\x06 \x01(\t\x12\x15\n\rworktree_path\x18\x07 \x01(\t\x12\x0e\n\x06pr_url\x18\x08 \x01(\t\x12\x17\n\x0fstarted_at_unix\x18\t \x01(\x03\x12\x17\n\x0fupdated_at_unix\x18\n \x01(\x03\x12\x17\n\x0f\x65lapsed_seconds\x18\x0b \x01(\x05\x12\x17\n\x0f\x65lapsed_display\x18\x0c \x01(\t\x12&\n\ndiff_stats\x18\r \x01(\x0b\x32\x12.orch.v1.DiffStats\x12*\n\x0c\x62ranch_state\x18\x0e \x01(\x0e\x32\x14.orch.v1.BranchState\x12\x14\n\x0csession_name\x18\x0f \x01(\t\x12)\n\x0bmultiplexer\x18\x10 \x01(\x0e\x32\x14.orch.v1.Multiplexer\x12\x13\n\x0bserver_port\x18\x11 \x01(\x05\x12\x1b\n\x13opencode_session_id\x18\x12 \x01(\t\x12\x16\n\x0e\x63ontinued_from\x18\x13 \x01(\t\x12\x11\n\tpr_number\x18\x14 \x01(\x05\x12\x10\n\x08pr_state\x18\x15 \x01(\t\x12\x14\n\x0cissue_status\x18\x16 \x01(\t\x12\x13\n\x0bissue_topic\x18\x17 \x01(\t\x12\r\n\x05\x61live\x18\x18 \x01(\x08\x12\x13\n\x0b\x61live_known\x18\x19 \x01(\x08\x12\x17\n\x0fworktree_exists\x18\x1a \x01(\x08\"\xac\x01\n\x05Issue\x12\n\n\x02id\x18\x01 \x01(\t\x12\r\n\x05title\x18\x02 \x01(\t\x12\x0f\n\x07summary\x18\x03 \x01(\t\x12$\n\x06status\x18\x04 \x01(\x0e\x32\x14.orch.v1.IssueStatus\x12\x0c\n\x04tags\x18\x05 \x03(\t\x12\x0c\n\x04\x62ody\x18\x06 \x01(\t\x12\x0c\n\x04path\x18\x07 \x01(\t\x12\x18\n\x10modified_at_unix\x18\x08 \x01(\x03\x12\r\n\x05topic\x18\t \x01(\t\"\x93\x01\n\x05\x45vent\x12\x16\n\x0etimestamp_unix\x18\x01 \x01(\x03\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12(\n\x05\x61ttrs\x18\x04 \x03(\x0b\x32\x19.orch.v1.Event.AttrsEntry\x1a,\n\nAttrsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\r\n\x0bPingRequest\"+\n\x0cPingResponse\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x0f\n\x07version\x18\x02 \x01(\t\"\xc7\x01\n\x0fListRunsRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\"\n\x06status\x18\x03 \x03(\x0e\x32\x12.orch.v1.RunStatus\x12\r\n\x05\x61gent\x18\x04 \x01(\t\x12\x13\n\x0btext_search\x18\x05 \x01(\t\x12\x12\n\ntime_range\x18\x06 \x01(\t\x12\r\n\x05limit\x18\x07 \x01(\x05\x12\x0e\n\x06\x63ursor\x18\x08 \x01(\t\x12\x12\n\nolder_than\x18\t \x01(\t\"R\n\x10ListRunsResponse\x12\x1a\n\x04runs\x18\x01 \x03(\x0b\x32\x0c.orch.v1.Run\x12\r\n\x05total\x18\x02 \x01(\x05\x12\x13\n\x0bnext_cursor\x18\x03 \x01(\t\"F\n\rGetRunRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\"K\n\x0eGetRunResponse\x12\x19\n\x03run\x18\x01 \x01(\x0b\x32\x0c.orch.v1.Run\x12\x1e\n\x06\x65vents\x18\x02 \x03(\x0b\x32\x0e.orch.v1.Event\"\xff\x02\n\x0fStartRunRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\r\n\x05\x61gent\x18\x03 \x01(\t\x12\r\n\x05model\x18\x04 \x01(\t\x12\x15\n\rmodel_variant\x18\x05 \x01(\t\x12\x13\n\x0b\x62\x61se_branch\x18\x06 \x01(\t\x12\x14\n\x0cproject_root\x18\x07 \x01(\t\x12\x0e\n\x06preset\x18\x08 \x01(\t\x12\x0e\n\x06\x62ranch\x18\t \x01(\t\x12\x14\n\x0cworktree_dir\x18\n \x01(\t\x12\r\n\x05no_pr\x18\x0b \x01(\x08\x12\x17\n\x0fprompt_template\x18\x0c \x01(\t\x12\x18\n\x10pr_target_branch\x18\r \x01(\t\x12\x0f\n\x07\x64ry_run\x18\x0e \x01(\x08\x12\r\n\x05reuse\x18\x0f \x01(\x08\x12\x0e\n\x06run_id\x18\x10 \x01(\t\x12\x11\n\tagent_cmd\x18\x11 \x01(\t\x12\x15\n\ragent_profile\x18\x12 \x01(\t\x12\x13\n\x0bmultiplexer\x18\x13 \x01(\t\"o\n\x10StartRunResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12\x0e\n\x06\x62ranch\x18\x02 \x01(\t\x12\x15\n\rworktree_path\x18\x03 \x01(\t\x12\x14\n\x0csession_name\x18\x04 \x01(\t\x12\x0e\n\x06status\x18\x05 \x01(\t\"\xb5\x01\n\x10\x43reateRunRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x39\n\x08metadata\x18\x04 \x03(\x0b\x32\'.orch.v1.CreateRunRequest.MetadataEntry\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"C\n\x11\x43reateRunResponse\x12\x10\n\x08issue_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t\x12\x0c\n\x04path\x18\x03 \x01(\t\"G\n\x0eStopRunRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\"\x11\n\x0fStopRunResponse\"J\n\x11ResolveRunRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\"\x14\n\x12ResolveRunResponse\"\xa3\x01\n\x11ListIssuesRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12$\n\x06status\x18\x02 \x03(\x0e\x32\x14.orch.v1.IssueStatus\x12\x0c\n\x04tags\x18\x03 \x03(\t\x12\x11\n\ttags_mode\x18\x04 \x01(\t\x12\x13\n\x0btext_search\x18\x05 \x01(\t\x12\r\n\x05limit\x18\x06 \x01(\x05\x12\x0e\n\x06\x63ursor\x18\x07 \x01(\t\"X\n\x12ListIssuesResponse\x12\x1e\n\x06issues\x18\x01 \x03(\x0b\x32\x0e.orch.v1.Issue\x12\r\n\x05total\x18\x02 \x01(\x05\x12\x13\n\x0bnext_cursor\x18\x03 \x01(\t\"8\n\x0fGetIssueRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\"1\n\x10GetIssueResponse\x12\x1d\n\x05issue\x18\x01 \x01(\x0b\x32\x0e.orch.v1.Issue\"f\n\x12\x43reateIssueRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\r\n\x05title\x18\x03 \x01(\t\x12\x0c\n\x04\x62ody\x18\x04 \x01(\t\x12\x0c\n\x04tags\x18\x05 \x03(\t\"#\n\x13\x43reateIssueResponse\x12\x0c\n\x04path\x18\x01 \x01(\t\":\n\x11\x43loseIssueRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\"\x14\n\x12\x43loseIssueResponse\"X\n\x1cGetControlAgentLaunchRequest\x12\x14\n\x0cproject_root\x18\x01 \x01(\t\x12\r\n\x05\x61gent\x18\x02 \x01(\t\x12\x13\n\x0bnew_session\x18\x03 \x01(\x08\"g\n\x1dGetControlAgentLaunchResponse\x12\x0f\n\x07\x63ommand\x18\x01 \x01(\t\x12\x13\n\x0bprompt_file\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\x05\x12\x12\n\nsession_id\x18\x04 \x01(\t\"_\n\x14GetAttachInfoRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08short_id\x18\x04 \x01(\t\"\xe3\x01\n\x15GetAttachInfoResponse\x12\x0f\n\x07\x63ommand\x18\x01 \x03(\t\x12)\n\x0bmultiplexer\x18\x02 \x01(\x0e\x32\x14.orch.v1.Multiplexer\x12\x14\n\x0csession_name\x18\x03 \x01(\t\x12\x15\n\rworktree_path\x18\x04 \x01(\t\x12\r\n\x05\x61gent\x18\x05 \x01(\t\x12\x13\n\x0bserver_port\x18\x06 \x01(\x05\x12\x1b\n\x13opencode_session_id\x18\x07 \x01(\t\x12\x10\n\x08issue_id\x18\x08 \x01(\t\x12\x0e\n\x06run_id\x18\t \x01(\t\"N\n\x15\x43\x61ptureSessionRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\"Q\n\x16\x43\x61ptureSessionResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12\x16\n\x0etimestamp_unix\x18\x02 \x01(\x03\x12\x0e\n\x06source\x18\x03 \x01(\t\"\\\n\x12SendMessageRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x0f\n\x07message\x18\x04 \x01(\t\"\x15\n\x13SendMessageResponse\"L\n\x13GetDiffStatsRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\">\n\x14GetDiffStatsResponse\x12&\n\ndiff_stats\x18\x01 \x01(\x0b\x32\x12.orch.v1.DiffStats\"N\n\x15GetBranchStateRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\"=\n\x16GetBranchStateResponse\x12#\n\x05state\x18\x01 \x01(\x0e\x32\x14.orch.v1.BranchState\"G\n\x0eGetDiffRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\"\x1f\n\x0fGetDiffResponse\x12\x0c\n\x04\x64iff\x18\x01 \x01(\t\"p\n\x16RegisterMonitorRequest\x12\x0b\n\x03pid\x18\x01 \x01(\x05\x12\x14\n\x0cmonitor_type\x18\x02 \x01(\t\x12\x0c\n\x04view\x18\x03 \x01(\t\x12\x0f\n\x07project\x18\x04 \x01(\t\x12\x14\n\x0csession_name\x18\x05 \x01(\t\"-\n\x17RegisterMonitorResponse\x12\x12\n\nmonitor_id\x18\x01 \x01(\t\".\n\x18UnregisterMonitorRequest\x12\x12\n\nmonitor_id\x18\x01 \x01(\t\"\x1b\n\x19UnregisterMonitorResponse\"&\n\x10HeartbeatRequest\x12\x12\n\nmonitor_id\x18\x01 \x01(\t\"\x13\n\x11HeartbeatResponse\"3\n\x13ListMonitorsRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x0b\n\x03\x61ll\x18\x02 \x01(\x08\"\x9f\x01\n\x0bMonitorInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0b\n\x03pid\x18\x02 \x01(\x05\x12\x0c\n\x04type\x18\x03 \x01(\t\x12\x0c\n\x04view\x18\x04 \x01(\t\x12\x0f\n\x07project\x18\x05 \x01(\t\x12\x14\n\x0csession_name\x18\x06 \x01(\t\x12\x17\n\x0fstarted_at_unix\x18\x07 \x01(\x03\x12\x1b\n\x13last_heartbeat_unix\x18\x08 \x01(\x03\">\n\x14ListMonitorsResponse\x12&\n\x08monitors\x18\x01 \x03(\x0b\x32\x14.orch.v1.MonitorInfo\"V\n\x12KillMonitorRequest\x12\x12\n\nmonitor_id\x18\x01 \x01(\t\x12\x0b\n\x03\x61ll\x18\x02 \x01(\x08\x12\x0e\n\x06global\x18\x03 \x01(\x08\x12\x0f\n\x07project\x18\x04 \x01(\t\"+\n\x13KillMonitorResponse\x12\x14\n\x0ckilled_count\x18\x01 \x01(\x05\"?\n\x16GetRunByShortIDRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08short_id\x18\x02 \x01(\t\"T\n\x17GetRunByShortIDResponse\x12\x19\n\x03run\x18\x01 \x01(\x0b\x32\x0c.orch.v1.Run\x12\x1e\n\x06\x65vents\x18\x02 \x03(\x0b\x32\x0e.orch.v1.Event\"K\n\x13ResolveIssueRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\r\n\x05\x66orce\x18\x03 \x01(\x08\"(\n\x14ResolveIssueResponse\x12\x10\n\x08issue_id\x18\x01 \x01(\t\"\xfe\x01\n\x12\x41ppendEventRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x12\n\nevent_type\x18\x04 \x01(\t\x12\x12\n\nevent_name\x18\x05 \x01(\t\x12@\n\x0b\x65vent_attrs\x18\x06 \x03(\x0b\x32+.orch.v1.AppendEventRequest.EventAttrsEntry\x12\x14\n\x0c\x65vent_source\x18\x07 \x01(\t\x1a\x31\n\x0f\x45ventAttrsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"6\n\x13\x41ppendEventResponse\x12\x0f\n\x07skipped\x18\x01 \x01(\x08\x12\x0e\n\x06reason\x18\x02 \x01(\t\"3\n\x1b\x45nsureOpenCodeServerRequest\x12\x14\n\x0cproject_root\x18\x01 \x01(\t\"E\n\x1c\x45nsureOpenCodeServerResponse\x12\x0c\n\x04port\x18\x01 \x01(\x05\x12\x17\n\x0f\x61lready_running\x18\x02 \x01(\x08\"+\n\x13RegisterRepoRequest\x12\x14\n\x0cproject_root\x18\x01 \x01(\t\"\'\n\x14RegisterRepoResponse\x12\x0f\n\x07repo_id\x18\x01 \x01(\t\"\x12\n\x10ListReposRequest\"A\n\x08RepoInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0cproject_root\x18\x02 \x01(\t\x12\x13\n\x0bissues_root\x18\x03 \x01(\t\"5\n\x11ListReposResponse\x12 \n\x05repos\x18\x01 \x03(\x0b\x32\x11.orch.v1.RepoInfo\"\x96\x01\n\x10\x44\x65leteRunRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08short_id\x18\x04 \x01(\t\x12\x15\n\rwith_worktree\x18\x05 \x01(\x08\x12\x13\n\x0bwith_branch\x18\x06 \x01(\x08\x12\r\n\x05\x66orce\x18\x07 \x01(\x08\"\x91\x01\n\x11\x44\x65leteRunResponse\x12\x10\n\x08issue_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t\x12\x10\n\x08short_id\x18\x03 \x01(\t\x12\x18\n\x10worktree_removed\x18\x04 \x01(\x08\x12\x16\n\x0e\x62ranch_removed\x18\x05 \x01(\x08\x12\x16\n\x0esession_killed\x18\x06 \x01(\x08\"y\n\x12UpdateIssueRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\r\n\x05title\x18\x03 \x01(\t\x12\x0f\n\x07summary\x18\x04 \x01(\t\x12\x0c\n\x04\x62ody\x18\x05 \x01(\t\x12\x0e\n\x06status\x18\x06 \x01(\t\"4\n\x13UpdateIssueResponse\x12\x1d\n\x05issue\x18\x01 \x01(\x0b\x32\x0e.orch.v1.Issue\"B\n\x19ValidateIssueFilesRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\"M\n\x0fValidationIssue\x12\x0c\n\x04\x63ode\x18\x01 \x01(\t\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x0c\n\x04line\x18\x03 \x01(\x05\x12\r\n\x05level\x18\x04 \x01(\t\"\x8c\x01\n\x14ValidationResultItem\x12\x0c\n\x04\x66ile\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12(\n\x06\x65rrors\x18\x03 \x03(\x0b\x32\x18.orch.v1.ValidationIssue\x12*\n\x08warnings\x18\x04 \x03(\x0b\x32\x18.orch.v1.ValidationIssue\",\n\x0f\x44uplicateIDItem\x12\n\n\x02id\x18\x01 \x01(\t\x12\r\n\x05\x66iles\x18\x02 \x03(\t\"\xc8\x01\n\x1aValidateIssueFilesResponse\x12\r\n\x05total\x18\x01 \x01(\x05\x12\r\n\x05valid\x18\x02 \x01(\x05\x12-\n\x06\x65rrors\x18\x03 \x03(\x0b\x32\x1d.orch.v1.ValidationResultItem\x12/\n\x08warnings\x18\x04 \x03(\x0b\x32\x1d.orch.v1.ValidationResultItem\x12,\n\nduplicates\x18\x05 \x03(\x0b\x32\x18.orch.v1.DuplicateIDItem\"s\n\x17WriteAgentPromptRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08short_id\x18\x04 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x05 \x01(\t\"\x1a\n\x18WriteAgentPromptResponse\"a\n\x16ReadAgentPromptRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08short_id\x18\x04 \x01(\t\"*\n\x17ReadAgentPromptResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\"4\n\x12RepairStateRequest\x12\x0f\n\x07\x64ry_run\x18\x01 \x01(\x08\x12\r\n\x05\x66orce\x18\x02 \x01(\x08\"V\n\x13RepairStateResponse\x12\x16\n\x0eproblems_found\x18\x01 \x01(\x05\x12\x16\n\x0eproblems_fixed\x18\x02 \x01(\x05\x12\x0f\n\x07\x64\x65tails\x18\x03 \x03(\t\"$\n\x13GetDaemonLogRequest\x12\r\n\x05lines\x18\x01 \x01(\x05\"\'\n\x14GetDaemonLogResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\"\x1f\n\x0fReadFileRequest\x12\x0c\n\x04path\x18\x01 \x01(\t\"#\n\x10ReadFileResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\x0c\"?\n\x10WriteFileRequest\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\x0c\x12\x0c\n\x04perm\x18\x03 \x01(\r\"\x13\n\x11WriteFileResponse\"U\n\x12KillSessionRequest\x12\x14\n\x0csession_name\x18\x01 \x01(\t\x12)\n\x0bmultiplexer\x18\x02 \x01(\x0e\x32\x14.orch.v1.Multiplexer\"%\n\x13KillSessionResponse\x12\x0e\n\x06killed\x18\x01 \x01(\x08\"@\n\x13ListSessionsRequest\x12)\n\x0bmultiplexer\x18\x01 \x01(\x0e\x32\x14.orch.v1.Multiplexer\"(\n\x14ListSessionsResponse\x12\x10\n\x08sessions\x18\x01 \x03(\t\"[\n\x10ResumeRunRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08short_id\x18\x04 \x01(\t\")\n\x11ResumeRunResponse\x12\x14\n\x0csession_name\x18\x01 \x01(\t\"@\n\x1aQueryOpenCodeServerRequest\x12\x0c\n\x04port\x18\x01 \x01(\x05\x12\x14\n\x0cproject_root\x18\x02 \x01(\t\"\\\n\x14OpenCodeProviderInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12*\n\x06models\x18\x03 \x03(\x0b\x32\x1a.orch.v1.OpenCodeModelInfo\"?\n\x11OpenCodeModelInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x10\n\x08variants\x18\x03 \x03(\t\"\xee\x01\n\x1bQueryOpenCodeServerResponse\x12\x16\n\x0eserver_running\x18\x01 \x01(\x08\x12\x30\n\tproviders\x18\x02 \x03(\x0b\x32\x1d.orch.v1.OpenCodeProviderInfo\x12O\n\x0esession_status\x18\x03 \x03(\x0b\x32\x37.orch.v1.QueryOpenCodeServerResponse.SessionStatusEntry\x1a\x34\n\x12SessionStatusEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xa9\x01\n\x1aInjectInitialPromptRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x10\n\x08issue_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x0e\n\x06prompt\x18\x04 \x01(\t\x12\r\n\x05model\x18\x05 \x01(\t\x12\x15\n\rmodel_variant\x18\x06 \x01(\t\x12\x10\n\x08work_dir\x18\x07 \x01(\t\x12\x0c\n\x04port\x18\x08 \x01(\x05\"?\n\x1bInjectInitialPromptResponse\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12\x0c\n\x04port\x18\x02 \x01(\x05\"\xd2\x02\n\x12\x43ontinueRunRequest\x12\x13\n\x0bissues_root\x18\x01 \x01(\t\x12\x14\n\x0cproject_root\x18\x02 \x01(\t\x12\x10\n\x08issue_id\x18\x03 \x01(\t\x12\x0e\n\x06run_id\x18\x04 \x01(\t\x12\x10\n\x08short_id\x18\x05 \x01(\t\x12\x0e\n\x06\x62ranch\x18\x06 \x01(\t\x12\r\n\x05\x61gent\x18\x07 \x01(\t\x12\x11\n\tagent_cmd\x18\x08 \x01(\t\x12\x15\n\ragent_profile\x18\t \x01(\t\x12\x14\n\x0cworktree_dir\x18\n \x01(\t\x12\r\n\x05no_pr\x18\x0b \x01(\x08\x12\x17\n\x0fprompt_template\x18\x0c \x01(\t\x12\x18\n\x10pr_target_branch\x18\r \x01(\t\x12\x13\n\x0bmultiplexer\x18\x0e \x01(\t\x12\x14\n\x0csession_name\x18\x0f \x01(\t\x12\x11\n\trepo_root\x18\x10 \x01(\t\"\x9c\x01\n\x13\x43ontinueRunResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12\x0e\n\x06\x62ranch\x18\x02 \x01(\t\x12\x15\n\rworktree_path\x18\x03 \x01(\t\x12\x14\n\x0csession_name\x18\x04 \x01(\t\x12\x0e\n\x06status\x18\x05 \x01(\t\x12\x16\n\x0e\x63ontinued_from\x18\x06 \x01(\t\x12\x10\n\x08issue_id\x18\x07 \x01(\t\"(\n\x10GetConfigRequest\x12\x14\n\x0cproject_root\x18\x01 \x01(\t\"o\n\x10SlackConfigProto\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x13\n\x0bwebhook_url\x18\x02 \x01(\t\x12\x11\n\tbot_token\x18\x03 \x01(\t\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\x12\x11\n\tnotify_on\x18\x05 \x03(\t\"\x8e\x01\n\x13OpenCodeConfigProto\x12\x15\n\rdefault_model\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65\x66\x61ult_variant\x18\x02 \x01(\t\x12\x17\n\x0fprompt_template\x18\x03 \x01(\t\x12\x12\n\nextra_args\x18\x04 \x03(\t\x12\x1a\n\x12\x63ontrol_extra_args\x18\x05 \x03(\t\"\\\n\x11\x43laudeConfigProto\x12\x17\n\x0fprompt_template\x18\x01 \x01(\t\x12\x12\n\nextra_args\x18\x02 \x03(\t\x12\x1a\n\x12\x63ontrol_extra_args\x18\x03 \x03(\t\"[\n\x10\x43odexConfigProto\x12\x17\n\x0fprompt_template\x18\x01 \x01(\t\x12\x12\n\nextra_args\x18\x02 \x03(\t\x12\x1a\n\x12\x63ontrol_extra_args\x18\x03 \x03(\t\"\\\n\x11GeminiConfigProto\x12\x17\n\x0fprompt_template\x18\x01 \x01(\t\x12\x12\n\nextra_args\x18\x02 \x03(\t\x12\x1a\n\x12\x63ontrol_extra_args\x18\x03 \x03(\t\"]\n\x0bPresetProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x62\x61\x63kend\x18\x02 \x01(\t\x12\r\n\x05model\x18\x03 \x01(\t\x12\x0f\n\x07variant\x18\x04 \x01(\t\x12\x0f\n\x07profile\x18\x05 \x01(\t\"2\n\x11IssuesConfigProto\x12\x0f\n\x07\x62\x61\x63kend\x18\x01 \x01(\t\x12\x0c\n\x04path\x18\x02 \x01(\t\"\xd7\x01\n\x11GitHubConfigProto\x12\r\n\x05owner\x18\x01 \x01(\t\x12\x0c\n\x04repo\x18\x02 \x01(\t\x12\x14\n\x0clabel_filter\x18\x03 \x01(\t\x12\x15\n\rpoll_interval\x18\x04 \x01(\x05\x12\x43\n\rstatus_labels\x18\x05 \x03(\x0b\x32,.orch.v1.GitHubConfigProto.StatusLabelsEntry\x1a\x33\n\x11StatusLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"(\n\x12MonitorConfigProto\x12\x12\n\nps_columns\x18\x01 \x03(\t\"\x96\x06\n\x11GetConfigResponse\x12\r\n\x05\x61gent\x18\x01 \x01(\t\x12\r\n\x05model\x18\x02 \x01(\t\x12\x15\n\rmodel_variant\x18\x03 \x01(\t\x12\x14\n\x0cworktree_dir\x18\x04 \x01(\t\x12\x13\n\x0b\x62\x61se_branch\x18\x05 \x01(\t\x12\x18\n\x10pr_target_branch\x18\x06 \x01(\t\x12\x11\n\tlog_level\x18\x07 \x01(\t\x12\x17\n\x0fprompt_template\x18\x08 \x01(\t\x12\x13\n\x0bmultiplexer\x18\t \x01(\t\x12\x1b\n\x13monitor_multiplexer\x18\n \x01(\t\x12\x19\n\x11\x61gent_multiplexer\x18\x0b \x01(\t\x12\r\n\x05no_pr\x18\x0c \x01(\x08\x12\x16\n\x0e\x64\x65\x66\x61ult_preset\x18\r \x01(\t\x12\x15\n\rcontrol_agent\x18\x0e \x01(\t\x12\x15\n\rcontrol_model\x18\x0f \x01(\t\x12\x1d\n\x15\x63ontrol_model_variant\x18\x10 \x01(\t\x12\x11\n\tdiff_tool\x18\x11 \x01(\t\x12,\n\x07monitor\x18\x12 \x01(\x0b\x32\x1b.orch.v1.MonitorConfigProto\x12%\n\x07presets\x18\x13 \x03(\x0b\x32\x14.orch.v1.PresetProto\x12.\n\x08opencode\x18\x14 \x01(\x0b\x32\x1c.orch.v1.OpenCodeConfigProto\x12*\n\x06\x63laude\x18\x15 \x01(\x0b\x32\x1a.orch.v1.ClaudeConfigProto\x12(\n\x05\x63odex\x18\x16 \x01(\x0b\x32\x19.orch.v1.CodexConfigProto\x12*\n\x06gemini\x18\x17 \x01(\x0b\x32\x1a.orch.v1.GeminiConfigProto\x12(\n\x05slack\x18\x18 \x01(\x0b\x32\x19.orch.v1.SlackConfigProto\x12*\n\x06issues\x18\x19 \x01(\x0b\x32\x1a.orch.v1.IssuesConfigProto\x12*\n\x06github\x18\x1a \x01(\x0b\x32\x1a.orch.v1.GitHubConfigProto\"\x18\n\x16GetDaemonStatusRequest\"Z\n\x17GetDaemonStatusResponse\x12\x0f\n\x07running\x18\x01 \x01(\x08\x12\x0b\n\x03pid\x18\x02 \x01(\x05\x12\x10\n\x08log_path\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\t\"\xec\x13\n\x07Request\x12$\n\x04ping\x18\x01 \x01(\x0b\x32\x14.orch.v1.PingRequestH\x00\x12-\n\tlist_runs\x18\x02 \x01(\x0b\x32\x18.orch.v1.ListRunsRequestH\x00\x12)\n\x07get_run\x18\x03 \x01(\x0b\x32\x16.orch.v1.GetRunRequestH\x00\x12-\n\tstart_run\x18\x04 \x01(\x0b\x32\x18.orch.v1.StartRunRequestH\x00\x12+\n\x08stop_run\x18\x05 \x01(\x0b\x32\x17.orch.v1.StopRunRequestH\x00\x12\x31\n\x0bresolve_run\x18\x06 \x01(\x0b\x32\x1a.orch.v1.ResolveRunRequestH\x00\x12\x31\n\x0blist_issues\x18\x07 \x01(\x0b\x32\x1a.orch.v1.ListIssuesRequestH\x00\x12-\n\tget_issue\x18\x08 \x01(\x0b\x32\x18.orch.v1.GetIssueRequestH\x00\x12\x33\n\x0c\x63reate_issue\x18\t \x01(\x0b\x32\x1b.orch.v1.CreateIssueRequestH\x00\x12\x31\n\x0b\x63lose_issue\x18\n \x01(\x0b\x32\x1a.orch.v1.CloseIssueRequestH\x00\x12I\n\x18get_control_agent_launch\x18\x0b \x01(\x0b\x32%.orch.v1.GetControlAgentLaunchRequestH\x00\x12\x38\n\x0fget_attach_info\x18\x0c \x01(\x0b\x32\x1d.orch.v1.GetAttachInfoRequestH\x00\x12\x39\n\x0f\x63\x61pture_session\x18\r \x01(\x0b\x32\x1e.orch.v1.CaptureSessionRequestH\x00\x12\x33\n\x0csend_message\x18\x0e \x01(\x0b\x32\x1b.orch.v1.SendMessageRequestH\x00\x12\x36\n\x0eget_diff_stats\x18\x0f \x01(\x0b\x32\x1c.orch.v1.GetDiffStatsRequestH\x00\x12:\n\x10get_branch_state\x18\x10 \x01(\x0b\x32\x1e.orch.v1.GetBranchStateRequestH\x00\x12+\n\x08get_diff\x18\x11 \x01(\x0b\x32\x17.orch.v1.GetDiffRequestH\x00\x12;\n\x10register_monitor\x18\x12 \x01(\x0b\x32\x1f.orch.v1.RegisterMonitorRequestH\x00\x12?\n\x12unregister_monitor\x18\x13 \x01(\x0b\x32!.orch.v1.UnregisterMonitorRequestH\x00\x12.\n\theartbeat\x18\x14 \x01(\x0b\x32\x19.orch.v1.HeartbeatRequestH\x00\x12\x35\n\rlist_monitors\x18\x15 \x01(\x0b\x32\x1c.orch.v1.ListMonitorsRequestH\x00\x12\x33\n\x0ckill_monitor\x18\x16 \x01(\x0b\x32\x1b.orch.v1.KillMonitorRequestH\x00\x12>\n\x13get_run_by_short_id\x18\x17 \x01(\x0b\x32\x1f.orch.v1.GetRunByShortIDRequestH\x00\x12\x35\n\rresolve_issue\x18\x18 \x01(\x0b\x32\x1c.orch.v1.ResolveIssueRequestH\x00\x12\x33\n\x0c\x61ppend_event\x18\x19 \x01(\x0b\x32\x1b.orch.v1.AppendEventRequestH\x00\x12\x46\n\x16\x65nsure_opencode_server\x18\x1a \x01(\x0b\x32$.orch.v1.EnsureOpenCodeServerRequestH\x00\x12\x35\n\rregister_repo\x18\x1b \x01(\x0b\x32\x1c.orch.v1.RegisterRepoRequestH\x00\x12/\n\nlist_repos\x18\x1c \x01(\x0b\x32\x19.orch.v1.ListReposRequestH\x00\x12/\n\ndelete_run\x18\x1d \x01(\x0b\x32\x19.orch.v1.DeleteRunRequestH\x00\x12\x33\n\x0cupdate_issue\x18\x1e \x01(\x0b\x32\x1b.orch.v1.UpdateIssueRequestH\x00\x12\x42\n\x14validate_issue_files\x18\x1f \x01(\x0b\x32\".orch.v1.ValidateIssueFilesRequestH\x00\x12>\n\x12write_agent_prompt\x18 \x01(\x0b\x32 .orch.v1.WriteAgentPromptRequestH\x00\x12<\n\x11read_agent_prompt\x18! \x01(\x0b\x32\x1f.orch.v1.ReadAgentPromptRequestH\x00\x12\x33\n\x0crepair_state\x18\" \x01(\x0b\x32\x1b.orch.v1.RepairStateRequestH\x00\x12\x36\n\x0eget_daemon_log\x18# \x01(\x0b\x32\x1c.orch.v1.GetDaemonLogRequestH\x00\x12-\n\tread_file\x18$ \x01(\x0b\x32\x18.orch.v1.ReadFileRequestH\x00\x12/\n\nwrite_file\x18% \x01(\x0b\x32\x19.orch.v1.WriteFileRequestH\x00\x12/\n\ncreate_run\x18& \x01(\x0b\x32\x19.orch.v1.CreateRunRequestH\x00\x12\x33\n\x0ckill_session\x18\' \x01(\x0b\x32\x1b.orch.v1.KillSessionRequestH\x00\x12\x35\n\rlist_sessions\x18( \x01(\x0b\x32\x1c.orch.v1.ListSessionsRequestH\x00\x12/\n\nresume_run\x18) \x01(\x0b\x32\x19.orch.v1.ResumeRunRequestH\x00\x12\x44\n\x15query_opencode_server\x18* \x01(\x0b\x32#.orch.v1.QueryOpenCodeServerRequestH\x00\x12\x44\n\x15inject_initial_prompt\x18+ \x01(\x0b\x32#.orch.v1.InjectInitialPromptRequestH\x00\x12\x33\n\x0c\x63ontinue_run\x18, \x01(\x0b\x32\x1b.orch.v1.ContinueRunRequestH\x00\x12/\n\nget_config\x18- \x01(\x0b\x32\x19.orch.v1.GetConfigRequestH\x00\x12<\n\x11get_daemon_status\x18. \x01(\x0b\x32\x1f.orch.v1.GetDaemonStatusRequestH\x00\x42\t\n\x07request\"\xb7\x14\n\x08Response\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12%\n\x04ping\x18\x03 \x01(\x0b\x32\x15.orch.v1.PingResponseH\x00\x12.\n\tlist_runs\x18\x04 \x01(\x0b\x32\x19.orch.v1.ListRunsResponseH\x00\x12*\n\x07get_run\x18\x05 \x01(\x0b\x32\x17.orch.v1.GetRunResponseH\x00\x12.\n\tstart_run\x18\x06 \x01(\x0b\x32\x19.orch.v1.StartRunResponseH\x00\x12,\n\x08stop_run\x18\x07 \x01(\x0b\x32\x18.orch.v1.StopRunResponseH\x00\x12\x32\n\x0bresolve_run\x18\x08 \x01(\x0b\x32\x1b.orch.v1.ResolveRunResponseH\x00\x12\x32\n\x0blist_issues\x18\t \x01(\x0b\x32\x1b.orch.v1.ListIssuesResponseH\x00\x12.\n\tget_issue\x18\n \x01(\x0b\x32\x19.orch.v1.GetIssueResponseH\x00\x12\x34\n\x0c\x63reate_issue\x18\x0b \x01(\x0b\x32\x1c.orch.v1.CreateIssueResponseH\x00\x12\x32\n\x0b\x63lose_issue\x18\x0c \x01(\x0b\x32\x1b.orch.v1.CloseIssueResponseH\x00\x12J\n\x18get_control_agent_launch\x18\r \x01(\x0b\x32&.orch.v1.GetControlAgentLaunchResponseH\x00\x12\x39\n\x0fget_attach_info\x18\x0e \x01(\x0b\x32\x1e.orch.v1.GetAttachInfoResponseH\x00\x12:\n\x0f\x63\x61pture_session\x18\x0f \x01(\x0b\x32\x1f.orch.v1.CaptureSessionResponseH\x00\x12\x34\n\x0csend_message\x18\x10 \x01(\x0b\x32\x1c.orch.v1.SendMessageResponseH\x00\x12\x37\n\x0eget_diff_stats\x18\x11 \x01(\x0b\x32\x1d.orch.v1.GetDiffStatsResponseH\x00\x12;\n\x10get_branch_state\x18\x12 \x01(\x0b\x32\x1f.orch.v1.GetBranchStateResponseH\x00\x12,\n\x08get_diff\x18\x13 \x01(\x0b\x32\x18.orch.v1.GetDiffResponseH\x00\x12<\n\x10register_monitor\x18\x14 \x01(\x0b\x32 .orch.v1.RegisterMonitorResponseH\x00\x12@\n\x12unregister_monitor\x18\x15 \x01(\x0b\x32\".orch.v1.UnregisterMonitorResponseH\x00\x12/\n\theartbeat\x18\x16 \x01(\x0b\x32\x1a.orch.v1.HeartbeatResponseH\x00\x12\x36\n\rlist_monitors\x18\x17 \x01(\x0b\x32\x1d.orch.v1.ListMonitorsResponseH\x00\x12\x34\n\x0ckill_monitor\x18\x18 \x01(\x0b\x32\x1c.orch.v1.KillMonitorResponseH\x00\x12?\n\x13get_run_by_short_id\x18\x19 \x01(\x0b\x32 .orch.v1.GetRunByShortIDResponseH\x00\x12\x36\n\rresolve_issue\x18\x1a \x01(\x0b\x32\x1d.orch.v1.ResolveIssueResponseH\x00\x12\x34\n\x0c\x61ppend_event\x18\x1b \x01(\x0b\x32\x1c.orch.v1.AppendEventResponseH\x00\x12G\n\x16\x65nsure_opencode_server\x18\x1c \x01(\x0b\x32%.orch.v1.EnsureOpenCodeServerResponseH\x00\x12\x36\n\rregister_repo\x18\x1d \x01(\x0b\x32\x1d.orch.v1.RegisterRepoResponseH\x00\x12\x30\n\nlist_repos\x18\x1e \x01(\x0b\x32\x1a.orch.v1.ListReposResponseH\x00\x12\x30\n\ndelete_run\x18\x1f \x01(\x0b\x32\x1a.orch.v1.DeleteRunResponseH\x00\x12\x34\n\x0cupdate_issue\x18 \x01(\x0b\x32\x1c.orch.v1.UpdateIssueResponseH\x00\x12\x43\n\x14validate_issue_files\x18! \x01(\x0b\x32#.orch.v1.ValidateIssueFilesResponseH\x00\x12?\n\x12write_agent_prompt\x18\" \x01(\x0b\x32!.orch.v1.WriteAgentPromptResponseH\x00\x12=\n\x11read_agent_prompt\x18# \x01(\x0b\x32 .orch.v1.ReadAgentPromptResponseH\x00\x12\x34\n\x0crepair_state\x18$ \x01(\x0b\x32\x1c.orch.v1.RepairStateResponseH\x00\x12\x37\n\x0eget_daemon_log\x18% \x01(\x0b\x32\x1d.orch.v1.GetDaemonLogResponseH\x00\x12.\n\tread_file\x18& \x01(\x0b\x32\x19.orch.v1.ReadFileResponseH\x00\x12\x30\n\nwrite_file\x18\' \x01(\x0b\x32\x1a.orch.v1.WriteFileResponseH\x00\x12\x30\n\ncreate_run\x18( \x01(\x0b\x32\x1a.orch.v1.CreateRunResponseH\x00\x12\x34\n\x0ckill_session\x18) \x01(\x0b\x32\x1c.orch.v1.KillSessionResponseH\x00\x12\x36\n\rlist_sessions\x18* \x01(\x0b\x32\x1d.orch.v1.ListSessionsResponseH\x00\x12\x30\n\nresume_run\x18+ \x01(\x0b\x32\x1a.orch.v1.ResumeRunResponseH\x00\x12\x45\n\x15query_opencode_server\x18, \x01(\x0b\x32$.orch.v1.QueryOpenCodeServerResponseH\x00\x12\x45\n\x15inject_initial_prompt\x18- \x01(\x0b\x32$.orch.v1.InjectInitialPromptResponseH\x00\x12\x34\n\x0c\x63ontinue_run\x18. \x01(\x0b\x32\x1c.orch.v1.ContinueRunResponseH\x00\x12\x30\n\nget_config\x18/ \x01(\x0b\x32\x1a.orch.v1.GetConfigResponseH\x00\x12=\n\x11get_daemon_status\x18\x30 \x01(\x0b\x32 .orch.v1.GetDaemonStatusResponseH\x00\x42\n\n\x08response*\x80\x02\n\tRunStatus\x12\x1a\n\x16RUN_STATUS_UNSPECIFIED\x10\x00\x12\x15\n\x11RUN_STATUS_QUEUED\x10\x01\x12\x16\n\x12RUN_STATUS_BOOTING\x10\x02\x12\x16\n\x12RUN_STATUS_RUNNING\x10\x03\x12\x16\n\x12RUN_STATUS_WAITING\x10\x04\x12\x1b\n\x17RUN_STATUS_RATE_LIMITED\x10\x05\x12\x16\n\x12RUN_STATUS_PR_OPEN\x10\x06\x12\x13\n\x0fRUN_STATUS_DONE\x10\x07\x12\x15\n\x11RUN_STATUS_FAILED\x10\x08\x12\x17\n\x13RUN_STATUS_CANCELED\x10\t*v\n\x0bIssueStatus\x12\x1c\n\x18ISSUE_STATUS_UNSPECIFIED\x10\x00\x12\x15\n\x11ISSUE_STATUS_OPEN\x10\x01\x12\x19\n\x15ISSUE_STATUS_RESOLVED\x10\x02\x12\x17\n\x13ISSUE_STATUS_CLOSED\x10\x03*\xf4\x01\n\x0b\x42ranchState\x12\x1c\n\x18\x42RANCH_STATE_UNSPECIFIED\x10\x00\x12\x16\n\x12\x42RANCH_STATE_CLEAN\x10\x01\x12\x16\n\x12\x42RANCH_STATE_DIRTY\x10\x02\x12\x17\n\x13\x42RANCH_STATE_MERGED\x10\x03\x12\x19\n\x15\x42RANCH_STATE_CONFLICT\x10\x04\x12\x16\n\x12\x42RANCH_STATE_AHEAD\x10\x05\x12\x17\n\x13\x42RANCH_STATE_BEHIND\x10\x06\x12\x19\n\x15\x42RANCH_STATE_DIVERGED\x10\x07\x12\x17\n\x13\x42RANCH_STATE_SYNCED\x10\x08*X\n\x0bMultiplexer\x12\x1b\n\x17MULTIPLEXER_UNSPECIFIED\x10\x00\x12\x14\n\x10MULTIPLEXER_TMUX\x10\x01\x12\x16\n\x12MULTIPLEXER_ZELLIJ\x10\x02\x42#Z!github.com/s22625/orch/api/orchpbb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -43,13 +43,13 @@ _globals['_GITHUBCONFIGPROTO_STATUSLABELSENTRY']._loaded_options = None _globals['_GITHUBCONFIGPROTO_STATUSLABELSENTRY']._serialized_options = b'8\001' _globals['_RUNSTATUS']._serialized_start=16244 - _globals['_RUNSTATUS']._serialized_end=16499 - _globals['_ISSUESTATUS']._serialized_start=16501 - _globals['_ISSUESTATUS']._serialized_end=16619 - _globals['_BRANCHSTATE']._serialized_start=16622 - _globals['_BRANCHSTATE']._serialized_end=16866 - _globals['_MULTIPLEXER']._serialized_start=16868 - _globals['_MULTIPLEXER']._serialized_end=16956 + _globals['_RUNSTATUS']._serialized_end=16500 + _globals['_ISSUESTATUS']._serialized_start=16502 + _globals['_ISSUESTATUS']._serialized_end=16620 + _globals['_BRANCHSTATE']._serialized_start=16623 + _globals['_BRANCHSTATE']._serialized_end=16867 + _globals['_MULTIPLEXER']._serialized_start=16869 + _globals['_MULTIPLEXER']._serialized_end=16957 _globals['_DIFFSTATS']._serialized_start=23 _globals['_DIFFSTATS']._serialized_end=110 _globals['_RUN']._serialized_start=113 diff --git a/orch-monitor-tui/orch_monitor/api/orch_pb2.pyi b/orch-monitor-tui/orch_monitor/api/orch_pb2.pyi index 730be7d4..c437e516 100644 --- a/orch-monitor-tui/orch_monitor/api/orch_pb2.pyi +++ b/orch-monitor-tui/orch_monitor/api/orch_pb2.pyi @@ -13,8 +13,8 @@ class RunStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): RUN_STATUS_QUEUED: _ClassVar[RunStatus] RUN_STATUS_BOOTING: _ClassVar[RunStatus] RUN_STATUS_RUNNING: _ClassVar[RunStatus] - RUN_STATUS_BLOCKED: _ClassVar[RunStatus] - RUN_STATUS_BLOCKED_API: _ClassVar[RunStatus] + RUN_STATUS_WAITING: _ClassVar[RunStatus] + RUN_STATUS_RATE_LIMITED: _ClassVar[RunStatus] RUN_STATUS_PR_OPEN: _ClassVar[RunStatus] RUN_STATUS_DONE: _ClassVar[RunStatus] RUN_STATUS_FAILED: _ClassVar[RunStatus] @@ -48,8 +48,8 @@ RUN_STATUS_UNSPECIFIED: RunStatus RUN_STATUS_QUEUED: RunStatus RUN_STATUS_BOOTING: RunStatus RUN_STATUS_RUNNING: RunStatus -RUN_STATUS_BLOCKED: RunStatus -RUN_STATUS_BLOCKED_API: RunStatus +RUN_STATUS_WAITING: RunStatus +RUN_STATUS_RATE_LIMITED: RunStatus RUN_STATUS_PR_OPEN: RunStatus RUN_STATUS_DONE: RunStatus RUN_STATUS_FAILED: RunStatus diff --git a/orch-monitor-tui/orch_monitor/config.py b/orch-monitor-tui/orch_monitor/config.py index 2260bafd..acd3a500 100644 --- a/orch-monitor-tui/orch_monitor/config.py +++ b/orch-monitor-tui/orch_monitor/config.py @@ -34,8 +34,8 @@ def _log_config_error(operation: str, error: str, orch_dir: Optional[Path]) -> N "queued", "booting", "running", - "blocked", - "blocked_api", + "waiting", + "rate_limited", "pr_open", "failed", ] diff --git a/orch-monitor-tui/orch_monitor/converters.py b/orch-monitor-tui/orch_monitor/converters.py index 0a8ed86e..1963078d 100644 --- a/orch-monitor-tui/orch_monitor/converters.py +++ b/orch-monitor-tui/orch_monitor/converters.py @@ -14,8 +14,8 @@ def api_run_status_to_model(status: ApiRunStatus) -> Status: ApiRunStatus.QUEUED: Status.QUEUED, ApiRunStatus.BOOTING: Status.BOOTING, ApiRunStatus.RUNNING: Status.RUNNING, - ApiRunStatus.BLOCKED: Status.BLOCKED, - ApiRunStatus.BLOCKED_API: Status.BLOCKED_API, + ApiRunStatus.WAITING: Status.WAITING, + ApiRunStatus.RATE_LIMITED: Status.RATE_LIMITED, ApiRunStatus.PR_OPEN: Status.PR_OPEN, ApiRunStatus.DONE: Status.DONE, ApiRunStatus.FAILED: Status.FAILED, diff --git a/orch-monitor-tui/orch_monitor/daemon_api.py b/orch-monitor-tui/orch_monitor/daemon_api.py index d537608e..cd444958 100644 --- a/orch-monitor-tui/orch_monitor/daemon_api.py +++ b/orch-monitor-tui/orch_monitor/daemon_api.py @@ -96,8 +96,8 @@ def _model_status_to_api(status: ModelStatus) -> RunStatus: ModelStatus.QUEUED: RunStatus.QUEUED, ModelStatus.BOOTING: RunStatus.BOOTING, ModelStatus.RUNNING: RunStatus.RUNNING, - ModelStatus.BLOCKED: RunStatus.BLOCKED, - ModelStatus.BLOCKED_API: RunStatus.BLOCKED_API, + ModelStatus.WAITING: RunStatus.WAITING, + ModelStatus.RATE_LIMITED: RunStatus.RATE_LIMITED, ModelStatus.PR_OPEN: RunStatus.PR_OPEN, ModelStatus.DONE: RunStatus.DONE, ModelStatus.FAILED: RunStatus.FAILED, @@ -122,8 +122,8 @@ def _api_run_status_to_model(status: RunStatus) -> ModelStatus: RunStatus.QUEUED: ModelStatus.QUEUED, RunStatus.BOOTING: ModelStatus.BOOTING, RunStatus.RUNNING: ModelStatus.RUNNING, - RunStatus.BLOCKED: ModelStatus.BLOCKED, - RunStatus.BLOCKED_API: ModelStatus.BLOCKED_API, + RunStatus.WAITING: ModelStatus.WAITING, + RunStatus.RATE_LIMITED: ModelStatus.RATE_LIMITED, RunStatus.PR_OPEN: ModelStatus.PR_OPEN, RunStatus.DONE: ModelStatus.DONE, RunStatus.FAILED: ModelStatus.FAILED, diff --git a/orch-monitor-tui/orch_monitor/help_screen.hy b/orch-monitor-tui/orch_monitor/help_screen.hy index 25f1c6f2..04ad60ec 100644 --- a/orch-monitor-tui/orch_monitor/help_screen.hy +++ b/orch-monitor-tui/orch_monitor/help_screen.hy @@ -97,7 +97,7 @@ (yield (Label " queued -> Run waiting to start")) (yield (Label " booting -> Agent starting up")) (yield (Label " running -> Agent actively working")) - (yield (Label " blocked -> Agent needs input (attach!)")) + (yield (Label " waiting -> Agent needs input (attach!)")) (yield (Label " pr_open -> PR created, review it")) (yield (Label " done -> Work completed"))) diff --git a/orch-monitor-tui/orch_monitor/models.py b/orch-monitor-tui/orch_monitor/models.py index e2580991..9e188d22 100644 --- a/orch-monitor-tui/orch_monitor/models.py +++ b/orch-monitor-tui/orch_monitor/models.py @@ -17,8 +17,8 @@ class Status(str, Enum): QUEUED = "queued" BOOTING = "booting" RUNNING = "running" - BLOCKED = "blocked" - BLOCKED_API = "blocked_api" + WAITING = "waiting" + RATE_LIMITED = "rate_limited" PR_OPEN = "pr_open" DONE = "done" FAILED = "failed" @@ -162,8 +162,8 @@ def is_active(self) -> bool: Status.QUEUED, Status.BOOTING, Status.RUNNING, - Status.BLOCKED, - Status.BLOCKED_API, + Status.WAITING, + Status.RATE_LIMITED, } return self.status in active_states diff --git a/orch-monitor-tui/orch_monitor/orch_api.py b/orch-monitor-tui/orch_monitor/orch_api.py index 9637f241..368c56b4 100644 --- a/orch-monitor-tui/orch_monitor/orch_api.py +++ b/orch-monitor-tui/orch_monitor/orch_api.py @@ -25,8 +25,8 @@ class RunStatus(str, Enum): QUEUED = "queued" BOOTING = "booting" RUNNING = "running" - BLOCKED = "blocked" - BLOCKED_API = "blocked_api" + WAITING = "waiting" + RATE_LIMITED = "rate_limited" PR_OPEN = "pr_open" DONE = "done" FAILED = "failed" diff --git a/orch-monitor-tui/orch_monitor/proto_client.hy b/orch-monitor-tui/orch_monitor/proto_client.hy index ac3bf835..98877b94 100644 --- a/orch-monitor-tui/orch_monitor/proto_client.hy +++ b/orch-monitor-tui/orch_monitor/proto_client.hy @@ -49,8 +49,8 @@ (setv mapping {Status.QUEUED pb.RUN_STATUS_QUEUED Status.BOOTING pb.RUN_STATUS_BOOTING Status.RUNNING pb.RUN_STATUS_RUNNING - Status.BLOCKED pb.RUN_STATUS_BLOCKED - Status.BLOCKED_API pb.RUN_STATUS_BLOCKED_API + Status.BLOCKED pb.RUN_STATUS_WAITING + Status.BLOCKED_API pb.RUN_STATUS_RATE_LIMITED Status.PR_OPEN pb.RUN_STATUS_PR_OPEN Status.DONE pb.RUN_STATUS_DONE Status.FAILED pb.RUN_STATUS_FAILED @@ -61,8 +61,8 @@ (setv mapping {pb.RUN_STATUS_QUEUED Status.QUEUED pb.RUN_STATUS_BOOTING Status.BOOTING pb.RUN_STATUS_RUNNING Status.RUNNING - pb.RUN_STATUS_BLOCKED Status.BLOCKED - pb.RUN_STATUS_BLOCKED_API Status.BLOCKED_API + pb.RUN_STATUS_WAITING Status.BLOCKED + pb.RUN_STATUS_RATE_LIMITED Status.BLOCKED_API pb.RUN_STATUS_PR_OPEN Status.PR_OPEN pb.RUN_STATUS_DONE Status.DONE pb.RUN_STATUS_FAILED Status.FAILED diff --git a/orch-monitor-tui/orch_monitor/widgets.py b/orch-monitor-tui/orch_monitor/widgets.py index 4e056836..4d778963 100644 --- a/orch-monitor-tui/orch_monitor/widgets.py +++ b/orch-monitor-tui/orch_monitor/widgets.py @@ -187,8 +187,8 @@ def short_agent_status(status: Status) -> str: Status.QUEUED: "queue", Status.BOOTING: "boot", Status.RUNNING: "run", - Status.BLOCKED: "block", - Status.BLOCKED_API: "block", + Status.WAITING: "wait", + Status.RATE_LIMITED: "rlimit", Status.PR_OPEN: "pr", Status.DONE: "done", Status.FAILED: "fail", @@ -203,8 +203,8 @@ def color_agent_status(status: Status) -> str: colors = { Status.RUNNING: "green", Status.BOOTING: "green", - Status.BLOCKED: "yellow", - Status.BLOCKED_API: "yellow", + Status.WAITING: "yellow", + Status.RATE_LIMITED: "yellow", Status.FAILED: "red", Status.DONE: "blue", Status.PR_OPEN: "cyan", diff --git a/orch-monitor-tui/tests/conftest.py b/orch-monitor-tui/tests/conftest.py index e68518a3..e60638d9 100644 --- a/orch-monitor-tui/tests/conftest.py +++ b/orch-monitor-tui/tests/conftest.py @@ -93,7 +93,7 @@ def sample_runs() -> list[Run]: create_mock_run( issue_id="orch-124", run_id="20260115-110000", - status=Status.BLOCKED, + status=Status.WAITING, agent="opencode", ), create_mock_run( diff --git a/orch-monitor-tui/tests/test_proto_client.py b/orch-monitor-tui/tests/test_proto_client.py index 67b97d60..5d372b96 100644 --- a/orch-monitor-tui/tests/test_proto_client.py +++ b/orch-monitor-tui/tests/test_proto_client.py @@ -76,8 +76,8 @@ def test_all_known_statuses(self): assert _proto_status_to_model(pb.RUN_STATUS_QUEUED) == Status.QUEUED assert _proto_status_to_model(pb.RUN_STATUS_BOOTING) == Status.BOOTING assert _proto_status_to_model(pb.RUN_STATUS_RUNNING) == Status.RUNNING - assert _proto_status_to_model(pb.RUN_STATUS_BLOCKED) == Status.BLOCKED - assert _proto_status_to_model(pb.RUN_STATUS_BLOCKED_API) == Status.BLOCKED_API + assert _proto_status_to_model(pb.RUN_STATUS_WAITING) == Status.WAITING + assert _proto_status_to_model(pb.RUN_STATUS_RATE_LIMITED) == Status.RATE_LIMITED assert _proto_status_to_model(pb.RUN_STATUS_PR_OPEN) == Status.PR_OPEN assert _proto_status_to_model(pb.RUN_STATUS_DONE) == Status.DONE assert _proto_status_to_model(pb.RUN_STATUS_FAILED) == Status.FAILED diff --git a/orch-monitor-tui/tests/test_widgets.py b/orch-monitor-tui/tests/test_widgets.py index b8ddbb77..14ab89ba 100644 --- a/orch-monitor-tui/tests/test_widgets.py +++ b/orch-monitor-tui/tests/test_widgets.py @@ -152,13 +152,13 @@ def test_is_active_running(self): ) assert run.is_active() is True - def test_is_active_blocked(self): - """Test is_active for blocked status.""" + def test_is_active_waiting(self): + """Test is_active for waiting status.""" run = Run( issue_id="test", run_id="123", path=Path(), - status=Status.BLOCKED, + status=Status.WAITING, ) assert run.is_active() is True @@ -697,11 +697,11 @@ def test_booting(self): def test_running(self): assert short_agent_status(Status.RUNNING) == "run" - def test_blocked(self): - assert short_agent_status(Status.BLOCKED) == "block" + def test_waiting(self): + assert short_agent_status(Status.WAITING) == "wait" - def test_blocked_api(self): - assert short_agent_status(Status.BLOCKED_API) == "block" + def test_rate_limited(self): + assert short_agent_status(Status.RATE_LIMITED) == "rlimit" def test_pr_open(self): assert short_agent_status(Status.PR_OPEN) == "pr" @@ -725,10 +725,10 @@ def test_running_is_green(self): assert "[green]" in result assert "run" in result - def test_blocked_is_yellow(self): - result = color_agent_status(Status.BLOCKED) + def test_waiting_is_yellow(self): + result = color_agent_status(Status.WAITING) assert "[yellow]" in result - assert "block" in result + assert "wait" in result def test_failed_is_red(self): result = color_agent_status(Status.FAILED) @@ -1024,7 +1024,7 @@ def compose(self) -> ComposeResult: assert table.row_count > 0 assert short_agent_status(Status.RUNNING) == "run" - assert short_agent_status(Status.BLOCKED) == "block" + assert short_agent_status(Status.WAITING) == "wait" assert short_agent_status(Status.DONE) == "done" async def test_run_table_pr_column_derives_status_correctly(self): diff --git a/specs/00-overview.md b/specs/00-overview.md index 018bf7a8..6dce5c12 100644 --- a/specs/00-overview.md +++ b/specs/00-overview.md @@ -9,7 +9,7 @@ orch は「複数LLM CLI(claude/codex/gemini等)を、issue/run/event とい 1. **non-interactive がデフォルト** - 実行中に入力待ちはしない - - 人間の判断が必要なら question event を追記して終了(blocked) + - 人間の判断が必要なら question event を追記して終了(waiting) 2. **真実は append-only events** - 既存イベントを書き換えない diff --git a/specs/01-concepts.md b/specs/01-concepts.md index 5a5307ea..b4dbcc80 100644 --- a/specs/01-concepts.md +++ b/specs/01-concepts.md @@ -54,7 +54,7 @@ Runのステータス(eventsから派生): | queued | 作成直後、まだ起動していない | | booting | agent起動中 | | running | agent実行中 | -| blocked | 入力待ち(question未回答) | +| waiting | 入力待ち(question未回答) | | pr_open | PR作成済み、レビュー待ち | | done | 正常完了 | | failed | エラー終了 | diff --git a/specs/03-commands.md b/specs/03-commands.md index 0a2a4727..7605ea4c 100644 --- a/specs/03-commands.md +++ b/specs/03-commands.md @@ -35,7 +35,7 @@ | オプション | 説明 | |-----------|------| | `--new` | 常に新run(デフォルト) | -| `--reuse` | 最新runを再開(blocked向け) | +| `--reuse` | 最新runを再開(waiting向け) | | `--run-id ` | 手動指定 | | `--agent claude\|codex\|gemini\|custom:` | agent種別 | | `--agent-cmd` | custom時の起動コマンド | @@ -100,7 +100,7 @@ runs一覧を表示(人間/機械) | オプション | 説明 | |-----------|------| -| `--status` | queued,booting,running,blocked,blocked_api,pr_open,done,resolved,failed,canceled,unknown | +| `--status` | queued,booting,running,waiting,rate_limited,pr_open,done,resolved,failed,canceled,unknown | | `--issue-status` | open,closed,etc | | `--issue ` | 特定issueのrunのみ | | `--limit N` | default 50 | @@ -152,13 +152,13 @@ tmux attach(画像コピペ等の手動対話) ## orch tick RUN_REF | --all -blocked等のrunを再開するトリガ(質問が解消されていれば次フェーズを進める) +waiting等のrunを再開するトリガ(質問が解消されていれば次フェーズを進める) ### オプション | オプション | 説明 | |-----------|------| -| `--only-blocked` | default on when --all | +| `--only-waiting` | default on when --all | | `--agent …` | 再開時のagent指定 | | `--max N` | --all時の最大処理件数 | @@ -188,7 +188,7 @@ Obsidian/Editorで該当ノートを開く ### 挙動 -- ISSUE_ID のみ指定 → そのissueの全アクティブrun(running/booting/blocked/queued)を停止 +- ISSUE_ID のみ指定 → そのissueの全アクティブrun(running/booting/waiting/queued)を停止 - ISSUE_ID#RUN_ID 指定 → 特定runのみ停止 - --all → 全アクティブrunを停止 @@ -270,7 +270,7 @@ vault内の全issueを一覧表示 ``` ID STATUS TITLE RUNS plc-123 open Fix login timeout 1 running -plc-124 open Add dark mode 1 blocked, 1 done +plc-124 open Add dark mode 1 waiting, 1 done plc-125 closed Update documentation - ``` diff --git a/specs/04-daemon.md b/specs/04-daemon.md index e596756e..af26899e 100644 --- a/specs/04-daemon.md +++ b/specs/04-daemon.md @@ -13,7 +13,7 @@ orchは自動的にバックグラウンドdaemonを起動・管理する。ユ ## 監視ループ(5-10秒間隔) -各"running"/"blocked"/"unknown"状態のrunに対して: +各"running"/"waiting"/"unknown"状態のrunに対して: 1. tmuxセッション存在確認 2. capture-paneで最新出力を取得 @@ -27,7 +27,7 @@ claude-squad互換のロジック: 2. **完了パターン**: "task completed successfully" 等 → `done` 3. **エラーパターン**: "fatal error" 等 → `failed` 4. **Content変化あり**: 主要コンテンツが変化 → `running` -5. **Content安定 + Prompt検出**: 入力待ちパターン → `blocked` +5. **Content安定 + Prompt検出**: 入力待ちパターン → `waiting` 6. **その他**: 状態維持 ### Content変化検出 diff --git a/specs/06-events.md b/specs/06-events.md index 2292b992..b47637a8 100644 --- a/specs/06-events.md +++ b/specs/06-events.md @@ -21,7 +21,7 @@ Run状態の変更: | queued | 作成直後 | | booting | agent起動中 | | running | 実行中 | -| blocked | 入力待ち | +| waiting | 入力待ち | | pr_open | PR作成済み | | done | 正常完了 | | failed | エラー終了 | diff --git a/specs/08-monitor.md b/specs/08-monitor.md index a3575630..875c1788 100644 --- a/specs/08-monitor.md +++ b/specs/08-monitor.md @@ -37,11 +37,11 @@ All runs consolidated into one tmux session with: ┌─ ORCH MONITOR ──────────────────────────────────────────────────┐ │ │ │ # ID ISSUE STATUS AGO SUMMARY │ -│ 1 3f68c8 orch-008 blocked 5m Add issue status to ps │ -│ 2 f94c3e orch-009 blocked 3m Show elapsed time │ +│ 1 3f68c8 orch-008 waiting 5m Add issue status to ps │ +│ 2 f94c3e orch-009 waiting 3m Show elapsed time │ │ 3 43d956 orch-010 running 1m Add summary column to ps │ │ │ -│ ● running: 1 ◐ blocked: 2 ✓ done: 0 ✗ failed: 0 │ +│ ● running: 1 ◐ waiting: 2 ✓ done: 0 ✗ failed: 0 │ │ │ ├──────────────────────────────────────────────────────────────────┤ │ [1-9] attach [s] stop [n] new run [q] quit │ @@ -95,7 +95,7 @@ orch monitor --attach # Auto-attach to monitor if exists | Option | Description | |--------|-------------| | `--issue ` | Filter to specific issue | -| `--status ` | Filter by status (running,blocked) | +| `--status ` | Filter by status (running,waiting) | | `--attach` | Attach to existing monitor session | | `--new` | Force create new monitor (kill existing) | @@ -141,7 +141,7 @@ When switching to a run window, show context header: ``` ┌─ orch-008 ─────────────────────────────────────────────────────┐ │ Issue: Show issue status in orch ps output │ -│ Status: blocked (2 questions pending) │ +│ Status: waiting (2 questions pending) │ │ Branch: issue/orch-008/run-20251221-122336 │ │ Updated: 5 minutes ago │ ├─────────────────────────────────────────────────────────────────┤ @@ -154,7 +154,7 @@ When switching to a run window, show context header: ## Notifications Desktop notifications (optional) when: -- Run becomes blocked (needs input) +- Run becomes waiting (needs input) - Run completes (done/failed) - Run stalls (no output for N minutes) diff --git a/test/integration/integration_test.go b/test/integration/integration_test.go index 0454f4e3..043feb6c 100644 --- a/test/integration/integration_test.go +++ b/test/integration/integration_test.go @@ -1287,8 +1287,8 @@ func TestPsAgentStatusValues(t *testing.T) { {"queued", "queue"}, {"booting", "boot"}, {"running", "run"}, - {"blocked", "block"}, - {"blocked_api", "block"}, + {"waiting", "wait"}, + {"rate_limited", "rlimit"}, {"pr_open", "pr"}, {"done", "done"}, {"failed", "fail"}, diff --git a/vscode-orch/package.json b/vscode-orch/package.json index cac22d35..5d58a031 100644 --- a/vscode-orch/package.json +++ b/vscode-orch/package.json @@ -153,8 +153,8 @@ "queued", "booting", "running", - "blocked", - "blocked_api", + "waiting", + "rate_limited", "pr_open", "done", "failed", diff --git a/vscode-orch/src/providers/runsProvider.ts b/vscode-orch/src/providers/runsProvider.ts index a0297ba6..097fa4e9 100644 --- a/vscode-orch/src/providers/runsProvider.ts +++ b/vscode-orch/src/providers/runsProvider.ts @@ -70,6 +70,8 @@ function iconForStatus(status: string): vscode.ThemeIcon { switch (status) { case 'running': return new vscode.ThemeIcon('play-circle', new vscode.ThemeColor('charts.green')); + case 'waiting': + case 'rate_limited': case 'blocked': case 'blocked_api': return new vscode.ThemeIcon('warning', new vscode.ThemeColor('charts.yellow'));