Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
139 commits
Select commit Hold shift + click to select a range
30713c8
fix(providers): raise default model call timeout to 300s
xuzhiqin1990 Sep 19, 2026
a88abc5
feat(models): add Custom protocols, thinking budget, and image declar…
Wang-Daoji Sep 14, 2026
35c53cb
Let memory summary and evolution call Bedrock Converse and OpenAI Res…
Wang-Daoji Sep 29, 2026
284639b
Add official thinking levels for the September model releases.
Wang-Daoji Sep 29, 2026
cddc65b
Remove the brain icon from the thinking mode toggle.
Wang-Daoji Sep 29, 2026
cb48326
Explain custom model capability options on hover.
Wang-Daoji Sep 29, 2026
2560b36
Save Custom as its own provider and route each protocol to the matchi…
Wang-Daoji Sep 29, 2026
776753b
Let Custom connection tests follow the selected protocol.
Wang-Daoji Sep 29, 2026
a1fae2a
Send max_completion_tokens for newer OpenAI model names on Custom Cha…
Wang-Daoji Sep 29, 2026
f37f21f
Hide thinking for OpenAI Chat Completions on newer reasoning models, …
Wang-Daoji Sep 30, 2026
407cf02
Send reasoning_effort none for OpenAI Chat Completions when newer rea…
Wang-Daoji Sep 30, 2026
85893b7
Send reasoning_effort none on Chat Completions for any GPT or o-serie…
Wang-Daoji Sep 30, 2026
1785faf
Recommend the Responses API for GPT models in the Custom thinking hin…
Wang-Daoji Sep 30, 2026
69cfa4c
Give the Custom provider its own puzzle mark so it is no longer shown…
Wang-Daoji Sep 30, 2026
4e4c5fc
feat: package agent runtime for npm
ZongYue99 Oct 9, 2026
2f934f5
chore: relocate agent npm package output
ZongYue99 Oct 9, 2026
019cce2
fix: sync generated agent npm package version
ZongYue99 Oct 9, 2026
8f9c428
fix(chat): keep copy actions on completed historical replies
Oct 9, 2026
a69fe87
fix: reuse degraded development memory service
Oct 9, 2026
d862a1c
Merge remote-tracking branch 'upstream/v1.2.2' into fix-bug-476-memor…
Oct 10, 2026
37598d5
fix: forward native progress events over SSE
ZongYue99 Oct 11, 2026
256b369
Merge pull request #4 from ZongYue99/main
ZongYue99 Oct 12, 2026
3a6adec
Merge branch 'zy_npm_1008' of https://github.com/ZongYue99/memmy-agen…
ZongYue99 Oct 12, 2026
c3b8162
fix: 支持 apply_patch 工作区内绝对路径
Oct 12, 2026
02e0bad
Merge pull request #613 from ZongYue99/zy_npm_1008
ZongYue99 Oct 12, 2026
22f542f
fix: add correlated API and model latency diagnostics
ZongYue99 Oct 12, 2026
3691ca6
Merge pull request #490 from xuzhiqin1990/fix/model-call-timeout-300s
syzsunshine219 Oct 12, 2026
8f1b045
Merge pull request #596 from zxr19980213/codex/fix-historical-reply-c…
syzsunshine219 Oct 12, 2026
27e0418
fix: wake exec waits on process completion and buffered output
yxyi123 Oct 12, 2026
e48fe20
Merge remote-tracking branch 'upstream/v1.2.3' into fix/memmy-675-exe…
yxyi123 Oct 12, 2026
ca38419
fix: locate bundled Codex CLI for hook trust installation
Orsted290 Oct 10, 2026
f0bd53b
fix: preserve hook removals and roll back failed Codex trust
Orsted290 Oct 12, 2026
516ba15
fix: allow models to share identical endpoints
Orsted290 Oct 12, 2026
b5ac066
fix: reuse saved BYOK endpoint in setup connection tests
Orsted290 Oct 13, 2026
b273fc5
Merge pull request #620 from Orsted290/fix-codex-hook-repeat-install-…
origami-chen Oct 13, 2026
b433ef3
fix: apply explicit API model selection to resumed sessions
ZongYue99 Oct 13, 2026
1d09b69
fix: 拒绝绝对路径中的父级跳转
Oct 13, 2026
440c289
Merge remote-tracking branch 'upstream/v1.2.3' into fix-671-apply-pat…
Oct 13, 2026
980ef05
Merge pull request #600 from jia030928/fix-bug-476-memory-port-conflict
origami-chen Oct 13, 2026
94e3eb9
fix: 恢复登录账号的内置模型
Oct 10, 2026
11c5ef9
Merge pull request #619 from MemTensor/main
syzsunshine219 Oct 13, 2026
0b83e1f
Merge remote-tracking branch 'upstream/v1.2.4' into fix-builtin-model…
Oct 13, 2026
3ba189a
fix: 修复登录账号内置模型配置恢复
Oct 13, 2026
516e2df
fix: 保留账号模型恢复结果并校验账号一致性
Oct 14, 2026
679b0ec
fix: 保留补丁规范化路径中的文件名空格
Oct 14, 2026
4557c6e
Merge remote-tracking branch 'upstream/v1.2.4' into fix-671-apply-pat…
Oct 14, 2026
daf6bc7
Merge pull request #612 from jia030928/fix-671-apply-patch-absolute-path
origami-chen Oct 14, 2026
43ead00
fix: trim API keys when matching pending presets
Orsted290 Oct 14, 2026
f83cdf1
fix: build bundled agent and memory runtimes in Memmy
ZongYue99 Oct 14, 2026
248bbaf
fix: deduplicate native source captures across account changes
Orsted290 Oct 10, 2026
4d38f01
test: clarify native conversation scope regression name
Orsted290 Oct 10, 2026
0e48c98
fix: collapse duplicate cross-account L1 recall candidates
Orsted290 Oct 14, 2026
a892b26
Merge pull request #622 from Orsted290/fix-duplicate-openai-endpoint-…
origami-chen Oct 14, 2026
15164f9
style(desktop): place social sign-in below email login
dolnna Oct 14, 2026
b041201
feat(desktop): refine model thinking controls and custom capabilities
dolnna Oct 14, 2026
9a08615
Merge pull request #1 from Wang-Daoji/codex/provider-thinking-controls
Wang-Daoji Oct 14, 2026
f2e4f07
Let external plugins participate in the agent lifecycle
penggaolai Oct 14, 2026
4753a6b
feat: build and verify runtime artifacts for all platforms
ZongYue99 Oct 14, 2026
6cb5ca4
chore: merge upstream v1.2.4
ZongYue99 Oct 14, 2026
1559374
Merge pull request #631 from ZongYue99/zy_runtime_artifacts
ZongYue99 Oct 14, 2026
7ec2e5e
Keep installed Memmy CLI directly executable
penggaolai Oct 14, 2026
f030dba
feat(auth): enable Google and GitHub social login
Oct 14, 2026
3512c27
Integrate upstream v1.2.4 updates into the customized agent branch
penggaolai Oct 14, 2026
374a4e5
feat: build all Memory runtime platforms with one command
ZongYue99 Oct 14, 2026
7db6ad6
fix(runtime): skip third-party install scripts when packaging the age…
Oct 14, 2026
fff7913
Merge pull request #635 from EF1874/fix/windows-runtime-package-posti…
ZongYue99 Oct 14, 2026
5a756c1
Merge pull request #634 from whipser030/v1.2.4-niu
ZongYue99 Oct 14, 2026
5dcbd11
Merge pull request #637 from antalike/codex/intl-social-login-style
dolnna Oct 14, 2026
195a8fa
fix: 保留迁移后的模型模式并刷新记忆配置
Oct 15, 2026
b9871f4
Merge remote-tracking branch 'upstream/v1.2.4' into fix-builtin-model…
Oct 15, 2026
0192ab8
fix: guard embedding inputs and isolate rejected batches (MEMMY-488)
whqf1123 Oct 10, 2026
ecb2c74
Merge pull request #624 from jia030928/fix-builtin-models-login-v1.2.3
origami-chen Oct 15, 2026
b972ec1
fix: delete generated spans with task memory
Orsted290 Oct 15, 2026
2b8f186
fix: emit SHA-256 checksum files for Agent runtime packages
ZongYue99 Oct 15, 2026
a8dbb8a
Merge remote-tracking branch 'upstream/v1.2.4' into fix-memory-task-s…
Orsted290 Oct 15, 2026
d698dd4
fix: clarify missing ASR model error
Oct 15, 2026
ff70711
Merge pull request #643 from memory-lee/codex/fix-asr-model-not-confi…
ZongYue99 Oct 15, 2026
bb21412
fix: remove whole-turn deadlines from Memmy API
ZongYue99 Oct 15, 2026
b596123
fix: cancel Memmy turns only through explicit stop requests
ZongYue99 Oct 15, 2026
30a1199
fix(desktop): render skill markdown and restore slash command suggest…
Oct 15, 2026
16920de
fix(desktop): limit command palette height and surface stop in search
Oct 15, 2026
1755344
fix(desktop): match slash suggestions by command name prefix
Oct 15, 2026
7de08d5
feat(desktop): complete slash subcommands and model presets
Oct 15, 2026
117f7b2
fix(desktop): expand exact commands and place closest suggestions last
Oct 15, 2026
1cebfe0
fix(desktop): match commands and subcommands as flat candidates
Oct 15, 2026
5140cdd
Merge remote-tracking branch 'origin/v1.2.4' into codex/skill-markdow…
Oct 15, 2026
afece27
Merge pull request #636 from ZongYue99/zy_runtime_artifacts
ZongYue99 Oct 15, 2026
7a121e3
Merge pull request #618 from yxyi123/fix/memmy-675-exec-wakeup
origami-chen Oct 15, 2026
4d3228f
test: distinguish unexpected memory endpoint fixtures
Oct 15, 2026
896ca85
test(windows): model non-Memory health responses
Orsted290 Oct 16, 2026
533d91e
Merge pull request #647 from GUANGDIJI/codex/skill-markdown-display
ZongYue99 Oct 16, 2026
083e5e1
fix: 统一 BYOK 显示、测试与保存的凭据来源
Orsted290 Oct 16, 2026
19ad8c9
Merge remote-tracking branch 'upstream/v1.2.4' into fix/memmy-488-emb…
whqf1123 Oct 16, 2026
3a0d9a4
fix: refine embedding failure isolation and empty chunk handling (MEM…
whqf1123 Oct 16, 2026
2184ff4
fix: 统一设置页凭据校验并保留连续复用关系
Orsted290 Oct 16, 2026
c6061ee
Merge pull request #639 from Orsted290/fix-cross-account-recall-dedup…
origami-chen Oct 16, 2026
58543d4
fix: 同步 v1.2.4 并解决 BYOK 凭据映射冲突
Orsted290 Oct 16, 2026
356a779
fix: find task spans by their own ownership links
Orsted290 Oct 16, 2026
b633dfa
Merge remote-tracking branch 'upstream/v1.2.4' into fix-memory-task-s…
Orsted290 Oct 16, 2026
7e2607d
Merge pull request #641 from whqf1123/fix/memmy-488-embedding-guard-v124
origami-chen Oct 16, 2026
e1683a6
Merge remote branch origin/fix-memory-task-span-delete
Orsted290 Oct 16, 2026
5a0951f
fix: 恢复记忆模型编辑后的连接重测并隔离卡片请求
Orsted290 Oct 17, 2026
4febd21
Merge branch 'v1.2.4' of github.com:MemTensor/memmy-agent into fix-by…
Orsted290 Oct 17, 2026
1fe1a6f
fix: isolate onboarding connection test responses
Orsted290 Oct 17, 2026
e00425f
test: cover BYOK requests completing after page unmount
Orsted290 Oct 17, 2026
4dd3d72
Merge pull request #623 from Orsted290/fix-byok-onboarding-key-test
origami-chen Oct 18, 2026
439ec88
Merge pull request #642 from Orsted290/fix-memory-task-span-delete
origami-chen Oct 18, 2026
9a86f50
Register DeepSeek-V4.1-Flash-1017 so exact model lookups keep its tok…
Wang-Daoji Oct 19, 2026
5d0253f
Merge remote-tracking branch 'upstream/v1.2.4' into feature/update-mo…
Wang-Daoji Oct 19, 2026
5326b30
Merge pull request #665 from Wang-Daoji/feature/update-model-ability-…
Wang-Daoji Oct 19, 2026
6e93119
Merge remote-tracking branch 'upstream/main' into cursor/provider-and…
Wang-Daoji Oct 19, 2026
7cba17e
Merge v1.2.4 into main for PR #668
Oct 19, 2026
98f629b
fix: close PR 668 merge verification
Oct 20, 2026
5846951
Merge pull request #671 from MemTensor/pr-668-resolve
syzsunshine219 Oct 20, 2026
dcca767
fix: 兼容 OpenCode V2 并更新已有记忆插件
Oct 20, 2026
af9319e
Merge remote-tracking branch 'upstream/v1.2.4' into fix-678-opencode-…
Oct 20, 2026
8abb13b
Merge remote-tracking branch 'upstream/v1.2.4' into cursor/provider-a…
Wang-Daoji Oct 20, 2026
f0b6dd9
Merge pull request #680 from Wang-Daoji/cursor/provider-and-thinking-…
Wang-Daoji Oct 20, 2026
cf9db79
fix(wechat): clarify Memmy Full Disk Access permission copy
Oct 20, 2026
216e3ce
fix: 修复 OpenCode V2 连续对话记忆漏采集
Oct 20, 2026
d365096
Merge remote-tracking branch 'upstream/v1.2.4' into fix-678-opencode-…
Oct 20, 2026
9a65ee3
Merge pull request #682 from Grace-dev11/codex/wechat-full-disk-acces…
syzsunshine219 Oct 20, 2026
57acb2a
fix: 退出时取消 OpenCode 采集重试等待
Oct 20, 2026
2ca30e7
fix(wechat): retry snapshots after transient source changes
endxxxx Oct 20, 2026
2853f20
Merge pull request #683 from MemTensor/codex/fix-wechat-snapshot-retry
endxxxx Oct 20, 2026
6a3b011
fix: 补齐 OpenCode 生命周期与迁移记忆采集
Oct 20, 2026
8580b72
Merge remote-tracking branch 'upstream/v1.2.4' into fix-678-opencode-…
Oct 20, 2026
a4b869c
fix: 保持 L3 暂不可用时的 OpenCode 记忆采集
Oct 20, 2026
7001b4f
test: 验证 OpenCode 真实扫描先到时的采集去重
Oct 20, 2026
67bc48c
Merge pull request #679 from jia030928/fix-678-opencode-v2-compat
origami-chen Oct 20, 2026
26fd17f
chore(release): prepare v1.2.4 metadata
Oct 20, 2026
1c85772
fix(release): align provider lint and model key copy
Oct 20, 2026
b0feb21
fix(mac): collect task native agents when desktop exits
Oct 21, 2026
fe02b11
fix(memory): recover History quotes from retained source text
Oct 21, 2026
c41b978
fix(installer): let non-C Windows installs continue after an explicit…
Oct 21, 2026
09e9c7e
Merge pull request #698 from Grace-dev11/codex/bugfix-macos-exit-hist…
syzsunshine219 Oct 21, 2026
ab7d746
fix: declare fzstd for the Memmy agent runtime package
Oct 22, 2026
b1e4276
Merge pull request #711 from memory-lee/fix/memmy-runtime-fzstd
ZongYue99 Oct 22, 2026
9693d25
fix(cli): retry transient gateway bootstrap 503
Oct 22, 2026
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
12 changes: 12 additions & 0 deletions .github/release-notes/v1.2.4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Memmy v1.2.4

基于 v1.2.3 之后已合入并验证的改动,发布稳定版 v1.2.4;本版本集中改进模型配置、Agent 运行时、Memory 可靠性、浏览器与电脑历史采集,以及跨平台运行时分发。

- OpenCode V2 对话采集支持连续会话和真实扫描先到场景,补齐生命周期、去重和 L3 暂不可用时的可恢复行为;微信采集增加瞬态变化重试,并在退出时取消等待中的采集任务。
- BYOK 与内置模型配置统一凭据来源和账号投影,连接测试隔离页面卸载后的响应,保留迁移后的模型模式;补充模型思考级别、token 预算、协议选择、视觉能力和 DeepSeek-V4.1-Flash-1017 能力登记。
- Agent runtime 增强 Hook 生命周期、执行等待、OpenAI-compatible 请求、模型错误诊断和工具结果处理;显式停止取代整轮超时,恢复会话继续使用明确的模型选择。
- Memory 改进跨账号候选去重、Embedding 输入校验与失败批次隔离、任务记忆删除、采集边界和运行时构建,并为各平台 runtime 产物生成校验和。
- 桌面端补齐 slash command 与子命令建议、技能 Markdown 展示、社交登录入口、模型思考控制,以及浏览器和电脑历史授权与恢复路径;Windows History 与微信路径继续保留失败可见性和隐私边界。
- 打包链路统一 Agent 与 Memory 多平台 runtime 构建、版本同步和依赖安装脚本,保持源码、安装包和运行时元数据一致;真实安装、覆盖升级和签名发布仍需按发版门禁分别验收。

本文件只记录 v1.2.4 的版本范围;Full Harness 回归、独立审查、Draft PR、合并、签名 tag、打包和发布仍是相互独立的门槛。
2 changes: 1 addition & 1 deletion .github/workflows/memory-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
version:
description: Memory version (X.Y.Z)
required: true
default: 2.1.6
default: 2.1.7
push:
tags:
- "memory-v*"
Expand Down
101 changes: 101 additions & 0 deletions .github/workflows/runtime-artifacts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
name: Agent and Memory runtime artifacts
run-name: runtime-artifacts-${{ github.ref_name }}

on:
workflow_dispatch:
inputs:
runtime:
description: Runtime packages to build
type: choice
options: [both, memory]
default: both
push:
branches: [zy_memory_runtime_*]
permissions:
contents: read

jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- target: darwin-arm64
runner: macos-14
arch: arm64
- target: darwin-x64
runner: macos-15-intel
arch: x64
- target: linux-arm64
runner: ubuntu-24.04-arm
arch: arm64
- target: linux-x64
runner: ubuntu-24.04
arch: x64
- target: windows-arm64
runner: windows-11-arm
arch: arm64
- target: windows-x64
runner: windows-2025
arch: x64
runs-on: ${{ matrix.runner }}
timeout-minutes: 45
defaults:
run:
shell: bash
env:
COPYFILE_DISABLE: '1'
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
ELECTRON_SKIP_BINARY_DOWNLOAD: '1'
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: 26.0.0
architecture: ${{ matrix.arch }}
cache: npm
- name: Install build dependencies
run: npm ci --no-audit --no-fund
- name: Install Agent build dependencies
if: github.event_name == 'workflow_dispatch' && inputs.runtime != 'memory'
run: npm --prefix App/memmy-agent install --ignore-scripts --no-audit --no-fund
- name: Build Agent runtime
if: github.event_name == 'workflow_dispatch' && inputs.runtime != 'memory'
run: npm run agent:runtime:package -- --target "${{ matrix.target }}" --output "$RUNNER_TEMP/runtime-artifacts"
- name: Build Memory runtime
run: npm run runtime:package -w @memmy/memory -- --target "${{ matrix.target }}" --output "$RUNNER_TEMP/runtime-artifacts"
- name: Verify unpacked runtimes and native dependencies
run: node scripts/internal/shared/verify-runtime-artifacts.mjs "$RUNNER_TEMP/runtime-artifacts" "${{ matrix.target }}" "${{ inputs.runtime || 'memory' }}"
- uses: actions/upload-artifact@v4
with:
name: runtimes-${{ matrix.target }}
path: ${{ runner.temp }}/runtime-artifacts/*
if-no-files-found: error
compression-level: 0
retention-days: 14

collect:
needs: build
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: 26.0.0
- uses: actions/download-artifact@v4
with:
pattern: runtimes-*
path: runtime-input
- name: Collect runtime archives with checksums
run: node scripts/internal/shared/collect-runtime-artifacts.mjs runtime-input runtime-output "${{ inputs.runtime || 'memory' }}"
- uses: actions/upload-artifact@v4
with:
name: all-platform-runtimes
path: runtime-output/*
if-no-files-found: error
compression-level: 0
retention-days: 14
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
node_modules/
dist/
App/memmy-agent/npm-packages/
release/
App/shell/desktop/release/
Mac软件打包/
Expand Down
1 change: 1 addition & 0 deletions AgentSourceCore/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from "./cursor-source-turn.js";
export * from "./claude-code-source-turn.js";
export * from "./openclaw-source-turn.js";
export * from "./opencode-source-turn.js";
export * from "./opencode-database-source.js";
export * from "./hermes-source-turn.js";
export * from "./deepseek-source-turn.js";
export * from "./deepseek-session-files.js";
Expand Down
123 changes: 123 additions & 0 deletions AgentSourceCore/src/opencode-database-source.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import type { OpencodeSource, OpencodeSession } from "./opencode-source-turn.js";

interface Database {
prepare(sql: string): { all(...parameters: string[]): unknown[]; get(...parameters: string[]): unknown };
}
type Row = Record<string, unknown>;
const record = (value: unknown): Row => value && typeof value === "object" && !Array.isArray(value) ? value as Row : {};
const parse = (value: unknown): Row => {
if (typeof value !== "string") return record(value);
try { return record(JSON.parse(value)); } catch { return {}; }
};

/** Adapt both native database generations to the same turn parser. Only one
* session is materialised at a time; all callers open the database read-only. */
export function createOpencodeDatabaseSource(db: Database, conversationId?: string): OpencodeSource | null {
const hasTable = (name: string) => Boolean(db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?").get(name));
const v2 = hasTable("session_v2") && hasTable("session_message");
if (!v2 && !(hasTable("session") && hasTable("message") && hasTable("part"))) return null;
const table = v2 ? "session_v2" : "session";
const columns = new Set(db.prepare(`PRAGMA table_info(${table})`).all().map(row => record(row).name));
const sessions = db.prepare(`SELECT id, parent_id AS parentId, directory${columns.has("agent") ? ", agent" : ""}${columns.has("revert") ? ", revert" : ""} FROM ${table}${conversationId ? " WHERE id = ?" : ""} ORDER BY time_created ASC, id ASC`);
const messages = db.prepare(v2
? "SELECT id, type, data, time_created FROM session_message WHERE session_id = ? ORDER BY seq ASC, id ASC"
: "SELECT id, data FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC");
const parts = v2 ? null : db.prepare("SELECT id, data FROM part WHERE message_id = ? ORDER BY time_created ASC, id ASC");
// Official V1 -> V2 migration retains the original tables. They are positive
// provenance for migrated messages, whose completion/parent/tool identity must
// remain identical to captures made before upgrading. Native V2 rows still
// require their own durable idle boundary.
const legacyMessages = v2 && hasTable("message") && hasTable("part")
? db.prepare("SELECT id, data FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC") : null;
const legacyParts = legacyMessages ? db.prepare("SELECT id, data FROM part WHERE message_id = ? ORDER BY time_created ASC, id ASC") : null;
const currentParts = new Map<string, Array<{ id: string; data: unknown }>>();
return {
sessions: () => sessions.all(...(conversationId ? [conversationId] : [])).map(value => {
const row = record(value);
const revert = parse(row.revert);
return {
id: String(row.id), parentId: row.parentId == null ? null : String(row.parentId),
directory: row.directory == null ? null : String(row.directory),
agent: typeof row.agent === "string" ? row.agent : null,
revertMessageId: typeof revert.messageID === "string" ? revert.messageID : null
} satisfies OpencodeSession;
}),
messages(sessionId) {
const rows = messages.all(sessionId).map(record);
if (!v2) return rows.map(row => ({ id: String(row.id), data: parse(row.data) }));
currentParts.clear();
let parentID = "";
let agent = "";
let turnAssistants: Row[] = [];
const result: Array<{ id: string; data: Row }> = [];
const legacy = legacyMessages?.all(sessionId).map(record) || [];
const migratedIds = new Set(rows.map(row => String(row.id)));
const legacyIds = new Set<string>();
if (legacy.some(row => migratedIds.has(String(row.id)))) {
for (const row of legacy) {
const id = String(row.id);
legacyIds.add(id);
const data = parse(row.data);
const owner = data.role === "assistant" ? String(data.parentID || "") : id;
// V2 may have removed/reverted a migrated user. Keep its ordinal slot
// for existing source identities, but never resurrect deleted history.
const visible = migratedIds.has(owner);
result.push({ id, data: visible ? data : {} });
if (!visible) continue;
currentParts.set(id, legacyParts!.all(id).map(value => {
const part = record(value); return { id: String(part.id), data: parse(part.data) };
}));
}
}
for (const row of rows) {
const data = parse(row.data);
const type = data.type || row.type;
const id = String(row.id);
if (legacyIds.has(id)) continue;
if (type === "agent-switched") { agent = String(data.agent || ""); continue; }
if (type === "user") {
parentID = id;
turnAssistants = [];
result.push({ id, data: { role: "user", time: data.time || { created: row.time_created } } });
currentParts.set(id, [{ id: id + ":text", data: { type: "text", text: data.text } }]);
} else if (type === "assistant" && parentID) {
const assistant = { ...data, nativeTurnPending: true, role: "assistant", parentID, agent: data.agent || agent };
turnAssistants.push(assistant);
result.push({ id, data: assistant });
const content = Array.isArray(data.content) ? data.content.map(record) : [];
currentParts.set(id, content.map((part, index) => {
if (part.type !== "tool") return { id: `${id}:${index}`, data: part };
const state = record(part.state);
const output = Array.isArray(state.content)
? state.content.map(record).filter(item => item.type === "text").map(item => item.text).join("\n") : "";
return { id: `${id}:${index}`, data: {
type: "tool", callID: part.id, tool: part.name,
metadata: { providerExecuted: part.executed === true },
state: { ...state, output: state.status === "error" ? record(state.error).message || output : output, time: part.time }
} };
}));
} else if (type === "idle") {
// V2 persists an explicit terminal boundary, including tool-only and
// interrupted turns. Do not infer completion from an intermediate step.
// A final failed/empty assistant can be filtered by the shared parser.
// Close all steps so the last useful text/tool still has the terminal status.
for (const assistant of turnAssistants) {
assistant.nativeTurnCompleted = true;
assistant.nativeTurnPending = false;
if (data.outcome === "failed" || data.outcome === "interrupted") assistant.nativeStatus = "failed";
if (data.outcome === "interrupted") assistant.error = { name: "MessageAbortedError" };
assistant.time = { ...record(assistant.time), completed: record(data.time).created || row.time_created };
}
// Freeze the completed turn. Synthetic continuations and independent
// compactions are not new user turns and cannot reopen this parent.
parentID = "";
turnAssistants = [];
}
}
return result;
},
parts: messageId => parts
? parts.all(messageId).map(value => { const row = record(value); return { id: String(row.id), data: parse(row.data) }; })
: currentParts.get(messageId) || []
};
}
13 changes: 10 additions & 3 deletions AgentSourceCore/src/opencode-source-turn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ interface OpencodeAssistant {
pendingTool: boolean;
hasOrdinaryTool: boolean;
hasStructuredOutput: boolean;
nativeTurnCompleted: boolean;
nativeTurnPending: boolean;
nativeStatus?: "succeeded" | "failed";
}

/**
Expand Down Expand Up @@ -112,7 +115,10 @@ export async function* readOpencodeSession(
toolResults: collected.toolResults,
pendingTool: collected.pendingTool,
hasOrdinaryTool: collected.hasOrdinaryTool,
hasStructuredOutput: data.structured !== undefined
hasStructuredOutput: data.structured !== undefined,
nativeTurnCompleted: data.nativeTurnCompleted === true,
nativeTurnPending: data.nativeTurnPending === true,
nativeStatus: data.nativeStatus === "failed" ? "failed" : undefined
};
// An assistant with neither text nor a tool is idle bookkeeping (`step-finish` only).
if (!assistant.assistantText && assistant.toolCalls.length === 0 && !assistant.pendingTool) continue;
Expand All @@ -134,7 +140,7 @@ export async function* readOpencodeSession(
const profileId = user.userAgent || assistantProfileId(assistants);
let reason = "";
if (!user.query.trim()) reason = "turn_content_incomplete";
else if (!last) reason = "turn_incomplete";
else if (!last || last.nativeTurnPending) reason = "turn_incomplete";
else if (assistants.some((assistant) => assistant.pendingTool)) reason = "turn_incomplete";
else if (!last.aborted && opencodeAssistantStillOpen(last)) reason = "turn_incomplete";
// An interrupted turn still counts when it produced text or a finished tool.
Expand All @@ -145,7 +151,7 @@ export async function* readOpencodeSession(
source: OPENCODE_SOURCE_ID, conversationId: session.id, turnId: user.id, profileId,
startedAt: user.startedAt, completedAt, sequence: user.ordinal,
completionEvidence: last!.aborted ? `assistant_aborted:${last!.id}` : `assistant_completed:${last!.id}`,
query: redactSecrets(user.query), answer: redactSecrets(answer), status: "succeeded",
query: redactSecrets(user.query), answer: redactSecrets(answer), status: last!.nativeStatus || "succeeded",
toolCalls: pairSourceToolCalls(toolCalls, toolResults).map(redactCall), toolResults: toolResults.map(redactResult),
...(session.directory ? { workspacePath: session.directory } : {})
};
Expand Down Expand Up @@ -229,6 +235,7 @@ function collectParts(source: OpencodeSource, messageId: string): {
*/
function opencodeAssistantStillOpen(last: OpencodeAssistant): boolean {
if (!last.completedAt) return true;
if (last.nativeTurnCompleted) return false;
if (last.hasStructuredOutput) return false;
if (!last.finish || last.finish === "tool-calls" || last.finish === "unknown") return true;
return last.hasOrdinaryTool;
Expand Down
2 changes: 1 addition & 1 deletion App/backend/local-api-contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
},
"scripts": {
"build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.json",
"test": "npm run typecheck && vitest run tests/desktop-runtime-manifest.test.ts tests/language.test.ts",
"test": "npm run typecheck && vitest run tests/desktop-runtime-manifest.test.ts tests/language.test.ts tests/model-catalog-resolver.test.ts tests/model-thinking-levels.test.ts",
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
Expand Down
Loading
Loading