switchroom 0.19.2 → 0.19.4
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/agent-scheduler/index.js +2 -0
- package/dist/auth-broker/index.js +109 -7
- package/dist/cli/autoaccept-poll.js +2 -0
- package/dist/cli/drive-write-pretool.mjs +2 -0
- package/dist/cli/ms-365-write-pretool.mjs +2 -0
- package/dist/cli/switchroom.js +404 -245
- package/dist/host-control/main.js +1 -1
- package/package.json +1 -1
- package/profiles/default/CLAUDE.md.hbs +8 -0
- package/skills/mental-model-curator/SKILL.md +68 -2
- package/telegram-plugin/auth-snapshot-format.ts +104 -12
- package/telegram-plugin/dist/bridge/bridge.js +8 -2
- package/telegram-plugin/dist/gateway/gateway.js +1194 -794
- package/telegram-plugin/dist/server.js +8 -2
- package/telegram-plugin/flushed-turn-supersede.ts +117 -13
- package/telegram-plugin/gateway/auth-add-flow.ts +215 -6
- package/telegram-plugin/gateway/auth-command.ts +138 -5
- package/telegram-plugin/gateway/gateway.ts +68 -101
- package/telegram-plugin/gateway/inbound-interceptors.ts +13 -3
- package/telegram-plugin/gateway/model-command.ts +203 -1
- package/telegram-plugin/gateway/outbound-send-path.ts +68 -15
- package/telegram-plugin/gateway/session-model-source.ts +90 -10
- package/telegram-plugin/gateway/stream-render.ts +22 -5
- package/telegram-plugin/quota-bar-format.ts +60 -12
- package/telegram-plugin/reply-owner-resolve.ts +76 -11
- package/telegram-plugin/session-tail.ts +27 -3
- package/telegram-plugin/tests/auth-add-flow.test.ts +367 -5
- package/telegram-plugin/tests/auth-snapshot-format.test.ts +41 -0
- package/telegram-plugin/tests/flushed-turn-supersede.test.ts +117 -0
- package/telegram-plugin/tests/gateway-session-model-relaunch.test.ts +185 -29
- package/telegram-plugin/tests/model-command.test.ts +220 -0
- package/telegram-plugin/tests/reply-owner-resolve.test.ts +257 -13
- package/telegram-plugin/tests/send-reply-golden.test.ts +154 -0
- package/telegram-plugin/tests/session-model-source.test.ts +142 -0
- package/telegram-plugin/tests/session-tail-first-attach.test.ts +115 -2
- package/vendor/hindsight-memory/CHANGELOG.md +102 -0
- package/vendor/hindsight-memory/README.md +2 -1
- package/vendor/hindsight-memory/hooks/hooks.json +12 -0
- package/vendor/hindsight-memory/scripts/directive_verify.py +100 -3
- package/vendor/hindsight-memory/scripts/lib/config.py +150 -1
- package/vendor/hindsight-memory/scripts/lib/content.py +55 -5
- package/vendor/hindsight-memory/scripts/lib/directives.py +152 -15
- package/vendor/hindsight-memory/scripts/lib/parallel_recall.py +142 -0
- package/vendor/hindsight-memory/scripts/lib/state.py +31 -0
- package/vendor/hindsight-memory/scripts/recall.py +789 -143
- package/vendor/hindsight-memory/scripts/reconcile_tail.py +22 -1
- package/vendor/hindsight-memory/scripts/retain.py +71 -2
- package/vendor/hindsight-memory/scripts/subagent_retain.py +501 -0
- package/vendor/hindsight-memory/scripts/tests/test_directive_verify.py +169 -0
- package/vendor/hindsight-memory/scripts/tests/test_directives.py +177 -0
- package/vendor/hindsight-memory/scripts/tests/test_lesson_tagging.py +200 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_context_turns_default.py +200 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_envelope_strip_telemetry.py +477 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_integration.py +51 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_parallel_deadline.py +409 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_tag_weights.py +96 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_transcript_fallback.py +413 -0
- package/vendor/hindsight-memory/scripts/tests/test_reconcile_durability.py +49 -0
- package/vendor/hindsight-memory/scripts/tests/test_subagent_retain.py +439 -0
- package/vendor/hindsight-memory/settings.json +3 -1
|
@@ -13945,6 +13945,8 @@ var AccountStateSchema = exports_external.object({
|
|
|
13945
13945
|
label: exports_external.string(),
|
|
13946
13946
|
expiresAt: exports_external.number().optional(),
|
|
13947
13947
|
exhausted: exports_external.boolean(),
|
|
13948
|
+
in_service: exports_external.boolean().optional(),
|
|
13949
|
+
entitlement_blocked: exports_external.boolean().optional(),
|
|
13948
13950
|
exhausted_until: exports_external.number().optional(),
|
|
13949
13951
|
throttled_until: exports_external.number().optional(),
|
|
13950
13952
|
threshold_violations: exports_external.number().int().nonnegative().optional(),
|
|
@@ -18839,6 +18839,27 @@ function parseQuotaHeaders(headers, now = new Date) {
|
|
|
18839
18839
|
}
|
|
18840
18840
|
};
|
|
18841
18841
|
}
|
|
18842
|
+
function extractApiErrorMessage(bodyText) {
|
|
18843
|
+
if (!bodyText || bodyText.trim().length === 0)
|
|
18844
|
+
return null;
|
|
18845
|
+
try {
|
|
18846
|
+
const parsed = JSON.parse(bodyText);
|
|
18847
|
+
const msg = parsed?.error?.message;
|
|
18848
|
+
if (typeof msg === "string" && msg.trim().length > 0)
|
|
18849
|
+
return msg.trim();
|
|
18850
|
+
} catch {}
|
|
18851
|
+
return bodyText.trim().slice(0, 500);
|
|
18852
|
+
}
|
|
18853
|
+
function isEntitlementDisabledMessage(message) {
|
|
18854
|
+
if (!message)
|
|
18855
|
+
return false;
|
|
18856
|
+
const m = message.toLowerCase();
|
|
18857
|
+
if (!m.includes("disabled"))
|
|
18858
|
+
return false;
|
|
18859
|
+
const mentionsCode = m.includes("claude code") || m.includes("claude subscription");
|
|
18860
|
+
const mentionsOrgOrSub = m.includes("organization") || m.includes("subscription") || m.includes(" org ");
|
|
18861
|
+
return mentionsCode && mentionsOrgOrSub;
|
|
18862
|
+
}
|
|
18842
18863
|
async function fetchQuota(opts) {
|
|
18843
18864
|
const token = opts.accessToken?.trim();
|
|
18844
18865
|
if (!token || token.length === 0) {
|
|
@@ -18874,13 +18895,28 @@ async function fetchQuota(opts) {
|
|
|
18874
18895
|
}
|
|
18875
18896
|
return { ok: false, reason: `quota probe network error: ${msg}` };
|
|
18876
18897
|
}
|
|
18877
|
-
clearTimeout(timeout);
|
|
18878
18898
|
const parsed = parseQuotaHeaders(resp.headers);
|
|
18879
|
-
if (parsed.ok)
|
|
18899
|
+
if (parsed.ok) {
|
|
18900
|
+
clearTimeout(timeout);
|
|
18880
18901
|
return parsed;
|
|
18902
|
+
}
|
|
18881
18903
|
if (!resp.ok) {
|
|
18882
|
-
|
|
18904
|
+
let bodyText = "";
|
|
18905
|
+
try {
|
|
18906
|
+
bodyText = await resp.text();
|
|
18907
|
+
} catch {}
|
|
18908
|
+
clearTimeout(timeout);
|
|
18909
|
+
const apiErrorMessage = extractApiErrorMessage(bodyText);
|
|
18910
|
+
const entitlement = resp.status === 403 && isEntitlementDisabledMessage(apiErrorMessage);
|
|
18911
|
+
return {
|
|
18912
|
+
ok: false,
|
|
18913
|
+
reason: `HTTP ${resp.status} from Anthropic (${parsed.reason})`,
|
|
18914
|
+
httpStatus: resp.status,
|
|
18915
|
+
...apiErrorMessage ? { apiErrorMessage } : {},
|
|
18916
|
+
failureKind: entitlement ? "entitlement_blocked" : "other"
|
|
18917
|
+
};
|
|
18883
18918
|
}
|
|
18919
|
+
clearTimeout(timeout);
|
|
18884
18920
|
return parsed;
|
|
18885
18921
|
}
|
|
18886
18922
|
|
|
@@ -18912,7 +18948,9 @@ function snapshotClearlyHealthy(s) {
|
|
|
18912
18948
|
return s.fiveHourUtilizationPct < HEALTHY_CLEAR_PCT && s.sevenDayUtilizationPct < HEALTHY_CLEAR_PCT;
|
|
18913
18949
|
}
|
|
18914
18950
|
function accountEligibility(opts) {
|
|
18915
|
-
const { mark, snapshot, now, allowOverage = false } = opts;
|
|
18951
|
+
const { mark, snapshot, now, allowOverage = false, entitlementBlocked = false } = opts;
|
|
18952
|
+
if (entitlementBlocked)
|
|
18953
|
+
return "blocked";
|
|
18916
18954
|
if (snapshotFresh(snapshot, now) && snapshotWalled(snapshot) && !overageLiftsWall(snapshot, allowOverage)) {
|
|
18917
18955
|
return "blocked";
|
|
18918
18956
|
}
|
|
@@ -20401,6 +20439,8 @@ var AccountStateSchema = exports_external.object({
|
|
|
20401
20439
|
label: exports_external.string(),
|
|
20402
20440
|
expiresAt: exports_external.number().optional(),
|
|
20403
20441
|
exhausted: exports_external.boolean(),
|
|
20442
|
+
in_service: exports_external.boolean().optional(),
|
|
20443
|
+
entitlement_blocked: exports_external.boolean().optional(),
|
|
20404
20444
|
exhausted_until: exports_external.number().optional(),
|
|
20405
20445
|
throttled_until: exports_external.number().optional(),
|
|
20406
20446
|
threshold_violations: exports_external.number().int().nonnegative().optional(),
|
|
@@ -21168,8 +21208,50 @@ class AuthBroker {
|
|
|
21168
21208
|
mark: this.exhaustionMarkOf(account),
|
|
21169
21209
|
snapshot: this.lastQuotaCache[account],
|
|
21170
21210
|
now: this.now(),
|
|
21171
|
-
allowOverage: this.isOverageAllowed(account)
|
|
21211
|
+
allowOverage: this.isOverageAllowed(account),
|
|
21212
|
+
entitlementBlocked: this.isAccountEntitlementBlocked(account)
|
|
21213
|
+
});
|
|
21214
|
+
}
|
|
21215
|
+
isAccountEntitlementBlocked(account) {
|
|
21216
|
+
return this.quota[account]?.entitlement_blocked === true;
|
|
21217
|
+
}
|
|
21218
|
+
markEntitlementBlocked(label, result) {
|
|
21219
|
+
if (result.ok || result.failureKind !== "entitlement_blocked")
|
|
21220
|
+
return;
|
|
21221
|
+
if (this.quota[label]?.entitlement_blocked === true)
|
|
21222
|
+
return;
|
|
21223
|
+
this.quota[label] = {
|
|
21224
|
+
...this.quota[label],
|
|
21225
|
+
entitlement_blocked: true,
|
|
21226
|
+
entitlement_blocked_at: this.now(),
|
|
21227
|
+
...result.apiErrorMessage ? { entitlement_blocked_reason: result.apiErrorMessage } : {}
|
|
21228
|
+
};
|
|
21229
|
+
this.persistQuota();
|
|
21230
|
+
this.audit({
|
|
21231
|
+
op: "mark-exhausted",
|
|
21232
|
+
identity: { kind: "operator" },
|
|
21233
|
+
account: label,
|
|
21234
|
+
accountKind: "claude",
|
|
21235
|
+
ok: true,
|
|
21236
|
+
reason: "entitlement-403"
|
|
21172
21237
|
});
|
|
21238
|
+
process.stdout.write(`auth-broker: ${label} returned entitlement-403 (Claude Code access disabled) — marked entitlement_blocked${result.apiErrorMessage ? ` (${result.apiErrorMessage})` : ""}
|
|
21239
|
+
`);
|
|
21240
|
+
}
|
|
21241
|
+
clearEntitlementBlocked(label) {
|
|
21242
|
+
const entry = this.quota[label];
|
|
21243
|
+
if (!entry?.entitlement_blocked)
|
|
21244
|
+
return;
|
|
21245
|
+
const {
|
|
21246
|
+
entitlement_blocked,
|
|
21247
|
+
entitlement_blocked_at,
|
|
21248
|
+
entitlement_blocked_reason,
|
|
21249
|
+
...rest
|
|
21250
|
+
} = entry;
|
|
21251
|
+
this.quota[label] = rest;
|
|
21252
|
+
this.persistQuota();
|
|
21253
|
+
process.stdout.write(`auth-broker: live probe of ${label} succeeded — cleared entitlement_blocked mark
|
|
21254
|
+
`);
|
|
21173
21255
|
}
|
|
21174
21256
|
premiumWallMarkOf(account) {
|
|
21175
21257
|
const q = this.quota[account];
|
|
@@ -21327,7 +21409,8 @@ class AuthBroker {
|
|
|
21327
21409
|
mark: this.exhaustionMarkOf(account),
|
|
21328
21410
|
snapshot,
|
|
21329
21411
|
now,
|
|
21330
|
-
allowOverage: true
|
|
21412
|
+
allowOverage: true,
|
|
21413
|
+
entitlementBlocked: this.isAccountEntitlementBlocked(account)
|
|
21331
21414
|
}) === "eligible";
|
|
21332
21415
|
}
|
|
21333
21416
|
accountEligibilityOf(account) {
|
|
@@ -21337,7 +21420,8 @@ class AuthBroker {
|
|
|
21337
21420
|
mark: this.exhaustionMarkOf(account),
|
|
21338
21421
|
snapshot,
|
|
21339
21422
|
now: this.now(),
|
|
21340
|
-
allowOverage
|
|
21423
|
+
allowOverage,
|
|
21424
|
+
entitlementBlocked: this.isAccountEntitlementBlocked(account)
|
|
21341
21425
|
});
|
|
21342
21426
|
if (verdict === "eligible" && allowOverage && snapshot && (snapshot.fiveHourUtilizationPct >= WALL_PCT || snapshot.sevenDayUtilizationPct >= WALL_PCT)) {
|
|
21343
21427
|
process.stdout.write(`auth-broker: ${account} is past the utilization wall but eligible via allow_overage — Anthropic overage billing active (5h=${snapshot.fiveHourUtilizationPct.toFixed(1)}%, 7d=${snapshot.sevenDayUtilizationPct.toFixed(1)}%)
|
|
@@ -21354,6 +21438,8 @@ class AuthBroker {
|
|
|
21354
21438
|
const result = await this.probeQuotaSingleFlight(account, token);
|
|
21355
21439
|
if (result.ok)
|
|
21356
21440
|
this.cacheQuotaSnapshot(account, result);
|
|
21441
|
+
else
|
|
21442
|
+
this.markEntitlementBlocked(account, result);
|
|
21357
21443
|
} catch {}
|
|
21358
21444
|
}
|
|
21359
21445
|
async nextHealthyAccountLive(current, order) {
|
|
@@ -21441,6 +21527,7 @@ class AuthBroker {
|
|
|
21441
21527
|
}
|
|
21442
21528
|
async opListState(socket, id, identity) {
|
|
21443
21529
|
const auth = this.config.auth ?? {};
|
|
21530
|
+
const inService = this.inServiceSet();
|
|
21444
21531
|
const accounts = listAccounts(this.home).map((label) => {
|
|
21445
21532
|
const creds = readAccountCredentials(label, this.home);
|
|
21446
21533
|
const meta = readAccountMeta(label, this.home);
|
|
@@ -21451,6 +21538,7 @@ class AuthBroker {
|
|
|
21451
21538
|
label,
|
|
21452
21539
|
expiresAt: creds?.claudeAiOauth?.expiresAt,
|
|
21453
21540
|
exhausted,
|
|
21541
|
+
in_service: inService.has(label),
|
|
21454
21542
|
exhausted_until: q?.exhausted_until,
|
|
21455
21543
|
throttled_until: q?.throttled_until,
|
|
21456
21544
|
threshold_violations: this.thresholdViolations[label] ?? 0,
|
|
@@ -21460,6 +21548,7 @@ class AuthBroker {
|
|
|
21460
21548
|
premium_walled_until: q?.premium_walled_until,
|
|
21461
21549
|
premium_wall_bucket: q?.premium_wall_bucket,
|
|
21462
21550
|
last_tier_quota: this.lastTierQuotaCache[label] ?? null,
|
|
21551
|
+
entitlement_blocked: q?.entitlement_blocked ?? false,
|
|
21463
21552
|
usage_ledger: summarizeAccountUsage(this.usageLedger, label, this.now())
|
|
21464
21553
|
};
|
|
21465
21554
|
});
|
|
@@ -21534,6 +21623,7 @@ class AuthBroker {
|
|
|
21534
21623
|
this.cacheQuotaSnapshot(label, result);
|
|
21535
21624
|
return { label, result, served: "live" };
|
|
21536
21625
|
}
|
|
21626
|
+
this.markEntitlementBlocked(label, result);
|
|
21537
21627
|
if (cached) {
|
|
21538
21628
|
return { label, result: cachedSnapshotToResult(cached), served: "cache", capturedAt: cached.capturedAt };
|
|
21539
21629
|
}
|
|
@@ -21555,6 +21645,7 @@ class AuthBroker {
|
|
|
21555
21645
|
cacheQuotaSnapshot(label, result) {
|
|
21556
21646
|
if (!result.ok)
|
|
21557
21647
|
return;
|
|
21648
|
+
this.clearEntitlementBlocked(label);
|
|
21558
21649
|
const snapshot = {
|
|
21559
21650
|
fiveHourUtilizationPct: result.data.fiveHourUtilizationPct,
|
|
21560
21651
|
sevenDayUtilizationPct: result.data.sevenDayUtilizationPct,
|
|
@@ -21584,6 +21675,8 @@ class AuthBroker {
|
|
|
21584
21675
|
const canarySet = this.premiumCanarySet();
|
|
21585
21676
|
const canaryEnabled = process.env.SWITCHROOM_DISABLE_MODEL_TIER_PROBE !== "1";
|
|
21586
21677
|
for (const label of listAccounts(this.home)) {
|
|
21678
|
+
if (this.isAccountEntitlementBlocked(label))
|
|
21679
|
+
continue;
|
|
21587
21680
|
const creds = readAccountCredentials(label, this.home);
|
|
21588
21681
|
const token = creds?.claudeAiOauth?.accessToken;
|
|
21589
21682
|
if (!token)
|
|
@@ -21596,6 +21689,7 @@ class AuthBroker {
|
|
|
21596
21689
|
continue;
|
|
21597
21690
|
}
|
|
21598
21691
|
this.cacheQuotaSnapshot(label, result);
|
|
21692
|
+
this.markEntitlementBlocked(label, result);
|
|
21599
21693
|
probed.push({ label, result });
|
|
21600
21694
|
if (canaryEnabled && canarySet.has(label)) {
|
|
21601
21695
|
let tierResult;
|
|
@@ -21700,6 +21794,14 @@ class AuthBroker {
|
|
|
21700
21794
|
set.add(acct);
|
|
21701
21795
|
return set;
|
|
21702
21796
|
}
|
|
21797
|
+
inServiceSet() {
|
|
21798
|
+
const set = this.premiumCanarySet();
|
|
21799
|
+
for (const c of this.config.auth?.consumers ?? []) {
|
|
21800
|
+
if (c.account)
|
|
21801
|
+
set.add(c.account);
|
|
21802
|
+
}
|
|
21803
|
+
return set;
|
|
21804
|
+
}
|
|
21703
21805
|
pinnedAgentAccounts() {
|
|
21704
21806
|
return new Set(Object.values(this.config.agents ?? {}).map((a) => a.auth?.override).filter((x) => typeof x === "string" && x.length > 0));
|
|
21705
21807
|
}
|
|
@@ -4874,6 +4874,8 @@ var AccountStateSchema = exports_external.object({
|
|
|
4874
4874
|
label: exports_external.string(),
|
|
4875
4875
|
expiresAt: exports_external.number().optional(),
|
|
4876
4876
|
exhausted: exports_external.boolean(),
|
|
4877
|
+
in_service: exports_external.boolean().optional(),
|
|
4878
|
+
entitlement_blocked: exports_external.boolean().optional(),
|
|
4877
4879
|
exhausted_until: exports_external.number().optional(),
|
|
4878
4880
|
throttled_until: exports_external.number().optional(),
|
|
4879
4881
|
threshold_violations: exports_external.number().int().nonnegative().optional(),
|
|
@@ -4162,6 +4162,8 @@ var init_protocol = __esm(() => {
|
|
|
4162
4162
|
label: exports_external.string(),
|
|
4163
4163
|
expiresAt: exports_external.number().optional(),
|
|
4164
4164
|
exhausted: exports_external.boolean(),
|
|
4165
|
+
in_service: exports_external.boolean().optional(),
|
|
4166
|
+
entitlement_blocked: exports_external.boolean().optional(),
|
|
4165
4167
|
exhausted_until: exports_external.number().optional(),
|
|
4166
4168
|
throttled_until: exports_external.number().optional(),
|
|
4167
4169
|
threshold_violations: exports_external.number().int().nonnegative().optional(),
|
|
@@ -4164,6 +4164,8 @@ var init_protocol = __esm(() => {
|
|
|
4164
4164
|
label: exports_external.string(),
|
|
4165
4165
|
expiresAt: exports_external.number().optional(),
|
|
4166
4166
|
exhausted: exports_external.boolean(),
|
|
4167
|
+
in_service: exports_external.boolean().optional(),
|
|
4168
|
+
entitlement_blocked: exports_external.boolean().optional(),
|
|
4167
4169
|
exhausted_until: exports_external.number().optional(),
|
|
4168
4170
|
throttled_until: exports_external.number().optional(),
|
|
4169
4171
|
threshold_violations: exports_external.number().int().nonnegative().optional(),
|