@wrongstack/core 0.310.0 → 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 +22 -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/runtime.d.ts +7 -0
- package/dist/types/config/tools.d.ts +30 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +5 -2
- package/dist/wiring/proxy-rewrite.d.ts +146 -0
- package/dist/wiring/proxy-rewrite.js +132 -0
- package/package.json +8 -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
|
@@ -3484,6 +3484,7 @@ function resolveTokenSavingTier(val, maxContext) {
|
|
|
3484
3484
|
}
|
|
3485
3485
|
var DEFAULT_TUI_THINKING_WORD = "thinking";
|
|
3486
3486
|
var MAX_TUI_THINKING_WORD_LENGTH = 16;
|
|
3487
|
+
var MAX_WRONGPROXY_URL_LENGTH = 2048;
|
|
3487
3488
|
function normalizeTuiThinkingWord(value) {
|
|
3488
3489
|
if (typeof value !== "string") return DEFAULT_TUI_THINKING_WORD;
|
|
3489
3490
|
const word = value.trim();
|
|
@@ -38420,7 +38421,7 @@ import { randomUUID as randomUUID18 } from "node:crypto";
|
|
|
38420
38421
|
init_errors();
|
|
38421
38422
|
|
|
38422
38423
|
// src/types/quota-regex.ts
|
|
38423
|
-
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;
|
|
38424
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;
|
|
38425
38426
|
|
|
38426
38427
|
// src/types/provider.ts
|
|
@@ -38457,7 +38458,8 @@ function classifyProviderError(status, body, message) {
|
|
|
38457
38458
|
if (status === 0) return "network";
|
|
38458
38459
|
if (status === 408) return "timeout";
|
|
38459
38460
|
if (status === 599) return "stream_hang";
|
|
38460
|
-
if (
|
|
38461
|
+
if (QUOTA_EXHAUSTED_RE.test(text2)) return "quota_exhausted";
|
|
38462
|
+
if (status === 402) return "quota_exhausted";
|
|
38461
38463
|
if (status === 429 && body?.message && body.type !== "rate_limit_exceeded" && RATE_LIMIT_EXCEEDED_RE.test(body.message)) {
|
|
38462
38464
|
return "quota_exhausted";
|
|
38463
38465
|
}
|
|
@@ -73360,8 +73362,8 @@ var EventBus = class {
|
|
|
73360
73362
|
*
|
|
73361
73363
|
* Returns an unsubscribe function.
|
|
73362
73364
|
*/
|
|
73363
|
-
onAny(fn) {
|
|
73364
|
-
return this.onPattern("*", fn);
|
|
73365
|
+
onAny(fn, owner) {
|
|
73366
|
+
return this.onPattern("*", fn, owner);
|
|
73365
73367
|
}
|
|
73366
73368
|
/**
|
|
73367
73369
|
* Subscribe to all events whose name matches a glob-style prefix.
|
|
@@ -73374,16 +73376,16 @@ var EventBus = class {
|
|
|
73374
73376
|
*
|
|
73375
73377
|
* Returns an unsubscribe function.
|
|
73376
73378
|
*/
|
|
73377
|
-
onPattern(pattern, fn) {
|
|
73379
|
+
onPattern(pattern, fn, owner) {
|
|
73378
73380
|
if (this.wildcards.length >= MAX_WILDCARDS) {
|
|
73379
73381
|
this.logger?.error(
|
|
73380
|
-
`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."
|
|
73381
73383
|
);
|
|
73382
73384
|
return () => {
|
|
73383
73385
|
};
|
|
73384
73386
|
}
|
|
73385
73387
|
const match = makePatternMatcher(pattern);
|
|
73386
|
-
const entry = { match, fn };
|
|
73388
|
+
const entry = { match, fn, owner };
|
|
73387
73389
|
this.wildcards.push(entry);
|
|
73388
73390
|
this.wildcardSnapshotCache = null;
|
|
73389
73391
|
return () => {
|
|
@@ -73401,15 +73403,15 @@ var EventBus = class {
|
|
|
73401
73403
|
*
|
|
73402
73404
|
* Returns an unsubscribe function.
|
|
73403
73405
|
*/
|
|
73404
|
-
onRegex(regex, fn) {
|
|
73406
|
+
onRegex(regex, fn, owner) {
|
|
73405
73407
|
if (this.wildcards.length >= MAX_WILDCARDS) {
|
|
73406
73408
|
this.logger?.error(
|
|
73407
|
-
`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."
|
|
73408
73410
|
);
|
|
73409
73411
|
return () => {
|
|
73410
73412
|
};
|
|
73411
73413
|
}
|
|
73412
|
-
const entry = { match: (e) => regex.test(e), fn };
|
|
73414
|
+
const entry = { match: (e) => regex.test(e), fn, owner };
|
|
73413
73415
|
this.wildcards.push(entry);
|
|
73414
73416
|
this.wildcardSnapshotCache = null;
|
|
73415
73417
|
return () => {
|
|
@@ -73587,16 +73589,16 @@ var ScopedEventBus = class extends EventBus {
|
|
|
73587
73589
|
* Subscribe to all events. Alias for `onPattern('*')` — the listener is
|
|
73588
73590
|
* tracked so that `teardown()` will remove it automatically.
|
|
73589
73591
|
*/
|
|
73590
|
-
onAny(fn) {
|
|
73592
|
+
onAny(fn, owner) {
|
|
73591
73593
|
if (this.wildcards.length >= MAX_WILDCARDS) {
|
|
73592
73594
|
this.logger?.error(
|
|
73593
|
-
`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."
|
|
73594
73596
|
);
|
|
73595
73597
|
return () => {
|
|
73596
73598
|
};
|
|
73597
73599
|
}
|
|
73598
73600
|
const key = this.nextKey++;
|
|
73599
|
-
const unsub = EventBus.prototype.onPattern.call(this, "*", fn);
|
|
73601
|
+
const unsub = EventBus.prototype.onPattern.call(this, "*", fn, owner);
|
|
73600
73602
|
this.registrations.set(key, unsub);
|
|
73601
73603
|
return () => {
|
|
73602
73604
|
this.registrations.delete(key);
|
|
@@ -73607,16 +73609,16 @@ var ScopedEventBus = class extends EventBus {
|
|
|
73607
73609
|
* Identical to `EventBus.onPattern` but the listener is tracked so that
|
|
73608
73610
|
* `teardown()` will remove it automatically.
|
|
73609
73611
|
*/
|
|
73610
|
-
onPattern(pattern, fn) {
|
|
73612
|
+
onPattern(pattern, fn, owner) {
|
|
73611
73613
|
if (this.wildcards.length >= MAX_WILDCARDS) {
|
|
73612
73614
|
this.logger?.error(
|
|
73613
|
-
`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."
|
|
73614
73616
|
);
|
|
73615
73617
|
return () => {
|
|
73616
73618
|
};
|
|
73617
73619
|
}
|
|
73618
73620
|
const key = this.nextKey++;
|
|
73619
|
-
const unsub = super.onPattern(pattern, fn);
|
|
73621
|
+
const unsub = super.onPattern(pattern, fn, owner);
|
|
73620
73622
|
this.registrations.set(key, unsub);
|
|
73621
73623
|
return () => {
|
|
73622
73624
|
this.registrations.delete(key);
|
|
@@ -73627,16 +73629,16 @@ var ScopedEventBus = class extends EventBus {
|
|
|
73627
73629
|
* Identical to `EventBus.onRegex` but the listener is tracked so that
|
|
73628
73630
|
* `teardown()` will remove it automatically.
|
|
73629
73631
|
*/
|
|
73630
|
-
onRegex(regex, fn) {
|
|
73632
|
+
onRegex(regex, fn, owner) {
|
|
73631
73633
|
if (this.wildcards.length >= MAX_WILDCARDS) {
|
|
73632
73634
|
this.logger?.error(
|
|
73633
|
-
`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."
|
|
73634
73636
|
);
|
|
73635
73637
|
return () => {
|
|
73636
73638
|
};
|
|
73637
73639
|
}
|
|
73638
73640
|
const key = this.nextKey++;
|
|
73639
|
-
const unsub = super.onRegex(regex, fn);
|
|
73641
|
+
const unsub = super.onRegex(regex, fn, owner);
|
|
73640
73642
|
this.registrations.set(key, unsub);
|
|
73641
73643
|
return () => {
|
|
73642
73644
|
this.registrations.delete(key);
|
|
@@ -92933,6 +92935,7 @@ export {
|
|
|
92933
92935
|
MAX_SUBAGENT_STRUCTURED_REPORT_CHARS,
|
|
92934
92936
|
MAX_SUBJECT_LEN,
|
|
92935
92937
|
MAX_TUI_THINKING_WORD_LENGTH,
|
|
92938
|
+
MAX_WRONGPROXY_URL_LENGTH,
|
|
92936
92939
|
MEDIUM_BUDGET,
|
|
92937
92940
|
MEMORY_EVIDENCE_TAG,
|
|
92938
92941
|
MEMORY_TYPE_LABELS,
|
|
@@ -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
|
}
|
|
@@ -162,6 +162,13 @@ export declare function normalizeTokenSavingTier(val?: TokenSavingTier | boolean
|
|
|
162
162
|
export declare function resolveTokenSavingTier(val: TokenSavingTier | boolean | undefined, maxContext: number | undefined): ConcreteTokenSavingTier;
|
|
163
163
|
export declare const DEFAULT_TUI_THINKING_WORD = "thinking";
|
|
164
164
|
export declare const MAX_TUI_THINKING_WORD_LENGTH = 16;
|
|
165
|
+
/**
|
|
166
|
+
* Hard cap on the WrongProxy / WrongTrace URL draft. URLs don't have a
|
|
167
|
+
* strict length limit, but capping to a sane 2 KiB prevents a runaway
|
|
168
|
+
* paste from bloating the `settingsPicker` slice. The runtime probe
|
|
169
|
+
* accepts any well-formed URL up to whatever the OS / fetch allow.
|
|
170
|
+
*/
|
|
171
|
+
export declare const MAX_WRONGPROXY_URL_LENGTH = 2048;
|
|
165
172
|
/**
|
|
166
173
|
* Normalize the configurable statusline word shown while the TUI is working.
|
|
167
174
|
* The value must be a single short word; invalid values fall back to the default.
|
|
@@ -109,6 +109,36 @@ export interface ToolsConfig {
|
|
|
109
109
|
* in the next session (same as `features.tokenSavingMode`).
|
|
110
110
|
*/
|
|
111
111
|
nextsteps?: NextStepsToolConfig | undefined;
|
|
112
|
+
/**
|
|
113
|
+
* WrongProxy / WrongTrace: automatic base-URL rerouting through a
|
|
114
|
+
* local proxy daemon (default `http://localhost:3444`). When
|
|
115
|
+
* `enabled` is true AND the daemon at `url` is reachable, every
|
|
116
|
+
* provider's base URL is rewritten through
|
|
117
|
+
* `${url}/proxy/<host><path>`. openai-codex is excluded by spec.
|
|
118
|
+
*
|
|
119
|
+
* Mirrors the WebUI `LocalPrefs` shape (single object with two
|
|
120
|
+
* fields, not two top-level keys). Persisted to the encrypted
|
|
121
|
+
* profile config and mirrored into `ctx.meta` by the TUI settings
|
|
122
|
+
* adapter so the runtime probe can read it mid-session.
|
|
123
|
+
*/
|
|
124
|
+
wrongProxy?: WrongProxyToolConfig | undefined;
|
|
125
|
+
}
|
|
126
|
+
/** WrongProxy / WrongTrace tool-config (`tools.wrongProxy`). */
|
|
127
|
+
export interface WrongProxyToolConfig {
|
|
128
|
+
/**
|
|
129
|
+
* Master switch. When true AND the daemon at `url` is reachable,
|
|
130
|
+
* every provider's base URL is rewritten through
|
|
131
|
+
* `${url}/proxy/<host><path>`. openai-codex is excluded by spec.
|
|
132
|
+
* Default: false.
|
|
133
|
+
*/
|
|
134
|
+
enabled?: boolean | undefined;
|
|
135
|
+
/**
|
|
136
|
+
* Where the local proxy daemon listens. Default
|
|
137
|
+
* `http://localhost:3444`. The CLI's periodic probe targets
|
|
138
|
+
* `<url>/api/health`; a 2xx response flips the runtime's
|
|
139
|
+
* `active` flag.
|
|
140
|
+
*/
|
|
141
|
+
url?: string | undefined;
|
|
112
142
|
}
|
|
113
143
|
/** Opt-in switch for the agent-callable `nextsteps` tool (`tools.nextsteps`). */
|
|
114
144
|
export interface NextStepsToolConfig {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export type { ContentBlock, ImageBlock, TextBlock, ThinkingBlock, ToolResultBloc
|
|
|
4
4
|
export { isImageBlock, isTextBlock, isToolResultBlock, isToolUseBlock } from './blocks.js';
|
|
5
5
|
export type { Compactor, CompactReport } from './compactor.js';
|
|
6
6
|
export type { AdaptiveConcurrencyConfig, AgentLearningConfig, AutonomyConfig, BrainConfig, BrainCouncilConfig, BrainCouncilVoterConfig, BrainModelEntry, CircuitBreakerRuntimeConfig, ConcreteTokenSavingTier, Config, ConfigLoader, ConfigStore, ContextConfig, CouncilPersonaDefinition, CouncilToolConfig, CouncilToolProfileDefinition, CustomModelDefinition, ExecDangerConfig, ExecToolConfig, FeaturesConfig, FleetChatVerbosity, FleetConfig, FleetSupervisorConfig, GitBehaviorConfig, HqClientConfig, IndexingConfig, InputHistoryConfig, LaunchConfig, LaunchMenuChoice, LogConfig, LoopDetectionConfig, MCPHealthConfig, MCPHealthThresholds, MCPServerConfig, ModelMatrixEntry, ModelRuntimeCacheConfig, ModelRuntimeConfig, ModelRuntimeParametersConfig, ModelRuntimeReasoningConfig, NextStepsToolConfig, PluginConfig, PluginManagerConfig, ProviderApiKey, ProviderConfig, SageConfig, SessionLoggingConfig, SkillsConfig, SyncCategory, SyncConfig, ThemePresetId, TokenSavingTier, ToolDescriptionMode, ToolDescriptionModeConfig, ToolResultRenderMode, ToolResultRenderModeConfig, ToolsConfig, } from './config.js';
|
|
7
|
-
export { DEFAULT_TUI_THINKING_WORD, FLEET_CHAT_VERBOSITY_VALUES, MAX_TUI_THINKING_WORD_LENGTH, normalizeTokenSavingTier, normalizeTuiThinkingWord, resolveFleetChatVerbosity, resolveTokenSavingTier, THEME_PRESET_IDS, } from './config.js';
|
|
7
|
+
export { DEFAULT_TUI_THINKING_WORD, FLEET_CHAT_VERBOSITY_VALUES, MAX_TUI_THINKING_WORD_LENGTH, MAX_WRONGPROXY_URL_LENGTH, normalizeTokenSavingTier, normalizeTuiThinkingWord, resolveFleetChatVerbosity, resolveTokenSavingTier, THEME_PRESET_IDS, } from './config.js';
|
|
8
8
|
export type { CompletedWorkEvidence, CompletedWorkSource, ContextEvidenceState, ContextFileEvidence, ContextIntentEvidence, ContextRepeatedReadEvidence, ToolEvidenceStatus, ToolOutputMetadata, } from './context-evidence.js';
|
|
9
9
|
export type { ContextSnapshot, ContextWindowAggressiveOn, ContextWindowConfigLike, ContextWindowMode, ContextWindowModeId, ContextWindowModeSelectionId, ContextWindowPolicy, ContextWindowThresholds, DeprecatedContextWindowModeId, } from './context-window.js';
|
|
10
10
|
export { CONTEXT_WINDOW_MODE_PINNED_META_KEY, CONTEXT_WINDOW_MODES, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES, formatContextWindowModeList, getContextWindowMode, isContextWindowModeId, isContextWindowModeSelectionId, isDeprecatedContextWindowModeId, LARGE_WINDOW_DEEP_MODE_THRESHOLD, listContextWindowModes, normalizeContextWindowModeId, resolveContextWindowPolicy, } from './context-window.js';
|
package/dist/types/index.js
CHANGED
|
@@ -49,6 +49,7 @@ function resolveTokenSavingTier(val, maxContext) {
|
|
|
49
49
|
}
|
|
50
50
|
var DEFAULT_TUI_THINKING_WORD = "thinking";
|
|
51
51
|
var MAX_TUI_THINKING_WORD_LENGTH = 16;
|
|
52
|
+
var MAX_WRONGPROXY_URL_LENGTH = 2048;
|
|
52
53
|
function normalizeTuiThinkingWord(value) {
|
|
53
54
|
if (typeof value !== "string") return DEFAULT_TUI_THINKING_WORD;
|
|
54
55
|
const word = value.trim();
|
|
@@ -911,7 +912,7 @@ function truncate(s, max) {
|
|
|
911
912
|
}
|
|
912
913
|
|
|
913
914
|
// src/types/quota-regex.ts
|
|
914
|
-
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;
|
|
915
916
|
|
|
916
917
|
// src/types/provider.ts
|
|
917
918
|
var REASONING_EFFORT_LEVELS = [
|
|
@@ -947,7 +948,8 @@ function classifyProviderError(status, body, message) {
|
|
|
947
948
|
if (status === 0) return "network";
|
|
948
949
|
if (status === 408) return "timeout";
|
|
949
950
|
if (status === 599) return "stream_hang";
|
|
950
|
-
if (
|
|
951
|
+
if (QUOTA_EXHAUSTED_RE.test(text)) return "quota_exhausted";
|
|
952
|
+
if (status === 402) return "quota_exhausted";
|
|
951
953
|
if (status === 429 && body?.message && body.type !== "rate_limit_exceeded" && RATE_LIMIT_EXCEEDED_RE.test(body.message)) {
|
|
952
954
|
return "quota_exhausted";
|
|
953
955
|
}
|
|
@@ -1367,6 +1369,7 @@ export {
|
|
|
1367
1369
|
LARGE_WINDOW_DEEP_MODE_THRESHOLD,
|
|
1368
1370
|
MALFORMED_ARG_MARKERS,
|
|
1369
1371
|
MAX_TUI_THINKING_WORD_LENGTH,
|
|
1372
|
+
MAX_WRONGPROXY_URL_LENGTH,
|
|
1370
1373
|
MEMORY_TYPE_LABELS,
|
|
1371
1374
|
PROMPT_CATEGORY_LABELS,
|
|
1372
1375
|
ParseError,
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Automatic proxy/trace rerouting for provider base URLs.
|
|
3
|
+
*
|
|
4
|
+
* Lives in `@wrongstack/core` so both `cli` and `runtime` packages can
|
|
5
|
+
* import the same pure-logic rewriter without reaching across workspaces.
|
|
6
|
+
* The CLI's `proxy-probe` (side-effectful, owns `setInterval`) lives in
|
|
7
|
+
* `@wrongstack/cli` and pushes state into this module via `applyProxyConfig`.
|
|
8
|
+
*
|
|
9
|
+
* Example:
|
|
10
|
+
* original = "https://api.openai.com/v1"
|
|
11
|
+
* proxyUrl = "http://localhost:3444"
|
|
12
|
+
* output = "http://localhost:3444/proxy/api.openai.com/v1"
|
|
13
|
+
*
|
|
14
|
+
* The host appears *without* a scheme in the path; the proxy terminates
|
|
15
|
+
* TLS (or speaks plain HTTP for localhost) and forwards the original
|
|
16
|
+
* scheme in the `X-Forwarded-Proto` header.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Providers whose base URLs MUST NOT be rewritten. openai-codex talks
|
|
20
|
+
* directly to the ChatGPT backend over an OAuth-issued token, and the
|
|
21
|
+
* `client_id` / audience in the token constrains the request origin —
|
|
22
|
+
* routing it through a generic proxy breaks the auth check.
|
|
23
|
+
*/
|
|
24
|
+
export declare const PROXY_EXCLUDED_PROVIDERS: ReadonlySet<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Decide whether a provider id is eligible for proxy rerouting.
|
|
27
|
+
*
|
|
28
|
+
* - `openai-codex` is excluded by spec.
|
|
29
|
+
* - Everything else (openai, anthropic, google, openai-compatible,
|
|
30
|
+
* custom saved-config aliases) flows through.
|
|
31
|
+
*/
|
|
32
|
+
export declare function isProxyEligible(providerId: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Rewrite a provider base URL through the proxy.
|
|
35
|
+
*
|
|
36
|
+
* Returns the original input (passthrough) when the input is missing or
|
|
37
|
+
* malformed — we never want the proxy rewriter to be the reason a request
|
|
38
|
+
* fails when the proxy itself is misconfigured. The guards below are
|
|
39
|
+
* deliberately permissive: a misconfigured proxy must NOT silently turn
|
|
40
|
+
* into a hard error during provider construction.
|
|
41
|
+
*/
|
|
42
|
+
export declare function rewriteBaseUrl(originalBaseUrl: string | undefined, proxyUrl: string | undefined): string | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Active proxy configuration consumed by `resolveProviderCfg`. Module-scoped
|
|
45
|
+
* so the wiring layer can read it without threading state through every
|
|
46
|
+
* call site; updates from `applyProxyConfig()` are atomic from the JS
|
|
47
|
+
* perspective (a single assignment).
|
|
48
|
+
*/
|
|
49
|
+
export interface ProxyConfig {
|
|
50
|
+
/** Master switch — when false, no rewrite happens regardless of url. */
|
|
51
|
+
enabled: boolean;
|
|
52
|
+
/** Where the proxy listens. Empty string = unset (treated as disabled). */
|
|
53
|
+
url: string;
|
|
54
|
+
/** Last-known reachability state from the periodic probe. */
|
|
55
|
+
active: boolean;
|
|
56
|
+
}
|
|
57
|
+
/** Read the current proxy configuration. Safe to call from any layer. */
|
|
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;
|
|
73
|
+
/**
|
|
74
|
+
* Apply a new proxy configuration. Returns the previous config so callers
|
|
75
|
+
* (notably the probe) can decide whether the change requires an immediate
|
|
76
|
+
* probe vs. waiting for the next tick. Notifies subscribers only when the
|
|
77
|
+
* merged result actually differs from the previous state.
|
|
78
|
+
*/
|
|
79
|
+
export declare function applyProxyConfig(next: Partial<ProxyConfig>): ProxyConfig;
|
|
80
|
+
/**
|
|
81
|
+
* Convenience: should the rewriter run for a given provider id given the
|
|
82
|
+
* current configuration? Centralizes the "is proxy on AND active AND not
|
|
83
|
+
* excluded" rule so future call sites can't accidentally skip a check.
|
|
84
|
+
*/
|
|
85
|
+
export declare function shouldRewriteFor(providerId: string): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Reset to defaults. Intended for tests; do NOT call from production code
|
|
88
|
+
* (the singleton lives for the lifetime of the process). Also drops all
|
|
89
|
+
* change listeners so tests never observe each other's notifications.
|
|
90
|
+
*/
|
|
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;
|
|
146
|
+
//# sourceMappingURL=proxy-rewrite.d.ts.map
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// src/wiring/proxy-rewrite.ts
|
|
2
|
+
var PROXY_PATH_PREFIX = "/proxy/";
|
|
3
|
+
var PROXY_EXCLUDED_PROVIDERS = /* @__PURE__ */ new Set(["openai-codex"]);
|
|
4
|
+
function isProxyEligible(providerId) {
|
|
5
|
+
if (!providerId) return false;
|
|
6
|
+
if (PROXY_EXCLUDED_PROVIDERS.has(providerId)) return false;
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
function rewriteBaseUrl(originalBaseUrl, proxyUrl) {
|
|
10
|
+
if (!originalBaseUrl) return void 0;
|
|
11
|
+
if (!proxyUrl) return originalBaseUrl;
|
|
12
|
+
if (!isProxyEligibleForRewrite(originalBaseUrl, proxyUrl)) return originalBaseUrl;
|
|
13
|
+
return composeRewrittenUrl(originalBaseUrl, proxyUrl);
|
|
14
|
+
}
|
|
15
|
+
function isProxyEligibleForRewrite(originalBaseUrl, proxyUrl) {
|
|
16
|
+
if (!originalBaseUrl.includes("://")) return false;
|
|
17
|
+
if (!proxyUrl.includes("://")) return false;
|
|
18
|
+
const normalizedProxy = proxyUrl.replace(/\/+$/, "");
|
|
19
|
+
if (originalBaseUrl.startsWith(`${normalizedProxy}${PROXY_PATH_PREFIX}`)) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const parsed = new URL(originalBaseUrl);
|
|
24
|
+
const isLoopback = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || // WHATWG URL serializes IPv6 hosts with brackets — `[::1]`, not `::1`.
|
|
25
|
+
parsed.hostname === "[::1]";
|
|
26
|
+
if (isLoopback && parsed.port !== "") return false;
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
function composeRewrittenUrl(originalBaseUrl, proxyUrl) {
|
|
32
|
+
const parsedOriginal = new URL(originalBaseUrl);
|
|
33
|
+
const hostAndPath = `${parsedOriginal.host}${parsedOriginal.pathname}`;
|
|
34
|
+
const trailingQuery = parsedOriginal.search || "";
|
|
35
|
+
const trailingHash = parsedOriginal.hash || "";
|
|
36
|
+
const proxyRoot = proxyUrl.replace(/\/+$/, "");
|
|
37
|
+
return `${proxyRoot}${PROXY_PATH_PREFIX}${hostAndPath}${trailingQuery}${trailingHash}`;
|
|
38
|
+
}
|
|
39
|
+
var DEFAULT_PROXY_CONFIG = { enabled: false, url: "", active: false };
|
|
40
|
+
var currentConfig = { ...DEFAULT_PROXY_CONFIG };
|
|
41
|
+
function getProxyConfig() {
|
|
42
|
+
return currentConfig;
|
|
43
|
+
}
|
|
44
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
45
|
+
function subscribeToProxyConfig(listener) {
|
|
46
|
+
listeners.add(listener);
|
|
47
|
+
return () => {
|
|
48
|
+
listeners.delete(listener);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function applyProxyConfig(next) {
|
|
52
|
+
const previous = currentConfig;
|
|
53
|
+
currentConfig = {
|
|
54
|
+
enabled: next.enabled ?? previous.enabled,
|
|
55
|
+
url: next.url ?? previous.url,
|
|
56
|
+
active: next.active ?? previous.active
|
|
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
|
+
}
|
|
67
|
+
return previous;
|
|
68
|
+
}
|
|
69
|
+
function shouldRewriteFor(providerId) {
|
|
70
|
+
const cfg = currentConfig;
|
|
71
|
+
if (!cfg.enabled || !cfg.active || !cfg.url) return false;
|
|
72
|
+
return isProxyEligible(providerId);
|
|
73
|
+
}
|
|
74
|
+
function __resetProxyConfigForTests() {
|
|
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
|
+
};
|
|
120
|
+
}
|
|
121
|
+
export {
|
|
122
|
+
PROXY_EXCLUDED_PROVIDERS,
|
|
123
|
+
__resetProxyConfigForTests,
|
|
124
|
+
applyProxyConfig,
|
|
125
|
+
createProxyInstantApply,
|
|
126
|
+
getProxyConfig,
|
|
127
|
+
isProxyEligible,
|
|
128
|
+
rewriteBaseUrl,
|
|
129
|
+
shouldRewriteFor,
|
|
130
|
+
subscribeToProxyConfig
|
|
131
|
+
};
|
|
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": {
|
|
@@ -165,6 +165,10 @@
|
|
|
165
165
|
"./replay": {
|
|
166
166
|
"types": "./dist/replay/index.d.ts",
|
|
167
167
|
"import": "./dist/replay/index.js"
|
|
168
|
+
},
|
|
169
|
+
"./wiring/proxy-rewrite": {
|
|
170
|
+
"types": "./dist/wiring/proxy-rewrite.d.ts",
|
|
171
|
+
"import": "./dist/wiring/proxy-rewrite.js"
|
|
168
172
|
}
|
|
169
173
|
},
|
|
170
174
|
"files": [
|
|
@@ -178,9 +182,9 @@
|
|
|
178
182
|
"wrongstackApiVersion": "0.1.10",
|
|
179
183
|
"dependencies": {
|
|
180
184
|
"zod": "4.4.3",
|
|
181
|
-
"@wrongstack/
|
|
182
|
-
"@wrongstack/
|
|
183
|
-
"@wrongstack/persistence": "0.
|
|
185
|
+
"@wrongstack/kanban": "0.313.0",
|
|
186
|
+
"@wrongstack/primitives": "0.313.0",
|
|
187
|
+
"@wrongstack/persistence": "0.313.0"
|
|
184
188
|
},
|
|
185
189
|
"devDependencies": {
|
|
186
190
|
"@types/node": "^22.19.0",
|