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.
Files changed (54) hide show
  1. delegate_agent/__init__.py +5 -0
  2. delegate_agent/archived_logs.py +44 -0
  3. delegate_agent/argv_builders.py +423 -0
  4. delegate_agent/argv_utils.py +36 -0
  5. delegate_agent/capability_commands.py +99 -0
  6. delegate_agent/cli.py +987 -0
  7. delegate_agent/cli_parser.py +1627 -0
  8. delegate_agent/command_errors.py +29 -0
  9. delegate_agent/command_help.py +1264 -0
  10. delegate_agent/config.py +1117 -0
  11. delegate_agent/config_commands.py +143 -0
  12. delegate_agent/constants.py +50 -0
  13. delegate_agent/describe_payload.py +1018 -0
  14. delegate_agent/errors.py +32 -0
  15. delegate_agent/git_utils.py +180 -0
  16. delegate_agent/harness_events.py +719 -0
  17. delegate_agent/inspection_commands.py +104 -0
  18. delegate_agent/isolation.py +316 -0
  19. delegate_agent/json_types.py +18 -0
  20. delegate_agent/log_output.py +78 -0
  21. delegate_agent/private_io.py +109 -0
  22. delegate_agent/profile_commands.py +42 -0
  23. delegate_agent/profile_guard.py +116 -0
  24. delegate_agent/profiles.py +389 -0
  25. delegate_agent/prompt_instructions.py +39 -0
  26. delegate_agent/prompt_transport.py +12 -0
  27. delegate_agent/reasoning.py +916 -0
  28. delegate_agent/redaction.py +193 -0
  29. delegate_agent/rendering.py +573 -0
  30. delegate_agent/request_build.py +1681 -0
  31. delegate_agent/request_models.py +191 -0
  32. delegate_agent/retention.py +321 -0
  33. delegate_agent/run_metadata.py +78 -0
  34. delegate_agent/run_output_commands.py +645 -0
  35. delegate_agent/run_registry.py +747 -0
  36. delegate_agent/run_status.py +300 -0
  37. delegate_agent/runner.py +1830 -0
  38. delegate_agent/safe_workspace.py +821 -0
  39. delegate_agent/snapshot_view.py +229 -0
  40. delegate_agent/wait_cancel_commands.py +559 -0
  41. delegate_agent/worktree_commands.py +218 -0
  42. delegate_agent/worktree_execution.py +654 -0
  43. delegate_agent/worktree_gc.py +529 -0
  44. delegate_agent/worktree_mgmt.py +782 -0
  45. delegate_agent/worktree_records.py +232 -0
  46. delegate_agent/worktree_remove.py +547 -0
  47. delegate_agent/worktree_summary.py +242 -0
  48. delegate_agent/wsl.py +65 -0
  49. delegate_agent_cli-0.11.0.dist-info/METADATA +325 -0
  50. delegate_agent_cli-0.11.0.dist-info/RECORD +54 -0
  51. delegate_agent_cli-0.11.0.dist-info/WHEEL +5 -0
  52. delegate_agent_cli-0.11.0.dist-info/entry_points.txt +2 -0
  53. delegate_agent_cli-0.11.0.dist-info/licenses/LICENSE +21 -0
  54. 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
+ )