@data-matters.io/agent-harness 0.1.9 → 0.1.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@data-matters.io/agent-harness",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Spec-Driven Development with Claude Code: gates, task loop and standards installed into a product repository",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -47,6 +47,15 @@ harness_session_error_text() {
47
47
  : raw);' "$1" 2>/dev/null
48
48
  }
49
49
 
50
+ # harness_session_api_error <session.json>: the session's api_error field, the
51
+ # structured value the runtime sets when the API refused (usage_limit_reached).
52
+ # Empty when the session carries none.
53
+ harness_session_api_error() {
54
+ node -e "$harness_session_js"'
55
+ const d = session(process.argv[1]);
56
+ process.stdout.write(typeof d.api_error === "string" ? d.api_error : "");' "$1" 2>/dev/null
57
+ }
58
+
50
59
  # harness_api_refused <session.json>: the session ended on an error that says
51
60
  # the API cannot serve it: no credit, a quota, a spend or usage limit, billing,
52
61
  # an invalid key, or the gateway itself failing (503 or another 5xx, a
@@ -156,8 +165,30 @@ harness_claude_flags() {
156
165
  # session limit") and sets api_error to usage_limit_reached, so both are matched;
157
166
  # the gateway's Portuguese quota message is matched too, and its transient 429
158
167
  # is not (quota.sh, ADR 0025 section 8).
168
+ #
169
+ # Only the session's own FAILURE is read: the api_error field, and the error
170
+ # text of a session that ended with is_error (harness_session_error_text).
171
+ # The answer of a session that ended well is never a usage error by what it
172
+ # says: a task that explains an old quota event ("cota semanal") is a task
173
+ # about a quota, not a quota. The one exception is the gateway's quota notice,
174
+ # which arrives unmarked, in the body of a 200: there the whole answer IS the
175
+ # notice, so a short answer that carries a quota marker still is a usage error
176
+ # (harness_quota_notice_unmarked).
159
177
  harness_usage_error() {
160
- harness_quota_exhausted "$(cat -- "$1" 2>/dev/null)"
178
+ local text reply
179
+ text="$(harness_session_error_text "$1")"
180
+ if [[ -n "$text" ]]; then
181
+ # The session failed: its error text (its result and its api_error) is the
182
+ # message the rule is about.
183
+ if harness_quota_exhausted "$text"; then return 0; fi
184
+ return 1
185
+ fi
186
+ # The session ended well: its answer is not a usage error by what it says.
187
+ # The api_error field still counts (usage_limit_reached), and the gateway's
188
+ # unmarked notice is the one answer that IS the notice.
189
+ if harness_quota_exhausted "$(harness_session_api_error "$1")"; then return 0; fi
190
+ reply="$(harness_session_reply "$1")" || return 1
191
+ harness_quota_notice_unmarked "$reply"
161
192
  }
162
193
 
163
194
  # harness_level_descend <level>: the level one below, for the fallback after a
@@ -11,6 +11,10 @@
11
11
  # a quota marker and no transient marker. A 429 alone is not enough in
12
12
  # either direction: both gateway messages carry the same structured
13
13
  # fields, so only the text tells them apart.
14
+ # (c) is a session that ENDED WELL the gateway's quota notice?
15
+ # harness_quota_notice_unmarked <text>
16
+ # the gateway sometimes answers 200 with the quota notice in the body,
17
+ # without marking it as an error: there the whole answer is the notice.
14
18
  # A transient 429 (harness_quota_transient) is neither: it may be retried and
15
19
  # changes nothing.
16
20
  #
@@ -52,6 +56,25 @@ harness_quota_exhausted() {
52
56
  grep -qE -- "$harness_quota_markers" <<<"$t" && ! grep -qE -- "$harness_quota_transient_markers" <<<"$t"
53
57
  }
54
58
 
59
+ # The longest text that can be the gateway's quota notice. The notice arrives
60
+ # in the body of a 200 response, without being marked as an error (the rule of
61
+ # the watchdog, vps-bootstrap, 2026-11-11): there the whole answer IS the
62
+ # notice. A long answer that talks about a quota is a task about a quota, not a
63
+ # quota.
64
+ harness_quota_notice_max_len=400
65
+
66
+ # harness_quota_notice_unmarked <text>: the text is the gateway's quota notice
67
+ # that arrived unmarked: a SHORT message (at most harness_quota_notice_max_len
68
+ # characters) that carries a quota marker. The markers are the ones above (one
69
+ # list, ADR 0025 section 8): this rule only says how much of the answer may
70
+ # be the notice.
71
+ harness_quota_notice_unmarked() {
72
+ local t="$1" len
73
+ len=${#t}
74
+ (( len <= harness_quota_notice_max_len )) || return 1
75
+ harness_quota_exhausted "$t"
76
+ }
77
+
55
78
  # harness_api_unreachable <text>: the gateway failed (503 or another 5xx, a
56
79
  # connection failure, a timeout): the API does not answer, whatever the quota.
57
80
  harness_api_unreachable() {