pi-harness-runtime 1.1.80 → 1.1.82

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.
Files changed (116) hide show
  1. package/harness/browser-scrapers/base-browser-scraper.d.ts +91 -0
  2. package/harness/browser-scrapers/chrome-profile-manager.d.ts +80 -0
  3. package/harness/browser-scrapers/codex-browser-scraper.d.ts +60 -0
  4. package/harness/browser-scrapers/index.d.ts +29 -0
  5. package/harness/browser-scrapers/zai-browser-scraper.d.ts +79 -0
  6. package/harness/e2e/browser-scrapers/base-browser-scraper.d.ts +92 -0
  7. package/harness/e2e/browser-scrapers/base-browser-scraper.ts +244 -0
  8. package/harness/e2e/browser-scrapers/chrome-profile-manager.d.ts +81 -0
  9. package/harness/e2e/browser-scrapers/chrome-profile-manager.ts +297 -0
  10. package/harness/e2e/browser-scrapers/codex-browser-scraper.d.ts +61 -0
  11. package/harness/e2e/browser-scrapers/codex-browser-scraper.ts +343 -0
  12. package/harness/e2e/browser-scrapers/index.d.ts +30 -0
  13. package/harness/e2e/browser-scrapers/index.ts +48 -0
  14. package/harness/e2e/browser-scrapers/zai-browser-scraper.d.ts +80 -0
  15. package/harness/e2e/browser-scrapers/zai-browser-scraper.ts +392 -0
  16. package/harness/e2e/glm-quota-scraper.ts +106 -15
  17. package/harness/e2e/index.ts +29 -0
  18. package/harness/e2e/unified-quota-manager.ts +364 -0
  19. package/index.ts +72 -11
  20. package/mirror.ts +32 -28
  21. package/package.json +1 -1
  22. package/packages/a2a-adapter/package.json +1 -1
  23. package/packages/architecture-generator/package.json +1 -1
  24. package/packages/auth/package.json +1 -1
  25. package/packages/autonomous-refactor/package.json +1 -1
  26. package/packages/autonomous-runtime/package.json +1 -1
  27. package/packages/cache-strategy/package.json +1 -1
  28. package/packages/capability-registry/package.json +1 -1
  29. package/packages/checkpoint/package.json +1 -1
  30. package/packages/cli-plugin-sdk/package.json +1 -1
  31. package/packages/clipboard/package.json +1 -1
  32. package/packages/clipboard-plugin/package.json +1 -1
  33. package/packages/code-generation/package.json +1 -1
  34. package/packages/code-review/package.json +1 -1
  35. package/packages/codex-adapter/package.json +1 -1
  36. package/packages/config-capture/package.json +1 -1
  37. package/packages/context-compiler/package.json +1 -1
  38. package/packages/context-discovery/package.json +1 -1
  39. package/packages/context-manager/package.json +1 -1
  40. package/packages/cookie-sanitizer/package.json +1 -1
  41. package/packages/cost-optimizer/package.json +1 -1
  42. package/packages/dependency-analyzer/package.json +1 -1
  43. package/packages/django-plugin/package.json +1 -1
  44. package/packages/doc-generator/package.json +1 -1
  45. package/packages/evaluation-engine/package.json +1 -1
  46. package/packages/evaluation-runner/package.json +1 -1
  47. package/packages/event-bus/package.json +1 -1
  48. package/packages/event-store/package.json +1 -1
  49. package/packages/experience-replay/package.json +1 -1
  50. package/packages/feedback-collector/package.json +1 -1
  51. package/packages/file-copy-helper/package.json +1 -1
  52. package/packages/framework-detector/package.json +1 -1
  53. package/packages/framework-plugin-sdk/package.json +1 -1
  54. package/packages/frappe-plugin/package.json +1 -1
  55. package/packages/generic-web-plugin/package.json +1 -1
  56. package/packages/health-monitor/package.json +1 -1
  57. package/packages/intent-analyzer/package.json +1 -1
  58. package/packages/jev-judge/package.json +1 -1
  59. package/packages/knowledge-graph/package.json +1 -1
  60. package/packages/knowledge-retrieval/package.json +1 -1
  61. package/packages/laravel-plugin/package.json +1 -1
  62. package/packages/learning-engine/package.json +1 -1
  63. package/packages/mcp-adapter/package.json +1 -1
  64. package/packages/memory-engine/package.json +1 -1
  65. package/packages/milestone-manager/package.json +1 -1
  66. package/packages/model-registry/package.json +1 -1
  67. package/packages/nextjs-plugin/package.json +1 -1
  68. package/packages/notification/package.json +1 -1
  69. package/packages/observability/package.json +1 -1
  70. package/packages/okf-indexer/package.json +1 -1
  71. package/packages/performance-optimizer/package.json +1 -1
  72. package/packages/privilege-broker/package.json +1 -1
  73. package/packages/project-analyzer/package.json +1 -1
  74. package/packages/project-bootstrap/package.json +1 -1
  75. package/packages/projection-engine/package.json +1 -1
  76. package/packages/prompt-compiler/package.json +1 -1
  77. package/packages/prompt-versioning/package.json +1 -1
  78. package/packages/provider-adapter-sdk/package.json +1 -1
  79. package/packages/provider-router/package.json +1 -1
  80. package/packages/provider-selector/package.json +1 -1
  81. package/packages/providers/dist/provider-id.d.ts.map +1 -1
  82. package/packages/providers/dist/provider-id.js +1 -2
  83. package/packages/providers/dist/provider-id.js.map +1 -1
  84. package/packages/providers/package.json +1 -1
  85. package/packages/providers/src/provider-id.ts +1 -2
  86. package/packages/quota-manager/package.json +1 -1
  87. package/packages/rate-limiter/package.json +1 -1
  88. package/packages/react-vite-plugin/package.json +1 -1
  89. package/packages/release-manager/package.json +1 -1
  90. package/packages/requirement-compiler/package.json +1 -1
  91. package/packages/runtime/package.json +1 -1
  92. package/packages/scheduler/package.json +1 -1
  93. package/packages/scheduler-adapter/package.json +1 -1
  94. package/packages/session/package.json +1 -1
  95. package/packages/session-api/package.json +1 -1
  96. package/packages/session-export/package.json +1 -1
  97. package/packages/shared-context/package.json +1 -1
  98. package/packages/skill-mcp-client/package.json +1 -1
  99. package/packages/skill-registry/package.json +1 -1
  100. package/packages/sprint-planner/package.json +1 -1
  101. package/packages/subscription-engine/package.json +1 -1
  102. package/packages/task-compiler/package.json +1 -1
  103. package/packages/tencentdb-memory/package.json +1 -1
  104. package/packages/tencentdb-sync/package.json +1 -1
  105. package/packages/test-data-generator/package.json +1 -1
  106. package/packages/test-generator/package.json +1 -1
  107. package/packages/todo-bd-sync/package.json +1 -1
  108. package/packages/token-estimation/package.json +1 -1
  109. package/packages/token-optimizer/package.json +1 -1
  110. package/packages/trajectory/package.json +1 -1
  111. package/packages/tui/package.json +1 -1
  112. package/packages/types/package.json +1 -1
  113. package/packages/workflow-events/package.json +1 -1
  114. package/packages/workspace-scanner/package.json +1 -1
  115. package/packages/worktree/package.json +1 -1
  116. package/packages/write-review/package.json +1 -1
