Skip to content
Merged
16 changes: 9 additions & 7 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ Options:
--set TEXT... Set metadata key value
--add-tag TEXT Add tags (comma-separated)
--plain Force non-interactive plain output
--no-daemon Run archive reads in-process instead of
using the local daemon.
-v, --verbose Verbose output
--diagnose Explain CLI parser decisions on stderr
before running. Useful when query-first
Expand All @@ -160,7 +162,7 @@ Commands:
analyze Analyze matched sessions and named facet families.
facets Show global or scoped archive facet families.
delete Delete matched sessions.
continue Compile a successor-agent continuation report.
continue Print or execute the harness command that resumes a session.
Use `find QUERY then ACTION`; `facets` is the direct archive aggregate
command.

Expand Down Expand Up @@ -474,16 +476,16 @@ Commands:
```text
Usage: polylogue continue [OPTIONS]

Compile a successor-agent continuation report for one matched session.
Print or explicitly execute a verified harness-resume command.

Examples:
polylogue find id:abc then continue
polylogue find id:abc then continue --format json
polylogue --latest continue --to clipboard
polylogue find 'repo:polylogue near:id:abc' then continue --to file --out handoff.md
polylogue find id:abc then continue --exec
polylogue continue --candidates --repo /workspace/polylogue --recent polylogue/cli/query_verbs.py

Options:
--exec Run the verified interactive resume command
instead of printing it.
--to [terminal|stdout|browser|clipboard|file]
Output destination. [default: terminal]
--out PATH File path for --to file.
Expand All @@ -498,8 +500,8 @@ Options:
--limit INTEGER Maximum continuation candidates to return.
[default: 10]
--json Shortcut for --format json.
-f, --format [json] Output format. JSON emits the shared
ContextImage payload.
-f, --format [json] Reserved for compatibility; continue emits a
shell command only.
--help Show this message and exit.
```

Expand Down
7 changes: 7 additions & 0 deletions docs/openapi/search.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3080,6 +3080,13 @@ x-polylogue-route-contracts:
stability: stable
auth_policy: credential_if_configured
response_contract: SearchEnvelope / SessionListResponse with route_state
- method: POST
pattern: /api/cli/query
kind: read_query
stability: private
auth_policy: credential_if_configured
response_contract: SearchEnvelope / SessionListResponse with route_state
notes: Local UDS-only root-request parameter envelope; daemon owns query compilation.
- method: GET
pattern: /api/facets
kind: read_query
Expand Down
18 changes: 15 additions & 3 deletions docs/plans/topology-target.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,10 @@ files:
target: polylogue/archive/raw_payload/streams.py
owner: archive-raw-payload
reason: archive-domain semantics
- path: polylogue/archive/resume_routing.py
loc: 77
target: polylogue/archive/resume_routing.py
owner: stable
- path: polylogue/archive/revision_authority.py
loc: 158
target: polylogue/archive/revision_authority.py
Expand Down Expand Up @@ -861,6 +865,10 @@ files:
loc: 82
target: polylogue/cli/convergence_feedback.py
owner: stable
- path: polylogue/cli/daemon_client.py
loc: 62
target: polylogue/cli/daemon_client.py
owner: stable
- path: polylogue/cli/help_markdown.py
loc: 59
target: polylogue/cli/help_markdown.py
Expand Down Expand Up @@ -922,7 +930,7 @@ files:
target: polylogue/cli/query_stats.py
owner: stable
- path: polylogue/cli/query_verbs.py
loc: 2691
loc: 2720
target: polylogue/cli/query_verbs.py
owner: stable
- path: polylogue/cli/read_view_handlers.py
Expand Down Expand Up @@ -1287,7 +1295,7 @@ files:
target: polylogue/daemon/catchup_status.py
owner: stable
- path: polylogue/daemon/cli.py
loc: 1919
loc: 1936
target: polylogue/daemon/cli.py
owner: stable
- path: polylogue/daemon/compare.py
Expand Down Expand Up @@ -1363,7 +1371,7 @@ files:
target: polylogue/daemon/healthz.py
owner: stable
- path: polylogue/daemon/http.py
loc: 4545
loc: 4553
target: polylogue/daemon/http.py
owner: stable
- path: polylogue/daemon/lifecycle.py
Expand Down Expand Up @@ -1455,6 +1463,10 @@ files:
loc: 236
target: polylogue/daemon/topology_http.py
owner: stable
- path: polylogue/daemon/uds.py
loc: 71
target: polylogue/daemon/uds.py
owner: stable
- path: polylogue/daemon/user_state_http.py
loc: 636
target: polylogue/daemon/user_state_http.py
Expand Down
6 changes: 3 additions & 3 deletions docs/topology-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ Generated by `devtools render topology-status`. Reads `docs/plans/topology-targe

### Summary

- **Stable** (no move scoped): 774
- **Stable** (no move scoped): 777
- **Kernel** (polylogue/ root): 10
- **Primitives** (storage-root): 18
- **TBD** (cell needs explicit assignment): 6
- **Total declared**: 927
- **Realized polylogue/**/*.py**: 927 files declared
- **Total declared**: 930
- **Realized polylogue/**/*.py**: 930 files declared

### TBD cells (require explicit routing)

Expand Down
77 changes: 77 additions & 0 deletions polylogue/archive/resume_routing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""Safe, explicit harness-resume commands for archived local sessions.

Only origins with a documented native resume contract receive a command. The
route is deliberately a value object: callers can print it for a human or
execute it behind an explicit opt-in without guessing for unsupported exports.
"""

from __future__ import annotations

import shlex
from dataclasses import dataclass
from typing import Literal

from polylogue.archive.session.domain_models import Session
from polylogue.core.enums import Origin
from polylogue.core.web_urls import native_id_from_session_id

ResumeRouteStatus = Literal["supported", "unsupported"]


@dataclass(frozen=True, slots=True)
class ResumeRoute:
"""One resolved resume route, safe to render or execute verbatim."""

status: ResumeRouteStatus
origin: str
native_session_id: str
argv: tuple[str, ...] = ()
cwd: str | None = None
detail: str | None = None

@property
def command(self) -> str | None:
"""Shell-safe interactive command, prefixed with its known cwd."""

if not self.argv:
return None
command = shlex.join(self.argv)
return f"cd {shlex.quote(self.cwd)} && {command}" if self.cwd else command


def route_resume(session: Session) -> ResumeRoute:
"""Map a local harness session to its interactive resume command.

``codex exec resume`` is intentionally not selected here: ``continue`` is
human-facing and must reopen the interactive TUI. It remains the correct
headless alternative for automation.
"""

origin = str(session.origin)
native_id = native_id_from_session_id(session.id)
cwd = next((path for path in session.working_directories if path), None)
if native_id is None:
return ResumeRoute(
status="unsupported",
origin=origin,
native_session_id="",
cwd=cwd,
detail="The archive session id has no native harness session id.",
)
commands: dict[Origin, tuple[str, ...]] = {
Origin.CLAUDE_CODE_SESSION: ("claude", "--resume", native_id),
Origin.CODEX_SESSION: ("codex", "resume", native_id),
}
argv = commands.get(session.origin)
if argv is None:
return ResumeRoute(
status="unsupported",
origin=origin,
native_session_id=native_id,
cwd=cwd,
detail=f"No verified interactive resume command for origin {origin!r}.",
)
return ResumeRoute(status="supported", origin=origin, native_session_id=native_id, argv=argv, cwd=cwd)


__all__ = ["ResumeRoute", "ResumeRouteStatus", "route_resume"]
Loading
Loading