@coclaw/openclaw-coclaw 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coclaw/openclaw-coclaw",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "description": "OpenClaw CoClaw channel plugin for remote chat",
@@ -152,7 +152,6 @@ async function gatewayRpc(method, params = {}, options = {}) {
152
152
  });
153
153
  }
154
154
 
155
- // eslint-disable-next-line no-unused-vars -- 功能暂时禁用,保留函数体供后续修复参考
156
155
  async function ensureMainSessionKey() {
157
156
  if (mainSessionEnsured) {
158
157
  return { ok: true, state: 'ready' };
@@ -162,25 +161,25 @@ async function ensureMainSessionKey() {
162
161
  }
163
162
  mainSessionEnsurePromise = (async () => {
164
163
  const key = 'agent:main:main';
164
+ // sessions.resolve 仅返回 { ok, key },不含 entry
165
165
  const resolved = await gatewayRpc('sessions.resolve', { key }, { timeoutMs: 2000 });
166
- const resolvedSessionId = resolved?.response?.result?.entry?.sessionId;
167
- if (resolved?.ok === true && typeof resolvedSessionId === 'string' && resolvedSessionId.trim()) {
166
+ if (resolved?.ok === true) {
168
167
  mainSessionEnsured = true;
169
- logBridgeDebug(`main session key ensure: ready key=${key} sessionId=${resolvedSessionId}`);
170
- return { ok: true, state: 'ready', sessionId: resolvedSessionId };
168
+ logBridgeDebug(`main session key ensure: ready key=${key}`);
169
+ return { ok: true, state: 'ready' };
171
170
  }
171
+ // 仅当网关真实响应 "不存在" 时才创建;超时/网关未就绪等瞬态错误不触发 reset
172
+ if (!resolved?.response) {
173
+ return { ok: false, error: resolved?.error ?? 'resolve_transient_failure' };
174
+ }
175
+ // session key 不存在,通过 sessions.reset 创建
172
176
  const reset = await gatewayRpc('sessions.reset', { key, reason: 'new' }, { timeoutMs: 2500 });
173
177
  if (reset?.ok !== true) {
174
178
  return { ok: false, error: reset?.error ?? 'sessions_reset_failed' };
175
179
  }
176
- const verify = await gatewayRpc('sessions.resolve', { key }, { timeoutMs: 2000 });
177
- const verifySessionId = verify?.response?.result?.entry?.sessionId;
178
- if (verify?.ok === true && typeof verifySessionId === 'string' && verifySessionId.trim()) {
179
- mainSessionEnsured = true;
180
- logBridgeDebug(`main session key ensure: created key=${key} sessionId=${verifySessionId}`);
181
- return { ok: true, state: 'created', sessionId: verifySessionId };
182
- }
183
- return { ok: false, error: verify?.error ?? 'sessions_resolve_after_reset_failed' };
180
+ mainSessionEnsured = true;
181
+ logBridgeDebug(`main session key ensure: created key=${key}`);
182
+ return { ok: true, state: 'created' };
184
183
  })();
185
184
  try {
186
185
  const result = await mainSessionEnsurePromise;
@@ -259,9 +258,7 @@ function ensureGatewayConnection() {
259
258
  gatewayReady = true;
260
259
  logBridgeDebug(`gateway connect ok <- id=${payload.id}`);
261
260
  gatewayConnectReqId = null;
262
- // [DISABLED] ensureMainSessionKey 存在 bug,每次重连都会误触 sessions.reset
263
- // 导致对话被频繁重置。详见 docs/ensure-main-session-bug-analysis.md
264
- // void ensureMainSessionKey();
261
+ void ensureMainSessionKey();
265
262
  }
266
263
  else {
267
264
  gatewayReady = false;
@@ -528,6 +525,8 @@ export async function refreshRealtimeBridge() {
528
525
 
529
526
  export async function stopRealtimeBridge() {
530
527
  started = false;
528
+ mainSessionEnsured = false;
529
+ mainSessionEnsurePromise = null;
531
530
  clearConnectTimer();
532
531
  if (reconnectTimer) {
533
532
  clearTimeout(reconnectTimer);
@@ -184,10 +184,28 @@ export function createSessionManager(options = {}) {
184
184
  }
185
185
  }
186
186
 
187
+ // 补充 sessions.json 中有索引但无 transcript 文件的 session(如 reset 后未对话、新建 session)
188
+ for (const [sessionKey, entry] of Object.entries(index)) {
189
+ const sid = entry?.sessionId;
190
+ if (!sid || grouped.has(sid)) continue;
191
+ grouped.set(sid, {
192
+ sessionId: sid,
193
+ sessionKey,
194
+ indexed: true,
195
+ archiveType: 'live',
196
+ fileName: null,
197
+ updatedAt: entry.updatedAt ?? 0,
198
+ size: 0,
199
+ });
200
+ }
201
+
187
202
  const rows = Array.from(grouped.values());
188
203
  rows.sort((a, b) => b.updatedAt - a.updatedAt);
189
204
 
190
205
  const items = rows.slice(cursor, cursor + limit).map((row) => {
206
+ if (!row.fileName) {
207
+ return { ...row };
208
+ }
191
209
  const transcriptPath = nodePath.join(dir, row.fileName);
192
210
  const derivedTitle = deriveTitle(transcriptPath, logger);
193
211
  if (!derivedTitle) {
@@ -246,7 +264,9 @@ export function createSessionManager(options = {}) {
246
264
  const limit = clamp(params.limit, 1, 500, 100);
247
265
  const cursor = clamp(params.cursor, 0, Number.MAX_SAFE_INTEGER, 0);
248
266
  const file = resolveTranscriptFile(agentId, sessionId);
249
- if (!file) throw new Error(`session transcript not found: ${sessionId}`);
267
+ if (!file) {
268
+ return { agentId, sessionId, total: 0, cursor: String(cursor), nextCursor: null, messages: [] };
269
+ }
250
270
 
251
271
  const all = [];
252
272
  for (const line of fs.readFileSync(file, 'utf8').split(/\r?\n/).filter(Boolean)) {