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.js.timestamp-*
|
||||||
vite.config.ts.timestamp-*
|
vite.config.ts.timestamp-*
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
src/models.json
|
||||||
|
|||||||
@@ -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()}`;
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
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,
|
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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user