instar 1.3.29 → 1.3.31
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/.claude/skills/autonomous/hooks/autonomous-stop-hook.sh +32 -0
- package/dashboard/index.html +112 -0
- package/dashboard/process-health.js +504 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +22 -3
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/monitoring/FailureLedger.d.ts +11 -3
- package/dist/monitoring/FailureLedger.d.ts.map +1 -1
- package/dist/monitoring/FailureLedger.js +22 -5
- package/dist/monitoring/FailureLedger.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +6 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +48 -4
- package/dist/server/routes.js.map +1 -1
- package/package.json +2 -1
- package/scripts/analyze-release.js +21 -7
- package/src/data/builtin-manifest.json +62 -62
- package/src/scaffold/templates.ts +6 -0
- package/upgrades/1.3.30.md +35 -0
- package/upgrades/1.3.31.md +29 -0
- package/upgrades/side-effects/notify-on-stop-layer-a.md +31 -0
- package/upgrades/side-effects/process-health-dashboard-tab.md +59 -0
- package/upgrades/side-effects/release-readiness-visibility-pr1.md +26 -0
|
@@ -147,6 +147,32 @@ STARTED_AT=$(fm_get started_at)
|
|
|
147
147
|
COMPLETION_PROMISE=$(fm_get completion_promise)
|
|
148
148
|
COMPLETION_CONDITION=$(fm_get completion_condition)
|
|
149
149
|
GOAL_MODE=$(fm_get goal_mode) # "native" = the framework's own /goal loop drives completion
|
|
150
|
+
RUN_GOAL=$(fm_get goal)
|
|
151
|
+
|
|
152
|
+
# ── Layer A: notify-on-stop (2026-05-27 silent-stalls postmortem, Task 2) ──────
|
|
153
|
+
# When an autonomous run reaches a TERMINAL exit (completion / duration / emergency),
|
|
154
|
+
# send ONE plain-English Telegram to the run's report topic explaining why it
|
|
155
|
+
# stopped. Closes Justin's "a session either keeps going OR tells me why it
|
|
156
|
+
# stopped" requirement structurally: previously every terminal exit only echoed
|
|
157
|
+
# to stderr (the terminal the user can't see), so an autonomous run could end in
|
|
158
|
+
# silence. Best-effort + NON-BLOCKING — a delivery failure never blocks the exit.
|
|
159
|
+
# Fires at most once per run (the state file is removed right after each terminal
|
|
160
|
+
# exit, so the hook won't re-enter). Reuses telegram-reply.sh (the same transport
|
|
161
|
+
# the restart-resume recovery note uses), and only for the telegram channel.
|
|
162
|
+
goal_snippet() {
|
|
163
|
+
printf '%s' "${RUN_GOAL:-the autonomous run}" | tr '\n\t' ' ' | cut -c1-80
|
|
164
|
+
}
|
|
165
|
+
notify_terminal_stop() {
|
|
166
|
+
local msg="$1"
|
|
167
|
+
[[ -z "$REPORT_TOPIC" ]] && return 0
|
|
168
|
+
[[ "${REPORT_CHANNEL:-telegram}" != "telegram" ]] && return 0
|
|
169
|
+
local script=""
|
|
170
|
+
if [[ -x ".instar/scripts/telegram-reply.sh" ]]; then script=".instar/scripts/telegram-reply.sh"
|
|
171
|
+
elif [[ -x ".claude/scripts/telegram-reply.sh" ]]; then script=".claude/scripts/telegram-reply.sh"
|
|
172
|
+
fi
|
|
173
|
+
[[ -z "$script" ]] && return 0
|
|
174
|
+
printf '%s\n' "$msg" | "$script" "$REPORT_TOPIC" >/dev/null 2>&1 || true
|
|
175
|
+
}
|
|
150
176
|
|
|
151
177
|
# Validate recorded session_id is a real UUID. Claude sometimes writes a custom
|
|
152
178
|
# string instead of $CLAUDE_CODE_SESSION_ID; non-UUID values are treated as
|
|
@@ -245,12 +271,14 @@ if [[ "$GOAL_MODE" == "native" ]]; then
|
|
|
245
271
|
--data-binary @- "http://localhost:${port}/autonomous/native-goal/clear" >/dev/null 2>&1 || true
|
|
246
272
|
}
|
|
247
273
|
if [[ -f ".instar/autonomous-emergency-stop" ]]; then
|
|
274
|
+
notify_terminal_stop "🛑 My autonomous run on \"$(goal_snippet)\" was stopped (emergency stop)."
|
|
248
275
|
native_goal_clear; rm -f "$STATE_FILE"
|
|
249
276
|
echo "[autonomous] emergency stop — native /goal cleared" >&2; exit 0
|
|
250
277
|
fi
|
|
251
278
|
if [[ "$DURATION_SECONDS" =~ ^[0-9]+$ ]] && [[ $DURATION_SECONDS -gt 0 ]]; then
|
|
252
279
|
NG_START=$(date -u -j -f "%Y-%m-%dT%H:%M:%SZ" "$STARTED_AT" +%s 2>/dev/null || date -d "$STARTED_AT" +%s 2>/dev/null || echo 0)
|
|
253
280
|
if [[ "$NG_START" =~ ^[0-9]+$ ]] && [[ $NG_START -gt 0 ]] && [[ $(( $(date +%s) - NG_START )) -ge $DURATION_SECONDS ]]; then
|
|
281
|
+
notify_terminal_stop "⏰ My autonomous run on \"$(goal_snippet)\" hit its time limit and stopped. Ask me and I'll pick up where I left off."
|
|
254
282
|
native_goal_clear; rm -f "$STATE_FILE"
|
|
255
283
|
echo "[autonomous] duration expired — native /goal cleared" >&2; exit 0
|
|
256
284
|
fi
|
|
@@ -280,6 +308,7 @@ if [[ "$DURATION_SECONDS" =~ ^[0-9]+$ ]] && [[ $DURATION_SECONDS -gt 0 ]]; then
|
|
|
280
308
|
if [[ $ELAPSED -ge $DURATION_SECONDS ]]; then
|
|
281
309
|
echo "⏰ Autonomous mode: Duration expired ($ELAPSED seconds elapsed)."
|
|
282
310
|
echo " Session is free to exit."
|
|
311
|
+
notify_terminal_stop "⏰ My autonomous run on \"$(goal_snippet)\" just hit its time limit and stopped. Ask me and I'll pick up where I left off."
|
|
283
312
|
rm -f "$STATE_FILE"
|
|
284
313
|
exit 0
|
|
285
314
|
fi
|
|
@@ -293,6 +322,7 @@ fi
|
|
|
293
322
|
# Emergency stop (global — halts every autonomous job on its next fire)
|
|
294
323
|
if [[ -f ".instar/autonomous-emergency-stop" ]]; then
|
|
295
324
|
echo "🛑 Autonomous mode: Emergency stop detected."
|
|
325
|
+
notify_terminal_stop "🛑 My autonomous run on \"$(goal_snippet)\" was stopped (emergency stop)."
|
|
296
326
|
rm -f "$STATE_FILE"
|
|
297
327
|
# NOTE: the emergency flag is left in place so OTHER topics' hooks also see it
|
|
298
328
|
# and clear their own per-topic files; stop-all clears the flag when complete.
|
|
@@ -323,6 +353,7 @@ if [[ -n "$COMPLETION_CONDITION" ]] && [[ -n "$TRANSCRIPT_PATH" ]] && [[ -f "$TR
|
|
|
323
353
|
fi
|
|
324
354
|
if [[ "$EVAL_MET" == "true" ]]; then
|
|
325
355
|
echo "✅ Autonomous mode: completion condition met (independent evaluator): ${EVAL_REASON}"
|
|
356
|
+
notify_terminal_stop "✅ My autonomous run on \"$(goal_snippet)\" finished — the goal was met."
|
|
326
357
|
rm -f "$STATE_FILE"
|
|
327
358
|
exit 0
|
|
328
359
|
fi
|
|
@@ -341,6 +372,7 @@ if [[ -n "$TRANSCRIPT_PATH" ]] && [[ -f "$TRANSCRIPT_PATH" ]]; then
|
|
|
341
372
|
if [[ -n "$PROMISE_TEXT" ]] && [[ "$PROMISE_TEXT" = "$COMPLETION_PROMISE" ]]; then
|
|
342
373
|
echo "✅ Autonomous mode: Completion promise detected — <promise>$COMPLETION_PROMISE</promise>"
|
|
343
374
|
echo " Session is free to exit. Good work!"
|
|
375
|
+
notify_terminal_stop "✅ My autonomous run on \"$(goal_snippet)\" finished — all the work is done."
|
|
344
376
|
rm -f "$STATE_FILE"
|
|
345
377
|
exit 0
|
|
346
378
|
fi
|
package/dashboard/index.html
CHANGED
|
@@ -777,6 +777,50 @@
|
|
|
777
777
|
display: none !important;
|
|
778
778
|
}
|
|
779
779
|
|
|
780
|
+
/* ── process-health tab (PROCESS-HEALTH-DASHBOARD-TAB-SPEC §4.1) ──
|
|
781
|
+
Large, readable, calm. No monospace, no tables, no alarm colors.
|
|
782
|
+
Measurable bars: headline ≥24px, section headers ≥20px, body ≥17px,
|
|
783
|
+
line-height 1.5–1.6. One neutral accent token, used ONLY for the
|
|
784
|
+
maturation "you're here" marker. */
|
|
785
|
+
.ph-root {
|
|
786
|
+
--ph-accent: #4ade80;
|
|
787
|
+
max-width: 760px;
|
|
788
|
+
font-size: 18px;
|
|
789
|
+
line-height: 1.55;
|
|
790
|
+
color: var(--text-bright);
|
|
791
|
+
}
|
|
792
|
+
.ph-title { font-size: 22px; font-weight: 600; color: var(--text-bright); line-height: 1.5; }
|
|
793
|
+
.ph-stamp { font-size: 14px; color: var(--text-dim); }
|
|
794
|
+
.ph-headline-wrap { margin: 4px 0 8px; }
|
|
795
|
+
.ph-headline { font-size: 26px; font-weight: 600; line-height: 1.5; color: var(--text-bright); }
|
|
796
|
+
.ph-subline { font-size: 18px; color: var(--text-dim); margin-top: 8px; line-height: 1.55; }
|
|
797
|
+
.ph-section { display: flex; flex-direction: column; gap: 10px; }
|
|
798
|
+
.ph-h { font-size: 20px; font-weight: 600; color: var(--text-bright); line-height: 1.5; }
|
|
799
|
+
.ph-empty { font-size: 18px; color: var(--text-dim); line-height: 1.6; }
|
|
800
|
+
.ph-card {
|
|
801
|
+
padding: 14px 16px; border: 1px solid var(--border); border-radius: 10px;
|
|
802
|
+
display: flex; flex-direction: column; gap: 6px;
|
|
803
|
+
}
|
|
804
|
+
.ph-card-summary { font-size: 18px; color: var(--text-bright); line-height: 1.5; }
|
|
805
|
+
.ph-card-frame { font-size: 15px; color: var(--text-dim); font-style: italic; }
|
|
806
|
+
.ph-card-rec { font-size: 17px; color: var(--text); line-height: 1.55; }
|
|
807
|
+
.ph-card-evidence { font-size: 15px; color: var(--text-dim); }
|
|
808
|
+
.ph-record { font-size: 18px; color: var(--text); line-height: 1.6; padding: 5px 0; }
|
|
809
|
+
.ph-record-inert { color: var(--text-dim); }
|
|
810
|
+
.ph-stage { display: flex; align-items: baseline; gap: 10px; font-size: 18px; padding: 3px 0; color: var(--text-dim); line-height: 1.5; }
|
|
811
|
+
.ph-stage-here { color: var(--text-bright); }
|
|
812
|
+
.ph-stage-glyph { color: var(--text-dim); }
|
|
813
|
+
.ph-stage-here .ph-stage-glyph { color: var(--ph-accent); }
|
|
814
|
+
.ph-stage-marker { color: var(--ph-accent); font-size: 15px; }
|
|
815
|
+
.ph-stage-note { color: var(--text-dim); font-size: 15px; }
|
|
816
|
+
.ph-detail { margin-top: 4px; }
|
|
817
|
+
.ph-detail-summary { font-size: 17px; color: var(--text-dim); cursor: pointer; }
|
|
818
|
+
.ph-detail-body { display: flex; flex-direction: column; gap: 6px; margin-top: 12px; }
|
|
819
|
+
.ph-detail-row { font-size: 17px; color: var(--text); line-height: 1.55; }
|
|
820
|
+
.ph-operator { margin-top: 14px; }
|
|
821
|
+
.ph-operator-summary { font-size: 15px; color: var(--text-dim); cursor: pointer; }
|
|
822
|
+
.ph-operator-hint { font-size: 16px; color: var(--text-dim); margin-top: 8px; line-height: 1.55; }
|
|
823
|
+
|
|
780
824
|
/* ── File viewer ─────────────────────────────────────── */
|
|
781
825
|
.files-container {
|
|
782
826
|
display: flex;
|
|
@@ -2529,6 +2573,7 @@
|
|
|
2529
2573
|
<button class="tab" data-tab="tokens" onclick="switchTab('tokens')">Tokens</button>
|
|
2530
2574
|
<button class="tab" data-tab="threadline" onclick="switchTab('threadline')">Threadline</button>
|
|
2531
2575
|
<button class="tab" data-tab="evidence" onclick="switchTab('evidence')">Evidence</button>
|
|
2576
|
+
<button class="tab" data-tab="process-health" onclick="switchTab('process-health')">Process Health</button>
|
|
2532
2577
|
</nav>
|
|
2533
2578
|
</div>
|
|
2534
2579
|
<div class="vital-signs" id="vitalSigns">
|
|
@@ -3012,6 +3057,31 @@
|
|
|
3012
3057
|
|
|
3013
3058
|
<!-- Evidence Tab (WikiClaim Phase 4) — per-entity evidence + reverse "what cites this?" view.
|
|
3014
3059
|
Spec: docs/specs/OPENCLAW-IMPORT-WIKICLAIM-EVIDENCE-SPEC.md § Phase 4 (line 343). -->
|
|
3060
|
+
<!-- process-health tab -->
|
|
3061
|
+
<div id="processHealthPanel" class="ph-root" style="display:none;flex-direction:column;padding:28px;gap:24px;overflow-y:auto">
|
|
3062
|
+
<div style="display:flex;justify-content:space-between;align-items:baseline">
|
|
3063
|
+
<h2 class="ph-title">Process Health</h2>
|
|
3064
|
+
<div id="phStamp" class="ph-stamp" aria-live="polite"></div>
|
|
3065
|
+
</div>
|
|
3066
|
+
<div id="phHeadline" class="ph-headline-wrap" aria-live="polite"></div>
|
|
3067
|
+
<section class="ph-section">
|
|
3068
|
+
<h3 class="ph-h">Patterns to know about</h3>
|
|
3069
|
+
<div id="phPatterns"></div>
|
|
3070
|
+
</section>
|
|
3071
|
+
<section class="ph-section">
|
|
3072
|
+
<h3 class="ph-h">What’s been captured</h3>
|
|
3073
|
+
<div id="phCaptured"></div>
|
|
3074
|
+
</section>
|
|
3075
|
+
<section class="ph-section">
|
|
3076
|
+
<h3 class="ph-h">Maturation</h3>
|
|
3077
|
+
<div id="phMaturation"></div>
|
|
3078
|
+
</section>
|
|
3079
|
+
<details class="ph-detail">
|
|
3080
|
+
<summary class="ph-detail-summary">Detail</summary>
|
|
3081
|
+
<div id="phDetail" class="ph-detail-body"></div>
|
|
3082
|
+
</details>
|
|
3083
|
+
</div>
|
|
3084
|
+
|
|
3015
3085
|
<div id="evidenceTab" style="display:none;flex-direction:column;padding:20px;gap:16px;overflow-y:auto">
|
|
3016
3086
|
<div style="display:flex;justify-content:space-between;align-items:center">
|
|
3017
3087
|
<h2 style="margin:0">Evidence</h2>
|
|
@@ -4244,6 +4314,13 @@
|
|
|
4244
4314
|
display: ['flex'],
|
|
4245
4315
|
onActivate: null,
|
|
4246
4316
|
},
|
|
4317
|
+
{
|
|
4318
|
+
id: 'process-health',
|
|
4319
|
+
panels: ['processHealthPanel'],
|
|
4320
|
+
display: ['flex'],
|
|
4321
|
+
onActivate: () => { if (typeof startProcessHealth === 'function') startProcessHealth(); },
|
|
4322
|
+
onDeactivate: () => { if (typeof stopProcessHealth === 'function') stopProcessHealth(); },
|
|
4323
|
+
},
|
|
4247
4324
|
{
|
|
4248
4325
|
id: 'secrets',
|
|
4249
4326
|
panels: ['secretsPanel'],
|
|
@@ -7973,13 +8050,48 @@
|
|
|
7973
8050
|
return m + 'm';
|
|
7974
8051
|
}
|
|
7975
8052
|
|
|
8053
|
+
// ── Process Health tab (PROCESS-HEALTH-DASHBOARD-TAB-SPEC) ──
|
|
8054
|
+
// Lazy dynamic-import keeps the controller in this (classic) scope so it can
|
|
8055
|
+
// read `token`; the module ships the pure renderers + the polling controller.
|
|
8056
|
+
let __phController = null;
|
|
8057
|
+
let __phModule = null;
|
|
8058
|
+
async function startProcessHealth() {
|
|
8059
|
+
if (!__phModule) {
|
|
8060
|
+
try { __phModule = await import('/dashboard/process-health.js'); }
|
|
8061
|
+
catch (e) { console.error('[process-health] module load failed', e); return; }
|
|
8062
|
+
}
|
|
8063
|
+
if (!__phController) {
|
|
8064
|
+
const els = {
|
|
8065
|
+
headline: document.getElementById('phHeadline'),
|
|
8066
|
+
patterns: document.getElementById('phPatterns'),
|
|
8067
|
+
captured: document.getElementById('phCaptured'),
|
|
8068
|
+
maturation: document.getElementById('phMaturation'),
|
|
8069
|
+
detail: document.getElementById('phDetail'),
|
|
8070
|
+
stamp: document.getElementById('phStamp'),
|
|
8071
|
+
};
|
|
8072
|
+
// Raw fetch (NOT apiFetch) so the controller sees 304/503/ETag itself.
|
|
8073
|
+
const fetchImpl = (url, opts = {}) => fetch(url, {
|
|
8074
|
+
headers: { 'Authorization': `Bearer ${token}`, ...(opts.headers || {}) },
|
|
8075
|
+
signal: opts.signal,
|
|
8076
|
+
});
|
|
8077
|
+
__phController = __phModule.createController({ doc: document, els, fetchImpl });
|
|
8078
|
+
}
|
|
8079
|
+
__phController.start();
|
|
8080
|
+
}
|
|
8081
|
+
function stopProcessHealth() {
|
|
8082
|
+
if (__phController) __phController.stop();
|
|
8083
|
+
}
|
|
8084
|
+
|
|
7976
8085
|
// Handle tab visibility — close SSE when tab is backgrounded
|
|
7977
8086
|
document.addEventListener('visibilitychange', () => {
|
|
7978
8087
|
if (document.hidden) {
|
|
7979
8088
|
disconnectJobsSSE();
|
|
8089
|
+
if (currentTab === 'process-health') stopProcessHealth();
|
|
7980
8090
|
} else if (currentTab === 'jobs') {
|
|
7981
8091
|
connectJobsSSE();
|
|
7982
8092
|
loadJobs(); // Resync on return
|
|
8093
|
+
} else if (currentTab === 'process-health') {
|
|
8094
|
+
startProcessHealth(); // resume on return: one immediate refresh + re-arm
|
|
7983
8095
|
}
|
|
7984
8096
|
// Always refresh vital signs on tab focus
|
|
7985
8097
|
if (!document.hidden) pollVitalSigns();
|