Files
archive: a request the archive kept is opened and downloaded from the Usage page, with its bodies whole (jorben, #447: 请求存档 reachable from usage history, full bodies, download) — the archive's name was only on the Gateway page's recent calls, gone once a call left the list or the gateway restarted, and the archive kept the first 256 KB of each body, the recent list's cut. The usage ledger's row now names the call's archive (archive in usage.jsonl, kept through the Requests page's packed rows), and its details on Usage → Requests show the Request archive panel, Fetch from archive and Download; with the archive on, a request it has no copy of says "Not archived". The bodies are kept whole up to requestArchiveMaxMB in settings.json (32 MB unless set, at most 1024): the response is teed to a temp file as it is sent, the request body written to one when the call ends, so the upload queue holds file handles, not bodies, and one upload at a time reads them; a body past the limit is kept to it and marked cut, with its whole size. What goes upstream is unchanged, the archive stays off unless turned on, headers and bodies are scrubbed as before, and the temp files are removed after the upload or when the queue is full. The page is sent a body to show only when it is 256 KB or less (a longer one is said by its size), an archive over 80 MB is only said to be; Download streams the object whole, to Downloads in the app (archive/export, -2, -3… for a name taken) and as the browser's download in magpie web (archive/file). TestArchiveWholeBodies, TestArchiveLimit, TestRowKeepsArchive and TestArchivePreviewAndDownload fail without the change; TestArchiveOffUsage, TestArchiveTooLargeToPreview, TestArchiveOldPreview keep the rest. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; request-archive-usage (new), request-archive, click-scroll, old-webkit, usage-ledger, usage-ledger-detail, usage-ledger-content, web-page, sse-body pass.
usage: an Antigravity call at a level answered under its model's own name is no swap (#462: gemini-3.8-flash at medium marked Swapped, answered by gemini-3.8-flash) — on an Antigravity account a call at an effort goes out as that level's id (AntigravitySentID: gemini-3.8-flash-medium), and Cloud Code's reply names the family in modelVersion (gemini-3.8-flash), so Swapped compared gemini-3.8-flash-medium with gemini-3.8-flash and the request list and the Routing view tagged the model as swapped for itself. A sent id whose last word is an effort word (provider.EffortFamily: low, medium, high, xhigh, minimal, extra-low, extra-high) now matches a reply naming the id without it; a reply naming another level, or another model, is still a swap. TestLedgerAntigravityReplyNamingTheFamilyIsNoSwap fails without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestClaudeRunEndsWhenTheClientLeaves, a load flake, passes alone).
usage: with WebDAV or S3 sync, each computer shares its usage and sees the others', the total and each computer's part (#542) — sync carried keys, agents and the library, and each computer's Usage page counted only the calls made on it. Sync now has a Usage tick (off unless picked, usage=yes in
magpie webdav/magpie s3): a computer with it on puts its own calls up as one file a day, magpie/usage/<id>-<day>.magpie-usage beside the bundle (under the prefix's usage/ on S3), each a gzipped list of times, agents, providers, models, tokens, costs and statuses, sealed with the sync passphrase (backup.SealData, the key cached per passphrase so a sync derives it once); no request body, session, archive name or key goes, a call another computer passed on over the LAN (Via) isn't put up again, and an error is cut to 300 characters. The id is a random one kept in usage-computer.json, which no backup carries, with the host name beside it. A computer only ever writes and deletes its own names, so none overwrites another's; the files are not in the bundle and its hash, so sharing usage never makes a sync or a conflict of the bundle; the first sync puts up the last 90 days, after that only yesterday and today, at most every 15 minutes (a Save or Sync now at once), and a day past 90 is deleted by the computer it is of. The others' files, listed (PROPFIND Depth 1; ListObjectsV2) and fetched only when changed, are kept in usage-others and read with the ledger, as Record.Computer; a failure is said in the sync row ("Couldn't share usage") and never fails the sync. The Requests page and the panel's Usage tab get a Computer pick (this computer, other computers when more than one, each by name) sending computer=this|others|<id>; a row from another computer says "· on <name>" beside its agent, and its details name the computer and say what was said stays there, nothing asked of this computer's session files for it. With no other computer's usage there is no pick and nothing new on either page, and an older magpie never lists or reads usage/. TestUsageSharedBetweenComputers (computers a and b over a fake WebDAV under httptest: each sees 4 calls, the totals and its own and the other's part, nothing counted twice over repeated syncs, the bundle not put again for usage, a new call reaching the other, a day past the window deleted), TestUsageSharedThroughS3 (the same through a fake S3, signed list and delete, a prefix with a space and a plus) and TestUsageOffAsksNothing are new and don't build without the change; usage-computers.test.cjs (WebKit, en and zh: no pick without other computers, the pick and computer=this, another computer's row and details with no /content asked, the panel's pick, the sync form's tick posting usage: true, no click moving the page) fails without it on all but the no-other-computers case. s3-sync, sync-other-kind, panel-usage, request-archive-usage, click-scroll and usage-ledger-chart pass in WebKit. go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./... under a temp HOME pass (TestFXInState failed once on its TempDir cleanup, passes alone). Turning the Usage tick off, or sync off, lets the other computers' usage kept here go (DropAllShared), so the Usage page counts this computer's alone again: TestUsageSharedBetweenComputers checks both, and fails without it.
test: usage's and provider's TestMains clear the agents' variables too, and a new agent's variable left out of the list now fails (yetone, reviewing #543: internal/usage/main_test.go's TestMain still clears no agent variables, so a CLAUDE_CONFIG_DIR left in the shell reaches the real ~/.claude there too; nothing fails yet when a new agent's variable is left out of Vars) — both TestMains gave their package a home of its own and cleared no agent variable, the provider one as much as the usage one that was named. A CLAUDE_CONFIG_DIR left in the shell reached the real ~/.claude through the ledger's sessions.Calls (usage/ledger.go:122): with a canary Claude folder holding three of the sessions package's fixtures, six usage tests fail — TestLedger, TestSubscriptionListPrice, TestLedgerUsesTheStatedPrice, TestLedgerConfiguredPricesAndRefresh and the two Antigravity ledger ones — and pass with the clearing; provider passes with the same canary either way, but its Accounts read what an agent keeps on the machine too. #543 missed these by how it looked for its sites: by the clearing loops a sandbox already had, which one with no loop at all has no way to answer. TestFolderVarsAreListed reads the sources of the four packages an agent's folder variable is read in — this one, sessions, provider and library, the rest of the repository reading only magpie's own variables, the process' and the desktop's — and fails on a name that is neither in agentenv.Vars nor excused here with a reason, so a new agent's variable no longer waits for a developer's shell to meet one. It reads them as files, since a test in sessions or provider cannot import this package, which imports them; it asks for one name each way its pattern matches, so a change in how a variable is read empties the scan loudly instead of passing it, and for every name excused, so no excuse outlives its variable. What it cannot see is a name the sources build at runtime, as omoDir reads its two out of a slice (omo.go:33); both are listed, and a third taken the same way would not be caught. The package doc's VarsTest is TestVarsAreRead. TestFolderVarsAreListed fails without the change when KIMI_CODE_HOME is taken out of Vars, or when a pretend NEWAGENT_HOME is read in agents.go, and names the file that reads it; the six usage tests fail without theirs. On Windows 11 the whole suite answers as it did before the change: 31 packages pass, and the same 15 tests in internal/library, internal/gateway and internal/agent fail for what they failed for before. go vet ./..., go build -tags nogui (windows, GOOS=linux, GOOS=darwin), go vet -tags nogui for linux and darwin over every package touched, and gofmt pass. (#549)
otel: request and response bodies can go with a call's span, as Langfuse's input and output, behind a switch that is off by default (#538: Langfuse 似乎无法看到请求体,如果技术上好实现的话,希望可以加一个开关) — the OTLP export sent only an allowlist of metadata (model, tokens, status, timing), never a body, so a Langfuse trace had no Input or Output. Settings → Observability has a new row, "Include request and response bodies" (otel.bodies, MAGPIE_OTEL_BODIES overriding it as the other MAGPIE_OTEL_* do, and counted in the "environment variables override" note). When it and the export are on, the gateway hands the call's captured request and reply (the Recent-calls capture, first 256 KiB each, "… (cut here by magpie)" added when cut) to its usage record as BodyIn/BodyOut, json:"-" so they never reach usage.jsonl, scrubbed with redact.ScrubJSON as the request archive's are (on top of Mask secrets' placeholders); the span carries them as langfuse.observation.input and langfuse.observation.output. A streamed reply's output is its text put together from the events (Chat's choices[].delta.content, Responses' response.output_text.delta, Anthropic's content_block_delta), a reply that isn't a stream or has no text (tool calls only) going as it came. This is filled for the final record, a locally turned-away request, and an attempt refused before another account was tried (its refusal as output); draw/video/decide and Codex's own-login passthrough still send metadata only. Queued records keep the config they were queued under, so turning bodies off drops any not yet sent, and spans with bodies go four to a request so a batch stays well inside a collector's size limit. The OTLP export row's text now says credentials stay local and prompts and replies too unless bodies are included. Nothing was sent to a real collector or Langfuse. TestOTelExportsBodiesWhenOn (off: no input/output and no prompt in the export; on: the request as input, the reply as output, a streamed reply's text as output, a key in the request scrubbed) fails without the gateway change, and without the scrub; TestBodiesStayOutOfTheLog, TestOTelReplyText, TestOTelSettingsAndOverrides (MAGPIE_OTEL_BODIES) and otel.test.cjs (the row off at first, en and zh, a click saves bodies:true without scrolling, kept through another preference's save; WebKit) pass. go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./... under a temp HOME pass.
otel: request and response bodies can go with a call's span, as Langfuse's input and output, behind a switch that is off by default (#538: Langfuse 似乎无法看到请求体,如果技术上好实现的话,希望可以加一个开关) — the OTLP export sent only an allowlist of metadata (model, tokens, status, timing), never a body, so a Langfuse trace had no Input or Output. Settings → Observability has a new row, "Include request and response bodies" (otel.bodies, MAGPIE_OTEL_BODIES overriding it as the other MAGPIE_OTEL_* do, and counted in the "environment variables override" note). When it and the export are on, the gateway hands the call's captured request and reply (the Recent-calls capture, first 256 KiB each, "… (cut here by magpie)" added when cut) to its usage record as BodyIn/BodyOut, json:"-" so they never reach usage.jsonl, scrubbed with redact.ScrubJSON as the request archive's are (on top of Mask secrets' placeholders); the span carries them as langfuse.observation.input and langfuse.observation.output. A streamed reply's output is its text put together from the events (Chat's choices[].delta.content, Responses' response.output_text.delta, Anthropic's content_block_delta), a reply that isn't a stream or has no text (tool calls only) going as it came. This is filled for the final record, a locally turned-away request, and an attempt refused before another account was tried (its refusal as output); draw/video/decide and Codex's own-login passthrough still send metadata only. Queued records keep the config they were queued under, so turning bodies off drops any not yet sent, and spans with bodies go four to a request so a batch stays well inside a collector's size limit. The OTLP export row's text now says credentials stay local and prompts and replies too unless bodies are included. Nothing was sent to a real collector or Langfuse. TestOTelExportsBodiesWhenOn (off: no input/output and no prompt in the export; on: the request as input, the reply as output, a streamed reply's text as output, a key in the request scrubbed) fails without the gateway change, and without the scrub; TestBodiesStayOutOfTheLog, TestOTelReplyText, TestOTelSettingsAndOverrides (MAGPIE_OTEL_BODIES) and otel.test.cjs (the row off at first, en and zh, a click saves bodies:true without scrolling, kept through another preference's save; WebKit) pass. go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./... under a temp HOME pass.
otel: request and response bodies can go with a call's span, as Langfuse's input and output, behind a switch that is off by default (#538: Langfuse 似乎无法看到请求体,如果技术上好实现的话,希望可以加一个开关) — the OTLP export sent only an allowlist of metadata (model, tokens, status, timing), never a body, so a Langfuse trace had no Input or Output. Settings → Observability has a new row, "Include request and response bodies" (otel.bodies, MAGPIE_OTEL_BODIES overriding it as the other MAGPIE_OTEL_* do, and counted in the "environment variables override" note). When it and the export are on, the gateway hands the call's captured request and reply (the Recent-calls capture, first 256 KiB each, "… (cut here by magpie)" added when cut) to its usage record as BodyIn/BodyOut, json:"-" so they never reach usage.jsonl, scrubbed with redact.ScrubJSON as the request archive's are (on top of Mask secrets' placeholders); the span carries them as langfuse.observation.input and langfuse.observation.output. A streamed reply's output is its text put together from the events (Chat's choices[].delta.content, Responses' response.output_text.delta, Anthropic's content_block_delta), a reply that isn't a stream or has no text (tool calls only) going as it came. This is filled for the final record, a locally turned-away request, and an attempt refused before another account was tried (its refusal as output); draw/video/decide and Codex's own-login passthrough still send metadata only. Queued records keep the config they were queued under, so turning bodies off drops any not yet sent, and spans with bodies go four to a request so a batch stays well inside a collector's size limit. The OTLP export row's text now says credentials stay local and prompts and replies too unless bodies are included. Nothing was sent to a real collector or Langfuse. TestOTelExportsBodiesWhenOn (off: no input/output and no prompt in the export; on: the request as input, the reply as output, a streamed reply's text as output, a key in the request scrubbed) fails without the gateway change, and without the scrub; TestBodiesStayOutOfTheLog, TestOTelReplyText, TestOTelSettingsAndOverrides (MAGPIE_OTEL_BODIES) and otel.test.cjs (the row off at first, en and zh, a click saves bodies:true without scrolling, kept through another preference's save; WebKit) pass. go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./... under a temp HOME pass.
usage: with WebDAV or S3 sync, each computer shares its usage and sees the others', the total and each computer's part (#542) — sync carried keys, agents and the library, and each computer's Usage page counted only the calls made on it. Sync now has a Usage tick (off unless picked, usage=yes in
magpie webdav/magpie s3): a computer with it on puts its own calls up as one file a day, magpie/usage/<id>-<day>.magpie-usage beside the bundle (under the prefix's usage/ on S3), each a gzipped list of times, agents, providers, models, tokens, costs and statuses, sealed with the sync passphrase (backup.SealData, the key cached per passphrase so a sync derives it once); no request body, session, archive name or key goes, a call another computer passed on over the LAN (Via) isn't put up again, and an error is cut to 300 characters. The id is a random one kept in usage-computer.json, which no backup carries, with the host name beside it. A computer only ever writes and deletes its own names, so none overwrites another's; the files are not in the bundle and its hash, so sharing usage never makes a sync or a conflict of the bundle; the first sync puts up the last 90 days, after that only yesterday and today, at most every 15 minutes (a Save or Sync now at once), and a day past 90 is deleted by the computer it is of. The others' files, listed (PROPFIND Depth 1; ListObjectsV2) and fetched only when changed, are kept in usage-others and read with the ledger, as Record.Computer; a failure is said in the sync row ("Couldn't share usage") and never fails the sync. The Requests page and the panel's Usage tab get a Computer pick (this computer, other computers when more than one, each by name) sending computer=this|others|<id>; a row from another computer says "· on <name>" beside its agent, and its details name the computer and say what was said stays there, nothing asked of this computer's session files for it. With no other computer's usage there is no pick and nothing new on either page, and an older magpie never lists or reads usage/. TestUsageSharedBetweenComputers (computers a and b over a fake WebDAV under httptest: each sees 4 calls, the totals and its own and the other's part, nothing counted twice over repeated syncs, the bundle not put again for usage, a new call reaching the other, a day past the window deleted), TestUsageSharedThroughS3 (the same through a fake S3, signed list and delete, a prefix with a space and a plus) and TestUsageOffAsksNothing are new and don't build without the change; usage-computers.test.cjs (WebKit, en and zh: no pick without other computers, the pick and computer=this, another computer's row and details with no /content asked, the panel's pick, the sync form's tick posting usage: true, no click moving the page) fails without it on all but the no-other-computers case. s3-sync, sync-other-kind, panel-usage, request-archive-usage, click-scroll and usage-ledger-chart pass in WebKit. go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./... under a temp HOME pass (TestFXInState failed once on its TempDir cleanup, passes alone). Turning the Usage tick off, or sync off, lets the other computers' usage kept here go (DropAllShared), so the Usage page counts this computer's alone again: TestUsageSharedBetweenComputers checks both, and fails without it.
usage: an Antigravity call at a level answered under its model's own name is no swap (#462: gemini-3.8-flash at medium marked Swapped, answered by gemini-3.8-flash) — on an Antigravity account a call at an effort goes out as that level's id (AntigravitySentID: gemini-3.8-flash-medium), and Cloud Code's reply names the family in modelVersion (gemini-3.8-flash), so Swapped compared gemini-3.8-flash-medium with gemini-3.8-flash and the request list and the Routing view tagged the model as swapped for itself. A sent id whose last word is an effort word (provider.EffortFamily: low, medium, high, xhigh, minimal, extra-low, extra-high) now matches a reply naming the id without it; a reply naming another level, or another model, is still a swap. TestLedgerAntigravityReplyNamingTheFamilyIsNoSwap fails without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestClaudeRunEndsWhenTheClientLeaves, a load flake, passes alone).
usage: with WebDAV or S3 sync, each computer shares its usage and sees the others', the total and each computer's part (#542) — sync carried keys, agents and the library, and each computer's Usage page counted only the calls made on it. Sync now has a Usage tick (off unless picked, usage=yes in
magpie webdav/magpie s3): a computer with it on puts its own calls up as one file a day, magpie/usage/<id>-<day>.magpie-usage beside the bundle (under the prefix's usage/ on S3), each a gzipped list of times, agents, providers, models, tokens, costs and statuses, sealed with the sync passphrase (backup.SealData, the key cached per passphrase so a sync derives it once); no request body, session, archive name or key goes, a call another computer passed on over the LAN (Via) isn't put up again, and an error is cut to 300 characters. The id is a random one kept in usage-computer.json, which no backup carries, with the host name beside it. A computer only ever writes and deletes its own names, so none overwrites another's; the files are not in the bundle and its hash, so sharing usage never makes a sync or a conflict of the bundle; the first sync puts up the last 90 days, after that only yesterday and today, at most every 15 minutes (a Save or Sync now at once), and a day past 90 is deleted by the computer it is of. The others' files, listed (PROPFIND Depth 1; ListObjectsV2) and fetched only when changed, are kept in usage-others and read with the ledger, as Record.Computer; a failure is said in the sync row ("Couldn't share usage") and never fails the sync. The Requests page and the panel's Usage tab get a Computer pick (this computer, other computers when more than one, each by name) sending computer=this|others|<id>; a row from another computer says "· on <name>" beside its agent, and its details name the computer and say what was said stays there, nothing asked of this computer's session files for it. With no other computer's usage there is no pick and nothing new on either page, and an older magpie never lists or reads usage/. TestUsageSharedBetweenComputers (computers a and b over a fake WebDAV under httptest: each sees 4 calls, the totals and its own and the other's part, nothing counted twice over repeated syncs, the bundle not put again for usage, a new call reaching the other, a day past the window deleted), TestUsageSharedThroughS3 (the same through a fake S3, signed list and delete, a prefix with a space and a plus) and TestUsageOffAsksNothing are new and don't build without the change; usage-computers.test.cjs (WebKit, en and zh: no pick without other computers, the pick and computer=this, another computer's row and details with no /content asked, the panel's pick, the sync form's tick posting usage: true, no click moving the page) fails without it on all but the no-other-computers case. s3-sync, sync-other-kind, panel-usage, request-archive-usage, click-scroll and usage-ledger-chart pass in WebKit. go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./... under a temp HOME pass (TestFXInState failed once on its TempDir cleanup, passes alone). Turning the Usage tick off, or sync off, lets the other computers' usage kept here go (DropAllShared), so the Usage page counts this computer's alone again: TestUsageSharedBetweenComputers checks both, and fails without it.
usage: with WebDAV or S3 sync, each computer shares its usage and sees the others', the total and each computer's part (#542) — sync carried keys, agents and the library, and each computer's Usage page counted only the calls made on it. Sync now has a Usage tick (off unless picked, usage=yes in
magpie webdav/magpie s3): a computer with it on puts its own calls up as one file a day, magpie/usage/<id>-<day>.magpie-usage beside the bundle (under the prefix's usage/ on S3), each a gzipped list of times, agents, providers, models, tokens, costs and statuses, sealed with the sync passphrase (backup.SealData, the key cached per passphrase so a sync derives it once); no request body, session, archive name or key goes, a call another computer passed on over the LAN (Via) isn't put up again, and an error is cut to 300 characters. The id is a random one kept in usage-computer.json, which no backup carries, with the host name beside it. A computer only ever writes and deletes its own names, so none overwrites another's; the files are not in the bundle and its hash, so sharing usage never makes a sync or a conflict of the bundle; the first sync puts up the last 90 days, after that only yesterday and today, at most every 15 minutes (a Save or Sync now at once), and a day past 90 is deleted by the computer it is of. The others' files, listed (PROPFIND Depth 1; ListObjectsV2) and fetched only when changed, are kept in usage-others and read with the ledger, as Record.Computer; a failure is said in the sync row ("Couldn't share usage") and never fails the sync. The Requests page and the panel's Usage tab get a Computer pick (this computer, other computers when more than one, each by name) sending computer=this|others|<id>; a row from another computer says "· on <name>" beside its agent, and its details name the computer and say what was said stays there, nothing asked of this computer's session files for it. With no other computer's usage there is no pick and nothing new on either page, and an older magpie never lists or reads usage/. TestUsageSharedBetweenComputers (computers a and b over a fake WebDAV under httptest: each sees 4 calls, the totals and its own and the other's part, nothing counted twice over repeated syncs, the bundle not put again for usage, a new call reaching the other, a day past the window deleted), TestUsageSharedThroughS3 (the same through a fake S3, signed list and delete, a prefix with a space and a plus) and TestUsageOffAsksNothing are new and don't build without the change; usage-computers.test.cjs (WebKit, en and zh: no pick without other computers, the pick and computer=this, another computer's row and details with no /content asked, the panel's pick, the sync form's tick posting usage: true, no click moving the page) fails without it on all but the no-other-computers case. s3-sync, sync-other-kind, panel-usage, request-archive-usage, click-scroll and usage-ledger-chart pass in WebKit. go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./... under a temp HOME pass (TestFXInState failed once on its TempDir cleanup, passes alone). Turning the Usage tick off, or sync off, lets the other computers' usage kept here go (DropAllShared), so the Usage page counts this computer's alone again: TestUsageSharedBetweenComputers checks both, and fails without it.