/** * Fleet worker-side wiring(announce/heartbeat + usage 批报;契约 = registry-core 0.1.48+ * `/fleet`,center 端点 = sema-registry `/api/fleet/*`)。设计要点: * - **可选分层(D3)**:仅当 center 已配置(sema-registry lane 的 baseUrl+token+worker)∧ `FLEET_ADVERTISE_ADDRESS` * 显式声明时才启动 —— 不配 center 的部署零行为变化。address 不自猜(k8s pod IP / docker alias / 127.0.0.1:port * 只有部署环境自己知道,故用显式 env)。 * - **announce=注册=心跳同形状幂等 upsert**:boot 后 POST 一次 + 每 heartbeatMs 续;`ready = bootDone ∧ !draining`。 * - **draining 翻转即刻再 announce**(第一时间自摘流,比任何观测都快);退出前 DELETE(显式注销,幂等)。 * - **usage 批报**:tracer 的 `brain.call` 已带 ALS principal —— FleetUsageAccumulator 按 principal×model * 累计,每窗口(默认 60s,半开 [windowStartMs, windowEndMs))POST /api/fleet/usage。幂等键 * (worker,instanceId,windowStartMs) 重发安全;**发送失败 → merge 回累计器**,并入下一窗(失败窗从未入账, * 向前合并不会双计 —— 只延迟,不丢量)。 * - 全部 fire-and-forget + swallow-guarded:fleet 面是运维旁路,绝不影响任务面;网络错误只 warn 一次/连败类。 */ import { UsageReport } from "@sema-agent/settings-schema/fleet"; /** Bound on distinct principal×model keys per window — a runaway/multi-tenant burst collapses into * `__other__` instead of growing unbounded (same posture as budget.ts cardinalityGuard). */ export declare const FLEET_USAGE_KEY_CAP = 1024; /** C1(2026-08-03):announce/usage/deregister 三站点的 fetch 上界(fleet-lease `LEASE_FETCH_TIMEOUT_MS` * 同款判据的普适形)。比 lease 的 2500 略宽——这三条不在提交热路径,5s 足以吃掉慢网 RTT,又远小于 * undici 默认 ~300s headersTimeout(此前半开 center 把 stop()/心跳各钉住到那个量级)。 */ export declare const FLEET_CLIENT_FETCH_TIMEOUT_MS = 5000; /** * Per-window principal×model usage accumulator, fed from the tracer's `brain.call` hook (which carries the * ALS principal — reaches council/team sub-task spend too, same attribution point costQuota uses). Sync, * never throws (TracerHook contract). */ export declare class FleetUsageAccumulator { private cells; private runsSeen; /** * 🔴 [ref](core 2.3.0;registry-core 0.11.0 收口,[ref]):`d.costMicroUsd` **可缺席 = 未知** * (unpriced 部署,budget.ts 如实不带键)。wire 自 0.11.0 起有缺席位(UsageEntry.costMicroUsd * optional)⇒ 本处**整键透传缺席**,不再折 0 低估。cell 级累加语义与 budget.ts ModelUsageTracker * 同款=**传染**(同为单 producer 累加器:已知+未知=未知);center 聚合面的「下界 + costUnknown * 标记」语义在 registry-core aggregateUsage(跨 worker 审计总账层,两层判据见 [ref])。 * 配额轴(weightedTokens)不受影响,仍逐笔精确。 * * core 3.0.0([ref] 装车单 S4c)语义注:`d.inputTokens` 在唯一调用点(budget.ts 的 * `fleetUsage.record(...)`,S1 已迁)传的是 `e.totalInputTokens`(归一化总输入含 cache),不是 * `e.promptTokens`(3.0.0 起恒 cache-MISS)——上游已切换口径,本函数只是照收不重算,值域/签名零改动。 */ record(principal: string | undefined, model: string, taskId: string, d: { inputTokens: number; outputTokens: number; costMicroUsd?: number; weightedTokens?: number; quotaWeightAtUse?: number; }): void; /** Drain the window into UsageReport entries and RESET. Empty window → []. */ drain(): UsageReport["entries"]; /** Merge a FAILED batch back (delivery error → carry the amounts into the next window; never double-counts * because the failed window was never recorded center-side). */ mergeBack(entries: UsageReport["entries"]): void; get size(): number; } export interface FleetClientOpts { center: { baseUrl: string; token: string; worker: string; }; instanceId: string; /** Reachable base URL of THIS instance — the operator's explicit declaration (`FLEET_ADVERTISE_ADDRESS`). */ advertiseAddress: string; version: string; /** Live drain flag (drainState) — read at every announce. */ isDraining: () => boolean; usage?: FleetUsageAccumulator; heartbeatMs?: number; usageWindowMs?: number; logger?: { info: (m: string, d?: Record) => void; warn: (m: string, d?: Record) => void; }; /** Injectable for tests. */ fetchImpl?: typeof fetch; } export interface FleetClient { /** Immediate out-of-cycle announce — call when `draining` flips (self-remove from routing FIRST). */ announceNow: () => Promise; /** Stop timers + best-effort DELETE deregistration (bounded; never throws). */ stop: () => Promise; } /** * Start the worker-side fleet loops. Caller has already gated on "center configured ∧ advertise address set" * (startFleetClientFromEnv below) — this function assumes a complete opts. */ export declare function startFleetClient(opts: FleetClientOpts): FleetClient; /** * Env/config gate (D3 可选分层): start ONLY when the sema-registry lane is configured (baseUrl+token+worker) * AND the operator explicitly declared this instance's reachable address. Anything missing → undefined (zero * behavior change for non-fleet deployments); worker missing is warned when the rest is present (half-configured). */ export declare function startFleetClientFromEnv(cfg: { configCenter?: { baseUrl: string; token: string; worker?: string; }; }, opts: Omit & { advertiseAddress?: string; }): FleetClient | undefined; //# sourceMappingURL=fleet-client.d.ts.map