assistme 0.8.1 → 0.8.2

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.
@@ -59,9 +59,28 @@ async function callMcpHandler(action, params = {}, overrideToken) {
59
59
  // src/utils/logger.ts
60
60
  import chalk from "chalk";
61
61
  import { randomUUID } from "crypto";
62
+ import { readFileSync as readFileSync2 } from "fs";
63
+ import { join as join2, dirname } from "path";
64
+ import { fileURLToPath } from "url";
62
65
  var currentLevel = "info";
63
66
  var currentCorrelationId = null;
67
+ var currentConversationId = null;
64
68
  var logHook = null;
69
+ var logTransport = null;
70
+ var PKG_VERSION = (() => {
71
+ try {
72
+ const __dirname = dirname(fileURLToPath(import.meta.url));
73
+ for (const rel of ["../package.json", "../../package.json"]) {
74
+ try {
75
+ const pkg = JSON.parse(readFileSync2(join2(__dirname, rel), "utf-8"));
76
+ if (pkg.version) return pkg.version;
77
+ } catch {
78
+ }
79
+ }
80
+ } catch {
81
+ }
82
+ return "unknown";
83
+ })();
65
84
  var LEVEL_ORDER = {
66
85
  debug: 0,
67
86
  info: 1,
@@ -74,9 +93,15 @@ function setLogLevel(level) {
74
93
  function setLogHook(hook) {
75
94
  logHook = hook;
76
95
  }
96
+ function setLogTransport(transport) {
97
+ logTransport = transport;
98
+ }
77
99
  function setCorrelationId(id) {
78
100
  currentCorrelationId = id;
79
101
  }
102
+ function setLogConversationId(id) {
103
+ currentConversationId = id;
104
+ }
80
105
  function newCorrelationId() {
81
106
  const id = randomUUID().slice(0, 8);
82
107
  currentCorrelationId = id;
@@ -90,54 +115,87 @@ function timestamp() {
90
115
  }
91
116
  function prefix() {
92
117
  const ts = timestamp();
93
- return currentCorrelationId ? `${ts} ${currentCorrelationId}` : ts;
118
+ const base = `${ts} v${PKG_VERSION}`;
119
+ return currentCorrelationId ? `${base} ${currentCorrelationId}` : base;
94
120
  }
95
121
  var log = {
96
122
  debug(msg, ...args) {
97
123
  if (shouldLog("debug")) {
98
124
  const text = formatArgs(msg, args);
99
- logHook?.("stdout", `[DEBUG] ${text}`);
125
+ if (logTransport) {
126
+ logTransport("debug", text);
127
+ return;
128
+ }
129
+ logHook?.("stdout", `[DEBUG] ${text}`, currentConversationId);
100
130
  console.log(chalk.gray(`[${prefix()}] DEBUG`), msg, ...args);
101
131
  }
102
132
  },
103
133
  info(msg, ...args) {
104
134
  if (shouldLog("info")) {
105
135
  const text = formatArgs(msg, args);
106
- logHook?.("stdout", text);
136
+ if (logTransport) {
137
+ logTransport("info", text);
138
+ return;
139
+ }
140
+ logHook?.("stdout", text, currentConversationId);
107
141
  console.log(chalk.blue(`[${prefix()}]`), msg, ...args);
108
142
  }
109
143
  },
110
144
  success(msg, ...args) {
111
145
  if (shouldLog("info")) {
112
146
  const text = formatArgs(msg, args);
113
- logHook?.("stdout", `\u2713 ${text}`);
147
+ if (logTransport) {
148
+ logTransport("success", text);
149
+ return;
150
+ }
151
+ logHook?.("stdout", `\u2713 ${text}`, currentConversationId);
114
152
  console.log(chalk.green(`[${prefix()}] \u2713`), msg, ...args);
115
153
  }
116
154
  },
117
155
  warn(msg, ...args) {
118
156
  if (shouldLog("warn")) {
119
157
  const text = formatArgs(msg, args);
120
- logHook?.("stderr", `[WARN] ${text}`);
158
+ if (logTransport) {
159
+ logTransport("warn", text);
160
+ return;
161
+ }
162
+ logHook?.("stderr", `[WARN] ${text}`, currentConversationId);
121
163
  console.log(chalk.yellow(`[${prefix()}] WARN`), msg, ...args);
122
164
  }
123
165
  },
124
166
  error(msg, ...args) {
125
167
  if (shouldLog("error")) {
126
168
  const text = formatArgs(msg, args);
127
- logHook?.("stderr", `[ERROR] ${text}`);
169
+ if (logTransport) {
170
+ logTransport("error", text);
171
+ return;
172
+ }
173
+ logHook?.("stderr", `[ERROR] ${text}`, currentConversationId);
128
174
  console.error(chalk.red(`[${prefix()}] ERROR`), msg, ...args);
129
175
  }
130
176
  },
131
177
  agent(msg) {
132
- logHook?.("stdout", `\u25B8 ${msg}`);
178
+ if (logTransport) {
179
+ logTransport("agent", msg);
180
+ return;
181
+ }
182
+ logHook?.("stdout", `\u25B8 ${msg}`, currentConversationId);
133
183
  console.log(chalk.cyan(" \u25B8"), msg);
134
184
  },
135
185
  tool(name, msg) {
136
- logHook?.("stdout", `\u26A1 ${name}: ${msg}`);
186
+ if (logTransport) {
187
+ logTransport("tool", msg, name);
188
+ return;
189
+ }
190
+ logHook?.("stdout", `\u26A1 ${name}: ${msg}`, currentConversationId);
137
191
  console.log(chalk.magenta(` \u26A1 ${name}:`), msg);
138
192
  },
139
193
  result(msg) {
140
- logHook?.("stdout", `\u2190 ${msg}`);
194
+ if (logTransport) {
195
+ logTransport("result", msg);
196
+ return;
197
+ }
198
+ logHook?.("stdout", `\u2190 ${msg}`, currentConversationId);
141
199
  console.log(chalk.green(" \u2190"), msg);
142
200
  }
143
201
  };
@@ -480,7 +538,9 @@ export {
480
538
  callMcpHandler,
481
539
  setLogLevel,
482
540
  setLogHook,
541
+ setLogTransport,
483
542
  setCorrelationId,
543
+ setLogConversationId,
484
544
  newCorrelationId,
485
545
  log,
486
546
  MAX_RESPONSE_CONTENT_LENGTH,
@@ -19,7 +19,7 @@ import {
19
19
  readAuthStore,
20
20
  safeParse,
21
21
  writeAuthStore
22
- } from "./chunk-QHMIXIWO.js";
22
+ } from "./chunk-A2NR7LCQ.js";
23
23
  import {
24
24
  getConfig
25
25
  } from "./chunk-YYSJHZSO.js";
package/dist/index.js CHANGED
@@ -26,7 +26,7 @@ import {
26
26
  setSessionBusy,
27
27
  toggleScheduledTask,
28
28
  updateHeartbeat
29
- } from "./chunk-4RHN77BN.js";
29
+ } from "./chunk-IKYXC4RJ.js";
30
30
  import {
31
31
  HEARTBEAT_INTERVAL_MS,
32
32
  HEARTBEAT_LOG_MAX_ENTRIES,
@@ -36,9 +36,10 @@ import {
36
36
  callMcpHandler,
37
37
  errorMessage,
38
38
  log,
39
+ setLogConversationId,
39
40
  setLogHook,
40
41
  setLogLevel
41
- } from "./chunk-QHMIXIWO.js";
42
+ } from "./chunk-A2NR7LCQ.js";
42
43
  import {
43
44
  clearConfig,
44
45
  getConfig,
@@ -884,8 +885,6 @@ var WorkerManager = class {
884
885
  workers = /* @__PURE__ */ new Map();
885
886
  /** Maps conversation ID → worker ID for fast lookup. */
886
887
  conversationWorkers = /* @__PURE__ */ new Map();
887
- /** The singleton job-analyzer worker ID (if running). */
888
- jobAnalyzerWorkerId = null;
889
888
  userId;
890
889
  sessionId;
891
890
  running = true;
@@ -941,17 +940,6 @@ var WorkerManager = class {
941
940
  }
942
941
  await completionPromise;
943
942
  }
944
- /**
945
- * Ensure the job-analyzer worker is running.
946
- * The job-analyzer runs its own polling loop — no need to send tasks.
947
- */
948
- async ensureJobAnalyzer() {
949
- if (this.jobAnalyzerWorkerId && this.workers.has(this.jobAnalyzerWorkerId)) {
950
- return;
951
- }
952
- const worker = await this.spawnWorker("job-analyzer");
953
- this.jobAnalyzerWorkerId = worker.id;
954
- }
955
943
  /**
956
944
  * Graceful shutdown: stop all workers.
957
945
  */
@@ -968,7 +956,6 @@ var WorkerManager = class {
968
956
  this.taskCompletions.clear();
969
957
  this.workers.clear();
970
958
  this.conversationWorkers.clear();
971
- this.jobAnalyzerWorkerId = null;
972
959
  }
973
960
  /**
974
961
  * Get info about all active workers.
@@ -1075,11 +1062,6 @@ var WorkerManager = class {
1075
1062
  }
1076
1063
  break;
1077
1064
  }
1078
- case "job_analyzed":
1079
- log.info(
1080
- `[worker:${worker.id}] Job "${message.jobName}" analyzed, skills: ${message.skillsCreated.join(", ") || "(none)"}`
1081
- );
1082
- break;
1083
1065
  case "idle":
1084
1066
  this.workerBecameIdle(worker);
1085
1067
  break;
@@ -1098,6 +1080,38 @@ var WorkerManager = class {
1098
1080
  log.debug(`[worker:${worker.id}] ${message.message}`);
1099
1081
  }
1100
1082
  break;
1083
+ case "log_forward": {
1084
+ const convId = worker.conversationId || null;
1085
+ setLogConversationId(convId);
1086
+ const convTag = convId ? `[conv:${convId.slice(0, 8)}]` : `[worker:${worker.id}]`;
1087
+ switch (message.method) {
1088
+ case "agent":
1089
+ log.agent(`${convTag} ${message.message}`);
1090
+ break;
1091
+ case "tool":
1092
+ log.tool(message.extra || "unknown", `${convTag} ${message.message}`);
1093
+ break;
1094
+ case "result":
1095
+ log.result(`${convTag} ${message.message}`);
1096
+ break;
1097
+ case "success":
1098
+ log.success(`${convTag} ${message.message}`);
1099
+ break;
1100
+ case "error":
1101
+ log.error(`${convTag} ${message.message}`);
1102
+ break;
1103
+ case "warn":
1104
+ log.warn(`${convTag} ${message.message}`);
1105
+ break;
1106
+ case "info":
1107
+ log.info(`${convTag} ${message.message}`);
1108
+ break;
1109
+ default:
1110
+ log.debug(`${convTag} ${message.message}`);
1111
+ }
1112
+ setLogConversationId(null);
1113
+ break;
1114
+ }
1101
1115
  case "error":
1102
1116
  log.error(`[worker:${worker.id}] Error: ${message.error}`);
1103
1117
  break;
@@ -1165,9 +1179,6 @@ var WorkerManager = class {
1165
1179
  }
1166
1180
  worker.pendingTaskIds.clear();
1167
1181
  this.workers.delete(worker.id);
1168
- if (this.jobAnalyzerWorkerId === worker.id) {
1169
- this.jobAnalyzerWorkerId = null;
1170
- }
1171
1182
  }
1172
1183
  async stopWorker(worker, timeoutMs) {
1173
1184
  if (worker.status === "stopped") return;
@@ -1214,6 +1225,7 @@ var WorkerManager = class {
1214
1225
  var DEFAULT_HEARTBEAT_INTERVAL = 3e4;
1215
1226
  var DEFAULT_POLL_INTERVAL = 2e3;
1216
1227
  var MAX_POLL_INTERVAL = 3e4;
1228
+ var JOB_ANALYSIS_POLL_INTERVAL = 3e4;
1217
1229
  var Orchestrator = class {
1218
1230
  session = null;
1219
1231
  heartbeatTimer = null;
@@ -1227,6 +1239,9 @@ var Orchestrator = class {
1227
1239
  consecutivePollFailures = 0;
1228
1240
  heartbeatInterval;
1229
1241
  basePollInterval;
1242
+ jobAnalysisTimer = null;
1243
+ /** Track jobs currently being analyzed to avoid duplicate tasks. */
1244
+ analyzingJobIds = /* @__PURE__ */ new Set();
1230
1245
  constructor() {
1231
1246
  this.scheduler = new Scheduler();
1232
1247
  this.heartbeatEngine = new HeartbeatEngine();
@@ -1261,9 +1276,7 @@ var Orchestrator = class {
1261
1276
  this.heartbeatEngine.start(async (prompt) => {
1262
1277
  await this.executeHeartbeatAction(prompt);
1263
1278
  });
1264
- this.workerManager.ensureJobAnalyzer().catch((err) => {
1265
- log.debug(`Job analyzer startup deferred: ${err}`);
1266
- });
1279
+ this.startJobAnalysisPoll();
1267
1280
  this.cleanupStaleSessions().catch(() => {
1268
1281
  });
1269
1282
  return this.session;
@@ -1369,6 +1382,94 @@ var Orchestrator = class {
1369
1382
  }
1370
1383
  }
1371
1384
  }
1385
+ // ── Job Analysis (as regular tasks) ─────────────────────────────
1386
+ startJobAnalysisPoll() {
1387
+ setTimeout(() => this.pollForUnanalyzedJobs(), 5e3);
1388
+ this.jobAnalysisTimer = setInterval(() => {
1389
+ this.pollForUnanalyzedJobs();
1390
+ }, JOB_ANALYSIS_POLL_INTERVAL);
1391
+ }
1392
+ async pollForUnanalyzedJobs() {
1393
+ if (!this.running || !this.session || !this.conversationId) return;
1394
+ try {
1395
+ const jobs = await callMcpHandler(
1396
+ "job.list_unanalyzed"
1397
+ );
1398
+ if (!jobs || jobs.length === 0) return;
1399
+ for (const job of jobs) {
1400
+ if (this.analyzingJobIds.has(job.id)) continue;
1401
+ this.analyzingJobIds.add(job.id);
1402
+ log.info(`Dispatching job analysis task for "${job.name}"`);
1403
+ const runner = new JobRunner();
1404
+ const jobInfo = await runner.loadJob(job.name);
1405
+ if (!jobInfo) {
1406
+ log.debug(`Job "${job.name}" not found, skipping analysis`);
1407
+ await this.markJobAnalyzed(job.id);
1408
+ this.analyzingJobIds.delete(job.id);
1409
+ continue;
1410
+ }
1411
+ const prompt = this.buildJobAnalysisPrompt(jobInfo);
1412
+ await this.markJobAnalyzed(job.id);
1413
+ this.dispatchTask(`[JobAnalysis: ${job.name}] ${prompt}`).then(() => {
1414
+ log.info(`Job "${job.name}" analysis task dispatched`);
1415
+ }).catch((err) => {
1416
+ log.debug(`Job analysis dispatch failed for "${job.name}": ${err}`);
1417
+ }).finally(() => {
1418
+ this.analyzingJobIds.delete(job.id);
1419
+ });
1420
+ }
1421
+ } catch (err) {
1422
+ log.debug(`Job analysis poll error: ${err}`);
1423
+ }
1424
+ }
1425
+ buildJobAnalysisPrompt(job) {
1426
+ let prompt = `Analyze this job definition and determine what skills are needed to execute it effectively.
1427
+
1428
+ `;
1429
+ prompt += `## Job Definition
1430
+ `;
1431
+ prompt += `**Name:** ${job.jobName}
1432
+ `;
1433
+ prompt += `**Description:** ${job.jobDescription}
1434
+
1435
+ `;
1436
+ if (job.skills.length > 0) {
1437
+ prompt += `**Current Skills:**
1438
+ `;
1439
+ for (const skill of job.skills) {
1440
+ const emoji = skill.skillEmoji ? `${skill.skillEmoji} ` : "";
1441
+ prompt += `- ${emoji}${skill.skillName}: ${skill.skillDescription}
1442
+ `;
1443
+ }
1444
+ prompt += `
1445
+ `;
1446
+ }
1447
+ prompt += `## Instructions
1448
+ `;
1449
+ prompt += `For each capability the job needs:
1450
+ `;
1451
+ prompt += `1. Check if an existing skill covers it (use \`skill_search\` to check)
1452
+ `;
1453
+ prompt += `2. If no existing skill covers it, create a new one using \`skill_create\`
1454
+ `;
1455
+ prompt += `3. If an existing skill needs improvement, update it using \`skill_improve\`
1456
+ `;
1457
+ prompt += `4. Link all relevant skills to the job using \`skill_link_job\`
1458
+
1459
+ `;
1460
+ prompt += `Be practical \u2014 only create skills that would genuinely help automate this job.
1461
+ `;
1462
+ prompt += `When done, summarize what skills were created, updated, or already existed.
1463
+ `;
1464
+ return prompt;
1465
+ }
1466
+ async markJobAnalyzed(jobId) {
1467
+ try {
1468
+ await callMcpHandler("job.mark_analyzed", { job_id: jobId });
1469
+ } catch (err) {
1470
+ log.debug(`Failed to mark job analyzed: ${err}`);
1471
+ }
1472
+ }
1372
1473
  // ── Scheduled & Heartbeat Tasks ─────────────────────────────────
1373
1474
  async executeScheduledTask(scheduledTask) {
1374
1475
  if (!this.session || !this.userId || !this.conversationId) return;
@@ -1446,6 +1547,10 @@ var Orchestrator = class {
1446
1547
  clearTimeout(this.pollTimer);
1447
1548
  this.pollTimer = null;
1448
1549
  }
1550
+ if (this.jobAnalysisTimer) {
1551
+ clearInterval(this.jobAnalysisTimer);
1552
+ this.jobAnalysisTimer = null;
1553
+ }
1449
1554
  if (this.workerManager) {
1450
1555
  await this.workerManager.shutdown(timeoutMs);
1451
1556
  }
@@ -1496,9 +1601,9 @@ var SessionLogEmitter = class {
1496
1601
  flushTimer = null;
1497
1602
  flushing = false;
1498
1603
  /** Queue a log entry for batch insertion */
1499
- push(logType, message) {
1604
+ push(logType, message, conversationId) {
1500
1605
  this.sequence++;
1501
- this.buffer.push({ log_type: logType, message, seq: this.sequence });
1606
+ this.buffer.push({ log_type: logType, message, seq: this.sequence, conversation_id: conversationId || null });
1502
1607
  if (this.buffer.length >= MAX_BATCH_SIZE) {
1503
1608
  this.flush();
1504
1609
  }
@@ -1570,17 +1675,15 @@ function registerMonitorCommands(program2) {
1570
1675
  }
1571
1676
  const status = sharedHeartbeat.getStatus();
1572
1677
  console.log(chalk4.bold("\nHeartbeat Status:"));
1678
+ console.log(` Enabled: ${status.enabled ? chalk4.green("yes") : chalk4.red("no")}`);
1573
1679
  console.log(
1574
- ` Enabled: ${status.enabled ? chalk4.green("yes") : chalk4.red("no")}`
1680
+ ` Interval: ${status.intervalMs / 1e3}s (${status.intervalMs / 6e4} min)`
1575
1681
  );
1576
- console.log(` Interval: ${status.intervalMs / 1e3}s (${status.intervalMs / 6e4} min)`);
1577
1682
  console.log(` Cycles: ${status.cycleCount}`);
1578
1683
  console.log(
1579
1684
  ` Checklist: ${status.hasChecklist ? chalk4.green("found") : chalk4.yellow("no HEARTBEAT.md")}`
1580
1685
  );
1581
- console.log(
1582
- ` Executing: ${status.executing ? chalk4.yellow("running now") : "idle"}`
1583
- );
1686
+ console.log(` Executing: ${status.executing ? chalk4.yellow("running now") : "idle"}`);
1584
1687
  console.log();
1585
1688
  } catch (err) {
1586
1689
  log.error(`${err instanceof Error ? err.message : err}`);
@@ -1765,11 +1868,11 @@ async function runAgent(opts) {
1765
1868
  const session = await orchestrator.start(userId);
1766
1869
  setSharedMonitor(orchestrator.getHeartbeatEngine());
1767
1870
  logEmitter = new SessionLogEmitter(session.id);
1768
- setLogHook((logType, message) => {
1769
- logEmitter?.push(logType, message);
1871
+ setLogHook((logType, message, conversationId) => {
1872
+ logEmitter?.push(logType, message, conversationId);
1770
1873
  });
1771
1874
  log.info("Listening for tasks (chat + jobs) from web UI...");
1772
- log.info(`Workers: conversation (per-conversation), job-analyzer (singleton)`);
1875
+ log.info(`Workers: conversation (per-conversation)`);
1773
1876
  log.info("Press Ctrl+C to stop.\n");
1774
1877
  const rl = createInterface2({
1775
1878
  input: process.stdin,
@@ -2186,7 +2289,7 @@ function registerJobCommands(program2) {
2186
2289
  jobCmd.command("list").description("List your defined jobs").action(async () => {
2187
2290
  try {
2188
2291
  const userId = await getCurrentUserId();
2189
- const { JobRunner: JobRunner2 } = await import("./job-runner-YM2NBIL3.js");
2292
+ const { JobRunner: JobRunner2 } = await import("./job-runner-PECVS424.js");
2190
2293
  const runner = new JobRunner2();
2191
2294
  const jobs = await runner.listJobs();
2192
2295
  if (jobs.length === 0) {
@@ -2210,7 +2313,7 @@ function registerJobCommands(program2) {
2210
2313
  jobCmd.command("status [name]").description("Show run history for a job (or all jobs)").option("-l, --limit <number>", "Max runs to show (default: 5)").action(async (name, opts) => {
2211
2314
  try {
2212
2315
  const userId = await getCurrentUserId();
2213
- const { JobRunner: JobRunner2 } = await import("./job-runner-YM2NBIL3.js");
2316
+ const { JobRunner: JobRunner2 } = await import("./job-runner-PECVS424.js");
2214
2317
  const runner = new JobRunner2();
2215
2318
  const runs = await runner.getRunHistory(name, parseInt(opts.limit || "5"));
2216
2319
  if (runs.length === 0) {
@@ -2249,7 +2352,7 @@ Job Run History${name ? ` \u2014 ${name}` : ""}:`));
2249
2352
  process.exit(1);
2250
2353
  }
2251
2354
  const userId = await getCurrentUserId();
2252
- const { JobRunner: JobRunner2 } = await import("./job-runner-YM2NBIL3.js");
2355
+ const { JobRunner: JobRunner2 } = await import("./job-runner-PECVS424.js");
2253
2356
  const runner = new JobRunner2();
2254
2357
  const job = await runner.loadJob(name);
2255
2358
  if (!job) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  JobRunner
3
- } from "./chunk-QHMIXIWO.js";
3
+ } from "./chunk-A2NR7LCQ.js";
4
4
  import "./chunk-YYSJHZSO.js";
5
5
  export {
6
6
  JobRunner