@mclean-capital/neura 3.5.0 → 3.5.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/core/server.bundled.mjs +101 -49
- package/core/server.bundled.mjs.map +4 -4
- package/core/version.txt +1 -1
- package/package.json +1 -1
package/core/server.bundled.mjs
CHANGED
|
@@ -76397,7 +76397,7 @@ async function handleTaskTool(name, args, ctx) {
|
|
|
76397
76397
|
result: {
|
|
76398
76398
|
dispatched: true,
|
|
76399
76399
|
workerId: outcome.workerId,
|
|
76400
|
-
message: `Worker
|
|
76400
|
+
message: `Worker dispatched. You'll hear progress updates as it works.`
|
|
76401
76401
|
}
|
|
76402
76402
|
};
|
|
76403
76403
|
}
|
|
@@ -77355,6 +77355,53 @@ async function sweepCrashedWorkers(db, fileExists) {
|
|
|
77355
77355
|
// src/workers/agent-worker.ts
|
|
77356
77356
|
init_work_item_queries();
|
|
77357
77357
|
|
|
77358
|
+
// src/stores/task-comment-queries.ts
|
|
77359
|
+
init_mappers();
|
|
77360
|
+
import crypto4 from "crypto";
|
|
77361
|
+
var TASK_COMMENT_CONTENT_MAX_BYTES = 32 * 1024;
|
|
77362
|
+
async function insertComment(db, opts) {
|
|
77363
|
+
if (Buffer.byteLength(opts.content, "utf8") > TASK_COMMENT_CONTENT_MAX_BYTES) {
|
|
77364
|
+
throw new Error(
|
|
77365
|
+
`task comment content exceeds ${TASK_COMMENT_CONTENT_MAX_BYTES} bytes; write to disk and pass attachment_path with a summary in content`
|
|
77366
|
+
);
|
|
77367
|
+
}
|
|
77368
|
+
const id = crypto4.randomUUID();
|
|
77369
|
+
const result = await db.query(
|
|
77370
|
+
`INSERT INTO task_comments (
|
|
77371
|
+
id, task_id, type, author, content, attachment_path, urgency, metadata
|
|
77372
|
+
)
|
|
77373
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
77374
|
+
RETURNING *`,
|
|
77375
|
+
[
|
|
77376
|
+
id,
|
|
77377
|
+
opts.taskId,
|
|
77378
|
+
opts.type,
|
|
77379
|
+
opts.author,
|
|
77380
|
+
opts.content,
|
|
77381
|
+
opts.attachmentPath ?? null,
|
|
77382
|
+
opts.urgency ?? null,
|
|
77383
|
+
opts.metadata ? JSON.stringify(opts.metadata) : null
|
|
77384
|
+
]
|
|
77385
|
+
);
|
|
77386
|
+
return mapTaskComment(result.rows[0]);
|
|
77387
|
+
}
|
|
77388
|
+
async function countOpenRequests(db, taskId) {
|
|
77389
|
+
const result = await db.query(
|
|
77390
|
+
`SELECT COUNT(*)::TEXT as count FROM task_comments
|
|
77391
|
+
WHERE task_id = $1
|
|
77392
|
+
AND type IN ('clarification_request', 'approval_request')
|
|
77393
|
+
AND id NOT IN (
|
|
77394
|
+
-- requests with a matching response comment are considered resolved
|
|
77395
|
+
SELECT (metadata->>'resolves_comment_id')::text FROM task_comments
|
|
77396
|
+
WHERE task_id = $1
|
|
77397
|
+
AND type IN ('clarification_response', 'approval_response')
|
|
77398
|
+
AND metadata ? 'resolves_comment_id'
|
|
77399
|
+
)`,
|
|
77400
|
+
[taskId]
|
|
77401
|
+
);
|
|
77402
|
+
return Number(result.rows[0]?.count ?? 0);
|
|
77403
|
+
}
|
|
77404
|
+
|
|
77358
77405
|
// src/workers/worktree-manager.ts
|
|
77359
77406
|
import { execFileSync } from "node:child_process";
|
|
77360
77407
|
import {
|
|
@@ -77590,7 +77637,15 @@ var CANONICAL_WORKER_SYSTEM_PROMPT = `You are a Neura worker \u2014 a capable en
|
|
|
77590
77637
|
|
|
77591
77638
|
Your posture: be decisive. You have full tool access \u2014 Read, Write, Edit, Bash \u2014 scoped to an isolated worktree directory (your cwd). Make progress. Don't ask the user to double-check obvious things. Don't propose a plan and wait for approval when the path is clear.
|
|
77592
77639
|
|
|
77640
|
+
Your cwd is an isolated sandbox under \`~/.neura/worktrees/<workerId>/\`. It is NOT the user's home directory, desktop, or documents folder. Files you create land in the sandbox, not in user-visible locations. A task whose goal requires writing to the user's Desktop, Documents, or any absolute path outside your cwd cannot be satisfied by writing inside cwd \u2014 that would silently put the file in the wrong place. In that case:
|
|
77641
|
+
|
|
77642
|
+
1. If the outside path is essential to the task, call \`request_approval\` with a rationale ("task goal requires writing to ~/Desktop, which is outside my sandbox \u2014 authorize access?") and wait. If denied, call \`fail_task\` with \`reason_code: 'impossible'\`.
|
|
77643
|
+
2. If you can satisfy the goal inside cwd without writing to the user-visible path, do that and make it clear in your \`complete_task\` summary where the file actually lives.
|
|
77644
|
+
|
|
77645
|
+
Do NOT pretend success when you wrote to the wrong place. Silent "it completed" with no file where the user expects it is the worst outcome.
|
|
77646
|
+
|
|
77593
77647
|
Reversibility rule: before any destructive or hard-to-reverse action **outside** your worktree, call \`request_approval\` and wait for the user's answer. Examples that require approval:
|
|
77648
|
+
- Writing to the user's home directory, Desktop, Documents, or any absolute path outside cwd
|
|
77594
77649
|
- Deleting files the user owns
|
|
77595
77650
|
- Force-pushing branches, rewriting git history
|
|
77596
77651
|
- Sending email, SMS, or other external messages
|
|
@@ -78030,6 +78085,31 @@ var AgentWorker = class {
|
|
|
78030
78085
|
});
|
|
78031
78086
|
}
|
|
78032
78087
|
}
|
|
78088
|
+
if ((status === "failed" || status === "crashed") && result.error) {
|
|
78089
|
+
try {
|
|
78090
|
+
const { reason, detail } = result.error;
|
|
78091
|
+
const MAX_DETAIL_BYTES = 3e4;
|
|
78092
|
+
const safeDetail = detail ? detail.slice(0, MAX_DETAIL_BYTES) : "";
|
|
78093
|
+
const content = safeDetail ? `${reason}: ${safeDetail}` : reason;
|
|
78094
|
+
await insertComment(this.db, {
|
|
78095
|
+
taskId,
|
|
78096
|
+
type: "error",
|
|
78097
|
+
author: "system",
|
|
78098
|
+
content,
|
|
78099
|
+
metadata: {
|
|
78100
|
+
workerId,
|
|
78101
|
+
workerStatus: status,
|
|
78102
|
+
reason
|
|
78103
|
+
}
|
|
78104
|
+
});
|
|
78105
|
+
} catch (err) {
|
|
78106
|
+
log23.warn("failed to persist error comment on task", {
|
|
78107
|
+
workerId,
|
|
78108
|
+
taskId,
|
|
78109
|
+
err: String(err)
|
|
78110
|
+
});
|
|
78111
|
+
}
|
|
78112
|
+
}
|
|
78033
78113
|
}
|
|
78034
78114
|
try {
|
|
78035
78115
|
if (status === "completed" || status === "cancelled") {
|
|
@@ -78195,53 +78275,6 @@ function buildClarificationTool(workerId, bridge) {
|
|
|
78195
78275
|
return tool;
|
|
78196
78276
|
}
|
|
78197
78277
|
|
|
78198
|
-
// src/stores/task-comment-queries.ts
|
|
78199
|
-
init_mappers();
|
|
78200
|
-
import crypto4 from "crypto";
|
|
78201
|
-
var TASK_COMMENT_CONTENT_MAX_BYTES = 32 * 1024;
|
|
78202
|
-
async function insertComment(db, opts) {
|
|
78203
|
-
if (Buffer.byteLength(opts.content, "utf8") > TASK_COMMENT_CONTENT_MAX_BYTES) {
|
|
78204
|
-
throw new Error(
|
|
78205
|
-
`task comment content exceeds ${TASK_COMMENT_CONTENT_MAX_BYTES} bytes; write to disk and pass attachment_path with a summary in content`
|
|
78206
|
-
);
|
|
78207
|
-
}
|
|
78208
|
-
const id = crypto4.randomUUID();
|
|
78209
|
-
const result = await db.query(
|
|
78210
|
-
`INSERT INTO task_comments (
|
|
78211
|
-
id, task_id, type, author, content, attachment_path, urgency, metadata
|
|
78212
|
-
)
|
|
78213
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
78214
|
-
RETURNING *`,
|
|
78215
|
-
[
|
|
78216
|
-
id,
|
|
78217
|
-
opts.taskId,
|
|
78218
|
-
opts.type,
|
|
78219
|
-
opts.author,
|
|
78220
|
-
opts.content,
|
|
78221
|
-
opts.attachmentPath ?? null,
|
|
78222
|
-
opts.urgency ?? null,
|
|
78223
|
-
opts.metadata ? JSON.stringify(opts.metadata) : null
|
|
78224
|
-
]
|
|
78225
|
-
);
|
|
78226
|
-
return mapTaskComment(result.rows[0]);
|
|
78227
|
-
}
|
|
78228
|
-
async function countOpenRequests(db, taskId) {
|
|
78229
|
-
const result = await db.query(
|
|
78230
|
-
`SELECT COUNT(*)::TEXT as count FROM task_comments
|
|
78231
|
-
WHERE task_id = $1
|
|
78232
|
-
AND type IN ('clarification_request', 'approval_request')
|
|
78233
|
-
AND id NOT IN (
|
|
78234
|
-
-- requests with a matching response comment are considered resolved
|
|
78235
|
-
SELECT (metadata->>'resolves_comment_id')::text FROM task_comments
|
|
78236
|
-
WHERE task_id = $1
|
|
78237
|
-
AND type IN ('clarification_response', 'approval_response')
|
|
78238
|
-
AND metadata ? 'resolves_comment_id'
|
|
78239
|
-
)`,
|
|
78240
|
-
[taskId]
|
|
78241
|
-
);
|
|
78242
|
-
return Number(result.rows[0]?.count ?? 0);
|
|
78243
|
-
}
|
|
78244
|
-
|
|
78245
78278
|
// src/tools/task-update-handler.ts
|
|
78246
78279
|
init_work_item_queries();
|
|
78247
78280
|
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["done", "cancelled", "failed"]);
|
|
@@ -78672,6 +78705,21 @@ Reason: ${params.rationale}` : params.action;
|
|
|
78672
78705
|
return tools;
|
|
78673
78706
|
}
|
|
78674
78707
|
|
|
78708
|
+
// src/server/lifecycle.ts
|
|
78709
|
+
import { AuthStorage } from "@mariozechner/pi-coding-agent";
|
|
78710
|
+
|
|
78711
|
+
// src/workers/auth-bridge.ts
|
|
78712
|
+
function seedAuthStorageFromConfig(authStorage, providers) {
|
|
78713
|
+
let count = 0;
|
|
78714
|
+
for (const [providerId, creds] of Object.entries(providers)) {
|
|
78715
|
+
if (creds?.apiKey) {
|
|
78716
|
+
authStorage.setRuntimeApiKey(providerId, creds.apiKey);
|
|
78717
|
+
count++;
|
|
78718
|
+
}
|
|
78719
|
+
}
|
|
78720
|
+
return count;
|
|
78721
|
+
}
|
|
78722
|
+
|
|
78675
78723
|
// src/tools/vision-tools.ts
|
|
78676
78724
|
var visionToolDefs = [
|
|
78677
78725
|
{
|
|
@@ -79332,6 +79380,9 @@ async function initServices() {
|
|
|
79332
79380
|
`${wProvider}/${wModel} model not registered in pi-ai. Check config.routing.worker or verify pi-ai supports this provider.`
|
|
79333
79381
|
);
|
|
79334
79382
|
}
|
|
79383
|
+
const authStorage = AuthStorage.create(join4(agentDir, "auth.json"));
|
|
79384
|
+
const seededProviders = seedAuthStorageFromConfig(authStorage, config2.providers);
|
|
79385
|
+
log29.info("seeded pi auth storage from config", { count: seededProviders });
|
|
79335
79386
|
const piRuntime = new PiRuntime({
|
|
79336
79387
|
model,
|
|
79337
79388
|
thinkingLevel: "low",
|
|
@@ -79340,7 +79391,8 @@ async function initServices() {
|
|
|
79340
79391
|
sessionDir,
|
|
79341
79392
|
buildTools,
|
|
79342
79393
|
voiceFanoutBridge,
|
|
79343
|
-
skillRegistry
|
|
79394
|
+
skillRegistry,
|
|
79395
|
+
authStorage
|
|
79344
79396
|
});
|
|
79345
79397
|
agentWorker = new AgentWorker({ db: rawDb, runtime: piRuntime });
|
|
79346
79398
|
await agentWorker.recoverFromCrash();
|