@gobing-ai/ts-ai-runner 0.5.18 → 0.5.20

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/dist/quota.d.ts CHANGED
@@ -58,8 +58,10 @@ export declare const MAX_QUOTA_EVIDENCE_BYTES = 8192;
58
58
  /**
59
59
  * Classify one error record as quota exhaustion. Accepts only structured
60
60
  * provider error envelopes — a JSON object with an `error` object whose
61
- * `type` or `code` exactly matches a canonical quota code. Free text, quoted
62
- * prompt content inside `error.message`, plain HTTP 429 rate-limit codes,
61
+ * `type` or `code` exactly matches a canonical quota code, or (for providers
62
+ * that omit the wrapper) a top-level `{ code, message }` pair where `code`
63
+ * matches a canonical or verified provider code. Free text, quoted prompt
64
+ * content inside `error.message`, plain HTTP 429 rate-limit codes,
63
65
  * throttling/overload, authentication, context/output limits, and timeouts
64
66
  * all classify as negative (R2). Input is bounded to
65
67
  * {@link MAX_QUOTA_EVIDENCE_BYTES} trailing bytes.
@@ -1 +1 @@
1
- {"version":3,"file":"quota.d.ts","sourceRoot":"","sources":["../src/quota.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,GAC3B,oBAAoB,GACpB,6BAA6B,GAC7B,gBAAgB,GAChB,qBAAqB,GACrB,mBAAmB,GACnB,4BAA4B;AAC9B,+FAA+F;GAC7F,0BAA0B,CAAC;AAEjC,mHAAmH;AACnH,MAAM,MAAM,mBAAmB,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,cAAc,CAAC;AAExF;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,+GAA+G;AAC/G,MAAM,WAAW,qBAAqB;IAClC,kHAAkH;IAClH,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,mBAAmB,CAAC;IACpC,MAAM,EAAE,qBAAqB,CAAC;IAC9B,sGAAsG;IACtG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED,4HAA4H;AAC5H,MAAM,WAAW,kBAAkB;IAC/B,qDAAqD;IACrD,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED,sDAAsD;AACtD,MAAM,MAAM,mBAAmB,GAAG;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AAEpG,kHAAkH;AAClH,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAW7C;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAU5E;AAkBD,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,IAAI,GAAG,MAAM,CAEpD;AAYD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC1C,MAAM,EAAE,mBAAmB,CAAC;IAC5B,MAAM,EAAE,qBAAqB,CAAC;IAC9B,UAAU,CAAC,EAAE,IAAI,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACrC,GAAG,qBAAqB,CAoBxB;AAED;;;;;GAKG;AACH,qBAAa,wBAAwB;IAGrB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;gBAEhB,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,SAAS;IAEtE,mGAAmG;IACnG,OAAO,CAAC,WAAW,EAAE,qBAAqB,GAAG,OAAO;IAOpD,mGAAmG;IACnG,eAAe,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO;CAKzD"}
1
+ {"version":3,"file":"quota.d.ts","sourceRoot":"","sources":["../src/quota.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,GAC3B,oBAAoB,GACpB,6BAA6B,GAC7B,gBAAgB,GAChB,qBAAqB,GACrB,mBAAmB,GACnB,4BAA4B;AAC9B,+FAA+F;GAC7F,0BAA0B,CAAC;AAEjC,mHAAmH;AACnH,MAAM,MAAM,mBAAmB,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,cAAc,CAAC;AAExF;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,+GAA+G;AAC/G,MAAM,WAAW,qBAAqB;IAClC,kHAAkH;IAClH,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,mBAAmB,CAAC;IACpC,MAAM,EAAE,qBAAqB,CAAC;IAC9B,sGAAsG;IACtG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED,4HAA4H;AAC5H,MAAM,WAAW,kBAAkB;IAC/B,qDAAqD;IACrD,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED,sDAAsD;AACtD,MAAM,MAAM,mBAAmB,GAAG;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AAEpG,kHAAkH;AAClH,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAmB7C;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAY5E;AA8BD,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,IAAI,GAAG,MAAM,CAEpD;AAYD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC1C,MAAM,EAAE,mBAAmB,CAAC;IAC5B,MAAM,EAAE,qBAAqB,CAAC;IAC9B,UAAU,CAAC,EAAE,IAAI,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACrC,GAAG,qBAAqB,CAoBxB;AAED;;;;;GAKG;AACH,qBAAa,wBAAwB;IAGrB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;gBAEhB,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,SAAS;IAEtE,mGAAmG;IACnG,OAAO,CAAC,WAAW,EAAE,qBAAqB,GAAG,OAAO;IAOpD,mGAAmG;IACnG,eAAe,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO;CAKzD"}
package/dist/quota.js CHANGED
@@ -9,11 +9,20 @@ const QUOTA_CODES = new Set([
9
9
  'credits_exhausted',
10
10
  'billing_hard_limit_reached',
11
11
  ]);
12
+ /**
13
+ * Verified provider-specific exhaustion codes, mapped onto the canonical reason
14
+ * they confirm. The canonical allowlist above stays exact-match; a provider code
15
+ * is added here only when it has been observed to mean exhaustion (Spur 1134 R2 —
16
+ * `1310` is z.ai's weekly/monthly allowance cap).
17
+ */
18
+ const PROVIDER_QUOTA_CODES = new Map([['1310', 'usage_limit_reached']]);
12
19
  /**
13
20
  * Classify one error record as quota exhaustion. Accepts only structured
14
21
  * provider error envelopes — a JSON object with an `error` object whose
15
- * `type` or `code` exactly matches a canonical quota code. Free text, quoted
16
- * prompt content inside `error.message`, plain HTTP 429 rate-limit codes,
22
+ * `type` or `code` exactly matches a canonical quota code, or (for providers
23
+ * that omit the wrapper) a top-level `{ code, message }` pair where `code`
24
+ * matches a canonical or verified provider code. Free text, quoted prompt
25
+ * content inside `error.message`, plain HTTP 429 rate-limit codes,
17
26
  * throttling/overload, authentication, context/output limits, and timeouts
18
27
  * all classify as negative (R2). Input is bounded to
19
28
  * {@link MAX_QUOTA_EVIDENCE_BYTES} trailing bytes.
@@ -29,9 +38,19 @@ export function classifyQuotaErrorRecord(record) {
29
38
  if (QUOTA_CODES.has(candidate))
30
39
  return { quota: true, reason: candidate };
31
40
  }
41
+ const providerReason = PROVIDER_QUOTA_CODES.get(code);
42
+ if (providerReason !== undefined)
43
+ return { quota: true, reason: providerReason };
32
44
  return { quota: false };
33
45
  }
34
- /** Best-effort parse of a JSON provider error envelope (`{ error: { type?, code?, message? } }`). */
46
+ /**
47
+ * Best-effort parse of a JSON provider error envelope. Two shapes are accepted:
48
+ * the `error`-wrapped form (`{ error: { type?, code?, message? } }`) used by most
49
+ * providers, and a top-level `{ code, message }` form used by providers that omit
50
+ * the wrapper. The top-level form requires both a string `code` and a string
51
+ * `message` so an incidental `{"code":"…"}` echo in surrounding text is not read
52
+ * as an envelope. A present-but-malformed `error` key stays negative.
53
+ */
35
54
  function tryParseErrorEnvelope(record) {
36
55
  const start = record.indexOf('{');
37
56
  const end = record.lastIndexOf('}');
@@ -41,10 +60,16 @@ function tryParseErrorEnvelope(record) {
41
60
  const parsed = JSON.parse(record.slice(start, end + 1));
42
61
  if (typeof parsed !== 'object' || parsed === null)
43
62
  return null;
44
- const error = parsed.error;
45
- if (typeof error !== 'object' || error === null)
63
+ if ('error' in parsed) {
64
+ const error = parsed.error;
65
+ if (typeof error !== 'object' || error === null)
66
+ return null;
67
+ return error;
68
+ }
69
+ const topLevel = parsed;
70
+ if (typeof topLevel.code !== 'string' || typeof topLevel.message !== 'string')
46
71
  return null;
47
- return error;
72
+ return { type: topLevel.type, code: topLevel.code };
48
73
  }
49
74
  catch {
50
75
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/ts-ai-runner",
3
- "version": "0.5.18",
3
+ "version": "0.5.20",
4
4
  "description": "@gobing-ai/ts-ai-runner — Coding-agent shims, detection, doctor checks, and prompt execution.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -47,13 +47,13 @@
47
47
  "release": "echo 'Manual publish is disabled. Releases go through GitHub Actions via Trusted Publishing — push a tag: git tag @gobing-ai/ts-ai-runner-v<version> && git push --tags' && exit 1"
48
48
  },
49
49
  "dependencies": {
50
- "@gobing-ai/ts-infra": "^0.5.18",
51
- "@gobing-ai/ts-runtime": "^0.5.18",
50
+ "@gobing-ai/ts-infra": "^0.5.20",
51
+ "@gobing-ai/ts-runtime": "^0.5.20",
52
52
  "@typesafe-ai/sdk": "0.6.0"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/bun": "1.3.14",
56
- "@gobing-ai/ts-db": "^0.5.18"
56
+ "@gobing-ai/ts-db": "^0.5.20"
57
57
  },
58
58
  "publishConfig": {
59
59
  "access": "public"
package/src/quota.ts CHANGED
@@ -74,11 +74,21 @@ const QUOTA_CODES: ReadonlySet<string> = new Set([
74
74
  'billing_hard_limit_reached',
75
75
  ]);
76
76
 
77
+ /**
78
+ * Verified provider-specific exhaustion codes, mapped onto the canonical reason
79
+ * they confirm. The canonical allowlist above stays exact-match; a provider code
80
+ * is added here only when it has been observed to mean exhaustion (Spur 1134 R2 —
81
+ * `1310` is z.ai's weekly/monthly allowance cap).
82
+ */
83
+ const PROVIDER_QUOTA_CODES: ReadonlyMap<string, QuotaExhaustionReason> = new Map([['1310', 'usage_limit_reached']]);
84
+
77
85
  /**
78
86
  * Classify one error record as quota exhaustion. Accepts only structured
79
87
  * provider error envelopes — a JSON object with an `error` object whose
80
- * `type` or `code` exactly matches a canonical quota code. Free text, quoted
81
- * prompt content inside `error.message`, plain HTTP 429 rate-limit codes,
88
+ * `type` or `code` exactly matches a canonical quota code, or (for providers
89
+ * that omit the wrapper) a top-level `{ code, message }` pair where `code`
90
+ * matches a canonical or verified provider code. Free text, quoted prompt
91
+ * content inside `error.message`, plain HTTP 429 rate-limit codes,
82
92
  * throttling/overload, authentication, context/output limits, and timeouts
83
93
  * all classify as negative (R2). Input is bounded to
84
94
  * {@link MAX_QUOTA_EVIDENCE_BYTES} trailing bytes.
@@ -92,10 +102,19 @@ export function classifyQuotaErrorRecord(record: string): QuotaClassification {
92
102
  for (const candidate of [type, code]) {
93
103
  if (QUOTA_CODES.has(candidate)) return { quota: true, reason: candidate as QuotaExhaustionReason };
94
104
  }
105
+ const providerReason = PROVIDER_QUOTA_CODES.get(code);
106
+ if (providerReason !== undefined) return { quota: true, reason: providerReason };
95
107
  return { quota: false };
96
108
  }
97
109
 
98
- /** Best-effort parse of a JSON provider error envelope (`{ error: { type?, code?, message? } }`). */
110
+ /**
111
+ * Best-effort parse of a JSON provider error envelope. Two shapes are accepted:
112
+ * the `error`-wrapped form (`{ error: { type?, code?, message? } }`) used by most
113
+ * providers, and a top-level `{ code, message }` form used by providers that omit
114
+ * the wrapper. The top-level form requires both a string `code` and a string
115
+ * `message` so an incidental `{"code":"…"}` echo in surrounding text is not read
116
+ * as an envelope. A present-but-malformed `error` key stays negative.
117
+ */
99
118
  function tryParseErrorEnvelope(record: string): { type?: unknown; code?: unknown } | null {
100
119
  const start = record.indexOf('{');
101
120
  const end = record.lastIndexOf('}');
@@ -103,9 +122,14 @@ function tryParseErrorEnvelope(record: string): { type?: unknown; code?: unknown
103
122
  try {
104
123
  const parsed: unknown = JSON.parse(record.slice(start, end + 1));
105
124
  if (typeof parsed !== 'object' || parsed === null) return null;
106
- const error = (parsed as { error?: unknown }).error;
107
- if (typeof error !== 'object' || error === null) return null;
108
- return error as { type?: unknown; code?: unknown };
125
+ if ('error' in parsed) {
126
+ const error = (parsed as { error?: unknown }).error;
127
+ if (typeof error !== 'object' || error === null) return null;
128
+ return error as { type?: unknown; code?: unknown };
129
+ }
130
+ const topLevel = parsed as { type?: unknown; code?: unknown; message?: unknown };
131
+ if (typeof topLevel.code !== 'string' || typeof topLevel.message !== 'string') return null;
132
+ return { type: topLevel.type, code: topLevel.code };
109
133
  } catch {
110
134
  return null;
111
135
  }