Files
magpie/internal/sessions
yetone dc3deaf09a sessions: Alma's chats are on the Sessions page, read only (mamba on Discord: magpie现在是不是还不能管理alma的会话呢) — Alma keeps every chat in one SQLite database in WAL mode, chat_threads.db in its data folder (~/Library/Application Support/alma on a Mac, $XDG_CONFIG_HOME/alma on Linux, %APPDATA%\alma on Windows): chat_threads (title, "New Chat" until named; model; workspace → its folder; incognito), chat_messages (AI SDK UIMessages in a tree, subagents' under parent_tool_call_id) and usage_records / aux_usage_records (a reply's tokens, cache reads and writes included in input). magpie now lists each chat that has something in it, incognito ones left out, and so are the chats Alma's scheduled tasks run in (thousands of them, which would bury every other agent's sessions), told the way Alma itself tells them: metadata.isCron, or a title starting "⏰ Cron:" (the title is set before the flag; 39 here have only the title): its title (else its first prompt), its folder, prompts and replies, tools and skills (tool-<Name>, dynamic-tool, Skill's skill), and models and tokens from the usage records (the model without Alma's provider id or plugin:<plugin>:<provider>:), priced like the others. It reads through a read-only connection, one read transaction a chat, and reads a chat again only when its own rows changed (updated_at, its messages' count, time and bytes, its usage rows), since the database's files change with every write to any chat. Alma's chats can't be deleted, moved or resumed from magpie: a chat is rows in Alma's live database, with triggers, a full-text index and other tables pointing at it, that only Alma can change safely, and Alma has no command to pick a chat up again; its data folder is in Dirs for discovery. Against a copy of the real database (6126 threads, 25424 messages, 11038 usage rows): 928 chats listed (3763 scheduled ones and the empty ones not), 18300 messages, all titled, every token total equal to the database's sums for those chats; the first listing takes 2.4 s, later ones 0.1 s. TestAlmaSessions (with a chat flagged isCron and one titled "⏰ Cron:" only, each failing without its rule), TestAlmaChatReadAgainWhenItChanges (a title and a usage row changed with no newer time) and TestAlmaModel fail without the change; tests set APPDATA so Windows never reads the real Alma. go vet, builds (darwin, GOOS=linux -tags nogui, GOOS=windows) and go test -tags nogui -count=1 ./... under a temp HOME pass. Not tried in the GUI against the live app.
2026-10-03 19:18:46 +08:00
..
sessions: Alma's chats are on the Sessions page, read only (mamba on Discord: magpie现在是不是还不能管理alma的会话呢) — Alma keeps every chat in one SQLite database in WAL mode, chat_threads.db in its data folder (~/Library/Application Support/alma on a Mac, $XDG_CONFIG_HOME/alma on Linux, %APPDATA%\alma on Windows): chat_threads (title, "New Chat" until named; model; workspace → its folder; incognito), chat_messages (AI SDK UIMessages in a tree, subagents' under parent_tool_call_id) and usage_records / aux_usage_records (a reply's tokens, cache reads and writes included in input). magpie now lists each chat that has something in it, incognito ones left out, and so are the chats Alma's scheduled tasks run in (thousands of them, which would bury every other agent's sessions), told the way Alma itself tells them: metadata.isCron, or a title starting "⏰ Cron:" (the title is set before the flag; 39 here have only the title): its title (else its first prompt), its folder, prompts and replies, tools and skills (tool-<Name>, dynamic-tool, Skill's skill), and models and tokens from the usage records (the model without Alma's provider id or plugin:<plugin>:<provider>:), priced like the others. It reads through a read-only connection, one read transaction a chat, and reads a chat again only when its own rows changed (updated_at, its messages' count, time and bytes, its usage rows), since the database's files change with every write to any chat. Alma's chats can't be deleted, moved or resumed from magpie: a chat is rows in Alma's live database, with triggers, a full-text index and other tables pointing at it, that only Alma can change safely, and Alma has no command to pick a chat up again; its data folder is in Dirs for discovery. Against a copy of the real database (6126 threads, 25424 messages, 11038 usage rows): 928 chats listed (3763 scheduled ones and the empty ones not), 18300 messages, all titled, every token total equal to the database's sums for those chats; the first listing takes 2.4 s, later ones 0.1 s. TestAlmaSessions (with a chat flagged isCron and one titled "⏰ Cron:" only, each failing without its rule), TestAlmaChatReadAgainWhenItChanges (a title and a usage row changed with no newer time) and TestAlmaModel fail without the change; tests set APPDATA so Windows never reads the real Alma. go vet, builds (darwin, GOOS=linux -tags nogui, GOOS=windows) and go test -tags nogui -count=1 ./... under a temp HOME pass. Not tried in the GUI against the live app.
2026-10-03 19:18:46 +08:00
sessions: Alma's chats are on the Sessions page, read only (mamba on Discord: magpie现在是不是还不能管理alma的会话呢) — Alma keeps every chat in one SQLite database in WAL mode, chat_threads.db in its data folder (~/Library/Application Support/alma on a Mac, $XDG_CONFIG_HOME/alma on Linux, %APPDATA%\alma on Windows): chat_threads (title, "New Chat" until named; model; workspace → its folder; incognito), chat_messages (AI SDK UIMessages in a tree, subagents' under parent_tool_call_id) and usage_records / aux_usage_records (a reply's tokens, cache reads and writes included in input). magpie now lists each chat that has something in it, incognito ones left out, and so are the chats Alma's scheduled tasks run in (thousands of them, which would bury every other agent's sessions), told the way Alma itself tells them: metadata.isCron, or a title starting "⏰ Cron:" (the title is set before the flag; 39 here have only the title): its title (else its first prompt), its folder, prompts and replies, tools and skills (tool-<Name>, dynamic-tool, Skill's skill), and models and tokens from the usage records (the model without Alma's provider id or plugin:<plugin>:<provider>:), priced like the others. It reads through a read-only connection, one read transaction a chat, and reads a chat again only when its own rows changed (updated_at, its messages' count, time and bytes, its usage rows), since the database's files change with every write to any chat. Alma's chats can't be deleted, moved or resumed from magpie: a chat is rows in Alma's live database, with triggers, a full-text index and other tables pointing at it, that only Alma can change safely, and Alma has no command to pick a chat up again; its data folder is in Dirs for discovery. Against a copy of the real database (6126 threads, 25424 messages, 11038 usage rows): 928 chats listed (3763 scheduled ones and the empty ones not), 18300 messages, all titled, every token total equal to the database's sums for those chats; the first listing takes 2.4 s, later ones 0.1 s. TestAlmaSessions (with a chat flagged isCron and one titled "⏰ Cron:" only, each failing without its rule), TestAlmaChatReadAgainWhenItChanges (a title and a usage row changed with no newer time) and TestAlmaModel fail without the change; tests set APPDATA so Windows never reads the real Alma. go vet, builds (darwin, GOOS=linux -tags nogui, GOOS=windows) and go test -tags nogui -count=1 ./... under a temp HOME pass. Not tried in the GUI against the live app.
2026-10-03 19:18:46 +08:00
usage: a Codex call through the gateway that wrote to its upstream's cache is no longer listed a second time as a local session call (laowangba233 on #589: 用量中的统计是不是重复了 … 为啥会一个是提供商的一个是本地会话的) — the Usage page drops a Codex rollout's call when it matches a gateway record by session, end time within 2s and equal tokens. Codex reads input_tokens_details.cache_write_tokens from a Responses reply and keeps it as cache_write_input_tokens; the gateway's rUsage read only cached_tokens, so for a call whose upstream told a write the record had CacheWrite 0 and the rollout didn't, the tokens never matched, and the call stayed listed under Local session beside its provider's row. Calls without a write matched, so only some calls were counted twice (Maru Code 92 calls plus 22 "本地会话" in the report). Also, sessions' spent took only the cache read out of Codex's input_tokens, which holds the write too, so such a call's write was counted twice in its own input. Now: the gateway reads cache_write_tokens (input = input_tokens - cached - written) and tells it to Codex when it translates a reply; spent takes the write out of the input as well; and the matcher compares a call's input plus cache writes, its output and its cache reads, so a record and a rollout that split the write differently — an older Codex without the field, or records already in usage.jsonl from before this change — still match, and the duplicates already on a user's page go too. The session caches' versions go up (sessions.json 11, calls-v4) so kept parses are read again, and older calls-v* folders are removed. Under a provider filter the provider ranking still lists the other providers, as it is a picker; the duplicates were what made it read as double counting. TestCodexCacheWriteMatchesGatewayCall (a Codex rollout and gateway records for two calls, one with a 9000-token cache write; with the rollout naming the write and the record not, both naming it, and an older Codex without it: two rows, no Local session row, the provider ranking maru-code alone, unfiltered and filtered to the provider, by QueryPage and LedgerOf; fails without the change with a third row from the log), TestCodexCacheWriteNotInInput and TestResponsesUsageCacheWrite (parse and round trip through what Codex is told) are new and fail without the change; TestCallsCodex's call with a write now expects input 200 (2500 - 2000 read - 300 written). go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./internal/... under a temp HOME pass (TestFXInState's known TempDir-cleanup flake aside, which passes alone).
2026-10-03 00:47:33 +08:00
usage: a Codex call through the gateway that wrote to its upstream's cache is no longer listed a second time as a local session call (laowangba233 on #589: 用量中的统计是不是重复了 … 为啥会一个是提供商的一个是本地会话的) — the Usage page drops a Codex rollout's call when it matches a gateway record by session, end time within 2s and equal tokens. Codex reads input_tokens_details.cache_write_tokens from a Responses reply and keeps it as cache_write_input_tokens; the gateway's rUsage read only cached_tokens, so for a call whose upstream told a write the record had CacheWrite 0 and the rollout didn't, the tokens never matched, and the call stayed listed under Local session beside its provider's row. Calls without a write matched, so only some calls were counted twice (Maru Code 92 calls plus 22 "本地会话" in the report). Also, sessions' spent took only the cache read out of Codex's input_tokens, which holds the write too, so such a call's write was counted twice in its own input. Now: the gateway reads cache_write_tokens (input = input_tokens - cached - written) and tells it to Codex when it translates a reply; spent takes the write out of the input as well; and the matcher compares a call's input plus cache writes, its output and its cache reads, so a record and a rollout that split the write differently — an older Codex without the field, or records already in usage.jsonl from before this change — still match, and the duplicates already on a user's page go too. The session caches' versions go up (sessions.json 11, calls-v4) so kept parses are read again, and older calls-v* folders are removed. Under a provider filter the provider ranking still lists the other providers, as it is a picker; the duplicates were what made it read as double counting. TestCodexCacheWriteMatchesGatewayCall (a Codex rollout and gateway records for two calls, one with a 9000-token cache write; with the rollout naming the write and the record not, both naming it, and an older Codex without it: two rows, no Local session row, the provider ranking maru-code alone, unfiltered and filtered to the provider, by QueryPage and LedgerOf; fails without the change with a third row from the log), TestCodexCacheWriteNotInInput and TestResponsesUsageCacheWrite (parse and round trip through what Codex is told) are new and fail without the change; TestCallsCodex's call with a write now expects input 200 (2500 - 2000 read - 300 written). go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./internal/... under a temp HOME pass (TestFXInState's known TempDir-cleanup flake aside, which passes alone).
2026-10-03 00:47:33 +08:00
sessions: ZCode, DeepSeek Harness, Cline and Qoder's sessions are listed too (lml: 希望会话里也能看到 ZCode、Qoder CN、Cline、DeepSeek Harness 的) — ZCode's are OpenCode's tables in ~/.zcode/cli/db/db.sqlite, read as OpenCode's database is (opened read-only), its tokens the AI SDK's (input less the cache, output as it is), its model modelId, a title_source of default no title, a prompt typed a real_user's; dsh's are ~/.dsh/sessions ($DSH_HOME), a folder a session, the latest format's session[.vN].jsonl[.zstd] read whole, zstd frames and all, a subagent's (parentSession) in its parent's, a fork's seeded events (seedLength, or up to an inherited session/end-seed) counted where they were first written; Cline's are ~/.cline/data/sessions ($CLINE_SESSION_DATA_DIR, $CLINE_DATA_DIR, $CLINE_DIR), <id>.messages.json with its manifest <id>.json for the cwd, the prompt and the title, a subagent's other messages file beside it, a fork's messages before its forkedAt not counted, cline --id <id> to pick it up; Qoder's and Qoder CN's are Claude Code's lines in ~/.qoder/projects and ~/.qoder-cn/projects ($QODER_CONFIG_DIR, $QODERCN_CONFIG_DIR), qodercli --resume / qoderclicn --resume. Each folder is watched when it's there (Dirs). Tests keep HOME in their sandbox now. Tested with made-up fixtures for each (a ZCode database made in the test, dsh plain and zstd, forks of both formats, a Cline fork and subagent, both Qoder builds), and read-only against copies of this machine's ZCode database and dsh sessions: 2 and 13 sessions, their token totals the same as a separate tally of the raw rows
2026-09-28 22:45:50 +08:00
usage: a Codex call through the gateway that wrote to its upstream's cache is no longer listed a second time as a local session call (laowangba233 on #589: 用量中的统计是不是重复了 … 为啥会一个是提供商的一个是本地会话的) — the Usage page drops a Codex rollout's call when it matches a gateway record by session, end time within 2s and equal tokens. Codex reads input_tokens_details.cache_write_tokens from a Responses reply and keeps it as cache_write_input_tokens; the gateway's rUsage read only cached_tokens, so for a call whose upstream told a write the record had CacheWrite 0 and the rollout didn't, the tokens never matched, and the call stayed listed under Local session beside its provider's row. Calls without a write matched, so only some calls were counted twice (Maru Code 92 calls plus 22 "本地会话" in the report). Also, sessions' spent took only the cache read out of Codex's input_tokens, which holds the write too, so such a call's write was counted twice in its own input. Now: the gateway reads cache_write_tokens (input = input_tokens - cached - written) and tells it to Codex when it translates a reply; spent takes the write out of the input as well; and the matcher compares a call's input plus cache writes, its output and its cache reads, so a record and a rollout that split the write differently — an older Codex without the field, or records already in usage.jsonl from before this change — still match, and the duplicates already on a user's page go too. The session caches' versions go up (sessions.json 11, calls-v4) so kept parses are read again, and older calls-v* folders are removed. Under a provider filter the provider ranking still lists the other providers, as it is a picker; the duplicates were what made it read as double counting. TestCodexCacheWriteMatchesGatewayCall (a Codex rollout and gateway records for two calls, one with a 9000-token cache write; with the rollout naming the write and the record not, both naming it, and an older Codex without it: two rows, no Local session row, the provider ranking maru-code alone, unfiltered and filtered to the provider, by QueryPage and LedgerOf; fails without the change with a third row from the log), TestCodexCacheWriteNotInInput and TestResponsesUsageCacheWrite (parse and round trip through what Codex is told) are new and fail without the change; TestCallsCodex's call with a write now expects input 200 (2500 - 2000 read - 300 written). go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./internal/... under a temp HOME pass (TestFXInState's known TempDir-cleanup flake aside, which passes alone).
2026-10-03 00:47:33 +08:00
sessions: Cursor CLI's chats are on the Sessions page, each listed, opened, resumed and deleted to magpie's trash as another agent's sessions are (@ownsh1p on X: why doesn't the Sessions page show cursor-agent's sessions?) — magpie read the session files of Claude Code, Codex, OpenCode, Pi and the rest, and none of Cursor's. cursor-agent (2026.10.01) keeps a chat in its config folder ($CURSOR_CONFIG_DIR, else $XDG_CONFIG_HOME/cursor, else ~/.cursor) at chats/<md5 of the cwd>/<chat id>/store.db, a SQLite file of two tables as its run store (cursor-sdk-local-runtime's sqlite-blob-store) makes them: meta, whose row "0" is the chat's metadata as JSON in hex (agentId, name — "New Agent" until it has a title — createdAt, lastUsedModel, latestRootBlobId and, for a subagent's chat, subagentInfo with its parent's id), and blobs, content-addressed by the hex of their SHA-256. The root blob is a ConversationStateStructure (protobuf): field 1 the ids of the conversation's messages in order, field 13 the archives a compaction summed up (each one's field 1 the messages it summed up), field 26 when the conversation started. Each message is the AI SDK's JSON ({role, content: a string or parts — text, reasoning, tool-call, tool-result, image, file}). Newer builds also write meta.json beside store.db: title, createdAtMs, updatedAtMs, isSubagent and cwd, the one place the chat's folder is said in words (the folder's name is only its hash). This was read from the installed bundle and matches what other projects that read these stores found; no real chat was opened.
2026-10-02 20:47:22 +08:00
sessions: Cursor CLI's chats are on the Sessions page, each listed, opened, resumed and deleted to magpie's trash as another agent's sessions are (@ownsh1p on X: why doesn't the Sessions page show cursor-agent's sessions?) — magpie read the session files of Claude Code, Codex, OpenCode, Pi and the rest, and none of Cursor's. cursor-agent (2026.10.01) keeps a chat in its config folder ($CURSOR_CONFIG_DIR, else $XDG_CONFIG_HOME/cursor, else ~/.cursor) at chats/<md5 of the cwd>/<chat id>/store.db, a SQLite file of two tables as its run store (cursor-sdk-local-runtime's sqlite-blob-store) makes them: meta, whose row "0" is the chat's metadata as JSON in hex (agentId, name — "New Agent" until it has a title — createdAt, lastUsedModel, latestRootBlobId and, for a subagent's chat, subagentInfo with its parent's id), and blobs, content-addressed by the hex of their SHA-256. The root blob is a ConversationStateStructure (protobuf): field 1 the ids of the conversation's messages in order, field 13 the archives a compaction summed up (each one's field 1 the messages it summed up), field 26 when the conversation started. Each message is the AI SDK's JSON ({role, content: a string or parts — text, reasoning, tool-call, tool-result, image, file}). Newer builds also write meta.json beside store.db: title, createdAtMs, updatedAtMs, isSubagent and cwd, the one place the chat's folder is said in words (the folder's name is only its hash). This was read from the installed bundle and matches what other projects that read these stores found; no real chat was opened.
2026-10-02 20:47:22 +08:00
test: every package's tests pass on Windows, and none writes outside its sandbox (the user: 把 Windows 上那几个失败的测试修掉) — on a Windows box the suite failed in most packages, and the root package hung for 25 minutes. Tests set HOME, which os.UserHomeDir ignores on Windows (it reads USERPROFILE), so they read and wrote the runner's own home; each now sets USERPROFILE beside HOME, and Devin's, Goose's, Crush's and Claude Desktop's tests set APPDATA/LOCALAPPDATA, where those keep their files there. said() read the command's output only once it returned, and a pipe holds a few KB on Windows, so magpie model --help blocked forever; it reads while the command writes. Unix permission bits (the gateway key store's 0600 too), exec bits and symlinks Windows lacks are checked only off Windows; TOML strings hold Windows paths as literal strings, JSON as marshalled ones; a URL's path is compared as one. The provider and usage packages run in a home of their own (TestMain, as the gateway's already does), since a CLI's identity kept by a refresh still going when its test ended landed in the runner's ~/.config/magpie — and on a Mac in the user's own. Two product fixes came out of it: imagemcp took a file:///C:/… root or image for the path /C:/…, which doesn't exist on Windows, and now makes a Windows path of it (a host for a UNC share); and session discovery could keep a new Cowork session hidden on Linux, where a folder's mtime is a kernel tick and its size doesn't change, so an entry added within the tick of the last listing left both as they were — a listing made within 2s of its folder's last change is read again, as git does for racy files. TestFilePath and TestRacyDirectoryListedAgain fail without the change. On Windows 11 every package passes (32 of 32) and a run of each package in a fresh home leaves nothing in home, APPDATA or LOCALAPPDATA; on Linux (WSL) and macOS go test -tags nogui ./... passes. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build pass.
2026-10-01 23:15:07 +08:00
test: every package's tests pass on Windows, and none writes outside its sandbox (the user: 把 Windows 上那几个失败的测试修掉) — on a Windows box the suite failed in most packages, and the root package hung for 25 minutes. Tests set HOME, which os.UserHomeDir ignores on Windows (it reads USERPROFILE), so they read and wrote the runner's own home; each now sets USERPROFILE beside HOME, and Devin's, Goose's, Crush's and Claude Desktop's tests set APPDATA/LOCALAPPDATA, where those keep their files there. said() read the command's output only once it returned, and a pipe holds a few KB on Windows, so magpie model --help blocked forever; it reads while the command writes. Unix permission bits (the gateway key store's 0600 too), exec bits and symlinks Windows lacks are checked only off Windows; TOML strings hold Windows paths as literal strings, JSON as marshalled ones; a URL's path is compared as one. The provider and usage packages run in a home of their own (TestMain, as the gateway's already does), since a CLI's identity kept by a refresh still going when its test ended landed in the runner's ~/.config/magpie — and on a Mac in the user's own. Two product fixes came out of it: imagemcp took a file:///C:/… root or image for the path /C:/…, which doesn't exist on Windows, and now makes a Windows path of it (a host for a UNC share); and session discovery could keep a new Cowork session hidden on Linux, where a folder's mtime is a kernel tick and its size doesn't change, so an entry added within the tick of the last listing left both as they were — a listing made within 2s of its folder's last change is read again, as git does for racy files. TestFilePath and TestRacyDirectoryListedAgain fail without the change. On Windows 11 every package passes (32 of 32) and a run of each package in a fresh home leaves nothing in home, APPDATA or LOCALAPPDATA; on Linux (WSL) and macOS go test -tags nogui ./... passes. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build pass.
2026-10-01 23:15:07 +08:00
sessions: a Sessions page lists an agent's sessions by folder, to resume or delete, and deleted ones wait in magpie's trash to be restored (TJHHHH on Discord: session management like CC Switch's, deleting above all, in the GUI) — sessions were only seen on Usage, with no way to remove one. The new Sessions tab shows, per agent, every session under the folder it ran in, with its title or first prompt, when, how many messages and how big, a filter, its resume command to copy and, in the Mac app, Open in terminal. Picked sessions (or one, by its own button) are deleted after magpie's own dialog: their files (Claude Code's .jsonl, its subagents folder and what it keeps by the id under file-history, session-env and todos; every segment of a Codex rollout; Qoder's, WorkBuddy's, Pi's, oh-my-pi's) are moved into magpie's folder, trash/sessions/<agent>/<when>-<id>, with a manifest, never erased; Trash lists them and Restore puts them back where they were. A session written to in the last minute is left, and said so, as the agent may still be running it. Agents' own indexes (Codex's session_index.jsonl and state database, Claude Code's history.jsonl) are not edited: they are written while the agent runs, and a stale entry there is harmless. OpenCode, ZCode, dsh, Cline and Grok keep sessions in databases magpie doesn't write, and show no delete. With nine tabs the narrowest header gets a further step that draws the tabs in, and past that they scroll sideways rather than push the page wider (560px in English, or at 150% text). The dialog uses the shared confirmAsk/closeConfirmAsk, as the gateway keys' do. TestListAgent, TestDeleteRestoreClaude, TestDeleteRestoreCodex, TestDeleteActiveRefused (fails without the running check) and TestDeleteOnlyKnownAgents run in a temp HOME; sessions-manage.test.cjs (Chromium, WebKit, en, zh) fails without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; sessions-manage, click-scroll, old-webkit, text-size, session-terminal, sessions-today, brand, rail-tip, web-page, omarchy, model-test-one, gateway-caller-keys, gateway-connect-keys, api-key-usage, api-key-theme pass.
2026-10-01 22:11:46 +08:00
sessions: Alma's chats are on the Sessions page, read only (mamba on Discord: magpie现在是不是还不能管理alma的会话呢) — Alma keeps every chat in one SQLite database in WAL mode, chat_threads.db in its data folder (~/Library/Application Support/alma on a Mac, $XDG_CONFIG_HOME/alma on Linux, %APPDATA%\alma on Windows): chat_threads (title, "New Chat" until named; model; workspace → its folder; incognito), chat_messages (AI SDK UIMessages in a tree, subagents' under parent_tool_call_id) and usage_records / aux_usage_records (a reply's tokens, cache reads and writes included in input). magpie now lists each chat that has something in it, incognito ones left out, and so are the chats Alma's scheduled tasks run in (thousands of them, which would bury every other agent's sessions), told the way Alma itself tells them: metadata.isCron, or a title starting "⏰ Cron:" (the title is set before the flag; 39 here have only the title): its title (else its first prompt), its folder, prompts and replies, tools and skills (tool-<Name>, dynamic-tool, Skill's skill), and models and tokens from the usage records (the model without Alma's provider id or plugin:<plugin>:<provider>:), priced like the others. It reads through a read-only connection, one read transaction a chat, and reads a chat again only when its own rows changed (updated_at, its messages' count, time and bytes, its usage rows), since the database's files change with every write to any chat. Alma's chats can't be deleted, moved or resumed from magpie: a chat is rows in Alma's live database, with triggers, a full-text index and other tables pointing at it, that only Alma can change safely, and Alma has no command to pick a chat up again; its data folder is in Dirs for discovery. Against a copy of the real database (6126 threads, 25424 messages, 11038 usage rows): 928 chats listed (3763 scheduled ones and the empty ones not), 18300 messages, all titled, every token total equal to the database's sums for those chats; the first listing takes 2.4 s, later ones 0.1 s. TestAlmaSessions (with a chat flagged isCron and one titled "⏰ Cron:" only, each failing without its rule), TestAlmaChatReadAgainWhenItChanges (a title and a usage row changed with no newer time) and TestAlmaModel fail without the change; tests set APPDATA so Windows never reads the real Alma. go vet, builds (darwin, GOOS=linux -tags nogui, GOOS=windows) and go test -tags nogui -count=1 ./... under a temp HOME pass. Not tried in the GUI against the live app.
2026-10-03 19:18:46 +08:00
sessions: OpenCode v2's sessions are read (Kumo31: opencode v2的session改写到新表里了) — OpenCode 2 (v2.0.x, checked against v2.0.18's source) keeps sessions in the same opencode.db but in tables of its own (packages/core/src/session/sql.ts): session_v2 (the session row as before — directory, parent_id, a title that may now be null, time_created/updated, model {id, providerID, variant}, session totals) and session_message (id, session_id, type, seq, time_created/updated, data), not "sessionmessage". A message's kind is its type column (user, assistant, synthetic, system, compaction, idle, …); a user message's data is its prompt {text, files, …} with no part rows; an assistant's has model {id, providerID, variant}, content (text/reasoning/tool), cost, tokens {input, output, reasoning, cache {read, write}} and time {created, completed} (packages/schema/src/session-message.ts, token-usage.ts), counted as before: input without the cache, output without the reasoning (core/src/session/usage.ts). A compaction carries its own tokens, counted on its model, else the one last replied with. On first start OpenCode 2 copies every old session over (core/src/database/v1-migration.bun.ts), leaving session/message/part unwritten and recording kv migration.v1-v2 {"phase":"completed"}; so with session_v2 present only it is read once that is recorded (or no old table is left, as for the pre-split v2 databases it renamed in place), and while the copy is under way an old session not yet in session_v2 is read from the old tables — never both, so nothing counts twice and a session deleted in v2 doesn't come back. Resume stays opencode --session <ses…> (cli/src/commands/commands.ts). Tested with a v2 fixture made in the test from SQL beside stale old tables; the old-table and JSON-file tests still pass.
2026-09-29 00:38:10 +08:00
sessions: OpenCode v2's sessions are read (Kumo31: opencode v2的session改写到新表里了) — OpenCode 2 (v2.0.x, checked against v2.0.18's source) keeps sessions in the same opencode.db but in tables of its own (packages/core/src/session/sql.ts): session_v2 (the session row as before — directory, parent_id, a title that may now be null, time_created/updated, model {id, providerID, variant}, session totals) and session_message (id, session_id, type, seq, time_created/updated, data), not "sessionmessage". A message's kind is its type column (user, assistant, synthetic, system, compaction, idle, …); a user message's data is its prompt {text, files, …} with no part rows; an assistant's has model {id, providerID, variant}, content (text/reasoning/tool), cost, tokens {input, output, reasoning, cache {read, write}} and time {created, completed} (packages/schema/src/session-message.ts, token-usage.ts), counted as before: input without the cache, output without the reasoning (core/src/session/usage.ts). A compaction carries its own tokens, counted on its model, else the one last replied with. On first start OpenCode 2 copies every old session over (core/src/database/v1-migration.bun.ts), leaving session/message/part unwritten and recording kv migration.v1-v2 {"phase":"completed"}; so with session_v2 present only it is read once that is recorded (or no old table is left, as for the pre-split v2 databases it renamed in place), and while the copy is under way an old session not yet in session_v2 is read from the old tables — never both, so nothing counts twice and a session deleted in v2 doesn't come back. Resume stays opencode --session <ses…> (cli/src/commands/commands.ts). Tested with a v2 fixture made in the test from SQL beside stale old tables; the old-table and JSON-file tests still pass.
2026-09-29 00:38:10 +08:00
test: every package's tests pass on Windows, and none writes outside its sandbox (the user: 把 Windows 上那几个失败的测试修掉) — on a Windows box the suite failed in most packages, and the root package hung for 25 minutes. Tests set HOME, which os.UserHomeDir ignores on Windows (it reads USERPROFILE), so they read and wrote the runner's own home; each now sets USERPROFILE beside HOME, and Devin's, Goose's, Crush's and Claude Desktop's tests set APPDATA/LOCALAPPDATA, where those keep their files there. said() read the command's output only once it returned, and a pipe holds a few KB on Windows, so magpie model --help blocked forever; it reads while the command writes. Unix permission bits (the gateway key store's 0600 too), exec bits and symlinks Windows lacks are checked only off Windows; TOML strings hold Windows paths as literal strings, JSON as marshalled ones; a URL's path is compared as one. The provider and usage packages run in a home of their own (TestMain, as the gateway's already does), since a CLI's identity kept by a refresh still going when its test ended landed in the runner's ~/.config/magpie — and on a Mac in the user's own. Two product fixes came out of it: imagemcp took a file:///C:/… root or image for the path /C:/…, which doesn't exist on Windows, and now makes a Windows path of it (a host for a UNC share); and session discovery could keep a new Cowork session hidden on Linux, where a folder's mtime is a kernel tick and its size doesn't change, so an entry added within the tick of the last listing left both as they were — a listing made within 2s of its folder's last change is read again, as git does for racy files. TestFilePath and TestRacyDirectoryListedAgain fail without the change. On Windows 11 every package passes (32 of 32) and a run of each package in a fresh home leaves nothing in home, APPDATA or LOCALAPPDATA; on Linux (WSL) and macOS go test -tags nogui ./... passes. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build pass.
2026-10-01 23:15:07 +08:00
2026-10-02 08:20:33 +08:00
sessions: ZCode, DeepSeek Harness, Cline and Qoder's sessions are listed too (lml: 希望会话里也能看到 ZCode、Qoder CN、Cline、DeepSeek Harness 的) — ZCode's are OpenCode's tables in ~/.zcode/cli/db/db.sqlite, read as OpenCode's database is (opened read-only), its tokens the AI SDK's (input less the cache, output as it is), its model modelId, a title_source of default no title, a prompt typed a real_user's; dsh's are ~/.dsh/sessions ($DSH_HOME), a folder a session, the latest format's session[.vN].jsonl[.zstd] read whole, zstd frames and all, a subagent's (parentSession) in its parent's, a fork's seeded events (seedLength, or up to an inherited session/end-seed) counted where they were first written; Cline's are ~/.cline/data/sessions ($CLINE_SESSION_DATA_DIR, $CLINE_DATA_DIR, $CLINE_DIR), <id>.messages.json with its manifest <id>.json for the cwd, the prompt and the title, a subagent's other messages file beside it, a fork's messages before its forkedAt not counted, cline --id <id> to pick it up; Qoder's and Qoder CN's are Claude Code's lines in ~/.qoder/projects and ~/.qoder-cn/projects ($QODER_CONFIG_DIR, $QODERCN_CONFIG_DIR), qodercli --resume / qoderclicn --resume. Each folder is watched when it's there (Dirs). Tests keep HOME in their sandbox now. Tested with made-up fixtures for each (a ZCode database made in the test, dsh plain and zstd, forks of both formats, a Cline fork and subagent, both Qoder builds), and read-only against copies of this machine's ZCode database and dsh sessions: 2 and 13 sessions, their token totals the same as a separate tally of the raw rows
2026-09-28 22:45:50 +08:00
sessions: Alma's chats are on the Sessions page, read only (mamba on Discord: magpie现在是不是还不能管理alma的会话呢) — Alma keeps every chat in one SQLite database in WAL mode, chat_threads.db in its data folder (~/Library/Application Support/alma on a Mac, $XDG_CONFIG_HOME/alma on Linux, %APPDATA%\alma on Windows): chat_threads (title, "New Chat" until named; model; workspace → its folder; incognito), chat_messages (AI SDK UIMessages in a tree, subagents' under parent_tool_call_id) and usage_records / aux_usage_records (a reply's tokens, cache reads and writes included in input). magpie now lists each chat that has something in it, incognito ones left out, and so are the chats Alma's scheduled tasks run in (thousands of them, which would bury every other agent's sessions), told the way Alma itself tells them: metadata.isCron, or a title starting "⏰ Cron:" (the title is set before the flag; 39 here have only the title): its title (else its first prompt), its folder, prompts and replies, tools and skills (tool-<Name>, dynamic-tool, Skill's skill), and models and tokens from the usage records (the model without Alma's provider id or plugin:<plugin>:<provider>:), priced like the others. It reads through a read-only connection, one read transaction a chat, and reads a chat again only when its own rows changed (updated_at, its messages' count, time and bytes, its usage rows), since the database's files change with every write to any chat. Alma's chats can't be deleted, moved or resumed from magpie: a chat is rows in Alma's live database, with triggers, a full-text index and other tables pointing at it, that only Alma can change safely, and Alma has no command to pick a chat up again; its data folder is in Dirs for discovery. Against a copy of the real database (6126 threads, 25424 messages, 11038 usage rows): 928 chats listed (3763 scheduled ones and the empty ones not), 18300 messages, all titled, every token total equal to the database's sums for those chats; the first listing takes 2.4 s, later ones 0.1 s. TestAlmaSessions (with a chat flagged isCron and one titled "⏰ Cron:" only, each failing without its rule), TestAlmaChatReadAgainWhenItChanges (a title and a usage row changed with no newer time) and TestAlmaModel fail without the change; tests set APPDATA so Windows never reads the real Alma. go vet, builds (darwin, GOOS=linux -tags nogui, GOOS=windows) and go test -tags nogui -count=1 ./... under a temp HOME pass. Not tried in the GUI against the live app.
2026-10-03 19:18:46 +08:00
sessions: Alma's chats are on the Sessions page, read only (mamba on Discord: magpie现在是不是还不能管理alma的会话呢) — Alma keeps every chat in one SQLite database in WAL mode, chat_threads.db in its data folder (~/Library/Application Support/alma on a Mac, $XDG_CONFIG_HOME/alma on Linux, %APPDATA%\alma on Windows): chat_threads (title, "New Chat" until named; model; workspace → its folder; incognito), chat_messages (AI SDK UIMessages in a tree, subagents' under parent_tool_call_id) and usage_records / aux_usage_records (a reply's tokens, cache reads and writes included in input). magpie now lists each chat that has something in it, incognito ones left out, and so are the chats Alma's scheduled tasks run in (thousands of them, which would bury every other agent's sessions), told the way Alma itself tells them: metadata.isCron, or a title starting "⏰ Cron:" (the title is set before the flag; 39 here have only the title): its title (else its first prompt), its folder, prompts and replies, tools and skills (tool-<Name>, dynamic-tool, Skill's skill), and models and tokens from the usage records (the model without Alma's provider id or plugin:<plugin>:<provider>:), priced like the others. It reads through a read-only connection, one read transaction a chat, and reads a chat again only when its own rows changed (updated_at, its messages' count, time and bytes, its usage rows), since the database's files change with every write to any chat. Alma's chats can't be deleted, moved or resumed from magpie: a chat is rows in Alma's live database, with triggers, a full-text index and other tables pointing at it, that only Alma can change safely, and Alma has no command to pick a chat up again; its data folder is in Dirs for discovery. Against a copy of the real database (6126 threads, 25424 messages, 11038 usage rows): 928 chats listed (3763 scheduled ones and the empty ones not), 18300 messages, all titled, every token total equal to the database's sums for those chats; the first listing takes 2.4 s, later ones 0.1 s. TestAlmaSessions (with a chat flagged isCron and one titled "⏰ Cron:" only, each failing without its rule), TestAlmaChatReadAgainWhenItChanges (a title and a usage row changed with no newer time) and TestAlmaModel fail without the change; tests set APPDATA so Windows never reads the real Alma. go vet, builds (darwin, GOOS=linux -tags nogui, GOOS=windows) and go test -tags nogui -count=1 ./... under a temp HOME pass. Not tried in the GUI against the live app.
2026-10-03 19:18:46 +08:00
sessions: ZCode, DeepSeek Harness, Cline and Qoder's sessions are listed too (lml: 希望会话里也能看到 ZCode、Qoder CN、Cline、DeepSeek Harness 的) — ZCode's are OpenCode's tables in ~/.zcode/cli/db/db.sqlite, read as OpenCode's database is (opened read-only), its tokens the AI SDK's (input less the cache, output as it is), its model modelId, a title_source of default no title, a prompt typed a real_user's; dsh's are ~/.dsh/sessions ($DSH_HOME), a folder a session, the latest format's session[.vN].jsonl[.zstd] read whole, zstd frames and all, a subagent's (parentSession) in its parent's, a fork's seeded events (seedLength, or up to an inherited session/end-seed) counted where they were first written; Cline's are ~/.cline/data/sessions ($CLINE_SESSION_DATA_DIR, $CLINE_DATA_DIR, $CLINE_DIR), <id>.messages.json with its manifest <id>.json for the cwd, the prompt and the title, a subagent's other messages file beside it, a fork's messages before its forkedAt not counted, cline --id <id> to pick it up; Qoder's and Qoder CN's are Claude Code's lines in ~/.qoder/projects and ~/.qoder-cn/projects ($QODER_CONFIG_DIR, $QODERCN_CONFIG_DIR), qodercli --resume / qoderclicn --resume. Each folder is watched when it's there (Dirs). Tests keep HOME in their sandbox now. Tested with made-up fixtures for each (a ZCode database made in the test, dsh plain and zstd, forks of both formats, a Cline fork and subagent, both Qoder builds), and read-only against copies of this machine's ZCode database and dsh sessions: 2 and 13 sessions, their token totals the same as a separate tally of the raw rows
2026-09-28 22:45:50 +08:00