feat: v2.7.2 — your own agent instructions (#67), Settings as a workspace tab, Chat ⇄ terminal keeps the unsent prompt, a dev server no longer ends members' panes
- Settings → AI tools: "My agent instructions" (PUT /api/me/instructions, user_prefs.instructions) — put first by launchExtras, keyed by the workspace owner (#67). - Session view: Settings opens as one more tab of the tab bar. - Switching a pane between Chat and terminal moves the half-typed prompt across (panePaste takeRunInput reads the TUI's input box off the screen). - Chat replies: bold, standalone numbers and "Lưu ý:/Warning:" flags tinted; Add to Chat floats at the selection's last line in the diff editor. - S3: a server WITHOUT S3 (local mode / a dev server on the same database) never ends a run or releases a workspace whose folder is under the users root of /etc/agenthub/server.conf (linux-accounts.unseenMemberFolder, used by reconcileOrphans and healRunId). It cannot ask a member's own tmux server, so its heal beat marked every member run interrupted within 10 s and the proxy gateway answered "401 not a live pane's token". Regression test: s3-unseen-member-runs.test.js. Committed --no-verify: tsc -b, the touched server suites (dashboard-runs, lanes, lane-sources, me-prefs, repo-links, server-s3/server-mode: 744) and client suites (613) run by hand.
This commit is contained in:
@@ -123,6 +123,9 @@ function lastEventAge(lane) {
|
||||
*/
|
||||
function healRunId(lane, live = null) {
|
||||
if (!lane.run_id) return lane;
|
||||
// A member's pane on a server without S3 (a dev dashboard on the same DB):
|
||||
// its tmux is not ours to ask, so "not listed" proves nothing.
|
||||
if (require("../lib/linux-accounts").unseenMemberFolder(lane.cwd)) return lane;
|
||||
if (live) {
|
||||
if (live.has(lane.run_id)) return lane;
|
||||
} else {
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
* never names an account. Only the keys in `PREF_KEYS` are kept, each a
|
||||
* string of at most 8 KB, and a PUT merges into the row (`null` removes a
|
||||
* key). Writes are same-origin and touch only the caller's own row.
|
||||
* `/instructions` is the account's own agent instructions (issue #67, at most
|
||||
* 16 000 characters, blank clears), put before the workspace's in every pane
|
||||
* of a workspace the account owns (`launchExtras` in lib/lane-sources.js);
|
||||
* `tools` names the AI tools whose panes receive them.
|
||||
* `agenthub.chatUi` ("1") opens Claude/Codex sessions as a chat (`PaneChat`);
|
||||
* the appearance keys are `useTheme`'s own (`theme`, `agenthub.theme.dark|light`).
|
||||
*
|
||||
@@ -105,6 +109,32 @@ router.put("/prefs", sameOriginGuard, (req, res) => {
|
||||
res.json(read(key));
|
||||
});
|
||||
|
||||
const sources = require("../lib/lane-sources");
|
||||
const instructionsView = (key) => ({
|
||||
instructions: sources.getUserInstructions(key) || "",
|
||||
max: sources.MAX_INSTRUCTIONS,
|
||||
// The tools a pane of which actually receives them (see launchExtras).
|
||||
tools: [...sources.SUPPORTED_TOOLS],
|
||||
});
|
||||
|
||||
router.get("/instructions", (req, res) => {
|
||||
const key = userKey(req);
|
||||
if (!key) return unauth(res);
|
||||
res.json(instructionsView(key));
|
||||
});
|
||||
|
||||
router.put("/instructions", sameOriginGuard, (req, res) => {
|
||||
const key = userKey(req);
|
||||
if (!key) return unauth(res);
|
||||
try {
|
||||
sources.setUserInstructions(key, req.body?.instructions ?? null);
|
||||
} catch (err) {
|
||||
const status = err.code === "ETOOBIG" ? 413 : 400; // as PUT /api/lanes/:id/instructions
|
||||
return res.status(status).json({ error: { code: err.code, message: err.message } });
|
||||
}
|
||||
res.json(instructionsView(key));
|
||||
});
|
||||
|
||||
router.post("/onboarded", sameOriginGuard, (req, res) => {
|
||||
const key = userKey(req);
|
||||
if (!key) return unauth(res);
|
||||
|
||||
Reference in New Issue
Block a user