cc-claw 0.13.2 → 0.14.0

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/dist/cli.js CHANGED
@@ -72,7 +72,7 @@ var VERSION;
72
72
  var init_version = __esm({
73
73
  "src/version.ts"() {
74
74
  "use strict";
75
- VERSION = true ? "0.13.2" : (() => {
75
+ VERSION = true ? "0.14.0" : (() => {
76
76
  try {
77
77
  return JSON.parse(readFileSync(join2(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
78
78
  } catch {
@@ -5126,8 +5126,24 @@ ${instructions}`;
5126
5126
  }
5127
5127
  return null;
5128
5128
  }
5129
+ const MEMORY_DISCIPLINE = `
5130
+ [Memory discipline]
5131
+ Use cc_claw_memory for ALL memory operations. Do NOT use your native memory system (no .claude/projects/*/memory/, no .gemini/, etc.).
5132
+ - To save: cc_claw_memory(action: "remember", tag: "...", content: "...")
5133
+ - To search: cc_claw_memory(action: "recall", query: "...")
5134
+ - To list: cc_claw_memory(action: "list")
5135
+ - To search history: cc_claw_memory(action: "history", query: "...")
5136
+ For scheduling: cc_claw_schedule(action: "create", schedule: "...", task: "...")
5137
+ If an action is not available via MCP tools, fall back to the cc-claw CLI.`;
5129
5138
  if (agentMode === "claw") {
5130
- return "[Sub-agent capabilities]\nCC-Claw orchestration is active. Use the cc-claw MCP tools (spawn_agent, send_message, list_tasks, etc.) for cross-backend agent coordination, inter-agent communication, and shared task management.";
5139
+ return `[Sub-agent capabilities]
5140
+ CC-Claw orchestration is active. Use the unified domain tools:
5141
+ - cc_claw_agents \u2014 spawn/cancel/list/status sub-agents, list templates/runners/mcps
5142
+ - cc_claw_tasks \u2014 create/list/update shared tasks
5143
+ - cc_claw_comms \u2014 send/read messages, broadcast, shared whiteboard
5144
+ - cc_claw_memory \u2014 remember/recall/list/forget/history (cross-backend persistent memory)
5145
+ - cc_claw_schedule \u2014 create/list/edit/run/cancel/pause/resume scheduled jobs
5146
+ ${MEMORY_DISCIPLINE}`;
5131
5147
  }
5132
5148
  if (agentMode === "auto" && process.env.DASHBOARD_ENABLED === "1") {
5133
5149
  try {
@@ -5135,10 +5151,10 @@ ${instructions}`;
5135
5151
  const nativeInstructions = adapter.getSubagentInstructions?.();
5136
5152
  const parts = [];
5137
5153
  if (nativeInstructions) parts.push(`Native: ${nativeInstructions}`);
5138
- parts.push("CC-Claw: For cross-backend coordination, inter-agent debate, or shared task management, use the cc-claw MCP tools (spawn_agent, send_message, etc.).");
5154
+ parts.push(`CC-Claw: For cross-backend coordination, memory, scheduling, or shared task management, use the cc_claw_* MCP tools (cc_claw_agents, cc_claw_memory, cc_claw_schedule, etc.).`);
5139
5155
  return `[Sub-agent capabilities]
5140
5156
  You have two sub-agent modes:
5141
- ${parts.join("\n")}`;
5157
+ ${parts.join("\n")}${MEMORY_DISCIPLINE}`;
5142
5158
  } catch {
5143
5159
  }
5144
5160
  }
@@ -7198,6 +7214,91 @@ var init_health = __esm({
7198
7214
  }
7199
7215
  });
7200
7216
 
7217
+ // src/scheduler/health.ts
7218
+ var health_exports2 = {};
7219
+ __export(health_exports2, {
7220
+ computeStaggerMs: () => computeStaggerMs,
7221
+ formatHealthReport: () => formatHealthReport,
7222
+ getHealthReport: () => getHealthReport,
7223
+ startHealthMonitor: () => startHealthMonitor2,
7224
+ stopHealthMonitor: () => stopHealthMonitor2
7225
+ });
7226
+ import * as os from "os";
7227
+ function startHealthMonitor2() {
7228
+ lastHeartbeat = /* @__PURE__ */ new Date();
7229
+ heartbeatTimer = setInterval(() => {
7230
+ lastHeartbeat = /* @__PURE__ */ new Date();
7231
+ log(`[health] Scheduler heartbeat at ${lastHeartbeat.toISOString()}`);
7232
+ }, HEARTBEAT_INTERVAL_MS);
7233
+ heartbeatTimer.unref();
7234
+ }
7235
+ function stopHealthMonitor2() {
7236
+ if (heartbeatTimer) {
7237
+ clearInterval(heartbeatTimer);
7238
+ heartbeatTimer = null;
7239
+ }
7240
+ }
7241
+ function getHealthReport() {
7242
+ const allJobs = getAllJobs();
7243
+ const activeJobs = allJobs.filter((j) => j.enabled && j.active);
7244
+ const pausedJobs = allJobs.filter((j) => !j.enabled && j.active);
7245
+ const failingJobs = activeJobs.filter((j) => j.consecutiveFailures > 0).map((j) => ({ id: j.id, description: j.description, failures: j.consecutiveFailures }));
7246
+ let status = "healthy";
7247
+ if (failingJobs.length > 0) status = "degraded";
7248
+ if (!lastHeartbeat) status = "unhealthy";
7249
+ return {
7250
+ status,
7251
+ lastHeartbeat: lastHeartbeat?.toISOString() ?? null,
7252
+ activeJobs: activeJobs.length,
7253
+ pausedJobs: pausedJobs.length,
7254
+ failingJobs,
7255
+ totalJobs: allJobs.length,
7256
+ // Audit O39: Include system metrics for operator visibility
7257
+ memoryMB: Math.round(process.memoryUsage().heapUsed / 1024 / 1024),
7258
+ loadAvg: os.loadavg()[0]
7259
+ };
7260
+ }
7261
+ function formatHealthReport(report) {
7262
+ const statusEmoji = report.status === "healthy" ? "OK" : report.status === "degraded" ? "DEGRADED" : "UNHEALTHY";
7263
+ const lines = [
7264
+ `Scheduler: ${statusEmoji}`,
7265
+ `Last heartbeat: ${report.lastHeartbeat ?? "never"}`,
7266
+ `Active jobs: ${report.activeJobs}`,
7267
+ `Paused jobs: ${report.pausedJobs}`,
7268
+ `Total jobs: ${report.totalJobs}`
7269
+ ];
7270
+ if (report.failingJobs.length > 0) {
7271
+ lines.push("", "Jobs with failures:");
7272
+ for (const j of report.failingJobs) {
7273
+ lines.push(` #${j.id}: ${j.description} (${j.failures} consecutive failures)`);
7274
+ }
7275
+ }
7276
+ return lines.join("\n");
7277
+ }
7278
+ function computeStaggerMs(jobId, cronExpr) {
7279
+ const parts = cronExpr.split(/\s+/);
7280
+ if (parts.length < 5) return 0;
7281
+ if (parts[0] !== "0") return 0;
7282
+ if (!/[*/]/.test(parts[1])) return 0;
7283
+ const maxStagger = 12e4;
7284
+ let h = jobId * 2654435761;
7285
+ h = (h >>> 16 ^ h) * 73244475;
7286
+ h = (h >>> 16 ^ h) * 73244475;
7287
+ h = h >>> 16 ^ h;
7288
+ return Math.abs(h) % maxStagger;
7289
+ }
7290
+ var lastHeartbeat, heartbeatTimer, HEARTBEAT_INTERVAL_MS;
7291
+ var init_health2 = __esm({
7292
+ "src/scheduler/health.ts"() {
7293
+ "use strict";
7294
+ init_store5();
7295
+ init_log();
7296
+ lastHeartbeat = null;
7297
+ heartbeatTimer = null;
7298
+ HEARTBEAT_INTERVAL_MS = 5 * 60 * 1e3;
7299
+ }
7300
+ });
7301
+
7201
7302
  // src/reflection/metrics.ts
7202
7303
  var metrics_exports = {};
7203
7304
  __export(metrics_exports, {
@@ -8298,7 +8399,15 @@ function startDashboard() {
8298
8399
  "/api/orchestrator/get-state",
8299
8400
  "/api/orchestrator/list-state",
8300
8401
  "/api/orchestrator/check-agent",
8301
- "/api/health"
8402
+ "/api/health",
8403
+ // Memory tools — accessible to all agents
8404
+ "/api/memory/remember",
8405
+ "/api/memory/add",
8406
+ // deprecated alias for /remember
8407
+ "/api/memory/recall",
8408
+ "/api/memory/list",
8409
+ "/api/memory/forget",
8410
+ "/api/memory/history"
8302
8411
  ]);
8303
8412
  if (isSubAgentToken && !SUB_AGENT_ALLOWED_PATHS.has(url.pathname)) {
8304
8413
  return jsonResponse(res, { error: "Forbidden: sub-agent tokens can only access designated endpoints" }, 403);
@@ -8744,22 +8853,173 @@ data: ${JSON.stringify(data)}
8744
8853
  return jsonResponse(res, { error: errorMessage(err) }, 400);
8745
8854
  }
8746
8855
  }
8747
- if (url.pathname === "/api/memory/add" && req.method === "POST") {
8856
+ if ((url.pathname === "/api/memory/remember" || url.pathname === "/api/memory/add") && req.method === "POST") {
8748
8857
  try {
8749
8858
  const body = JSON.parse(await readBody(req));
8859
+ validateAgentIdentity(req, body);
8750
8860
  const { saveMemoryWithEmbedding: saveMemoryWithEmbedding2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
8751
- const id = saveMemoryWithEmbedding2(body.trigger, body.content, body.type ?? "semantic");
8861
+ const id = saveMemoryWithEmbedding2(body.trigger ?? body.tag, body.content, body.type ?? "semantic");
8752
8862
  return jsonResponse(res, { success: true, id });
8753
8863
  } catch (err) {
8754
8864
  return jsonResponse(res, { error: errorMessage(err) }, 400);
8755
8865
  }
8756
8866
  }
8867
+ if (url.pathname === "/api/memory/recall" && req.method === "POST") {
8868
+ try {
8869
+ const body = JSON.parse(await readBody(req));
8870
+ validateAgentIdentity(req, body);
8871
+ const { searchMemories: searchMemories2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
8872
+ const results = searchMemories2(body.query, body.limit ?? 5);
8873
+ return jsonResponse(res, { success: true, results });
8874
+ } catch (err) {
8875
+ return jsonResponse(res, { error: errorMessage(err) }, 400);
8876
+ }
8877
+ }
8878
+ if (url.pathname === "/api/memory/list" && req.method === "GET") {
8879
+ try {
8880
+ const limit = parseInt(url.searchParams.get("limit") ?? "10", 10);
8881
+ const { getRecentMemories: getRecentMemories2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
8882
+ const results = getRecentMemories2(limit);
8883
+ return jsonResponse(res, { success: true, results });
8884
+ } catch (err) {
8885
+ return jsonResponse(res, { error: errorMessage(err) }, 400);
8886
+ }
8887
+ }
8757
8888
  if (url.pathname === "/api/memory/forget" && req.method === "POST") {
8758
8889
  try {
8759
8890
  const body = JSON.parse(await readBody(req));
8760
- const { forgetMemory: forgetMemory2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
8761
- const count = forgetMemory2(body.keyword);
8762
- return jsonResponse(res, { success: true, count });
8891
+ validateAgentIdentity(req, body);
8892
+ if (body.memoryId) {
8893
+ const { deleteMemoryById: deleteMemoryById2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
8894
+ const deleted = deleteMemoryById2(body.memoryId);
8895
+ return jsonResponse(res, { success: deleted, mode: "id" });
8896
+ }
8897
+ if (body.keyword) {
8898
+ const { forgetMemory: forgetMemory2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
8899
+ const count = forgetMemory2(body.keyword);
8900
+ return jsonResponse(res, { success: true, count, mode: "keyword" });
8901
+ }
8902
+ return jsonResponse(res, { error: "Either 'keyword' or 'memoryId' is required" }, 400);
8903
+ } catch (err) {
8904
+ return jsonResponse(res, { error: errorMessage(err) }, 400);
8905
+ }
8906
+ }
8907
+ if (url.pathname === "/api/memory/history" && req.method === "POST") {
8908
+ try {
8909
+ const body = JSON.parse(await readBody(req));
8910
+ validateAgentIdentity(req, body);
8911
+ const { searchMessageLog: searchMessageLog2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
8912
+ const results = searchMessageLog2(body.chatId, body.query, body.limit ?? 20);
8913
+ return jsonResponse(res, { success: true, results });
8914
+ } catch (err) {
8915
+ return jsonResponse(res, { error: errorMessage(err) }, 400);
8916
+ }
8917
+ }
8918
+ if (url.pathname === "/api/schedule/create" && req.method === "POST") {
8919
+ try {
8920
+ const body = JSON.parse(await readBody(req));
8921
+ validateAgentIdentity(req, body);
8922
+ const { insertJob: insertJob2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
8923
+ const { startSingleJob: startSingleJob2 } = await Promise.resolve().then(() => (init_cron(), cron_exports));
8924
+ const job = insertJob2({
8925
+ scheduleType: body.scheduleType ?? "cron",
8926
+ cron: body.cron ?? body.schedule ?? null,
8927
+ atTime: body.atTime ?? null,
8928
+ everyMs: body.everyMs ?? null,
8929
+ title: body.title ?? body.name ?? null,
8930
+ description: body.task ?? body.description,
8931
+ chatId: body.chatId,
8932
+ backend: body.backend ?? null,
8933
+ model: body.model ?? null,
8934
+ thinking: body.thinking ?? null,
8935
+ timeout: body.timeout ?? null,
8936
+ sessionType: body.sessionType ?? "isolated",
8937
+ deliveryMode: body.deliveryMode ?? "announce",
8938
+ timezone: body.timezone ?? "UTC"
8939
+ });
8940
+ startSingleJob2(job);
8941
+ return jsonResponse(res, { success: true, job });
8942
+ } catch (err) {
8943
+ return jsonResponse(res, { error: errorMessage(err) }, 400);
8944
+ }
8945
+ }
8946
+ if (url.pathname === "/api/schedule/list" && req.method === "GET") {
8947
+ try {
8948
+ const { listJobs: listJobs2 } = await Promise.resolve().then(() => (init_cron(), cron_exports));
8949
+ const jobs = listJobs2();
8950
+ return jsonResponse(res, { success: true, jobs });
8951
+ } catch (err) {
8952
+ return jsonResponse(res, { error: errorMessage(err) }, 400);
8953
+ }
8954
+ }
8955
+ if (url.pathname === "/api/schedule/edit" && req.method === "POST") {
8956
+ try {
8957
+ const body = JSON.parse(await readBody(req));
8958
+ validateAgentIdentity(req, body);
8959
+ const { updateJob: updateJob2, getJobById: getJobById2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
8960
+ const { stopJobTimer: stopJobTimer2, startSingleJob: startSingleJob2 } = await Promise.resolve().then(() => (init_cron(), cron_exports));
8961
+ const updated = updateJob2(body.jobId, body.updates);
8962
+ if (updated) {
8963
+ stopJobTimer2(body.jobId);
8964
+ const freshJob = getJobById2(body.jobId);
8965
+ if (freshJob && freshJob.active && freshJob.enabled) {
8966
+ startSingleJob2(freshJob);
8967
+ }
8968
+ }
8969
+ return jsonResponse(res, { success: updated });
8970
+ } catch (err) {
8971
+ return jsonResponse(res, { error: errorMessage(err) }, 400);
8972
+ }
8973
+ }
8974
+ if (url.pathname === "/api/schedule/run" && req.method === "POST") {
8975
+ try {
8976
+ const body = JSON.parse(await readBody(req));
8977
+ validateAgentIdentity(req, body);
8978
+ const { triggerJob: triggerJob2 } = await Promise.resolve().then(() => (init_cron(), cron_exports));
8979
+ const result = await triggerJob2(body.jobId);
8980
+ return jsonResponse(res, { success: true, message: result });
8981
+ } catch (err) {
8982
+ return jsonResponse(res, { error: errorMessage(err) }, 400);
8983
+ }
8984
+ }
8985
+ if (url.pathname === "/api/schedule/cancel" && req.method === "POST") {
8986
+ try {
8987
+ const body = JSON.parse(await readBody(req));
8988
+ validateAgentIdentity(req, body);
8989
+ const { cancelJob: cancelJob2 } = await Promise.resolve().then(() => (init_cron(), cron_exports));
8990
+ const cancelled = cancelJob2(body.jobId);
8991
+ return jsonResponse(res, { success: cancelled });
8992
+ } catch (err) {
8993
+ return jsonResponse(res, { error: errorMessage(err) }, 400);
8994
+ }
8995
+ }
8996
+ if (url.pathname === "/api/schedule/pause" && req.method === "POST") {
8997
+ try {
8998
+ const body = JSON.parse(await readBody(req));
8999
+ validateAgentIdentity(req, body);
9000
+ const { pauseJob: pauseJob2 } = await Promise.resolve().then(() => (init_cron(), cron_exports));
9001
+ const paused = pauseJob2(body.jobId);
9002
+ return jsonResponse(res, { success: paused });
9003
+ } catch (err) {
9004
+ return jsonResponse(res, { error: errorMessage(err) }, 400);
9005
+ }
9006
+ }
9007
+ if (url.pathname === "/api/schedule/resume" && req.method === "POST") {
9008
+ try {
9009
+ const body = JSON.parse(await readBody(req));
9010
+ validateAgentIdentity(req, body);
9011
+ const { resumeJob: resumeJob2 } = await Promise.resolve().then(() => (init_cron(), cron_exports));
9012
+ const resumed = resumeJob2(body.jobId);
9013
+ return jsonResponse(res, { success: resumed });
9014
+ } catch (err) {
9015
+ return jsonResponse(res, { error: errorMessage(err) }, 400);
9016
+ }
9017
+ }
9018
+ if (url.pathname === "/api/schedule/status" && req.method === "GET") {
9019
+ try {
9020
+ const { getHealthReport: getHealthReport2, formatHealthReport: formatHealthReport2 } = await Promise.resolve().then(() => (init_health2(), health_exports2));
9021
+ const report = getHealthReport2();
9022
+ return jsonResponse(res, { success: true, report, formatted: formatHealthReport2(report) });
8763
9023
  } catch (err) {
8764
9024
  return jsonResponse(res, { error: errorMessage(err) }, 400);
8765
9025
  }
@@ -10495,83 +10755,6 @@ var init_retry = __esm({
10495
10755
  }
10496
10756
  });
10497
10757
 
10498
- // src/scheduler/health.ts
10499
- import * as os from "os";
10500
- function startHealthMonitor2() {
10501
- lastHeartbeat = /* @__PURE__ */ new Date();
10502
- heartbeatTimer = setInterval(() => {
10503
- lastHeartbeat = /* @__PURE__ */ new Date();
10504
- log(`[health] Scheduler heartbeat at ${lastHeartbeat.toISOString()}`);
10505
- }, HEARTBEAT_INTERVAL_MS);
10506
- heartbeatTimer.unref();
10507
- }
10508
- function stopHealthMonitor2() {
10509
- if (heartbeatTimer) {
10510
- clearInterval(heartbeatTimer);
10511
- heartbeatTimer = null;
10512
- }
10513
- }
10514
- function getHealthReport() {
10515
- const allJobs = getAllJobs();
10516
- const activeJobs = allJobs.filter((j) => j.enabled && j.active);
10517
- const pausedJobs = allJobs.filter((j) => !j.enabled && j.active);
10518
- const failingJobs = activeJobs.filter((j) => j.consecutiveFailures > 0).map((j) => ({ id: j.id, description: j.description, failures: j.consecutiveFailures }));
10519
- let status = "healthy";
10520
- if (failingJobs.length > 0) status = "degraded";
10521
- if (!lastHeartbeat) status = "unhealthy";
10522
- return {
10523
- status,
10524
- lastHeartbeat: lastHeartbeat?.toISOString() ?? null,
10525
- activeJobs: activeJobs.length,
10526
- pausedJobs: pausedJobs.length,
10527
- failingJobs,
10528
- totalJobs: allJobs.length,
10529
- // Audit O39: Include system metrics for operator visibility
10530
- memoryMB: Math.round(process.memoryUsage().heapUsed / 1024 / 1024),
10531
- loadAvg: os.loadavg()[0]
10532
- };
10533
- }
10534
- function formatHealthReport(report) {
10535
- const statusEmoji = report.status === "healthy" ? "OK" : report.status === "degraded" ? "DEGRADED" : "UNHEALTHY";
10536
- const lines = [
10537
- `Scheduler: ${statusEmoji}`,
10538
- `Last heartbeat: ${report.lastHeartbeat ?? "never"}`,
10539
- `Active jobs: ${report.activeJobs}`,
10540
- `Paused jobs: ${report.pausedJobs}`,
10541
- `Total jobs: ${report.totalJobs}`
10542
- ];
10543
- if (report.failingJobs.length > 0) {
10544
- lines.push("", "Jobs with failures:");
10545
- for (const j of report.failingJobs) {
10546
- lines.push(` #${j.id}: ${j.description} (${j.failures} consecutive failures)`);
10547
- }
10548
- }
10549
- return lines.join("\n");
10550
- }
10551
- function computeStaggerMs(jobId, cronExpr) {
10552
- const parts = cronExpr.split(/\s+/);
10553
- if (parts.length < 5) return 0;
10554
- if (parts[0] !== "0") return 0;
10555
- if (!/[*/]/.test(parts[1])) return 0;
10556
- const maxStagger = 12e4;
10557
- let h = jobId * 2654435761;
10558
- h = (h >>> 16 ^ h) * 73244475;
10559
- h = (h >>> 16 ^ h) * 73244475;
10560
- h = h >>> 16 ^ h;
10561
- return Math.abs(h) % maxStagger;
10562
- }
10563
- var lastHeartbeat, heartbeatTimer, HEARTBEAT_INTERVAL_MS;
10564
- var init_health2 = __esm({
10565
- "src/scheduler/health.ts"() {
10566
- "use strict";
10567
- init_store5();
10568
- init_log();
10569
- lastHeartbeat = null;
10570
- heartbeatTimer = null;
10571
- HEARTBEAT_INTERVAL_MS = 5 * 60 * 1e3;
10572
- }
10573
- });
10574
-
10575
10758
  // src/reflection/propose.ts
10576
10759
  var propose_exports = {};
10577
10760
  __export(propose_exports, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-claw",
3
- "version": "0.13.2",
3
+ "version": "0.14.0",
4
4
  "description": "CC-Claw: Personal AI assistant on Telegram — multi-backend (Claude, Gemini, Codex, Cursor), sub-agent orchestration, MCP management",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",