@cohortapp/agent-sdk 2.18.13 → 2.18.14

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.
@@ -1,24 +1,109 @@
1
1
  #!/bin/bash
2
2
  # Health Check — Verifies all Maestro agent subsystems are operational
3
- # Usage: ./scripts/healthcheck.sh
4
- # Exit codes: 0 = healthy, 1 = degraded, 2 = critical
3
+ # Usage: ./scripts/healthcheck.sh [--ignore-emergency-stop] [--offline] [--help]
4
+ # Exit codes: 0 = healthy, 1 = degraded (warnings only), 2 = critical (errors)
5
+ #
6
+ # A refusal NAMES what blocked it. The summary always prints a `Blocking:`
7
+ # line listing the id of every failed check (and a `Warnings:` line for the
8
+ # soft ones), because the operator's next question after "1 errors" is always
9
+ # "which one?" — and on 2026-09-24 answering it cost a re-run with a filter
10
+ # while a seat stayed halted.
11
+ #
12
+ # --ignore-emergency-stop
13
+ # Demote check 1 (the .emergency-stop flag) from ERROR to NOTE. It is meant
14
+ # for one caller: resume-operations.sh, whose whole job is to lift that
15
+ # flag. Counting the flag as a reason not to lift the flag deadlocked the
16
+ # only tool that can lift it (a seat sat halted from 2026-09-24T08:16Z
17
+ # until it was cleared by hand).
18
+ #
19
+ # SCOPE, PLAINLY. This is a PUBLIC CLI flag. Any caller, script or operator
20
+ # can pass it; nothing refuses it, and nothing here pretends to. An earlier
21
+ # draft of this header said the exemption "is refused to everyone else" —
22
+ # it never was. The scoping is convention plus this comment, and a gate that
23
+ # exists only in a comment is exactly the thing this file was repaired for.
24
+ #
25
+ # What keeps it honest is scope, not exclusivity:
26
+ # - It demotes exactly ONE check. Every other check runs at full severity,
27
+ # so an exempted run still cannot green-light a broken seat.
28
+ # - It is off by default, so an operator running healthcheck on a halted
29
+ # seat is still told it is halted — the first thing they need to know.
30
+ # - It is explicit, greppable, and reviewable at its single call site.
31
+ # - The demotion is ANNOUNCED in the output ([NOTE] ...). That line is the
32
+ # receipt resume-operations.sh checks before it trusts the verdict, so a
33
+ # run where the flag was silently swallowed is caught rather than obeyed.
34
+ # Requiring a second token (an env var alongside the flag) would add another
35
+ # thing to forge, not a boundary: anyone who can run this script can set an
36
+ # env var. The real boundary is permission to execute it at all.
37
+ #
38
+ # The alternatives are worse on their merits, not on access control:
39
+ # - Dropping the check outright, or having it always ignore the flag, is
40
+ # wrong for the same reason as above: a halted seat must say so.
41
+ # - Re-ordering resume (remove flag, then check) is worse still: it lifts
42
+ # the stop on a genuinely broken seat and only then discovers it is
43
+ # broken, which is the failure the stop flag exists to prevent.
44
+ #
45
+ # --offline (or MAESTRO_HEALTHCHECK_SKIP_NETWORK=1)
46
+ # Record check 6 as SKIPPED instead of reaching the network. Not a weakening:
47
+ # the check is neither counted healthy nor counted failed, and the summary
48
+ # says it was skipped.
5
49
 
6
50
  set -e
7
51
 
8
52
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
- SOPHIE_AI_DIR="$(dirname "$SCRIPT_DIR")"
53
+ AGENT_DIR="$(dirname "$SCRIPT_DIR")"
10
54
  TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
11
- HEALTHY=0
12
55
  WARNINGS=0
13
56
  ERRORS=0
