fix(core): present the project dir to the model as the App Data Dir in the default system prompt (#61)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Yaowei ZhengandClaude Fable 5 authored and GitHub committed 2026-07-26 15:08:09 +08:00
1 parent 14b5e6384f
commit 816a823b75
24 files changed
+423 -68

No files matched your search

+7
View File
@@ -179,6 +179,13 @@ export const putAgentConfig = (
{ method: "PUT", body },
);
/** Overwrite system_config.yaml with the current defaults (keeps only name/description/version). */
export const resetAgentConfig = (projectId: string, agentId: string) =>
apiFetch<AgentConfigResponse>(
`/api/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(agentId)}/config/reset`,
{ method: "POST" },
);
export const getAgentTraces = (projectId: string, agentId: string) =>
apiFetch<AgentTracesResponse>(
`/api/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(agentId)}/traces`,
@@ -1,7 +1,7 @@
/**
* Agent settings page: six tabs —
* Overview (name/description/State path/active count/State version + snapshot
* export-import), Prompt (AGENTS.md and system_prompt editors + placeholder
* export-import + restore default configuration), Prompt (AGENTS.md and system_prompt editors + placeholder
* reference), Runtime (max_turns, model.*, compaction.*), Tools (editable built-in
* tools table, MCP Server read-only JSON), Vault (vault-tab.tsx), Schedule
* (schedules-tab.tsx).
@@ -116,6 +116,13 @@ export function AgentSettingsPage() {
[load, reloadAgents],
);
/** Config reset succeeded: the whole system_config.yaml was replaced, so reload every tab's data (and the list's tool counts). */
const onConfigReset = useCallback(() => {
toastSuccess(S.agent.resetConfigDone);
load();
void reloadAgents();
}, [load, reloadAgents]);
const save = useCallback(
async (update: AgentConfigUpdateRequest) => {
if (!projectId || !agentId) return;
@@ -177,7 +184,13 @@ export function AgentSettingsPage() {
<Tabs items={TABS} active={tab} onChange={setTab} />
<div className="py-4">
{tab === "overview" && (
<OverviewTab data={data} agentId={agentId} onSave={save} onImported={onImported} />
<OverviewTab
data={data}
agentId={agentId}
onSave={save}
onImported={onImported}
onConfigReset={onConfigReset}
/>
)}
{tab === "prompt" && <PromptTab data={data} onSave={save} />}
{tab === "runtime" && <RuntimeTab data={data} onSave={save} />}
@@ -204,11 +217,13 @@ function OverviewTab({
agentId,
onSave,
onImported,
onConfigReset,
}: {
data: AgentConfigResponse;
agentId: string;
onSave: SaveFn;
onImported: (version: number) => void;
onConfigReset: () => void;
}) {
const { currentProject } = useProject();
const projectId = currentProject?.projectId ?? null;
@@ -219,8 +234,24 @@ function OverviewTab({
const [importError, setImportError] = useState<string | null>(null);
// base64 of the snapshot package pending confirmation for a version conflict (409 version_conflict); non-null shows the confirm modal.
const [conflict, setConflict] = useState<string | null>(null);
const [resetOpen, setResetOpen] = useState(false);
const [resetting, setResetting] = useState(false);
const { requestSave, element: saveConfirm } = useSaveConfirm();
const runReset = async () => {
if (!projectId) return;
setResetting(true);
try {
await api.resetAgentConfig(projectId, agentId);
setResetOpen(false);
onConfigReset();
} catch (e) {
toastError(apiErrorText(e));
} finally {
setResetting(false);
}
};
const submit = () => {
const config: NonNullable<AgentConfigUpdateRequest["config"]> = {};
if (name.trim() !== (data.config.name ?? "")) config.name = name.trim();
@@ -336,6 +367,15 @@ function OverviewTab({
)}
</div>
{/* Restore default configuration: overwrite system_config.yaml with the current defaults (name/description/version kept) — the config-side analogue of a skill update. */}
<div>
<p className="mb-1 text-xs font-medium text-gray-500">{S.agent.resetConfigTitle}</p>
<p className="mb-2 text-xs text-gray-500 dark:text-gray-400">{S.agent.resetConfigDesc}</p>
<Button size="sm" disabled={resetting} onClick={() => setResetOpen(true)}>
{S.agent.resetConfigAction}
</Button>
</div>
<Button size="sm" variant="primary" onClick={submit}>
{S.common.save}
</Button>
@@ -353,6 +393,18 @@ function OverviewTab({
>
<p className="text-sm text-gray-600 dark:text-gray-300">{S.agent.importConflictBody}</p>
</ConfirmModal>
{/* Reset confirmation: overwriting customizations with the defaults is destructive, so it keeps the danger tone. */}
<ConfirmModal
open={resetOpen}
title={S.agent.resetConfigTitle}
busy={resetting}
onClose={() => setResetOpen(false)}
onConfirm={() => void runReset()}
confirmLabel={S.agent.resetConfigAction}
>
<p className="text-sm text-gray-600 dark:text-gray-300">{S.agent.resetConfigConfirmBody}</p>
</ConfirmModal>
</div>
);
}
+15 -2
View File
@@ -190,12 +190,18 @@ export const en: Strings = {
placeholders: [
["{{AGENTS_MD}}", "Injects the AGENTS.md content"],
["{{VAULT_KEYS}}", "Injects the vault key-name section (empty when no keys)"],
["{{SKILL_METADATA}}", "Injects the installed skills' metadata lines (empty when none)"],
["{{PLATFORM}}", "Runtime platform"],
["{{OS_VERSION}}", "Operating system version"],
["{{DATE}}", "Current date"],
["{{CWD}}", "Absolute Workspace path"],
[
"{{PROJECT_DIR}}",
"PenguinHarness app data root — all agents' data and project-level data; not the task working directory",
],
["{{AGENT_ID}}", "Current agent id"],
["{{PROJECT_DIR}}", "Absolute Project directory (Agent State/scratchpad derive from it)"],
["{{CWD}}", "Absolute Workspace path"],
["{{PROVIDER}}", "Model provider group"],
["{{MODEL_ID}}", "Upstream model id"],
["{{SESSION_ID}}", "Current Session id"],
] as ReadonlyArray<readonly [string, string]>,
maxTurns: "max_turns (max turns per Task, -1 = unlimited)",
@@ -265,6 +271,13 @@ export const en: Strings = {
importConflictTitle: "Version conflict",
importConflictBody:
"The snapshot's version is not newer than the current one; importing will overwrite the existing Agent State. Continue?",
resetConfigTitle: "Restore default configuration",
resetConfigDesc:
"Restores system_config.yaml to the current built-in defaults (same semantics as a skill update): the custom system prompt, tool list, model/compaction settings and MCP servers are overwritten; only name, description and the State version are kept.",
resetConfigAction: "Restore default configuration",
resetConfigConfirmBody:
"This overwrites the agent's existing configuration with the current defaults: the custom system prompt, tool list, model/compaction settings and MCP servers are all replaced, keeping only name and description. Like a skill update this cannot be undone. Continue?",
resetConfigDone: "Configuration restored to the current defaults",
},
models: {
+15 -2
View File
@@ -181,12 +181,18 @@ export const zh = {
placeholders: [
["{{AGENTS_MD}}", "注入 AGENTS.md 内容"],
["{{VAULT_KEYS}}", "注入密钥保险柜的键名小节(无键时为空)"],
["{{SKILL_METADATA}}", "注入已安装 Skill 的元数据行(无 Skill 时为空)"],
["{{PLATFORM}}", "运行平台"],
["{{OS_VERSION}}", "操作系统版本"],
["{{DATE}}", "当前日期"],
["{{CWD}}", "Workspace 绝对路径"],
[
"{{PROJECT_DIR}}",
"PenguinHarness 应用数据根目录(存放全部 Agent 数据与 Project 级数据;不是本次任务的工作目录)",
],
["{{AGENT_ID}}", "当前 Agent id"],
["{{PROJECT_DIR}}", "Project 目录绝对路径(Agent State/scratchpad 由此拼出)"],
["{{CWD}}", "Workspace 绝对路径"],
["{{PROVIDER}}", "模型 provider 分组"],
["{{MODEL_ID}}", "上游模型 id"],
["{{SESSION_ID}}", "当前 Session id"],
] as ReadonlyArray<readonly [string, string]>,
maxTurns: "max_turns(单 Task 最大轮次,-1 不限制)",
@@ -242,6 +248,13 @@ export const zh = {
importDone: (v: number): string => `导入完成,Agent State 版本 v${v}`,
importConflictTitle: "版本冲突",
importConflictBody: "快照包版本不高于当前版本,导入将覆盖现有 Agent State。确认继续?",
resetConfigTitle: "还原为默认配置",
resetConfigDesc:
"把 system_config.yaml 还原为当前内置默认值(与 Skill 更新同语义):自定义的系统提示词、工具列表、模型/压缩参数与 MCP Server 将被覆盖,仅保留名称、描述与版本号。",
resetConfigAction: "还原为默认配置",
resetConfigConfirmBody:
"此操作会用当前默认值覆盖该 Agent 的现有配置:自定义系统提示词、工具列表、模型/压缩参数与 MCP Server 全部被替换,仅保留名称与描述。与 Skill 更新一样不可撤销,确认继续?",
resetConfigDone: "配置已还原为当前默认值",
},
models: {