@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.
- package/core/server.bundled.mjs +104 -15
- package/core/server.bundled.mjs.map +3 -3
- package/core/version.txt +1 -1
- package/package.json +2 -1
- package/skills/orchestrator-worker-control/SKILL.md +134 -0
- package/skills/red-test-triage/SKILL.md +80 -0
- package/skills/wiki-upload/SKILL.md +79 -0
- package/stores/task-comment-queries.d.ts +13 -0
- package/stores/task-comment-queries.d.ts.map +1 -1
- package/stores/task-comment-queries.js +7 -1
- package/stores/task-comment-queries.js.map +1 -1
- package/stores/task-comment-queries.test.js +50 -0
- package/stores/task-comment-queries.test.js.map +1 -1
package/core/server.bundled.mjs
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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", {
|
|
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, {
|
|
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, {
|
|
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);
|