mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
fix(cli): /diff session reads the session's checkpoint policy and renders in full
/diff session on a rewired classic CLI went through slash.exec to the messaging /diff handler. That handler builds its CheckpointManager from the profile config. A `hermes --checkpoints` launch enables checkpoints only in the session's frozen policy, so with the default `checkpoints.enabled: false` it answered "not enabled" even after the session's tools had saved checkpoints. Its output was also chat text, cut to 60 lines / 3000 chars and fenced, where the terminal renderer shows 400 lines. The owner now serves rollback.session_diff next to rollback.list/diff/restore. It resolves the manager from the session's frozen checkpoint policy, as rollback already does, applies the same container-backend refusal, and returns main's session_diff result as data. The body is bounded to stay inside one frame, and diff_lines carries the full line count. The CLI hands that result to main's terminal renderer: the session-diff tail of _print_session_diff moves into print_session_diff, shared by the local and gateway paths, and _print_diff_body takes the sender's uncut line count for its "N more lines" note. The method is declared in CANONICAL_METHODS and the generated TS/OpenRPC contracts are regenerated. Refs NousResearch#134889
This commit is contained in:
1 parent
ecfcf103ad
commit
64f516c914
9 files changed
+254
-36
No files matched your search
@@ -4625,6 +4625,20 @@ export interface SessionStats {
|
||||
provider: string
|
||||
fallback_model?: string | null
|
||||
}
|
||||
export interface RollbackSessionDiffParams {
|
||||
session_id: string
|
||||
profile?: string | null
|
||||
}
|
||||
/** ``gateway/session_tui_ops.py::_session_diff``: ``CheckpointManager.session_diff`` under the session's frozen checkpoint policy (``enabled: false`` when that policy does not checkpoint). ``diff`` is bounded to fit one frame; ``diff_lines`` counts the whole body. */
|
||||
export interface RollbackSessionDiffResult {
|
||||
enabled: boolean
|
||||
success?: boolean
|
||||
stat?: string
|
||||
diff?: string
|
||||
diff_lines?: number
|
||||
empty?: boolean
|
||||
error?: string | null
|
||||
}
|
||||
export interface SessionAutomationParams {
|
||||
session_id: string
|
||||
kind: 'goal' | 'loop' | 'heartbeat'
|
||||
@@ -6670,6 +6684,8 @@ export interface CanonicalRpcMethods {
|
||||
/** Acknowledge a turn lost across an owner restart; the FIFO behind it resumes. Never requeues the lost input. */
|
||||
'prompt.resolve_unknown': { params: PromptResolveUnknownParams; result: AdmissionStatus }
|
||||
'prompt.submit': { params: CanonicalSubmitParams; result: AdmissionStatus }
|
||||
/** The classic CLI's /diff session: everything changed since the session's earliest retained checkpoint, as data for its terminal renderer. */
|
||||
'rollback.session_diff': { params: RollbackSessionDiffParams; result: RollbackSessionDiffResult }
|
||||
/** Owner identity and the canonical capabilities a client may rely on. */
|
||||
'runtime.describe': { params: RuntimeDescribeParams; result: RuntimeDescribeResult }
|
||||
/** The classic CLI's /goal, /subgoal, /loop, /heartbeat manager calls (state writes, the goal judge, quality gates), run by the owner on the session's row. */
|
||||
@@ -6725,6 +6741,7 @@ export const CANONICAL_RPC_METHODS = [
|
||||
'prompt.receipt',
|
||||
'prompt.resolve_unknown',
|
||||
'prompt.submit',
|
||||
'rollback.session_diff',
|
||||
'runtime.describe',
|
||||
'session.automation',
|
||||
'session.create',
|
||||
|
||||
@@ -32683,6 +32683,84 @@
|
||||
"title": "RollbackRestoreResult",
|
||||
"type": "object"
|
||||
},
|
||||
"RollbackSessionDiffParams": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"session_id": {
|
||||
"title": "Session Id",
|
||||
"type": "string"
|
||||
},
|
||||
"profile": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"title": "Profile"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"session_id"
|
||||
],
|
||||
"title": "RollbackSessionDiffParams",
|
||||
"type": "object"
|
||||
},
|
||||
"RollbackSessionDiffResult": {
|
||||
"additionalProperties": false,
|
||||
"description": "``gateway/session_tui_ops.py::_session_diff``: ``CheckpointManager.session_diff`` under the\nsession's frozen checkpoint policy (``enabled: false`` when that policy does not checkpoint).\n``diff`` is bounded to fit one frame; ``diff_lines`` counts the whole body.",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"title": "Enabled",
|
||||
"type": "boolean"
|
||||
},
|
||||
"success": {
|
||||
"default": false,
|
||||
"title": "Success",
|
||||
"type": "boolean"
|
||||
},
|
||||
"stat": {
|
||||
"default": "",
|
||||
"title": "Stat",
|
||||
"type": "string"
|
||||
},
|
||||
"diff": {
|
||||
"default": "",
|
||||
"title": "Diff",
|
||||
"type": "string"
|
||||
},
|
||||
"diff_lines": {
|
||||
"default": 0,
|
||||
"title": "Diff Lines",
|
||||
"type": "integer"
|
||||
},
|
||||
"empty": {
|
||||
"default": false,
|
||||
"title": "Empty",
|
||||
"type": "boolean"
|
||||
},
|
||||
"error": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"title": "Error"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"enabled"
|
||||
],
|
||||
"title": "RollbackSessionDiffResult",
|
||||
"type": "object"
|
||||
},
|
||||
"Room": {
|
||||
"additionalProperties": false,
|
||||
"description": "``gateway/hosted_rooms.py::_room_from_row`` plus the branch-only keys ``create`` (legacy\nadoption), ``state`` (``authority_claim``) and ``rename`` (``event``) add.",
|
||||
@@ -46666,6 +46744,24 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "rollback.session_diff",
|
||||
"summary": "The classic CLI's /diff session: everything changed since the session's earliest retained checkpoint, as data for its terminal renderer.",
|
||||
"params": [
|
||||
{
|
||||
"name": "params",
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/RollbackSessionDiffParams"
|
||||
}
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"name": "result",
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/RollbackSessionDiffResult"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "runtime.describe",
|
||||
"summary": "Owner identity and the canonical capabilities a client may rely on.",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Ink's session utilities on the shared owner: ``/save`` (``session.save``), ``/reload``
|
||||
(``reload.env``), ``/agents pause|resume`` (``delegation.pause``) and ``/rollback``
|
||||
(``rollback.list`` / ``rollback.diff`` / ``rollback.restore``).
|
||||
(``rollback.list`` / ``rollback.diff`` / ``rollback.restore``), plus the classic CLI's ``/diff session``
|
||||
(``rollback.session_diff``).
|
||||
|
||||
Main's per-TUI sidecar served these from its own session table, which never holds a shared-gateway
|
||||
session, so the canonical TUI refused them. Each verb reuses main's helpers rather than re-deriving
|
||||
@@ -26,6 +27,7 @@ def handlers(connection):
|
||||
'rollback.list': partial(rollback, connection, kind='list'),
|
||||
'rollback.diff': partial(rollback, connection, kind='diff'),
|
||||
'rollback.restore': partial(rollback, connection, kind='restore'),
|
||||
'rollback.session_diff': partial(rollback, connection, kind='session_diff'),
|
||||
'session.context_breakdown': partial(context_breakdown, connection)}
|
||||
|
||||
|
||||
@@ -140,11 +142,14 @@ async def pause_delegation(connection, ref, params):
|
||||
|
||||
|
||||
_ROLLBACK_FIELDS = {'list': {'session_id', 'profile'}, 'diff': {'session_id', 'profile', 'hash'},
|
||||
'restore': {'session_id', 'profile', 'hash', 'file_path'}}
|
||||
'restore': {'session_id', 'profile', 'hash', 'file_path'},
|
||||
'session_diff': {'session_id', 'profile'}}
|
||||
# ``rollback.session_diff`` body bound: well inside one 8 MiB frame (the classic CLI shows 400 lines).
|
||||
_SESSION_DIFF_CHARS = 2 * 1024 * 1024
|
||||
|
||||
|
||||
async def rollback(connection, ref, params, *, kind):
|
||||
"""``/rollback [list] | diff <n|hash> | <n|hash> [file]``: main's ``rollback.*`` over this
|
||||
"""``/rollback [list] | diff <n|hash> | <n|hash> [file]`` and ``/diff session``: main's ``rollback.*`` over this
|
||||
session's checkpoints (the shared ``<profile>/checkpoints`` store, keyed by the frozen launch cwd).
|
||||
|
||||
The manager is built from the session's frozen ``checkpoints`` policy rather than read off the
|
||||
@@ -154,7 +159,7 @@ async def rollback(connection, ref, params, *, kind):
|
||||
(main's ``_rewind_active_session_history``) through the owner's fenced ``rewind`` mutation."""
|
||||
fields = _ROLLBACK_FIELDS[kind]
|
||||
if any(key in params and (not isinstance(params[key], str) or (key == 'hash' and not params[key]))
|
||||
for key in ('hash', 'file_path')) or (kind != 'list' and 'hash' not in params):
|
||||
for key in ('hash', 'file_path')) or (kind in ('diff', 'restore') and 'hash' not in params):
|
||||
raise RuntimeStoreError('invalid_params')
|
||||
live = _live_ref(connection, ref, params, fields, 'session:read' if kind != 'restore' else 'session:control')
|
||||
authority = connection.authority
|
||||
@@ -172,15 +177,15 @@ async def rollback(connection, ref, params, *, kind):
|
||||
kwargs = _checkpoint_agent_kwargs(policy.config(authority, secrets=False))
|
||||
terminal = json.loads(policy.terminal_json)
|
||||
|
||||
def run():
|
||||
def run() -> dict:
|
||||
from tools.checkpoint_manager import CheckpointManager
|
||||
from tui_gateway.checkpoints import _resolve_checkpoint_hash
|
||||
mgr = CheckpointManager(enabled=bool(kwargs['checkpoints_enabled']), **{
|
||||
key[len('checkpoint_'):]: value for key, value in kwargs.items() if key.startswith('checkpoint_')})
|
||||
cwd = policy.cwd
|
||||
if not mgr.enabled and kind in ('list', 'session_diff'):
|
||||
return {'enabled': False, 'checkpoints': []} if kind == 'list' else {'enabled': False}
|
||||
if kind == 'list':
|
||||
if not mgr.enabled:
|
||||
return {'enabled': False, 'checkpoints': []}
|
||||
# The TUI renders ``message``; the manager calls it ``reason``.
|
||||
return {'enabled': True, 'checkpoints': [
|
||||
{'hash': c.get('hash', ''), 'timestamp': c.get('timestamp', ''), 'message': c.get('reason', '')}
|
||||
@@ -193,6 +198,8 @@ async def rollback(connection, ref, params, *, kind):
|
||||
refusal = mgr.unsupported_backend_reason(task_id)
|
||||
finally:
|
||||
reset_terminal_scope(token)
|
||||
if kind == 'session_diff':
|
||||
return _session_diff(mgr, cwd, refusal)
|
||||
try:
|
||||
target = _resolve_checkpoint_hash(mgr, cwd, params['hash'])
|
||||
except ValueError as exc:
|
||||
@@ -218,6 +225,20 @@ async def rollback(connection, ref, params, *, kind):
|
||||
return result
|
||||
|
||||
|
||||
def _session_diff(mgr, cwd, refusal):
|
||||
"""``/diff session``: main's ``session_diff`` (earliest retained checkpoint vs the tree) as data,
|
||||
so the classic CLI renders it with its own terminal renderer instead of chat-sized text."""
|
||||
if refusal:
|
||||
return {'enabled': True, 'success': False, 'error': refusal}
|
||||
result = mgr.session_diff(cwd)
|
||||
if not result.get('success'):
|
||||
return {'enabled': True, 'success': False, 'error': str(result.get('error') or '')}
|
||||
diff = result.get('diff', '')
|
||||
body = diff if len(diff) <= _SESSION_DIFF_CHARS else diff[:_SESSION_DIFF_CHARS].rpartition('\n')[0]
|
||||
return {'enabled': True, 'success': True, 'stat': result.get('stat', ''), 'diff': body,
|
||||
'diff_lines': len(diff.splitlines()), 'empty': bool(result.get('empty'))}
|
||||
|
||||
|
||||
async def _rewind_last_turn(connection, ref):
|
||||
"""Drop the newest user turn and everything after it (main's full ``/rollback`` restore) via the
|
||||
owner's revision-fenced ``rewind`` mutation; 0 when the transcript has no user turn."""
|
||||
|
||||
@@ -664,13 +664,13 @@ class CLICommandsMixin(CLICommandsSessionToolsMixin):
|
||||
if diff and not stat_only:
|
||||
self._print_diff_body(diff, _t("diff.stat_hint"))
|
||||
|
||||
def _print_diff_body(self, diff: str, stat_hint: str, limit: int = 400) -> None:
|
||||
"""Print a diff, capped at ``limit`` lines with a pointer to the --stat form."""
|
||||
def _print_diff_body(self, diff: str, stat_hint: str, limit: int = 400, total: int = 0) -> None:
|
||||
"""Print a diff, capped at ``limit`` lines (of ``total`` when its sender cut it) with a pointer to the --stat form."""
|
||||
print()
|
||||
diff_lines = diff.splitlines()
|
||||
if len(diff_lines) > limit:
|
||||
if (total := max(total, len(diff_lines))) > limit:
|
||||
self._print_diff_text("\n".join(diff_lines[:limit]))
|
||||
print(f"\n {_t('diff.more_lines', count=len(diff_lines) - limit, hint=stat_hint)}")
|
||||
print(f"\n {_t('diff.more_lines', count=total - limit, hint=stat_hint)}")
|
||||
else:
|
||||
self._print_diff_text(diff)
|
||||
|
||||
@@ -681,16 +681,8 @@ class CLICommandsMixin(CLICommandsSessionToolsMixin):
|
||||
return
|
||||
if reason := mgr.unsupported_backend_reason(): # host baseline is not this session's tree
|
||||
return print(f" {reason}")
|
||||
result = mgr.session_diff(cwd)
|
||||
if not result.get("success"):
|
||||
return print(f" {result.get('error') or _t('shared.could_not_generate_diff')}")
|
||||
stat, diff = result.get("stat", ""), result.get("diff", "")
|
||||
if result.get("empty") or (not stat and not diff):
|
||||
return print(f" {_t('diff.no_session_changes')}")
|
||||
if stat:
|
||||
self._print_diff_text(f"\n{stat}")
|
||||
if diff and not stat_only:
|
||||
self._print_diff_body(diff, _t("diff.stat_hint_session"))
|
||||
from hermes_cli.cli_commands_session_tools import print_session_diff
|
||||
print_session_diff(self, mgr.session_diff(cwd), stat_only)
|
||||
|
||||
def _print_diff_text(self, text: str) -> None:
|
||||
"""Render diff/stat text with color when a rich console is present; plain print otherwise
|
||||
|
||||
@@ -291,6 +291,20 @@ class CLICommandsSessionToolsMixin:
|
||||
f" {_t('profile.home', home=reply.data['home'])}")
|
||||
|
||||
|
||||
def print_session_diff(cli, result: dict, stat_only: bool) -> None:
|
||||
"""Render a ``CheckpointManager.session_diff`` result (``/diff session``): this process's agent's,
|
||||
or the gateway session's ``rollback.session_diff`` (``diff_lines``: its uncut body's line count)."""
|
||||
if not result.get("success"):
|
||||
return print(f" {result.get('error') or _t('shared.could_not_generate_diff')}")
|
||||
stat, diff = result.get("stat", ""), result.get("diff", "")
|
||||
if result.get("empty") or (not stat and not diff):
|
||||
return print(f" {_t('diff.no_session_changes')}")
|
||||
if stat:
|
||||
cli._print_diff_text(f"\n{stat}")
|
||||
if diff and not stat_only:
|
||||
cli._print_diff_body(diff, _t("diff.stat_hint_session"), total=int(result.get("diff_lines") or 0))
|
||||
|
||||
|
||||
def _print_local_delegations() -> None:
|
||||
"""This process's background (async) delegations — delegate_task(background=true)."""
|
||||
delegations = _probe("tools.async_delegation", "list_async_delegations", [])
|
||||
|
||||
@@ -4,9 +4,9 @@ Rewired, the session's tools, checkpoints, environment and handoff watcher run i
|
||||
main's handlers (which read this process's own agent and environment) answer nothing or the wrong
|
||||
thing:
|
||||
|
||||
- ``/diff session``: the checkpoint baseline is the gateway tools'; its ``/diff`` handler (a reviewed
|
||||
``slash.exec`` read, run in this session's cwd) renders it. Working-tree modes stay main's local
|
||||
git read of the same checkout.
|
||||
- ``/diff session``: the checkpoint baseline is the gateway tools'. ``rollback.session_diff`` reads it
|
||||
under the session's frozen checkpoint policy (``--checkpoints`` included) and returns it as data
|
||||
for main's terminal renderer. Working-tree modes stay main's local git read of the same checkout.
|
||||
- ``/reload``: ``reload.env`` re-reads ``.env`` where turns and tools run (this process re-reads too).
|
||||
- ``/handoff``: no gateway verb carries a handoff yet; refused as Ink and Desktop refuse it, instead
|
||||
of the store's ``request_handoff`` refusal dying silently in the process loop.
|
||||
@@ -18,15 +18,15 @@ from hermes_cli.gateway_client import GatewayClientError
|
||||
|
||||
|
||||
def _diff(cli, connection, cmd):
|
||||
args = cmd.partition(" ")[2].strip()
|
||||
if "session" not in args.lower().split():
|
||||
args = cmd.partition(" ")[2].strip().lower().split()
|
||||
if "session" not in args:
|
||||
return False
|
||||
from cli import _cprint
|
||||
reply = connection.call("slash.exec", session_id=cli.session_id, command=f"diff {args}")
|
||||
# The messaging handler fences its blocks for chat; a terminal shows the text itself.
|
||||
for line in str((reply or {}).get("output") or "").splitlines():
|
||||
if not line.startswith("```"):
|
||||
_cprint(f" {line}" if line else "")
|
||||
from hermes_cli.cli_commands_session_tools import _lines, _pr, _t, print_session_diff
|
||||
result = connection.call("rollback.session_diff", session_id=cli.session_id) or {}
|
||||
if not result.get("enabled"):
|
||||
_pr(*_lines(_t("diff.not_enabled")))
|
||||
else:
|
||||
print_session_diff(cli, result, stat_only=bool({"--stat", "stat"} & set(args)))
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
"""The classic CLI's ``/diff session`` on the shared owner (``rollback.session_diff``): the session's
|
||||
frozen checkpoint policy decides, so a ``--checkpoints`` launch on a profile whose config leaves
|
||||
checkpoints off still diffs, and the whole body comes back as data (no chat-sized cut)."""
|
||||
import asyncio
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.gateway.fixtures.local_recovery_probe import rpc, websocket
|
||||
from tests.gateway.test_session_busy_controls import owner
|
||||
from tests.gateway.test_session_tui_ops import _checkpoint, _git_rpc
|
||||
|
||||
|
||||
@pytest.mark.platforms("linux")
|
||||
def test_session_diff_follows_the_sessions_checkpoint_policy_and_keeps_the_whole_body(tmp_path):
|
||||
with owner(tmp_path) as (home, _peer, desc):
|
||||
work = tmp_path / 'work'
|
||||
work.mkdir()
|
||||
(work / 'big.txt').write_text(''.join(f'line {i}\n' for i in range(450)))
|
||||
|
||||
async def probe():
|
||||
async with websocket(home, desc) as ws:
|
||||
plain = (await rpc(ws, 'session.create', request_id='plain', source='cli', toolsets=[],
|
||||
cwd=str(work)))['result']['session_id']
|
||||
sid = (await rpc(ws, 'session.create', request_id='launch', source='cli', toolsets=[],
|
||||
cwd=str(work), checkpoints=True))['result']['session_id']
|
||||
await asyncio.to_thread(_checkpoint, home, work, 'before write_file')
|
||||
(work / 'big.txt').write_text(''.join(f'edited {i}\n' for i in range(450)))
|
||||
# The profile's config leaves checkpoints off: only the --checkpoints session diffs.
|
||||
off = await _git_rpc(ws, 'rollback.session_diff', session_id=plain)
|
||||
assert off.get('result', {}).get('enabled') is False, off
|
||||
reply = await _git_rpc(ws, 'rollback.session_diff', session_id=sid)
|
||||
assert 'result' in reply, reply
|
||||
result = reply['result']
|
||||
assert result['enabled'] is True and result['success'] is True, result
|
||||
assert 'big.txt' in result['stat'] and '+edited 449' in result['diff'], result['stat']
|
||||
assert result['diff_lines'] == len(result['diff'].splitlines()) > 900
|
||||
for params, reason in [({'session_id': sid, 'hash': '1'}, 'invalid_params'),
|
||||
({'session_id': sid, 'profile': 'foreign'}, 'profile_mismatch')]:
|
||||
assert (await rpc(ws, 'rollback.session_diff', **params))['error']['message'] == reason
|
||||
asyncio.run(probe())
|
||||
@@ -35,11 +35,25 @@ def gateway_cli(monkeypatch):
|
||||
|
||||
|
||||
def test_diff_session_renders_the_gateway_sessions_checkpoint_diff(gateway_cli, capsys):
|
||||
cli, conn = gateway_cli(**{"slash.exec": {"type": "exec", "output": "```\n a.py | 2 +-\n```"}})
|
||||
assert cli.process_command("/diff session --stat") is True
|
||||
assert conn.calls == [("slash.exec", {"session_id": "local-s", "command": "diff session --stat"})]
|
||||
"""The gateway session's checkpoint diff comes back as data and gets main's terminal renderer:
|
||||
400 body lines, not the chat handler's 60 lines / 3000 chars (Greptile 4277259989)."""
|
||||
body = "\n".join(f"+edited {i}" for i in range(450))
|
||||
cli, conn = gateway_cli(**{"rollback.session_diff": {
|
||||
"enabled": True, "success": True, "stat": " a.py | 450 +", "diff": body, "diff_lines": 450}})
|
||||
assert cli.process_command("/diff session") is True
|
||||
assert conn.calls == [("rollback.session_diff", {"session_id": "local-s"})]
|
||||
out = capsys.readouterr().out
|
||||
assert "a.py | 2 +-" in out and "```" not in out
|
||||
assert "a.py | 450 +" in out and "+edited 399" in out and "+edited 400" not in out
|
||||
assert "50 more lines" in out and "```" not in out
|
||||
cli.process_command("/diff session --stat")
|
||||
out = capsys.readouterr().out
|
||||
assert "a.py | 450 +" in out and "+edited 0" not in out
|
||||
|
||||
|
||||
def test_diff_session_reports_a_session_without_checkpoints(gateway_cli, capsys):
|
||||
cli, _conn = gateway_cli(**{"rollback.session_diff": {"enabled": False}})
|
||||
assert cli.process_command("/diff session") is True
|
||||
assert "Checkpoints are not enabled" in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_diff_of_the_working_tree_stays_a_local_git_read(gateway_cli, monkeypatch):
|
||||
|
||||
@@ -220,6 +220,30 @@ canonical_method("session.stats", params=SessionStatsParams, result=SessionStats
|
||||
doc="The session.usage dict on demand: what main's classic CLI read off its agent.")
|
||||
|
||||
|
||||
class RollbackSessionDiffParams(Params):
|
||||
session_id: str
|
||||
profile: str | None = None
|
||||
|
||||
|
||||
class RollbackSessionDiffResult(Result):
|
||||
"""``gateway/session_tui_ops.py::_session_diff``: ``CheckpointManager.session_diff`` under the
|
||||
session's frozen checkpoint policy (``enabled: false`` when that policy does not checkpoint).
|
||||
``diff`` is bounded to fit one frame; ``diff_lines`` counts the whole body."""
|
||||
|
||||
enabled: bool
|
||||
success: bool = False
|
||||
stat: str = ""
|
||||
diff: str = ""
|
||||
diff_lines: int = 0
|
||||
empty: bool = False
|
||||
error: str | None = None
|
||||
|
||||
|
||||
canonical_method("rollback.session_diff", params=RollbackSessionDiffParams, result=RollbackSessionDiffResult,
|
||||
doc="The classic CLI's /diff session: everything changed since the session's earliest "
|
||||
"retained checkpoint, as data for its terminal renderer.")
|
||||
|
||||
|
||||
class SessionAutomationParams(Params):
|
||||
session_id: str
|
||||
kind: Literal["goal", "loop", "heartbeat"]
|
||||
|
||||
Reference in new issue
Block a user