@mclean-capital/neura 3.5.2 → 3.5.3

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.
@@ -73035,7 +73035,9 @@ function loadNeuraSkills(options = {}) {
73035
73035
  const repoLocal = resolve(cwd, ".neura", "skills");
73036
73036
  const global2 = options.globalSkillsDir ?? resolve(homedir2(), ".neura", "skills");
73037
73037
  const explicit = options.explicitPaths ?? [];
73038
- const skillPaths = [repoLocal, global2, ...explicit];
73038
+ const skillPaths = [repoLocal, global2];
73039
+ if (options.bundledSkillsDir) skillPaths.push(options.bundledSkillsDir);
73040
+ skillPaths.push(...explicit);
73039
73041
  log12.info("loading Neura skills (repo-local \u2192 global \u2192 explicit, pi defaults excluded)", {
73040
73042
  cwd,
73041
73043
  skillPaths
@@ -73142,6 +73144,7 @@ var SkillWatcher = class {
73142
73144
  registry;
73143
73145
  cwd;
73144
73146
  globalSkillsDir;
73147
+ bundledSkillsDir;
73145
73148
  explicitPaths;
73146
73149
  debounceMs;
73147
73150
  onReload;
@@ -73153,6 +73156,7 @@ var SkillWatcher = class {
73153
73156
  this.registry = options.registry;
73154
73157
  this.cwd = options.cwd ?? process.cwd();
73155
73158
  this.globalSkillsDir = options.globalSkillsDir ?? resolve2(homedir3(), ".neura", "skills");
73159
+ this.bundledSkillsDir = options.bundledSkillsDir;
73156
73160
  this.explicitPaths = options.explicitPaths ?? [];
73157
73161
  this.debounceMs = options.debounceMs ?? 200;
73158
73162
  this.onReload = options.onReload;
@@ -73165,7 +73169,9 @@ var SkillWatcher = class {
73165
73169
  if (this.watcher) return;
73166
73170
  this.reload();
73167
73171
  const repoLocal = resolve2(this.cwd, ".neura", "skills");
73168
- const paths = [repoLocal, this.globalSkillsDir, ...this.explicitPaths];
73172
+ const paths = [repoLocal, this.globalSkillsDir];
73173
+ if (this.bundledSkillsDir) paths.push(this.bundledSkillsDir);
73174
+ paths.push(...this.explicitPaths);
73169
73175
  log13.info("starting skill watcher", { paths, debounceMs: this.debounceMs });
73170
73176
  this.watcher = chokidar.watch(paths, {
73171
73177
  ignoreInitial: true,
@@ -73241,6 +73247,7 @@ var SkillWatcher = class {
73241
73247
  const result = loadNeuraSkills({
73242
73248
  cwd: this.cwd,
73243
73249
  globalSkillsDir: this.globalSkillsDir,
73250
+ bundledSkillsDir: this.bundledSkillsDir,
73244
73251
  explicitPaths: this.explicitPaths
73245
73252
  });
73246
73253
  this.registry.replaceAll(result.skills);
@@ -76084,6 +76091,18 @@ var MEMORY_NAMES = /* @__PURE__ */ new Set([
76084
76091
  "memory_stats"
76085
76092
  ]);
76086
76093
 
76094
+ // src/tools/voice-redact.ts
76095
+ function redactTaskForVoice(task) {
76096
+ const { workerId, ...rest } = task;
76097
+ return { ...rest, hasActiveWorker: workerId !== null };
76098
+ }
76099
+ function redactCommentForVoice(comment) {
76100
+ if (comment.author.startsWith("worker:")) {
76101
+ return { ...comment, author: "worker" };
76102
+ }
76103
+ return comment;
76104
+ }
76105
+
76087
76106
  // src/tools/task-tools.ts
76088
76107
  var log15 = new Logger("tool:task");
76089
76108
  var ALL_STATUSES = [
@@ -76325,7 +76344,12 @@ async function handleTaskTool(name, args, ctx) {
76325
76344
  goal: t2.goal,
76326
76345
  source: t2.source,
76327
76346
  version: t2.version,
76328
- workerId: t2.workerId
76347
+ // workerId deliberately omitted from the voice-facing
76348
+ // listing — TTS reads UUIDs letter-by-letter. Callers
76349
+ // that need the worker id should go through
76350
+ // list_active_workers (which runs internal tool calls
76351
+ // that don't narrate).
76352
+ hasActiveWorker: t2.workerId !== null
76329
76353
  }))
76330
76354
  }
76331
76355
  };
@@ -76337,14 +76361,25 @@ async function handleTaskTool(name, args, ctx) {
76337
76361
  if (!task) return { result: { found: false } };
76338
76362
  let comments = [];
76339
76363
  try {
76340
- comments = await ctx.taskTools.listTaskComments(task.id, { limit: 50 });
76364
+ const recentDesc = await ctx.taskTools.listTaskComments(task.id, {
76365
+ limit: 50,
76366
+ order: "desc",
76367
+ excludeTypes: ["heartbeat"]
76368
+ });
76369
+ comments = recentDesc.reverse();
76341
76370
  } catch (err) {
76342
- log15.warn("failed to load task comments for get_task", {
76371
+ log15.error("failed to load task comments for get_task", {
76343
76372
  taskId: task.id,
76344
76373
  err: String(err)
76345
76374
  });
76346
76375
  }
76347
- return { result: { found: true, task, comments } };
76376
+ return {
76377
+ result: {
76378
+ found: true,
76379
+ task: redactTaskForVoice(task),
76380
+ comments: comments.map(redactCommentForVoice)
76381
+ }
76382
+ };
76348
76383
  }
76349
76384
  case "update_task": {
76350
76385
  if (!ctx.taskTools) return { error: "Task system not available" };
@@ -77395,6 +77430,7 @@ async function insertComment(db, opts) {
77395
77430
  }
77396
77431
  async function listComments(db, opts) {
77397
77432
  const limit2 = opts.limit ?? 500;
77433
+ const order = opts.order ?? "asc";
77398
77434
  const filters = ["task_id = $1"];
77399
77435
  const values = [opts.taskId];
77400
77436
  let idx = 2;
@@ -77403,6 +77439,10 @@ async function listComments(db, opts) {
77403
77439
  const placeholders = types3.map(() => `$${idx++}`).join(", ");
77404
77440
  filters.push(`type IN (${placeholders})`);
77405
77441
  values.push(...types3);
77442
+ } else if (opts.excludeTypes && opts.excludeTypes.length > 0) {
77443
+ const placeholders = opts.excludeTypes.map(() => `$${idx++}`).join(", ");
77444
+ filters.push(`type NOT IN (${placeholders})`);
77445
+ values.push(...opts.excludeTypes);
77406
77446
  }
77407
77447
  if (opts.since) {
77408
77448
  filters.push(`created_at > $${idx++}`);
@@ -77411,12 +77451,21 @@ async function listComments(db, opts) {
77411
77451
  const result = await db.query(
77412
77452
  `SELECT * FROM task_comments
77413
77453
  WHERE ${filters.join(" AND ")}
77414
- ORDER BY created_at ASC
77454
+ ORDER BY created_at ${order === "desc" ? "DESC" : "ASC"}
77415
77455
  LIMIT $${idx}`,
77416
77456
  [...values, limit2]
77417
77457
  );
77418
77458
  return result.rows.map((r2) => mapTaskComment(r2));
77419
77459
  }
77460
+ async function pruneHeartbeats(db, taskId, author) {
77461
+ const result = await db.query(
77462
+ `DELETE FROM task_comments
77463
+ WHERE task_id = $1 AND type = 'heartbeat' AND author = $2
77464
+ RETURNING id`,
77465
+ [taskId, author]
77466
+ );
77467
+ return result.rows.length;
77468
+ }
77420
77469
  async function countOpenRequests(db, taskId) {
77421
77470
  const result = await db.query(
77422
77471
  `SELECT COUNT(*)::TEXT as count FROM task_comments
@@ -78455,6 +78504,12 @@ async function applyTaskUpdate(args) {
78455
78504
  urgency: payload.comment.urgency ?? null,
78456
78505
  metadata: payload.comment.metadata ?? null
78457
78506
  });
78507
+ if (payload.comment.type !== "heartbeat" && actor.startsWith("worker:")) {
78508
+ try {
78509
+ await pruneHeartbeats(db, task.id, actor);
78510
+ } catch {
78511
+ }
78512
+ }
78458
78513
  }
78459
78514
  const refreshed = await getWorkItem(db, task.id);
78460
78515
  if (!refreshed) {
@@ -78964,22 +79019,40 @@ async function handleWorkerControlTool(name, args, ctx) {
78964
79019
  case "pause_worker": {
78965
79020
  const workerId = args.worker_id;
78966
79021
  const result = await ctx.workerControl.pauseWorker(workerId);
78967
- return { result };
79022
+ return {
79023
+ result: { paused: result.paused, ...result.reason ? { reason: result.reason } : {} }
79024
+ };
78968
79025
  }
78969
79026
  case "resume_worker": {
78970
79027
  const workerId = args.worker_id;
78971
79028
  const message = args.message;
78972
79029
  const result = await ctx.workerControl.resumeWorker(workerId, message);
78973
- return { result };
79030
+ return {
79031
+ result: { resumed: result.resumed, ...result.reason ? { reason: result.reason } : {} }
79032
+ };
78974
79033
  }
78975
79034
  case "cancel_worker": {
78976
79035
  const workerId = args.worker_id;
78977
79036
  const result = await ctx.workerControl.cancelWorker(workerId);
78978
- return { result };
79037
+ return {
79038
+ result: {
79039
+ cancelled: result.cancelled,
79040
+ ...result.reason ? { reason: result.reason } : {}
79041
+ }
79042
+ };
78979
79043
  }
78980
79044
  case "list_active_workers": {
78981
79045
  const workers = await ctx.workerControl.listActive();
78982
- return { result: { count: workers.length, workers } };
79046
+ return {
79047
+ result: {
79048
+ count: workers.length,
79049
+ workers: workers.map((w) => ({
79050
+ status: w.status,
79051
+ skillName: w.skillName,
79052
+ startedAt: w.startedAt
79053
+ }))
79054
+ }
79055
+ };
78983
79056
  }
78984
79057
  default:
78985
79058
  return null;
@@ -79288,14 +79361,20 @@ async function initServices() {
79288
79361
  const agentDir = join4(config2.neuraHome, "agent");
79289
79362
  const sessionDir = defaultSessionDir(agentDir);
79290
79363
  const globalSkillsDir = join4(homedir5(), ".neura", "skills");
79364
+ const bundleDir = dirname2(fileURLToPath(import.meta.url));
79365
+ const bundledSkillsDir = join4(bundleDir, "..", "skills");
79291
79366
  skillRegistry = new SkillRegistry();
79292
79367
  skillWatcher = new SkillWatcher({
79293
79368
  registry: skillRegistry,
79294
79369
  cwd: process.cwd(),
79295
- globalSkillsDir
79370
+ globalSkillsDir,
79371
+ bundledSkillsDir
79296
79372
  });
79297
79373
  await skillWatcher.start();
79298
- log29.info("skill registry loaded", { count: skillRegistry.size });
79374
+ log29.info("skill registry loaded", {
79375
+ count: skillRegistry.size,
79376
+ bundledSkillsDir
79377
+ });
79299
79378
  voiceFanoutBridge = new VoiceFanoutBridge({
79300
79379
  interjector: { interject: () => Promise.resolve() }
79301
79380
  });
@@ -79347,7 +79426,12 @@ async function initServices() {
79347
79426
  },
79348
79427
  getTask: (idOrTitle) => store.getWorkItem(idOrTitle),
79349
79428
  listTaskComments: async (taskId, options) => {
79350
- return listComments(rawDb, { taskId, limit: options?.limit });
79429
+ return listComments(rawDb, {
79430
+ taskId,
79431
+ limit: options?.limit,
79432
+ order: options?.order,
79433
+ excludeTypes: options?.excludeTypes
79434
+ });
79351
79435
  },
79352
79436
  updateTask: async (idOrTitle, payload) => {
79353
79437
  const current = await store.getWorkItem(idOrTitle);
@@ -99061,7 +99145,12 @@ function attachWebSocket(httpServer2, services2) {
99061
99145
  listTaskComments: async (taskId, options) => {
99062
99146
  const db = store.getRawDb?.();
99063
99147
  if (!db) throw new Error("store does not expose a raw PGlite handle");
99064
- return listComments(db, { taskId, limit: options?.limit });
99148
+ return listComments(db, {
99149
+ taskId,
99150
+ limit: options?.limit,
99151
+ order: options?.order,
99152
+ excludeTypes: options?.excludeTypes
99153
+ });
99065
99154
  },
99066
99155
  updateTask: async (idOrTitle, payload) => {
99067
99156
  const current = await resolveTask(store, idOrTitle);