@xingjisoft/neutrino 0.5.2 → 0.6.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.
package/dist/web/hub.js CHANGED
@@ -189,6 +189,15 @@ class LiveHub {
189
189
  this.notify(key);
190
190
  return result;
191
191
  }
192
+ setAgentMode(key, mode) {
193
+ const entry = this.entries.get(key);
194
+ if (!entry?.agent.liveSetAgentMode)
195
+ return { ok: false, error: "this session cannot change agent mode" };
196
+ const result = entry.agent.liveSetAgentMode(mode);
197
+ if (result.ok)
198
+ this.notify(key);
199
+ return result;
200
+ }
192
201
  setApprovalMode(key, mode) {
193
202
  const entry = this.entries.get(key);
194
203
  if (!entry?.agent.liveSetApprovalMode)
@@ -17,7 +17,7 @@ import os from "node:os";
17
17
  import path from "node:path";
18
18
  import { timingSafeEqual } from "node:crypto";
19
19
  import { fileURLToPath } from "node:url";
20
- import { deleteSession, listSessions, loadConfig, normalizeApprovalMode, resolveModelSelection, saveConfig, sessionDir, } from "../config.js";
20
+ import { deleteSession, listSessions, loadConfig, normalizeApprovalMode, isAgentMode, resolveModelSelection, saveConfig, sessionDir, } from "../config.js";
21
21
  import { getWebSettings, updateWebSettings } from "./settings.js";
22
22
  import { isSeenIdentity, loadSeen, updateSeen, SEEN_LIMIT } from "./clientState.js";
23
23
  import { WebAuth } from "./auth.js";
@@ -538,18 +538,20 @@ function serveStatic(res, pathname) {
538
538
  sendText(res, 403, "Forbidden");
539
539
  return;
540
540
  }
541
- fs.stat(filePath, (err, stat) => {
542
- if (err || !stat.isFile()) {
541
+ // Send an immutable snapshot: the length and body must agree even while
542
+ // the development checkout changes or a filesystem stream adapter retries.
543
+ fs.readFile(filePath, (err, contents) => {
544
+ if (err) {
543
545
  sendText(res, 404, "Not found");
544
546
  return;
545
547
  }
546
548
  const type = MIME_TYPES[path.extname(filePath).toLowerCase()] ?? "application/octet-stream";
547
549
  res.writeHead(200, {
548
550
  "Content-Type": type,
549
- "Content-Length": stat.size,
551
+ "Content-Length": contents.length,
550
552
  "Cache-Control": "no-cache",
551
553
  });
552
- fs.createReadStream(filePath).pipe(res);
554
+ res.end(contents);
553
555
  });
554
556
  }
555
557
  function streamLive(key, req, res) {
@@ -1039,7 +1041,10 @@ async function handleApi(req, res, url, auth, mode, pathname, binding, updates)
1039
1041
  }
1040
1042
  approvalMode = normalizeApprovalMode(input.approval_mode);
1041
1043
  }
1042
- const session = createWebSession(cwd, selection, approvalMode, mode, preset);
1044
+ if (input.agent_mode !== undefined && (!isAgentMode(input.agent_mode) || (mode === "pro" && input.agent_mode !== "standard"))) {
1045
+ throw new ApiError(400, "invalid agent mode", "invalid agent mode");
1046
+ }
1047
+ const session = createWebSession(cwd, selection, approvalMode, mode, preset, input.agent_mode);
1043
1048
  const key = session.start();
1044
1049
  webSessions.set(key, session);
1045
1050
  recordWorkspace(cwd, mode);
@@ -1179,6 +1184,7 @@ async function handleApi(req, res, url, auth, mode, pathname, binding, updates)
1179
1184
  }
1180
1185
  let selection;
1181
1186
  let preset;
1187
+ let agentMode;
1182
1188
  try {
1183
1189
  const body = await readJsonBody(req);
1184
1190
  if (!body || typeof body !== "object" || Array.isArray(body))
@@ -1189,6 +1195,12 @@ async function handleApi(req, res, url, auth, mode, pathname, binding, updates)
1189
1195
  const requestedPreset = body.pro_preset;
1190
1196
  if (requestedPreset !== undefined && mode === "pro")
1191
1197
  preset = requireProPreset(requestedPreset);
1198
+ const requestedMode = body.agent_mode;
1199
+ if (requestedMode !== undefined) {
1200
+ if (!isAgentMode(requestedMode) || (mode === "pro" && requestedMode !== "standard"))
1201
+ throw new Error("invalid agent mode");
1202
+ agentMode = requestedMode;
1203
+ }
1192
1204
  }
1193
1205
  catch (err) {
1194
1206
  sendJson(res, 400, { error: err instanceof Error ? err.message : "invalid model selection" });
@@ -1198,6 +1210,22 @@ async function handleApi(req, res, url, auth, mode, pathname, binding, updates)
1198
1210
  const existing = hub.list(mode).find((entry) => entry.sessionId === id);
1199
1211
  if (existing) {
1200
1212
  const remote = hub.remoteAction(existing.key);
1213
+ if (agentMode !== undefined && mode !== "pro") {
1214
+ if (remote) {
1215
+ const result = await remote("agent-mode", { mode: agentMode });
1216
+ if (result.status !== 200) {
1217
+ sendJson(res, result.status, result.data);
1218
+ return true;
1219
+ }
1220
+ }
1221
+ else {
1222
+ const changed = hub.setAgentMode(existing.key, agentMode);
1223
+ if (!changed.ok) {
1224
+ sendJson(res, 409, { error: changed.error });
1225
+ return true;
1226
+ }
1227
+ }
1228
+ }
1201
1229
  if (remote && preset !== undefined) {
1202
1230
  const result = await remote("pro-preset", { preset });
1203
1231
  if (result.status !== 200) {
@@ -1236,6 +1264,8 @@ async function handleApi(req, res, url, auth, mode, pathname, binding, updates)
1236
1264
  }
1237
1265
  if (preset !== undefined)
1238
1266
  data.pro_preset = preset;
1267
+ if (agentMode !== undefined)
1268
+ data.agent_mode = agentMode;
1239
1269
  const requested = selection ? {
1240
1270
  provider: selection.provider, model: selection.model.name, intensity: selection.intensity,
1241
1271
  } : undefined;
@@ -1261,7 +1291,7 @@ async function handleApi(req, res, url, auth, mode, pathname, binding, updates)
1261
1291
  streamLive(key, req, res);
1262
1292
  return true;
1263
1293
  }
1264
- const liveMatch = /^\/api\/live\/([^/]+)(?:\/(pause|resume|message|approve|close|model|pro-preset|approval-mode|clear))?$/.exec(pathname);
1294
+ const liveMatch = /^\/api\/live\/([^/]+)(?:\/(pause|resume|message|approve|close|model|pro-preset|approval-mode|agent-mode|clear))?$/.exec(pathname);
1265
1295
  if (liveMatch) {
1266
1296
  const key = decodeURIComponent(liveMatch[1]);
1267
1297
  const action = liveMatch[2];
@@ -1290,7 +1320,7 @@ async function handleApi(req, res, url, auth, mode, pathname, binding, updates)
1290
1320
  return true;
1291
1321
  }
1292
1322
  try {
1293
- const body = ["approve", "approval-mode", "message", "model", "pro-preset"].includes(action)
1323
+ const body = ["approve", "approval-mode", "agent-mode", "message", "model", "pro-preset"].includes(action)
1294
1324
  ? await readJsonBody(req) : {};
1295
1325
  const result = await remote(action, body);
1296
1326
  sendJson(res, result.status, result.data);
@@ -1319,6 +1349,25 @@ async function handleApi(req, res, url, auth, mode, pathname, binding, updates)
1319
1349
  }
1320
1350
  return true;
1321
1351
  }
1352
+ if (action === "agent-mode") {
1353
+ try {
1354
+ const body = await readJsonBody(req);
1355
+ const value = body?.mode;
1356
+ if (!isAgentMode(value) || mode === "pro") {
1357
+ sendJson(res, 400, { error: "invalid agent mode" });
1358
+ return true;
1359
+ }
1360
+ const changed = hub.setAgentMode(key, value);
1361
+ if (!changed.ok)
1362
+ sendJson(res, 409, { error: changed.error });
1363
+ else
1364
+ sendJson(res, 200, { ok: true, snapshot: hub.snapshot(key) });
1365
+ }
1366
+ catch (error) {
1367
+ sendJson(res, 400, { error: error instanceof Error ? error.message : "invalid agent mode" });
1368
+ }
1369
+ return true;
1370
+ }
1322
1371
  if (action === "approval-mode") {
1323
1372
  let body;
1324
1373
  try {