57
+ BLOCKING="" # space-separated ids of failed checks
58
+ SOFT="" # space-separated ids of warned checks
59
+
60
+ IGNORE_STOP_FLAG=0
61
+ SKIP_NETWORK=0
62
+ case "${MAESTRO_HEALTHCHECK_SKIP_NETWORK:-0}" in 1 | true | yes) SKIP_NETWORK=1 ;; esac
63
+
64
+ while [ $# -gt 0 ]; do
65
+ case "$1" in
66
+ --ignore-emergency-stop) IGNORE_STOP_FLAG=1 ;;
67
+ --offline) SKIP_NETWORK=1 ;;
68
+ -h | --help)
69
+ sed -n '2,/^$/p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
70
+ exit 0
71
+ ;;
72
+ *)
73
+ echo "healthcheck: unknown argument: $1" >&2
74
+ echo "Usage: healthcheck.sh [--ignore-emergency-stop] [--offline]" >&2
75
+ exit 2
76
+ ;;
77
+ esac
78
+ shift
79
+ done
80
+
81
+ # fail <id> <message> — record a blocking condition under a stable id.
82
+ fail() {
83
+ echo "[FAIL] $2"
84
+ BLOCKING="$BLOCKING $1"
85
+ ERRORS=$((ERRORS + 1))
86
+ }
87
+
88
+ # warn <id> <message> — record a soft condition under a stable id.
89
+ warn() {
90
+ echo "[WARN] $2"
91
+ SOFT="$SOFT $1"
92
+ WARNINGS=$((WARNINGS + 1))
93
+ }
14
94
 
15
95
  echo "Maestro Health Check — $TIMESTAMP"
16
96
  echo "======================================="
17
97
 
18
98
  # Check 1: Emergency stop flag
19
- if [ -f "$SOPHIE_AI_DIR/.emergency-stop" ]; then
20
- echo "[STOP] Emergency stop flag is active"
21
- ERRORS=$((ERRORS + 1))
99
+ if [ -f "$AGENT_DIR/.emergency-stop" ]; then
100
+ if [ "$IGNORE_STOP_FLAG" -eq 1 ]; then
101
+ echo "[NOTE] Emergency stop flag is active — not counted (--ignore-emergency-stop)"
102
+ else
103
+ echo "[STOP] Emergency stop flag is active"
104
+ BLOCKING="$BLOCKING emergency-stop-flag"
105
+ ERRORS=$((ERRORS + 1))
106
+ fi
22
107
  else
23
108
  echo "[OK] No emergency stop flag"
24
109
  fi
@@ -30,22 +115,20 @@ fi
30
115
  # in .env strips the var and falls back to Keychain OAuth).
31
116
  if [ -n "${ANTHROPIC_API_KEY:-}" ]; then
32
117
  echo "[OK] Environment variable: ANTHROPIC_API_KEY"
33
- elif [ -f "$SOPHIE_AI_DIR/.env" ] && grep -qE "^MAESTRO_PREFER_SUBSCRIPTION_AUTH=(1|true|yes)$" "$SOPHIE_AI_DIR/.env"; then
118
+ elif [ -f "$AGENT_DIR/.env" ] && grep -qE "^MAESTRO_PREFER_SUBSCRIPTION_AUTH=(1|true|yes)$" "$AGENT_DIR/.env"; then
34
119
  echo "[OK] Anthropic auth: subscription mode (.env: MAESTRO_PREFER_SUBSCRIPTION_AUTH=1)"
35
- elif [ -f "$SOPHIE_AI_DIR/.env" ] && grep -qE "^ANTHROPIC_API_KEY=." "$SOPHIE_AI_DIR/.env"; then
120
+ elif [ -f "$AGENT_DIR/.env" ] && grep -qE "^ANTHROPIC_API_KEY=." "$AGENT_DIR/.env"; then
36
121
  echo "[OK] Anthropic auth: ANTHROPIC_API_KEY in .env"
37
122
  else
