prizmkit 1.1.62 → 1.1.64
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.
- package/bundled/VERSION.json +3 -3
- package/bundled/adapters/codex/settings-adapter.js +1 -0
- package/bundled/dev-pipeline/lib/common.sh +38 -4
- package/bundled/dev-pipeline/lib/heartbeat.sh +39 -7
- package/bundled/dev-pipeline/run-bugfix.sh +2 -25
- package/bundled/dev-pipeline/run-feature.sh +2 -26
- package/bundled/dev-pipeline/run-refactor.sh +2 -25
- package/bundled/dev-pipeline/scripts/parse-stream-progress.py +96 -0
- package/bundled/dev-pipeline-windows/.env.example +11 -0
- package/bundled/dev-pipeline-windows/SCHEMA_ANALYSIS.md +11 -0
- package/bundled/dev-pipeline-windows/lib/branch.ps1 +222 -0
- package/bundled/dev-pipeline-windows/lib/common.ps1 +160 -10
- package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +332 -30
- package/bundled/dev-pipeline-windows/run-recovery.ps1 +101 -5
- package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +96 -0
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/templates/project-memory-template.md +5 -0
- package/package.json +1 -1
- package/src/scaffold.js +117 -1
- package/src/upgrade.js +14 -2
package/bundled/VERSION.json
CHANGED
|
@@ -369,7 +369,23 @@ prizm_start_ai_session() {
|
|
|
369
369
|
"$CLI_CMD" "${claude_args[@]}" > "$log_path" 2>&1 &
|
|
370
370
|
;;
|
|
371
371
|
codex)
|
|
372
|
-
local codex_args=(--ask-for-approval never --sandbox danger-full-access
|
|
372
|
+
local codex_args=(--ask-for-approval never --sandbox danger-full-access)
|
|
373
|
+
local codex_subagent_timeout="${CODEX_SUBAGENT_TIMEOUT_SECONDS:-}"
|
|
374
|
+
if [[ -z "$codex_subagent_timeout" ]]; then
|
|
375
|
+
local outer_stale_threshold="${STALE_KILL_THRESHOLD:-900}"
|
|
376
|
+
if [[ "$outer_stale_threshold" =~ ^[0-9]+$ && "$outer_stale_threshold" -gt 120 ]]; then
|
|
377
|
+
codex_subagent_timeout=$((outer_stale_threshold - 60))
|
|
378
|
+
else
|
|
379
|
+
codex_subagent_timeout=840
|
|
380
|
+
fi
|
|
381
|
+
fi
|
|
382
|
+
if [[ "$codex_subagent_timeout" =~ ^[0-9]+$ && "$codex_subagent_timeout" -gt 0 ]]; then
|
|
383
|
+
codex_args+=(--config "agents.job_max_runtime_seconds=$codex_subagent_timeout")
|
|
384
|
+
fi
|
|
385
|
+
codex_args+=(exec --cd "$PROJECT_ROOT" --skip-git-repo-check)
|
|
386
|
+
if [[ "$USE_STREAM_JSON" == "true" ]]; then
|
|
387
|
+
codex_args+=(--json)
|
|
388
|
+
fi
|
|
373
389
|
if [[ -n "$model" ]]; then
|
|
374
390
|
codex_args+=(--model "$model")
|
|
375
391
|
fi
|
|
@@ -413,7 +429,23 @@ prizm_run_ai_session() {
|
|
|
413
429
|
"$CLI_CMD" "${claude_args[@]}" > "$log_path" 2>&1
|
|
414
430
|
;;
|
|
415
431
|
codex)
|
|
416
|
-
local codex_args=(--ask-for-approval never --sandbox danger-full-access
|
|
432
|
+
local codex_args=(--ask-for-approval never --sandbox danger-full-access)
|
|
433
|
+
local codex_subagent_timeout="${CODEX_SUBAGENT_TIMEOUT_SECONDS:-}"
|
|
434
|
+
if [[ -z "$codex_subagent_timeout" ]]; then
|
|
435
|
+
local outer_stale_threshold="${STALE_KILL_THRESHOLD:-900}"
|
|
436
|
+
if [[ "$outer_stale_threshold" =~ ^[0-9]+$ && "$outer_stale_threshold" -gt 120 ]]; then
|
|
437
|
+
codex_subagent_timeout=$((outer_stale_threshold - 60))
|
|
438
|
+
else
|
|
439
|
+
codex_subagent_timeout=840
|
|
440
|
+
fi
|
|
441
|
+
fi
|
|
442
|
+
if [[ "$codex_subagent_timeout" =~ ^[0-9]+$ && "$codex_subagent_timeout" -gt 0 ]]; then
|
|
443
|
+
codex_args+=(--config "agents.job_max_runtime_seconds=$codex_subagent_timeout")
|
|
444
|
+
fi
|
|
445
|
+
codex_args+=(exec --cd "$PROJECT_ROOT" --skip-git-repo-check)
|
|
446
|
+
if [[ "$USE_STREAM_JSON" == "true" ]]; then
|
|
447
|
+
codex_args+=(--json)
|
|
448
|
+
fi
|
|
417
449
|
if [[ -n "$model" ]]; then
|
|
418
450
|
codex_args+=(--model "$model")
|
|
419
451
|
fi
|
|
@@ -441,10 +473,12 @@ prizm_detect_subagents() {
|
|
|
441
473
|
[[ -f "$session_log" ]] || return 0
|
|
442
474
|
|
|
443
475
|
local count=0
|
|
444
|
-
if [[ "$USE_STREAM_JSON" == "true" ]]; then
|
|
476
|
+
if [[ "$USE_STREAM_JSON" == "true" && "${STREAM_JSON_FORMAT:-}" == "codex-json" ]]; then
|
|
477
|
+
count=$(grep -c '"tool"[[:space:]]*:[[:space:]]*"spawn_agent"' "$session_log" 2>/dev/null) || true
|
|
478
|
+
elif [[ "$USE_STREAM_JSON" == "true" ]]; then
|
|
445
479
|
count=$(grep -c '"name"[[:space:]]*:[[:space:]]*"Agent"' "$session_log" 2>/dev/null) || true
|
|
446
480
|
else
|
|
447
|
-
count=$(grep -cE '(Tool: Agent|"tool":\s*"Agent"|tool_use.*Agent|subagent_type)' "$session_log" 2>/dev/null) || true
|
|
481
|
+
count=$(grep -cE '(Tool: Agent|"tool":\s*"Agent"|"tool":\s*"spawn_agent"|tool_use.*Agent|subagent_type|collab: SpawnAgent)' "$session_log" 2>/dev/null) || true
|
|
448
482
|
fi
|
|
449
483
|
|
|
450
484
|
count=${count:-0}
|
|
@@ -88,17 +88,22 @@ start_heartbeat() {
|
|
|
88
88
|
local stale_mins=$((stale_seconds / 60))
|
|
89
89
|
echo -e " ${RED}[HEARTBEAT]${NC} ${mins}m${secs}s | log: ${size_display} | ${RED}STALE-KILL: no progress for ${stale_mins}m (threshold: ${stale_kill_threshold}s)${NC}"
|
|
90
90
|
echo -e " ${RED}[HEARTBEAT]${NC} Killing AI CLI process $cli_pid (stale session)..."
|
|
91
|
+
# Write the marker before killing. Some CLIs exit quickly, and the
|
|
92
|
+
# parent runner may stop this heartbeat process immediately after
|
|
93
|
+
# wait(1) returns.
|
|
94
|
+
local _marker_dir
|
|
95
|
+
_marker_dir="$(dirname "$session_log")"
|
|
96
|
+
echo "{\"killed_at\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\", \"reason\": \"stale_session\", \"stale_seconds\": $stale_seconds, \"threshold\": $stale_kill_threshold}" > "$_marker_dir/stale-kill.json" 2>/dev/null || true
|
|
91
97
|
kill -TERM "$cli_pid" 2>/dev/null || true
|
|
92
98
|
# Give process 10s to exit gracefully, then force kill
|
|
93
|
-
|
|
99
|
+
local stale_kill_grace_seconds="${STALE_KILL_GRACE_SECONDS:-10}"
|
|
100
|
+
if [[ $stale_kill_grace_seconds -gt 0 ]]; then
|
|
101
|
+
sleep "$stale_kill_grace_seconds"
|
|
102
|
+
fi
|
|
94
103
|
if kill -0 "$cli_pid" 2>/dev/null; then
|
|
95
104
|
echo -e " ${RED}[HEARTBEAT]${NC} Process still alive after SIGTERM, sending SIGKILL..."
|
|
96
105
|
kill -9 "$cli_pid" 2>/dev/null || true
|
|
97
106
|
fi
|
|
98
|
-
# Write stale-kill marker so spawn_and_wait_session knows this wasn't a crash
|
|
99
|
-
local _marker_dir
|
|
100
|
-
_marker_dir="$(dirname "$session_log")"
|
|
101
|
-
echo "{\"killed_at\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\", \"reason\": \"stale_session\", \"stale_seconds\": $stale_seconds, \"threshold\": $stale_kill_threshold}" > "$_marker_dir/stale-kill.json" 2>/dev/null || true
|
|
102
107
|
break
|
|
103
108
|
fi
|
|
104
109
|
|
|
@@ -200,7 +205,7 @@ stop_progress_parser() {
|
|
|
200
205
|
fi
|
|
201
206
|
}
|
|
202
207
|
|
|
203
|
-
# Detect whether the AI CLI supports
|
|
208
|
+
# Detect whether the AI CLI supports structured JSON progress.
|
|
204
209
|
# Sets USE_STREAM_JSON to "true" or "false".
|
|
205
210
|
#
|
|
206
211
|
# Arguments:
|
|
@@ -208,10 +213,35 @@ stop_progress_parser() {
|
|
|
208
213
|
detect_stream_json_support() {
|
|
209
214
|
local cli_cmd="$1"
|
|
210
215
|
USE_STREAM_JSON="false"
|
|
216
|
+
STREAM_JSON_FORMAT=""
|
|
217
|
+
|
|
218
|
+
local session_platform=""
|
|
219
|
+
session_platform="$(_prizm_known_platform_from_cli "$cli_cmd" 2>/dev/null || true)"
|
|
220
|
+
if [[ -z "$session_platform" ]]; then
|
|
221
|
+
case "$(_prizm_normalize_platform "${PRIZMKIT_PLATFORM:-${PLATFORM:-}}")" in
|
|
222
|
+
codex|claude|codebuddy)
|
|
223
|
+
session_platform="$(_prizm_normalize_platform "${PRIZMKIT_PLATFORM:-${PLATFORM:-}}")"
|
|
224
|
+
;;
|
|
225
|
+
esac
|
|
226
|
+
fi
|
|
211
227
|
|
|
212
228
|
# CodeBuddy (cbc) always supports stream-json
|
|
213
|
-
if [[ "$cli_cmd" == "cbc" ]]; then
|
|
229
|
+
if [[ "$session_platform" == "codebuddy" || "$cli_cmd" == "cbc" ]]; then
|
|
214
230
|
USE_STREAM_JSON="true"
|
|
231
|
+
STREAM_JSON_FORMAT="stream-json"
|
|
232
|
+
export USE_STREAM_JSON STREAM_JSON_FORMAT
|
|
233
|
+
return 0
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
# Codex uses `codex exec --json` JSONL, not `--output-format stream-json`.
|
|
237
|
+
if [[ "$session_platform" == "codex" ]]; then
|
|
238
|
+
local codex_help
|
|
239
|
+
codex_help=$("$cli_cmd" exec --help 2>&1) || true
|
|
240
|
+
if echo "$codex_help" | grep -q -- "--json"; then
|
|
241
|
+
USE_STREAM_JSON="true"
|
|
242
|
+
STREAM_JSON_FORMAT="codex-json"
|
|
243
|
+
fi
|
|
244
|
+
export USE_STREAM_JSON STREAM_JSON_FORMAT
|
|
215
245
|
return 0
|
|
216
246
|
fi
|
|
217
247
|
|
|
@@ -222,5 +252,7 @@ detect_stream_json_support() {
|
|
|
222
252
|
|
|
223
253
|
if echo "$help_output" | grep -q "stream-json"; then
|
|
224
254
|
USE_STREAM_JSON="true"
|
|
255
|
+
STREAM_JSON_FORMAT="stream-json"
|
|
225
256
|
fi
|
|
257
|
+
export USE_STREAM_JSON STREAM_JSON_FORMAT
|
|
226
258
|
}
|
|
@@ -164,31 +164,8 @@ spawn_and_wait_session() {
|
|
|
164
164
|
session_status="timed_out"
|
|
165
165
|
elif [[ "$was_stale_killed" == true ]]; then
|
|
166
166
|
log_warn "Session stale-killed (no progress for ${STALE_KILL_THRESHOLD}s)"
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
has_commits=$(git -C "$project_root" log "${default_branch}..HEAD" --oneline 2>/dev/null | head -1)
|
|
170
|
-
fi
|
|
171
|
-
if [[ -n "$has_commits" ]]; then
|
|
172
|
-
log_info "Stale-killed session has commits — treating as success"
|
|
173
|
-
session_status="success"
|
|
174
|
-
else
|
|
175
|
-
local uncommitted=""
|
|
176
|
-
uncommitted=$(git -C "$project_root" status --porcelain 2>/dev/null | head -1 || true)
|
|
177
|
-
if [[ -n "$uncommitted" ]]; then
|
|
178
|
-
log_warn "Stale-killed session has uncommitted changes — auto-committing..."
|
|
179
|
-
git -C "$project_root" add -A 2>/dev/null || true
|
|
180
|
-
if git -C "$project_root" commit --no-verify -m "chore($bug_id): auto-commit session work (stale-killed)" 2>/dev/null; then
|
|
181
|
-
log_info "Auto-commit succeeded"
|
|
182
|
-
session_status="success"
|
|
183
|
-
else
|
|
184
|
-
log_warn "Auto-commit failed — no changes to commit"
|
|
185
|
-
session_status="crashed"
|
|
186
|
-
fi
|
|
187
|
-
else
|
|
188
|
-
log_warn "Stale-killed session produced no commits and no changes"
|
|
189
|
-
session_status="crashed"
|
|
190
|
-
fi
|
|
191
|
-
fi
|
|
167
|
+
log_warn "Stale-killed sessions are treated as failed; dev branch is preserved for inspection"
|
|
168
|
+
session_status="crashed"
|
|
192
169
|
elif [[ $exit_code -ne 0 ]]; then
|
|
193
170
|
log_warn "Session exited with code $exit_code"
|
|
194
171
|
session_status="crashed"
|
|
@@ -174,32 +174,8 @@ spawn_and_wait_session() {
|
|
|
174
174
|
session_status="timed_out"
|
|
175
175
|
elif [[ "$was_stale_killed" == true ]]; then
|
|
176
176
|
log_warn "Session stale-killed (no progress for ${STALE_KILL_THRESHOLD}s)"
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if git -C "$project_root" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
180
|
-
has_commits=$(git -C "$project_root" log "${default_branch}..HEAD" --oneline 2>/dev/null | head -1)
|
|
181
|
-
fi
|
|
182
|
-
if [[ -n "$has_commits" ]]; then
|
|
183
|
-
log_info "Stale-killed session has commits — treating as success"
|
|
184
|
-
session_status="success"
|
|
185
|
-
else
|
|
186
|
-
local uncommitted=""
|
|
187
|
-
uncommitted=$(git -C "$project_root" status --porcelain 2>/dev/null | head -1 || true)
|
|
188
|
-
if [[ -n "$uncommitted" ]]; then
|
|
189
|
-
log_warn "Stale-killed session has uncommitted changes — auto-committing..."
|
|
190
|
-
git -C "$project_root" add -A 2>/dev/null || true
|
|
191
|
-
if git -C "$project_root" commit --no-verify -m "chore($feature_id): auto-commit session work (stale-killed)" 2>/dev/null; then
|
|
192
|
-
log_info "Auto-commit succeeded"
|
|
193
|
-
session_status="success"
|
|
194
|
-
else
|
|
195
|
-
log_warn "Auto-commit failed — no changes to commit"
|
|
196
|
-
session_status="crashed"
|
|
197
|
-
fi
|
|
198
|
-
else
|
|
199
|
-
log_warn "Stale-killed session produced no commits and no changes"
|
|
200
|
-
session_status="crashed"
|
|
201
|
-
fi
|
|
202
|
-
fi
|
|
177
|
+
log_warn "Stale-killed sessions are treated as failed; dev branch is preserved for inspection"
|
|
178
|
+
session_status="crashed"
|
|
203
179
|
elif [[ $exit_code -ne 0 ]]; then
|
|
204
180
|
log_warn "Session exited with code $exit_code"
|
|
205
181
|
session_status="crashed"
|
|
@@ -166,31 +166,8 @@ spawn_and_wait_session() {
|
|
|
166
166
|
session_status="timed_out"
|
|
167
167
|
elif [[ "$was_stale_killed" == true ]]; then
|
|
168
168
|
log_warn "Session stale-killed (no progress for ${STALE_KILL_THRESHOLD}s)"
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
has_commits=$(git -C "$project_root" log "${default_branch}..HEAD" --oneline 2>/dev/null | head -1)
|
|
172
|
-
fi
|
|
173
|
-
if [[ -n "$has_commits" ]]; then
|
|
174
|
-
log_info "Stale-killed session has commits — treating as success"
|
|
175
|
-
session_status="success"
|
|
176
|
-
else
|
|
177
|
-
local uncommitted=""
|
|
178
|
-
uncommitted=$(git -C "$project_root" status --porcelain 2>/dev/null | head -1 || true)
|
|
179
|
-
if [[ -n "$uncommitted" ]]; then
|
|
180
|
-
log_warn "Stale-killed session has uncommitted changes — auto-committing..."
|
|
181
|
-
git -C "$project_root" add -A 2>/dev/null || true
|
|
182
|
-
if git -C "$project_root" commit --no-verify -m "chore($refactor_id): auto-commit session work (stale-killed)" 2>/dev/null; then
|
|
183
|
-
log_info "Auto-commit succeeded"
|
|
184
|
-
session_status="success"
|
|
185
|
-
else
|
|
186
|
-
log_warn "Auto-commit failed — no changes to commit"
|
|
187
|
-
session_status="crashed"
|
|
188
|
-
fi
|
|
189
|
-
else
|
|
190
|
-
log_warn "Stale-killed session produced no commits and no changes"
|
|
191
|
-
session_status="crashed"
|
|
192
|
-
fi
|
|
193
|
-
fi
|
|
169
|
+
log_warn "Stale-killed sessions are treated as failed; dev branch is preserved for inspection"
|
|
170
|
+
session_status="crashed"
|
|
194
171
|
elif [[ $exit_code -ne 0 ]]; then
|
|
195
172
|
log_warn "Session exited with code $exit_code"
|
|
196
173
|
session_status="crashed"
|
|
@@ -73,6 +73,9 @@ class ProgressTracker:
|
|
|
73
73
|
self.last_text_snippet = ""
|
|
74
74
|
self.is_active = True
|
|
75
75
|
self.errors = []
|
|
76
|
+
self.event_format = ""
|
|
77
|
+
self.active_subagent_count = 0
|
|
78
|
+
self.subagent_status_counts = Counter()
|
|
76
79
|
self._text_buffer = ""
|
|
77
80
|
self._in_tool_use = False
|
|
78
81
|
self._current_tool_input_parts = []
|
|
@@ -87,8 +90,72 @@ class ProgressTracker:
|
|
|
87
90
|
"""
|
|
88
91
|
event_type = event.get("type", "")
|
|
89
92
|
|
|
93
|
+
# ── Codex exec --json JSONL format ──────────────────────────
|
|
94
|
+
if event_type in (
|
|
95
|
+
"thread.started", "turn.started", "turn.completed",
|
|
96
|
+
"turn.failed", "item.started", "item.completed", "error",
|
|
97
|
+
):
|
|
98
|
+
self.event_format = "codex-json"
|
|
99
|
+
self.is_active = True
|
|
100
|
+
|
|
101
|
+
if event_type == "turn.started":
|
|
102
|
+
self.message_count += 1
|
|
103
|
+
|
|
104
|
+
elif event_type in ("item.started", "item.completed"):
|
|
105
|
+
item = event.get("item", {})
|
|
106
|
+
item_type = item.get("type", "")
|
|
107
|
+
|
|
108
|
+
if item_type == "agent_message":
|
|
109
|
+
text = item.get("text", "")
|
|
110
|
+
if text.strip():
|
|
111
|
+
self.last_text_snippet = text.strip()[:120]
|
|
112
|
+
self._detect_phase(text)
|
|
113
|
+
|
|
114
|
+
elif item_type == "collab_tool_call":
|
|
115
|
+
tool_name = item.get("tool", "collab")
|
|
116
|
+
if event_type == "item.started":
|
|
117
|
+
self.current_tool = tool_name
|
|
118
|
+
self.tool_call_counts[tool_name] += 1
|
|
119
|
+
self.total_tool_calls += 1
|
|
120
|
+
elif item.get("status") == "completed":
|
|
121
|
+
self.current_tool = None
|
|
122
|
+
self._extract_tool_summary_from_dict(item)
|
|
123
|
+
self._update_subagent_status_counts(
|
|
124
|
+
item.get("agents_states", {})
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
prompt = item.get("prompt")
|
|
128
|
+
if prompt:
|
|
129
|
+
self._detect_phase(prompt)
|
|
130
|
+
|
|
131
|
+
else:
|
|
132
|
+
tool_name = item.get("tool") or item.get("name")
|
|
133
|
+
if tool_name:
|
|
134
|
+
if event_type == "item.started":
|
|
135
|
+
self.current_tool = tool_name
|
|
136
|
+
self.tool_call_counts[tool_name] += 1
|
|
137
|
+
self.total_tool_calls += 1
|
|
138
|
+
elif item.get("status") == "completed":
|
|
139
|
+
self.current_tool = None
|
|
140
|
+
self._extract_tool_summary_from_dict(item)
|
|
141
|
+
|
|
142
|
+
elif event_type == "turn.completed":
|
|
143
|
+
self.current_tool = None
|
|
144
|
+
|
|
145
|
+
elif event_type == "turn.failed":
|
|
146
|
+
error = event.get("error") or event.get("message") or "Codex turn failed"
|
|
147
|
+
self.errors.append(str(error))
|
|
148
|
+
self.current_tool = None
|
|
149
|
+
|
|
150
|
+
elif event_type == "error":
|
|
151
|
+
error = event.get("error") or event.get("message") or "Unknown error"
|
|
152
|
+
self.errors.append(str(error))
|
|
153
|
+
|
|
154
|
+
return
|
|
155
|
+
|
|
90
156
|
# ── Claude Code verbose format ──────────────────────────────
|
|
91
157
|
if event_type == "assistant":
|
|
158
|
+
self.event_format = self.event_format or "stream-json"
|
|
92
159
|
self.message_count += 1
|
|
93
160
|
self.is_active = True
|
|
94
161
|
message = event.get("message", {})
|
|
@@ -113,16 +180,19 @@ class ProgressTracker:
|
|
|
113
180
|
|
|
114
181
|
elif event_type == "tool_result" or event_type == "user":
|
|
115
182
|
# tool_result contains output from tool execution
|
|
183
|
+
self.event_format = self.event_format or "stream-json"
|
|
116
184
|
self.is_active = True
|
|
117
185
|
|
|
118
186
|
elif event_type == "system":
|
|
119
187
|
# System events (hooks, init, etc.) — track but don't count as messages
|
|
188
|
+
self.event_format = self.event_format or "stream-json"
|
|
120
189
|
subtype = event.get("subtype", "")
|
|
121
190
|
if subtype == "init":
|
|
122
191
|
self.is_active = True
|
|
123
192
|
|
|
124
193
|
# ── Claude API raw stream format ────────────────────────────
|
|
125
194
|
elif event_type == "message_start":
|
|
195
|
+
self.event_format = self.event_format or "stream-json"
|
|
126
196
|
self.message_count += 1
|
|
127
197
|
self.is_active = True
|
|
128
198
|
|
|
@@ -256,14 +326,38 @@ class ProgressTracker:
|
|
|
256
326
|
elif "prompt" in data:
|
|
257
327
|
self.current_tool_input_summary = str(data["prompt"])[:100]
|
|
258
328
|
|
|
329
|
+
def _update_subagent_status_counts(self, agents_states):
|
|
330
|
+
"""Track Codex subagent state counts from collab_tool_call items."""
|
|
331
|
+
counts = Counter()
|
|
332
|
+
active = 0
|
|
333
|
+
if isinstance(agents_states, dict):
|
|
334
|
+
for state in agents_states.values():
|
|
335
|
+
if not isinstance(state, dict):
|
|
336
|
+
continue
|
|
337
|
+
status = str(state.get("status", "unknown"))
|
|
338
|
+
counts[status] += 1
|
|
339
|
+
if status not in ("completed", "failed", "cancelled", "canceled"):
|
|
340
|
+
active += 1
|
|
341
|
+
message = state.get("message")
|
|
342
|
+
if message:
|
|
343
|
+
self.last_text_snippet = str(message).strip()[:120]
|
|
344
|
+
self._detect_phase(str(message))
|
|
345
|
+
self.subagent_status_counts = counts
|
|
346
|
+
self.active_subagent_count = active
|
|
347
|
+
|
|
259
348
|
def to_dict(self):
|
|
260
349
|
"""Export current state as a dictionary for JSON serialization."""
|
|
261
350
|
tool_calls = [
|
|
262
351
|
{"name": name, "count": count}
|
|
263
352
|
for name, count in self.tool_call_counts.most_common()
|
|
264
353
|
]
|
|
354
|
+
subagent_states = [
|
|
355
|
+
{"status": status, "count": count}
|
|
356
|
+
for status, count in self.subagent_status_counts.most_common()
|
|
357
|
+
]
|
|
265
358
|
return {
|
|
266
359
|
"updated_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
360
|
+
"event_format": self.event_format,
|
|
267
361
|
"message_count": self.message_count,
|
|
268
362
|
"current_tool": self.current_tool,
|
|
269
363
|
"current_tool_input_summary": self.current_tool_input_summary,
|
|
@@ -271,6 +365,8 @@ class ProgressTracker:
|
|
|
271
365
|
"detected_phases": self.detected_phases,
|
|
272
366
|
"tool_calls": tool_calls,
|
|
273
367
|
"total_tool_calls": self.total_tool_calls,
|
|
368
|
+
"active_subagent_count": self.active_subagent_count,
|
|
369
|
+
"subagent_states": subagent_states,
|
|
274
370
|
"last_text_snippet": self.last_text_snippet,
|
|
275
371
|
"is_active": self.is_active,
|
|
276
372
|
"errors": self.errors[-10:], # Keep last 10 errors
|
|
@@ -21,6 +21,17 @@
|
|
|
21
21
|
# MAX_RETRIES=3 # Max retry attempts per task before marking failed
|
|
22
22
|
# SESSION_TIMEOUT=0 # Session timeout in seconds (0 = no limit)
|
|
23
23
|
# VERBOSE=1 # Verbose logging (1=on, 0=off)
|
|
24
|
+
# HEARTBEAT_INTERVAL=30 # Poll interval for session progress/stale checks
|
|
25
|
+
# STALE_KILL_THRESHOLD=900 # Auto-kill session after N seconds without log progress (0 = disabled)
|
|
26
|
+
# STALE_KILL_GRACE_SECONDS=10 # Grace period after stale-kill before force-stopping the job
|
|
27
|
+
# CODEX_SUBAGENT_TIMEOUT_SECONDS=840 # Codex subagent max runtime; defaults to stale threshold - 60
|
|
28
|
+
# LOG_CLEANUP_ENABLED=1 # Run periodic session log cleanup
|
|
29
|
+
# LOG_RETENTION_DAYS=14 # Delete session logs older than N days
|
|
30
|
+
# LOG_MAX_TOTAL_MB=1024 # Keep total logs under N MB via oldest-first cleanup
|
|
31
|
+
# STOP_ON_FAILURE=0 # Stop after the first failed task (1=stop, 0=continue)
|
|
32
|
+
# ENABLE_DEPLOY=0 # Start a deploy session after all tasks complete successfully
|
|
33
|
+
# DEV_BRANCH= # Optional custom dev branch name for each task
|
|
34
|
+
# AUTO_PUSH=0 # Push original branch after successful merge (1=enabled)
|
|
24
35
|
|
|
25
36
|
# ─── Feature Pipeline Only ────────────────────────────────────────────
|
|
26
37
|
# ENABLE_CRITIC=false # Enable adversarial critic review (true/false)
|
|
@@ -345,6 +345,17 @@ pending, in_progress, completed, failed, skipped
|
|
|
345
345
|
| `MAX_RETRIES` | integer | (not specified) | Retry attempts per task |
|
|
346
346
|
| `SESSION_TIMEOUT` | integer | 0 | 0 = no limit |
|
|
347
347
|
| `VERBOSE` | integer | (not specified) | 1=on, 0=off |
|
|
348
|
+
| `HEARTBEAT_INTERVAL` | integer | 30 | Poll interval for session progress/stale checks |
|
|
349
|
+
| `STALE_KILL_THRESHOLD` | integer | 900 | Auto-kill after N seconds without log progress; 0 disables |
|
|
350
|
+
| `STALE_KILL_GRACE_SECONDS` | integer | 10 | Grace period after stale-kill before force-stopping |
|
|
351
|
+
| `CODEX_SUBAGENT_TIMEOUT_SECONDS` | integer | 840 | Codex subagent max runtime |
|
|
352
|
+
| `LOG_CLEANUP_ENABLED` | boolean | 1 | Periodic session log cleanup |
|
|
353
|
+
| `LOG_RETENTION_DAYS` | integer | 14 | Delete session logs older than N days |
|
|
354
|
+
| `LOG_MAX_TOTAL_MB` | integer | 1024 | Keep total logs under N MB |
|
|
355
|
+
| `STOP_ON_FAILURE` | boolean | 0 | Stop after the first failed task |
|
|
356
|
+
| `ENABLE_DEPLOY` | boolean | 0 | Start deploy session after all tasks complete |
|
|
357
|
+
| `DEV_BRANCH` | string | auto-generated | Optional custom dev branch name |
|
|
358
|
+
| `AUTO_PUSH` | boolean | 0 | Push original branch after successful merge |
|
|
348
359
|
| `ENABLE_CRITIC` | boolean | false | Adversarial review enable |
|
|
349
360
|
| `PIPELINE_MODE` | string | auto-detect | lite/standard/full override |
|
|
350
361
|
|