instar 1.3.781 → 1.3.783
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/core/IntelligenceRouter.d.ts +9 -14
- package/dist/core/IntelligenceRouter.d.ts.map +1 -1
- package/dist/core/IntelligenceRouter.js +117 -56
- package/dist/core/IntelligenceRouter.js.map +1 -1
- package/dist/core/MeteredSpendGate.d.ts +119 -0
- package/dist/core/MeteredSpendGate.d.ts.map +1 -0
- package/dist/core/MeteredSpendGate.js +228 -0
- package/dist/core/MeteredSpendGate.js.map +1 -0
- package/dist/core/MeteredSpendLedger.d.ts +154 -0
- package/dist/core/MeteredSpendLedger.d.ts.map +1 -0
- package/dist/core/MeteredSpendLedger.js +434 -0
- package/dist/core/MeteredSpendLedger.js.map +1 -0
- package/dist/core/PinAttemptStore.d.ts +35 -0
- package/dist/core/PinAttemptStore.d.ts.map +1 -0
- package/dist/core/PinAttemptStore.js +85 -0
- package/dist/core/PinAttemptStore.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts +7 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +24 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/RenderedPlanStore.d.ts +61 -0
- package/dist/core/RenderedPlanStore.d.ts.map +1 -0
- package/dist/core/RenderedPlanStore.js +92 -0
- package/dist/core/RenderedPlanStore.js.map +1 -0
- package/dist/core/RoutingSpendCapsStore.d.ts +113 -0
- package/dist/core/RoutingSpendCapsStore.d.ts.map +1 -0
- package/dist/core/RoutingSpendCapsStore.js +241 -0
- package/dist/core/RoutingSpendCapsStore.js.map +1 -0
- package/dist/core/SpendAlertResolver.d.ts +77 -0
- package/dist/core/SpendAlertResolver.d.ts.map +1 -0
- package/dist/core/SpendAlertResolver.js +141 -0
- package/dist/core/SpendAlertResolver.js.map +1 -0
- package/dist/core/WriteDomainRegistry.d.ts.map +1 -1
- package/dist/core/WriteDomainRegistry.js +17 -0
- package/dist/core/WriteDomainRegistry.js.map +1 -1
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +9 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/routingSpendView.d.ts +10 -2
- package/dist/core/routingSpendView.d.ts.map +1 -1
- package/dist/core/routingSpendView.js +18 -10
- package/dist/core/routingSpendView.js.map +1 -1
- package/dist/core/types.d.ts +24 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/server/AgentServer.d.ts +9 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +177 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +10 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +196 -4
- package/dist/server/routes.js.map +1 -1
- package/dist/testing/selfActionRegistry.d.ts.map +1 -1
- package/dist/testing/selfActionRegistry.js +78 -0
- package/dist/testing/selfActionRegistry.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +64 -64
- package/upgrades/1.3.782.md +60 -0
- package/upgrades/1.3.783.md +66 -0
- package/upgrades/nature-routing-a2.2-enforcing.eli16.md +50 -0
- package/upgrades/side-effects/nature-routing-a2.2-enforcing.md +142 -0
- package/upgrades/side-effects/routing-spend-increment-b.md +44 -0
|
@@ -85,6 +85,15 @@ import os from 'node:os';
|
|
|
85
85
|
import { TokenLedger } from '../monitoring/TokenLedger.js';
|
|
86
86
|
import { FeatureMetricsLedger } from '../monitoring/FeatureMetricsLedger.js';
|
|
87
87
|
import { RoutingPriceAuthority } from '../core/routingPriceAuthority.js';
|
|
88
|
+
import { MeteredSpendLedger } from '../core/MeteredSpendLedger.js';
|
|
89
|
+
import { RoutingSpendCapsStore } from '../core/RoutingSpendCapsStore.js';
|
|
90
|
+
import { MeteredSpendGate } from '../core/MeteredSpendGate.js';
|
|
91
|
+
import { RenderedPlanStore } from '../core/RenderedPlanStore.js';
|
|
92
|
+
import { PinAttemptStore } from '../core/PinAttemptStore.js';
|
|
93
|
+
import { SpendAlertResolver, buildStalePriceAlert } from '../core/SpendAlertResolver.js';
|
|
94
|
+
import { DEFAULT_FRESHNESS_SLA_DAYS } from '../core/routingPriceAuthority.js';
|
|
95
|
+
import { METERED_ROUTING_DOORS } from '../data/llmBenchCoverage.js';
|
|
96
|
+
import { DEFAULT_METERED_CAPS } from '../core/routingSpendView.js';
|
|
88
97
|
import { A2ADeliveryTracker } from '../threadline/A2ADeliveryTracker.js';
|
|
89
98
|
import { setFeatureMetricsRecorder } from '../core/CircuitBreakingIntelligenceProvider.js';
|
|
90
99
|
import { TokenLedgerPoller } from '../monitoring/TokenLedgerPoller.js';
|
|
@@ -170,6 +179,15 @@ export class AgentServer {
|
|
|
170
179
|
tokenLedger = null;
|
|
171
180
|
featureMetricsLedger = null;
|
|
172
181
|
routingPriceAuthority = null;
|
|
182
|
+
/** Increment B money layer — all null unless routingSpend.money.enabled === true (FD-16). */
|
|
183
|
+
meteredSpendLedger = null;
|
|
184
|
+
routingSpendCapsStore = null;
|
|
185
|
+
meteredSpendGate = null;
|
|
186
|
+
spendPlanStore = null;
|
|
187
|
+
pinAttemptStore = null;
|
|
188
|
+
meteredSweepTimer = null;
|
|
189
|
+
spendAlertResolver = null;
|
|
190
|
+
spendAlertTimer = null;
|
|
173
191
|
featureMetricsPruneTimer = null;
|
|
174
192
|
a2aDeliveryTracker = null;
|
|
175
193
|
tokenLedgerPoller = null;
|
|
@@ -624,6 +642,150 @@ export class AgentServer {
|
|
|
624
642
|
this.routingPriceAuthority = null;
|
|
625
643
|
}
|
|
626
644
|
}
|
|
645
|
+
// Durable per-IP PIN-attempt lockout (S2-1) — hardens EVERY PIN route
|
|
646
|
+
// (mandates, money surfaces, external-hog arm) so a restart no longer
|
|
647
|
+
// resets brute-force lockout. Independent of the money layer.
|
|
648
|
+
try {
|
|
649
|
+
this.pinAttemptStore = new PinAttemptStore({ stateDir: options.config.stateDir });
|
|
650
|
+
}
|
|
651
|
+
catch (err) {
|
|
652
|
+
// @silent-fallback-ok: loud warn + degrade to the pre-existing in-memory
|
|
653
|
+
// lockout (defence-in-depth layer, never the auth decision itself).
|
|
654
|
+
console.warn('[instar] pin-attempt store init failed (non-fatal — in-memory lockout only):', err);
|
|
655
|
+
this.pinAttemptStore = null;
|
|
656
|
+
}
|
|
657
|
+
// ── Increment B MONEY layer (routing-control-room-spend §Layer 3 / Surface 2).
|
|
658
|
+
// EXPLICIT enable only (`routingSpend.money.enabled === true`) — a documented
|
|
659
|
+
// DARK_GATE_EXCLUSIONS action-bearing case (FD-16): NEVER resolveDevAgentGate.
|
|
660
|
+
// Even enabled, every door stays deny-by-default until a per-door PIN go-live.
|
|
661
|
+
const moneyCfg = options.config.routingSpend?.money;
|
|
662
|
+
if (moneyCfg?.enabled === true) {
|
|
663
|
+
try {
|
|
664
|
+
this.meteredSpendLedger = new MeteredSpendLedger({
|
|
665
|
+
stateDir: options.config.stateDir,
|
|
666
|
+
reserveTtlMs: moneyCfg.reserveTtlMs,
|
|
667
|
+
});
|
|
668
|
+
this.routingSpendCapsStore = new RoutingSpendCapsStore({
|
|
669
|
+
stateDir: options.config.stateDir,
|
|
670
|
+
knownKeyRefs: new Set(Object.keys(DEFAULT_METERED_CAPS)),
|
|
671
|
+
knownDoors: new Set([...METERED_ROUTING_DOORS]),
|
|
672
|
+
});
|
|
673
|
+
this.spendPlanStore = new RenderedPlanStore();
|
|
674
|
+
const machineId = options.config.machineId ?? 'single-machine';
|
|
675
|
+
const poolOn = options.config.multiMachine?.sessionPool?.enabled === true;
|
|
676
|
+
this.meteredSpendGate = this.routingPriceAuthority
|
|
677
|
+
? new MeteredSpendGate({
|
|
678
|
+
ledger: this.meteredSpendLedger,
|
|
679
|
+
prices: this.routingPriceAuthority,
|
|
680
|
+
capsStore: this.routingSpendCapsStore,
|
|
681
|
+
machineId,
|
|
682
|
+
// Single-machine trivially self-confirms (it IS the pool). On a
|
|
683
|
+
// multi-machine pool the REAL designation re-confirmation plumbing
|
|
684
|
+
// lands with the metered dispatch seam — until then the gate
|
|
685
|
+
// SELF-FENCES (fails closed) there, the safe direction, matching
|
|
686
|
+
// the spec's own "paid routing is single-machine until D".
|
|
687
|
+
leaseConfirmedAgoMs: () => (poolOn ? null : 0),
|
|
688
|
+
})
|
|
689
|
+
: null;
|
|
690
|
+
// Reserve-expiry sweep: at boot + cadence (money-side; takes the per-key mutex).
|
|
691
|
+
// @silent-fallback-ok: a failed sweep pass retries on the next cadence tick;
|
|
692
|
+
// un-expired reserves only ever OVER-count committed (the safe direction).
|
|
693
|
+
const sweep = () => void this.meteredSpendLedger?.sweepExpired().catch(() => { });
|
|
694
|
+
sweep();
|
|
695
|
+
this.meteredSweepTimer = setInterval(sweep, 5 * 60 * 1000);
|
|
696
|
+
this.meteredSweepTimer.unref?.();
|
|
697
|
+
// Stale-price / observed-drift alerts ride Increment B (C5-5: stale pricing
|
|
698
|
+
// changes money ADMISSION behavior, so its alarm belongs to the money
|
|
699
|
+
// increment) on the minimal resolver foundation — ladder + lifeline fallback.
|
|
700
|
+
const alertsCfg = options.config.routingSpend;
|
|
701
|
+
const tg = this.telegramAdapter;
|
|
702
|
+
if (tg) {
|
|
703
|
+
const persistPath = path.join(options.config.stateDir, 'state', 'routing-spend-alert-topic.json');
|
|
704
|
+
this.spendAlertResolver = new SpendAlertResolver({
|
|
705
|
+
configuredTopicId: () => {
|
|
706
|
+
const id = alertsCfg?.alerts?.telegramTopicId;
|
|
707
|
+
return typeof id === 'number' && Number.isFinite(id) ? id : undefined;
|
|
708
|
+
},
|
|
709
|
+
readPersistedTopicId: () => {
|
|
710
|
+
try {
|
|
711
|
+
const raw = JSON.parse(fs.readFileSync(persistPath, 'utf-8'));
|
|
712
|
+
return typeof raw.topicId === 'number' ? raw.topicId : undefined;
|
|
713
|
+
}
|
|
714
|
+
catch {
|
|
715
|
+
// @silent-fallback-ok: no persisted record yet — rung 2 simply misses.
|
|
716
|
+
return undefined;
|
|
717
|
+
}
|
|
718
|
+
},
|
|
719
|
+
persistTopicId: (topicId) => {
|
|
720
|
+
try {
|
|
721
|
+
const tmp = persistPath + '.tmp';
|
|
722
|
+
fs.writeFileSync(tmp, JSON.stringify({ topicId }), { mode: 0o600 });
|
|
723
|
+
fs.renameSync(tmp, persistPath);
|
|
724
|
+
}
|
|
725
|
+
catch {
|
|
726
|
+
// @silent-fallback-ok: a failed persist means the next resolve re-runs
|
|
727
|
+
// the ladder; creation stays fenced, so no duplicate can result.
|
|
728
|
+
}
|
|
729
|
+
},
|
|
730
|
+
// Single machine trivially self-confirms (it IS the serving lease); on a
|
|
731
|
+
// multi-machine pool the confirmed-lease plumbing rides the C increment —
|
|
732
|
+
// until then this machine does NOT create (fail toward the lifeline).
|
|
733
|
+
servingLeaseConfirmedAgoMs: () => (poolOn ? null : 0),
|
|
734
|
+
createTopic: async () => {
|
|
735
|
+
const t = await tg.createForumTopic('💰 Routing & Spend Alerts', undefined, {
|
|
736
|
+
origin: 'system',
|
|
737
|
+
bounded: true,
|
|
738
|
+
label: 'routing-spend-alerts',
|
|
739
|
+
});
|
|
740
|
+
return t.topicId;
|
|
741
|
+
},
|
|
742
|
+
sendToTopic: async (topicId, text) => {
|
|
743
|
+
await tg.sendToTopic(topicId, text);
|
|
744
|
+
return true;
|
|
745
|
+
},
|
|
746
|
+
lifelineTopicId: () => tg.getLifelineTopicId(),
|
|
747
|
+
});
|
|
748
|
+
const staleCheck = () => {
|
|
749
|
+
try {
|
|
750
|
+
const prices = this.routingPriceAuthority;
|
|
751
|
+
const caps = this.routingSpendCapsStore;
|
|
752
|
+
const resolver = this.spendAlertResolver;
|
|
753
|
+
if (!prices || !caps || !resolver)
|
|
754
|
+
return;
|
|
755
|
+
prices.reloadIfChanged();
|
|
756
|
+
const store = caps.read();
|
|
757
|
+
for (const [door, g] of Object.entries(store.goLive)) {
|
|
758
|
+
if (!g.enabled)
|
|
759
|
+
continue; // staleness only alarms where money can move
|
|
760
|
+
const res = prices.resolve(door, '__staleness-probe__', Date.now());
|
|
761
|
+
if (res.priceStale && res.newestPointAgeDays !== null) {
|
|
762
|
+
const meta = prices.doorMetaFor(door);
|
|
763
|
+
void resolver.emit(buildStalePriceAlert(door, res.newestPointAgeDays, meta?.freshnessSlaDays ?? DEFAULT_FRESHNESS_SLA_DAYS));
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
catch {
|
|
768
|
+
// @silent-fallback-ok: the alert cadence is observability — a failed
|
|
769
|
+
// pass retries next tick; it must never disturb the server loop.
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
const staleIntervalMs = Math.max(1, alertsCfg?.money?.priceStaleCheckIntervalHours ?? 6) * 60 * 60 * 1000;
|
|
773
|
+
staleCheck();
|
|
774
|
+
this.spendAlertTimer = setInterval(staleCheck, staleIntervalMs);
|
|
775
|
+
this.spendAlertTimer.unref?.();
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
catch (err) {
|
|
779
|
+
// @silent-fallback-ok: NOT a silent fallback — fail CLOSED, loudly: a
|
|
780
|
+
// money-layer init failure leaves every component null, the routes 503,
|
|
781
|
+
// and no metered admission is possible anywhere.
|
|
782
|
+
console.error('[instar] routing-spend MONEY layer init FAILED (fail-closed — money routes 503):', err);
|
|
783
|
+
this.meteredSpendLedger = null;
|
|
784
|
+
this.routingSpendCapsStore = null;
|
|
785
|
+
this.meteredSpendGate = null;
|
|
786
|
+
this.spendPlanStore = null;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
627
789
|
// Observable Intelligence × Responsible Resource: the audit trail is kept
|
|
628
790
|
// long enough to see behaviour/performance trends, then aged out — never
|
|
629
791
|
// hoarded forever. Default 30d; tune via monitoring.featureMetrics.retentionDays
|
|
@@ -1959,6 +2121,11 @@ export class AgentServer {
|
|
|
1959
2121
|
tokenLedger: this.tokenLedger,
|
|
1960
2122
|
featureMetricsLedger: this.featureMetricsLedger,
|
|
1961
2123
|
routingPriceAuthority: this.routingPriceAuthority,
|
|
2124
|
+
meteredSpendLedger: this.meteredSpendLedger,
|
|
2125
|
+
routingSpendCapsStore: this.routingSpendCapsStore,
|
|
2126
|
+
meteredSpendGate: this.meteredSpendGate,
|
|
2127
|
+
spendPlanStore: this.spendPlanStore,
|
|
2128
|
+
pinAttemptStore: this.pinAttemptStore,
|
|
1962
2129
|
resourceLedger: this.resourceLedger,
|
|
1963
2130
|
processFootprintMonitor: this.processFootprintMonitor,
|
|
1964
2131
|
approvalLedger: this.approvalLedger,
|
|
@@ -3740,6 +3907,16 @@ export class AgentServer {
|
|
|
3740
3907
|
clearInterval(this.featureMetricsPruneTimer);
|
|
3741
3908
|
}
|
|
3742
3909
|
catch { /* @silent-fallback-ok: timer teardown is best-effort cleanup at shutdown */ }
|
|
3910
|
+
try {
|
|
3911
|
+
if (this.meteredSweepTimer)
|
|
3912
|
+
clearInterval(this.meteredSweepTimer);
|
|
3913
|
+
}
|
|
3914
|
+
catch { /* @silent-fallback-ok: timer teardown is best-effort cleanup at shutdown */ }
|
|
3915
|
+
try {
|
|
3916
|
+
if (this.spendAlertTimer)
|
|
3917
|
+
clearInterval(this.spendAlertTimer);
|
|
3918
|
+
}
|
|
3919
|
+
catch { /* @silent-fallback-ok: timer teardown is best-effort cleanup at shutdown */ }
|
|
3743
3920
|
this.featureMetricsPruneTimer = null;
|
|
3744
3921
|
}
|
|
3745
3922
|
if (this.tokenLedger) {
|