prizmkit 1.1.93 → 1.1.94
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/dev-pipeline/lib/heartbeat.sh +4 -50
- package/bundled/dev-pipeline/run-bugfix.sh +2 -10
- package/bundled/dev-pipeline/run-feature.sh +2 -10
- package/bundled/dev-pipeline/run-refactor.sh +2 -10
- package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +6 -49
- package/bundled/skills/_metadata.json +1 -1
- package/package.json +1 -1
package/bundled/VERSION.json
CHANGED
|
@@ -44,7 +44,6 @@ start_heartbeat() {
|
|
|
44
44
|
local prev_max_size=0
|
|
45
45
|
local prev_child_activity_signature=""
|
|
46
46
|
local prev_progress_signature=""
|
|
47
|
-
local prev_error_loop_signature=""
|
|
48
47
|
local stale_seconds=0
|
|
49
48
|
local stale_reason="stale_session"
|
|
50
49
|
while kill -0 "$cli_pid" 2>/dev/null; do
|
|
@@ -197,64 +196,24 @@ PY
|
|
|
197
196
|
fi
|
|
198
197
|
fi
|
|
199
198
|
|
|
200
|
-
# Check for error-loop: agent is actively producing output but results are
|
|
201
|
-
# all read-offset errors or wasted calls. This is a stuck agent that appears
|
|
202
|
-
# "active" by log growth but is accomplishing nothing. Only a newly advancing
|
|
203
|
-
# error signature counts; stale historical errors must not turn later normal
|
|
204
|
-
# output into a stale kill.
|
|
205
|
-
local error_loop_detected=false
|
|
206
|
-
local error_loop_signature=""
|
|
207
|
-
if [[ $effective_stale_kill_threshold -gt 0 && $growth -gt 0 && -f "$progress_json" ]]; then
|
|
208
|
-
local error_loop_flag
|
|
209
|
-
error_loop_flag=$(python3 - "$progress_json" <<'PY' 2>/dev/null || true
|
|
210
|
-
import json, sys
|
|
211
|
-
try:
|
|
212
|
-
with open(sys.argv[1], encoding="utf-8") as fh:
|
|
213
|
-
progress = json.load(fh)
|
|
214
|
-
except Exception:
|
|
215
|
-
raise SystemExit(0)
|
|
216
|
-
errors = progress.get("errors", [])
|
|
217
|
-
if isinstance(errors, list) and len(errors) >= 5:
|
|
218
|
-
recent = errors[-5:]
|
|
219
|
-
if all(isinstance(e, dict) and e.get("type") in ("read_offset_overflow", "wasted_call") for e in recent):
|
|
220
|
-
print(json.dumps(recent, sort_keys=True, separators=(",", ":")))
|
|
221
|
-
PY
|
|
222
|
-
)
|
|
223
|
-
if [[ -n "$error_loop_flag" ]]; then
|
|
224
|
-
error_loop_signature="$error_loop_flag"
|
|
225
|
-
if [[ -n "$prev_error_loop_signature" && "$error_loop_signature" != "$prev_error_loop_signature" ]]; then
|
|
226
|
-
error_loop_detected=true
|
|
227
|
-
fi
|
|
228
|
-
fi
|
|
229
|
-
fi
|
|
230
|
-
prev_error_loop_signature="$error_loop_signature"
|
|
231
|
-
|
|
232
199
|
# Track progress staleness. Parent sessions can sit in a wait/polling
|
|
233
200
|
# tool while child transcripts or structured progress counters keep
|
|
234
201
|
# advancing, so both child activity and progress.json state changes
|
|
235
|
-
# count as progress.
|
|
236
|
-
# because the log is only growing with repeated failed reads or wasted
|
|
237
|
-
# calls.
|
|
202
|
+
# count as progress.
|
|
238
203
|
#
|
|
239
204
|
# When the main session log has been truncated (e.g. by subagent stdout
|
|
240
205
|
# redirection severing the file descriptor), use structured progress and
|
|
241
206
|
# child activity as the primary progress signals — the parent process is
|
|
242
207
|
# still running and children may still be producing output.
|
|
243
|
-
if [[ "$
|
|
244
|
-
stale_reason="error_loop"
|
|
245
|
-
stale_seconds=$effective_stale_kill_threshold
|
|
246
|
-
elif [[ "$log_truncated" == "true" ]]; then
|
|
247
|
-
stale_reason="stale_session"
|
|
208
|
+
if [[ "$log_truncated" == "true" ]]; then
|
|
248
209
|
if [[ $progress_advanced -gt 0 || $child_growth -gt 0 ]]; then
|
|
249
210
|
stale_seconds=0
|
|
250
211
|
else
|
|
251
212
|
stale_seconds=$((stale_seconds + heartbeat_interval))
|
|
252
213
|
fi
|
|
253
214
|
elif [[ $growth -le 0 && $child_growth -eq 0 && $progress_advanced -eq 0 ]]; then
|
|
254
|
-
stale_reason="stale_session"
|
|
255
215
|
stale_seconds=$((stale_seconds + heartbeat_interval))
|
|
256
216
|
else
|
|
257
|
-
stale_reason="stale_session"
|
|
258
217
|
stale_seconds=0
|
|
259
218
|
fi
|
|
260
219
|
|
|
@@ -338,13 +297,8 @@ PY
|
|
|
338
297
|
# stale window to surface stuck agents promptly.
|
|
339
298
|
if [[ $effective_stale_kill_threshold -gt 0 && $stale_seconds -ge $effective_stale_kill_threshold ]]; then
|
|
340
299
|
local stale_mins=$((stale_seconds / 60))
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
echo -e " ${RED}[HEARTBEAT]${NC} Killing AI CLI process $cli_pid (error loop)..."
|
|
344
|
-
else
|
|
345
|
-
echo -e " ${RED}[HEARTBEAT]${NC} ${mins}m${secs}s | log: ${size_display} | ${RED}STALE-KILL: no progress for ${stale_mins}m (threshold: ${effective_stale_kill_threshold}s)${NC}"
|
|
346
|
-
echo -e " ${RED}[HEARTBEAT]${NC} Killing AI CLI process $cli_pid (stale session)..."
|
|
347
|
-
fi
|
|
300
|
+
echo -e " ${RED}[HEARTBEAT]${NC} ${mins}m${secs}s | log: ${size_display} | ${RED}STALE-KILL: no progress for ${stale_mins}m (threshold: ${effective_stale_kill_threshold}s)${NC}"
|
|
301
|
+
echo -e " ${RED}[HEARTBEAT]${NC} Killing AI CLI process $cli_pid (stale session)..."
|
|
348
302
|
# Write the marker before killing. Some CLIs exit quickly, and the
|
|
349
303
|
# parent runner may stop this heartbeat process immediately after
|
|
350
304
|
# wait(1) returns.
|
|
@@ -144,11 +144,7 @@ except Exception:
|
|
|
144
144
|
pass
|
|
145
145
|
PY
|
|
146
146
|
)
|
|
147
|
-
|
|
148
|
-
log_warn "Session was killed by heartbeat monitor due to repeated read-offset/wasted-call errors"
|
|
149
|
-
else
|
|
150
|
-
log_warn "Session was stale-killed by heartbeat monitor (no progress for too long)"
|
|
151
|
-
fi
|
|
147
|
+
log_warn "Session was stale-killed by heartbeat monitor (no progress for too long)"
|
|
152
148
|
fi
|
|
153
149
|
|
|
154
150
|
local was_infra_error=false
|
|
@@ -179,11 +175,7 @@ PY
|
|
|
179
175
|
log_warn "Infrastructure errors are retried without consuming code retry budget"
|
|
180
176
|
session_status="infra_error"
|
|
181
177
|
elif [[ "$was_stale_killed" == true ]]; then
|
|
182
|
-
|
|
183
|
-
log_warn "Session killed due to repeated read-offset/wasted-call error loop"
|
|
184
|
-
else
|
|
185
|
-
log_warn "Session stale-killed (no progress for ${STALE_KILL_THRESHOLD}s)"
|
|
186
|
-
fi
|
|
178
|
+
log_warn "Session stale-killed (no progress for ${STALE_KILL_THRESHOLD}s)"
|
|
187
179
|
log_warn "Stale-killed sessions are treated as failed; dev branch is preserved for inspection"
|
|
188
180
|
session_status="crashed"
|
|
189
181
|
elif [[ $exit_code -ne 0 ]]; then
|
|
@@ -155,11 +155,7 @@ except Exception:
|
|
|
155
155
|
pass
|
|
156
156
|
PY
|
|
157
157
|
)
|
|
158
|
-
|
|
159
|
-
log_warn "Session was killed by heartbeat monitor due to repeated read-offset/wasted-call errors"
|
|
160
|
-
else
|
|
161
|
-
log_warn "Session was stale-killed by heartbeat monitor (no progress for too long)"
|
|
162
|
-
fi
|
|
158
|
+
log_warn "Session was stale-killed by heartbeat monitor (no progress for too long)"
|
|
163
159
|
fi
|
|
164
160
|
|
|
165
161
|
local was_infra_error=false
|
|
@@ -213,11 +209,7 @@ PY
|
|
|
213
209
|
log_warn "Infrastructure errors are retried without consuming code retry budget"
|
|
214
210
|
session_status="infra_error"
|
|
215
211
|
elif [[ "$was_stale_killed" == true ]]; then
|
|
216
|
-
|
|
217
|
-
log_warn "Session killed due to repeated read-offset/wasted-call error loop"
|
|
218
|
-
else
|
|
219
|
-
log_warn "Session stale-killed (no progress for ${STALE_KILL_THRESHOLD}s)"
|
|
220
|
-
fi
|
|
212
|
+
log_warn "Session stale-killed (no progress for ${STALE_KILL_THRESHOLD}s)"
|
|
221
213
|
log_warn "Heartbeat-killed sessions are treated as failed; dev branch is preserved for inspection"
|
|
222
214
|
session_status="crashed"
|
|
223
215
|
elif [[ $exit_code -ne 0 ]]; then
|
|
@@ -145,11 +145,7 @@ except Exception:
|
|
|
145
145
|
pass
|
|
146
146
|
PY
|
|
147
147
|
)
|
|
148
|
-
|
|
149
|
-
log_warn "Session was killed by heartbeat monitor due to repeated read-offset/wasted-call errors"
|
|
150
|
-
else
|
|
151
|
-
log_warn "Session was stale-killed by heartbeat monitor (no progress for too long)"
|
|
152
|
-
fi
|
|
148
|
+
log_warn "Session was stale-killed by heartbeat monitor (no progress for too long)"
|
|
153
149
|
fi
|
|
154
150
|
|
|
155
151
|
local was_infra_error=false
|
|
@@ -180,11 +176,7 @@ PY
|
|
|
180
176
|
log_warn "Infrastructure errors are retried without consuming code retry budget"
|
|
181
177
|
session_status="infra_error"
|
|
182
178
|
elif [[ "$was_stale_killed" == true ]]; then
|
|
183
|
-
|
|
184
|
-
log_warn "Session killed due to repeated read-offset/wasted-call error loop"
|
|
185
|
-
else
|
|
186
|
-
log_warn "Session stale-killed (no progress for ${STALE_KILL_THRESHOLD}s)"
|
|
187
|
-
fi
|
|
179
|
+
log_warn "Session stale-killed (no progress for ${STALE_KILL_THRESHOLD}s)"
|
|
188
180
|
log_warn "Stale-killed sessions are treated as failed; dev branch is preserved for inspection"
|
|
189
181
|
session_status="crashed"
|
|
190
182
|
elif [[ $exit_code -ne 0 ]]; then
|
|
@@ -291,12 +291,12 @@ function Invoke-PrizmPipeline {
|
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
function Write-PrizmStaleKillMarker {
|
|
294
|
-
param([string]$MarkerPath, [int]$StaleSeconds, [int]$Threshold
|
|
294
|
+
param([string]$MarkerPath, [int]$StaleSeconds, [int]$Threshold)
|
|
295
295
|
$markerDir = Split-Path $MarkerPath -Parent
|
|
296
296
|
if ($markerDir) { New-Item -ItemType Directory -Force -Path $markerDir | Out-Null }
|
|
297
297
|
[ordered]@{
|
|
298
298
|
killed_at = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
|
|
299
|
-
reason =
|
|
299
|
+
reason = 'stale_session'
|
|
300
300
|
stale_seconds = $StaleSeconds
|
|
301
301
|
threshold = $Threshold
|
|
302
302
|
} | ConvertTo-Json -Compress | Set-Content -Path $MarkerPath -Encoding UTF8
|
|
@@ -726,11 +726,9 @@ function Invoke-PrizmPipeline {
|
|
|
726
726
|
|
|
727
727
|
$elapsedSeconds = 0
|
|
728
728
|
$staleSeconds = 0
|
|
729
|
-
$staleReason = 'stale_session'
|
|
730
729
|
$previousLogSize = 0
|
|
731
730
|
$previousProgressSignature = ''
|
|
732
731
|
$previousChildActivitySignature = ''
|
|
733
|
-
$previousErrorLoopSignature = ''
|
|
734
732
|
$staleKillMarker = Join-Path $logsDir 'stale-kill.json'
|
|
735
733
|
$wasStaleKilled = $false
|
|
736
734
|
while ($true) {
|
|
@@ -755,42 +753,9 @@ function Invoke-PrizmPipeline {
|
|
|
755
753
|
|
|
756
754
|
$effectiveStaleKillThreshold = Get-PrizmEffectiveStaleKillThreshold -ProgressFile $progressJson -BaseThreshold $staleKillThreshold
|
|
757
755
|
|
|
758
|
-
|
|
759
|
-
# all read-offset errors or wasted calls. Only a newly advancing error
|
|
760
|
-
# signature counts; stale historical errors must not turn later normal output
|
|
761
|
-
# into a stale kill.
|
|
762
|
-
$errorLoopDetected = $false
|
|
763
|
-
$errorLoopSignature = ''
|
|
764
|
-
if ($effectiveStaleKillThreshold -gt 0 -and $growth -gt 0 -and (Test-Path $progressJson)) {
|
|
765
|
-
try {
|
|
766
|
-
$progress = Get-Content $progressJson -Raw | ConvertFrom-Json
|
|
767
|
-
$errors = $progress.errors
|
|
768
|
-
if ($errors -is [array] -and $errors.Count -ge 5) {
|
|
769
|
-
$recent = $errors[-5..-1]
|
|
770
|
-
$allBad = ($recent | Where-Object {
|
|
771
|
-
$_.type -in @("read_offset_overflow", "wasted_call")
|
|
772
|
-
}).Count -eq 5
|
|
773
|
-
if ($allBad) {
|
|
774
|
-
$errorLoopSignature = ($recent | ConvertTo-Json -Compress)
|
|
775
|
-
if ($previousErrorLoopSignature -and $errorLoopSignature -ne $previousErrorLoopSignature) {
|
|
776
|
-
$errorLoopDetected = $true
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
} catch {
|
|
781
|
-
# Ignore JSON parse errors — progress file may be incomplete or malformed
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
$previousErrorLoopSignature = $errorLoopSignature
|
|
785
|
-
|
|
786
|
-
if ($errorLoopDetected) {
|
|
787
|
-
$staleReason = 'error_loop'
|
|
788
|
-
$staleSeconds = $effectiveStaleKillThreshold
|
|
789
|
-
} elseif ($growth -gt 0 -or $childAdvanced -or $progressAdvanced) {
|
|
790
|
-
$staleReason = 'stale_session'
|
|
756
|
+
if ($growth -gt 0 -or $childAdvanced -or $progressAdvanced) {
|
|
791
757
|
$staleSeconds = 0
|
|
792
758
|
} else {
|
|
793
|
-
$staleReason = 'stale_session'
|
|
794
759
|
$staleSeconds += $heartbeatInterval
|
|
795
760
|
}
|
|
796
761
|
|
|
@@ -807,12 +772,8 @@ function Invoke-PrizmPipeline {
|
|
|
807
772
|
}
|
|
808
773
|
if ($effectiveStaleKillThreshold -gt 0 -and $staleSeconds -ge $effectiveStaleKillThreshold) {
|
|
809
774
|
$wasStaleKilled = $true
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
} else {
|
|
813
|
-
Write-PrizmWarn "Session stale-killed (no progress for ${effectiveStaleKillThreshold}s)"
|
|
814
|
-
}
|
|
815
|
-
Write-PrizmStaleKillMarker $staleKillMarker $staleSeconds $effectiveStaleKillThreshold $staleReason
|
|
775
|
+
Write-PrizmWarn "Session stale-killed (no progress for ${effectiveStaleKillThreshold}s)"
|
|
776
|
+
Write-PrizmStaleKillMarker $staleKillMarker $staleSeconds $effectiveStaleKillThreshold
|
|
816
777
|
Stop-PrizmSessionProcess $pidPath
|
|
817
778
|
if ($staleKillGraceSeconds -gt 0) { Start-Sleep -Seconds $staleKillGraceSeconds }
|
|
818
779
|
break
|
|
@@ -866,11 +827,7 @@ function Invoke-PrizmPipeline {
|
|
|
866
827
|
Write-PrizmWarn "AI session failed due to AI CLI/provider infrastructure error"
|
|
867
828
|
Write-PrizmWarn "Infrastructure errors are retried without consuming code retry budget"
|
|
868
829
|
} elseif ($wasStaleKilled -or (Test-Path $staleKillMarker)) {
|
|
869
|
-
|
|
870
|
-
Write-PrizmWarn "Session was killed by heartbeat monitor due to repeated read-offset/wasted-call errors"
|
|
871
|
-
} else {
|
|
872
|
-
Write-PrizmWarn "Session was stale-killed by heartbeat monitor (no progress for too long)"
|
|
873
|
-
}
|
|
830
|
+
Write-PrizmWarn "Session was stale-killed by heartbeat monitor (no progress for too long)"
|
|
874
831
|
Write-PrizmWarn "Heartbeat-killed sessions are treated as failed; dev branch is preserved for inspection"
|
|
875
832
|
} elseif ($exitCode -ne 0) {
|
|
876
833
|
Write-PrizmWarn "AI session exited with code $exitCode"
|