evolclaw 2.7.0 → 2.7.1

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.
@@ -1,5 +1,6 @@
1
1
  import { query, forkSession as sdkForkSession, getSessionMessages as sdkGetSessionMessages } from '@anthropic-ai/claude-agent-sdk';
2
2
  import { ensureDir, resolveAnthropicConfig } from '../config.js';
3
+ import { DEFAULT_PERMISSION_MODE } from '../types.js';
3
4
  import path from 'path';
4
5
  import fs from 'fs';
5
6
  import os from 'os';
@@ -72,7 +73,7 @@ export class AgentRunner {
72
73
  apiKey;
73
74
  model;
74
75
  effort;
75
- permissionMode = 'auto';
76
+ permissionMode = DEFAULT_PERMISSION_MODE;
76
77
  baseUrl;
77
78
  config;
78
79
  activeSessions = new Map();
@@ -8,6 +8,7 @@ import { StreamIdleMonitor } from './stream-idle-monitor.js';
8
8
  import { logger } from '../../utils/logger.js';
9
9
  import { getErrorMessage, classifyError, ErrorType, ERROR_PREFIX, isInfraError, prefixErrorType, isRetryableError } from '../../utils/error-utils.js';
10
10
  import { summarizeToolInput } from '../permission.js';
11
+ import { DEFAULT_PERMISSION_MODE } from '../../types.js';
11
12
  import { getOwner } from '../../config.js';
12
13
  import { getPackageRoot, resolveRoot } from '../../paths.js';
13
14
  import { renderPromptSection } from '../../prompts/templates.js';
@@ -385,10 +386,8 @@ export class MessageProcessor {
385
386
  ? (sessionKey) => this.messageQueue.cancelIntercept(sessionKey)
386
387
  : undefined,
387
388
  });
388
- // 设置 per-session 权限模式(动态默认值:owner bypass,admin → auto,guest → auto)
389
- const role = session.identity?.role;
390
- const defaultPermMode = role === 'owner' ? 'bypass' : 'auto';
391
- agent.setMode(session.metadata?.permissionMode ?? defaultPermMode);
389
+ // 设置 per-session 权限模式(默认 bypass,所有角色统一)
390
+ agent.setMode(session.metadata?.permissionMode ?? DEFAULT_PERMISSION_MODE);
392
391
  // 标记会话为处理中(实时持久化,重启后可恢复)
393
392
  this.sessionManager.markProcessing(session.id);
394
393
  logger.info(`[MessageProcessor] session ${session.id} marked as processing task=${taskId}`);
@@ -1,4 +1,5 @@
1
1
  import { DatabaseSync } from 'node:sqlite';
2
+ import { DEFAULT_PERMISSION_MODE } from '../../types.js';
2
3
  import { ensureDir } from '../../config.js';
3
4
  import { resolvePaths } from '../../paths.js';
4
5
  import { logger } from '../../utils/logger.js';
