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:
Ani Tunturi
2026-03-26 09:09:37 -04:00
committed by Ani
parent 2a14e315e1
commit 328532d184
7 changed files with 37 additions and 1676 deletions

1
.gitignore vendored
View File

@@ -157,3 +157,4 @@ dist
vite.config.js.timestamp-* vite.config.js.timestamp-*
vite.config.ts.timestamp-* vite.config.ts.timestamp-*
__pycache__/ __pycache__/
src/models.json

View File

@@ -335,13 +335,20 @@ export async function createAgent(
// - memory_blocks: new blocks to create inline // - memory_blocks: new blocks to create inline
// - block_ids: references to existing blocks (for shared memory) // - block_ids: references to existing blocks (for shared memory)
const isSubagent = process.env.LETTA_CODE_AGENT_ROLE === "subagent"; const isSubagent = process.env.LETTA_CODE_AGENT_ROLE === "subagent";
const tags = ["origin:letta-code"];
if (isSubagent) { // Start with empty array, add user's tags first
tags.push("role:subagent"); let tags: string[] = [];
}
if (options.tags && Array.isArray(options.tags)) { if (options.tags && Array.isArray(options.tags)) {
tags.push(...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 = const agentDescription =
options.description ?? `Letta Code agent created in ${process.cwd()}`; options.description ?? `Letta Code agent created in ${process.cwd()}`;

View File

@@ -70,8 +70,10 @@ interface ExecutionState {
* Fetches from API and resolves to a known model ID * Fetches from API and resolves to a known model ID
*/ */
function getModelHandleFromAgent(agent: { 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 { }): string | null {
const handle = agent.llm_config?.handle;
if (handle) return handle;
const endpoint = agent.llm_config?.model_endpoint_type; const endpoint = agent.llm_config?.model_endpoint_type;
const model = agent.llm_config?.model; const model = agent.llm_config?.model;
if (endpoint && model) { if (endpoint && model) {

View File

@@ -2466,12 +2466,12 @@ async function runBidirectionalMode(
const { const {
buildAutoReflectionPayload, buildAutoReflectionPayload,
finalizeAutoReflectionPayload, finalizeAutoReflectionPayload,
buildParentMemorySnapshot,
buildReflectionSubagentPrompt, buildReflectionSubagentPrompt,
} = await import("./cli/helpers/reflectionTranscript"); } = await import("./cli/helpers/reflectionTranscript");
const { getMemoryFilesystemRoot } = await import( const { getMemoryFilesystemRoot } = await import(
"./agent/memoryFilesystem" "./agent/memoryFilesystem"
); );
const { recompileAgentSystemPrompt } = await import("./agent/modify");
const autoPayload = await buildAutoReflectionPayload( const autoPayload = await buildAutoReflectionPayload(
agent.id, agent.id,
@@ -2486,7 +2486,16 @@ async function runBidirectionalMode(
} }
const memoryDir = getMemoryFilesystemRoot(agent.id); 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({ const reflectionPrompt = buildReflectionSubagentPrompt({
transcriptPath: autoPayload.payloadPath, transcriptPath: autoPayload.payloadPath,
memoryDir, memoryDir,
@@ -2495,11 +2504,20 @@ async function runBidirectionalMode(
}); });
const { spawnBackgroundSubagentTask } = await import("./tools/impl/Task"); 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({ spawnBackgroundSubagentTask({
subagentType: "reflection", subagentType: "reflection",
prompt: reflectionPrompt, prompt: reflectionPrompt,
description: "Reflect on recent conversations", description: "Reflect on recent conversations",
silentCompletion: true, silentCompletion: true,
...(conscienceConversationId
? { existingConversationId: conscienceConversationId }
: conscienceAgentId
? { existingAgentId: conscienceAgentId }
: {}),
onComplete: async ({ success, error }) => { onComplete: async ({ success, error }) => {
await finalizeAutoReflectionPayload( await finalizeAutoReflectionPayload(
agent.id, agent.id,

View File

@@ -1875,7 +1875,7 @@ async function main(): Promise<void> {
// so their prompts are left untouched by auto-heal. // so their prompts are left untouched by auto-heal.
if ( if (
!storedPreset && !storedPreset &&
agent.tags?.includes("origin:letta-code") && (agent.tags?.includes("origin:letta-code") || agent.tags?.includes("origin:lettabot")) &&
!agent.tags?.includes("role:subagent") !agent.tags?.includes("role:subagent")
) { ) {
storedPreset = "custom"; storedPreset = "custom";

File diff suppressed because it is too large Load Diff

View File

@@ -226,7 +226,7 @@ export function shouldClearPersistedToolRules(
agent: AgentWithToolsAndRules, agent: AgentWithToolsAndRules,
): boolean { ): boolean {
return ( 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 (agent.tool_rules?.length ?? 0) > 0
); );
} }