Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ export function UsageStatisticsNotice({ onDetails }: { onDetails: () => void })
<p>{zh
? "用于改进平台支持与使用体验。发送随机安装标识和环境信息,以及另行汇总的 CLI 使用次数、结果、耗时和 Goal 时长区间;不采集对话、代码、路径或命令参数。可随时关闭。"
: "Helps improve platform support and usage. Sends a random installation ID and environment information, plus separate CLI usage, result, timing and Goal duration summaries. No conversations, code, paths or command arguments. You can turn it off at any time."}</p>
<p>{zh
? "首个已测量的 CLI 结果立即上报,后续由使用活动触发,至少间隔 15 分钟发送一批。CLI 汇总不含安装标识;更频繁的请求仍可能让网络服务通过 IP 和请求时间关联活动。"
: "The first measured CLI result is sent immediately; later activity sends buffered counts at most once every 15 minutes. CLI summaries contain no installation ID; more frequent requests may still let network services correlate activity using IP addresses and request timing."}</p>
<p className="personal-usage-recipient">{zh ? "接收方:" : "Recipient: "}{state.endpoint}</p>
{error ? <p role="alert">{zh ? "设置未能保存,请打开详情重试。" : "Could not save this setting. Open details to retry."}</p> : null}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export function UsageStatisticsSettings() {
return <details className="personal-capability-scope-note personal-usage-statistics" data-testid="usage-statistics-settings">
<summary>{zh ? "基础使用统计 · 告知后默认开启,可关闭" : "Basic usage statistics · on after notice, optional"}</summary>
<p>{zh
? "用于决定平台支持和改进命令体验。每天向 LoopX 的 Cloudflare 收集服务发送随机安装标识、版本、系统、CPU 架构、Python 版本和安装渠道;固定的 CLI 功能、结果、耗时区间和错误类别在本机按天汇总后另行发送,不带安装标识。"
: "Helps prioritize platform support and CLI improvements. A daily heartbeat sends a random installation ID, version, OS, CPU architecture, Python version and install channel to the LoopX Cloudflare collector. Fixed CLI feature, result, duration and error counts are aggregated locally by day and sent separately without the ID."}</p>
? "用于决定平台支持和改进命令体验。每天向 LoopX 的 Cloudflare 收集服务发送随机安装标识、版本、系统、CPU 架构、Python 版本和安装渠道;固定的 CLI 功能、结果、耗时区间和错误类别在本机汇总,不带安装标识。首个可采集的命令结果立即尝试发送,之后有活动时每隔至少 15 分钟发送一批。"
: "Helps prioritize platform support and CLI improvements. A daily heartbeat sends a random installation ID, version, OS, CPU architecture, Python version and install channel to the LoopX Cloudflare collector. Fixed CLI feature, result, duration and error counts are aggregated locally without the ID. The first measured result attempts a send immediately; later activity sends batches at least 15 minutes apart."}</p>
<p>{zh ? "不采集提示词、代码、路径、命令参数、Goal 内容或原始错误。当前功能计数仅覆盖 CLI;命令成功不等于 Goal 完成。" : "No prompts, code, paths, arguments, Goal contents or raw errors. Feature counts currently cover CLI only; command success is not Goal completion."}</p>
<p>{zh ? "按天分别汇总所有 Host 的 quota→spend 推进周期、已绑定 Codex 任务的本地轮次时间、受管 Turn 与普通 Goal 对话的 Host 调用时间。上传固定 Host 类别及跨度/时长区间,不上传会话内容、Goal 或安装标识。三种口径重叠,不能相加;可能漏计,不代表完成、CPU 用时或计费。" : "Daily, separate span/duration buckets for all Hosts using quota→spend, local timing events from bound Codex tasks, and direct Host calls in managed Turns and regular owner Goal chat. Sends fixed Host categories, never session contents, Goal or installation IDs. The three overlapping populations cannot be added; partial observations are not completion, CPU time or billing."}</p>
{state ? <>
Expand Down
5 changes: 4 additions & 1 deletion apps/usage-collector/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ Aggregate requests merge directly into `usage_counts(day, feature, outcome,
duration, error, count)` and are retained 30 days. No raw request rows, ID,
version or per-request timestamps enter that table. Aggregate writes are lossy,
not idempotent: clients make no retry. The server uses its UTC reception date.
Counters are estimates, not people, accepted Goal outcomes or billing records.
Clients may send multiple non-overlapping CLI batches within a day; the collector
adds each delta without requiring a schema migration. Delivery cadence does not
add a version or installation join key. Counters are estimates, not people,
accepted Goal outcomes or billing records.

Neither handler reads/stores IP, user agent or Cloudflare request metadata.
The template disables Worker observability; Cloudflare still handles network
Expand Down
47 changes: 34 additions & 13 deletions docs/reference/usage-ping.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ OS is `darwin|linux|windows|other`, CPU is `x64|arm64|x86|other`, and channel is
`pip|local_release|source|unknown`. Version accepts only numeric major.minor.patch;
a custom version containing a private suffix is not sent.

**Closed-day CLI aggregate** (`POST /v1/aggregate`):
**CLI aggregate batch** (`POST /v1/aggregate`):

```json
{"schema":"loopx_usage_aggregate_v1","counters":[{"feature":"todo","outcome":"ok","duration":"lt_1s","error":"none","count":4}]}
Expand All @@ -75,8 +75,9 @@ Additional payload fields and invalid enum combinations are rejected.

Interactive CLI, unattended scripts/agents and the App use the same
**first disclosure → automatic activation → subsequent measurement** policy.
The first ordinary CLI command prints the recipient, fields, purpose and
both disable mechanisms to stderr, records the disclosure, and sends nothing.
The first ordinary CLI command prints the recipient, fields, purpose, CLI delivery
cadence, network timing correlation boundary and both disable mechanisms to
stderr, records the disclosure, and sends nothing.
This also applies to captured stderr in scripts and Agent tool calls; JSON
stdout is unaffected. Discarded stderr (the null device) or a failed write
cannot acknowledge a notice. Background `chat`/`serve-status` services defer
Expand Down Expand Up @@ -128,16 +129,36 @@ CLI invocation reads only a small local hint; a detached Node process owns
measurement, locks and network I/O. A first-use/settings operation may wait for
local Node execution, never for a collector connection.

Each installation attempts at most one heartbeat per UTC day. The detached
sender persists that daily claim and starts the request under one short lock;
network waiting happens after release, so another observer cannot consume the
claim between persistence and request initiation. Counts are capped
at 128 distinct rows and 10,000 per row, then flushed on the first eligible
command after the UTC day closes. Unsent counts older than seven days are
discarded. An installation that never runs again will not flush its final day.
Lock contention, crashes and failed requests can lose counts. There are no
immediate retries and no durable network queue. Clock rollback does not reopen
a daily attempt. These are **lossy diagnostics**, not billing or audit records.
Each installation attempts at most one heartbeat per UTC day. The first measured
CLI result attempts an aggregate send immediately, including a failed result.
Later eligible activity sends buffered deltas at most once every 15 minutes;
UTC midnight does not reset that interval. Heartbeats and CLI batches have
independent claims, so a heartbeat attempt cannot suppress a completed result.
Startup alone never invents a result. Settings/status operations never flush.
This replaces next-day-only CLI delivery for enabled installations across
interactive and unattended CLI lanes; Goal-duration snapshots remain daily.

Counts are capped at 128 distinct rows and 10,000 per row. Buffered counts expire
after seven UTC days measured from the oldest buffered day. Existing daily
buffer shapes remain readable. Notice revision 4 renews disclosure before the
faster cadence takes effect: old notice state cannot send or consume buffers.
Acknowledging the renewed notice discards old-scope counters and fences queued
observations with a new generation; only subsequent measurements can send.
Explicit disable remains disabled, and acknowledgment cannot replace explicit
enable under `consent_required`. Each batch is removed and its attempt time
persisted before the request starts under the same short lock; network waiting
happens after release. Attempts are spaced even on failure or clock rollback.
There are no immediate retries, background timers or durable network queues.
A session that stops within the interval can still lose its unsent tail; this
reduces dependence on next-day return without promising complete coverage.
Lock contention, crashes and failed requests can also lose counts. These are
**lossy diagnostics**, not billing or audit records.

CLI batches still contain no installation ID, version or event date. The
collector groups them by UTC reception date, which can differ from the activity
date. Do not divide their totals by reporting installations to infer per-install
usage, or attribute them to a release version. More frequent requests can make
network timing correlation easier; identity-free payloads do not prevent that.

Requests have a three-second deadline, do not block command completion, and
cannot change its output or exit code. They use the supported Node runtime's
Expand Down
28 changes: 21 additions & 7 deletions docs/reference/usage-ping.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ ID 随机生成,不绑定账号、不从硬件派生,但能跨天关联,
安装渠道只允许 `pip|local_release|source|unknown`。版本只接受数字三段式,包含
自定义后缀的版本不会上传。

独立的 CLI 日汇总 `POST /v1/aggregate`:
独立的 CLI 汇总批次 `POST /v1/aggregate`:

```json
{"schema":"loopx_usage_aggregate_v1","counters":[{"feature":"todo","outcome":"ok","duration":"lt_1s","error":"none","count":4}]}
Expand All @@ -66,7 +66,8 @@ ID 随机生成,不绑定账号、不从硬件派生,但能跨天关联,
## 告知、设置与升级

交互式 CLI、后台脚本/Agent 和 App 统一采用 **首次告知 → 自动开启 → 后续采集**。
首次普通 CLI 命令向 stderr 显示接收方、字段、用途和关闭方法,然后记录告知;
首次普通 CLI 命令向 stderr 显示接收方、字段、用途、CLI 发送频率、网络时序关联
边界和关闭方法,然后记录告知;
这一轮不计数、不发送。脚本和 Agent 工具调用捕获的 stderr 也适用,JSON stdout
保持不变。stderr 指向空设备或输出失败时不记录告知。后台 `chat`/`serve-status`
服务把首次告知交给 App,不以服务日志代替 App 界面。
Expand Down Expand Up @@ -103,11 +104,24 @@ App 不再要求首次点击启用。环境变量覆盖和 `consent_required`
authority provider 备份、公共投影。普通命令只读取很小的本地提示;独立 Node 后台
进程负责计数、锁和网络。首次告知和设置操作可能等待本机 Node,不等待收集服务。

每天最多尝试一次心跳;本地汇总最多 128 种计数组合,每项封顶 10,000。
UTC 日期结束后的下一次合格调用发送上一日汇总,超过七天的积压丢弃;最后一天
之后不再运行的安装不会发送最后一日计数。锁竞争、进程退出和网络故障可能丢数,
不会立即重试,也没有持久网络队列。时钟回拨不重新开放当日尝试。
这是有损诊断,不能当账单或审计日志。
每天最多尝试一次心跳。首次可采集的 CLI 命令结束后立即尝试发送汇总,包括失败结果;
后续有合格活动时,每隔至少 15 分钟发送一批新增计数,UTC 换日不重置间隔。
心跳和 CLI 汇总分别认领发送机会,心跳尝试不会压掉命令结果。单独启动不产生成功
计数,设置和 status 查询不触发发送。此行为替代已开启统计的交互式、无人值守 CLI
原有的次日发送机制;Goal 时长快照仍按天发送。

本地汇总最多 128 种计数组合,每项封顶 10,000;以最早积压日期计算,超过七个 UTC
日的计数丢弃。旧按日缓冲格式仍可读取。告知版本 4 要求在新频率生效前重新告知:
确认前不发送、不消费缓存;确认后丢弃旧范围计数并更新 generation,阻止旧排队
观测补发,后续新测量才可发送。明确关闭继续生效,`consent_required` 仍须明确启用。
每批在发送前持久化认领时间、移除对应计数,并在同一短锁内发起请求,网络等待不占锁。失败或时钟回拨也不能绕过间隔。
不会立即重试,不增加后台定时器或持久网络队列。间隔内停止使用仍可能丢失未发送的
尾部计数;优化减少对次日回访的依赖,但不承诺完整采集。锁竞争、进程退出和网络
故障也可能丢数。这是有损诊断,不能当账单或审计日志。

CLI 汇总仍不携带安装 ID、版本或活动日期;服务端按 UTC 接收日期汇总,可能与实际
使用日期不同。不能拿调用量除以上报安装数推算每安装用量,也不能归因到某个版本。
更频繁的请求可能增加网络时序关联的机会;载荷不带标识不代表无法关联。

每个网络请求限时 3 秒,不阻塞命令完成、不改变输出和退出码。使用受支持 Node
运行时的 `HTTP_PROXY`、`HTTPS_PROXY`、`NO_PROXY` 配置,代理地址与凭证不会进入遥测数据。
Expand Down
Loading
Loading