@@ -471,7 +472,7 @@ export class SessionManager {
471
472
  session.identity = this.resolveIdentity(channel, userId);
472
473
  // 新话题会话补写默认权限模式
473
474
  if (session.metadata && !session.metadata.permissionMode) {
474
- session.metadata.permissionMode = session.identity?.role === 'owner' ? 'bypass' : 'auto';
475
+ session.metadata.permissionMode = DEFAULT_PERMISSION_MODE;
475
476
  this.db.prepare(`UPDATE sessions SET metadata = ?, updated_at = ? WHERE id = ?`)
476
477
  .run(JSON.stringify(session.metadata), Date.now(), session.id);
477
478
  }
@@ -580,9 +581,9 @@ export class SessionManager {
580
581
  updatedAt: Date.now()
581
582
  };
582
583
  session.identity = this.resolveIdentity(channel, userId);
583
- // 写入默认权限模式(基于角色,只在首次创建时设置)
584
+ // 写入默认权限模式(统一 bypass,只在首次创建时设置)
584
585
  if (!sessionMetadata.permissionMode) {
585
- sessionMetadata.permissionMode = session.identity?.role === 'owner' ? 'bypass' : 'auto';
586
+ sessionMetadata.permissionMode = DEFAULT_PERMISSION_MODE;
586
587
  }
587
588
  this.insertSession(session);
588
589
  this.eventBus.publish({
package/dist/index.js CHANGED
@@ -239,7 +239,7 @@ async function main() {
239
239
  peerId: peerId || '', peerName, messageId, mentions, threadId,
240
240
  // 只在话题场景(threadId 有值)才设置 replyContext;
241
241
  // 纯引用回复(rootId 有值但无 threadId)不设置,避免所有回复都带引用头
242
- replyContext: threadId ? { replyToMessageId: rootId, replyInThread: true } : undefined,
242
+ replyContext: threadId ? { replyToMessageId: rootId ?? threadId, replyInThread: true } : undefined,
243
243
  });
244
244
  }), (channelId, text, replyContext) => inst.channel.sendMessage(channelId, text, {
245
245
  replyToMessageId: replyContext?.replyToMessageId,
@@ -18,10 +18,10 @@
18
18
 
19
19
  ## proactive
20
20
 
21
- [Proactive 模式] 本次对话中你的流式输出不会自动发送给用户,必须通过以下命令主动发送:
22
- - 发送文本:evolclaw ctl send "<消息内容>"
23
- - 发送文件:evolclaw ctl file <路径>
24
- 可多次调用。如不调用,用户将看不到任何回复。
21
+ [Proactive 模式] 你的所有文本输出都会被静默丢弃,用户永远看不到。唯一能让用户收到消息的方式:
22
+ 调用 Bash 工具执行命令 :evolclaw ctl send "<消息内容>"
23
+ 发送文件: evolclaw ctl file <路径>
24
+ 可多次调用发送多条消息 ,如果不想回复停止调用即可。
25
25
 
26
26
 
27
27
  ---
package/dist/types.js CHANGED
@@ -1,4 +1,5 @@
1
1
  // ── Channel config types ──
2
2
  // Single-object form: `name` is optional (defaults to channel type name).
3
3
  // Array form: `name` is required to distinguish instances.
4
- export {};
4
+ /** Default permission mode applied to new sessions. Change here to affect all roles. */
5
+ export const DEFAULT_PERMISSION_MODE = 'bypass';
@@ -152,25 +152,36 @@ export function tailFile(filePath) {
152
152
  child.on('exit', (code) => process.exit(code || 0));
153
153
  return { abort: () => child.kill() };
154
154
  }
155
- // Windows: Node.js-based implementation
155
+ // Windows: Node.js-based implementation using stat polling
156
+ // (fs.watch / ReadDirectoryChangesW is unreliable for cross-process appends)
156
157
  // Output last 20 lines of existing content
157
158
  const content = fs.readFileSync(filePath, 'utf-8');
158
159
  const lines = content.split('\n');
159
160
  const lastLines = lines.slice(-20);
160
161
  process.stdout.write(lastLines.join('\n'));
161
162
  let position = fs.statSync(filePath).size;
162
- const watcher = fs.watch(filePath, () => {
163
- const stat = fs.statSync(filePath);
164
- if (stat.size > position) {
165
- const fd = fs.openSync(filePath, 'r');
166
- const buffer = Buffer.alloc(stat.size - position);
167
- fs.readSync(fd, buffer, 0, buffer.length, position);
168
- fs.closeSync(fd);
169
- process.stdout.write(buffer.toString('utf-8'));
170
- position = stat.size;
163
+ const listener = () => {
164
+ try {
165
+ const stat = fs.statSync(filePath);
166
+ if (stat.size < position) {
167
+ // File was truncated (log rotation) — reset and re-read from start
168
+ position = 0;
169
+ }
170
+ if (stat.size > position) {
171
+ const fd = fs.openSync(filePath, 'r');
172
+ const buffer = Buffer.alloc(stat.size - position);
173
+ fs.readSync(fd, buffer, 0, buffer.length, position);
174
+ fs.closeSync(fd);
175
+ process.stdout.write(buffer.toString('utf-8'));
176
+ position = stat.size;
177
+ }
178
+ }
179
+ catch {
180
+ // File may be briefly unavailable during rotation — ignore and retry next tick
171
181
  }
172
- });
173
- return { abort: () => watcher.close() };
182
+ };
183
+ fs.watchFile(filePath, { interval: 500, persistent: true }, listener);
184
+ return { abort: () => fs.unwatchFile(filePath, listener) };
174
185
  }
175
186
  /**
176
187
  * Resolve file path from import.meta.url (cross-platform safe).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evolclaw",
3
- "version": "2.7.0",
3
+ "version": "2.7.1",
4
4
  "description": "Lightweight AI Agent gateway connecting Claude Agent SDK to messaging channels (Feishu, ACP) with multi-project session management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",