38
- echo "[FAIL] Anthropic auth not configured (.env needs either ANTHROPIC_API_KEY or MAESTRO_PREFER_SUBSCRIPTION_AUTH=1)"
39
- ERRORS=$((ERRORS + 1))
123
+ fail "anthropic-auth" "Anthropic auth not configured (.env needs either ANTHROPIC_API_KEY or MAESTRO_PREFER_SUBSCRIPTION_AUTH=1)"
40
124
  fi
41
125
 
42
126
  # Check 3: Key directories exist and are writable
43
127
  for dir in logs outputs knowledge/memory config; do
44
- if [ -d "$SOPHIE_AI_DIR/$dir" ] && [ -w "$SOPHIE_AI_DIR/$dir" ]; then
128
+ if [ -d "$AGENT_DIR/$dir" ] && [ -w "$AGENT_DIR/$dir" ]; then
45
129
  echo "[OK] Directory writable: $dir"
46
130
  else
47
- echo "[FAIL] Directory missing or not writable: $dir"
48
- ERRORS=$((ERRORS + 1))
131
+ fail "dir:$dir" "Directory missing or not writable: $dir"
49
132
  fi
50
133
  done
51
134
 
@@ -54,8 +137,7 @@ for repo in company-web innovation-lab partner-framework company-legal regulator
54
137
  if [ -d "$HOME/$repo" ]; then
55
138
  echo "[OK] Source repo accessible: ~/$repo"
56
139
  else
57
- echo "[WARN] Source repo not found: ~/$repo"
58
- WARNINGS=$((WARNINGS + 1))
140
+ warn "repo:$repo" "Source repo not found: ~/$repo"
59
141
  fi
60
142
  done
61
143
 
@@ -64,45 +146,61 @@ for app in Slack WhatsApp Safari; do
64
146
  if [ -d "/Applications/$app.app" ] || [ -d "$HOME/Applications/$app.app" ]; then
65
147
  echo "[OK] Application installed: $app"
66
148
  else
67
- echo "[WARN] Application not found: $app"
68
- WARNINGS=$((WARNINGS + 1))
149
+ warn "app:$app" "Application not found: $app"
69
150
  fi
70
151
  done
71
152
 
72
153
  # Check 6: Network connectivity
73
- if curl -s --max-time 5 https://api.anthropic.com > /dev/null 2>&1; then
154
+ if [ "$SKIP_NETWORK" -eq 1 ]; then
155
+ echo "[SKIP] Network: not probed (--offline)"
156
+ elif curl -s --max-time 5 https://api.anthropic.com > /dev/null 2>&1; then
74
157
  echo "[OK] Network: Anthropic API reachable"
75
158
  else
76
- echo "[FAIL] Network: Cannot reach Anthropic API"
77
- ERRORS=$((ERRORS + 1))
159
+ fail "network" "Network: Cannot reach Anthropic API"
78
160
  fi
79
161
 
80
- # Check 7: Disk space
81
- DISK_FREE=$(df -h "$SOPHIE_AI_DIR" | awk 'NR==2{print $5}' | tr -d '%')
82
- if [ "$DISK_FREE" -gt 90 ]; then
83
- echo "[WARN] Disk usage: ${DISK_FREE}% — consider cleanup"
84
- WARNINGS=$((WARNINGS + 1))
85
- elif [ "$DISK_FREE" -gt 95 ]; then
86
- echo "[FAIL] Disk usage: ${DISK_FREE}% — critically low"
87
- ERRORS=$((ERRORS + 1))
162
+ # Check 7: Disk space. Ordered most-severe-first: the previous order tested
163
+ # >90 before >95, so the critical branch could never be reached and disk was
164
+ # warn-only in practice. Reaching it is a REAL severity change with a real
165
+ # consequence — resume-operations.sh refuses on any exit 2, so a seat above
166
+ # 95% can no longer be resumed until space is freed. That is deliberate, and
167
+ # it is not the deadlock this trio was repaired for: unlike the stop flag
168
+ # (which only resume could lift), a full disk is named, actionable, and
169
+ # fixable without running this script. Both branches — >95 blocking and >90
170
+ # warning — are pinned in resume-operations.test.mjs, at the healthcheck and
171
+ # at the resume gate, because they are the only severities that changed.
172
+ DISK_FREE=$(df -h "$AGENT_DIR" | awk 'NR==2{print $5}' | tr -d '%')
173
+ if [ "$DISK_FREE" -gt 95 ]; then
174
+ fail "disk" "Disk usage: ${DISK_FREE}% — critically low"
175
+ elif [ "$DISK_FREE" -gt 90 ]; then
176
+ warn "disk" "Disk usage: ${DISK_FREE}% — consider cleanup"
88
177
  else
