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,193 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from collections.abc import Mapping
|
|
5
|
+
|
|
6
|
+
from delegate_agent.json_types import JsonValue
|
|
7
|
+
|
|
8
|
+
SLACK_WEBHOOK_SCHEME = "https"
|
|
9
|
+
URL_SCHEME_SEPARATOR = "://"
|
|
10
|
+
SLACK_WEBHOOK_HOST_PATTERN = r"hooks\.slack(?:-gov)?\.com"
|
|
11
|
+
SLACK_WEBHOOK_PATH_PATTERN = r"/services/T[A-Z0-9]+/B[A-Z0-9]+/[A-Za-z0-9]+"
|
|
12
|
+
|
|
13
|
+
REDACT_PATTERNS: list[tuple[re.Pattern[str], str]] = [
|
|
14
|
+
# Authorization header value, quoted or bare. The optional quote after the key
|
|
15
|
+
# tolerates JSON ({"Authorization": "..."}); the value is bounded on the right
|
|
16
|
+
# so we don't swallow trailing structure (closing quote/brace/&/,).
|
|
17
|
+
(
|
|
18
|
+
re.compile(
|
|
19
|
+
r"(?i)\b(authorization[\"']?\s*[:=]\s*)"
|
|
20
|
+
r"(?:\"[^\"\r\n]*\"|'[^'\r\n]*'|[^\s,&}\"'\r\n][^\r\n,&}]*)"
|
|
21
|
+
),
|
|
22
|
+
r"\1***",
|
|
23
|
+
),
|
|
24
|
+
# Bare scheme token not behind an Authorization key (e.g. "Bearer eyJ...").
|
|
25
|
+
(
|
|
26
|
+
re.compile(r"(?i)\b((?:bearer|basic)\s+)[A-Za-z0-9._~+/=-]{8,}"),
|
|
27
|
+
r"\1***",
|
|
28
|
+
),
|
|
29
|
+
# Bracketed environment assignments, e.g. os.environ["OPENAI_API_KEY"] = "..."
|
|
30
|
+
# and env['DB_PASSWORD']='...'. Keep this before the generic key matcher: the
|
|
31
|
+
# separator between the secret key and value is outside the bracketed lookup.
|
|
32
|
+
(
|
|
33
|
+
re.compile(
|
|
34
|
+
r"(?i)\b((?:os\.environ|env)\[\s*[\"'][^\"'\]\r\n]*"
|
|
35
|
+
r"(?:api[_-]?key|apikey|access[_-]?key|secret[_-]?key|private[_-]?key|"
|
|
36
|
+
r"access[_-]?token|refresh[_-]?token|auth[_-]?token|authtoken|"
|
|
37
|
+
r"client[_-]?secret|password|passwd|secret|token)"
|
|
38
|
+
r"[^\"'\]\r\n]*[\"']\s*\]\s*=\s*)"
|
|
39
|
+
r"(?:\"[^\"\r\n]*\"|'[^'\r\n]*'|[^\s,&};\"'\r\n][^\r\n,&};]*)"
|
|
40
|
+
),
|
|
41
|
+
r"\1***",
|
|
42
|
+
),
|
|
43
|
+
# Named credential keys with the value quoted, bare, or JSON-quoted. The left
|
|
44
|
+
# edge anchors on a non-alphanumeric character (or string start) rather than
|
|
45
|
+
# \b, so env-style prefixes joined by "_" still redact (OPENAI_API_KEY=,
|
|
46
|
+
# DB_PASSWORD=, aws_secret_access_key=). Separator is preserved so the shape
|
|
47
|
+
# stays readable.
|
|
48
|
+
(
|
|
49
|
+
re.compile(
|
|
50
|
+
r"(?i)(?:(?<=[^A-Za-z0-9])|^)("
|
|
51
|
+
r"api[_-]?key|apikey|access[_-]?key|secret[_-]?key|private[_-]?key|"
|
|
52
|
+
r"access[_-]?token|refresh[_-]?token|auth[_-]?token|authtoken|"
|
|
53
|
+
r"client[_-]?secret|password|passwd|secret|token"
|
|
54
|
+
r")([\"']?\s*[:=]\s*)"
|
|
55
|
+
r"(?:\"[^\"\r\n]*\"|'[^'\r\n]*'|[^\s,&};\"'\r\n][^\r\n,&};]*)"
|
|
56
|
+
),
|
|
57
|
+
r"\1\2***",
|
|
58
|
+
),
|
|
59
|
+
# Password embedded in a connection string: scheme://[user]:PASS@host. The
|
|
60
|
+
# scheme length is bounded so a long dotted string cannot backtrack quadratically.
|
|
61
|
+
(
|
|
62
|
+
re.compile(r"(?i)\b([a-z][a-z0-9+.-]{1,40}://[^\s:/@]*:)[^\s:/@]+(@)"),
|
|
63
|
+
r"\1***\2",
|
|
64
|
+
),
|
|
65
|
+
# JWTs are anchored on the eyJ header (base64url of '{"') so this does not shred
|
|
66
|
+
# ordinary dotted identifiers and tracebacks the parent agent needs to read.
|
|
67
|
+
(
|
|
68
|
+
re.compile(r"\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b"),
|
|
69
|
+
"***",
|
|
70
|
+
),
|
|
71
|
+
# PEM private key blocks are handled by _redact_pem_blocks() before this list
|
|
72
|
+
# runs. A DOTALL regex here is both easier to make backtracking-prone and can
|
|
73
|
+
# let keyed values like SECRET_KEY=<PEM> leak the body after the first newline.
|
|
74
|
+
# Provider token shapes are prefix-anchored; we deliberately avoid a blanket
|
|
75
|
+
# high-entropy matcher, which would redact legitimate hashes/IDs/output.
|
|
76
|
+
(re.compile(r"\bsk-(?:proj|ant|svcacct)-[A-Za-z0-9_-]{8,}"), "sk-***"),
|
|
77
|
+
(re.compile(r"\bsk-[A-Za-z0-9]{8,}"), "sk-***"),
|
|
78
|
+
(re.compile(r"\bgh[opusr]_[A-Za-z0-9]{20,}\b"), "gh***"),
|
|
79
|
+
(re.compile(r"\bgithub_pat_[A-Za-z0-9_]{20,}\b"), "github_pat_***"),
|
|
80
|
+
(re.compile(r"\b(AKIA|ASIA)[0-9A-Z]{16}\b"), r"\1***"),
|
|
81
|
+
(re.compile(r"\bAIza[0-9A-Za-z_-]{32,}\b"), "AIza***"),
|
|
82
|
+
(re.compile(r"\bxox[baprs]-[A-Za-z0-9-]{10,}"), "xox***"),
|
|
83
|
+
# Stripe secret/restricted keys (live or test); publishable pk_ keys are
|
|
84
|
+
# public by design and intentionally excluded.
|
|
85
|
+
(re.compile(r"\b([sr]k_(?:live|test)_)[0-9A-Za-z]{10,}\b"), r"\1***"),
|
|
86
|
+
(re.compile(r"\bwhsec_[A-Za-z0-9]{10,}\b"), "whsec_***"),
|
|
87
|
+
(re.compile(r"\bnpm_[A-Za-z0-9]{36,}\b"), "npm_***"),
|
|
88
|
+
(re.compile(r"\bSG\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}\b"), "SG.***"),
|
|
89
|
+
(
|
|
90
|
+
re.compile(
|
|
91
|
+
SLACK_WEBHOOK_SCHEME
|
|
92
|
+
+ URL_SCHEME_SEPARATOR
|
|
93
|
+
+ SLACK_WEBHOOK_HOST_PATTERN
|
|
94
|
+
+ SLACK_WEBHOOK_PATH_PATTERN
|
|
95
|
+
),
|
|
96
|
+
"***",
|
|
97
|
+
),
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
PEM_BLOCK_PLACEHOLDER = "***PRIVATE KEY REDACTED***"
|
|
102
|
+
_PEM_BEGIN = re.compile(r"-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----")
|
|
103
|
+
_PEM_END = re.compile(r"-----END [A-Z0-9 ]*PRIVATE KEY-----")
|
|
104
|
+
_URL_SPAN_RE = re.compile(r"[A-Za-z][A-Za-z0-9+.-]*://[^\s\"'`<>|;,)]+")
|
|
105
|
+
# Absolute POSIX paths outside URL spans; avoid matching ratio-style "1/2".
|
|
106
|
+
_ABSOLUTE_POSIX_PATH_RE = re.compile(
|
|
107
|
+
r"(?<![A-Za-z0-9_.~+-])/(?:[^\s\"'`<>|;,)]+(?:/[^\s\"'`<>|;,)]+)*)"
|
|
108
|
+
)
|
|
109
|
+
_TILDE_PATH_RE = re.compile(r"(?<!\S)~(?:/[^\s\"'`<>|;,)]+)+")
|
|
110
|
+
_WINDOWS_PATH_RE = re.compile(r"\b[A-Za-z]:\\[^\s\"'`<>|;,)]+")
|
|
111
|
+
_SECRET_KEY_RE = re.compile(
|
|
112
|
+
r"("
|
|
113
|
+
r"API[_-]?KEY|APIKEY|ACCESS[_-]?KEY|SECRET[_-]?KEY|PRIVATE[_-]?KEY|"
|
|
114
|
+
r"ACCESS[_-]?TOKEN|REFRESH[_-]?TOKEN|AUTH[_-]?TOKEN|AUTHTOKEN|"
|
|
115
|
+
r"CLIENT[_-]?SECRET|PASSWORD|PASSWD|SECRET|TOKEN|CREDENTIAL"
|
|
116
|
+
r")",
|
|
117
|
+
re.IGNORECASE,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _mask_progress_paths_outside_urls(value: str) -> str:
|
|
122
|
+
parts: list[str] = []
|
|
123
|
+
last = 0
|
|
124
|
+
for match in _URL_SPAN_RE.finditer(value):
|
|
125
|
+
segment = value[last : match.start()]
|
|
126
|
+
if segment:
|
|
127
|
+
segment = _ABSOLUTE_POSIX_PATH_RE.sub("<redacted-path>", segment)
|
|
128
|
+
segment = _TILDE_PATH_RE.sub("<redacted-path>", segment)
|
|
129
|
+
segment = _WINDOWS_PATH_RE.sub("<redacted-path>", segment)
|
|
130
|
+
parts.append(segment)
|
|
131
|
+
parts.append(match.group(0))
|
|
132
|
+
last = match.end()
|
|
133
|
+
tail = value[last:]
|
|
134
|
+
if tail:
|
|
135
|
+
tail = _ABSOLUTE_POSIX_PATH_RE.sub("<redacted-path>", tail)
|
|
136
|
+
tail = _TILDE_PATH_RE.sub("<redacted-path>", tail)
|
|
137
|
+
tail = _WINDOWS_PATH_RE.sub("<redacted-path>", tail)
|
|
138
|
+
parts.append(tail)
|
|
139
|
+
return "".join(parts)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _redact_pem_blocks(value: str) -> str:
|
|
143
|
+
match = _PEM_BEGIN.search(value)
|
|
144
|
+
if match is None:
|
|
145
|
+
return value
|
|
146
|
+
parts: list[str] = []
|
|
147
|
+
pos = 0
|
|
148
|
+
while match is not None:
|
|
149
|
+
parts.append(value[pos : match.start()])
|
|
150
|
+
end = _PEM_END.search(value, match.end())
|
|
151
|
+
parts.append(PEM_BLOCK_PLACEHOLDER)
|
|
152
|
+
if end is None:
|
|
153
|
+
return "".join(parts)
|
|
154
|
+
pos = end.end()
|
|
155
|
+
match = _PEM_BEGIN.search(value, pos)
|
|
156
|
+
parts.append(value[pos:])
|
|
157
|
+
return "".join(parts)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def redact_string(value: str) -> str:
|
|
161
|
+
redacted = _redact_pem_blocks(value)
|
|
162
|
+
for pattern, replacement in REDACT_PATTERNS:
|
|
163
|
+
redacted = pattern.sub(replacement, redacted)
|
|
164
|
+
return redacted
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def redact_progress_label(value: str) -> str:
|
|
168
|
+
redacted = redact_string(value)
|
|
169
|
+
return _mask_progress_paths_outside_urls(redacted)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def key_looks_secret(key: str) -> bool:
|
|
173
|
+
return _SECRET_KEY_RE.search(key) is not None
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def redact_mapping_value(key: str, value: JsonValue) -> JsonValue:
|
|
177
|
+
if key_looks_secret(key):
|
|
178
|
+
return "***"
|
|
179
|
+
return redact_value(value)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def redact_env_map(mapping: Mapping[str, str]) -> dict[str, JsonValue]:
|
|
183
|
+
return {key: redact_mapping_value(key, value) for key, value in mapping.items()}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def redact_value(value: JsonValue) -> JsonValue:
|
|
187
|
+
if isinstance(value, str):
|
|
188
|
+
return redact_string(value)
|
|
189
|
+
if isinstance(value, list):
|
|
190
|
+
return [redact_value(item) for item in value]
|
|
191
|
+
if isinstance(value, dict):
|
|
192
|
+
return {key: redact_value(item) for key, item in value.items()}
|
|
193
|
+
return value
|