feat: add system prompt and memory block configuration for headless mode (#450)
Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
2
build.js
2
build.js
@@ -78,7 +78,7 @@ if (existsSync(bundledSkillsSrc)) {
|
|||||||
// Generate type declarations for wire types export
|
// Generate type declarations for wire types export
|
||||||
console.log("📝 Generating type declarations...");
|
console.log("📝 Generating type declarations...");
|
||||||
await Bun.$`bunx tsc -p tsconfig.types.json`;
|
await Bun.$`bunx tsc -p tsconfig.types.json`;
|
||||||
console.log(" Output: dist/types/wire.d.ts");
|
console.log(" Output: dist/types/protocol.d.ts");
|
||||||
|
|
||||||
console.log("✅ Build complete!");
|
console.log("✅ Build complete!");
|
||||||
console.log(` Output: letta.js`);
|
console.log(` Output: letta.js`);
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
],
|
],
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./letta.js",
|
".": "./letta.js",
|
||||||
"./wire-types": {
|
"./protocol": {
|
||||||
"types": "./dist/types/wire.d.ts"
|
"types": "./dist/types/protocol.d.ts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import { discoverSkills, formatSkillsForMemory, SKILLS_DIR } from "./skills";
|
|||||||
*/
|
*/
|
||||||
export interface BlockProvenance {
|
export interface BlockProvenance {
|
||||||
label: string;
|
label: string;
|
||||||
source: "global" | "project" | "new";
|
source: "global" | "project" | "new" | "shared";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,24 +45,75 @@ export interface CreateAgentResult {
|
|||||||
provenance: AgentProvenance;
|
provenance: AgentProvenance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CreateAgentOptions {
|
||||||
|
name?: string;
|
||||||
|
model?: string;
|
||||||
|
embeddingModel?: string;
|
||||||
|
updateArgs?: Record<string, unknown>;
|
||||||
|
skillsDirectory?: string;
|
||||||
|
parallelToolCalls?: boolean;
|
||||||
|
enableSleeptime?: boolean;
|
||||||
|
/** System prompt preset (e.g., 'default', 'letta-claude', 'letta-codex') */
|
||||||
|
systemPromptPreset?: string;
|
||||||
|
/** Raw system prompt string (mutually exclusive with systemPromptPreset) */
|
||||||
|
systemPromptCustom?: string;
|
||||||
|
/** Additional text to append to the resolved system prompt */
|
||||||
|
systemPromptAppend?: string;
|
||||||
|
/** Block labels to initialize (from default blocks) */
|
||||||
|
initBlocks?: string[];
|
||||||
|
/** Base tools to include */
|
||||||
|
baseTools?: string[];
|
||||||
|
/** Custom memory blocks (overrides default blocks) */
|
||||||
|
memoryBlocks?: Array<
|
||||||
|
{ label: string; value: string; description?: string } | { blockId: string }
|
||||||
|
>;
|
||||||
|
/** Override values for preset blocks (label → value) */
|
||||||
|
blockValues?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
export async function createAgent(
|
export async function createAgent(
|
||||||
name = DEFAULT_AGENT_NAME,
|
nameOrOptions: string | CreateAgentOptions = DEFAULT_AGENT_NAME,
|
||||||
model?: string,
|
model?: string,
|
||||||
embeddingModel = "openai/text-embedding-3-small",
|
embeddingModel = "openai/text-embedding-3-small",
|
||||||
updateArgs?: Record<string, unknown>,
|
updateArgs?: Record<string, unknown>,
|
||||||
skillsDirectory?: string,
|
skillsDirectory?: string,
|
||||||
parallelToolCalls = true,
|
parallelToolCalls = true,
|
||||||
enableSleeptime = false,
|
enableSleeptime = false,
|
||||||
systemPromptId?: string,
|
systemPromptPreset?: string,
|
||||||
initBlocks?: string[],
|
initBlocks?: string[],
|
||||||
baseTools?: string[],
|
baseTools?: string[],
|
||||||
) {
|
) {
|
||||||
|
// Support both old positional args and new options object
|
||||||
|
let options: CreateAgentOptions;
|
||||||
|
if (typeof nameOrOptions === "object") {
|
||||||
|
options = nameOrOptions;
|
||||||
|
} else {
|
||||||
|
options = {
|
||||||
|
name: nameOrOptions,
|
||||||
|
model,
|
||||||
|
embeddingModel,
|
||||||
|
updateArgs,
|
||||||
|
skillsDirectory,
|
||||||
|
parallelToolCalls,
|
||||||
|
enableSleeptime,
|
||||||
|
systemPromptPreset,
|
||||||
|
initBlocks,
|
||||||
|
baseTools,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = options.name ?? DEFAULT_AGENT_NAME;
|
||||||
|
const embeddingModelVal =
|
||||||
|
options.embeddingModel ?? "openai/text-embedding-3-small";
|
||||||
|
const parallelToolCallsVal = options.parallelToolCalls ?? true;
|
||||||
|
const enableSleeptimeVal = options.enableSleeptime ?? false;
|
||||||
|
|
||||||
// Resolve model identifier to handle
|
// Resolve model identifier to handle
|
||||||
let modelHandle: string;
|
let modelHandle: string;
|
||||||
if (model) {
|
if (options.model) {
|
||||||
const resolved = resolveModel(model);
|
const resolved = resolveModel(options.model);
|
||||||
if (!resolved) {
|
if (!resolved) {
|
||||||
console.error(`Error: Unknown model "${model}"`);
|
console.error(`Error: Unknown model "${options.model}"`);
|
||||||
console.error("Available models:");
|
console.error("Available models:");
|
||||||
console.error(formatAvailableModels());
|
console.error(formatAvailableModels());
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@@ -79,14 +130,12 @@ export async function createAgent(
|
|||||||
// Map internal names to server names so the agent sees the correct tool names
|
// Map internal names to server names so the agent sees the correct tool names
|
||||||
const { getServerToolName } = await import("../tools/manager");
|
const { getServerToolName } = await import("../tools/manager");
|
||||||
const internalToolNames = getToolNames();
|
const internalToolNames = getToolNames();
|
||||||
const serverToolNames = internalToolNames.map((name) =>
|
const serverToolNames = internalToolNames.map((n) => getServerToolName(n));
|
||||||
getServerToolName(name),
|
|
||||||
);
|
|
||||||
|
|
||||||
const baseMemoryTool = modelHandle.startsWith("openai/gpt-5")
|
const baseMemoryTool = modelHandle.startsWith("openai/gpt-5")
|
||||||
? "memory_apply_patch"
|
? "memory_apply_patch"
|
||||||
: "memory";
|
: "memory";
|
||||||
const defaultBaseTools = baseTools ?? [
|
const defaultBaseTools = options.baseTools ?? [
|
||||||
baseMemoryTool,
|
baseMemoryTool,
|
||||||
"web_search",
|
"web_search",
|
||||||
"conversation_search",
|
"conversation_search",
|
||||||
@@ -122,37 +171,79 @@ export async function createAgent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load memory blocks from .mdx files
|
// Determine which memory blocks to use:
|
||||||
const defaultMemoryBlocks =
|
// 1. If options.memoryBlocks is provided, use those (custom blocks and/or block references)
|
||||||
initBlocks && initBlocks.length === 0 ? [] : await getDefaultMemoryBlocks();
|
// 2. Otherwise, use default blocks filtered by options.initBlocks
|
||||||
|
|
||||||
// Optional filter: only initialize a subset of memory blocks on creation
|
// Separate block references from blocks to create
|
||||||
const allowedBlockLabels = initBlocks
|
const referencedBlockIds: string[] = [];
|
||||||
? new Set(
|
let filteredMemoryBlocks: Array<{
|
||||||
initBlocks.map((name) => name.trim()).filter((name) => name.length > 0),
|
label: string;
|
||||||
)
|
value: string;
|
||||||
: undefined;
|
description?: string | null;
|
||||||
|
limit?: number;
|
||||||
|
}>;
|
||||||
|
|
||||||
if (allowedBlockLabels && allowedBlockLabels.size > 0) {
|
if (options.memoryBlocks !== undefined) {
|
||||||
const knownLabels = new Set(defaultMemoryBlocks.map((b) => b.label));
|
// Separate blockId references from CreateBlock items
|
||||||
for (const label of Array.from(allowedBlockLabels)) {
|
const createBlocks: typeof filteredMemoryBlocks = [];
|
||||||
if (!knownLabels.has(label)) {
|
for (const item of options.memoryBlocks) {
|
||||||
|
if ("blockId" in item) {
|
||||||
|
referencedBlockIds.push(item.blockId);
|
||||||
|
} else {
|
||||||
|
createBlocks.push(item as (typeof filteredMemoryBlocks)[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filteredMemoryBlocks = createBlocks;
|
||||||
|
} else {
|
||||||
|
// Load memory blocks from .mdx files
|
||||||
|
const defaultMemoryBlocks =
|
||||||
|
options.initBlocks && options.initBlocks.length === 0
|
||||||
|
? []
|
||||||
|
: await getDefaultMemoryBlocks();
|
||||||
|
|
||||||
|
// Optional filter: only initialize a subset of memory blocks on creation
|
||||||
|
const allowedBlockLabels = options.initBlocks
|
||||||
|
? new Set(
|
||||||
|
options.initBlocks.map((n) => n.trim()).filter((n) => n.length > 0),
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (allowedBlockLabels && allowedBlockLabels.size > 0) {
|
||||||
|
const knownLabels = new Set(defaultMemoryBlocks.map((b) => b.label));
|
||||||
|
for (const label of Array.from(allowedBlockLabels)) {
|
||||||
|
if (!knownLabels.has(label)) {
|
||||||
|
console.warn(
|
||||||
|
`Ignoring unknown init block "${label}". Valid blocks: ${Array.from(knownLabels).join(", ")}`,
|
||||||
|
);
|
||||||
|
allowedBlockLabels.delete(label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredMemoryBlocks =
|
||||||
|
allowedBlockLabels && allowedBlockLabels.size > 0
|
||||||
|
? defaultMemoryBlocks.filter((b) => allowedBlockLabels.has(b.label))
|
||||||
|
: defaultMemoryBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply blockValues overrides to preset blocks
|
||||||
|
if (options.blockValues) {
|
||||||
|
for (const [label, value] of Object.entries(options.blockValues)) {
|
||||||
|
const block = filteredMemoryBlocks.find((b) => b.label === label);
|
||||||
|
if (block) {
|
||||||
|
block.value = value;
|
||||||
|
} else {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Ignoring unknown init block "${label}". Valid blocks: ${Array.from(knownLabels).join(", ")}`,
|
`Ignoring --block-value for "${label}" - block not included in memory config`,
|
||||||
);
|
);
|
||||||
allowedBlockLabels.delete(label);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const filteredMemoryBlocks =
|
|
||||||
allowedBlockLabels && allowedBlockLabels.size > 0
|
|
||||||
? defaultMemoryBlocks.filter((b) => allowedBlockLabels.has(b.label))
|
|
||||||
: defaultMemoryBlocks;
|
|
||||||
|
|
||||||
// Resolve absolute path for skills directory
|
// Resolve absolute path for skills directory
|
||||||
const resolvedSkillsDirectory =
|
const resolvedSkillsDirectory =
|
||||||
skillsDirectory || join(process.cwd(), SKILLS_DIR);
|
options.skillsDirectory || join(process.cwd(), SKILLS_DIR);
|
||||||
|
|
||||||
// Discover skills from .skills directory and populate skills memory block
|
// Discover skills from .skills directory and populate skills memory block
|
||||||
try {
|
try {
|
||||||
@@ -198,12 +289,31 @@ export async function createAgent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add any referenced block IDs (existing blocks to attach)
|
||||||
|
for (const blockId of referencedBlockIds) {
|
||||||
|
blockIds.push(blockId);
|
||||||
|
blockProvenance.push({ label: blockId, source: "shared" });
|
||||||
|
}
|
||||||
|
|
||||||
// Get the model's context window from its configuration
|
// Get the model's context window from its configuration
|
||||||
const modelUpdateArgs = getModelUpdateArgs(modelHandle);
|
const modelUpdateArgs = getModelUpdateArgs(modelHandle);
|
||||||
const contextWindow = (modelUpdateArgs?.context_window as number) || 200_000;
|
const contextWindow = (modelUpdateArgs?.context_window as number) || 200_000;
|
||||||
|
|
||||||
// Resolve system prompt ID to content
|
// Resolve system prompt content:
|
||||||
const systemPromptContent = await resolveSystemPrompt(systemPromptId);
|
// 1. If systemPromptCustom is provided, use it as-is
|
||||||
|
// 2. Otherwise, resolve systemPromptPreset to content
|
||||||
|
// 3. If systemPromptAppend is provided, append it to the result
|
||||||
|
let systemPromptContent: string;
|
||||||
|
if (options.systemPromptCustom) {
|
||||||
|
systemPromptContent = options.systemPromptCustom;
|
||||||
|
} else {
|
||||||
|
systemPromptContent = await resolveSystemPrompt(options.systemPromptPreset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append additional instructions if provided
|
||||||
|
if (options.systemPromptAppend) {
|
||||||
|
systemPromptContent = `${systemPromptContent}\n\n${options.systemPromptAppend}`;
|
||||||
|
}
|
||||||
|
|
||||||
// Create agent with all block IDs (existing + newly created)
|
// Create agent with all block IDs (existing + newly created)
|
||||||
const tags = ["origin:letta-code"];
|
const tags = ["origin:letta-code"];
|
||||||
@@ -216,7 +326,7 @@ export async function createAgent(
|
|||||||
system: systemPromptContent,
|
system: systemPromptContent,
|
||||||
name,
|
name,
|
||||||
description: `Letta Code agent created in ${process.cwd()}`,
|
description: `Letta Code agent created in ${process.cwd()}`,
|
||||||
embedding: embeddingModel,
|
embedding: embeddingModelVal,
|
||||||
model: modelHandle,
|
model: modelHandle,
|
||||||
context_window_limit: contextWindow,
|
context_window_limit: contextWindow,
|
||||||
tools: toolNames,
|
tools: toolNames,
|
||||||
@@ -226,8 +336,8 @@ export async function createAgent(
|
|||||||
include_base_tools: false,
|
include_base_tools: false,
|
||||||
include_base_tool_rules: false,
|
include_base_tool_rules: false,
|
||||||
initial_message_sequence: [],
|
initial_message_sequence: [],
|
||||||
parallel_tool_calls: parallelToolCalls,
|
parallel_tool_calls: parallelToolCallsVal,
|
||||||
enable_sleeptime: enableSleeptime,
|
enable_sleeptime: enableSleeptimeVal,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Note: Preflight check above falls back to 'memory' when 'memory_apply_patch' is unavailable.
|
// Note: Preflight check above falls back to 'memory' when 'memory_apply_patch' is unavailable.
|
||||||
@@ -235,8 +345,8 @@ export async function createAgent(
|
|||||||
// Apply updateArgs if provided (e.g., context_window, reasoning_effort, verbosity, etc.)
|
// Apply updateArgs if provided (e.g., context_window, reasoning_effort, verbosity, etc.)
|
||||||
// We intentionally pass context_window through so updateAgentLLMConfig can set
|
// We intentionally pass context_window through so updateAgentLLMConfig can set
|
||||||
// context_window_limit using the latest server API, avoiding any fallback.
|
// context_window_limit using the latest server API, avoiding any fallback.
|
||||||
if (updateArgs && Object.keys(updateArgs).length > 0) {
|
if (options.updateArgs && Object.keys(options.updateArgs).length > 0) {
|
||||||
await updateAgentLLMConfig(agent.id, modelHandle, updateArgs);
|
await updateAgentLLMConfig(agent.id, modelHandle, options.updateArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always retrieve the agent to ensure we get the full state with populated memory blocks
|
// Always retrieve the agent to ensure we get the full state with populated memory blocks
|
||||||
@@ -245,7 +355,7 @@ export async function createAgent(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Update persona block for sleeptime agent
|
// Update persona block for sleeptime agent
|
||||||
if (enableSleeptime && fullAgent.managed_group) {
|
if (enableSleeptimeVal && fullAgent.managed_group) {
|
||||||
// Find the sleeptime agent in the managed group by checking agent_type
|
// Find the sleeptime agent in the managed group by checking agent_type
|
||||||
for (const groupAgentId of fullAgent.managed_group.agent_ids) {
|
for (const groupAgentId of fullAgent.managed_group.agent_ids) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -56,47 +56,48 @@ export const SYSTEM_PROMPTS: SystemPromptOption[] = [
|
|||||||
{
|
{
|
||||||
id: "default",
|
id: "default",
|
||||||
label: "Default",
|
label: "Default",
|
||||||
description: "Standard Letta Code system prompt (Claude-optimized)",
|
description: "Alias for letta-claude",
|
||||||
content: lettaAnthropicPrompt,
|
content: lettaAnthropicPrompt,
|
||||||
isDefault: true,
|
isDefault: true,
|
||||||
isFeatured: true,
|
isFeatured: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "legacy",
|
id: "letta-claude",
|
||||||
label: "Legacy",
|
label: "Letta Claude",
|
||||||
description: "Original system prompt",
|
description: "Full Letta Code system prompt (Claude-optimized)",
|
||||||
content: systemPrompt,
|
content: lettaAnthropicPrompt,
|
||||||
|
isFeatured: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "letta-codex",
|
id: "letta-codex",
|
||||||
label: "Codex",
|
label: "Letta Codex",
|
||||||
description: "For Codex models",
|
description: "Full Letta Code system prompt (Codex-optimized)",
|
||||||
content: lettaCodexPrompt,
|
content: lettaCodexPrompt,
|
||||||
isFeatured: true,
|
isFeatured: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "letta-gemini",
|
id: "letta-gemini",
|
||||||
label: "Gemini",
|
label: "Letta Gemini",
|
||||||
description: "For Gemini models",
|
description: "Full Letta Code system prompt (Gemini-optimized)",
|
||||||
content: lettaGeminiPrompt,
|
content: lettaGeminiPrompt,
|
||||||
isFeatured: true,
|
isFeatured: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "anthropic",
|
id: "claude",
|
||||||
label: "Claude (basic)",
|
label: "Claude (basic)",
|
||||||
description: "For Claude models (no skills/memory instructions)",
|
description: "Basic Claude prompt (no skills/memory instructions)",
|
||||||
content: anthropicPrompt,
|
content: anthropicPrompt,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "codex",
|
id: "codex",
|
||||||
label: "Codex (basic)",
|
label: "Codex (basic)",
|
||||||
description: "For Codex models (no skills/memory instructions)",
|
description: "Basic Codex prompt (no skills/memory instructions)",
|
||||||
content: codexPrompt,
|
content: codexPrompt,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "gemini",
|
id: "gemini",
|
||||||
label: "Gemini (basic)",
|
label: "Gemini (basic)",
|
||||||
description: "For Gemini models (no skills/memory instructions)",
|
description: "Basic Gemini prompt (no skills/memory instructions)",
|
||||||
content: geminiPrompt,
|
content: geminiPrompt,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -109,19 +110,19 @@ export const SYSTEM_PROMPTS: SystemPromptOption[] = [
|
|||||||
* 2. If it matches a subagent name, use that subagent's system prompt
|
* 2. If it matches a subagent name, use that subagent's system prompt
|
||||||
* 3. Otherwise, use the default system prompt
|
* 3. Otherwise, use the default system prompt
|
||||||
*
|
*
|
||||||
* @param systemPromptId - The system prompt ID (e.g., "codex") or subagent name (e.g., "explore")
|
* @param systemPromptPreset - The system prompt preset (e.g., "letta-claude") or subagent name (e.g., "explore")
|
||||||
* @returns The resolved system prompt content
|
* @returns The resolved system prompt content
|
||||||
*/
|
*/
|
||||||
export async function resolveSystemPrompt(
|
export async function resolveSystemPrompt(
|
||||||
systemPromptId: string | undefined,
|
systemPromptPreset: string | undefined,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
// No input - use default
|
// No input - use default
|
||||||
if (!systemPromptId) {
|
if (!systemPromptPreset) {
|
||||||
return SYSTEM_PROMPT;
|
return SYSTEM_PROMPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Check if it matches a system prompt ID
|
// 1. Check if it matches a system prompt ID
|
||||||
const matchedPrompt = SYSTEM_PROMPTS.find((p) => p.id === systemPromptId);
|
const matchedPrompt = SYSTEM_PROMPTS.find((p) => p.id === systemPromptPreset);
|
||||||
if (matchedPrompt) {
|
if (matchedPrompt) {
|
||||||
return matchedPrompt.content;
|
return matchedPrompt.content;
|
||||||
}
|
}
|
||||||
@@ -129,7 +130,7 @@ export async function resolveSystemPrompt(
|
|||||||
// 2. Check if it matches a subagent name
|
// 2. Check if it matches a subagent name
|
||||||
const { getAllSubagentConfigs } = await import("./subagents");
|
const { getAllSubagentConfigs } = await import("./subagents");
|
||||||
const subagentConfigs = await getAllSubagentConfigs();
|
const subagentConfigs = await getAllSubagentConfigs();
|
||||||
const matchedSubagent = subagentConfigs[systemPromptId];
|
const matchedSubagent = subagentConfigs[systemPromptPreset];
|
||||||
if (matchedSubagent) {
|
if (matchedSubagent) {
|
||||||
return matchedSubagent.systemPrompt;
|
return matchedSubagent.systemPrompt;
|
||||||
}
|
}
|
||||||
|
|||||||
174
src/headless.ts
174
src/headless.ts
@@ -48,7 +48,7 @@ import type {
|
|||||||
RetryMessage,
|
RetryMessage,
|
||||||
StreamEvent,
|
StreamEvent,
|
||||||
SystemInitMessage,
|
SystemInitMessage,
|
||||||
} from "./types/wire";
|
} from "./types/protocol";
|
||||||
|
|
||||||
// Maximum number of times to retry a turn when the backend
|
// Maximum number of times to retry a turn when the backend
|
||||||
// reports an `llm_api_error` stop reason. This helps smooth
|
// reports an `llm_api_error` stop reason. This helps smooth
|
||||||
@@ -74,6 +74,10 @@ export async function handleHeadlessCommand(
|
|||||||
agent: { type: "string", short: "a" },
|
agent: { type: "string", short: "a" },
|
||||||
model: { type: "string", short: "m" },
|
model: { type: "string", short: "m" },
|
||||||
system: { type: "string", short: "s" },
|
system: { type: "string", short: "s" },
|
||||||
|
"system-custom": { type: "string" },
|
||||||
|
"system-append": { type: "string" },
|
||||||
|
"memory-blocks": { type: "string" },
|
||||||
|
"block-value": { type: "string", multiple: true },
|
||||||
toolset: { type: "string" },
|
toolset: { type: "string" },
|
||||||
prompt: { type: "boolean", short: "p" },
|
prompt: { type: "boolean", short: "p" },
|
||||||
"output-format": { type: "string" },
|
"output-format": { type: "string" },
|
||||||
@@ -169,7 +173,11 @@ export async function handleHeadlessCommand(
|
|||||||
const specifiedAgentId = values.agent as string | undefined;
|
const specifiedAgentId = values.agent as string | undefined;
|
||||||
const shouldContinue = values.continue as boolean | undefined;
|
const shouldContinue = values.continue as boolean | undefined;
|
||||||
const forceNew = values.new as boolean | undefined;
|
const forceNew = values.new as boolean | undefined;
|
||||||
const systemPromptId = values.system as string | undefined;
|
const systemPromptPreset = values.system as string | undefined;
|
||||||
|
const systemCustom = values["system-custom"] as string | undefined;
|
||||||
|
const systemAppend = values["system-append"] as string | undefined;
|
||||||
|
const memoryBlocksJson = values["memory-blocks"] as string | undefined;
|
||||||
|
const blockValueArgs = values["block-value"] as string[] | undefined;
|
||||||
const initBlocksRaw = values["init-blocks"] as string | undefined;
|
const initBlocksRaw = values["init-blocks"] as string | undefined;
|
||||||
const baseToolsRaw = values["base-tools"] as string | undefined;
|
const baseToolsRaw = values["base-tools"] as string | undefined;
|
||||||
const sleeptimeFlag = (values.sleeptime as boolean | undefined) ?? undefined;
|
const sleeptimeFlag = (values.sleeptime as boolean | undefined) ?? undefined;
|
||||||
@@ -231,6 +239,84 @@ export async function handleHeadlessCommand(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate system prompt options (--system and --system-custom are mutually exclusive)
|
||||||
|
if (systemPromptPreset && systemCustom) {
|
||||||
|
console.error(
|
||||||
|
"Error: --system and --system-custom are mutually exclusive. Use one or the other.",
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse memory blocks JSON if provided
|
||||||
|
// Supports two formats:
|
||||||
|
// - CreateBlock: { label: string, value: string, description?: string }
|
||||||
|
// - BlockReference: { blockId: string }
|
||||||
|
let memoryBlocks:
|
||||||
|
| Array<
|
||||||
|
| { label: string; value: string; description?: string }
|
||||||
|
| { blockId: string }
|
||||||
|
>
|
||||||
|
| undefined;
|
||||||
|
if (memoryBlocksJson !== undefined) {
|
||||||
|
if (!forceNew) {
|
||||||
|
console.error(
|
||||||
|
"Error: --memory-blocks can only be used together with --new to provide initial memory blocks.",
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
memoryBlocks = JSON.parse(memoryBlocksJson);
|
||||||
|
if (!Array.isArray(memoryBlocks)) {
|
||||||
|
throw new Error("memory-blocks must be a JSON array");
|
||||||
|
}
|
||||||
|
// Validate each block has required fields
|
||||||
|
for (const block of memoryBlocks) {
|
||||||
|
const hasBlockId =
|
||||||
|
"blockId" in block && typeof block.blockId === "string";
|
||||||
|
const hasLabelValue =
|
||||||
|
"label" in block &&
|
||||||
|
"value" in block &&
|
||||||
|
typeof block.label === "string" &&
|
||||||
|
typeof block.value === "string";
|
||||||
|
|
||||||
|
if (!hasBlockId && !hasLabelValue) {
|
||||||
|
throw new Error(
|
||||||
|
"Each memory block must have either 'blockId' (string) or 'label' and 'value' (strings)",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`Error: Invalid --memory-blocks JSON: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse --block-value args (format: label=value)
|
||||||
|
let blockValues: Record<string, string> | undefined;
|
||||||
|
if (blockValueArgs && blockValueArgs.length > 0) {
|
||||||
|
if (!forceNew) {
|
||||||
|
console.error(
|
||||||
|
"Error: --block-value can only be used together with --new to set block values.",
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
blockValues = {};
|
||||||
|
for (const arg of blockValueArgs) {
|
||||||
|
const eqIndex = arg.indexOf("=");
|
||||||
|
if (eqIndex === -1) {
|
||||||
|
console.error(
|
||||||
|
`Error: Invalid --block-value format "${arg}". Expected format: label=value`,
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
const label = arg.slice(0, eqIndex);
|
||||||
|
const value = arg.slice(eqIndex + 1);
|
||||||
|
blockValues[label] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Priority 1: Import from AgentFile template
|
// Priority 1: Import from AgentFile template
|
||||||
if (fromAfFile) {
|
if (fromAfFile) {
|
||||||
const { importAgentFromFile } = await import("./agent/import");
|
const { importAgentFromFile } = await import("./agent/import");
|
||||||
@@ -254,36 +340,28 @@ export async function handleHeadlessCommand(
|
|||||||
// Priority 3: Check if --new flag was passed (skip all resume logic)
|
// Priority 3: Check if --new flag was passed (skip all resume logic)
|
||||||
if (!agent && forceNew) {
|
if (!agent && forceNew) {
|
||||||
const updateArgs = getModelUpdateArgs(model);
|
const updateArgs = getModelUpdateArgs(model);
|
||||||
|
const createOptions = {
|
||||||
|
model,
|
||||||
|
updateArgs,
|
||||||
|
skillsDirectory,
|
||||||
|
parallelToolCalls: true,
|
||||||
|
enableSleeptime: sleeptimeFlag ?? settings.enableSleeptime,
|
||||||
|
systemPromptPreset,
|
||||||
|
systemPromptCustom: systemCustom,
|
||||||
|
systemPromptAppend: systemAppend,
|
||||||
|
initBlocks,
|
||||||
|
baseTools,
|
||||||
|
memoryBlocks,
|
||||||
|
blockValues,
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
const result = await createAgent(
|
const result = await createAgent(createOptions);
|
||||||
undefined,
|
|
||||||
model,
|
|
||||||
undefined,
|
|
||||||
updateArgs,
|
|
||||||
skillsDirectory,
|
|
||||||
true, // parallelToolCalls always enabled
|
|
||||||
sleeptimeFlag ?? settings.enableSleeptime,
|
|
||||||
systemPromptId,
|
|
||||||
initBlocks,
|
|
||||||
baseTools,
|
|
||||||
);
|
|
||||||
agent = result.agent;
|
agent = result.agent;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (isToolsNotFoundError(err)) {
|
if (isToolsNotFoundError(err)) {
|
||||||
console.warn("Tools missing on server, re-uploading and retrying...");
|
console.warn("Tools missing on server, re-uploading and retrying...");
|
||||||
await forceUpsertTools(client, baseURL);
|
await forceUpsertTools(client, baseURL);
|
||||||
const result = await createAgent(
|
const result = await createAgent(createOptions);
|
||||||
undefined,
|
|
||||||
model,
|
|
||||||
undefined,
|
|
||||||
updateArgs,
|
|
||||||
skillsDirectory,
|
|
||||||
true,
|
|
||||||
sleeptimeFlag ?? settings.enableSleeptime,
|
|
||||||
systemPromptId,
|
|
||||||
initBlocks,
|
|
||||||
baseTools,
|
|
||||||
);
|
|
||||||
agent = result.agent;
|
agent = result.agent;
|
||||||
} else {
|
} else {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -320,36 +398,23 @@ export async function handleHeadlessCommand(
|
|||||||
// Priority 6: Create a new agent
|
// Priority 6: Create a new agent
|
||||||
if (!agent) {
|
if (!agent) {
|
||||||
const updateArgs = getModelUpdateArgs(model);
|
const updateArgs = getModelUpdateArgs(model);
|
||||||
|
const createOptions = {
|
||||||
|
model,
|
||||||
|
updateArgs,
|
||||||
|
skillsDirectory,
|
||||||
|
parallelToolCalls: true,
|
||||||
|
enableSleeptime: sleeptimeFlag ?? settings.enableSleeptime,
|
||||||
|
systemPromptPreset,
|
||||||
|
// Note: systemCustom, systemAppend, and memoryBlocks only apply with --new flag
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
const result = await createAgent(
|
const result = await createAgent(createOptions);
|
||||||
undefined,
|
|
||||||
model,
|
|
||||||
undefined,
|
|
||||||
updateArgs,
|
|
||||||
skillsDirectory,
|
|
||||||
true, // parallelToolCalls always enabled
|
|
||||||
sleeptimeFlag ?? settings.enableSleeptime,
|
|
||||||
systemPromptId,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
);
|
|
||||||
agent = result.agent;
|
agent = result.agent;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (isToolsNotFoundError(err)) {
|
if (isToolsNotFoundError(err)) {
|
||||||
console.warn("Tools missing on server, re-uploading and retrying...");
|
console.warn("Tools missing on server, re-uploading and retrying...");
|
||||||
await forceUpsertTools(client, baseURL);
|
await forceUpsertTools(client, baseURL);
|
||||||
const result = await createAgent(
|
const result = await createAgent(createOptions);
|
||||||
undefined,
|
|
||||||
model,
|
|
||||||
undefined,
|
|
||||||
updateArgs,
|
|
||||||
skillsDirectory,
|
|
||||||
true,
|
|
||||||
sleeptimeFlag ?? settings.enableSleeptime,
|
|
||||||
systemPromptId,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
);
|
|
||||||
agent = result.agent;
|
agent = result.agent;
|
||||||
} else {
|
} else {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -365,7 +430,7 @@ export async function handleHeadlessCommand(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// If resuming and a model or system prompt was specified, apply those changes
|
// If resuming and a model or system prompt was specified, apply those changes
|
||||||
if (isResumingAgent && (model || systemPromptId)) {
|
if (isResumingAgent && (model || systemPromptPreset)) {
|
||||||
if (model) {
|
if (model) {
|
||||||
const { resolveModel } = await import("./agent/model");
|
const { resolveModel } = await import("./agent/model");
|
||||||
const modelHandle = resolveModel(model);
|
const modelHandle = resolveModel(model);
|
||||||
@@ -388,9 +453,12 @@ export async function handleHeadlessCommand(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (systemPromptId) {
|
if (systemPromptPreset) {
|
||||||
const { updateAgentSystemPrompt } = await import("./agent/modify");
|
const { updateAgentSystemPrompt } = await import("./agent/modify");
|
||||||
const result = await updateAgentSystemPrompt(agent.id, systemPromptId);
|
const result = await updateAgentSystemPrompt(
|
||||||
|
agent.id,
|
||||||
|
systemPromptPreset,
|
||||||
|
);
|
||||||
if (!result.success || !result.agent) {
|
if (!result.success || !result.agent) {
|
||||||
console.error(`Failed to update system prompt: ${result.message}`);
|
console.error(`Failed to update system prompt: ${result.message}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|||||||
84
src/index.ts
84
src/index.ts
@@ -332,6 +332,10 @@ async function main(): Promise<void> {
|
|||||||
name: { type: "string", short: "n" },
|
name: { type: "string", short: "n" },
|
||||||
model: { type: "string", short: "m" },
|
model: { type: "string", short: "m" },
|
||||||
system: { type: "string", short: "s" },
|
system: { type: "string", short: "s" },
|
||||||
|
"system-custom": { type: "string" },
|
||||||
|
"system-append": { type: "string" },
|
||||||
|
"memory-blocks": { type: "string" },
|
||||||
|
"block-value": { type: "string", multiple: true },
|
||||||
toolset: { type: "string" },
|
toolset: { type: "string" },
|
||||||
prompt: { type: "boolean", short: "p" },
|
prompt: { type: "boolean", short: "p" },
|
||||||
run: { type: "boolean" },
|
run: { type: "boolean" },
|
||||||
@@ -406,7 +410,12 @@ async function main(): Promise<void> {
|
|||||||
let specifiedAgentId = (values.agent as string | undefined) ?? null;
|
let specifiedAgentId = (values.agent as string | undefined) ?? null;
|
||||||
const specifiedAgentName = (values.name as string | undefined) ?? null;
|
const specifiedAgentName = (values.name as string | undefined) ?? null;
|
||||||
const specifiedModel = (values.model as string | undefined) ?? undefined;
|
const specifiedModel = (values.model as string | undefined) ?? undefined;
|
||||||
const systemPromptId = (values.system as string | undefined) ?? undefined;
|
const systemPromptPreset = (values.system as string | undefined) ?? undefined;
|
||||||
|
const systemCustom =
|
||||||
|
(values["system-custom"] as string | undefined) ?? undefined;
|
||||||
|
// Note: systemAppend is also parsed but only used in headless mode (headless.ts handles it)
|
||||||
|
const memoryBlocksJson =
|
||||||
|
(values["memory-blocks"] as string | undefined) ?? undefined;
|
||||||
const specifiedToolset = (values.toolset as string | undefined) ?? undefined;
|
const specifiedToolset = (values.toolset as string | undefined) ?? undefined;
|
||||||
const skillsDirectory = (values.skills as string | undefined) ?? undefined;
|
const skillsDirectory = (values.skills as string | undefined) ?? undefined;
|
||||||
const sleeptimeFlag = (values.sleeptime as boolean | undefined) ?? undefined;
|
const sleeptimeFlag = (values.sleeptime as boolean | undefined) ?? undefined;
|
||||||
@@ -476,8 +485,16 @@ async function main(): Promise<void> {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate system prompt if provided (can be a system prompt ID or subagent name)
|
// Validate system prompt options (--system and --system-custom are mutually exclusive)
|
||||||
if (systemPromptId) {
|
if (systemPromptPreset && systemCustom) {
|
||||||
|
console.error(
|
||||||
|
"Error: --system and --system-custom are mutually exclusive. Use one or the other.",
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate system prompt preset if provided (can be a system prompt ID or subagent name)
|
||||||
|
if (systemPromptPreset) {
|
||||||
const { SYSTEM_PROMPTS } = await import("./agent/promptAssets");
|
const { SYSTEM_PROMPTS } = await import("./agent/promptAssets");
|
||||||
const { getAllSubagentConfigs } = await import("./agent/subagents");
|
const { getAllSubagentConfigs } = await import("./agent/subagents");
|
||||||
|
|
||||||
@@ -485,13 +502,42 @@ async function main(): Promise<void> {
|
|||||||
const subagentConfigs = await getAllSubagentConfigs();
|
const subagentConfigs = await getAllSubagentConfigs();
|
||||||
const validSubagentNames = Object.keys(subagentConfigs);
|
const validSubagentNames = Object.keys(subagentConfigs);
|
||||||
|
|
||||||
const isValidSystemPrompt = validSystemPrompts.includes(systemPromptId);
|
const isValidSystemPrompt = validSystemPrompts.includes(systemPromptPreset);
|
||||||
const isValidSubagent = validSubagentNames.includes(systemPromptId);
|
const isValidSubagent = validSubagentNames.includes(systemPromptPreset);
|
||||||
|
|
||||||
if (!isValidSystemPrompt && !isValidSubagent) {
|
if (!isValidSystemPrompt && !isValidSubagent) {
|
||||||
const allValid = [...validSystemPrompts, ...validSubagentNames];
|
const allValid = [...validSystemPrompts, ...validSubagentNames];
|
||||||
console.error(
|
console.error(
|
||||||
`Error: Invalid system prompt "${systemPromptId}". Must be one of: ${allValid.join(", ")}.`,
|
`Error: Invalid system prompt "${systemPromptPreset}". Must be one of: ${allValid.join(", ")}.`,
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse memory blocks JSON if provided
|
||||||
|
let memoryBlocks:
|
||||||
|
| Array<{ label: string; value: string; description?: string }>
|
||||||
|
| undefined;
|
||||||
|
if (memoryBlocksJson) {
|
||||||
|
try {
|
||||||
|
memoryBlocks = JSON.parse(memoryBlocksJson);
|
||||||
|
if (!Array.isArray(memoryBlocks)) {
|
||||||
|
throw new Error("memory-blocks must be a JSON array");
|
||||||
|
}
|
||||||
|
// Validate each block has required fields
|
||||||
|
for (const block of memoryBlocks) {
|
||||||
|
if (
|
||||||
|
typeof block.label !== "string" ||
|
||||||
|
typeof block.value !== "string"
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
"Each memory block must have 'label' and 'value' string fields",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`Error: Invalid --memory-blocks JSON: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
);
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
@@ -745,7 +791,7 @@ async function main(): Promise<void> {
|
|||||||
baseTools,
|
baseTools,
|
||||||
agentIdArg,
|
agentIdArg,
|
||||||
model,
|
model,
|
||||||
systemPromptId,
|
systemPromptPreset,
|
||||||
toolset,
|
toolset,
|
||||||
skillsDirectory,
|
skillsDirectory,
|
||||||
fromAfFile,
|
fromAfFile,
|
||||||
@@ -756,7 +802,7 @@ async function main(): Promise<void> {
|
|||||||
baseTools?: string[];
|
baseTools?: string[];
|
||||||
agentIdArg: string | null;
|
agentIdArg: string | null;
|
||||||
model?: string;
|
model?: string;
|
||||||
systemPromptId?: string;
|
systemPromptPreset?: string;
|
||||||
toolset?: "codex" | "default" | "gemini";
|
toolset?: "codex" | "default" | "gemini";
|
||||||
skillsDirectory?: string;
|
skillsDirectory?: string;
|
||||||
fromAfFile?: string;
|
fromAfFile?: string;
|
||||||
@@ -1027,13 +1073,13 @@ async function main(): Promise<void> {
|
|||||||
agent = await client.agents.retrieve(agentIdArg);
|
agent = await client.agents.retrieve(agentIdArg);
|
||||||
|
|
||||||
// Apply --system flag to existing agent if provided
|
// Apply --system flag to existing agent if provided
|
||||||
if (systemPromptId) {
|
if (systemPromptPreset) {
|
||||||
const { updateAgentSystemPrompt } = await import(
|
const { updateAgentSystemPrompt } = await import(
|
||||||
"./agent/modify"
|
"./agent/modify"
|
||||||
);
|
);
|
||||||
const result = await updateAgentSystemPrompt(
|
const result = await updateAgentSystemPrompt(
|
||||||
agent.id,
|
agent.id,
|
||||||
systemPromptId,
|
systemPromptPreset,
|
||||||
);
|
);
|
||||||
if (!result.success || !result.agent) {
|
if (!result.success || !result.agent) {
|
||||||
console.error(
|
console.error(
|
||||||
@@ -1067,7 +1113,7 @@ async function main(): Promise<void> {
|
|||||||
skillsDirectory,
|
skillsDirectory,
|
||||||
true, // parallelToolCalls always enabled
|
true, // parallelToolCalls always enabled
|
||||||
sleeptimeFlag ?? settings.enableSleeptime,
|
sleeptimeFlag ?? settings.enableSleeptime,
|
||||||
systemPromptId,
|
systemPromptPreset,
|
||||||
initBlocks,
|
initBlocks,
|
||||||
baseTools,
|
baseTools,
|
||||||
);
|
);
|
||||||
@@ -1089,7 +1135,7 @@ async function main(): Promise<void> {
|
|||||||
skillsDirectory,
|
skillsDirectory,
|
||||||
true,
|
true,
|
||||||
sleeptimeFlag ?? settings.enableSleeptime,
|
sleeptimeFlag ?? settings.enableSleeptime,
|
||||||
systemPromptId,
|
systemPromptPreset,
|
||||||
initBlocks,
|
initBlocks,
|
||||||
baseTools,
|
baseTools,
|
||||||
);
|
);
|
||||||
@@ -1144,7 +1190,7 @@ async function main(): Promise<void> {
|
|||||||
skillsDirectory,
|
skillsDirectory,
|
||||||
true, // parallelToolCalls always enabled
|
true, // parallelToolCalls always enabled
|
||||||
sleeptimeFlag ?? settings.enableSleeptime,
|
sleeptimeFlag ?? settings.enableSleeptime,
|
||||||
systemPromptId,
|
systemPromptPreset,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
@@ -1166,7 +1212,7 @@ async function main(): Promise<void> {
|
|||||||
skillsDirectory,
|
skillsDirectory,
|
||||||
true,
|
true,
|
||||||
sleeptimeFlag ?? settings.enableSleeptime,
|
sleeptimeFlag ?? settings.enableSleeptime,
|
||||||
systemPromptId,
|
systemPromptPreset,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
@@ -1247,7 +1293,7 @@ async function main(): Promise<void> {
|
|||||||
setIsResumingSession(resuming);
|
setIsResumingSession(resuming);
|
||||||
|
|
||||||
// If resuming and a model or system prompt was specified, apply those changes
|
// If resuming and a model or system prompt was specified, apply those changes
|
||||||
if (resuming && (model || systemPromptId)) {
|
if (resuming && (model || systemPromptPreset)) {
|
||||||
if (model) {
|
if (model) {
|
||||||
const { resolveModel } = await import("./agent/model");
|
const { resolveModel } = await import("./agent/model");
|
||||||
const modelHandle = resolveModel(model);
|
const modelHandle = resolveModel(model);
|
||||||
@@ -1271,11 +1317,11 @@ async function main(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (systemPromptId) {
|
if (systemPromptPreset) {
|
||||||
const { updateAgentSystemPrompt } = await import("./agent/modify");
|
const { updateAgentSystemPrompt } = await import("./agent/modify");
|
||||||
const result = await updateAgentSystemPrompt(
|
const result = await updateAgentSystemPrompt(
|
||||||
agent.id,
|
agent.id,
|
||||||
systemPromptId,
|
systemPromptPreset,
|
||||||
);
|
);
|
||||||
if (!result.success || !result.agent) {
|
if (!result.success || !result.agent) {
|
||||||
console.error(`Error: ${result.message}`);
|
console.error(`Error: ${result.message}`);
|
||||||
@@ -1312,7 +1358,7 @@ async function main(): Promise<void> {
|
|||||||
forceNew,
|
forceNew,
|
||||||
agentIdArg,
|
agentIdArg,
|
||||||
model,
|
model,
|
||||||
systemPromptId,
|
systemPromptPreset,
|
||||||
fromAfFile,
|
fromAfFile,
|
||||||
loadingState,
|
loadingState,
|
||||||
selectedGlobalAgentId,
|
selectedGlobalAgentId,
|
||||||
@@ -1384,7 +1430,7 @@ async function main(): Promise<void> {
|
|||||||
baseTools: baseTools,
|
baseTools: baseTools,
|
||||||
agentIdArg: specifiedAgentId,
|
agentIdArg: specifiedAgentId,
|
||||||
model: specifiedModel,
|
model: specifiedModel,
|
||||||
systemPromptId: systemPromptId,
|
systemPromptPreset: systemPromptPreset,
|
||||||
toolset: specifiedToolset as "codex" | "default" | "gemini" | undefined,
|
toolset: specifiedToolset as "codex" | "default" | "gemini" | undefined,
|
||||||
skillsDirectory: skillsDirectory,
|
skillsDirectory: skillsDirectory,
|
||||||
fromAfFile: fromAfFile,
|
fromAfFile: fromAfFile,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type {
|
|||||||
StreamEvent,
|
StreamEvent,
|
||||||
SystemInitMessage,
|
SystemInitMessage,
|
||||||
WireMessage,
|
WireMessage,
|
||||||
} from "../types/wire";
|
} from "../types/protocol";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for --input-format stream-json bidirectional communication.
|
* Tests for --input-format stream-json bidirectional communication.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type {
|
|||||||
ResultMessage,
|
ResultMessage,
|
||||||
StreamEvent,
|
StreamEvent,
|
||||||
SystemInitMessage,
|
SystemInitMessage,
|
||||||
} from "../types/wire";
|
} from "../types/protocol";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for stream-json output format.
|
* Tests for stream-json output format.
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* Wire Format Types
|
* Protocol Types for Letta Code
|
||||||
*
|
*
|
||||||
* These types define the JSON structure emitted by headless.ts when running
|
* These types define:
|
||||||
* in stream-json mode. They enable typed consumption of the bidirectional
|
* 1. The JSON structure emitted by headless.ts in stream-json mode (wire protocol)
|
||||||
* JSON protocol.
|
* 2. Configuration types for session options (used internally and by SDK)
|
||||||
*
|
*
|
||||||
* Design principle: Compose from @letta-ai/letta-client types where possible.
|
* Design principle: Compose from @letta-ai/letta-client types where possible.
|
||||||
*/
|
*/
|
||||||
@@ -16,6 +16,7 @@ import type {
|
|||||||
ToolCallMessage as LettaToolCallMessage,
|
ToolCallMessage as LettaToolCallMessage,
|
||||||
ToolCall,
|
ToolCall,
|
||||||
} from "@letta-ai/letta-client/resources/agents/messages";
|
} from "@letta-ai/letta-client/resources/agents/messages";
|
||||||
|
import type { CreateBlock } from "@letta-ai/letta-client/resources/blocks/blocks";
|
||||||
import type { StopReasonType } from "@letta-ai/letta-client/resources/runs/runs";
|
import type { StopReasonType } from "@letta-ai/letta-client/resources/runs/runs";
|
||||||
import type { ToolReturnMessage as LettaToolReturnMessage } from "@letta-ai/letta-client/resources/tools";
|
import type { ToolReturnMessage as LettaToolReturnMessage } from "@letta-ai/letta-client/resources/tools";
|
||||||
|
|
||||||
@@ -26,8 +27,42 @@ export type {
|
|||||||
StopReasonType,
|
StopReasonType,
|
||||||
MessageCreate,
|
MessageCreate,
|
||||||
LettaToolReturnMessage,
|
LettaToolReturnMessage,
|
||||||
|
CreateBlock,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════
|
||||||
|
// CONFIGURATION TYPES (session options)
|
||||||
|
// Used internally by headless.ts/App.tsx, also exported for SDK
|
||||||
|
// ═══════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* System prompt preset configuration.
|
||||||
|
* Use this to select a built-in system prompt with optional appended text.
|
||||||
|
*
|
||||||
|
* Available presets (validated at runtime by CLI):
|
||||||
|
* - 'default' - Alias for letta-claude
|
||||||
|
* - 'letta-claude' - Full Letta Code prompt (Claude-optimized)
|
||||||
|
* - 'letta-codex' - Full Letta Code prompt (Codex-optimized)
|
||||||
|
* - 'letta-gemini' - Full Letta Code prompt (Gemini-optimized)
|
||||||
|
* - 'claude' - Basic Claude (no skills/memory instructions)
|
||||||
|
* - 'codex' - Basic Codex (no skills/memory instructions)
|
||||||
|
* - 'gemini' - Basic Gemini (no skills/memory instructions)
|
||||||
|
*/
|
||||||
|
export interface SystemPromptPresetConfig {
|
||||||
|
type: "preset";
|
||||||
|
/** Preset ID (e.g., 'default', 'letta-codex'). Validated at runtime. */
|
||||||
|
preset: string;
|
||||||
|
/** Additional instructions to append to the preset */
|
||||||
|
append?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* System prompt configuration - either a raw string or preset config.
|
||||||
|
* - string: Use as the complete system prompt
|
||||||
|
* - SystemPromptPresetConfig: Use a preset, optionally with appended text
|
||||||
|
*/
|
||||||
|
export type SystemPromptConfig = string | SystemPromptPresetConfig;
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════
|
||||||
// BASE ENVELOPE
|
// BASE ENVELOPE
|
||||||
// All wire messages include these fields
|
// All wire messages include these fields
|
||||||
@@ -7,5 +7,5 @@
|
|||||||
"declarationMap": true,
|
"declarationMap": true,
|
||||||
"outDir": "./dist/types"
|
"outDir": "./dist/types"
|
||||||
},
|
},
|
||||||
"include": ["src/types/wire.ts"]
|
"include": ["src/types/protocol.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user