@@ -0,0 +1,364 @@
1
+ /**
2
+ * Unified Quota Manager — Handles all providers with appropriate auth
3
+ *
4
+ * Authentication strategies by provider:
5
+ * | Provider | Auth Method | Status |
6
+ * |------------|----------------|-----------|
7
+ * | MiniMax | Browser cookies| ✅ Working|
8
+ * | GLM/z.ai | API Key | ✅ Exists |
9
+ * | ChatGPT | OAuth tokens | ✅ Exists |
10
+ * | Codex | OAuth tokens | ✅ Exists |
11
+ * | OpenAI | API Key | ✅ Exists |
12
+ */
13
+
14
+ import {
15
+ MiniMaxQuotaManager,
16
+ type MiniMaxQuotaData,
17
+ } from "./minimax-quota-scraper.js";
18
+
19
+ import {
20
+ GLMQuotaManager,
21
+ GLMQuotaScraper,
22
+ type GLMQuotaData,
23
+ } from "./glm-quota-scraper.js";
24
+
25
+ import {
26
+ ChatGPTQuotaManager,
27
+ ChatGPTQuotaScraper,
28
+ type ChatGPTQuotaData,
29
+ } from "./chatgpt-quota-scraper.js";
30
+
31
+ import {
32
+ OpenAIQuotaManager,
33
+ OpenAIQuotaScraper,
34
+ type OpenAIQuotaData,
35
+ } from "./openai-quota-scraper.js";
36
+
37
+ export type Provider = "minimax" | "glm" | "chatgpt" | "openai-codex" | "openai";
38
+
39
+ export interface QuotaData {
40
+ provider: Provider;
41
+ /** Usage percentage (0-100) */
42
+ usagePercent: number;
43
+ /** Human-readable reset time */
44
+ resetsAt?: string;
45
+ /** Epoch ms when quota resets */
46
+ resetsAtEpoch?: number;
47
+ /** Plan type */
48
+ planType?: string;
49
+ /** Total quota amount */
50
+ totalQuota?: string;
51
+ /** Used quota amount */
52
+ usedQuota?: string;
53
+ /** Remaining quota */
54
+ remainingQuota?: string;
55
+ /** Raw data */
56
+ raw?: Record<string, unknown>;
57
+ /** Timestamp */
58
+ scrapedAt: string;
59
+ }
60
+
61
+ export interface UnifiedQuotaConfig {
62
+ /** Provider to use (auto-detect if not specified) */
63
+ provider?: Provider;
64
+ /** Cookie file path (for browser-based providers) */
65
+ cookieFile?: string;
66
+ /** API key file path (for API-based providers) */
67
+ apiKeyFile?: string;
68
+ /** Direct API key value */
69
+ apiKey?: string;
70
+ /** Auth file path (for OAuth-based providers) */
71
+ authFile?: string;
72
+ /** Cache duration in ms (default: 5 min) */
73
+ cacheDurationMs?: number;
74
+ /** Suppress console output */
75
+ quiet?: boolean;
76
+ }
77
+
78
+ interface QuotaManager {
79
+ isAvailable(): boolean;
80
+ getQuota(forceRefresh?: boolean): Promise<unknown>;
81
+ }
82
+
83
+ /**
84
+ * Unified Quota Manager
85
+ *
86
+ * Provides a single interface to query quota data from all supported providers.
87
+ * Automatically detects available providers based on credentials.
88
+ */
89
+ export class UnifiedQuotaManager {
90
+ private managers: Map<Provider, QuotaManager> = new Map();
91
+ private activeProvider?: Provider;
92
+ private cacheDurationMs: number;
93
+ private quiet: boolean;
94
+
95
+ constructor(config: UnifiedQuotaConfig = {}) {
96
+ this.cacheDurationMs = config.cacheDurationMs ?? 5 * 60 * 1000;
97
+ this.quiet = config.quiet ?? false;
98
+
99
+ // Initialize managers based on config
100
+ if (!config.provider || config.provider === "minimax") {
101
+ const mmManager = new MiniMaxQuotaManager({
102
+ cookieFile: config.cookieFile,
103
+ cacheDurationMs: this.cacheDurationMs,
104
+ quiet: this.quiet,
105
+ });
106
+ this.managers.set("minimax", mmManager);
107
+ }
108
+
109
+ if (!config.provider || config.provider === "glm") {
110
+ const glmManager = new GLMQuotaManager({
111
+ apiKey: config.apiKey,
112
+ apiKeyFile: config.apiKeyFile,
113
+ quiet: this.quiet,
114
+ });
115
+ this.managers.set("glm", glmManager);
116
+ }
117
+
118
+ if (!config.provider || config.provider === "chatgpt" || config.provider === "openai-codex") {
119
+ const chatgptManager = new ChatGPTQuotaManager({
120
+ authFile: config.authFile,
121
+ quiet: this.quiet,
122
+ });
123
+ this.managers.set("chatgpt", chatgptManager);
124
+ // openai-codex uses the same ChatGPT quota endpoint
125
+ this.managers.set("openai-codex", chatgptManager);
126
+ }
127
+
128
+ if (!config.provider || config.provider === "openai") {
129
+ const openaiManager = new OpenAIQuotaManager({
130
+ cookieFile: config.cookieFile,
131
+ quiet: this.quiet,
132
+ });
133
+ this.managers.set("openai", openaiManager);
134
+ }
135
+
136
+ // Set active provider
137
+ if (config.provider) {
138
+ this.activeProvider = config.provider;
139
+ } else {
140
+ // Auto-detect first available provider
141
+ this.activeProvider = this.detectAvailableProvider();
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Detect the first available provider
147
+ */
148
+ private detectAvailableProvider(): Provider | undefined {
149
+ const priority: Provider[] = ["minimax", "glm", "chatgpt", "openai-codex", "openai"];
150
+
151
+ for (const provider of priority) {
152
+ const manager = this.managers.get(provider);
153
+ if (manager && manager.isAvailable()) {
154
+ if (!this.quiet) {
155
+ console.log(`[UnifiedQuota] Auto-detected provider: ${provider}`);
156
+ }
157
+ return provider;
158
+ }
159
+ }
160
+
161
+ return undefined;
162
+ }
163
+
164
+ /**
165
+ * Get all available providers
166
+ */
167
+ getAvailableProviders(): Provider[] {
168
+ const available: Provider[] = [];
169
+ for (const [provider, manager] of this.managers) {
170
+ if (manager.isAvailable()) {
171
+ available.push(provider);
172
+ }
173
+ }
174
+ return available;
175
+ }
176
+
177
+ /**
178
+ * Check if a provider is available
179
+ */
180
+ isProviderAvailable(provider: Provider): boolean {
181
+ const manager = this.managers.get(provider);
182
+ return manager?.isAvailable() ?? false;
183
+ }
184
+
185
+ /**
186
+ * Set active provider
187
+ */
188
+ setProvider(provider: Provider): boolean {
189
+ const manager = this.managers.get(provider);
190
+ if (manager && manager.isAvailable()) {
191
+ this.activeProvider = provider;
192
+ return true;
193
+ }
194
+ return false;
195
+ }
196
+
197
+ /**
198
+ * Get current provider
199
+ */
200
+ getProvider(): Provider | undefined {
201
+ return this.activeProvider;
202
+ }
203
+
204
+ /**
205
+ * Get quota data from active provider
206
+ */
207
+ async getQuota(forceRefresh = false): Promise<QuotaData | null> {
208
+ if (!this.activeProvider) {
209
+ if (!this.quiet) {
210
+ console.warn("[UnifiedQuota] No provider available");
211
+ }
212
+ return null;
213
+ }
214
+
215
+ const manager = this.managers.get(this.activeProvider);
216
+ if (!manager) {
217
+ return null;
218
+ }
219
+
220
+ try {
221
+ const data = await manager.getQuota(forceRefresh);
222
+ if (!data) return null;
223
+
224
+ return this.normalizeData(this.activeProvider, data);
225
+ } catch (error) {
226
+ if (!this.quiet) {
227
+ console.error(`[UnifiedQuota] Failed to fetch quota:`, error);
228
+ }
229
+ return null;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Get quota from specific provider
235
+ */
236
+ async getQuotaForProvider(
237
+ provider: Provider,
238
+ forceRefresh = false,
239
+ ): Promise<QuotaData | null> {
240
+ const manager = this.managers.get(provider);
241
+ if (!manager) {
242
+ return null;
243
+ }
244
+
245
+ try {
246
+ const data = await manager.getQuota(forceRefresh);
247
+ if (!data) return null;
248
+
249
+ return this.normalizeData(provider, data);
250
+ } catch (error) {
251
+ if (!this.quiet) {
252
+ console.error(`[UnifiedQuota] Failed to fetch ${provider} quota:`, error);
253
+ }
254
+ return null;
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Normalize provider-specific data to unified format
260
+ */
261
+ private normalizeData(
262
+ provider: Provider,
263
+ data: unknown,
264
+ ): QuotaData {
265
+ const base: QuotaData = {
266
+ provider,
267
+ usagePercent: 0,
268
+ scrapedAt: new Date().toISOString(),
269
+ };
270
+
271
+ switch (provider) {
272
+ case "minimax": {
273
+ const d = data as MiniMaxQuotaData;
274
+ return {
275
+ ...base,
276
+ usagePercent: d.h5UsedPct,
277
+ resetsAt: d.h5ResetsAt,
278
+ resetsAtEpoch: d.h5ResetsAtEpoch,
279
+ remainingQuota: d.creditBalance,
280
+ // @ts-expect-error - raw access for diagnostics
281
+ raw: d,
282
+ };
283
+ }
284
+
285
+ case "glm": {
286
+ const d = data as GLMQuotaData;
287
+ // GLM doesn't provide usage percentage or quota limit directly
288
+ // Show actual usage - percentage is unknown without quota info
289
+ return {
290
+ ...base,
291
+ usagePercent: 0, // Unknown - no quota limit in API response
292
+ planType: d.modelName,
293
+ usedQuota: `${(d.totalTokens / 1_000_000).toFixed(1)}M tokens`,
294
+ totalQuota: "unknown",
295
+ // @ts-expect-error - raw access for diagnostics
296
+ raw: d,
297
+ };
298
+ }
299
+
300
+ case "chatgpt":
301
+ case "openai-codex": {
302
+ const d = data as ChatGPTQuotaData;
303
+ return {
304
+ ...base,
305
+ usagePercent: d.weeklyUsedPct,
306
+ resetsAt: d.weeklyResetsAt,
307
+ resetsAtEpoch: d.weeklyResetsAtEpoch,
308
+ planType: d.planType,
309
+ remainingQuota: d.creditBalance,
310
+ // @ts-expect-error - raw access for diagnostics
311
+ raw: d,
312
+ };
313
+ }
314
+
315
+ case "openai": {
316
+ const d = data as OpenAIQuotaData;
317
+ return {
318
+ ...base,
319
+ usagePercent: d.weeklyUsedPct,
320
+ resetsAt: d.weeklyResetsAt,
321
+ resetsAtEpoch: d.weeklyResetsAtEpoch,
322
+ remainingQuota: d.creditBalance,
323
+ // @ts-expect-error - raw access for diagnostics
324
+ raw: d,
325
+ };
326
+ }
327
+
328
+ default:
329
+ return base;
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Get summary for status display
335
+ */
336
+ async getSummary(): Promise<string> {
337
+ const quota = await this.getQuota();
338
+ if (!quota) {
339
+ return `${this.activeProvider ?? "unknown"}: no data`;
340
+ }
341
+
342
+ const left = Math.max(0, 100 - quota.usagePercent);
343
+ const reset = quota.resetsAt ? ` · resets ${quota.resetsAt}` : "";
344
+
345
+ return `${this.activeProvider}: ${left.toFixed(0)}% left${reset}`;
346
+ }
347
+ }
348
+
349
+ /**
350
+ * Create unified quota manager from environment variables
351
+ */
352
+ export function createUnifiedQuotaManagerFromEnv(): UnifiedQuotaManager | null {
353
+ const provider = (process.env.QUOTA_PROVIDER ?? "auto") as Provider | "auto";
354
+
355
+ return new UnifiedQuotaManager({
356
+ provider: provider === "auto" ? undefined : provider,
357
+ cookieFile: process.env.QUOTA_COOKIE_FILE,
358
+ apiKeyFile: process.env.QUOTA_API_KEY_FILE,
359
+ apiKey: process.env.QUOTA_API_KEY,
360
+ authFile: process.env.QUOTA_AUTH_FILE,
361
+ cacheDurationMs: parseInt(process.env.QUOTA_CACHE_MS ?? "300000", 10),
362
+ quiet: process.env.QUOTA_QUIET === "true",
363
+ });
364
+ }
package/index.ts CHANGED
@@ -23,6 +23,7 @@ import { UsageTracker } from "./tracker.ts";
23
23
  import { MirrorStore, type MirrorRecord } from "./mirror.ts";
24
24
  import { MiniMaxQuotaScraper } from "./harness/e2e/minimax-quota-scraper.js";
25
25
  import { OpenAIQuotaScraper } from "./harness/e2e/openai-quota-scraper.js";
26
+ import { GLMQuotaScraper } from "./harness/e2e/glm-quota-scraper.js";
26
27
  import { parseMiniMaxQuotaText } from "./harness/e2e/minimax-quota-parser.js";
27
28
  import {
28
29
  CookieWatcher,
@@ -622,7 +623,8 @@ Before running a build, ensure you update the current task status:
622
623
  Run \`bd ready\` to see current bd issues.
623
624
  `;
624
625
 
625
- // Detect build commands and append todo reminder to their output
626
+ // Detect build commands and log reminder (don't modify result.content)
627
+ // Modifying result.content is fragile - pi-coding-agent expects specific structures
626
628
  pi.on("tool_execution_end", async (event) => {
627
629
  const toolName = (event as { toolName?: string }).toolName;
628
630
  if (toolName !== "bash") return;
@@ -630,7 +632,7 @@ Run \`bd ready\` to see current bd issues.
630
632
  const result = (event as { result?: { content?: unknown } }).result;
631
633
  if (!result) return;
632
634
 
633
- // Handle content that might be an array or object
635
+ // Get content for detection only - don't modify result.content
634
636
  const rawContent = result.content;
635
637
  let content: string;
636
638
  if (typeof rawContent === "string") {
@@ -639,8 +641,10 @@ Run \`bd ready\` to see current bd issues.
639
641
  content = rawContent
640
642
  .map((c) => (typeof c === "string" ? c : JSON.stringify(c)))
641
643
  .join("\n");
644
+ } else if (rawContent) {
645
+ content = JSON.stringify(rawContent);
642
646
  } else {
643
- content = JSON.stringify(rawContent ?? "");
647
+ return; // No content to analyze
644
648
  }
645
649
 
646
650
  // Check if this is a build command
@@ -648,18 +652,13 @@ Run \`bd ready\` to see current bd issues.
648
652
  content.toLowerCase().includes(cmd.toLowerCase()),
649
653
  );
650
654
 
655
+ // Log reminder to console (agent will see it)
651
656
  if (
652
657
  isBuildCommand &&
653
658
  !content.includes("bd ready") &&
654
659
  !content.includes("TODO UPDATE")
655
660
  ) {
656
- // Append todo reminder without changing the tool result content shape.
657
- const reminderBlock = { type: "text", text: TODO_BUILD_REMINDER };
658
- if (Array.isArray(rawContent)) {
659
- result.content = [...rawContent, reminderBlock];
660
- } else {
661
- result.content = [{ type: "text", text: content + TODO_BUILD_REMINDER }];
662
- }
661
+ console.log(TODO_BUILD_REMINDER.trim());
663
662
  }
664
663
  });
665
664
 
@@ -757,7 +756,9 @@ Run \`bd ready\` to see current bd issues.
757
756
  // --- Smart quota fetch for OpenAI status -------------------------
758
757
  const OPENAI_REFRESH_MIN_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
759
758
  const _openaiQuotaScraper = new OpenAIQuotaScraper({ quiet: true });
759
+ const _glmQuotaScraper = new GLMQuotaScraper({ quiet: true });
760
760
  let lastOpenAIQuotaFetchAt = 0;
761
+ let lastGLMQuotaFetchAt = 0;
761
762
 
762
763
  // --- Cookie sanitizer integration ------------------------------------
763
764
  // The drop folder is the user-facing, forgiving input. The canonical
@@ -1137,6 +1138,47 @@ Run \`bd ready\` to see current bd issues.
1137
1138
  }
1138
1139
  }
1139
1140
 
1141
+ /**
1142
+ * Auto-fetch GLM quota via z.ai API.
1143
+ * GLM has both 5h and weekly windows.
1144
+ */
1145
+ async function autoFetchGLMQuota(options?: {
1146
+ suppressErrors?: boolean;
1147
+ }): Promise<boolean> {
1148
+ const suppressErrors = options?.suppressErrors === true;
1149
+
1150
+ try {
1151
+ const data = await _glmQuotaScraper.fetchUsage();
1152
+ if (!data) {
1153
+ if (!suppressErrors) {
1154
+ console.error("[pi-harness] GLM quota auto-fetch: no data returned");
1155
+ }
1156
+ return false;
1157
+ }
1158
+
1159
+ writeMirrorRecord("glm", {
1160
+ synced_at: data.scrapedAt,
1161
+ source: "scrape",
1162
+ h5_used_pct: data.h5UsedPct,
1163
+ h5_resets_at: data.h5ResetsAt,
1164
+ h5_resets_at_epoch: data.h5ResetsAtEpoch,
1165
+ weekly_used_pct: data.weeklyUsedPct,
1166
+ weekly_resets_at: data.weeklyResetsAt,
1167
+ weekly_resets_at_epoch: data.weeklyResetsAtEpoch,
1168
+ model: data.modelName,
1169
+ });
1170
+ return true;
1171
+ } catch (error) {
1172
+ if (!suppressErrors) {
1173
+ console.error(
1174
+ "[pi-harness] GLM quota auto-fetch skipped:",
1175
+ error instanceof Error ? error.message : String(error),
1176
+ );
1177
+ }
1178
+ return false;
1179
+ }
1180
+ }
1181
+
1140
1182
  async function maybeAutoFetchQuota(
1141
1183
  modelId: string | null | undefined,
1142
1184
  ): Promise<void> {
@@ -1201,7 +1243,26 @@ Run \`bd ready\` to see current bd issues.
1201
1243
  return;
1202
1244
  }
1203
1245
 
1204
- // Other providers (GLM, Anthropic, etc.) - TUI signal path only for now
1246
+ // GLM path (z.ai has both 5h and weekly windows)
1247
+ if (provider === "glm") {
1248
+ if (quotaAutoFetchInFlight) return;
1249
+
1250
+ const nowMs = Date.now();
1251
+ if (nowMs - lastGLMQuotaFetchAt < OPENAI_REFRESH_MIN_INTERVAL_MS) {
1252
+ return;
1253
+ }
1254
+
1255
+ quotaAutoFetchInFlight = true;
1256
+ lastGLMQuotaFetchAt = nowMs;
1257
+ try {
1258
+ await autoFetchGLMQuota({ suppressErrors: true });
1259
+ } finally {
1260
+ quotaAutoFetchInFlight = false;
1261
+ }
1262
+ return;
1263
+ }
1264
+
1265
+ // Other providers (Anthropic, OpenRouter) - TUI signal path only for now
1205
1266
  return;
1206
1267
  }
1207
1268
 
package/mirror.ts CHANGED
@@ -58,34 +58,38 @@ export type MirrorSource = "scrape" | "tui-signal" | "manual";
58
58
 
59
59
  /** Per-provider mirror record. */
60
60
  export interface ProviderMirrorRecord {
61
- /** ISO-8601 UTC timestamp when this row was last updated. */
62
- synced_at: string;
63
- /** Provider id (e.g. "minimax", "openai", "glm"). Must match the key. */
64
- provider: string;
65
- /** Source of the data — used for diagnostics. */
66
- source: MirrorSource;
67
- /** Optional model id the data was captured for. */
68
- model?: string;
69
- /** 5h window used percent (0-100). Provider-reported when available. */
70
- h5_used_pct?: number;
71
- /** When the 5h window resets — human-readable or ISO. */
72
- h5_resets_at?: string;
73
- /** Epoch ms when the 5h window resets — precise UTC timestamp. */
74
- h5_resets_at_epoch?: number;
75
- /** Weekly used percent. */
76
- weekly_used_pct?: number;
77
- /** When the weekly window resets. */
78
- weekly_resets_at?: string;
79
- /** Epoch ms when the weekly window resets — precise UTC timestamp. */
80
- weekly_resets_at_epoch?: number;
81
- /** Set when a limit was hit and we have the exhaustion signal. */
82
- exhausted?: boolean;
83
- /** Limit type from TUI signal: tokens, context_window, rate_limit, unknown. */
84
- limitType?: "tokens" | "context_window" | "rate_limit" | "unknown";
85
- /** Remaining pct (0-100) at exhaustion — 0 means exactly exhausted. */
86
- remainingPct?: number;
87
- /** ISO-8601 reset timestamp from TUI signal, if absolute. */
88
- resets_at?: string;
61
+ /** ISO-8601 UTC timestamp when this row was last updated. */
62
+ synced_at: string;
63
+ /** Provider id (e.g. "minimax", "openai", "glm"). Must match the key. */
64
+ provider: string;
65
+ /** Source of the data — used for diagnostics. */
66
+ source: MirrorSource;
67
+ /** Optional model id the data was captured for. */
68
+ model?: string;
69
+ /** 5h window used percent (0-100). Provider-reported when available. */
70
+ h5_used_pct?: number;
71
+ /** When the 5h window resets — human-readable or ISO. */
72
+ h5_resets_at?: string;
73
+ /** Epoch ms when the 5h window resets — precise UTC timestamp. */
74
+ h5_resets_at_epoch?: number;
75
+ /** Weekly used percent. */
76
+ weekly_used_pct?: number;
77
+ /** When the weekly window resets. */
78
+ weekly_resets_at?: string;
79
+ /** Epoch ms when the weekly window resets — precise UTC timestamp. */
80
+ weekly_resets_at_epoch?: number;
81
+ /** Raw tokens used (for GLM which has no percentage). */
82
+ weekly_used_tokens?: number;
83
+ /** Monthly quota in tokens (for GLM). */
84
+ monthly_quota_tokens?: number;
85
+ /** Set when a limit was hit and we have the exhaustion signal. */
86
+ exhausted?: boolean;
87
+ /** Limit type from TUI signal: tokens, context_window, rate_limit, unknown. */
88
+ limitType?: "tokens" | "context_window" | "rate_limit" | "unknown";
89
+ /** Remaining pct (0-100) at exhaustion — 0 means exactly exhausted. */
90
+ remainingPct?: number;
91
+ /** ISO-8601 reset timestamp from TUI signal, if absolute. */
92
+ resets_at?: string;
89
93
  }
90
94
 
91
95
  /** Map of provider id → its mirror record. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-harness-runtime",
3
- "version": "1.1.80",
3
+ "version": "1.1.82",
4
4
  "description": "[BETA] Codex-style /usage status + autonomous coding harness for pi. Not production ready — expect breaking changes.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/a2a-adapter",
3
- "version": "1.1.80",
3
+ "version": "1.1.82",
4
4
  "type": "module",
5
5
  "description": "Google Agent-to-Agent (A2A) protocol adapter for pi-harness",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/architecture-generator",
3
- "version": "1.1.80",
3
+ "version": "1.1.82",
4
4
  "description": "Generate ADR and Mermaid diagrams from project analysis",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/auth",
3
- "version": "1.1.80",
3
+ "version": "1.1.82",
4
4
  "description": "pi-harness-runtime — auth",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/autonomous-refactor",
3
- "version": "1.1.80",
3
+ "version": "1.1.82",
4
4
  "type": "module",
5
5
  "description": "Autonomous refactoring engine",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/autonomous-runtime",
3
- "version": "1.1.80",
3
+ "version": "1.1.82",
4
4
  "description": "Autonomous Operations Runtime — task inbox, lease management, and worker lifecycle (RFC-0101)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/cache-strategy",
3
- "version": "1.1.80",
3
+ "version": "1.1.82",
4
4
  "type": "module",
5
5
  "description": "Smart caching with TTL and invalidation strategies (RFC-0022)",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/capability-registry",
3
- "version": "1.1.80",
3
+ "version": "1.1.82",
4
4
  "description": "Capability Registry for pi-harness-runtime — tracks model capabilities and enables capability-based routing",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/checkpoint",
3
- "version": "1.1.80",
3
+ "version": "1.1.82",
4
4
  "description": "Enhanced checkpoint system with incremental snapshots, diff-based storage, and recovery strategies",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/cli-plugin-sdk",
3
- "version": "1.1.80",
3
+ "version": "1.1.82",
4
4
  "type": "module",
5
5
  "description": "CLI Plugin SDK for pi-harness — install, list, remove, update, invoke plugins",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/clipboard",
3
- "version": "1.1.80",
3
+ "version": "1.1.82",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/clipboard-plugin",
3
- "version": "1.1.80",
3
+ "version": "1.1.82",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {