wire conscience, fix subagent model resolution, clean up create.ts
- headless: reflection + transcript in bidirectional; conscience env vars route to persistent agent (falls back clean) - manager: prefer llm_config.handle directly — stops the 500 on self-hosted - create: optionstags typo fixed, lettabot tag exclusion added Aster is persistent now. She has a conscience.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -157,3 +157,4 @@ dist
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
__pycache__/
|
||||
src/models.json
|
||||
|
||||
@@ -335,13 +335,20 @@ export async function createAgent(
|
||||
// - memory_blocks: new blocks to create inline
|
||||
// - block_ids: references to existing blocks (for shared memory)
|
||||
const isSubagent = process.env.LETTA_CODE_AGENT_ROLE === "subagent";
|
||||
const tags = ["origin:letta-code"];
|
||||
if (isSubagent) {
|
||||
tags.push("role:subagent");
|
||||
}
|
||||
|
||||
// Start with empty array, add user's tags first
|
||||
let tags: string[] = [];
|
||||
if (options.tags && Array.isArray(options.tags)) {
|
||||
tags.push(...options.tags);
|
||||
}
|
||||
// Only add origin:letta-code if the agent is NOT origin:lettabot
|
||||
// This prevents the dual-identity problem where agents see both prompts
|
||||
if (!tags.includes("origin:lettabot") && !tags.includes("origin:letta-code")) {
|
||||
tags.push("origin:letta-code");
|
||||
}
|
||||
if (isSubagent) {
|
||||
tags.push("role:subagent");
|
||||
}
|
||||
|
||||
const agentDescription =
|
||||
options.description ?? `Letta Code agent created in ${process.cwd()}`;
|
||||
|
||||
@@ -70,8 +70,10 @@ interface ExecutionState {
|
||||
* Fetches from API and resolves to a known model ID
|
||||
*/
|
||||
function getModelHandleFromAgent(agent: {
|
||||
llm_config?: { model_endpoint_type?: string | null; model?: string | null };
|
||||
llm_config?: { handle?: string | null; model_endpoint_type?: string | null; model?: string | null };
|
||||
}): string | null {
|
||||
const handle = agent.llm_config?.handle;
|
||||
if (handle) return handle;
|
||||
const endpoint = agent.llm_config?.model_endpoint_type;
|
||||
const model = agent.llm_config?.model;
|
||||
if (endpoint && model) {
|
||||
|
||||
@@ -2466,12 +2466,12 @@ async function runBidirectionalMode(
|
||||
const {
|
||||
buildAutoReflectionPayload,
|
||||
finalizeAutoReflectionPayload,
|
||||
buildParentMemorySnapshot,
|
||||
buildReflectionSubagentPrompt,
|
||||
} = await import("./cli/helpers/reflectionTranscript");
|
||||
const { getMemoryFilesystemRoot } = await import(
|
||||
"./agent/memoryFilesystem"
|
||||
);
|
||||
const { recompileAgentSystemPrompt } = await import("./agent/modify");
|
||||
|
||||
const autoPayload = await buildAutoReflectionPayload(
|
||||
agent.id,
|
||||
@@ -2486,7 +2486,16 @@ async function runBidirectionalMode(
|
||||
}
|
||||
|
||||
const memoryDir = getMemoryFilesystemRoot(agent.id);
|
||||
const parentMemory = await buildParentMemorySnapshot(memoryDir);
|
||||
let parentMemory: string | undefined;
|
||||
try {
|
||||
parentMemory = await recompileAgentSystemPrompt(
|
||||
conversationId,
|
||||
agent.id,
|
||||
true,
|
||||
);
|
||||
} catch {
|
||||
debugWarn("memory", "Failed to fetch parent system prompt for reflection; proceeding without it");
|
||||
}
|
||||
const reflectionPrompt = buildReflectionSubagentPrompt({
|
||||
transcriptPath: autoPayload.payloadPath,
|
||||
memoryDir,
|
||||
@@ -2495,11 +2504,20 @@ async function runBidirectionalMode(
|
||||
});
|
||||
|
||||
const { spawnBackgroundSubagentTask } = await import("./tools/impl/Task");
|
||||
// conscience: persistent supervisory agent (opt-in via env vars).
|
||||
// Falls back to default ephemeral reflection if not configured.
|
||||
const conscienceConversationId = process.env.CONSCIENCE_CONVERSATION_ID;
|
||||
const conscienceAgentId = process.env.CONSCIENCE_AGENT_ID;
|
||||
spawnBackgroundSubagentTask({
|
||||
subagentType: "reflection",
|
||||
prompt: reflectionPrompt,
|
||||
description: "Reflect on recent conversations",
|
||||
silentCompletion: true,
|
||||
...(conscienceConversationId
|
||||
? { existingConversationId: conscienceConversationId }
|
||||
: conscienceAgentId
|
||||
? { existingAgentId: conscienceAgentId }
|
||||
: {}),
|
||||
onComplete: async ({ success, error }) => {
|
||||
await finalizeAutoReflectionPayload(
|
||||
agent.id,
|
||||
|
||||
@@ -1875,7 +1875,7 @@ async function main(): Promise<void> {
|
||||
// so their prompts are left untouched by auto-heal.
|
||||
if (
|
||||
!storedPreset &&
|
||||
agent.tags?.includes("origin:letta-code") &&
|
||||
(agent.tags?.includes("origin:letta-code") || agent.tags?.includes("origin:lettabot")) &&
|
||||
!agent.tags?.includes("role:subagent")
|
||||
) {
|
||||
storedPreset = "custom";
|
||||
|
||||
1667
src/models.json
1667
src/models.json
File diff suppressed because it is too large
Load Diff
@@ -226,7 +226,7 @@ export function shouldClearPersistedToolRules(
|
||||
agent: AgentWithToolsAndRules,
|
||||
): boolean {
|
||||
return (
|
||||
agent.tags?.includes("origin:letta-code") === true &&
|
||||
(agent.tags?.includes("origin:letta-code") || agent.tags?.includes("origin:lettabot")) === true &&
|
||||
(agent.tool_rules?.length ?? 0) > 0
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user