89
178
  echo "[OK] Disk usage: ${DISK_FREE}%"
90
179
  fi
91
180
 
92
181
  # Check 8: Config files present
93
182
  for config in priorities.yaml environment.yaml contacts.yaml; do
94
- if [ -f "$SOPHIE_AI_DIR/config/$config" ]; then
183
+ if [ -f "$AGENT_DIR/config/$config" ]; then
95
184
  echo "[OK] Config present: $config"
96
185
  else
97
- echo "[FAIL] Config missing: $config"
98
- ERRORS=$((ERRORS + 1))
186
+ fail "config:$config" "Config missing: $config"
99
187
  fi
100
188
  done
101
189
 
102
- # Summary
190
+ # Summary. `Blocking:` / `Warnings:` are the machine-readable contract:
191
+ # resume-operations.sh parses `Blocking:` to name the condition it refused on.
103
192
  echo ""
104
193
  echo "======================================="
105
194
  echo "Results: $ERRORS errors, $WARNINGS warnings"
195
+ # `if`, not `[ ... ] && echo` — but NOT for the reason first written here,
196
+ # which was false. `set -e` does NOT abort on a false short-circuit: bash
197
+ # exempts a failing command that is not the last in an && list. MEASURED:
198
+ # bash -c 'set -e; n=0; [ "$n" -gt 0 ] && echo hi; exit 0' → exit 0
199
+ # The genuine hazard is only that a false && list leaves status 1, which a
200
+ # script inherits if it is the last command run. The explicit exits below
201
+ # make that moot here; `if` is kept because it says what is meant.
202
+ if [ "$ERRORS" -gt 0 ]; then echo "Blocking:$BLOCKING"; fi
203
+ if [ "$WARNINGS" -gt 0 ]; then echo "Warnings:$SOFT"; fi
106
204
 
107
205
  if [ "$ERRORS" -gt 0 ]; then
108
206
  echo "Status: CRITICAL — fix errors before operating"
@@ -3,12 +3,34 @@
3
3
  # Usage: ./scripts/resume-operations.sh
4
4
  #
5
5
  # Reverses emergency-stop.sh:
6
- # 1. Verifies health check passes.
6
+ # 1. Verifies health check passes — EXCLUDING the stop flag itself.
7
7
  # 2. Removes the .emergency-stop flag.
8
8
  # 3. Reloads every installed `ai.maestro.<agent>-*` (and legacy
9
9
  # `ai.adaptic.<agent>-*`) launchd job.
10
10
  #
11
11
  # Agent first-name slug resolved from config/agent.json (SOT).
