@lightcone-ai/daemon 0.6.9 → 0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightcone-ai/daemon",
3
- "version": "0.6.9",
3
+ "version": "0.7.1",
4
4
  "type": "module",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -10,20 +10,20 @@ export class AgentManager {
10
10
  constructor({ serverUrl, machineApiKey }) {
11
11
  this.serverUrl = serverUrl;
12
12
  this.machineApiKey = machineApiKey;
13
- // key: `channelId:agentId` → { config, channelId, agentId, sessionId, proc }
13
+ // key: `teamId:agentId` → { config, teamId, agentId, sessionId, proc }
14
14
  this.agents = new Map();
15
15
  // key → true (spawn in progress)
16
16
  this.starting = new Set();
17
17
  }
18
18
 
19
- _key(agentId, channelId) {
20
- return `${channelId ?? ''}:${agentId}`;
19
+ _key(agentId, teamId) {
20
+ return `${teamId ?? ''}:${agentId}`;
21
21
  }
22
22
 
23
23
  handle(msg, connection) {
24
24
  switch (msg.type) {
25
25
  case 'agent:start': return this._startAgent(msg, connection);
26
- case 'agent:stop': return this._stopAgent(msg.agentId, msg.channelId, connection);
26
+ case 'agent:stop': return this._stopAgent(msg.agentId, msg.teamId, connection);
27
27
  case 'agent:deliver': return this._deliverMessage(msg, connection);
28
28
  case 'ping': return connection.send({ type: 'pong' });
29
29
  default:
@@ -40,16 +40,16 @@ export class AgentManager {
40
40
 
41
41
  // ── private ───────────────────────────────────────────────────────────────
42
42
 
43
- _workspaceDir(agentId, channelId) {
44
- const dir = channelId
45
- ? path.join(homedir(), '.lightcone', 'agents', channelId, agentId)
43
+ _workspaceDir(agentId, teamId) {
44
+ const dir = teamId
45
+ ? path.join(homedir(), '.lightcone', 'agents', teamId, agentId)
46
46
  : path.join(homedir(), '.lightcone', 'agents', agentId);
47
47
  mkdirSync(dir, { recursive: true });
48
48
  return dir;
49
49
  }
50
50
 
51
51
  _formatDeliveryText(message) {
52
- return `New message in ${message.channel_type === 'dm' ? 'dm from' : `#${message.channel_name} from`} ${message.sender_name}: ${message.content}`;
52
+ return `New message in ${message.team_type === 'dm' ? 'dm from' : `#${message.team_name} from`} ${message.sender_name}: ${message.content}`;
53
53
  }
54
54
 
55
55
  _takePendingMessage(key) {
@@ -61,23 +61,23 @@ export class AgentManager {
61
61
  return msg;
62
62
  }
63
63
 
64
- async _startAgent({ agentId, channelId, channelName, config }, connection) {
65
- const key = this._key(agentId, channelId);
64
+ async _startAgent({ agentId, teamId, teamName, config }, connection) {
65
+ const key = this._key(agentId, teamId);
66
66
  if (this.agents.has(key) || this.starting.has(key)) {
67
- console.log(`[AgentManager] Agent ${agentId} in channel ${channelId} already registered`);
67
+ console.log(`[AgentManager] Agent ${agentId} in team ${teamId} already registered`);
68
68
  return;
69
69
  }
70
70
  this.starting.add(key);
71
71
 
72
72
  const runtime = config.runtime ?? 'claude';
73
- const workspaceDir = this._workspaceDir(agentId, channelId);
73
+ const workspaceDir = this._workspaceDir(agentId, teamId);
74
74
  const chatBridgePath = new URL('./chat-bridge.js', import.meta.url).pathname;
75
75
  const startupMsg = runtime === 'codex' ? this._takePendingMessage(key) : null;
76
76
 
77
77
  // Fetch skills index for system prompt + MCP derivation (non-blocking on failure)
78
78
  let skills = [];
79
79
  try {
80
- const chParam = channelId ? `?channelId=${encodeURIComponent(channelId)}` : '';
80
+ const chParam = teamId ? `?teamId=${encodeURIComponent(teamId)}` : '';
81
81
  const res = await fetch(`${this.serverUrl}/internal/agent/${agentId}/skills${chParam}`, {
82
82
  headers: { 'Authorization': `Bearer ${this.machineApiKey}` },
83
83
  });
@@ -91,11 +91,11 @@ export class AgentManager {
91
91
  if (runtime === 'kimi') {
92
92
  // ── Kimi CLI ──────────────────────────────────────────────────────────
93
93
  const kimiSpawn = buildKimiSpawn({
94
- config, agentId, channelId, workspaceDir, chatBridgePath,
94
+ config, agentId, teamId, workspaceDir, chatBridgePath,
95
95
  serverUrl: this.serverUrl, machineApiKey: this.machineApiKey, skills,
96
96
  });
97
97
 
98
- console.log(`[AgentManager] Spawning kimi for ${config.displayName ?? agentId} channel=${channelName ?? channelId ?? 'none'} (session=${kimiSpawn.sessionId})`);
98
+ console.log(`[AgentManager] Spawning kimi for ${config.displayName ?? agentId} team=${teamName ?? teamId ?? 'none'} (session=${kimiSpawn.sessionId})`);
99
99
 
100
100
  proc = spawn('kimi', kimiSpawn.args, {
101
101
  cwd: workspaceDir,
@@ -113,7 +113,7 @@ export class AgentManager {
113
113
  const kimiState = { sessionId: kimiSpawn.sessionId, sessionAnnounced: false };
114
114
 
115
115
  this.agents.set(key, {
116
- config, channelId, agentId, sessionId: kimiSpawn.sessionId, proc,
116
+ config, teamId, agentId, sessionId: kimiSpawn.sessionId, proc,
117
117
  runtime: 'kimi', kimiState, kimiIdle: false,
118
118
  });
119
119
  this.starting.delete(key);
@@ -125,7 +125,7 @@ export class AgentManager {
125
125
  buffer = lines.pop();
126
126
  for (const line of lines) {
127
127
  if (!line.trim()) continue;
128
- this._parseKimiLine(key, agentId, channelId, line, connection);
128
+ this._parseKimiLine(key, agentId, teamId, line, connection);
129
129
  }
130
130
  });
131
131
  } else if (runtime === 'codex') {
@@ -136,7 +136,7 @@ export class AgentManager {
136
136
  const codexSpawn = buildCodexSpawn({
137
137
  config,
138
138
  agentId,
139
- channelId,
139
+ teamId,
140
140
  workspaceDir,
141
141
  chatBridgePath,
142
142
  serverUrl: this.serverUrl,
@@ -145,7 +145,7 @@ export class AgentManager {
145
145
  skills,
146
146
  });
147
147
 
148
- console.log(`[AgentManager] Spawning codex for ${config.displayName ?? agentId} channel=${channelName ?? channelId ?? 'none'} (session=${config.sessionId ?? 'new'})`);
148
+ console.log(`[AgentManager] Spawning codex for ${config.displayName ?? agentId} team=${teamName ?? teamId ?? 'none'} (session=${config.sessionId ?? 'new'})`);
149
149
 
150
150
  proc = spawn('codex', codexSpawn.args, {
151
151
  cwd: workspaceDir,
@@ -154,7 +154,7 @@ export class AgentManager {
154
154
  });
155
155
 
156
156
  this.agents.set(key, {
157
- config, channelId, agentId, sessionId: config.sessionId ?? null, proc,
157
+ config, teamId, agentId, sessionId: config.sessionId ?? null, proc,
158
158
  runtime: 'codex',
159
159
  });
160
160
  this.starting.delete(key);
@@ -166,7 +166,7 @@ export class AgentManager {
166
166
  buffer = lines.pop();
167
167
  for (const line of lines) {
168
168
  if (!line.trim()) continue;
169
- this._parseCodexLine(key, agentId, channelId, line, connection);
169
+ this._parseCodexLine(key, agentId, teamId, line, connection);
170
170
  }
171
171
  });
172
172
  } else {
@@ -179,7 +179,7 @@ export class AgentManager {
179
179
  SERVER_URL: this.serverUrl,
180
180
  MACHINE_API_KEY: this.machineApiKey,
181
181
  AGENT_ID: agentId,
182
- CHANNEL_ID: channelId ?? '',
182
+ TEAM_ID: teamId ?? '',
183
183
  },
184
184
  },
185
185
  };
@@ -235,7 +235,7 @@ export class AgentManager {
235
235
  const spawnEnv = { ...process.env, FORCE_COLOR: '0', ...(config.envVars ?? {}) };
236
236
  delete spawnEnv.CLAUDECODE;
237
237
 
238
- console.log(`[AgentManager] Spawning claude for ${config.displayName ?? agentId} channel=${channelName ?? channelId ?? 'none'} (session=${config.sessionId ?? 'new'})`);
238
+ console.log(`[AgentManager] Spawning claude for ${config.displayName ?? agentId} team=${teamName ?? teamId ?? 'none'} (session=${config.sessionId ?? 'new'})`);
239
239
 
240
240
  proc = spawn('claude', args, {
241
241
  cwd: workspaceDir,
@@ -243,7 +243,7 @@ export class AgentManager {
243
243
  stdio: ['pipe', 'pipe', 'pipe'],
244
244
  });
245
245
 
246
- this.agents.set(key, { config, channelId, agentId, sessionId: config.sessionId ?? null, proc, runtime: 'claude' });
246
+ this.agents.set(key, { config, teamId, agentId, sessionId: config.sessionId ?? null, proc, runtime: 'claude' });
247
247
  this.starting.delete(key);
248
248
 
249
249
  // Parse stdout stream for session ID and activity updates
@@ -254,24 +254,24 @@ export class AgentManager {
254
254
  buffer = lines.pop();
255
255
  for (const line of lines) {
256
256
  if (!line.trim()) continue;
257
- this._parseLine(key, agentId, channelId, line, connection);
257
+ this._parseLine(key, agentId, teamId, line, connection);
258
258
  }
259
259
  });
260
260
  }
261
261
 
262
262
  proc.stderr.on('data', (data) => {
263
263
  const text = data.toString().trim();
264
- if (text) console.error(`[AgentManager][${agentId}][${channelId}] stderr:`, text.slice(0, 300));
264
+ if (text) console.error(`[AgentManager][${agentId}][${teamId}] stderr:`, text.slice(0, 300));
265
265
  });
266
266
 
267
267
  proc.on('exit', (code) => {
268
268
  const agent = this.agents.get(key);
269
- console.log(`[AgentManager] Agent ${agentId} channel=${channelId ?? 'none'} exited (code=${code})`);
269
+ console.log(`[AgentManager] Agent ${agentId} team=${teamId ?? 'none'} exited (code=${code})`);
270
270
  this.agents.delete(key);
271
271
 
272
272
  if (code === 0 && runtime === 'codex' && this._pendingMessages?.get(key)?.length) {
273
273
  const restartConfig = { ...config, sessionId: agent?.sessionId ?? config.sessionId ?? null };
274
- this._startAgent({ agentId, channelId, config: restartConfig }, connection);
274
+ this._startAgent({ agentId, teamId, config: restartConfig }, connection);
275
275
  return;
276
276
  }
277
277
 
@@ -279,14 +279,14 @@ export class AgentManager {
279
279
  if (code !== 0 && config.sessionId && !this._retried?.has(key)) {
280
280
  if (!this._retried) this._retried = new Set();
281
281
  this._retried.add(key);
282
- console.log(`[AgentManager] Retrying ${agentId} channel=${channelId} without session (session may not exist locally)`);
282
+ console.log(`[AgentManager] Retrying ${agentId} team=${teamId} without session (session may not exist locally)`);
283
283
  const retryConfig = { ...config, sessionId: null };
284
- this._startAgent({ agentId, channelId, config: retryConfig }, connection);
284
+ this._startAgent({ agentId, teamId, config: retryConfig }, connection);
285
285
  return;
286
286
  }
287
287
 
288
- connection.send({ type: 'agent:status', agentId, channelId, status: 'inactive' });
289
- connection.send({ type: 'agent:activity', agentId, channelId, activity: 'offline', detail: '', entries: [] });
288
+ connection.send({ type: 'agent:status', agentId, teamId, status: 'inactive' });
289
+ connection.send({ type: 'agent:activity', agentId, teamId, activity: 'offline', detail: '', entries: [] });
290
290
  });
291
291
 
292
292
  // Send startup prompt
@@ -296,41 +296,41 @@ export class AgentManager {
296
296
  this._write(key, 'You have just started. Follow your startup sequence: first call read_memory with path="MEMORY.md" to load your memory index, then call check_messages.');
297
297
  }
298
298
 
299
- connection.send({ type: 'agent:status', agentId, channelId, status: 'active' });
300
- connection.send({ type: 'agent:activity', agentId, channelId, activity: 'online', detail: '', entries: [] });
299
+ connection.send({ type: 'agent:status', agentId, teamId, status: 'active' });
300
+ connection.send({ type: 'agent:activity', agentId, teamId, activity: 'online', detail: '', entries: [] });
301
301
 
302
302
  // Flush any messages that arrived while the agent was starting
303
303
  this._flushPending(key, connection);
304
304
  }
305
305
 
306
- _stopAgent(agentId, channelId, connection) {
307
- const key = this._key(agentId, channelId);
306
+ _stopAgent(agentId, teamId, connection) {
307
+ const key = this._key(agentId, teamId);
308
308
  const agent = this.agents.get(key);
309
309
  if (!agent) return;
310
- console.log(`[AgentManager] Stopping agent ${agentId} channel=${channelId ?? 'none'}`);
310
+ console.log(`[AgentManager] Stopping agent ${agentId} team=${teamId ?? 'none'}`);
311
311
  agent.proc?.kill();
312
312
  // exit handler will report status
313
313
  }
314
314
 
315
315
  _deliverMessage(msg, connection) {
316
- const { agentId, channelId, seq, message } = msg;
317
- const key = this._key(agentId, channelId);
318
- connection.send({ type: 'agent:deliver:ack', agentId, channelId, seq });
316
+ const { agentId, teamId, seq, message } = msg;
317
+ const key = this._key(agentId, teamId);
318
+ connection.send({ type: 'agent:deliver:ack', agentId, teamId, seq });
319
319
 
320
320
  if (!this.agents.has(key) && !this.starting.has(key)) {
321
321
  // Agent not running — queue the message and request config to spawn it
322
- console.log(`[AgentManager] Agent ${agentId} channel=${channelId} not running, requesting start for seq=${seq}`);
322
+ console.log(`[AgentManager] Agent ${agentId.slice(0,8)} team=${message.team_name ?? teamId} not running, requesting start for seq=${seq}`);
323
323
  if (!this._pendingMessages) this._pendingMessages = new Map();
324
324
  const pending = this._pendingMessages.get(key) ?? [];
325
325
  pending.push(msg);
326
326
  this._pendingMessages.set(key, pending);
327
- connection.send({ type: 'agent:request_start', agentId, channelId });
327
+ connection.send({ type: 'agent:request_start', agentId, teamId });
328
328
  return;
329
329
  }
330
330
 
331
331
  if (this.starting.has(key)) {
332
332
  // Spawn in progress — queue the message for delivery after start
333
- console.log(`[AgentManager] Agent ${agentId} channel=${channelId} still starting, queuing seq=${seq}`);
333
+ console.log(`[AgentManager] Agent ${agentId.slice(0,8)} team=${message.team_name ?? teamId} still starting, queuing seq=${seq}`);
334
334
  if (!this._pendingMessages) this._pendingMessages = new Map();
335
335
  const pending = this._pendingMessages.get(key) ?? [];
336
336
  pending.push(msg);
@@ -339,7 +339,7 @@ export class AgentManager {
339
339
  }
340
340
 
341
341
  const text = this._formatDeliveryText(message);
342
- console.log(`[AgentManager] Delivering seq=${seq} to agent ${agentId} channel=${channelId}`);
342
+ console.log(`[AgentManager] Delivering seq=${seq} to agent ${agent?.config?.displayName ?? agentId.slice(0,8)} team=${message.team_name ?? teamId}`);
343
343
  const agent = this.agents.get(key);
344
344
  if (agent?.runtime === 'codex') {
345
345
  if (!this._pendingMessages) this._pendingMessages = new Map();
@@ -358,9 +358,10 @@ export class AgentManager {
358
358
  if (!pending || pending.length === 0) return;
359
359
  this._pendingMessages.delete(key);
360
360
  for (const msg of pending) {
361
- const { agentId, channelId, seq, message } = msg;
361
+ const { agentId, teamId, seq, message } = msg;
362
362
  const text = this._formatDeliveryText(message);
363
- console.log(`[AgentManager] Flushing queued seq=${seq} to agent ${agentId} channel=${channelId}`);
363
+ const agent = this.agents.get(key);
364
+ console.log(`[AgentManager] Flushing queued seq=${seq} to agent ${agent?.config?.displayName ?? agentId.slice(0,8)} team=${message.team_name ?? teamId}`);
364
365
  this._write(key, text);
365
366
  }
366
367
  }
@@ -393,7 +394,7 @@ export class AgentManager {
393
394
  }
394
395
  }
395
396
 
396
- _parseKimiLine(key, agentId, channelId, line, connection) {
397
+ _parseKimiLine(key, agentId, teamId, line, connection) {
397
398
  const agent = this.agents.get(key);
398
399
  if (!agent) return;
399
400
  const events = parseKimiLine(line, agent.kimiState);
@@ -402,18 +403,18 @@ export class AgentManager {
402
403
  case 'session_init':
403
404
  if (agent.sessionId !== evt.sessionId) {
404
405
  agent.sessionId = evt.sessionId;
405
- connection.send({ type: 'agent:session', agentId, channelId, sessionId: evt.sessionId });
406
+ connection.send({ type: 'agent:session', agentId, teamId, sessionId: evt.sessionId });
406
407
  }
407
408
  break;
408
409
  case 'thinking':
409
- connection.send({ type: 'agent:activity', agentId, channelId, activity: 'thinking', detail: '', entries: [] });
410
+ connection.send({ type: 'agent:activity', agentId, teamId, activity: 'thinking', detail: '', entries: [] });
410
411
  break;
411
412
  case 'tool_call':
412
- connection.send({ type: 'agent:activity', agentId, channelId, activity: 'working', detail: evt.name, entries: [] });
413
+ connection.send({ type: 'agent:activity', agentId, teamId, activity: 'working', detail: evt.name, entries: [] });
413
414
  break;
414
415
  case 'turn_end':
415
416
  agent.kimiIdle = true;
416
- connection.send({ type: 'agent:activity', agentId, channelId, activity: 'online', detail: '', entries: [] });
417
+ connection.send({ type: 'agent:activity', agentId, teamId, activity: 'online', detail: '', entries: [] });
417
418
  break;
418
419
  case 'error':
419
420
  console.error(`[AgentManager][kimi][${agentId}] Error: ${evt.message}`);
@@ -422,7 +423,7 @@ export class AgentManager {
422
423
  }
423
424
  }
424
425
 
425
- _parseCodexLine(key, agentId, channelId, line, connection) {
426
+ _parseCodexLine(key, agentId, teamId, line, connection) {
426
427
  const agent = this.agents.get(key);
427
428
  if (!agent) return;
428
429
  const events = parseCodexLine(line);
@@ -431,17 +432,17 @@ export class AgentManager {
431
432
  case 'session_init':
432
433
  if (agent.sessionId !== evt.sessionId) {
433
434
  agent.sessionId = evt.sessionId;
434
- connection.send({ type: 'agent:session', agentId, channelId, sessionId: evt.sessionId });
435
+ connection.send({ type: 'agent:session', agentId, teamId, sessionId: evt.sessionId });
435
436
  }
436
437
  break;
437
438
  case 'thinking':
438
- connection.send({ type: 'agent:activity', agentId, channelId, activity: 'thinking', detail: '', entries: [] });
439
+ connection.send({ type: 'agent:activity', agentId, teamId, activity: 'thinking', detail: '', entries: [] });
439
440
  break;
440
441
  case 'tool_call':
441
- connection.send({ type: 'agent:activity', agentId, channelId, activity: 'working', detail: evt.name, entries: [] });
442
+ connection.send({ type: 'agent:activity', agentId, teamId, activity: 'working', detail: evt.name, entries: [] });
442
443
  break;
443
444
  case 'turn_end':
444
- connection.send({ type: 'agent:activity', agentId, channelId, activity: 'online', detail: '', entries: [] });
445
+ connection.send({ type: 'agent:activity', agentId, teamId, activity: 'online', detail: '', entries: [] });
445
446
  break;
446
447
  case 'error':
447
448
  console.error(`[AgentManager][codex][${agentId}] Error: ${evt.message}`);
@@ -450,7 +451,7 @@ export class AgentManager {
450
451
  }
451
452
  }
452
453
 
453
- _parseLine(key, agentId, channelId, line, connection) {
454
+ _parseLine(key, agentId, teamId, line, connection) {
454
455
  let event;
455
456
  try { event = JSON.parse(line); }
456
457
  catch { return; }
@@ -460,7 +461,7 @@ export class AgentManager {
460
461
  const agent = this.agents.get(key);
461
462
  if (agent && agent.sessionId !== event.session_id) {
462
463
  agent.sessionId = event.session_id;
463
- connection.send({ type: 'agent:session', agentId, channelId, sessionId: event.session_id });
464
+ connection.send({ type: 'agent:session', agentId, teamId, sessionId: event.session_id });
464
465
  }
465
466
  }
466
467
 
@@ -469,12 +470,12 @@ export class AgentManager {
469
470
  const hasToolUse = event.message?.content?.some?.(c => c.type === 'tool_use');
470
471
  if (hasToolUse) {
471
472
  const toolName = event.message.content.find(c => c.type === 'tool_use')?.name ?? 'tool';
472
- connection.send({ type: 'agent:activity', agentId, channelId, activity: 'working', detail: toolName, entries: [] });
473
+ connection.send({ type: 'agent:activity', agentId, teamId, activity: 'working', detail: toolName, entries: [] });
473
474
  } else {
474
- connection.send({ type: 'agent:activity', agentId, channelId, activity: 'thinking', detail: '', entries: [] });
475
+ connection.send({ type: 'agent:activity', agentId, teamId, activity: 'thinking', detail: '', entries: [] });
475
476
  }
476
477
  } else if (event.type === 'result') {
477
- connection.send({ type: 'agent:activity', agentId, channelId, activity: 'online', detail: '', entries: [] });
478
+ connection.send({ type: 'agent:activity', agentId, teamId, activity: 'online', detail: '', entries: [] });
478
479
  }
479
480
  }
480
481
  }
@@ -12,10 +12,10 @@ function getArg(name) {
12
12
  const SERVER_URL = process.env.SERVER_URL || getArg('--server-url') || 'http://localhost:8777';
13
13
  const MACHINE_API_KEY = process.env.MACHINE_API_KEY || getArg('--auth-token') || '';
14
14
  const AGENT_ID = process.env.AGENT_ID || getArg('--agent-id') || '';
15
- const CHANNEL_ID = process.env.CHANNEL_ID || getArg('--channel-id') || ''; // injected per-channel at spawn time
15
+ const TEAM_ID = process.env.TEAM_ID || getArg('--team-id') || ''; // injected per-team at spawn time
16
16
 
17
- // Current active channelId for memory isolation (defaults to spawn-time CHANNEL_ID)
18
- let currentChannelId = CHANNEL_ID;
17
+ // Current active teamId for memory isolation (defaults to spawn-time TEAM_ID)
18
+ let currentTeamId = TEAM_ID;
19
19
 
20
20
  async function api(method, path, body) {
21
21
  const url = `${SERVER_URL}/internal/agent/${AGENT_ID}${path}`;
@@ -42,20 +42,20 @@ server.tool('check_messages', 'Check for new messages in your inbox', {}, async
42
42
  const msgs = data.messages ?? [];
43
43
  if (msgs.length === 0) return { content: [{ type: 'text', text: 'No new messages.' }] };
44
44
 
45
- // Track the channelId of the most recent message for memory isolation
45
+ // Track the teamId of the most recent message for memory isolation
46
46
  const lastMsg = msgs[msgs.length - 1];
47
- if (lastMsg.channel_id) currentChannelId = lastMsg.channel_id;
47
+ if (lastMsg.team_id) currentTeamId = lastMsg.team_id;
48
48
 
49
49
  const text = msgs.map(m =>
50
- `[${m.channel_type === 'dm' ? `dm:@${m.channel_name}` : `#${m.channel_name}`}] ${m.sender_name}: ${m.content}`
50
+ `[${m.team_type === 'dm' ? `dm:@${m.team_name}` : `#${m.team_name}`}] ${m.sender_name}: ${m.content}`
51
51
  + (m.task_status ? ` [task #${m.task_number} ${m.task_status}]` : '')
52
52
  ).join('\n');
53
53
  return { content: [{ type: 'text', text }] };
54
54
  });
55
55
 
56
56
  // ── send_message ──────────────────────────────────────────────────────────────
57
- server.tool('send_message', 'Send a message to a channel, DM, or thread', {
58
- target: z.string().describe('Target: #channel-name | dm:@agentName | #channel-name:shortMsgId'),
57
+ server.tool('send_message', 'Send a message to a team, DM, or thread', {
58
+ target: z.string().describe('Target: #team-name | dm:@agentName | #team-name:shortMsgId'),
59
59
  content: z.string().describe('Message content'),
60
60
  }, async ({ target, content }) => {
61
61
  const data = await api('POST', '/send', { target, content });
@@ -63,14 +63,14 @@ server.tool('send_message', 'Send a message to a channel, DM, or thread', {
63
63
  });
64
64
 
65
65
  // ── read_history ──────────────────────────────────────────────────────────────
66
- server.tool('read_history', 'Read message history from a channel, DM, or thread. Supports pagination via before/after and context jumps via around.', {
67
- channel: z.string().describe('Target: #channel-name | dm:@agentName | #channel-name:shortMsgId'),
66
+ server.tool('read_history', 'Read message history from a team, DM, or thread. Supports pagination via before/after and context jumps via around.', {
67
+ team: z.string().describe('Target: #team-name | dm:@agentName | #team-name:shortMsgId'),
68
68
  limit: z.number().optional().describe('Max messages to return (default 50, max 100)'),
69
69
  around: z.union([z.string(), z.number()]).optional().describe('Center the result window around a messageId or seq number'),
70
70
  before: z.number().optional().describe('Return messages before this seq'),
71
71
  after: z.number().optional().describe('Return messages after this seq'),
72
- }, async ({ channel, limit, around, before, after }) => {
73
- const params = new URLSearchParams({ channel, limit: String(Math.min(limit ?? 50, 100)) });
72
+ }, async ({ team, limit, around, before, after }) => {
73
+ const params = new URLSearchParams({ team, limit: String(Math.min(limit ?? 50, 100)) });
74
74
  if (around != null) params.set('around', String(around));
75
75
  if (before != null) params.set('before', String(before));
76
76
  if (after != null) params.set('after', String(after));
@@ -97,29 +97,29 @@ server.tool('read_history', 'Read message history from a channel, DM, or thread.
97
97
  }
98
98
  }
99
99
 
100
- return { content: [{ type: 'text', text: `## History for ${channel} (${msgs.length} messages)\n\n${text}${footer}` }] };
100
+ return { content: [{ type: 'text', text: `## History for ${team} (${msgs.length} messages)\n\n${text}${footer}` }] };
101
101
  });
102
102
 
103
103
  // ── search_messages ──────────────────────────────────────────────────────────
104
- server.tool('search_messages', 'Search messages visible to you. Use this to find relevant conversations, then inspect a hit with read_history(channel=..., around=messageId).', {
104
+ server.tool('search_messages', 'Search messages visible to you. Use this to find relevant conversations, then inspect a hit with read_history(team=..., around=messageId).', {
105
105
  query: z.string().describe('Search query'),
106
- channel: z.string().optional().describe('Optional target to scope the search, e.g. "#general", "dm:@richard"'),
106
+ team: z.string().optional().describe('Optional target to scope the search, e.g. "#general", "dm:@richard"'),
107
107
  limit: z.number().optional().describe('Max results (default 10, max 20)'),
108
- }, async ({ query, channel, limit }) => {
108
+ }, async ({ query, team, limit }) => {
109
109
  const trimmed = query.trim();
110
110
  if (!trimmed) return { content: [{ type: 'text', text: 'Search query cannot be empty.' }] };
111
111
  const params = new URLSearchParams({ q: trimmed, limit: String(Math.min(limit ?? 10, 20)) });
112
- if (channel) params.set('channel', channel);
112
+ if (team) params.set('team', team);
113
113
  try {
114
114
  const data = await api('GET', `/search?${params}`);
115
115
  if (!data.results || data.results.length === 0)
116
116
  return { content: [{ type: 'text', text: 'No search results.' }] };
117
117
  const formatted = data.results.map((r, i) => [
118
118
  `[${i + 1}] msg=${r.id} seq=${r.seq} time=${r.createdAt}`,
119
- `channel: #${r.channelName}`,
119
+ `team: #${r.teamName}`,
120
120
  `sender: @${r.senderName}${r.senderType === 'agent' ? ' (agent)' : ''}`,
121
121
  `content: ${r.snippet}`,
122
- `next: read_history(channel="#${r.channelName}", around="${r.id}", limit=20)`,
122
+ `next: read_history(team="#${r.teamName}", around="${r.id}", limit=20)`,
123
123
  ].join('\n')).join('\n\n');
124
124
  return { content: [{ type: 'text', text: `## Search Results for "${trimmed}" (${data.results.length} results)\n\n${formatted}` }] };
125
125
  } catch (err) {
@@ -160,20 +160,20 @@ server.tool('view_file', 'Download an attached image by its attachment ID and sa
160
160
  });
161
161
 
162
162
  // ── list_server ───────────────────────────────────────────────────────────────
163
- server.tool('list_server', 'List channels, agents, and humans on the server', {}, async () => {
163
+ server.tool('list_server', 'List teams, agents, and humans on the server', {}, async () => {
164
164
  const data = await api('GET', '/server');
165
- const channels = (data.channels ?? []).map(c => ` #${c.name}${c.joined ? ' (joined)' : ''} — ${c.description}`).join('\n');
165
+ const teams = (data.teams ?? []).map(c => ` #${c.name}${c.joined ? ' (joined)' : ''} — ${c.description}`).join('\n');
166
166
  const agents = (data.agents ?? []).map(a => ` @${a.name} [${a.status}]`).join('\n');
167
167
  const humans = (data.humans ?? []).map(h => ` @${h.name}`).join('\n');
168
- return { content: [{ type: 'text', text: `Channels:\n${channels}\n\nAgents:\n${agents}\n\nHumans:\n${humans}` }] };
168
+ return { content: [{ type: 'text', text: `Teams:\n${teams}\n\nAgents:\n${agents}\n\nHumans:\n${humans}` }] };
169
169
  });
170
170
 
171
171
  // ── list_tasks ────────────────────────────────────────────────────────────────
172
- server.tool('list_tasks', 'List tasks in a channel', {
173
- channel: z.string().describe('Target: #channel-name'),
172
+ server.tool('list_tasks', 'List tasks in a team', {
173
+ team: z.string().describe('Target: #team-name'),
174
174
  status: z.enum(['all', 'todo', 'in_progress', 'in_review', 'done']).optional(),
175
- }, async ({ channel, status }) => {
176
- const params = new URLSearchParams({ channel, status: status ?? 'all' });
175
+ }, async ({ team, status }) => {
176
+ const params = new URLSearchParams({ team, status: status ?? 'all' });
177
177
  const data = await api('GET', `/tasks?${params}`);
178
178
  const tasks = data.tasks ?? [];
179
179
  if (tasks.length === 0) return { content: [{ type: 'text', text: 'No tasks found.' }] };
@@ -186,22 +186,22 @@ server.tool('list_tasks', 'List tasks in a channel', {
186
186
  });
187
187
 
188
188
  // ── create_tasks ──────────────────────────────────────────────────────────────
189
- server.tool('create_tasks', 'Create one or more tasks in a channel', {
190
- channel: z.string().describe('Target: #channel-name'),
189
+ server.tool('create_tasks', 'Create one or more tasks in a team', {
190
+ team: z.string().describe('Target: #team-name'),
191
191
  tasks: z.array(z.object({ title: z.string() })).describe('Array of tasks to create'),
192
- }, async ({ channel, tasks }) => {
193
- const data = await api('POST', '/tasks', { channel, tasks });
192
+ }, async ({ team, tasks }) => {
193
+ const data = await api('POST', '/tasks', { team, tasks });
194
194
  const created = (data.tasks ?? []).map(t => `#${t.taskNumber} ${t.title}`).join('\n');
195
195
  return { content: [{ type: 'text', text: `Created:\n${created}` }] };
196
196
  });
197
197
 
198
198
  // ── claim_tasks ───────────────────────────────────────────────────────────────
199
199
  server.tool('claim_tasks', 'Claim one or more tasks to work on', {
200
- channel: z.string().describe('Target: #channel-name'),
200
+ team: z.string().describe('Target: #team-name'),
201
201
  task_numbers: z.array(z.number()).optional().describe('Task numbers to claim'),
202
202
  message_ids: z.array(z.string()).optional().describe('Short message IDs to claim'),
203
- }, async ({ channel, task_numbers, message_ids }) => {
204
- const data = await api('POST', '/tasks/claim', { channel, task_numbers, message_ids });
203
+ }, async ({ team, task_numbers, message_ids }) => {
204
+ const data = await api('POST', '/tasks/claim', { team, task_numbers, message_ids });
205
205
  const results = (data.results ?? []).map(r =>
206
206
  `#${r.taskNumber}: ${r.success ? 'claimed' : `failed (${r.reason})`}`
207
207
  ).join('\n');
@@ -210,26 +210,26 @@ server.tool('claim_tasks', 'Claim one or more tasks to work on', {
210
210
 
211
211
  // ── unclaim_task ──────────────────────────────────────────────────────────────
212
212
  server.tool('unclaim_task', 'Release a claimed task', {
213
- channel: z.string().describe('Target: #channel-name'),
213
+ team: z.string().describe('Target: #team-name'),
214
214
  task_number: z.number().describe('Task number to unclaim'),
215
- }, async ({ channel, task_number }) => {
216
- await api('POST', '/tasks/unclaim', { channel, task_number });
215
+ }, async ({ team, task_number }) => {
216
+ await api('POST', '/tasks/unclaim', { team, task_number });
217
217
  return { content: [{ type: 'text', text: `Task #${task_number} unclaimed.` }] };
218
218
  });
219
219
 
220
220
  // ── update_task_status ────────────────────────────────────────────────────────
221
221
  server.tool('update_task_status', 'Update the status of a task', {
222
- channel: z.string().describe('Target: #channel-name'),
222
+ team: z.string().describe('Target: #team-name'),
223
223
  task_number: z.number().describe('Task number'),
224
224
  status: z.enum(['todo', 'in_progress', 'in_review', 'done']).describe('New status'),
225
- }, async ({ channel, task_number, status }) => {
226
- await api('POST', '/tasks/update-status', { channel, task_number, status });
225
+ }, async ({ team, task_number, status }) => {
226
+ await api('POST', '/tasks/update-status', { team, task_number, status });
227
227
  return { content: [{ type: 'text', text: `Task #${task_number} → ${status}` }] };
228
228
  });
229
229
 
230
230
  // ── list_memory ───────────────────────────────────────────────────────────────
231
- server.tool('list_memory', 'List all memory files stored for this agent in the current channel', {}, async () => {
232
- const chParam = currentChannelId ? `&channelId=${encodeURIComponent(currentChannelId)}` : '';
231
+ server.tool('list_memory', 'List all memory files stored for this agent in the current team', {}, async () => {
232
+ const chParam = currentTeamId ? `&teamId=${encodeURIComponent(currentTeamId)}` : '';
233
233
  const data = await api('GET', `/memory?_=1${chParam}`);
234
234
  const files = data.files ?? [];
235
235
  if (files.length === 0) return { content: [{ type: 'text', text: 'No memory files yet.' }] };
@@ -238,9 +238,9 @@ server.tool('list_memory', 'List all memory files stored for this agent in the c
238
238
 
239
239
  // ── read_memory ───────────────────────────────────────────────────────────────
240
240
  server.tool('read_memory', 'Read a memory file by path (e.g. "MEMORY.md" or "notes/work-log.md")', {
241
- path: z.string().describe('File path, e.g. "MEMORY.md" or "notes/channels.md"'),
241
+ path: z.string().describe('File path, e.g. "MEMORY.md" or "notes/teams.md"'),
242
242
  }, async ({ path }) => {
243
- const chParam = currentChannelId ? `&channelId=${encodeURIComponent(currentChannelId)}` : '';
243
+ const chParam = currentTeamId ? `&teamId=${encodeURIComponent(currentTeamId)}` : '';
244
244
  try {
245
245
  const data = await api('GET', `/memory?path=${encodeURIComponent(path)}${chParam}`);
246
246
  return { content: [{ type: 'text', text: data.content }] };
@@ -255,14 +255,14 @@ server.tool('write_memory', 'Write or update a memory file (full content replace
255
255
  path: z.string().describe('File path, e.g. "MEMORY.md" or "notes/work-log.md"'),
256
256
  content: z.string().describe('Full file content to store'),
257
257
  }, async ({ path, content }) => {
258
- const chParam = currentChannelId ? `&channelId=${encodeURIComponent(currentChannelId)}` : '';
258
+ const chParam = currentTeamId ? `&teamId=${encodeURIComponent(currentTeamId)}` : '';
259
259
  await api('PUT', `/memory?path=${encodeURIComponent(path)}${chParam}`, { content });
260
260
  return { content: [{ type: 'text', text: `Saved ${path}` }] };
261
261
  });
262
262
 
263
263
  // ── skill_list ───────────────────────────────────────────────────────────────
264
264
  server.tool('skill_list', 'List all skills available to you (platform + bound). Returns index only (name + description), not full content.', {}, async () => {
265
- const chParam = currentChannelId ? `?channelId=${encodeURIComponent(currentChannelId)}` : '';
265
+ const chParam = currentTeamId ? `?teamId=${encodeURIComponent(currentTeamId)}` : '';
266
266
  const skills = await api('GET', `/skills${chParam}`);
267
267
  if (!skills || skills.length === 0) return { content: [{ type: 'text', text: 'No skills available.' }] };
268
268
  const lines = skills.map(s => `- [${s.type}] **${s.name}** — ${s.description}`);
@@ -283,16 +283,16 @@ server.tool('skill_read', 'Read the full content of a skill by name or ID', {
283
283
  });
284
284
 
285
285
  // ── skill_create ─────────────────────────────────────────────────────────────
286
- server.tool('skill_create', 'Create a new reusable skill from what you have learned. Auto-binds to the current channel.', {
286
+ server.tool('skill_create', 'Create a new reusable skill from what you have learned. Auto-binds to the current team.', {
287
287
  name: z.string().describe('Short skill name (lowercase, hyphens ok), e.g. "xhs-posting"'),
288
288
  description: z.string().describe('One-line description of what this skill covers'),
289
289
  content: z.string().describe('Full skill content in markdown — procedures, steps, tips'),
290
290
  tags: z.array(z.string()).optional().describe('Optional tags for categorization'),
291
291
  }, async ({ name, description, content, tags }) => {
292
292
  const body = { name, description, content, tags: tags ?? [] };
293
- if (currentChannelId) body.channelId = currentChannelId;
293
+ if (currentTeamId) body.teamId = currentTeamId;
294
294
  const result = await api('POST', '/skills', body);
295
- return { content: [{ type: 'text', text: `Skill "${result.name}" created and bound to current channel.` }] };
295
+ return { content: [{ type: 'text', text: `Skill "${result.name}" created and bound to current team.` }] };
296
296
  });
297
297
 
298
298
  // ── skill_update ─────────────────────────────────────────────────────────────
@@ -13,12 +13,12 @@ Your workspace and MEMORY.md persist across turns, so you can recover context wh
13
13
  You have MCP tools from the "chat" server. Use ONLY these for communication:
14
14
 
15
15
  1. **${t("check_messages")}** — Non-blocking check for new messages. Use freely during work — at natural breakpoints or after notifications.
16
- 2. **${t("send_message")}** — Send a message to a channel or DM.
17
- 3. **${t("list_server")}** — List all channels in this server, which ones you have joined, plus all agents and humans.
18
- 4. **${t("read_history")}** — Read past messages from a channel, DM, or thread. Supports \`before\` / \`after\` pagination and \`around\` for centered context.
16
+ 2. **${t("send_message")}** — Send a message to a team or DM.
17
+ 3. **${t("list_server")}** — List all teams in this server, which ones you have joined, plus all agents and humans.
18
+ 4. **${t("read_history")}** — Read past messages from a team, DM, or thread. Supports \`before\` / \`after\` pagination and \`around\` for centered context.
19
19
  5. **${t("search_messages")}** — Search messages visible to you, then inspect a hit with \`${t("read_history")}\`.
20
- 6. **${t("list_tasks")}** — View a channel's task board.
21
- 7. **${t("create_tasks")}** — Create new task-messages in a channel (supports batch; equivalent to sending a new message and publishing it as a task-message, not claiming it for yourself).
20
+ 6. **${t("list_tasks")}** — View a team's task board.
21
+ 7. **${t("create_tasks")}** — Create new task-messages in a team (supports batch; equivalent to sending a new message and publishing it as a task-message, not claiming it for yourself).
22
22
  8. **${t("claim_tasks")}** — Claim tasks by number (supports batch, handles conflicts).
23
23
  9. **${t("unclaim_task")}** — Release your claim on a task.
24
24
  10. **${t("update_task_status")}** — Change a task's status (e.g. to in_review or done).
@@ -58,40 +58,40 @@ Header fields:
58
58
 
59
59
  ### Sending messages
60
60
 
61
- - **Reply to a channel**: \`send_message(target="#channel-name", content="...")\`
61
+ - **Reply to a team**: \`send_message(target="#team-name", content="...")\`
62
62
  - **Reply to a DM**: \`send_message(target="dm:@peer-name", content="...")\`
63
- - **Reply in a thread**: \`send_message(target="#channel:shortid", content="...")\` or \`send_message(target="dm:@peer:shortid", content="...")\`
63
+ - **Reply in a thread**: \`send_message(target="#team:shortid", content="...")\` or \`send_message(target="dm:@peer:shortid", content="...")\`
64
64
  - **Start a NEW DM**: \`send_message(target="dm:@person-name", content="...")\`
65
65
 
66
- **IMPORTANT**: To reply to any message, always reuse the exact \`target\` from the received message. This ensures your reply goes to the right place — whether it's a channel, DM, or thread.
66
+ **IMPORTANT**: To reply to any message, always reuse the exact \`target\` from the received message. This ensures your reply goes to the right place — whether it's a team, DM, or thread.
67
67
 
68
68
  ### Threads
69
69
 
70
- Threads are sub-conversations attached to a specific message. They let you discuss a topic without cluttering the main channel.
70
+ Threads are sub-conversations attached to a specific message. They let you discuss a topic without cluttering the main team.
71
71
 
72
72
  - **Thread targets** have a colon and short ID suffix: \`#general:a1b2c3d4\` (thread in #general) or \`dm:@richard:x9y8z7a0\` (thread in a DM).
73
73
  - When you receive a message from a thread (the target has a \`:shortid\` suffix), **always reply using that same target** to keep the conversation in the thread.
74
74
  - **Start a new thread**: Use the \`msg=\` field from the header as the thread suffix. For example, if you see \`[target=#general msg=a1b2c3d4 ...]\`, reply with \`send_message(target="#general:a1b2c3d4", content="...")\`. The thread will be auto-created if it doesn't exist yet.
75
75
  - When you send a message, the response includes the message ID. You can use it to start a thread on your own message.
76
- - You can read thread history: \`read_history(channel="#general:a1b2c3d4")\`
76
+ - You can read thread history: \`read_history(team="#general:a1b2c3d4")\`
77
77
  - Threads cannot be nested — you cannot start a thread inside a thread.
78
78
 
79
- ### Discovering people and channels
79
+ ### Discovering people and teams
80
80
 
81
- Call \`list_server\` to see all channels in this server, which ones you have joined, other agents, and humans.
81
+ Call \`list_server\` to see all teams in this server, which ones you have joined, other agents, and humans.
82
82
 
83
- ### Channel awareness
83
+ ### Team awareness
84
84
 
85
- Each channel has a **name** and optionally a **description** that define its purpose (visible via \`list_server\`). Respect them:
86
- - **Reply in context** — always respond in the channel/thread the message came from.
87
- - **Stay on topic** — when proactively sharing results or updates, post in the channel most relevant to the work. Don't scatter messages across unrelated channels.
88
- - If unsure where something belongs, call \`list_server\` to review channel descriptions.
85
+ Each team has a **name** and optionally a **description** that define its purpose (visible via \`list_server\`). Respect them:
86
+ - **Reply in context** — always respond in the team/thread the message came from.
87
+ - **Stay on topic** — when proactively sharing results or updates, post in the team most relevant to the work. Don't scatter messages across unrelated teams.
88
+ - If unsure where something belongs, call \`list_server\` to review team descriptions.
89
89
 
90
90
  ### Reading history
91
91
 
92
- \`read_history(channel="#channel-name")\` or \`read_history(channel="dm:@peer-name")\` or \`read_history(channel="#channel:shortid")\`
92
+ \`read_history(team="#team-name")\` or \`read_history(team="dm:@peer-name")\` or \`read_history(team="#team:shortid")\`
93
93
 
94
- To jump directly to a specific hit with nearby context, use \`read_history(channel="...", around="messageId")\` or \`read_history(channel="...", around=12345)\`.
94
+ To jump directly to a specific hit with nearby context, use \`read_history(team="...", around="messageId")\` or \`read_history(team="...", around=12345)\`.
95
95
 
96
96
  ### Tasks
97
97
 
@@ -104,7 +104,7 @@ When someone sends a message that asks you to do something — fix a bug, write
104
104
  - A regular message (no task suffix): \`@Alice: Can someone look into the login bug?\`
105
105
  - A system notification about task changes: \`📋 Alice converted a message to task #3 "Fix the login bug"\`
106
106
 
107
- Only top-level channel / DM messages can become tasks. Messages inside threads are discussion context — reply there, but keep claims and conversions to top-level messages.
107
+ Only top-level team / DM messages can become tasks. Messages inside threads are discussion context — reply there, but keep claims and conversions to top-level messages.
108
108
 
109
109
  \`read_history\` shows messages in their current state. If a message was later converted to a task, it will show the \`[task #N ...]\` suffix.
110
110
 
@@ -115,7 +115,7 @@ Only top-level channel / DM messages can become tasks. Messages inside threads a
115
115
  **Workflow:**
116
116
  1. Receive a message that requires action → claim it first (by task number if already a task, or by message ID if it's a regular message)
117
117
  2. If the claim fails, someone else is working on it — move on to another task
118
- 3. Post updates in the task's thread: \`send_message(target="#channel:msgShortId", ...)\`
118
+ 3. Post updates in the task's thread: \`send_message(target="#team:msgShortId", ...)\`
119
119
  4. When done, set status to \`in_review\` so a human can validate
120
120
  5. After approval (e.g. "looks good", "merge it"), set status to \`done\`
121
121
 
@@ -145,12 +145,12 @@ When you receive a notification about new tasks, check the task board and claim
145
145
 
146
146
  ## @Mentions
147
147
 
148
- In channel group chats, you can @mention people by their unique name (e.g. "@alice" or "@bob").
148
+ In team group chats, you can @mention people by their unique name (e.g. "@alice" or "@bob").
149
149
  - Your stable Lightcone @mention handle is \`@${name}\`.
150
150
  - Your display name is \`${displayName || name}\`. Treat it as presentation only — when reasoning about identity and @mentions, prefer your stable \`name\`.
151
151
  - Every human and agent has a unique \`name\` — this is their stable identifier for @mentions.
152
152
  - Mention others, not yourself — assign reviews and follow-ups to teammates.
153
- - @mentions only reach people inside the channelchannels are the isolation boundary.
153
+ - @mentions only reach people inside the teamteams are the isolation boundary.
154
154
 
155
155
  ## Communication style
156
156
 
@@ -166,14 +166,14 @@ Keep the user informed. They cannot see your internal reasoning, so:
166
166
  - **Only respond when relevant.** If a message does not @mention you and is not clearly directed at you or your expertise, do NOT respond. Let the appropriate agent handle it.
167
167
  - **Only the person doing the work should report on it.** If someone else completed a task or submitted a PR, don't echo or summarize their work — let them respond to questions about it.
168
168
  - **Claim before you start.** Always call \`${t("claim_tasks")}\` before doing any work on a task. If the claim fails, stop immediately and pick a different task.
169
- - **Before stopping, check for concrete blockers you own.** If you still owe a specific handoff, review, decision, or reply that is currently blocking a specific person, send one minimal actionable message to that person or channel before stopping.
169
+ - **Before stopping, check for concrete blockers you own.** If you still owe a specific handoff, review, decision, or reply that is currently blocking a specific person, send one minimal actionable message to that person or team before stopping.
170
170
  - **Skip idle narration.** Only send messages when you have actionable content — avoid broadcasting that you are waiting or idle.
171
171
 
172
172
  ### Formatting — No HTML
173
173
 
174
- Use plain-text @mentions (e.g. \`@alice\`) and #channel references (e.g. \`#general\`, \`#1\`) — no HTML tags.
174
+ Use plain-text @mentions (e.g. \`@alice\`) and #team references (e.g. \`#general\`, \`#1\`) — no HTML tags.
175
175
 
176
- When referencing a channel or mentioning someone, write them as plain text without backticks. Backtick-wrapped mentions render as code instead of interactive links.
176
+ When referencing a team or mentioning someone, write them as plain text without backticks. Backtick-wrapped mentions render as code instead of interactive links.
177
177
 
178
178
  ### Formatting — URLs in non-English text
179
179
 
@@ -210,7 +210,7 @@ Your memory is stored on the server and persists across machines and restarts. U
210
210
 
211
211
  ## Key Knowledge
212
212
  - Read notes/user-preferences.md for user preferences and conventions
213
- - Read notes/channels.md for what each channel is about and ongoing work
213
+ - Read notes/teams.md for what each team is about and ongoing work
214
214
  - Read notes/domain.md for domain-specific knowledge and conventions
215
215
  - ...
216
216
 
@@ -227,7 +227,7 @@ Your memory is stored on the server and persists across machines and restarts. U
227
227
  2. **World/project context** — The project structure, tech stack, architectural decisions, team conventions, deployment patterns.
228
228
  3. **Domain knowledge** — Domain-specific terminology, conventions, best practices you learn through tasks.
229
229
  4. **Work history** — What has been done, decisions made and why, problems solved, approaches that worked or failed.
230
- 5. **Channel context** — What each channel is about, who participates, what's being discussed, ongoing tasks per channel.
230
+ 5. **Team context** — What each team is about, who participates, what's being discussed, ongoing tasks per team.
231
231
  6. **Other agents** — What other agents do, their specialties, collaboration patterns, how to work with them effectively.
232
232
 
233
233
  ### How to organize memory
@@ -235,7 +235,7 @@ Your memory is stored on the server and persists across machines and restarts. U
235
235
  - **MEMORY.md** is always the index. Keep it concise but comprehensive as a table of contents.
236
236
  - Create a \`notes/\` directory for detailed knowledge files. Use descriptive names:
237
237
  - \`notes/user-preferences.md\` — User's preferences and conventions
238
- - \`notes/channels.md\` — Summary of each channel and its purpose
238
+ - \`notes/teams.md\` — Summary of each team and its purpose
239
239
  - \`notes/work-log.md\` — Important decisions and completed work
240
240
  - \`notes/<domain>.md\` — Domain-specific knowledge
241
241
  - You can also create any other files or directories for your work (scripts, notes, data, etc.)
@@ -250,7 +250,7 @@ Your context will be periodically compressed to stay within limits. When this ha
250
250
  - **MEMORY.md must be self-sufficient as a recovery point.** After reading it, you should be able to understand who you are, what you know, and what you were working on.
251
251
  - **Before a long task**, write a brief "Active Context" note in MEMORY.md so you can resume if interrupted mid-task.
252
252
  - **After completing work**, update your notes and MEMORY.md index so nothing is lost.
253
- - Keep MEMORY.md complete enough that context compression preserves: which channel is about what, what tasks are in progress, what the user has asked for, and what other agents are doing.
253
+ - Keep MEMORY.md complete enough that context compression preserves: which team is about what, what tasks are in progress, what the user has asked for, and what other agents are doing.
254
254
 
255
255
  ## Capabilities
256
256
 
@@ -277,11 +277,11 @@ Your agent ID is: ${agentId}
277
277
  const DATA_FETCHER_PROMPT = `
278
278
  ## 你的专属职责:获取数据
279
279
 
280
- 你是内容发布流水线的第一环。当 #content-publish 频道收到用户的职位推广需求时,你负责从职位数据库查询数据并输出结构化结果。
280
+ 你是内容发布流水线的第一环。当 #content-publish 团队收到用户的职位推广需求时,你负责从职位数据库查询数据并输出结构化结果。
281
281
 
282
282
  ### 工作流程
283
283
 
284
- 1. 监听 #content-publish 频道的用户消息
284
+ 1. 监听 #content-publish 团队的用户消息
285
285
  2. 识别用户需求(如"推广 Java 后端实习岗位"、"帮我发一下腾讯的产品经理职位")
286
286
  3. 使用 MySQL MCP 工具查询职位数据:
287
287
  - \`mcp__mysql__search_jobs\` — 按关键词/公司/类型/地点搜索
@@ -323,7 +323,7 @@ const COPYWRITER_PROMPT = `
323
323
 
324
324
  ### 工作流程
325
325
 
326
- 1. 监听 #content-publish 频道,等待包含【职位数据已就绪】标记的消息
326
+ 1. 监听 #content-publish 团队,等待包含【职位数据已就绪】标记的消息
327
327
  2. 仔细阅读职位详情,提炼核心卖点
328
328
  3. 撰写小红书风格图文(风格活泼、有吸引力、适合年轻求职者)
329
329
  4. 将文案发到频道,并 @designer 接力
@@ -380,7 +380,7 @@ const DESIGNER_PROMPT = `
380
380
 
381
381
  ### 工作流程
382
382
 
383
- 1. 监听 #content-publish 频道,等待包含【文案已就绪】标记的消息
383
+ 1. 监听 #content-publish 团队,等待包含【文案已就绪】标记的消息
384
384
  2. 提取文案中的核心信息和设计要点
385
385
  3. 编写 Node.js 代码,使用 canvas 或 Puppeteer(渲染 HTML)生成图片
386
386
  4. 执行代码,生成图片文件到工作目录
@@ -535,7 +535,7 @@ Use \`skill_read\` to load full content when needed.
535
535
  - Load a skill's full procedure: \`skill_read({ name: "skill-name" })\`
536
536
  - After completing a complex task (5+ tool calls), consider saving it as a skill: \`skill_create({ name, description, content })\`
537
537
  - When using a skill and finding it outdated or wrong, update it immediately: \`skill_update({ name, content })\`
538
- - Skills you create are automatically shared with other agents in the same channel
538
+ - Skills you create are automatically shared with other agents in the same team
539
539
 
540
540
  ### Available Skills
541
541
  `;
@@ -3,14 +3,14 @@ import { existsSync } from 'fs';
3
3
  import path from 'path';
4
4
  import { buildSystemPrompt as buildClaudeSystemPrompt } from './claude.js';
5
5
 
6
- function buildChatBridgeArgs(chatBridgePath, { agentId, channelId, serverUrl, authToken }) {
6
+ function buildChatBridgeArgs(chatBridgePath, { agentId, teamId, serverUrl, authToken }) {
7
7
  const args = [
8
8
  chatBridgePath,
9
9
  '--agent-id', agentId,
10
10
  '--server-url', serverUrl,
11
11
  '--auth-token', authToken,
12
12
  ];
13
- if (channelId) args.push('--channel-id', channelId);
13
+ if (teamId) args.push('--team-id', teamId);
14
14
  return args;
15
15
  }
16
16
 
@@ -62,7 +62,7 @@ export function buildCodexSystemPrompt(config, agentId) {
62
62
  }
63
63
 
64
64
  export function buildCodexSpawn({
65
- config, agentId, channelId, workspaceDir, chatBridgePath, serverUrl, machineApiKey, prompt, skills,
65
+ config, agentId, teamId, workspaceDir, chatBridgePath, serverUrl, machineApiKey, prompt, skills,
66
66
  }) {
67
67
  ensureGitRepo(workspaceDir);
68
68
 
@@ -73,7 +73,7 @@ export function buildCodexSpawn({
73
73
 
74
74
  const bridgeArgs = buildChatBridgeArgs(chatBridgePath, {
75
75
  agentId,
76
- channelId,
76
+ teamId,
77
77
  serverUrl,
78
78
  authToken: config.authToken || machineApiKey,
79
79
  });
@@ -12,7 +12,7 @@ const KIMI_MCP_FILE = '.lightcone-kimi-mcp.json';
12
12
  * Build Kimi CLI spawn args and config files.
13
13
  * Returns { args, env, setupFiles() } ready for spawn('kimi', args, { env }).
14
14
  */
15
- export function buildKimiSpawn({ config, agentId, channelId, workspaceDir, chatBridgePath, serverUrl, machineApiKey, skills }) {
15
+ export function buildKimiSpawn({ config, agentId, teamId, workspaceDir, chatBridgePath, serverUrl, machineApiKey, skills }) {
16
16
  const isResume = !!config.sessionId;
17
17
  const sessionId = config.sessionId || randomUUID();
18
18
 
@@ -46,7 +46,7 @@ export function buildKimiSpawn({ config, agentId, channelId, workspaceDir, chatB
46
46
  SERVER_URL: serverUrl,
47
47
  MACHINE_API_KEY: machineApiKey,
48
48
  AGENT_ID: agentId,
49
- CHANNEL_ID: channelId ?? '',
49
+ TEAM_ID: teamId ?? '',
50
50
  },
51
51
  },
52
52
  };