delegate-agent-cli 0.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- delegate_agent/__init__.py +5 -0
- delegate_agent/archived_logs.py +44 -0
- delegate_agent/argv_builders.py +423 -0
- delegate_agent/argv_utils.py +36 -0
- delegate_agent/capability_commands.py +99 -0
- delegate_agent/cli.py +987 -0
- delegate_agent/cli_parser.py +1627 -0
- delegate_agent/command_errors.py +29 -0
- delegate_agent/command_help.py +1264 -0
- delegate_agent/config.py +1117 -0
- delegate_agent/config_commands.py +143 -0
- delegate_agent/constants.py +50 -0
- delegate_agent/describe_payload.py +1018 -0
- delegate_agent/errors.py +32 -0
- delegate_agent/git_utils.py +180 -0
- delegate_agent/harness_events.py +719 -0
- delegate_agent/inspection_commands.py +104 -0
- delegate_agent/isolation.py +316 -0
- delegate_agent/json_types.py +18 -0
- delegate_agent/log_output.py +78 -0
- delegate_agent/private_io.py +109 -0
- delegate_agent/profile_commands.py +42 -0
- delegate_agent/profile_guard.py +116 -0
- delegate_agent/profiles.py +389 -0
- delegate_agent/prompt_instructions.py +39 -0
- delegate_agent/prompt_transport.py +12 -0
- delegate_agent/reasoning.py +916 -0
- delegate_agent/redaction.py +193 -0
- delegate_agent/rendering.py +573 -0
- delegate_agent/request_build.py +1681 -0
- delegate_agent/request_models.py +191 -0
- delegate_agent/retention.py +321 -0
- delegate_agent/run_metadata.py +78 -0
- delegate_agent/run_output_commands.py +645 -0
- delegate_agent/run_registry.py +747 -0
- delegate_agent/run_status.py +300 -0
- delegate_agent/runner.py +1830 -0
- delegate_agent/safe_workspace.py +821 -0
- delegate_agent/snapshot_view.py +229 -0
- delegate_agent/wait_cancel_commands.py +559 -0
- delegate_agent/worktree_commands.py +218 -0
- delegate_agent/worktree_execution.py +654 -0
- delegate_agent/worktree_gc.py +529 -0
- delegate_agent/worktree_mgmt.py +782 -0
- delegate_agent/worktree_records.py +232 -0
- delegate_agent/worktree_remove.py +547 -0
- delegate_agent/worktree_summary.py +242 -0
- delegate_agent/wsl.py +65 -0
- delegate_agent_cli-0.11.0.dist-info/METADATA +325 -0
- delegate_agent_cli-0.11.0.dist-info/RECORD +54 -0
- delegate_agent_cli-0.11.0.dist-info/WHEEL +5 -0
- delegate_agent_cli-0.11.0.dist-info/entry_points.txt +2 -0
- delegate_agent_cli-0.11.0.dist-info/licenses/LICENSE +21 -0
- delegate_agent_cli-0.11.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"""Persistent worktree record model and extraction layer.
|
|
2
|
+
|
|
3
|
+
Builds ``PersistentWorktreeRecord`` values from the run registry's
|
|
4
|
+
state/manifest/snapshot/index triplets. This module is a leaf: it depends only
|
|
5
|
+
on ``run_registry`` and ``json_types`` so the status, remove, and gc pipelines
|
|
6
|
+
(and the ``worktree_mgmt`` facade) can import the record model and shared
|
|
7
|
+
constants without an import cycle.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import shlex
|
|
13
|
+
from datetime import UTC, datetime
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import TypedDict
|
|
16
|
+
|
|
17
|
+
from delegate_agent import run_registry
|
|
18
|
+
from delegate_agent.json_types import JsonObject, first_string
|
|
19
|
+
|
|
20
|
+
SCHEMA_LIST = "delegate.worktree-list.v1"
|
|
21
|
+
SCHEMA_SHOW = "delegate.worktree-show.v1"
|
|
22
|
+
SCHEMA_REMOVE = "delegate.worktree-remove.v1"
|
|
23
|
+
SCHEMA_PRUNE = "delegate.worktree-prune.v1"
|
|
24
|
+
SCHEMA_GC = "delegate.worktree-gc.v1"
|
|
25
|
+
WORKTREE_ERROR_EXIT_CODE = 2
|
|
26
|
+
MAX_DIRTY_PATHS_REPORTED = 20
|
|
27
|
+
|
|
28
|
+
STATUS_PRESENT = "present"
|
|
29
|
+
STATUS_REMOVED = "removed"
|
|
30
|
+
STATUS_MISSING = "missing"
|
|
31
|
+
STATUS_UNKNOWN = run_registry.STATUS_UNKNOWN
|
|
32
|
+
VALID_STATUSES = {STATUS_PRESENT, STATUS_REMOVED, STATUS_MISSING, STATUS_UNKNOWN}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _registered_worktree_path_matches(listed_paths: set[str], execution_cwd: str) -> bool:
|
|
36
|
+
if execution_cwd in listed_paths:
|
|
37
|
+
return True
|
|
38
|
+
try:
|
|
39
|
+
return str(Path(execution_cwd).resolve()) in listed_paths
|
|
40
|
+
except OSError:
|
|
41
|
+
return False
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class PersistentWorktreeRecord(TypedDict, total=False):
|
|
45
|
+
alias: str | None
|
|
46
|
+
runId: str
|
|
47
|
+
harness: str | None
|
|
48
|
+
branch: str | None
|
|
49
|
+
executionCwd: str | None
|
|
50
|
+
sourceGitRoot: str | None
|
|
51
|
+
createdAt: str
|
|
52
|
+
lastActivityAt: str
|
|
53
|
+
creationContext: JsonObject
|
|
54
|
+
registryWorktreeStatus: str | None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _utc_now_iso() -> str:
|
|
58
|
+
return datetime.now(UTC).strftime(run_registry.UTC_TIMESTAMP_FORMAT)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _shell(args: list[str]) -> str:
|
|
62
|
+
return shlex.join(args)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _get_str(source: object, key: str) -> str | None:
|
|
66
|
+
if not isinstance(source, dict):
|
|
67
|
+
return None
|
|
68
|
+
value = source.get(key)
|
|
69
|
+
return value if isinstance(value, str) and value else None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _get_dict(source: object, key: str) -> JsonObject:
|
|
73
|
+
if not isinstance(source, dict):
|
|
74
|
+
return {}
|
|
75
|
+
value = source.get(key)
|
|
76
|
+
return value if isinstance(value, dict) else {}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _creation_context(manifest: JsonObject | None, snapshot: JsonObject | None) -> JsonObject:
|
|
80
|
+
for source in (manifest, snapshot):
|
|
81
|
+
value = _get_dict(source, "creationContext")
|
|
82
|
+
if value:
|
|
83
|
+
return value
|
|
84
|
+
return {}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _branch_from(manifest: JsonObject | None, snapshot: JsonObject | None) -> str | None:
|
|
88
|
+
creation = _creation_context(manifest, snapshot)
|
|
89
|
+
return first_string(
|
|
90
|
+
_get_str(manifest, "branch"),
|
|
91
|
+
_get_str(snapshot, "branch"),
|
|
92
|
+
_get_str(creation, "branch"),
|
|
93
|
+
_get_str(creation, "plannedBranch"),
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _execution_cwd_from(
|
|
98
|
+
manifest: JsonObject | None,
|
|
99
|
+
snapshot: JsonObject | None,
|
|
100
|
+
state: JsonObject | None,
|
|
101
|
+
) -> str | None:
|
|
102
|
+
return first_string(
|
|
103
|
+
_get_str(manifest, "executionCwd"),
|
|
104
|
+
_get_str(snapshot, "executionCwd"),
|
|
105
|
+
_get_str(state, "plannedExecutionCwd"),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _source_git_root_from(manifest: JsonObject | None, snapshot: JsonObject | None) -> str | None:
|
|
110
|
+
return first_string(
|
|
111
|
+
_get_str(manifest, "sourceGitRoot"),
|
|
112
|
+
_get_str(snapshot, "sourceGitRoot"),
|
|
113
|
+
_get_str(manifest, "cwd"),
|
|
114
|
+
_get_str(snapshot, "cwd"),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _registry_worktree_status(
|
|
119
|
+
state: JsonObject | None,
|
|
120
|
+
manifest: JsonObject | None,
|
|
121
|
+
snapshot: JsonObject | None,
|
|
122
|
+
) -> str | None:
|
|
123
|
+
for source in (state, manifest, snapshot):
|
|
124
|
+
value = _get_str(source, "worktreeStatus")
|
|
125
|
+
if isinstance(value, str) and value in VALID_STATUSES:
|
|
126
|
+
return value
|
|
127
|
+
return None
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _is_persistent_worktree_run(
|
|
131
|
+
state: JsonObject | None,
|
|
132
|
+
manifest: JsonObject | None,
|
|
133
|
+
snapshot: JsonObject | None,
|
|
134
|
+
) -> bool:
|
|
135
|
+
for source in (state, manifest, snapshot):
|
|
136
|
+
if not isinstance(source, dict):
|
|
137
|
+
continue
|
|
138
|
+
if source.get("isolationLifecycle") == "persistent":
|
|
139
|
+
return True
|
|
140
|
+
if source.get("preservedWorkspace") is True:
|
|
141
|
+
return True
|
|
142
|
+
return _registry_worktree_status(state, manifest, snapshot) is not None
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _record_for_run(
|
|
146
|
+
registry_root: Path,
|
|
147
|
+
run_id: str,
|
|
148
|
+
index_entry: JsonObject | None,
|
|
149
|
+
) -> PersistentWorktreeRecord | None:
|
|
150
|
+
state = run_registry.load_run_state_or_none(registry_root, run_id)
|
|
151
|
+
manifest = run_registry.load_run_manifest_or_none(registry_root, run_id)
|
|
152
|
+
snapshot = run_registry.load_run_snapshot_or_none(registry_root, run_id)
|
|
153
|
+
if not _is_persistent_worktree_run(state, manifest, snapshot):
|
|
154
|
+
return None
|
|
155
|
+
entry = index_entry if isinstance(index_entry, dict) else {}
|
|
156
|
+
alias = first_string(
|
|
157
|
+
_get_str(state, "alias"),
|
|
158
|
+
_get_str(manifest, "alias"),
|
|
159
|
+
_get_str(snapshot, "alias"),
|
|
160
|
+
_get_str(entry, "alias"),
|
|
161
|
+
)
|
|
162
|
+
harness = first_string(
|
|
163
|
+
_get_str(entry, "harness"),
|
|
164
|
+
_get_str(manifest, "harness"),
|
|
165
|
+
_get_str(snapshot, "harness"),
|
|
166
|
+
)
|
|
167
|
+
creation = _creation_context(manifest, snapshot)
|
|
168
|
+
created_at = first_string(
|
|
169
|
+
_get_str(manifest, "startedAt"),
|
|
170
|
+
_get_str(snapshot, "startedAt"),
|
|
171
|
+
run_registry.timestamp_from_run_id(run_id),
|
|
172
|
+
)
|
|
173
|
+
last_activity = run_registry.activity_timestamp(state, manifest, run_id)
|
|
174
|
+
return {
|
|
175
|
+
"alias": alias,
|
|
176
|
+
"runId": run_id,
|
|
177
|
+
"harness": harness,
|
|
178
|
+
"group": first_string(
|
|
179
|
+
_get_str(entry, "group"),
|
|
180
|
+
_get_str(state, "group"),
|
|
181
|
+
_get_str(manifest, "group"),
|
|
182
|
+
_get_str(snapshot, "group"),
|
|
183
|
+
),
|
|
184
|
+
"branch": _branch_from(manifest, snapshot),
|
|
185
|
+
"executionCwd": _execution_cwd_from(manifest, snapshot, state),
|
|
186
|
+
"sourceGitRoot": _source_git_root_from(manifest, snapshot),
|
|
187
|
+
"createdAt": created_at,
|
|
188
|
+
"lastActivityAt": last_activity,
|
|
189
|
+
"creationContext": creation,
|
|
190
|
+
"registryWorktreeStatus": _registry_worktree_status(state, manifest, snapshot),
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def load_persistent_records(registry_root: Path) -> list[PersistentWorktreeRecord]:
|
|
195
|
+
index = run_registry.load_index(registry_root)
|
|
196
|
+
records: list[PersistentWorktreeRecord] = []
|
|
197
|
+
for run_id, entry in index.get("runs", {}).items():
|
|
198
|
+
if not isinstance(run_id, str) or not isinstance(entry, dict):
|
|
199
|
+
continue
|
|
200
|
+
record = _record_for_run(registry_root, run_id, entry)
|
|
201
|
+
if record is not None:
|
|
202
|
+
records.append(record)
|
|
203
|
+
return records
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def latest_persistent_record_for_harness(
|
|
207
|
+
registry_root: Path,
|
|
208
|
+
harness: str,
|
|
209
|
+
) -> PersistentWorktreeRecord | None:
|
|
210
|
+
matches = [
|
|
211
|
+
record
|
|
212
|
+
for record in load_persistent_records(registry_root)
|
|
213
|
+
if record.get("harness") == harness
|
|
214
|
+
]
|
|
215
|
+
if not matches:
|
|
216
|
+
return None
|
|
217
|
+
matches.sort(
|
|
218
|
+
key=lambda record: (
|
|
219
|
+
str(record.get("lastActivityAt") or ""),
|
|
220
|
+
str(record.get("runId") or ""),
|
|
221
|
+
),
|
|
222
|
+
reverse=True,
|
|
223
|
+
)
|
|
224
|
+
return matches[0]
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _reload_record(registry_root: Path, run_id: str) -> PersistentWorktreeRecord | None:
|
|
228
|
+
index = run_registry.load_index(registry_root)
|
|
229
|
+
index_entry = index.get("runs", {}).get(run_id)
|
|
230
|
+
return _record_for_run(
|
|
231
|
+
registry_root, run_id, index_entry if isinstance(index_entry, dict) else {}
|
|
232
|
+
)
|