12
+ #
13
+ # THE DEADLOCK THIS SCRIPT USED TO HAVE. healthcheck.sh counts an active
14
+ # .emergency-stop flag as an ERROR; this script ran it bare and exited on any
15
+ # non-zero. So the one tool whose entire purpose is lifting the stop could
16
+ # never lift it — Eli Rosenberg's seat sat halted from 2026-09-24T08:16Z until
17
+ # the flag was removed by hand. Two independent faults produced that:
18
+ #
19
+ # (a) The stop flag was its own veto. Fixed by passing
20
+ # `--ignore-emergency-stop`, which demotes that ONE check to a note.
21
+ # It is a PUBLIC CLI flag — nothing refuses it to another caller, and
22
+ # nothing here pretends to (healthcheck.sh's header says so plainly).
23
+ # What makes it safe is scope, not exclusivity: it demotes exactly one
24
+ # check, every other check still gates the resume, and step 1b below
25
+ # VERIFIES that the healthcheck on this disk actually honoured it
26
+ # rather than assuming the two files are the same vintage.
27
+ # (b) DEGRADED was treated as failure. healthcheck exits 1 for warnings and
28
+ # 2 for errors; `if ! healthcheck` refused on both, so seven warnings
29
+ # about optional repos and an uninstalled Slack.app were enough to keep
30
+ # a seat halted. Warnings are "operational with limitations" by
31
+ # healthcheck's own contract — they are reported loudly and resumed.
32
+ # Only CRITICAL (exit 2) still refuses, and the refusal names which
33
+ # condition blocked it.
12
34
 
13
35
  set -e
14
36
 
@@ -46,19 +68,92 @@ if [ ! -f "$AGENT_DIR/.emergency-stop" ]; then
46
68
  launchctl load "$plist" 2>/dev/null && loaded=$((loaded + 1))
47
69
  fi
48
70
  done
49
- [ "$loaded" -gt 0 ] && echo "Loaded $loaded missing launchd job(s)"
71
+ # `if`, not `[ "$loaded" -gt 0 ] && echo ...` — but NOT for the reason
72
+ # first written here, which was wrong and would have seeded a wrong rule.
73
+ # `set -e` does not abort on the false short-circuit: bash exempts a
74
+ # failing command that is not the last in an && list. MEASURED:
75
+ # bash -c 'set -e; n=0; [ "$n" -gt 0 ] && echo hi; exit 0' → exit 0
76
+ # and the pre-fix script (which had `exit 0` on the next line) also
77
+ # exited 0 on a seat with nothing to load. The real hazard is narrower
78
+ # and has nothing to do with `set -e`: a false && list leaves status 1,
79
+ # so a script whose LAST command is one exits 1. `exit 0` follows today;
80
+ # the `if` keeps this line harmless if it ever stops following.
81
+ if [ "$loaded" -gt 0 ]; then echo "Loaded $loaded missing launchd job(s)"; fi
50
82
  exit 0
51
83
  fi
52
84
 
53
85
  echo "[$TIMESTAMP] RESUMING OPERATIONS (agent=$AGENT_FIRST)" | tee -a "$LOG_FILE"
54
86
 
