@wrongstack/core 0.310.1 → 0.313.0
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/coordination/index.js +3 -2
- package/dist/coordination/mailbox-project-server.js +70 -14
- package/dist/coordination/mailbox-types.d.ts +13 -1
- package/dist/coordination/sqlite-mailbox.d.ts +2 -1
- package/dist/core/index.js +3 -2
- package/dist/execution/index.js +3 -2
- package/dist/index.js +20 -19
- package/dist/kernel/events/wrongtrace-events.d.ts +26 -0
- package/dist/kernel/events.d.ts +16 -7
- package/dist/kernel/index.js +17 -17
- package/dist/tools/index.js +3 -2
- package/dist/types/config/tools.d.ts +2 -2
- package/dist/types/index.js +3 -2
- package/dist/wiring/proxy-rewrite.d.ts +74 -4
- package/dist/wiring/proxy-rewrite.js +63 -1
- package/package.json +4 -4
|
@@ -20988,7 +20988,7 @@ async function readDirectorSubagentSession(args) {
|
|
|
20988
20988
|
}
|
|
20989
20989
|
|
|
20990
20990
|
// src/types/quota-regex.ts
|
|
20991
|
-
var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)(?:\s+(?:has|have))?(?:\s+been)?[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|usage[-_\s]*limit[-_\s]*(?:reached|exceeded)/i;
|
|
20991
|
+
var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)(?:\s+(?:has|have))?(?:\s+been)?[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|(?:usage|credit)[-_\s]*(?:quota|limit)(?![-_\w])|usage[-_\s]*(?:limit|quota)[-_\s]*(?:reached|exceeded|for)/i;
|
|
20992
20992
|
var ROUTE_SCOPED_QUOTA_RE = /\b(?:for|on)\s+(?:(?:this|the)\s+)?(?:route|model)\b|\b(?:route|model)(?:[-_\s]+[\w.-]+)?[-_\s]*(?:quota|limit)\b|\b(?:quota|limit).{0,24}\b(?:for|on)\s+(?:(?:this|the)\s+)?(?:route|model)\b/i;
|
|
20993
20993
|
|
|
20994
20994
|
// src/types/provider.ts
|
|
@@ -21001,7 +21001,8 @@ function classifyProviderError(status, body, message) {
|
|
|
21001
21001
|
if (status === 0) return "network";
|
|
21002
21002
|
if (status === 408) return "timeout";
|
|
21003
21003
|
if (status === 599) return "stream_hang";
|
|
21004
|
-
if (
|
|
21004
|
+
if (QUOTA_EXHAUSTED_RE.test(text)) return "quota_exhausted";
|
|
21005
|
+
if (status === 402) return "quota_exhausted";
|
|
21005
21006
|
if (status === 429 && body?.message && body.type !== "rate_limit_exceeded" && RATE_LIMIT_EXCEEDED_RE.test(body.message)) {
|
|
21006
21007
|
return "quota_exhausted";
|
|
21007
21008
|
}
|
|
@@ -67,8 +67,8 @@ var EventBus = class {
|
|
|
67
67
|
*
|
|
68
68
|
* Returns an unsubscribe function.
|
|
69
69
|
*/
|
|
70
|
-
onAny(fn) {
|
|
71
|
-
return this.onPattern("*", fn);
|
|
70
|
+
onAny(fn, owner) {
|
|
71
|
+
return this.onPattern("*", fn, owner);
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
74
|
* Subscribe to all events whose name matches a glob-style prefix.
|
|
@@ -81,16 +81,16 @@ var EventBus = class {
|
|
|
81
81
|
*
|
|
82
82
|
* Returns an unsubscribe function.
|
|
83
83
|
*/
|
|
84
|
-
onPattern(pattern, fn) {
|
|
84
|
+
onPattern(pattern, fn, owner) {
|
|
85
85
|
if (this.wildcards.length >= MAX_WILDCARDS) {
|
|
86
86
|
this.logger?.error(
|
|
87
|
-
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onPattern("${pattern}"). Callers must dispose their wildcard listeners to prevent unbounded growth
|
|
87
|
+
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onPattern("${pattern}")` + (owner ? ` (owner: ${owner})` : "") + ". Callers must dispose their wildcard listeners to prevent unbounded growth."
|
|
88
88
|
);
|
|
89
89
|
return () => {
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
92
|
const match = makePatternMatcher(pattern);
|
|
93
|
-
const entry = { match, fn };
|
|
93
|
+
const entry = { match, fn, owner };
|
|
94
94
|
this.wildcards.push(entry);
|
|
95
95
|
this.wildcardSnapshotCache = null;
|
|
96
96
|
return () => {
|
|
@@ -108,15 +108,15 @@ var EventBus = class {
|
|
|
108
108
|
*
|
|
109
109
|
* Returns an unsubscribe function.
|
|
110
110
|
*/
|
|
111
|
-
onRegex(regex, fn) {
|
|
111
|
+
onRegex(regex, fn, owner) {
|
|
112
112
|
if (this.wildcards.length >= MAX_WILDCARDS) {
|
|
113
113
|
this.logger?.error(
|
|
114
|
-
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onRegex(${regex}). Callers must dispose their wildcard listeners to prevent unbounded growth
|
|
114
|
+
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onRegex(${regex})` + (owner ? ` (owner: ${owner})` : "") + ". Callers must dispose their wildcard listeners to prevent unbounded growth."
|
|
115
115
|
);
|
|
116
116
|
return () => {
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
|
-
const entry = { match: (e) => regex.test(e), fn };
|
|
119
|
+
const entry = { match: (e) => regex.test(e), fn, owner };
|
|
120
120
|
this.wildcards.push(entry);
|
|
121
121
|
this.wildcardSnapshotCache = null;
|
|
122
122
|
return () => {
|
|
@@ -1416,6 +1416,38 @@ function isMailboxReceiptRecordV2(value) {
|
|
|
1416
1416
|
return true;
|
|
1417
1417
|
}
|
|
1418
1418
|
|
|
1419
|
+
// src/coordination/mailbox-session-sync.ts
|
|
1420
|
+
function isAffectedBySessionAffinity(message) {
|
|
1421
|
+
return message.sessionAffinity !== void 0;
|
|
1422
|
+
}
|
|
1423
|
+
async function acceptMailboxMessageForSession(message, currentSessionId, ctx) {
|
|
1424
|
+
if (!isAffectedBySessionAffinity(message)) return true;
|
|
1425
|
+
const affinity = message.sessionAffinity;
|
|
1426
|
+
if (affinity === null || typeof affinity !== "object" || Array.isArray(affinity)) {
|
|
1427
|
+
return false;
|
|
1428
|
+
}
|
|
1429
|
+
if (affinity.sessionId !== void 0 && typeof affinity.sessionId !== "string" || affinity.reportId !== void 0 && typeof affinity.reportId !== "string") {
|
|
1430
|
+
return false;
|
|
1431
|
+
}
|
|
1432
|
+
if (!currentSessionId) {
|
|
1433
|
+
return ctx?.allowUnscoped === true;
|
|
1434
|
+
}
|
|
1435
|
+
if (typeof affinity.sessionId === "string" && affinity.sessionId.length > 0) {
|
|
1436
|
+
if (affinity.sessionId !== currentSessionId) return false;
|
|
1437
|
+
return true;
|
|
1438
|
+
}
|
|
1439
|
+
if (affinity.reportId && ctx?.resolveChimeraReportSessionId) {
|
|
1440
|
+
try {
|
|
1441
|
+
const resolved = await ctx.resolveChimeraReportSessionId(affinity.reportId);
|
|
1442
|
+
if (resolved === currentSessionId) return true;
|
|
1443
|
+
if (resolved !== void 0) return false;
|
|
1444
|
+
} catch {
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
if (ctx?.allowUnscoped === true) return true;
|
|
1448
|
+
return false;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1419
1451
|
// src/coordination/mailbox-message-codec.ts
|
|
1420
1452
|
var MESSAGE_TYPES = /* @__PURE__ */ new Set([
|
|
1421
1453
|
"note",
|
|
@@ -2772,7 +2804,7 @@ var SqliteMailbox = class {
|
|
|
2772
2804
|
}
|
|
2773
2805
|
const rows = this.stmt(sql).all(...params);
|
|
2774
2806
|
const idFilter = query.ids === void 0 ? void 0 : new Set(query.ids);
|
|
2775
|
-
const
|
|
2807
|
+
const filtered = this.materializeMessageRows(rows).filter((message) => {
|
|
2776
2808
|
if (idFilter !== void 0 && !idFilter.has(message.id)) return false;
|
|
2777
2809
|
if (query.to !== void 0 && message.to !== query.to && message.to !== "*") return false;
|
|
2778
2810
|
if (query.from !== void 0 && message.from !== query.from) return false;
|
|
@@ -2791,6 +2823,20 @@ var SqliteMailbox = class {
|
|
|
2791
2823
|
if (query.replyTo !== void 0 && message.replyTo !== query.replyTo) return false;
|
|
2792
2824
|
return true;
|
|
2793
2825
|
});
|
|
2826
|
+
let messages = filtered;
|
|
2827
|
+
if (query.currentSessionId !== void 0 || query.sessionAffinityCtx !== void 0) {
|
|
2828
|
+
const kept = [];
|
|
2829
|
+
for (const message of filtered) {
|
|
2830
|
+
if (await acceptMailboxMessageForSession(
|
|
2831
|
+
message,
|
|
2832
|
+
query.currentSessionId,
|
|
2833
|
+
query.sessionAffinityCtx
|
|
2834
|
+
)) {
|
|
2835
|
+
kept.push(message);
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
messages = kept;
|
|
2839
|
+
}
|
|
2794
2840
|
messages.sort((left, right) => right.timestamp.localeCompare(left.timestamp));
|
|
2795
2841
|
return messages.slice(0, query.limit ?? 50).map((message) => {
|
|
2796
2842
|
const copy = {
|
|
@@ -2903,7 +2949,7 @@ var SqliteMailbox = class {
|
|
|
2903
2949
|
* identity is `leader`. This call path carries no role, matching the
|
|
2904
2950
|
* `isMailboxMessageVisibleTo(message, forAgentId)` it replaces.
|
|
2905
2951
|
*/
|
|
2906
|
-
async unreadCount(forAgentId, sessionId) {
|
|
2952
|
+
async unreadCount(forAgentId, sessionId, ctx) {
|
|
2907
2953
|
const sessionAddress = sessionId === void 0 ? void 0 : sessionRecipient(sessionId);
|
|
2908
2954
|
const where = [];
|
|
2909
2955
|
const params = [];
|
|
@@ -2944,10 +2990,20 @@ var SqliteMailbox = class {
|
|
|
2944
2990
|
WHERE any_receipt.message_id = messages.id
|
|
2945
2991
|
)
|
|
2946
2992
|
)`);
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2993
|
+
if (sessionId === void 0 && ctx === void 0) {
|
|
2994
|
+
const row = this.stmt(
|
|
2995
|
+
`SELECT COUNT(*) AS total FROM messages WHERE ${where.join(" AND ")}`
|
|
2996
|
+
).get(...params);
|
|
2997
|
+
return Number(row?.total ?? 0);
|
|
2998
|
+
}
|
|
2999
|
+
const rows = this.stmt(
|
|
3000
|
+
`SELECT id, data, legacy_global_completion FROM messages WHERE ${where.join(" AND ")}`
|
|
3001
|
+
).all(...params);
|
|
3002
|
+
let total = 0;
|
|
3003
|
+
for (const message of this.materializeMessageRows(rows)) {
|
|
3004
|
+
if (await acceptMailboxMessageForSession(message, sessionId, ctx)) total += 1;
|
|
3005
|
+
}
|
|
3006
|
+
return total;
|
|
2951
3007
|
}
|
|
2952
3008
|
async softDelete(mailId, by) {
|
|
2953
3009
|
const message = this.findMessage(mailId);
|
|
@@ -3,6 +3,7 @@ import type { MailboxAudience, MailboxMessage, MailboxSessionAffinity, MailboxTa
|
|
|
3
3
|
export { MAILBOX_TYPE_PROPERTIES, type MailboxMessageType, type MailboxTypeCategory, } from './mailbox-type-properties.js';
|
|
4
4
|
export { expandMailboxCapabilities, hasMailboxCapability, MAILBOX_CAPABILITY_IMPLICATIONS, type MailboxActorContext, type MailboxAuthMode, type MailboxCapability, type MailboxPrincipalKind, } from './mailbox-auth-types.js';
|
|
5
5
|
export { isActionRequiredForActor, isMailboxLeader, isMailboxMessageVisibleTo, isMailboxReceiptRecordV2, isMailboxSenderInFamily, mailboxIdentityBase, normalizeRecipient, SESSION_RECIPIENT_PREFIX, sessionRecipient, validateSendType, } from './mailbox-predicates.js';
|
|
6
|
+
import type { MailboxSessionAffinityContext } from './mailbox-session-sync.js';
|
|
6
7
|
export { acceptMailboxMessageForSession, acceptMailboxMessageForSessionSync, type MailboxSessionAffinityContext, } from './mailbox-session-sync.js';
|
|
7
8
|
export { type ActorMailboxMessage, type MailboxAudience, type MailboxLegacyReportSessionAffinity, type MailboxMessage, type MailboxReceiptRecordV2, type MailboxScopedSessionAffinity, type MailboxSessionAffinity, type MailboxTaskContext, type ReadReceipts, } from './mailbox-message-types.js';
|
|
8
9
|
export interface RegisteredAgent {
|
|
@@ -48,7 +49,18 @@ export interface MailboxQuery {
|
|
|
48
49
|
minPriority?: 'low' | 'normal' | 'high' | undefined;
|
|
49
50
|
limit?: number | undefined;
|
|
50
51
|
since?: string | undefined;
|
|
52
|
+
/** Filters by the message's SENDER session id (origin), not the reader's session. */
|
|
51
53
|
sessionId?: string | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Reader's current session id. When present, messages carrying a
|
|
56
|
+
* session-affinity token for a DIFFERENT session are excluded, matching the
|
|
57
|
+
* inbox checker's receive-side filter (mailbox-attach applySessionAffinityFilter).
|
|
58
|
+
* Kept distinct from `sessionId` (sender origin) so a reader can filter by
|
|
59
|
+
* both simultaneously.
|
|
60
|
+
*/
|
|
61
|
+
currentSessionId?: string | undefined;
|
|
62
|
+
/** Resolver + unscoped policy for session-affinity tokens (see acceptMailboxMessageForSession). */
|
|
63
|
+
sessionAffinityCtx?: MailboxSessionAffinityContext | undefined;
|
|
52
64
|
includeDeleted?: boolean | undefined;
|
|
53
65
|
replyTo?: string | undefined;
|
|
54
66
|
}
|
|
@@ -172,7 +184,7 @@ export interface Mailbox {
|
|
|
172
184
|
registerAgent(input: AgentRegistrationInput): Promise<void>;
|
|
173
185
|
deregisterAgent(agentId: string): Promise<void>;
|
|
174
186
|
heartbeat(input: AgentHeartbeatInput): Promise<void>;
|
|
175
|
-
unreadCount(forAgentId: string, sessionId?: string): Promise<number>;
|
|
187
|
+
unreadCount(forAgentId: string, sessionId?: string, ctx?: MailboxSessionAffinityContext): Promise<number>;
|
|
176
188
|
close(): Promise<void>;
|
|
177
189
|
clearAll(): Promise<void>;
|
|
178
190
|
purgeStale(opts?: PurgeOptions): Promise<PurgeResult>;
|
|
@@ -2,6 +2,7 @@ import type { EventBus } from '../kernel/events.js';
|
|
|
2
2
|
import type { CredentialValidation, IssueCredentialOptions, MailboxCredential } from './mailbox-credential-store.js';
|
|
3
3
|
import type { MailboxEventEmitter } from './mailbox-events.js';
|
|
4
4
|
import type { AgentHeartbeatInput, AgentRegistrationInput, AutoCompactOptions, AutoCompactResult, ClientHeartbeatInput, ClientRegistrationInput, ClientStatus, Mailbox, MailboxAckBatchInput, MailboxAckInput, MailboxAgentStatus, MailboxMessage, MailboxQuery, MailboxSendInput, PurgeOptions, PurgeResult } from './mailbox-types.js';
|
|
5
|
+
import { type MailboxSessionAffinityContext } from './mailbox-types.js';
|
|
5
6
|
export declare const SQLITE_MAILBOX_FILE = "_mailbox.sqlite";
|
|
6
7
|
export declare class SqliteMailbox implements Mailbox {
|
|
7
8
|
readonly projectDir: string;
|
|
@@ -59,7 +60,7 @@ export declare class SqliteMailbox implements Mailbox {
|
|
|
59
60
|
* identity is `leader`. This call path carries no role, matching the
|
|
60
61
|
* `isMailboxMessageVisibleTo(message, forAgentId)` it replaces.
|
|
61
62
|
*/
|
|
62
|
-
unreadCount(forAgentId: string, sessionId?: string): Promise<number>;
|
|
63
|
+
unreadCount(forAgentId: string, sessionId?: string, ctx?: MailboxSessionAffinityContext): Promise<number>;
|
|
63
64
|
softDelete(mailId: string, by: string): Promise<MailboxMessage | null>;
|
|
64
65
|
restore(mailId: string): Promise<MailboxMessage | null>;
|
|
65
66
|
private persistAgent;
|
package/dist/core/index.js
CHANGED
|
@@ -3947,7 +3947,7 @@ function truncate(s, max) {
|
|
|
3947
3947
|
}
|
|
3948
3948
|
|
|
3949
3949
|
// src/types/quota-regex.ts
|
|
3950
|
-
var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)(?:\s+(?:has|have))?(?:\s+been)?[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|usage[-_\s]*limit[-_\s]*(?:reached|exceeded)/i;
|
|
3950
|
+
var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)(?:\s+(?:has|have))?(?:\s+been)?[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|(?:usage|credit)[-_\s]*(?:quota|limit)(?![-_\w])|usage[-_\s]*(?:limit|quota)[-_\s]*(?:reached|exceeded|for)/i;
|
|
3951
3951
|
|
|
3952
3952
|
// src/types/provider.ts
|
|
3953
3953
|
function effectiveInputTokens(usage) {
|
|
@@ -3962,7 +3962,8 @@ function classifyProviderError(status, body, message) {
|
|
|
3962
3962
|
if (status === 0) return "network";
|
|
3963
3963
|
if (status === 408) return "timeout";
|
|
3964
3964
|
if (status === 599) return "stream_hang";
|
|
3965
|
-
if (
|
|
3965
|
+
if (QUOTA_EXHAUSTED_RE.test(text)) return "quota_exhausted";
|
|
3966
|
+
if (status === 402) return "quota_exhausted";
|
|
3966
3967
|
if (status === 429 && body?.message && body.type !== "rate_limit_exceeded" && RATE_LIMIT_EXCEEDED_RE.test(body.message)) {
|
|
3967
3968
|
return "quota_exhausted";
|
|
3968
3969
|
}
|
package/dist/execution/index.js
CHANGED
|
@@ -4854,7 +4854,7 @@ function truncate(s, max) {
|
|
|
4854
4854
|
}
|
|
4855
4855
|
|
|
4856
4856
|
// src/types/quota-regex.ts
|
|
4857
|
-
var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)(?:\s+(?:has|have))?(?:\s+been)?[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|usage[-_\s]*limit[-_\s]*(?:reached|exceeded)/i;
|
|
4857
|
+
var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)(?:\s+(?:has|have))?(?:\s+been)?[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|(?:usage|credit)[-_\s]*(?:quota|limit)(?![-_\w])|usage[-_\s]*(?:limit|quota)[-_\s]*(?:reached|exceeded|for)/i;
|
|
4858
4858
|
|
|
4859
4859
|
// src/types/provider.ts
|
|
4860
4860
|
var CONTEXT_OVERFLOW_RE = /context.length|context.window|maximum context|max.*tokens?.*exceeded|(?:prompt|request|input|messages?).{0,12}too (?:large|long)|exceeds the context|\btokens\b.*exceed|too many tokens|reduce the length|resulted in \d+ tokens|context_length_exceeded/i;
|
|
@@ -4866,7 +4866,8 @@ function classifyProviderError(status, body, message) {
|
|
|
4866
4866
|
if (status === 0) return "network";
|
|
4867
4867
|
if (status === 408) return "timeout";
|
|
4868
4868
|
if (status === 599) return "stream_hang";
|
|
4869
|
-
if (
|
|
4869
|
+
if (QUOTA_EXHAUSTED_RE.test(text)) return "quota_exhausted";
|
|
4870
|
+
if (status === 402) return "quota_exhausted";
|
|
4870
4871
|
if (status === 429 && body?.message && body.type !== "rate_limit_exceeded" && RATE_LIMIT_EXCEEDED_RE.test(body.message)) {
|
|
4871
4872
|
return "quota_exhausted";
|
|
4872
4873
|
}
|
package/dist/index.js
CHANGED
|
@@ -38421,7 +38421,7 @@ import { randomUUID as randomUUID18 } from "node:crypto";
|
|
|
38421
38421
|
init_errors();
|
|
38422
38422
|
|
|
38423
38423
|
// src/types/quota-regex.ts
|
|
38424
|
-
var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)(?:\s+(?:has|have))?(?:\s+been)?[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|usage[-_\s]*limit[-_\s]*(?:reached|exceeded)/i;
|
|
38424
|
+
var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)(?:\s+(?:has|have))?(?:\s+been)?[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|(?:usage|credit)[-_\s]*(?:quota|limit)(?![-_\w])|usage[-_\s]*(?:limit|quota)[-_\s]*(?:reached|exceeded|for)/i;
|
|
38425
38425
|
var ROUTE_SCOPED_QUOTA_RE = /\b(?:for|on)\s+(?:(?:this|the)\s+)?(?:route|model)\b|\b(?:route|model)(?:[-_\s]+[\w.-]+)?[-_\s]*(?:quota|limit)\b|\b(?:quota|limit).{0,24}\b(?:for|on)\s+(?:(?:this|the)\s+)?(?:route|model)\b/i;
|
|
38426
38426
|
|
|
38427
38427
|
// src/types/provider.ts
|
|
@@ -38458,7 +38458,8 @@ function classifyProviderError(status, body, message) {
|
|
|
38458
38458
|
if (status === 0) return "network";
|
|
38459
38459
|
if (status === 408) return "timeout";
|
|
38460
38460
|
if (status === 599) return "stream_hang";
|
|
38461
|
-
if (
|
|
38461
|
+
if (QUOTA_EXHAUSTED_RE.test(text2)) return "quota_exhausted";
|
|
38462
|
+
if (status === 402) return "quota_exhausted";
|
|
38462
38463
|
if (status === 429 && body?.message && body.type !== "rate_limit_exceeded" && RATE_LIMIT_EXCEEDED_RE.test(body.message)) {
|
|
38463
38464
|
return "quota_exhausted";
|
|
38464
38465
|
}
|
|
@@ -73361,8 +73362,8 @@ var EventBus = class {
|
|
|
73361
73362
|
*
|
|
73362
73363
|
* Returns an unsubscribe function.
|
|
73363
73364
|
*/
|
|
73364
|
-
onAny(fn) {
|
|
73365
|
-
return this.onPattern("*", fn);
|
|
73365
|
+
onAny(fn, owner) {
|
|
73366
|
+
return this.onPattern("*", fn, owner);
|
|
73366
73367
|
}
|
|
73367
73368
|
/**
|
|
73368
73369
|
* Subscribe to all events whose name matches a glob-style prefix.
|
|
@@ -73375,16 +73376,16 @@ var EventBus = class {
|
|
|
73375
73376
|
*
|
|
73376
73377
|
* Returns an unsubscribe function.
|
|
73377
73378
|
*/
|
|
73378
|
-
onPattern(pattern, fn) {
|
|
73379
|
+
onPattern(pattern, fn, owner) {
|
|
73379
73380
|
if (this.wildcards.length >= MAX_WILDCARDS) {
|
|
73380
73381
|
this.logger?.error(
|
|
73381
|
-
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onPattern("${pattern}"). Callers must dispose their wildcard listeners to prevent unbounded growth
|
|
73382
|
+
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onPattern("${pattern}")` + (owner ? ` (owner: ${owner})` : "") + ". Callers must dispose their wildcard listeners to prevent unbounded growth."
|
|
73382
73383
|
);
|
|
73383
73384
|
return () => {
|
|
73384
73385
|
};
|
|
73385
73386
|
}
|
|
73386
73387
|
const match = makePatternMatcher(pattern);
|
|
73387
|
-
const entry = { match, fn };
|
|
73388
|
+
const entry = { match, fn, owner };
|
|
73388
73389
|
this.wildcards.push(entry);
|
|
73389
73390
|
this.wildcardSnapshotCache = null;
|
|
73390
73391
|
return () => {
|
|
@@ -73402,15 +73403,15 @@ var EventBus = class {
|
|
|
73402
73403
|
*
|
|
73403
73404
|
* Returns an unsubscribe function.
|
|
73404
73405
|
*/
|
|
73405
|
-
onRegex(regex, fn) {
|
|
73406
|
+
onRegex(regex, fn, owner) {
|
|
73406
73407
|
if (this.wildcards.length >= MAX_WILDCARDS) {
|
|
73407
73408
|
this.logger?.error(
|
|
73408
|
-
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onRegex(${regex}). Callers must dispose their wildcard listeners to prevent unbounded growth
|
|
73409
|
+
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onRegex(${regex})` + (owner ? ` (owner: ${owner})` : "") + ". Callers must dispose their wildcard listeners to prevent unbounded growth."
|
|
73409
73410
|
);
|
|
73410
73411
|
return () => {
|
|
73411
73412
|
};
|
|
73412
73413
|
}
|
|
73413
|
-
const entry = { match: (e) => regex.test(e), fn };
|
|
73414
|
+
const entry = { match: (e) => regex.test(e), fn, owner };
|
|
73414
73415
|
this.wildcards.push(entry);
|
|
73415
73416
|
this.wildcardSnapshotCache = null;
|
|
73416
73417
|
return () => {
|
|
@@ -73588,16 +73589,16 @@ var ScopedEventBus = class extends EventBus {
|
|
|
73588
73589
|
* Subscribe to all events. Alias for `onPattern('*')` — the listener is
|
|
73589
73590
|
* tracked so that `teardown()` will remove it automatically.
|
|
73590
73591
|
*/
|
|
73591
|
-
onAny(fn) {
|
|
73592
|
+
onAny(fn, owner) {
|
|
73592
73593
|
if (this.wildcards.length >= MAX_WILDCARDS) {
|
|
73593
73594
|
this.logger?.error(
|
|
73594
|
-
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onAny(). Callers must dispose their wildcard listeners to prevent unbounded growth
|
|
73595
|
+
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onAny()` + (owner ? ` (owner: ${owner})` : "") + ". Callers must dispose their wildcard listeners to prevent unbounded growth."
|
|
73595
73596
|
);
|
|
73596
73597
|
return () => {
|
|
73597
73598
|
};
|
|
73598
73599
|
}
|
|
73599
73600
|
const key = this.nextKey++;
|
|
73600
|
-
const unsub = EventBus.prototype.onPattern.call(this, "*", fn);
|
|
73601
|
+
const unsub = EventBus.prototype.onPattern.call(this, "*", fn, owner);
|
|
73601
73602
|
this.registrations.set(key, unsub);
|
|
73602
73603
|
return () => {
|
|
73603
73604
|
this.registrations.delete(key);
|
|
@@ -73608,16 +73609,16 @@ var ScopedEventBus = class extends EventBus {
|
|
|
73608
73609
|
* Identical to `EventBus.onPattern` but the listener is tracked so that
|
|
73609
73610
|
* `teardown()` will remove it automatically.
|
|
73610
73611
|
*/
|
|
73611
|
-
onPattern(pattern, fn) {
|
|
73612
|
+
onPattern(pattern, fn, owner) {
|
|
73612
73613
|
if (this.wildcards.length >= MAX_WILDCARDS) {
|
|
73613
73614
|
this.logger?.error(
|
|
73614
|
-
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onPattern("${pattern}"). Callers must dispose their wildcard listeners to prevent unbounded growth
|
|
73615
|
+
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onPattern("${pattern}")` + (owner ? ` (owner: ${owner})` : "") + ". Callers must dispose their wildcard listeners to prevent unbounded growth."
|
|
73615
73616
|
);
|
|
73616
73617
|
return () => {
|
|
73617
73618
|
};
|
|
73618
73619
|
}
|
|
73619
73620
|
const key = this.nextKey++;
|
|
73620
|
-
const unsub = super.onPattern(pattern, fn);
|
|
73621
|
+
const unsub = super.onPattern(pattern, fn, owner);
|
|
73621
73622
|
this.registrations.set(key, unsub);
|
|
73622
73623
|
return () => {
|
|
73623
73624
|
this.registrations.delete(key);
|
|
@@ -73628,16 +73629,16 @@ var ScopedEventBus = class extends EventBus {
|
|
|
73628
73629
|
* Identical to `EventBus.onRegex` but the listener is tracked so that
|
|
73629
73630
|
* `teardown()` will remove it automatically.
|
|
73630
73631
|
*/
|
|
73631
|
-
onRegex(regex, fn) {
|
|
73632
|
+
onRegex(regex, fn, owner) {
|
|
73632
73633
|
if (this.wildcards.length >= MAX_WILDCARDS) {
|
|
73633
73634
|
this.logger?.error(
|
|
73634
|
-
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onRegex(${regex}). Callers must dispose their wildcard listeners to prevent unbounded growth
|
|
73635
|
+
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onRegex(${regex})` + (owner ? ` (owner: ${owner})` : "") + ". Callers must dispose their wildcard listeners to prevent unbounded growth."
|
|
73635
73636
|
);
|
|
73636
73637
|
return () => {
|
|
73637
73638
|
};
|
|
73638
73639
|
}
|
|
73639
73640
|
const key = this.nextKey++;
|
|
73640
|
-
const unsub = super.onRegex(regex, fn);
|
|
73641
|
+
const unsub = super.onRegex(regex, fn, owner);
|
|
73641
73642
|
this.registrations.set(key, unsub);
|
|
73642
73643
|
return () => {
|
|
73643
73644
|
this.registrations.delete(key);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WrongTrace gate-decision events.
|
|
3
|
+
*
|
|
4
|
+
* Emitted by the WrongTrace lock-gate hooks (see
|
|
5
|
+
* `@wrongstack/wrongtrace` hooks.ts) through each host's EventBus:
|
|
6
|
+
* every mutating tool call's gate decision is observable — denied edits,
|
|
7
|
+
* fragile-file nudges, lock acquisition / race-loss / release.
|
|
8
|
+
*
|
|
9
|
+
* Kept structurally matching the adapter's `WrongTraceGateDecisionEvent`
|
|
10
|
+
* union so core never imports the adapter (direction: wrongtrace is the
|
|
11
|
+
* leaf; core must not depend on it).
|
|
12
|
+
*/
|
|
13
|
+
export interface WrongTraceEventMap {
|
|
14
|
+
'wrongtrace.gate.decision': {
|
|
15
|
+
kind: 'deny' | 'allow-fragile' | 'lock-acquired' | 'lock-conflict-race' | 'lock-released';
|
|
16
|
+
/** Target file path the gate decided on. */
|
|
17
|
+
path: string;
|
|
18
|
+
/** `deny` — human-readable owner/expiry reason shown to the model. */
|
|
19
|
+
reason?: string | undefined;
|
|
20
|
+
/** `allow-fragile` — why the file is considered fragile. */
|
|
21
|
+
reasons?: readonly string[] | undefined;
|
|
22
|
+
/** `lock-acquired` — lock owner identity (`wrongstack:<sessionId>`). */
|
|
23
|
+
owner?: string | undefined;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=wrongtrace-events.d.ts.map
|
package/dist/kernel/events.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type { SddEventMap } from './events/sdd-events.js';
|
|
|
14
14
|
import type { SessionEventMap } from './events/session-events.js';
|
|
15
15
|
import type { ToolEventMap } from './events/tool-events.js';
|
|
16
16
|
import type { WorktreeEventMap } from './events/worktree-events.js';
|
|
17
|
+
import type { WrongTraceEventMap } from './events/wrongtrace-events.js';
|
|
17
18
|
/** Distress signals the BrainMonitor watches. See `coordination/brain-monitor.ts`. */
|
|
18
19
|
export type BrainInterventionKind = 'tool_failure_streak' | 'error_storm' | 'agent_stall' | 'file_churn';
|
|
19
20
|
/**
|
|
@@ -48,7 +49,7 @@ export interface TrackedAgentSnapshot {
|
|
|
48
49
|
latestPromptAt?: number | undefined;
|
|
49
50
|
lastActivityAt: string;
|
|
50
51
|
}
|
|
51
|
-
export interface EventMap extends AgentEventMap, BrainEventMap, SessionEventMap, ProviderEventMap, ProcessEventMap, NetworkEventMap, FileEventMap, ToolEventMap, MemoryEventMap, SddEventMap, WorktreeEventMap, FleetEventMap {
|
|
52
|
+
export interface EventMap extends AgentEventMap, BrainEventMap, SessionEventMap, ProviderEventMap, ProcessEventMap, NetworkEventMap, FileEventMap, ToolEventMap, MemoryEventMap, SddEventMap, WorktreeEventMap, FleetEventMap, WrongTraceEventMap {
|
|
52
53
|
}
|
|
53
54
|
export type EventName = keyof EventMap;
|
|
54
55
|
export type Listener<E extends EventName> = (payload: EventMap[E]) => void;
|
|
@@ -60,6 +61,8 @@ export declare class EventBus {
|
|
|
60
61
|
protected readonly wildcards: Array<{
|
|
61
62
|
match: (event: string) => boolean;
|
|
62
63
|
fn: (event: string, payload: unknown) => void;
|
|
64
|
+
/** Optional registration-site label included in cap-rejection warnings. */
|
|
65
|
+
owner?: string | undefined;
|
|
63
66
|
}>;
|
|
64
67
|
protected logger?: EventLogger | undefined;
|
|
65
68
|
/**
|
|
@@ -82,7 +85,9 @@ export declare class EventBus {
|
|
|
82
85
|
*
|
|
83
86
|
* Returns an unsubscribe function.
|
|
84
87
|
*/
|
|
85
|
-
onAny(fn: (event: string, payload: unknown) => void
|
|
88
|
+
onAny(fn: (event: string, payload: unknown) => void,
|
|
89
|
+
/** Optional registration-site label included in cap-rejection warnings. */
|
|
90
|
+
owner?: string): () => void;
|
|
86
91
|
/**
|
|
87
92
|
* Subscribe to all events whose name matches a glob-style prefix.
|
|
88
93
|
* `'tool.*'` matches `tool.started`, `tool.executed`, `tool.progress`, etc.
|
|
@@ -94,7 +99,9 @@ export declare class EventBus {
|
|
|
94
99
|
*
|
|
95
100
|
* Returns an unsubscribe function.
|
|
96
101
|
*/
|
|
97
|
-
onPattern(pattern: string, fn: (event: string, payload: unknown) => void
|
|
102
|
+
onPattern(pattern: string, fn: (event: string, payload: unknown) => void,
|
|
103
|
+
/** Optional registration-site label included in cap-rejection warnings. */
|
|
104
|
+
owner?: string): () => void;
|
|
98
105
|
/**
|
|
99
106
|
* Subscribe to all events whose name matches a RegExp.
|
|
100
107
|
* More flexible than `onPattern` — use when you need regex features
|
|
@@ -102,7 +109,9 @@ export declare class EventBus {
|
|
|
102
109
|
*
|
|
103
110
|
* Returns an unsubscribe function.
|
|
104
111
|
*/
|
|
105
|
-
onRegex(regex: RegExp, fn: (event: string, payload: unknown) => void
|
|
112
|
+
onRegex(regex: RegExp, fn: (event: string, payload: unknown) => void,
|
|
113
|
+
/** Optional registration-site label included in cap-rejection warnings. */
|
|
114
|
+
owner?: string): () => void;
|
|
106
115
|
emit<E extends EventName>(event: E, payload: EventMap[E]): void;
|
|
107
116
|
/**
|
|
108
117
|
* Dispatch array for one event name, or `undefined` when nothing is
|
|
@@ -208,17 +217,17 @@ export declare class ScopedEventBus extends EventBus {
|
|
|
208
217
|
* Subscribe to all events. Alias for `onPattern('*')` — the listener is
|
|
209
218
|
* tracked so that `teardown()` will remove it automatically.
|
|
210
219
|
*/
|
|
211
|
-
onAny(fn: (event: string, payload: unknown) => void): () => void;
|
|
220
|
+
onAny(fn: (event: string, payload: unknown) => void, owner?: string): () => void;
|
|
212
221
|
/**
|
|
213
222
|
* Identical to `EventBus.onPattern` but the listener is tracked so that
|
|
214
223
|
* `teardown()` will remove it automatically.
|
|
215
224
|
*/
|
|
216
|
-
onPattern(pattern: string, fn: (event: string, payload: unknown) => void): () => void;
|
|
225
|
+
onPattern(pattern: string, fn: (event: string, payload: unknown) => void, owner?: string): () => void;
|
|
217
226
|
/**
|
|
218
227
|
* Identical to `EventBus.onRegex` but the listener is tracked so that
|
|
219
228
|
* `teardown()` will remove it automatically.
|
|
220
229
|
*/
|
|
221
|
-
onRegex(regex: RegExp, fn: (event: string, payload: unknown) => void): () => void;
|
|
230
|
+
onRegex(regex: RegExp, fn: (event: string, payload: unknown) => void, owner?: string): () => void;
|
|
222
231
|
/**
|
|
223
232
|
* Remove every listener that was registered through this scoped bus.
|
|
224
233
|
* Idempotent — calling it multiple times is safe.
|
package/dist/kernel/index.js
CHANGED
|
@@ -499,8 +499,8 @@ var EventBus = class {
|
|
|
499
499
|
*
|
|
500
500
|
* Returns an unsubscribe function.
|
|
501
501
|
*/
|
|
502
|
-
onAny(fn) {
|
|
503
|
-
return this.onPattern("*", fn);
|
|
502
|
+
onAny(fn, owner) {
|
|
503
|
+
return this.onPattern("*", fn, owner);
|
|
504
504
|
}
|
|
505
505
|
/**
|
|
506
506
|
* Subscribe to all events whose name matches a glob-style prefix.
|
|
@@ -513,16 +513,16 @@ var EventBus = class {
|
|
|
513
513
|
*
|
|
514
514
|
* Returns an unsubscribe function.
|
|
515
515
|
*/
|
|
516
|
-
onPattern(pattern, fn) {
|
|
516
|
+
onPattern(pattern, fn, owner) {
|
|
517
517
|
if (this.wildcards.length >= MAX_WILDCARDS) {
|
|
518
518
|
this.logger?.error(
|
|
519
|
-
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onPattern("${pattern}"). Callers must dispose their wildcard listeners to prevent unbounded growth
|
|
519
|
+
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onPattern("${pattern}")` + (owner ? ` (owner: ${owner})` : "") + ". Callers must dispose their wildcard listeners to prevent unbounded growth."
|
|
520
520
|
);
|
|
521
521
|
return () => {
|
|
522
522
|
};
|
|
523
523
|
}
|
|
524
524
|
const match = makePatternMatcher(pattern);
|
|
525
|
-
const entry = { match, fn };
|
|
525
|
+
const entry = { match, fn, owner };
|
|
526
526
|
this.wildcards.push(entry);
|
|
527
527
|
this.wildcardSnapshotCache = null;
|
|
528
528
|
return () => {
|
|
@@ -540,15 +540,15 @@ var EventBus = class {
|
|
|
540
540
|
*
|
|
541
541
|
* Returns an unsubscribe function.
|
|
542
542
|
*/
|
|
543
|
-
onRegex(regex, fn) {
|
|
543
|
+
onRegex(regex, fn, owner) {
|
|
544
544
|
if (this.wildcards.length >= MAX_WILDCARDS) {
|
|
545
545
|
this.logger?.error(
|
|
546
|
-
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onRegex(${regex}). Callers must dispose their wildcard listeners to prevent unbounded growth
|
|
546
|
+
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onRegex(${regex})` + (owner ? ` (owner: ${owner})` : "") + ". Callers must dispose their wildcard listeners to prevent unbounded growth."
|
|
547
547
|
);
|
|
548
548
|
return () => {
|
|
549
549
|
};
|
|
550
550
|
}
|
|
551
|
-
const entry = { match: (e) => regex.test(e), fn };
|
|
551
|
+
const entry = { match: (e) => regex.test(e), fn, owner };
|
|
552
552
|
this.wildcards.push(entry);
|
|
553
553
|
this.wildcardSnapshotCache = null;
|
|
554
554
|
return () => {
|
|
@@ -726,16 +726,16 @@ var ScopedEventBus = class extends EventBus {
|
|
|
726
726
|
* Subscribe to all events. Alias for `onPattern('*')` — the listener is
|
|
727
727
|
* tracked so that `teardown()` will remove it automatically.
|
|
728
728
|
*/
|
|
729
|
-
onAny(fn) {
|
|
729
|
+
onAny(fn, owner) {
|
|
730
730
|
if (this.wildcards.length >= MAX_WILDCARDS) {
|
|
731
731
|
this.logger?.error(
|
|
732
|
-
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onAny(). Callers must dispose their wildcard listeners to prevent unbounded growth
|
|
732
|
+
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onAny()` + (owner ? ` (owner: ${owner})` : "") + ". Callers must dispose their wildcard listeners to prevent unbounded growth."
|
|
733
733
|
);
|
|
734
734
|
return () => {
|
|
735
735
|
};
|
|
736
736
|
}
|
|
737
737
|
const key = this.nextKey++;
|
|
738
|
-
const unsub = EventBus.prototype.onPattern.call(this, "*", fn);
|
|
738
|
+
const unsub = EventBus.prototype.onPattern.call(this, "*", fn, owner);
|
|
739
739
|
this.registrations.set(key, unsub);
|
|
740
740
|
return () => {
|
|
741
741
|
this.registrations.delete(key);
|
|
@@ -746,16 +746,16 @@ var ScopedEventBus = class extends EventBus {
|
|
|
746
746
|
* Identical to `EventBus.onPattern` but the listener is tracked so that
|
|
747
747
|
* `teardown()` will remove it automatically.
|
|
748
748
|
*/
|
|
749
|
-
onPattern(pattern, fn) {
|
|
749
|
+
onPattern(pattern, fn, owner) {
|
|
750
750
|
if (this.wildcards.length >= MAX_WILDCARDS) {
|
|
751
751
|
this.logger?.error(
|
|
752
|
-
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onPattern("${pattern}"). Callers must dispose their wildcard listeners to prevent unbounded growth
|
|
752
|
+
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onPattern("${pattern}")` + (owner ? ` (owner: ${owner})` : "") + ". Callers must dispose their wildcard listeners to prevent unbounded growth."
|
|
753
753
|
);
|
|
754
754
|
return () => {
|
|
755
755
|
};
|
|
756
756
|
}
|
|
757
757
|
const key = this.nextKey++;
|
|
758
|
-
const unsub = super.onPattern(pattern, fn);
|
|
758
|
+
const unsub = super.onPattern(pattern, fn, owner);
|
|
759
759
|
this.registrations.set(key, unsub);
|
|
760
760
|
return () => {
|
|
761
761
|
this.registrations.delete(key);
|
|
@@ -766,16 +766,16 @@ var ScopedEventBus = class extends EventBus {
|
|
|
766
766
|
* Identical to `EventBus.onRegex` but the listener is tracked so that
|
|
767
767
|
* `teardown()` will remove it automatically.
|
|
768
768
|
*/
|
|
769
|
-
onRegex(regex, fn) {
|
|
769
|
+
onRegex(regex, fn, owner) {
|
|
770
770
|
if (this.wildcards.length >= MAX_WILDCARDS) {
|
|
771
771
|
this.logger?.error(
|
|
772
|
-
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onRegex(${regex}). Callers must dispose their wildcard listeners to prevent unbounded growth
|
|
772
|
+
`EventBus wildcard limit (${MAX_WILDCARDS}) reached \u2014 rejecting onRegex(${regex})` + (owner ? ` (owner: ${owner})` : "") + ". Callers must dispose their wildcard listeners to prevent unbounded growth."
|
|
773
773
|
);
|
|
774
774
|
return () => {
|
|
775
775
|
};
|
|
776
776
|
}
|
|
777
777
|
const key = this.nextKey++;
|
|
778
|
-
const unsub = super.onRegex(regex, fn);
|
|
778
|
+
const unsub = super.onRegex(regex, fn, owner);
|
|
779
779
|
this.registrations.set(key, unsub);
|
|
780
780
|
return () => {
|
|
781
781
|
this.registrations.delete(key);
|
package/dist/tools/index.js
CHANGED
|
@@ -1551,7 +1551,7 @@ var FsError = class extends WrongStackError {
|
|
|
1551
1551
|
};
|
|
1552
1552
|
|
|
1553
1553
|
// src/types/quota-regex.ts
|
|
1554
|
-
var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)(?:\s+(?:has|have))?(?:\s+been)?[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|usage[-_\s]*limit[-_\s]*(?:reached|exceeded)/i;
|
|
1554
|
+
var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)(?:\s+(?:has|have))?(?:\s+been)?[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|(?:usage|credit)[-_\s]*(?:quota|limit)(?![-_\w])|usage[-_\s]*(?:limit|quota)[-_\s]*(?:reached|exceeded|for)/i;
|
|
1555
1555
|
|
|
1556
1556
|
// src/types/provider.ts
|
|
1557
1557
|
var CONTEXT_OVERFLOW_RE = /context.length|context.window|maximum context|max.*tokens?.*exceeded|(?:prompt|request|input|messages?).{0,12}too (?:large|long)|exceeds the context|\btokens\b.*exceed|too many tokens|reduce the length|resulted in \d+ tokens|context_length_exceeded/i;
|
|
@@ -1563,7 +1563,8 @@ function classifyProviderError(status, body, message) {
|
|
|
1563
1563
|
if (status === 0) return "network";
|
|
1564
1564
|
if (status === 408) return "timeout";
|
|
1565
1565
|
if (status === 599) return "stream_hang";
|
|
1566
|
-
if (
|
|
1566
|
+
if (QUOTA_EXHAUSTED_RE.test(text)) return "quota_exhausted";
|
|
1567
|
+
if (status === 402) return "quota_exhausted";
|
|
1567
1568
|
if (status === 429 && body?.message && body.type !== "rate_limit_exceeded" && RATE_LIMIT_EXCEEDED_RE.test(body.message)) {
|
|
1568
1569
|
return "quota_exhausted";
|
|
1569
1570
|
}
|
|
@@ -111,7 +111,7 @@ export interface ToolsConfig {
|
|
|
111
111
|
nextsteps?: NextStepsToolConfig | undefined;
|
|
112
112
|
/**
|
|
113
113
|
* WrongProxy / WrongTrace: automatic base-URL rerouting through a
|
|
114
|
-
* local proxy daemon (default `http://localhost:
|
|
114
|
+
* local proxy daemon (default `http://localhost:3444`). When
|
|
115
115
|
* `enabled` is true AND the daemon at `url` is reachable, every
|
|
116
116
|
* provider's base URL is rewritten through
|
|
117
117
|
* `${url}/proxy/<host><path>`. openai-codex is excluded by spec.
|
|
@@ -134,7 +134,7 @@ export interface WrongProxyToolConfig {
|
|
|
134
134
|
enabled?: boolean | undefined;
|
|
135
135
|
/**
|
|
136
136
|
* Where the local proxy daemon listens. Default
|
|
137
|
-
* `http://localhost:
|
|
137
|
+
* `http://localhost:3444`. The CLI's periodic probe targets
|
|
138
138
|
* `<url>/api/health`; a 2xx response flips the runtime's
|
|
139
139
|
* `active` flag.
|
|
140
140
|
*/
|
package/dist/types/index.js
CHANGED
|
@@ -912,7 +912,7 @@ function truncate(s, max) {
|
|
|
912
912
|
}
|
|
913
913
|
|
|
914
914
|
// src/types/quota-regex.ts
|
|
915
|
-
var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)(?:\s+(?:has|have))?(?:\s+been)?[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|usage[-_\s]*limit[-_\s]*(?:reached|exceeded)/i;
|
|
915
|
+
var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)(?:\s+(?:has|have))?(?:\s+been)?[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|(?:usage|credit)[-_\s]*(?:quota|limit)(?![-_\w])|usage[-_\s]*(?:limit|quota)[-_\s]*(?:reached|exceeded|for)/i;
|
|
916
916
|
|
|
917
917
|
// src/types/provider.ts
|
|
918
918
|
var REASONING_EFFORT_LEVELS = [
|
|
@@ -948,7 +948,8 @@ function classifyProviderError(status, body, message) {
|
|
|
948
948
|
if (status === 0) return "network";
|
|
949
949
|
if (status === 408) return "timeout";
|
|
950
950
|
if (status === 599) return "stream_hang";
|
|
951
|
-
if (
|
|
951
|
+
if (QUOTA_EXHAUSTED_RE.test(text)) return "quota_exhausted";
|
|
952
|
+
if (status === 402) return "quota_exhausted";
|
|
952
953
|
if (status === 429 && body?.message && body.type !== "rate_limit_exceeded" && RATE_LIMIT_EXCEEDED_RE.test(body.message)) {
|
|
953
954
|
return "quota_exhausted";
|
|
954
955
|
}
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Example:
|
|
10
10
|
* original = "https://api.openai.com/v1"
|
|
11
|
-
* proxyUrl = "http://localhost:
|
|
12
|
-
* output = "http://localhost:
|
|
11
|
+
* proxyUrl = "http://localhost:3444"
|
|
12
|
+
* output = "http://localhost:3444/proxy/api.openai.com/v1"
|
|
13
13
|
*
|
|
14
14
|
* The host appears *without* a scheme in the path; the proxy terminates
|
|
15
15
|
* TLS (or speaks plain HTTP for localhost) and forwards the original
|
|
@@ -56,10 +56,25 @@ export interface ProxyConfig {
|
|
|
56
56
|
}
|
|
57
57
|
/** Read the current proxy configuration. Safe to call from any layer. */
|
|
58
58
|
export declare function getProxyConfig(): ProxyConfig;
|
|
59
|
+
/**
|
|
60
|
+
* Listener notified by `applyProxyConfig` when the resulting config
|
|
61
|
+
* MATERIALLY changed (`enabled` / `url` / `active`). The periodic probe
|
|
62
|
+
* re-writes the same healthy values every tick, so a value-identical
|
|
63
|
+
* write must NOT notify — subscribers (e.g. the instant-apply provider
|
|
64
|
+
* rebuilder) would otherwise run every probe interval.
|
|
65
|
+
*/
|
|
66
|
+
export type ProxyConfigListener = (next: ProxyConfig, previous: ProxyConfig) => void;
|
|
67
|
+
/**
|
|
68
|
+
* Subscribe to material proxy-config changes. Returns an unsubscribe
|
|
69
|
+
* function. A throwing listener is isolated: it cannot break the probe
|
|
70
|
+
* loop or starve other subscribers.
|
|
71
|
+
*/
|
|
72
|
+
export declare function subscribeToProxyConfig(listener: ProxyConfigListener): () => void;
|
|
59
73
|
/**
|
|
60
74
|
* Apply a new proxy configuration. Returns the previous config so callers
|
|
61
75
|
* (notably the probe) can decide whether the change requires an immediate
|
|
62
|
-
* probe vs. waiting for the next tick.
|
|
76
|
+
* probe vs. waiting for the next tick. Notifies subscribers only when the
|
|
77
|
+
* merged result actually differs from the previous state.
|
|
63
78
|
*/
|
|
64
79
|
export declare function applyProxyConfig(next: Partial<ProxyConfig>): ProxyConfig;
|
|
65
80
|
/**
|
|
@@ -70,7 +85,62 @@ export declare function applyProxyConfig(next: Partial<ProxyConfig>): ProxyConfi
|
|
|
70
85
|
export declare function shouldRewriteFor(providerId: string): boolean;
|
|
71
86
|
/**
|
|
72
87
|
* Reset to defaults. Intended for tests; do NOT call from production code
|
|
73
|
-
* (the singleton lives for the lifetime of the process).
|
|
88
|
+
* (the singleton lives for the lifetime of the process). Also drops all
|
|
89
|
+
* change listeners so tests never observe each other's notifications.
|
|
74
90
|
*/
|
|
75
91
|
export declare function __resetProxyConfigForTests(): void;
|
|
92
|
+
/** Minimal structural logger — keeps this module dependency-free. */
|
|
93
|
+
export interface ProxyInstantApplyLogger {
|
|
94
|
+
info: (message: string) => void;
|
|
95
|
+
warn: (message: string) => void;
|
|
96
|
+
}
|
|
97
|
+
export interface ProxyInstantApplyDeps {
|
|
98
|
+
/** Current active provider id — the provider whose URL is baked in. */
|
|
99
|
+
getActiveProviderId: () => string;
|
|
100
|
+
/**
|
|
101
|
+
* Raw (pre-rewrite) base URL for a provider id, exactly as
|
|
102
|
+
* `resolveProviderCfg` reads it: `savedCfg?.baseUrl ?? config.baseUrl`.
|
|
103
|
+
* The singleton holds no provider configs, so the caller — who owns the
|
|
104
|
+
* live Config — must inject this reader.
|
|
105
|
+
*/
|
|
106
|
+
getRawBaseUrl: (providerId: string) => string | undefined;
|
|
107
|
+
/**
|
|
108
|
+
* Rebuild the live provider for `providerId` and swap it into the live
|
|
109
|
+
* context. Callers inject their existing path — e.g.
|
|
110
|
+
* `buildProviderForModel(providerId, model)` — which re-resolves
|
|
111
|
+
* proxy-aware config via `shouldRewriteFor` on every build. Should
|
|
112
|
+
* serialize the swap through the host's model-transition gate and
|
|
113
|
+
* re-check the live provider id inside it (superseded guard).
|
|
114
|
+
*/
|
|
115
|
+
rebuildProvider: (providerId: string) => Promise<void>;
|
|
116
|
+
/** Structured logger for the rebuild decisions. */
|
|
117
|
+
logger: ProxyInstantApplyLogger;
|
|
118
|
+
}
|
|
119
|
+
export interface ProxyInstantApplyHandle {
|
|
120
|
+
/** Detach the subscription. Safe to call more than once. */
|
|
121
|
+
dispose: () => void;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Subscribe to material proxy-config changes and rebuild the live
|
|
125
|
+
* provider when the routing verdict for the ACTIVE provider changes.
|
|
126
|
+
*
|
|
127
|
+
* Comparison is on the effective base URL, so:
|
|
128
|
+
* - probe ticks that rewrite identical values never rebuild;
|
|
129
|
+
* - a proxy URL change while enabled+active rebuilds (new target);
|
|
130
|
+
* - deactivation rebuilds (proxy-rewritten → raw);
|
|
131
|
+
* - toggling off while already direct does NOT rebuild;
|
|
132
|
+
* - a provider/model switch between notifications re-baselines against
|
|
133
|
+
* the PREVIOUS config — the config the switch actually built under —
|
|
134
|
+
* and then falls through to the verdict comparison. Seeding from the
|
|
135
|
+
* new config instead would swallow a needed rebuild: a provider that
|
|
136
|
+
* switched in while the proxy was on (built rewritten) must rebuild
|
|
137
|
+
* direct when the very next change deactivates the proxy.
|
|
138
|
+
*
|
|
139
|
+
* Rebuilds are SERIALIZED: a toggle-off immediately followed by a probe
|
|
140
|
+
* verdict must not race two async rebuilds against each other — the
|
|
141
|
+
* slower, stale build could overwrite the fresh provider swap. Each
|
|
142
|
+
* rebuild re-reads proxy state at build time, so the chain converges on
|
|
143
|
+
* the latest verdict.
|
|
144
|
+
*/
|
|
145
|
+
export declare function createProxyInstantApply(deps: ProxyInstantApplyDeps): ProxyInstantApplyHandle;
|
|
76
146
|
//# sourceMappingURL=proxy-rewrite.d.ts.map
|
|
@@ -41,6 +41,13 @@ var currentConfig = { ...DEFAULT_PROXY_CONFIG };
|
|
|
41
41
|
function getProxyConfig() {
|
|
42
42
|
return currentConfig;
|
|
43
43
|
}
|
|
44
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
45
|
+
function subscribeToProxyConfig(listener) {
|
|
46
|
+
listeners.add(listener);
|
|
47
|
+
return () => {
|
|
48
|
+
listeners.delete(listener);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
44
51
|
function applyProxyConfig(next) {
|
|
45
52
|
const previous = currentConfig;
|
|
46
53
|
currentConfig = {
|
|
@@ -48,6 +55,15 @@ function applyProxyConfig(next) {
|
|
|
48
55
|
url: next.url ?? previous.url,
|
|
49
56
|
active: next.active ?? previous.active
|
|
50
57
|
};
|
|
58
|
+
const materialChange = currentConfig.enabled !== previous.enabled || currentConfig.url !== previous.url || currentConfig.active !== previous.active;
|
|
59
|
+
if (materialChange) {
|
|
60
|
+
for (const listener of listeners) {
|
|
61
|
+
try {
|
|
62
|
+
listener(currentConfig, previous);
|
|
63
|
+
} catch {
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
51
67
|
return previous;
|
|
52
68
|
}
|
|
53
69
|
function shouldRewriteFor(providerId) {
|
|
@@ -57,14 +73,60 @@ function shouldRewriteFor(providerId) {
|
|
|
57
73
|
}
|
|
58
74
|
function __resetProxyConfigForTests() {
|
|
59
75
|
currentConfig = { ...DEFAULT_PROXY_CONFIG };
|
|
76
|
+
listeners.clear();
|
|
77
|
+
}
|
|
78
|
+
function effectiveBaseUrlFor(cfg, providerId, rawBaseUrl) {
|
|
79
|
+
if (!rawBaseUrl) return void 0;
|
|
80
|
+
const active = cfg.enabled && cfg.active && !!cfg.url && isProxyEligible(providerId);
|
|
81
|
+
return active ? rewriteBaseUrl(rawBaseUrl, cfg.url) : rawBaseUrl;
|
|
82
|
+
}
|
|
83
|
+
function effectiveBaseUrl(providerId, rawBaseUrl) {
|
|
84
|
+
return effectiveBaseUrlFor(currentConfig, providerId, rawBaseUrl);
|
|
85
|
+
}
|
|
86
|
+
function createProxyInstantApply(deps) {
|
|
87
|
+
const { getActiveProviderId, getRawBaseUrl, rebuildProvider, logger } = deps;
|
|
88
|
+
let lastProviderId = getActiveProviderId();
|
|
89
|
+
let lastEffectiveUrl = effectiveBaseUrl(lastProviderId, getRawBaseUrl(lastProviderId));
|
|
90
|
+
let disposed = false;
|
|
91
|
+
let rebuildChain = Promise.resolve();
|
|
92
|
+
const unsubscribe = subscribeToProxyConfig((next, previous) => {
|
|
93
|
+
if (disposed) return;
|
|
94
|
+
const providerId = getActiveProviderId();
|
|
95
|
+
const raw = getRawBaseUrl(providerId);
|
|
96
|
+
const nextUrl = effectiveBaseUrlFor(next, providerId, raw);
|
|
97
|
+
const baselineUrl = providerId === lastProviderId ? lastEffectiveUrl : effectiveBaseUrlFor(previous, providerId, raw);
|
|
98
|
+
lastProviderId = providerId;
|
|
99
|
+
lastEffectiveUrl = nextUrl;
|
|
100
|
+
if (nextUrl === baselineUrl) return;
|
|
101
|
+
rebuildChain = rebuildChain.then(() => rebuildProvider(providerId)).then(() => {
|
|
102
|
+
if (!disposed) {
|
|
103
|
+
logger.info(`WrongProxy routing changed \u2014 live provider rebuilt (${providerId})`);
|
|
104
|
+
}
|
|
105
|
+
}).catch((err) => {
|
|
106
|
+
if (!disposed) {
|
|
107
|
+
logger.warn(
|
|
108
|
+
`WrongProxy instant-apply: provider rebuild failed for ${providerId}: ${err instanceof Error ? err.message : String(err)}`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
return {
|
|
114
|
+
dispose() {
|
|
115
|
+
if (disposed) return;
|
|
116
|
+
disposed = true;
|
|
117
|
+
unsubscribe();
|
|
118
|
+
}
|
|
119
|
+
};
|
|
60
120
|
}
|
|
61
121
|
export {
|
|
62
122
|
PROXY_EXCLUDED_PROVIDERS,
|
|
63
123
|
__resetProxyConfigForTests,
|
|
64
124
|
applyProxyConfig,
|
|
125
|
+
createProxyInstantApply,
|
|
65
126
|
getProxyConfig,
|
|
66
127
|
isProxyEligible,
|
|
67
128
|
rewriteBaseUrl,
|
|
68
|
-
shouldRewriteFor
|
|
129
|
+
shouldRewriteFor,
|
|
130
|
+
subscribeToProxyConfig
|
|
69
131
|
};
|
|
70
132
|
//# sourceMappingURL=proxy-rewrite.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.313.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack core: kernel, types, and shared utilities for the WrongStack CLI agent.",
|
|
6
6
|
"repository": {
|
|
@@ -182,9 +182,9 @@
|
|
|
182
182
|
"wrongstackApiVersion": "0.1.10",
|
|
183
183
|
"dependencies": {
|
|
184
184
|
"zod": "4.4.3",
|
|
185
|
-
"@wrongstack/
|
|
186
|
-
"@wrongstack/
|
|
187
|
-
"@wrongstack/persistence": "0.
|
|
185
|
+
"@wrongstack/kanban": "0.313.0",
|
|
186
|
+
"@wrongstack/primitives": "0.313.0",
|
|
187
|
+
"@wrongstack/persistence": "0.313.0"
|
|
188
188
|
},
|
|
189
189
|
"devDependencies": {
|
|
190
190
|
"@types/node": "^22.19.0",
|