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:
2026-10-12 13:06:04 +07:00
parent 2fd0c903a2
commit 3a8be55b55
52 changed files with 1559 additions and 203 deletions
+22 -2
View File
@@ -58,6 +58,9 @@ import {
import { AiToolIcon } from "../AiToolIcon";
import { RunDot, type RunState } from "./ConsoleTabs";
import { setSessionChat, useSessionChat } from "../../lib/chatUi";
import { readChatDraft, writeChatDraft } from "../../lib/chatDraft";
import { pasteToRun, takeRunInput } from "../../lib/panePaste";
import { promptWithImages } from "./RunSetup";
export const PANE_DRAG_TYPE = "application/x-agenthub-pane";
/** The slot being dragged: `dataTransfer` data cannot be read during dragover. */
@@ -195,7 +198,9 @@ const ITEM =
"flex w-full items-center gap-2.5 rounded-md px-2.5 py-1.5 text-left text-sm text-fg-secondary hover:bg-surface-4 hover:text-fg-primary";
/** Chat UI ⇄ terminal for a Claude/Codex pane — this session's own view
* (`lib/chatUi.ts`); a session with no id yet has no chat to show. */
* (`lib/chatUi.ts`); a session with no id yet has no chat to show. The
* unsent prompt goes along: the terminal's input box into the chat's draft,
* the draft (its files as paths) back into the input box, never sent. */
function ChatToggle({ run }: { run?: RunHandle }) {
const { t } = useTranslation("lanes");
const chat = useSessionChat(run?.sessionId);
@@ -209,7 +214,22 @@ function ChatToggle({ run }: { run?: RunHandle }) {
aria-pressed={chat}
title={label}
aria-label={label}
onClick={() => setSessionChat(sessionId, !chat)}
onClick={() => {
const draft = readChatDraft(sessionId);
if (chat) {
const text = promptWithImages(draft.prompt, draft.images);
if (text && pasteToRun(run.id, text)) writeChatDraft(sessionId, "", []);
} else {
const text = takeRunInput(run.id);
if (text)
writeChatDraft(
sessionId,
[draft.prompt, text].filter(Boolean).join("\n"),
draft.images
);
}
setSessionChat(sessionId, !chat);
}}
className="flex h-6 w-6 items-center justify-center rounded text-fg-muted hover:bg-surface-4 hover:text-fg-primary"
>
{chat ? (