feat: add master-agent prompts and memory management
This commit is contained in:
@@ -218,6 +218,44 @@ export interface UserAttachmentStorageConfig {
|
||||
validatedAt?: string;
|
||||
}
|
||||
|
||||
export interface MasterAgentPromptPolicy {
|
||||
globalPrompt: string;
|
||||
updatedAt: string;
|
||||
updatedBy?: string;
|
||||
}
|
||||
|
||||
export interface UserMasterPrompt {
|
||||
account: string;
|
||||
content: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export type MasterMemoryScope = "global" | "project";
|
||||
export type MasterMemoryType =
|
||||
| "user_preference"
|
||||
| "project_progress"
|
||||
| "decision"
|
||||
| "risk"
|
||||
| "blocking_issue"
|
||||
| "research_note"
|
||||
| "workflow_rule";
|
||||
|
||||
export interface MasterAgentMemory {
|
||||
memoryId: string;
|
||||
account: string;
|
||||
scope: MasterMemoryScope;
|
||||
projectId?: string;
|
||||
title: string;
|
||||
content: string;
|
||||
memoryType: MasterMemoryType;
|
||||
tags: string[];
|
||||
sourceMessageId?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
lastUsedAt?: string;
|
||||
archived: boolean;
|
||||
}
|
||||
|
||||
export interface GoalItem {
|
||||
id: string;
|
||||
text: string;
|
||||
@@ -328,6 +366,7 @@ function buildCollaborationGate(project: Pick<Project, "isGroup" | "collaboratio
|
||||
export interface ProjectAgentControls {
|
||||
modelOverride?: string;
|
||||
reasoningEffortOverride?: ReasoningEffort;
|
||||
promptOverride?: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
@@ -809,6 +848,9 @@ export interface BossState {
|
||||
deviceSkills: DeviceSkill[];
|
||||
appLogs: AppLogEntry[];
|
||||
userAttachmentStorageConfigs: UserAttachmentStorageConfig[];
|
||||
masterAgentPromptPolicy: MasterAgentPromptPolicy | null;
|
||||
userMasterPrompts: UserMasterPrompt[];
|
||||
masterAgentMemories: MasterAgentMemory[];
|
||||
threadContextSnapshots: ThreadContextSnapshot[];
|
||||
threadHandoffPackages: ThreadHandoffPackage[];
|
||||
threadContextAlerts: ThreadContextAlert[];
|
||||
@@ -1223,6 +1265,9 @@ const initialState: BossState = {
|
||||
updatedAt: nowIso(),
|
||||
},
|
||||
],
|
||||
masterAgentPromptPolicy: null,
|
||||
userMasterPrompts: [],
|
||||
masterAgentMemories: [],
|
||||
masterAgentTasks: [],
|
||||
dispatchPlans: [],
|
||||
dispatchExecutions: [],
|
||||
@@ -2079,14 +2124,16 @@ function normalizeProjectAgentControls(
|
||||
const reasoningEffortOverride = isReasoningEffort(raw?.reasoningEffortOverride)
|
||||
? raw.reasoningEffortOverride
|
||||
: undefined;
|
||||
const promptOverride = trimToDefined(raw?.promptOverride);
|
||||
|
||||
if (!modelOverride && !reasoningEffortOverride) {
|
||||
if (!modelOverride && !reasoningEffortOverride && !promptOverride) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
modelOverride,
|
||||
reasoningEffortOverride,
|
||||
promptOverride,
|
||||
updatedAt: raw?.updatedAt ?? nowIso(),
|
||||
};
|
||||
}
|
||||
@@ -2512,6 +2559,67 @@ function normalizeAttachmentStorageConfig(
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeMasterAgentPromptPolicy(
|
||||
raw: Partial<MasterAgentPromptPolicy> | null | undefined,
|
||||
fallback?: MasterAgentPromptPolicy | null,
|
||||
): MasterAgentPromptPolicy | null {
|
||||
if (!raw) {
|
||||
return fallback ?? null;
|
||||
}
|
||||
const globalPrompt = raw.globalPrompt?.trim();
|
||||
if (!globalPrompt) {
|
||||
return fallback ?? null;
|
||||
}
|
||||
return {
|
||||
globalPrompt,
|
||||
updatedAt: raw.updatedAt ?? fallback?.updatedAt ?? nowIso(),
|
||||
updatedBy: raw.updatedBy?.trim() || fallback?.updatedBy,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeUserMasterPrompt(
|
||||
raw: Partial<UserMasterPrompt>,
|
||||
fallback?: UserMasterPrompt,
|
||||
): UserMasterPrompt {
|
||||
const account = raw.account ?? fallback?.account ?? "";
|
||||
return {
|
||||
account,
|
||||
content: raw.content?.trim() ?? fallback?.content ?? "",
|
||||
updatedAt: raw.updatedAt ?? fallback?.updatedAt ?? nowIso(),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeMasterMemoryTags(values: string[] | undefined) {
|
||||
return dedupeStrings(
|
||||
(values ?? [])
|
||||
.map((value) => value.trim())
|
||||
.filter((value) => Boolean(value)),
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeUserMasterMemory(
|
||||
raw: Partial<MasterAgentMemory>,
|
||||
fallback?: MasterAgentMemory,
|
||||
): MasterAgentMemory {
|
||||
const scope = raw.scope ?? fallback?.scope ?? "global";
|
||||
const projectId = scope === "project" ? raw.projectId ?? fallback?.projectId : undefined;
|
||||
return {
|
||||
memoryId: raw.memoryId ?? fallback?.memoryId ?? randomToken("memory"),
|
||||
account: raw.account ?? fallback?.account ?? "",
|
||||
scope,
|
||||
projectId,
|
||||
title: raw.title?.trim() ?? fallback?.title ?? "",
|
||||
content: raw.content?.trim() ?? fallback?.content ?? "",
|
||||
memoryType: raw.memoryType ?? fallback?.memoryType ?? "user_preference",
|
||||
tags: normalizeMasterMemoryTags(raw.tags ?? fallback?.tags ?? []),
|
||||
sourceMessageId: raw.sourceMessageId ?? fallback?.sourceMessageId,
|
||||
createdAt: raw.createdAt ?? fallback?.createdAt ?? nowIso(),
|
||||
updatedAt: raw.updatedAt ?? fallback?.updatedAt ?? nowIso(),
|
||||
lastUsedAt: raw.lastUsedAt ?? fallback?.lastUsedAt,
|
||||
archived: raw.archived ?? fallback?.archived ?? false,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeProject(raw: Partial<Project>, fallback?: Project): Project {
|
||||
const base = fallback ?? cloneInitialState().projects[0];
|
||||
const projectId = raw.id ?? base.id;
|
||||
@@ -2745,6 +2853,24 @@ function normalizeState(raw: Partial<BossState> | undefined): BossState {
|
||||
base.userAttachmentStorageConfigs[index % base.userAttachmentStorageConfigs.length],
|
||||
),
|
||||
),
|
||||
masterAgentPromptPolicy: normalizeMasterAgentPromptPolicy(
|
||||
raw.masterAgentPromptPolicy,
|
||||
base.masterAgentPromptPolicy,
|
||||
),
|
||||
userMasterPrompts: ensureArray(raw.userMasterPrompts, base.userMasterPrompts).map(
|
||||
(prompt, index) =>
|
||||
normalizeUserMasterPrompt(
|
||||
prompt,
|
||||
base.userMasterPrompts[index % Math.max(1, base.userMasterPrompts.length)],
|
||||
),
|
||||
),
|
||||
masterAgentMemories: ensureArray(raw.masterAgentMemories, base.masterAgentMemories).map(
|
||||
(memory, index) =>
|
||||
normalizeUserMasterMemory(
|
||||
memory,
|
||||
base.masterAgentMemories[index % Math.max(1, base.masterAgentMemories.length)],
|
||||
),
|
||||
),
|
||||
threadContextSnapshots: ensureArray(raw.threadContextSnapshots, base.threadContextSnapshots).map(
|
||||
(snapshot, index) => ({
|
||||
...base.threadContextSnapshots[index % base.threadContextSnapshots.length],
|
||||
@@ -3410,6 +3536,7 @@ export async function updateProjectAgentControls(
|
||||
payload: {
|
||||
modelOverride?: unknown;
|
||||
reasoningEffortOverride?: unknown;
|
||||
promptOverride?: unknown;
|
||||
},
|
||||
) {
|
||||
if (projectId !== "master-agent") {
|
||||
@@ -3422,12 +3549,18 @@ export async function updateProjectAgentControls(
|
||||
const reasoningEffortInput = Object.prototype.hasOwnProperty.call(payload, "reasoningEffortOverride")
|
||||
? parseReasoningEffortOverride(payload.reasoningEffortOverride)
|
||||
: { kind: "preserve" as const };
|
||||
const promptOverrideInput = Object.prototype.hasOwnProperty.call(payload, "promptOverride")
|
||||
? parseControlTextOverride(payload.promptOverride)
|
||||
: { kind: "preserve" as const };
|
||||
if (modelOverrideInput.kind === "invalid") {
|
||||
throw new Error("INVALID_MODEL_OVERRIDE");
|
||||
}
|
||||
if (reasoningEffortInput.kind === "invalid") {
|
||||
throw new Error("INVALID_REASONING_EFFORT_OVERRIDE");
|
||||
}
|
||||
if (promptOverrideInput.kind === "invalid") {
|
||||
throw new Error("INVALID_PROMPT_OVERRIDE");
|
||||
}
|
||||
|
||||
return mutateStateIfChanged((state) => {
|
||||
const project = state.projects.find((item) => item.id === projectId);
|
||||
@@ -3446,16 +3579,28 @@ export async function updateProjectAgentControls(
|
||||
: reasoningEffortInput.kind === "clear"
|
||||
? undefined
|
||||
: currentControls?.reasoningEffortOverride;
|
||||
const promptOverride =
|
||||
promptOverrideInput.kind === "set"
|
||||
? promptOverrideInput.value
|
||||
: promptOverrideInput.kind === "clear"
|
||||
? undefined
|
||||
: currentControls?.promptOverride;
|
||||
|
||||
const currentModelOverride = currentControls?.modelOverride;
|
||||
const currentReasoningEffortOverride = currentControls?.reasoningEffortOverride;
|
||||
if (currentModelOverride === modelOverride && currentReasoningEffortOverride === reasoningEffortOverride) {
|
||||
const currentPromptOverride = currentControls?.promptOverride;
|
||||
if (
|
||||
currentModelOverride === modelOverride &&
|
||||
currentReasoningEffortOverride === reasoningEffortOverride &&
|
||||
currentPromptOverride === promptOverride
|
||||
) {
|
||||
return { result: currentControls, changed: false };
|
||||
}
|
||||
|
||||
const nextControls = {
|
||||
modelOverride,
|
||||
reasoningEffortOverride,
|
||||
promptOverride,
|
||||
updatedAt: nowIso(),
|
||||
} satisfies ProjectAgentControls;
|
||||
|
||||
@@ -3496,6 +3641,423 @@ export async function upsertAttachmentStorageConfig(config: UserAttachmentStorag
|
||||
});
|
||||
}
|
||||
|
||||
export async function getMasterAgentPromptPolicy() {
|
||||
const state = await readState();
|
||||
return state.masterAgentPromptPolicy ?? null;
|
||||
}
|
||||
|
||||
export async function updateMasterAgentPromptPolicy(input: {
|
||||
globalPrompt: string;
|
||||
updatedBy?: string;
|
||||
}) {
|
||||
const globalPrompt = input.globalPrompt.trim();
|
||||
if (!globalPrompt) {
|
||||
throw new Error("MASTER_AGENT_PROMPT_REQUIRED");
|
||||
}
|
||||
|
||||
return mutateState((state) => {
|
||||
const policy: MasterAgentPromptPolicy = {
|
||||
globalPrompt,
|
||||
updatedBy: input.updatedBy?.trim() || undefined,
|
||||
updatedAt: nowIso(),
|
||||
};
|
||||
state.masterAgentPromptPolicy = policy;
|
||||
return policy;
|
||||
});
|
||||
}
|
||||
|
||||
export async function getUserMasterPrompt(account: string) {
|
||||
const state = await readState();
|
||||
return state.userMasterPrompts.find((item) => item.account === account) ?? null;
|
||||
}
|
||||
|
||||
export async function updateUserMasterPrompt(account: string, content: string) {
|
||||
const trimmedContent = content.trim();
|
||||
if (!trimmedContent) {
|
||||
throw new Error("USER_MASTER_PROMPT_REQUIRED");
|
||||
}
|
||||
|
||||
return mutateState((state) => {
|
||||
const next: UserMasterPrompt = {
|
||||
account,
|
||||
content: trimmedContent,
|
||||
updatedAt: nowIso(),
|
||||
};
|
||||
const existing = state.userMasterPrompts.find((item) => item.account === account);
|
||||
if (existing) {
|
||||
Object.assign(existing, next);
|
||||
} else {
|
||||
state.userMasterPrompts.unshift(next);
|
||||
}
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
export async function clearUserMasterPrompt(account: string) {
|
||||
return mutateState((state) => {
|
||||
const before = state.userMasterPrompts.length;
|
||||
state.userMasterPrompts = state.userMasterPrompts.filter((item) => item.account !== account);
|
||||
return { cleared: before !== state.userMasterPrompts.length };
|
||||
});
|
||||
}
|
||||
|
||||
export async function listUserMasterMemories(
|
||||
account: string,
|
||||
options?: { includeArchived?: boolean; scope?: MasterMemoryScope; projectId?: string },
|
||||
) {
|
||||
const state = await readState();
|
||||
const includeArchived = options?.includeArchived ?? false;
|
||||
return [...state.masterAgentMemories]
|
||||
.filter((memory) => {
|
||||
if (memory.account !== account) return false;
|
||||
if (!includeArchived && memory.archived) return false;
|
||||
if (options?.scope && memory.scope !== options.scope) return false;
|
||||
if (options?.projectId && memory.projectId !== options.projectId) return false;
|
||||
return true;
|
||||
})
|
||||
.sort((a, b) => {
|
||||
const timeDiff =
|
||||
messageTimeValue(b.lastUsedAt ?? b.updatedAt ?? b.createdAt) -
|
||||
messageTimeValue(a.lastUsedAt ?? a.updatedAt ?? a.createdAt);
|
||||
if (timeDiff !== 0) return timeDiff;
|
||||
return b.memoryId.localeCompare(a.memoryId);
|
||||
});
|
||||
}
|
||||
|
||||
export async function createUserMasterMemory(input: {
|
||||
account: string;
|
||||
scope: MasterMemoryScope;
|
||||
projectId?: string;
|
||||
title: string;
|
||||
content: string;
|
||||
memoryType: MasterMemoryType;
|
||||
tags?: string[];
|
||||
sourceMessageId?: string;
|
||||
}) {
|
||||
const title = input.title.trim();
|
||||
const content = input.content.trim();
|
||||
if (!title) {
|
||||
throw new Error("USER_MASTER_MEMORY_TITLE_REQUIRED");
|
||||
}
|
||||
if (!content) {
|
||||
throw new Error("USER_MASTER_MEMORY_CONTENT_REQUIRED");
|
||||
}
|
||||
if (input.scope === "project" && !input.projectId?.trim()) {
|
||||
throw new Error("USER_MASTER_MEMORY_PROJECT_ID_REQUIRED");
|
||||
}
|
||||
|
||||
return mutateState((state) => {
|
||||
const now = nowIso();
|
||||
const memory: MasterAgentMemory = {
|
||||
memoryId: randomToken("memory"),
|
||||
account: input.account,
|
||||
scope: input.scope,
|
||||
projectId: input.scope === "project" ? input.projectId?.trim() : undefined,
|
||||
title,
|
||||
content,
|
||||
memoryType: input.memoryType,
|
||||
tags: normalizeMasterMemoryTags(input.tags),
|
||||
sourceMessageId: input.sourceMessageId,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
lastUsedAt: now,
|
||||
archived: false,
|
||||
};
|
||||
state.masterAgentMemories.unshift(memory);
|
||||
return memory;
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateUserMasterMemory(
|
||||
memoryId: string,
|
||||
account: string,
|
||||
patch: Partial<
|
||||
Pick<
|
||||
MasterAgentMemory,
|
||||
"scope" | "projectId" | "title" | "content" | "memoryType" | "tags" | "sourceMessageId" | "lastUsedAt"
|
||||
>
|
||||
>,
|
||||
) {
|
||||
return mutateState((state) => {
|
||||
const memory = state.masterAgentMemories.find(
|
||||
(item) => item.memoryId === memoryId && item.account === account,
|
||||
);
|
||||
if (!memory) {
|
||||
throw new Error("USER_MASTER_MEMORY_NOT_FOUND");
|
||||
}
|
||||
|
||||
if (patch.scope) {
|
||||
memory.scope = patch.scope;
|
||||
}
|
||||
if (memory.scope === "project" && patch.projectId !== undefined) {
|
||||
memory.projectId = patch.projectId.trim() || undefined;
|
||||
}
|
||||
if (memory.scope !== "project") {
|
||||
memory.projectId = undefined;
|
||||
}
|
||||
if (patch.title !== undefined) {
|
||||
const title = patch.title.trim();
|
||||
if (!title) throw new Error("USER_MASTER_MEMORY_TITLE_REQUIRED");
|
||||
memory.title = title;
|
||||
}
|
||||
if (patch.content !== undefined) {
|
||||
const content = patch.content.trim();
|
||||
if (!content) throw new Error("USER_MASTER_MEMORY_CONTENT_REQUIRED");
|
||||
memory.content = content;
|
||||
}
|
||||
if (patch.memoryType) {
|
||||
memory.memoryType = patch.memoryType;
|
||||
}
|
||||
if (patch.tags) {
|
||||
memory.tags = normalizeMasterMemoryTags(patch.tags);
|
||||
}
|
||||
if (patch.sourceMessageId !== undefined) {
|
||||
memory.sourceMessageId = patch.sourceMessageId;
|
||||
}
|
||||
if (patch.lastUsedAt !== undefined) {
|
||||
memory.lastUsedAt = patch.lastUsedAt;
|
||||
}
|
||||
memory.updatedAt = nowIso();
|
||||
return memory;
|
||||
});
|
||||
}
|
||||
|
||||
export async function archiveUserMasterMemory(memoryId: string, account: string) {
|
||||
return mutateState((state) => {
|
||||
const memory = state.masterAgentMemories.find(
|
||||
(item) => item.memoryId === memoryId && item.account === account,
|
||||
);
|
||||
if (!memory) {
|
||||
throw new Error("USER_MASTER_MEMORY_NOT_FOUND");
|
||||
}
|
||||
|
||||
memory.archived = true;
|
||||
memory.updatedAt = nowIso();
|
||||
return memory;
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeAutoMemoryText(value: string | undefined) {
|
||||
return (value ?? "")
|
||||
.replace(/\s+/g, " ")
|
||||
.replace(/[。;;!!]+$/g, "")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function inferAutoMemoryType(text: string): MasterMemoryType | null {
|
||||
if (!text.trim()) return null;
|
||||
if (/(微信|wechat|中文回复|中文沟通|UI风格|交互风格|偏好|习惯|默认)/i.test(text)) {
|
||||
return "user_preference";
|
||||
}
|
||||
if (/(规则|约束|优先|先.*再|必须|不要|需要|流程|逻辑)/i.test(text)) {
|
||||
return "workflow_rule";
|
||||
}
|
||||
if (/(阻塞|卡住|失败|异常|报错|问题|bug|未打通)/i.test(text)) {
|
||||
return "blocking_issue";
|
||||
}
|
||||
if (/(风险|隐患|告警)/i.test(text)) {
|
||||
return "risk";
|
||||
}
|
||||
if (/(决定|改成|采用|统一|确定|方案)/i.test(text)) {
|
||||
return "decision";
|
||||
}
|
||||
if (/(调研|研究|结论)/i.test(text)) {
|
||||
return "research_note";
|
||||
}
|
||||
if (/(进度|完成|已接通|已打通|上线|当前.*状态|回归|发布)/i.test(text)) {
|
||||
return "project_progress";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function inferProjectAutoMemoryType(text: string): Exclude<MasterMemoryType, "user_preference"> | null {
|
||||
if (!text.trim()) return null;
|
||||
if (/(阻塞|卡住|失败|异常|报错|问题|bug|未打通)/i.test(text)) {
|
||||
return "blocking_issue";
|
||||
}
|
||||
if (/(风险|隐患|告警)/i.test(text)) {
|
||||
return "risk";
|
||||
}
|
||||
if (/(决定|改成|采用|统一|确定|方案)/i.test(text)) {
|
||||
return "decision";
|
||||
}
|
||||
if (/(调研|研究|结论)/i.test(text)) {
|
||||
return "research_note";
|
||||
}
|
||||
if (/(进度|完成|已接通|已打通|上线|当前.*状态|回归|发布)/i.test(text)) {
|
||||
return "project_progress";
|
||||
}
|
||||
if (/(规则|约束|优先|先.*再|必须|不要|需要|流程|逻辑)/i.test(text)) {
|
||||
return "workflow_rule";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function buildAutoMemoryTitle(memoryType: MasterMemoryType, label?: string) {
|
||||
const typeLabel =
|
||||
memoryType === "user_preference"
|
||||
? "偏好"
|
||||
: memoryType === "workflow_rule"
|
||||
? "工作规则"
|
||||
: memoryType === "blocking_issue"
|
||||
? "阻塞"
|
||||
: memoryType === "risk"
|
||||
? "风险"
|
||||
: memoryType === "decision"
|
||||
? "决策"
|
||||
: memoryType === "research_note"
|
||||
? "调研结论"
|
||||
: "项目进度";
|
||||
return label ? `${label} · ${typeLabel}` : typeLabel;
|
||||
}
|
||||
|
||||
function detectReferencedProjectForMemory(state: BossState, text: string) {
|
||||
const lowered = text.toLowerCase();
|
||||
const candidates = state.projects
|
||||
.filter((project) => project.id !== "master-agent")
|
||||
.flatMap((project) => {
|
||||
const rawAliases = [
|
||||
project.id,
|
||||
project.name,
|
||||
project.threadMeta.folderName,
|
||||
project.threadMeta.threadDisplayName,
|
||||
]
|
||||
.map((value) => value.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
const aliases = Array.from(
|
||||
new Set(
|
||||
rawAliases.flatMap((alias) => {
|
||||
const normalized = alias.trim();
|
||||
if (!normalized) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const tokenCandidates = normalized
|
||||
.split(/[\s\-_/]+/)
|
||||
.map((token) => token.trim())
|
||||
.filter((token) => token.length >= 3);
|
||||
|
||||
return [normalized, ...tokenCandidates];
|
||||
}),
|
||||
),
|
||||
);
|
||||
return aliases.map((alias) => ({
|
||||
projectId: project.id,
|
||||
projectName: project.name,
|
||||
alias,
|
||||
}));
|
||||
})
|
||||
.sort((left, right) => right.alias.length - left.alias.length);
|
||||
|
||||
return candidates.find((candidate) => lowered.includes(candidate.alias.toLowerCase())) ?? null;
|
||||
}
|
||||
|
||||
function upsertAutoMasterMemoryInState(
|
||||
state: BossState,
|
||||
input: {
|
||||
account: string;
|
||||
scope: MasterMemoryScope;
|
||||
projectId?: string;
|
||||
title: string;
|
||||
content: string;
|
||||
memoryType: MasterMemoryType;
|
||||
tags: string[];
|
||||
sourceMessageId?: string;
|
||||
},
|
||||
) {
|
||||
const now = nowIso();
|
||||
const existing = state.masterAgentMemories.find(
|
||||
(memory) =>
|
||||
memory.account === input.account &&
|
||||
memory.scope === input.scope &&
|
||||
(memory.projectId ?? undefined) === (input.projectId ?? undefined) &&
|
||||
memory.title === input.title,
|
||||
);
|
||||
|
||||
if (existing) {
|
||||
existing.content = input.content;
|
||||
existing.memoryType = input.memoryType;
|
||||
existing.tags = normalizeMasterMemoryTags(input.tags);
|
||||
existing.sourceMessageId = input.sourceMessageId ?? existing.sourceMessageId;
|
||||
existing.archived = false;
|
||||
existing.updatedAt = now;
|
||||
existing.lastUsedAt = now;
|
||||
return existing;
|
||||
}
|
||||
|
||||
const memory: MasterAgentMemory = {
|
||||
memoryId: randomToken("memory"),
|
||||
account: input.account,
|
||||
scope: input.scope,
|
||||
projectId: input.scope === "project" ? input.projectId : undefined,
|
||||
title: input.title,
|
||||
content: input.content,
|
||||
memoryType: input.memoryType,
|
||||
tags: normalizeMasterMemoryTags(input.tags),
|
||||
sourceMessageId: input.sourceMessageId,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
lastUsedAt: now,
|
||||
archived: false,
|
||||
};
|
||||
state.masterAgentMemories.unshift(memory);
|
||||
return memory;
|
||||
}
|
||||
|
||||
function autoCaptureMasterAgentMemoriesInState(
|
||||
state: BossState,
|
||||
input: {
|
||||
account: string;
|
||||
requestText: string;
|
||||
replyText: string;
|
||||
sourceMessageId?: string;
|
||||
},
|
||||
) {
|
||||
const requestText = normalizeAutoMemoryText(input.requestText);
|
||||
const replyText = normalizeAutoMemoryText(input.replyText);
|
||||
if (!requestText && !replyText) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const createdOrUpdated: MasterAgentMemory[] = [];
|
||||
const combined = [requestText, replyText].filter(Boolean).join(" ");
|
||||
const preferenceType = inferAutoMemoryType(requestText);
|
||||
|
||||
if (preferenceType === "user_preference" || preferenceType === "workflow_rule") {
|
||||
createdOrUpdated.push(
|
||||
upsertAutoMasterMemoryInState(state, {
|
||||
account: input.account,
|
||||
scope: "global",
|
||||
title: buildAutoMemoryTitle(preferenceType),
|
||||
content: requestText,
|
||||
memoryType: preferenceType,
|
||||
tags: preferenceType === "user_preference" ? ["用户偏好"] : ["工作方式"],
|
||||
sourceMessageId: input.sourceMessageId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const referencedProject = detectReferencedProjectForMemory(state, combined);
|
||||
const projectType = inferProjectAutoMemoryType(replyText) ?? inferProjectAutoMemoryType(combined);
|
||||
if (referencedProject && projectType) {
|
||||
createdOrUpdated.push(
|
||||
upsertAutoMasterMemoryInState(state, {
|
||||
account: input.account,
|
||||
scope: "project",
|
||||
projectId: referencedProject.projectId,
|
||||
title: buildAutoMemoryTitle(projectType, referencedProject.projectName),
|
||||
content: replyText || requestText,
|
||||
memoryType: projectType,
|
||||
tags: [referencedProject.projectName, referencedProject.alias],
|
||||
sourceMessageId: input.sourceMessageId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return createdOrUpdated;
|
||||
}
|
||||
|
||||
function preferredDeviceForAccount(
|
||||
state: BossState,
|
||||
account: string,
|
||||
@@ -5078,6 +5640,12 @@ export async function completeMasterAgentTask(payload: {
|
||||
body: task.replyBody,
|
||||
kind: "text",
|
||||
});
|
||||
autoCaptureMasterAgentMemoriesInState(state, {
|
||||
account: task.requestedByAccount,
|
||||
requestText: task.requestText,
|
||||
replyText: task.replyBody,
|
||||
sourceMessageId: task.requestMessageId,
|
||||
});
|
||||
}
|
||||
} else if (!attachmentProjectId && payload.status === "failed") {
|
||||
const isThreadConversationReply =
|
||||
|
||||
@@ -22,6 +22,11 @@ import type { DispatchPlanTarget, Project, ProjectAgentControls, ReasoningEffort
|
||||
import { canInlineAttachmentText, extractAttachmentTextExcerpt } from "@/lib/boss-attachments";
|
||||
import { readAliyunOssObjectBuffer } from "@/lib/boss-storage-aliyun-oss";
|
||||
import { readServerFileAttachmentBuffer } from "@/lib/boss-storage-server-file";
|
||||
import {
|
||||
getMasterAgentPromptPolicyView,
|
||||
getUserMasterPromptView,
|
||||
listUserMasterMemoriesView,
|
||||
} from "@/lib/boss-projections";
|
||||
|
||||
type MasterAgentReplyState = "queued" | "running" | "completed";
|
||||
const OPENAI_MASTER_AGENT_DEVICE_ID = "master-agent-openai";
|
||||
@@ -38,28 +43,83 @@ type QueuedMasterAgentReplyEnvelope = {
|
||||
};
|
||||
};
|
||||
|
||||
export async function resolveMasterAgentExecutionConfig(projectId: string) {
|
||||
export async function resolveMasterAgentExecutionConfig(
|
||||
projectId: string,
|
||||
accountId?: string,
|
||||
requestText?: string,
|
||||
) {
|
||||
const runtime = await getMasterAgentRuntimeAccount();
|
||||
if (!runtime?.account) {
|
||||
throw new Error("NO_MASTER_AGENT_RUNTIME_ACCOUNT");
|
||||
}
|
||||
|
||||
const agentControls = await getProjectAgentControls(projectId);
|
||||
const state = await readState();
|
||||
const resolvedAccountId = accountId?.trim() || state.user.account || runtime.account.accountId;
|
||||
const reasoningEffort =
|
||||
agentControls?.reasoningEffortOverride ||
|
||||
(runtime.account as typeof runtime.account & { reasoningEffort?: ReasoningEffort }).reasoningEffort ||
|
||||
"medium";
|
||||
const promptPolicy = getMasterAgentPromptPolicyView(state);
|
||||
const userPrompt = getUserMasterPromptView(state, resolvedAccountId);
|
||||
const memoryScope = listUserMasterMemoriesView(state, resolvedAccountId, { includeArchived: false });
|
||||
const projectMemories = selectRelevantProjectMemories(memoryScope, projectId, requestText);
|
||||
const userMemories = memoryScope.filter((memory) => memory.scope === "global");
|
||||
|
||||
return {
|
||||
runtime,
|
||||
account: runtime.account,
|
||||
agentControls,
|
||||
projectPromptOverride: agentControls?.promptOverride ?? null,
|
||||
provider: runtime.account.provider,
|
||||
model: agentControls?.modelOverride || runtime.account.model || "gpt-5.4",
|
||||
reasoningEffort,
|
||||
promptPolicy,
|
||||
userPrompt,
|
||||
projectMemories,
|
||||
userMemories,
|
||||
executionPrompt: buildMasterAgentExecutionPrompt({
|
||||
state,
|
||||
projectId,
|
||||
requestText: requestText ?? "",
|
||||
currentSessionExpiresAt: undefined,
|
||||
agentControls,
|
||||
accountId: resolvedAccountId,
|
||||
promptPolicy,
|
||||
userPrompt,
|
||||
projectMemories,
|
||||
userMemories,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function selectRelevantProjectMemories(
|
||||
memories: Awaited<ReturnType<typeof listUserMasterMemoriesView>>,
|
||||
projectId: string,
|
||||
requestText?: string,
|
||||
) {
|
||||
const projectScoped = memories.filter((memory) => memory.scope === "project");
|
||||
if (projectId !== "master-agent") {
|
||||
return projectScoped.filter((memory) => memory.projectId === projectId);
|
||||
}
|
||||
if (projectScoped.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const lowered = requestText?.trim().toLowerCase() ?? "";
|
||||
if (!lowered) {
|
||||
return projectScoped.slice(0, 6);
|
||||
}
|
||||
|
||||
const matched = projectScoped.filter((memory) => {
|
||||
const haystacks = [memory.title, memory.content, ...(memory.tags ?? [])]
|
||||
.map((value) => value.toLowerCase());
|
||||
return haystacks.some((value) => lowered.includes(value) || value.includes(lowered));
|
||||
});
|
||||
|
||||
return (matched.length > 0 ? matched : projectScoped).slice(0, 6);
|
||||
}
|
||||
|
||||
function buildAgentControlsDigest(agentControls?: ProjectAgentControls | null) {
|
||||
if (!agentControls) {
|
||||
return "当前对话覆盖:无";
|
||||
@@ -69,9 +129,59 @@ function buildAgentControlsDigest(agentControls?: ProjectAgentControls | null) {
|
||||
"当前对话覆盖:",
|
||||
`model=${agentControls.modelOverride ?? "默认"}`,
|
||||
`reasoning=${agentControls.reasoningEffortOverride ?? "默认"}`,
|
||||
`prompt=${agentControls.promptOverride ? "已配置" : "默认"}`,
|
||||
].join(" ");
|
||||
}
|
||||
|
||||
function buildPromptPolicyDigest(promptPolicy: Awaited<ReturnType<typeof getMasterAgentPromptPolicyView>>) {
|
||||
return promptPolicy?.globalPrompt?.trim()
|
||||
? [`管理员全局主提示词:`, promptPolicy.globalPrompt.trim()].join("\n")
|
||||
: "管理员全局主提示词:无";
|
||||
}
|
||||
|
||||
function buildUserPromptDigest(userPrompt: Awaited<ReturnType<typeof getUserMasterPromptView>>) {
|
||||
return userPrompt?.content?.trim()
|
||||
? [`用户私有主提示词:`, userPrompt.content.trim()].join("\n")
|
||||
: "用户私有主提示词:无";
|
||||
}
|
||||
|
||||
function buildMemoryDigest(title: string, memories: Awaited<ReturnType<typeof listUserMasterMemoriesView>>) {
|
||||
if (memories.length === 0) {
|
||||
return `${title}:无`;
|
||||
}
|
||||
return [
|
||||
`${title}:`,
|
||||
...memories.map((memory) => `- ${memory.title}:${memory.content}`),
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function buildMasterAgentExecutionPrompt(params: {
|
||||
state: Awaited<ReturnType<typeof readState>>;
|
||||
projectId: string;
|
||||
requestText: string;
|
||||
currentSessionExpiresAt?: string;
|
||||
agentControls?: ProjectAgentControls | null;
|
||||
accountId: string;
|
||||
promptPolicy: Awaited<ReturnType<typeof getMasterAgentPromptPolicyView>>;
|
||||
userPrompt: Awaited<ReturnType<typeof getUserMasterPromptView>>;
|
||||
projectMemories: Awaited<ReturnType<typeof listUserMasterMemoriesView>>;
|
||||
userMemories: Awaited<ReturnType<typeof listUserMasterMemoriesView>>;
|
||||
}) {
|
||||
return [
|
||||
buildMasterAgentInstructions(),
|
||||
buildPromptPolicyDigest(params.promptPolicy),
|
||||
buildUserPromptDigest(params.userPrompt),
|
||||
params.agentControls?.promptOverride?.trim()
|
||||
? ["当前对话附加提示词:", params.agentControls.promptOverride.trim()].join("\n")
|
||||
: "当前对话附加提示词:无",
|
||||
buildMemoryDigest("项目记忆", params.projectMemories),
|
||||
buildMemoryDigest("用户记忆", params.userMemories),
|
||||
buildAgentControlsDigest(params.agentControls),
|
||||
"",
|
||||
buildRuntimeDigest(params.state, params.requestText, params.currentSessionExpiresAt, params.agentControls),
|
||||
].join("\n\n");
|
||||
}
|
||||
|
||||
function buildMasterAgentInstructions() {
|
||||
return [
|
||||
"你是 Boss 控制台的主 Agent。",
|
||||
@@ -262,6 +372,10 @@ async function replyViaOpenAiAccount(params: {
|
||||
currentSessionExpiresAt?: string;
|
||||
senderLabel: string;
|
||||
agentControls?: ProjectAgentControls | null;
|
||||
promptPolicy?: Awaited<ReturnType<typeof getMasterAgentPromptPolicyView>>;
|
||||
userPrompt?: Awaited<ReturnType<typeof getUserMasterPromptView>>;
|
||||
projectMemories?: Awaited<ReturnType<typeof listUserMasterMemoriesView>>;
|
||||
userMemories?: Awaited<ReturnType<typeof listUserMasterMemoriesView>>;
|
||||
}) {
|
||||
if (!params.account?.apiKey?.trim()) {
|
||||
throw new Error("OPENAI_ACCOUNT_NOT_CONFIGURED");
|
||||
@@ -274,6 +388,10 @@ async function replyViaOpenAiAccount(params: {
|
||||
requestText: params.requestText,
|
||||
currentSessionExpiresAt: params.currentSessionExpiresAt,
|
||||
agentControls: params.agentControls,
|
||||
promptPolicy: params.promptPolicy,
|
||||
userPrompt: params.userPrompt,
|
||||
projectMemories: params.projectMemories,
|
||||
userMemories: params.userMemories,
|
||||
});
|
||||
|
||||
await appendMasterAgentSystemReply(generated.content, params.senderLabel);
|
||||
@@ -298,8 +416,22 @@ async function generateOpenAiReply(params: {
|
||||
requestText: string;
|
||||
currentSessionExpiresAt?: string;
|
||||
agentControls?: ProjectAgentControls | null;
|
||||
promptPolicy?: Awaited<ReturnType<typeof getMasterAgentPromptPolicyView>>;
|
||||
userPrompt?: Awaited<ReturnType<typeof getUserMasterPromptView>>;
|
||||
projectMemories?: Awaited<ReturnType<typeof listUserMasterMemoriesView>>;
|
||||
userMemories?: Awaited<ReturnType<typeof listUserMasterMemoriesView>>;
|
||||
}) {
|
||||
const state = await readState();
|
||||
const effectiveProjectMemories =
|
||||
params.projectMemories && params.projectMemories.length > 0
|
||||
? params.projectMemories
|
||||
: selectRelevantProjectMemories(
|
||||
listUserMasterMemoriesView(state, params.userPrompt?.account ?? state.user.account, {
|
||||
includeArchived: false,
|
||||
}),
|
||||
"master-agent",
|
||||
params.requestText,
|
||||
);
|
||||
let response: Response;
|
||||
try {
|
||||
response = await fetch("https://api.openai.com/v1/responses", {
|
||||
@@ -311,13 +443,19 @@ async function generateOpenAiReply(params: {
|
||||
body: JSON.stringify({
|
||||
model: params.model,
|
||||
reasoning: { effort: params.reasoningEffort },
|
||||
instructions: buildMasterAgentInstructions(),
|
||||
input: buildRuntimeDigest(
|
||||
instructions: buildMasterAgentExecutionPrompt({
|
||||
state,
|
||||
params.requestText,
|
||||
params.currentSessionExpiresAt,
|
||||
params.agentControls,
|
||||
),
|
||||
projectId: "master-agent",
|
||||
requestText: params.requestText,
|
||||
currentSessionExpiresAt: params.currentSessionExpiresAt,
|
||||
agentControls: params.agentControls,
|
||||
accountId: "master-agent",
|
||||
promptPolicy: params.promptPolicy ?? null,
|
||||
userPrompt: params.userPrompt ?? null,
|
||||
projectMemories: effectiveProjectMemories,
|
||||
userMemories: params.userMemories ?? [],
|
||||
}),
|
||||
input: params.requestText,
|
||||
}),
|
||||
signal: AbortSignal.timeout(45_000),
|
||||
});
|
||||
@@ -362,12 +500,23 @@ function buildMasterOpenAiReplyPrompt(
|
||||
requestText: string,
|
||||
currentSessionExpiresAt?: string,
|
||||
agentControls?: ProjectAgentControls | null,
|
||||
promptPolicy?: Awaited<ReturnType<typeof getMasterAgentPromptPolicyView>>,
|
||||
userPrompt?: Awaited<ReturnType<typeof getUserMasterPromptView>>,
|
||||
projectMemories?: Awaited<ReturnType<typeof listUserMasterMemoriesView>>,
|
||||
userMemories?: Awaited<ReturnType<typeof listUserMasterMemoriesView>>,
|
||||
) {
|
||||
return [
|
||||
buildMasterAgentInstructions(),
|
||||
"",
|
||||
buildRuntimeDigest(state, requestText, currentSessionExpiresAt, agentControls),
|
||||
].join("\n");
|
||||
return buildMasterAgentExecutionPrompt({
|
||||
state,
|
||||
projectId: "master-agent",
|
||||
requestText,
|
||||
currentSessionExpiresAt,
|
||||
agentControls,
|
||||
accountId: "master-agent",
|
||||
promptPolicy: promptPolicy ?? null,
|
||||
userPrompt: userPrompt ?? null,
|
||||
projectMemories: projectMemories ?? [],
|
||||
userMemories: userMemories ?? [],
|
||||
});
|
||||
}
|
||||
|
||||
async function queueAndStartOpenAiMasterAgentReply(params: {
|
||||
@@ -379,6 +528,10 @@ async function queueAndStartOpenAiMasterAgentReply(params: {
|
||||
model: string;
|
||||
reasoningEffort: ReasoningEffort;
|
||||
agentControls?: ProjectAgentControls | null;
|
||||
promptPolicy?: Awaited<ReturnType<typeof getMasterAgentPromptPolicyView>>;
|
||||
userPrompt?: Awaited<ReturnType<typeof getUserMasterPromptView>>;
|
||||
projectMemories?: Awaited<ReturnType<typeof listUserMasterMemoriesView>>;
|
||||
userMemories?: Awaited<ReturnType<typeof listUserMasterMemoriesView>>;
|
||||
}) {
|
||||
const timer = setTimeout(() => {
|
||||
void (async () => {
|
||||
@@ -395,6 +548,10 @@ async function queueAndStartOpenAiMasterAgentReply(params: {
|
||||
requestText: params.requestText,
|
||||
currentSessionExpiresAt: params.currentSessionExpiresAt,
|
||||
agentControls: params.agentControls,
|
||||
promptPolicy: params.promptPolicy,
|
||||
userPrompt: params.userPrompt,
|
||||
projectMemories: params.projectMemories,
|
||||
userMemories: params.userMemories,
|
||||
});
|
||||
|
||||
await completeMasterAgentTask({
|
||||
@@ -429,6 +586,10 @@ async function enqueueOpenAiMasterAgentReply(params: {
|
||||
model: string;
|
||||
reasoningEffort: ReasoningEffort;
|
||||
agentControls?: ProjectAgentControls | null;
|
||||
promptPolicy?: Awaited<ReturnType<typeof getMasterAgentPromptPolicyView>>;
|
||||
userPrompt?: Awaited<ReturnType<typeof getUserMasterPromptView>>;
|
||||
projectMemories?: Awaited<ReturnType<typeof listUserMasterMemoriesView>>;
|
||||
userMemories?: Awaited<ReturnType<typeof listUserMasterMemoriesView>>;
|
||||
}) {
|
||||
const state = await readState();
|
||||
const task = await queueMasterAgentTask({
|
||||
@@ -439,6 +600,10 @@ async function enqueueOpenAiMasterAgentReply(params: {
|
||||
params.requestText,
|
||||
params.currentSessionExpiresAt,
|
||||
params.agentControls,
|
||||
params.promptPolicy,
|
||||
params.userPrompt,
|
||||
params.projectMemories,
|
||||
params.userMemories,
|
||||
),
|
||||
requestedBy: params.requestedBy,
|
||||
requestedByAccount: params.requestedByAccount,
|
||||
@@ -455,6 +620,10 @@ async function enqueueOpenAiMasterAgentReply(params: {
|
||||
model: params.model,
|
||||
reasoningEffort: params.reasoningEffort,
|
||||
agentControls: params.agentControls,
|
||||
promptPolicy: params.promptPolicy,
|
||||
userPrompt: params.userPrompt,
|
||||
projectMemories: params.projectMemories,
|
||||
userMemories: params.userMemories,
|
||||
});
|
||||
|
||||
const queuedReply: QueuedMasterAgentReplyEnvelope = {
|
||||
@@ -542,16 +711,23 @@ function buildMasterCodexNodePrompt(
|
||||
requestText: string,
|
||||
currentSessionExpiresAt?: string,
|
||||
agentControls?: ProjectAgentControls | null,
|
||||
promptPolicy?: Awaited<ReturnType<typeof getMasterAgentPromptPolicyView>>,
|
||||
userPrompt?: Awaited<ReturnType<typeof getUserMasterPromptView>>,
|
||||
projectMemories?: Awaited<ReturnType<typeof listUserMasterMemoriesView>>,
|
||||
userMemories?: Awaited<ReturnType<typeof listUserMasterMemoriesView>>,
|
||||
) {
|
||||
return [
|
||||
"你是 Boss 控制台的主 Agent,运行在用户自己的 Master Codex Node 上。",
|
||||
"请结合下面的运行时状态和用户消息,直接给出中文回复。",
|
||||
"如果你认为需要继续在当前仓库里推进实现、排障或验证,可以直接说明你下一步会做什么;如果必须先做交接或收尾,也要明确说出原因。",
|
||||
"保持简洁,优先给出结论、动作、验证点。",
|
||||
buildAgentControlsDigest(agentControls),
|
||||
"",
|
||||
buildRuntimeDigest(state, requestText, currentSessionExpiresAt, agentControls),
|
||||
].join("\n");
|
||||
return buildMasterAgentExecutionPrompt({
|
||||
state,
|
||||
projectId: "master-agent",
|
||||
requestText,
|
||||
currentSessionExpiresAt,
|
||||
agentControls,
|
||||
accountId: "master-agent",
|
||||
promptPolicy: promptPolicy ?? null,
|
||||
userPrompt: userPrompt ?? null,
|
||||
projectMemories: projectMemories ?? [],
|
||||
userMemories: userMemories ?? [],
|
||||
});
|
||||
}
|
||||
|
||||
function summarizeDispatchRequest(requestText: string) {
|
||||
@@ -1159,7 +1335,11 @@ export async function replyToMasterAgentUserMessage(params: {
|
||||
return { ok: false as const, reason: "NO_AI_ACCOUNT" };
|
||||
}
|
||||
|
||||
const executionConfig = await resolveMasterAgentExecutionConfig("master-agent");
|
||||
const executionConfig = await resolveMasterAgentExecutionConfig(
|
||||
"master-agent",
|
||||
params.requestedByAccount,
|
||||
params.requestText,
|
||||
);
|
||||
const agentControls = executionConfig.agentControls;
|
||||
|
||||
if (params.mode === "enqueue") {
|
||||
@@ -1195,6 +1375,10 @@ export async function replyToMasterAgentUserMessage(params: {
|
||||
model: agentControls?.modelOverride || fallbackAccount.model || "gpt-5.4",
|
||||
reasoningEffort: agentControls?.reasoningEffortOverride || "medium",
|
||||
agentControls,
|
||||
promptPolicy: executionConfig.promptPolicy,
|
||||
userPrompt: executionConfig.userPrompt,
|
||||
projectMemories: executionConfig.projectMemories,
|
||||
userMemories: executionConfig.userMemories,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1213,6 +1397,10 @@ export async function replyToMasterAgentUserMessage(params: {
|
||||
params.requestText,
|
||||
params.currentSessionExpiresAt,
|
||||
agentControls,
|
||||
executionConfig.promptPolicy,
|
||||
executionConfig.userPrompt,
|
||||
executionConfig.projectMemories,
|
||||
executionConfig.userMemories,
|
||||
),
|
||||
requestedBy: params.requestedBy,
|
||||
requestedByAccount: params.requestedByAccount,
|
||||
@@ -1248,6 +1436,10 @@ export async function replyToMasterAgentUserMessage(params: {
|
||||
model: executionConfig.model,
|
||||
reasoningEffort: executionConfig.reasoningEffort,
|
||||
agentControls,
|
||||
promptPolicy: executionConfig.promptPolicy,
|
||||
userPrompt: executionConfig.userPrompt,
|
||||
projectMemories: executionConfig.projectMemories,
|
||||
userMemories: executionConfig.userMemories,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1278,6 +1470,10 @@ export async function replyToMasterAgentUserMessage(params: {
|
||||
currentSessionExpiresAt: params.currentSessionExpiresAt,
|
||||
senderLabel: `主 Agent · ${fallbackAccount.label || aiRoleLabel(fallbackAccount.role)}`,
|
||||
agentControls,
|
||||
promptPolicy: executionConfig.promptPolicy,
|
||||
userPrompt: executionConfig.userPrompt,
|
||||
projectMemories: executionConfig.projectMemories,
|
||||
userMemories: executionConfig.userMemories,
|
||||
});
|
||||
} catch {
|
||||
// Fall through to the original offline guidance when the fallback API account cannot respond.
|
||||
@@ -1298,6 +1494,10 @@ export async function replyToMasterAgentUserMessage(params: {
|
||||
params.requestText,
|
||||
params.currentSessionExpiresAt,
|
||||
agentControls,
|
||||
executionConfig.promptPolicy,
|
||||
executionConfig.userPrompt,
|
||||
executionConfig.projectMemories,
|
||||
executionConfig.userMemories,
|
||||
),
|
||||
requestedBy: params.requestedBy,
|
||||
requestedByAccount: params.requestedByAccount,
|
||||
@@ -1324,6 +1524,10 @@ export async function replyToMasterAgentUserMessage(params: {
|
||||
currentSessionExpiresAt: params.currentSessionExpiresAt,
|
||||
senderLabel: `主 Agent · ${fallbackAccount.label || aiRoleLabel(fallbackAccount.role)}`,
|
||||
agentControls,
|
||||
promptPolicy: executionConfig.promptPolicy,
|
||||
userPrompt: executionConfig.userPrompt,
|
||||
projectMemories: executionConfig.projectMemories,
|
||||
userMemories: executionConfig.userMemories,
|
||||
});
|
||||
} catch {
|
||||
// Preserve the original execution failure below if the fallback account also fails.
|
||||
@@ -1367,6 +1571,10 @@ export async function replyToMasterAgentUserMessage(params: {
|
||||
requestText: params.requestText,
|
||||
currentSessionExpiresAt: params.currentSessionExpiresAt,
|
||||
agentControls,
|
||||
promptPolicy: executionConfig.promptPolicy,
|
||||
userPrompt: executionConfig.userPrompt,
|
||||
projectMemories: executionConfig.projectMemories,
|
||||
userMemories: executionConfig.userMemories,
|
||||
});
|
||||
|
||||
await appendMasterAgentSystemReply(
|
||||
|
||||
@@ -14,6 +14,8 @@ import type {
|
||||
DeviceImportResolution,
|
||||
DeviceSkill,
|
||||
MasterIdentitySummary,
|
||||
MasterAgentMemory,
|
||||
MasterAgentPromptPolicy,
|
||||
OpsFault,
|
||||
OpsRepairTicket,
|
||||
OpsRepairVerification,
|
||||
@@ -23,6 +25,7 @@ import type {
|
||||
ThreadContextAlert,
|
||||
ThreadContextSnapshot,
|
||||
ThreadHandoffPackage,
|
||||
UserMasterPrompt,
|
||||
} from "@/lib/boss-data";
|
||||
|
||||
export interface ContextIndicator {
|
||||
@@ -305,6 +308,30 @@ function getProjectMasterIdentity(state: BossState): MasterIdentitySummary {
|
||||
};
|
||||
}
|
||||
|
||||
export function getMasterAgentPromptPolicyView(state: BossState): MasterAgentPromptPolicy | null {
|
||||
return state.masterAgentPromptPolicy ?? null;
|
||||
}
|
||||
|
||||
export function getUserMasterPromptView(state: BossState, account: string): UserMasterPrompt | null {
|
||||
return state.userMasterPrompts.find((item) => item.account === account) ?? null;
|
||||
}
|
||||
|
||||
export function listUserMasterMemoriesView(
|
||||
state: BossState,
|
||||
account: string,
|
||||
options?: { includeArchived?: boolean },
|
||||
): MasterAgentMemory[] {
|
||||
const includeArchived = options?.includeArchived ?? false;
|
||||
return [...state.masterAgentMemories]
|
||||
.filter((memory) => memory.account === account && (includeArchived || !memory.archived))
|
||||
.sort((a, b) => {
|
||||
const aTime = Date.parse(a.lastUsedAt ?? a.updatedAt ?? a.createdAt) || 0;
|
||||
const bTime = Date.parse(b.lastUsedAt ?? b.updatedAt ?? b.createdAt) || 0;
|
||||
if (aTime !== bTime) return bTime - aTime;
|
||||
return b.memoryId.localeCompare(a.memoryId);
|
||||
});
|
||||
}
|
||||
|
||||
function threadViewsForProject(state: BossState, projectId: string) {
|
||||
return state.threadContextSnapshots
|
||||
.filter((snapshot) => snapshot.projectId === projectId)
|
||||
|
||||
Reference in New Issue
Block a user