55
- # 1. Run health check first.
56
- echo "Running health check..."
57
- if ! "$SCRIPT_DIR/healthcheck.sh"; then
58
- echo "ERROR: Health check failed. Fix issues before resuming."
87
+ # 1. Run health check first — with the stop flag exempted, since lifting it is
88
+ # this script's entire job. Every OTHER check still gates the resume.
89
+ echo "Running health check (emergency-stop flag exempted)..."
90
+ set +e
91
+ HEALTH_OUTPUT=$("$SCRIPT_DIR/healthcheck.sh" --ignore-emergency-stop 2>&1)
92
+ HEALTH_STATUS=$?
93
+ set -e
94
+ echo "$HEALTH_OUTPUT"
95
+
96
+ # 1b. Prove the exemption was HONOURED before trusting the verdict.
97
+ #
98
+ # Fault (a)'s fix is version-coupled: it only works if the healthcheck.sh
99
+ # next to this script understands --ignore-emergency-stop. A seat that
100
+ # pins framework files (.maestroignore) can carry the new resume script
101
+ # beside a pre-fix healthcheck, which has no argument parsing at all — it
102
+ # swallows the flag, still counts the stop as an error, and prints no
103
+ # `Blocking:` line. MEASURED on that exact pairing: the deadlock is back,
104
+ # and the refusal reads "(healthcheck named no condition; see output
105
+ # above)" — worse than the original, because it names nothing to fix.
106
+ # The seats most likely to be halted are the ones most likely to be
107
+ # pinned, so this has to be detected and named rather than inferred.
108
+ #
109
+ # We are past the `[ -f .emergency-stop ]` guard, so a healthcheck that
110
+ # honoured the flag MUST have printed the NOTE line. Its absence means
111
+ # the exemption did not take: stale, missing, or not executable.
112
+ if ! printf '%s\n' "$HEALTH_OUTPUT" | grep -q '^\[NOTE\] Emergency stop flag' ||
113
+ printf '%s\n' "$HEALTH_OUTPUT" | grep -q '^Blocking:.*emergency-stop-flag'; then
114
+ echo ""
115
+ echo "REFUSING TO RESUME — scripts/healthcheck.sh is stale."
116
+ echo " --ignore-emergency-stop was ignored (healthcheck exit $HEALTH_STATUS): it did"
117
+ echo " not report '[NOTE] Emergency stop flag ... not counted', so its verdict still"
118
+ echo " counts the very flag this script exists to lift. Resuming on that verdict"
119
+ echo " would be resuming on a health check nobody read."
120
+ echo " Fix: run 'maestro upgrade' on this seat, then confirm .maestroignore is not"
121
+ echo " pinning scripts/healthcheck.sh, and that the file exists and is executable."
122
+ echo " The .emergency-stop flag has been LEFT IN PLACE."
123
+ echo "[$TIMESTAMP] Resume REFUSED — healthcheck.sh stale (exemption ignored, status $HEALTH_STATUS)" >> "$LOG_FILE"
59
124
  exit 1
60
125
  fi
61
126
 
127
+ if [ "$HEALTH_STATUS" -ge 2 ]; then
128
+ # Name the blocking condition. The operator should never have to re-run
129
+ # the health check with a filter to learn which of "1 errors" stopped them.
130
+ BLOCKING=$(printf '%s\n' "$HEALTH_OUTPUT" | sed -n 's/^Blocking://p' | tr -s ' ' | sed 's/^ //')
131
+ [ -n "$BLOCKING" ] || BLOCKING="(healthcheck named no condition; see output above)"
132
+ echo ""
133
+ echo "REFUSING TO RESUME — health check is CRITICAL."
134
+ echo " Blocking condition(s): $BLOCKING"
135
+ # Re-list the failing lines under the refusal, RE-PREFIXED. The whole
136
+ # health output is echoed above, so the previous `grep '^\[FAIL\]'` here
137
+ # emitted bytes indistinguishable from it — deleting the line left the
138
+ # test that claimed to pin it green. The ` FAIL: ` prefix is produced
139
+ # only here, which is what makes that assertion load-bearing.
140
+ printf '%s\n' "$HEALTH_OUTPUT" | sed -n 's/^\[FAIL\] / FAIL: /p'
141
+ echo " The .emergency-stop flag has been LEFT IN PLACE."
142
+ echo " Fix the condition(s) above, then re-run: $0"
143
+ {
144
+ echo "[$TIMESTAMP] Resume REFUSED — blocking: $BLOCKING"
145
+ } >> "$LOG_FILE"
146
+ exit 1
147
+ fi
148
+
149
+ if [ "$HEALTH_STATUS" -eq 1 ]; then
150
+ DEGRADED=$(printf '%s\n' "$HEALTH_OUTPUT" | sed -n 's/^Warnings://p' | tr -s ' ' | sed 's/^ //')
151
+ echo ""
152
+ echo "NOTE: health check is DEGRADED — resuming anyway (warnings are not faults)."
153
+ echo " Warning condition(s): $DEGRADED"
154
+ echo "[$TIMESTAMP] Resume proceeding DEGRADED — warnings: $DEGRADED" >> "$LOG_FILE"
155
+ fi
156
+
62
157
  # 2. Remove stop flag.
63
158
  rm "$AGENT_DIR/.emergency-stop"
64
159
  echo "[$TIMESTAMP] Stop flag removed" >> "$LOG_FILE"