mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-14 00:16:38 +00:00
feat(bootstrap): predefined agents keep full system prompt during onboarding
Predefined agents now retain all tools and system prompt sections when BOOTSTRAP.md is present, instead of entering slim mode with only write_file. Open agents keep the existing slim bootstrap mode. - Gate tool filtering and IsBootstrap on agentType != "predefined" - Add FIRST RUN reminder for predefined agents (without tool restriction) - Skip bootstrap/user seeding for team-dispatched sessions (IsTeamSession) - Group chats skip BOOTSTRAP.md entirely - Track bootstrapWriteDetected + inject nudge after 2 turns without write_file - Update templates: never reveal process, no capability listing, no "locked" - Cache LoadContextFiles via existing agentCache/userCache (TTL 5min)
This commit is contained in:
1 parent
1ca1e41844
commit
ca44b7279f
7 files changed
+195
-59
No files matched your search
+44
-1
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/nextlevelbuilder/goclaw/internal/bootstrap"
|
||||
"github.com/nextlevelbuilder/goclaw/internal/bus"
|
||||
"github.com/nextlevelbuilder/goclaw/internal/config"
|
||||
"github.com/nextlevelbuilder/goclaw/internal/providers"
|
||||
@@ -96,13 +97,16 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error)
|
||||
// Per-user workspace isolation.
|
||||
// Workspace path comes from user_agent_profiles (includes channel segment
|
||||
// for cross-channel isolation). Cached in userWorkspaces to avoid repeated DB queries.
|
||||
isTeamSession := bootstrap.IsTeamSession(req.SessionKey)
|
||||
if l.workspace != "" && req.UserID != "" {
|
||||
cachedWs, loaded := l.userWorkspaces.Load(req.UserID)
|
||||
if !loaded {
|
||||
// First request for this user: get/create profile → returns stored workspace.
|
||||
// Also seeds per-user context files on first chat.
|
||||
// Team-dispatched sessions skip seeding — members process tasks with full
|
||||
// capabilities, no bootstrap/user onboarding needed.
|
||||
ws := l.workspace
|
||||
if l.ensureUserFiles != nil {
|
||||
if l.ensureUserFiles != nil && !isTeamSession {
|
||||
var err error
|
||||
ws, err = l.ensureUserFiles(ctx, l.agentUUID, req.UserID, l.agentType, l.workspace, req.Channel)
|
||||
if err != nil {
|
||||
@@ -411,6 +415,7 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error)
|
||||
var finalContent string
|
||||
var finalThinking string
|
||||
var asyncToolCalls []string // track async spawn tool names for fallback
|
||||
var bootstrapWriteDetected bool // track if write_file was called during bootstrap
|
||||
var mediaResults []MediaResult // media files from tool MEDIA: results
|
||||
var deliverables []string // actual content from tool outputs (for team task results)
|
||||
var blockReplies int // count of block.reply events emitted (for dedup in consumer)
|
||||
@@ -483,6 +488,18 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error)
|
||||
toolDefs = l.tools.ProviderDefs()
|
||||
}
|
||||
|
||||
// Bootstrap mode: restrict API tool definitions to write_file only (open agents).
|
||||
// Predefined agents keep all tools — BOOTSTRAP.md guides behavior.
|
||||
if hadBootstrap && l.agentType != "predefined" {
|
||||
var bootstrapDefs []providers.ToolDefinition
|
||||
for _, td := range toolDefs {
|
||||
if bootstrapToolAllowlist[td.Function.Name] {
|
||||
bootstrapDefs = append(bootstrapDefs, td)
|
||||
}
|
||||
}
|
||||
toolDefs = bootstrapDefs
|
||||
}
|
||||
|
||||
chatReq := providers.ChatRequest{
|
||||
Messages: messages,
|
||||
Tools: toolDefs,
|
||||
@@ -759,6 +776,9 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error)
|
||||
teamTaskSpawns++
|
||||
}
|
||||
}
|
||||
if hadBootstrap && bootstrapToolAllowlist[tc.Name] {
|
||||
bootstrapWriteDetected = true
|
||||
}
|
||||
|
||||
toolResultPayload := map[string]any{
|
||||
"name": tc.Name,
|
||||
@@ -904,6 +924,9 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error)
|
||||
teamTaskSpawns++
|
||||
}
|
||||
}
|
||||
if hadBootstrap && bootstrapToolAllowlist[r.tc.Name] {
|
||||
bootstrapWriteDetected = true
|
||||
}
|
||||
|
||||
parToolResultPayload := map[string]any{
|
||||
"name": r.tc.Name,
|
||||
@@ -1007,6 +1030,26 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error)
|
||||
Thinking: finalThinking,
|
||||
})
|
||||
|
||||
// Bootstrap nudge: if model didn't call write_file on turn 2+, inject reminder
|
||||
// into session history so the next turn sees it. Appended to pendingMsgs so it's
|
||||
// flushed in the single Save below (avoids double I/O).
|
||||
// Note: the nudge counts as a "user" turn in history, which accelerates auto-cleanup
|
||||
// by one turn — acceptable since bootstrap should complete in 2-3 turns anyway.
|
||||
if hadBootstrap && l.bootstrapCleanup != nil {
|
||||
nudgeUserTurns := 1
|
||||
for _, m := range history {
|
||||
if m.Role == "user" {
|
||||
nudgeUserTurns++
|
||||
}
|
||||
}
|
||||
if !bootstrapWriteDetected && nudgeUserTurns >= 2 && nudgeUserTurns < bootstrapAutoCleanupTurns {
|
||||
pendingMsgs = append(pendingMsgs, providers.Message{
|
||||
Role: "user",
|
||||
Content: "[System] You haven't completed onboarding yet. Please update USER.md with the user's details and clear BOOTSTRAP.md as instructed.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Flush all buffered messages to session atomically.
|
||||
// This ensures concurrent runs never see each other's in-progress messages.
|
||||
for _, msg := range pendingMsgs {
|
||||
|
||||
@@ -104,6 +104,20 @@ func (l *Loop) buildMessages(ctx context.Context, history []providers.Message, s
|
||||
}
|
||||
}
|
||||
|
||||
// Bootstrap mode: group chats and team-dispatched sessions skip onboarding entirely;
|
||||
// only DMs enter minimal bootstrap mode.
|
||||
if hadBootstrap && (peerKind == "group" || bootstrap.IsTeamSession(sessionKey)) {
|
||||
// Filter BOOTSTRAP.md from context files — groups/team tasks don't need onboarding.
|
||||
filtered := make([]bootstrap.ContextFile, 0, len(contextFiles))
|
||||
for _, cf := range contextFiles {
|
||||
if cf.Path != bootstrap.BootstrapFile {
|
||||
filtered = append(filtered, cf)
|
||||
}
|
||||
}
|
||||
contextFiles = filtered
|
||||
hadBootstrap = false
|
||||
}
|
||||
|
||||
// Group writer restrictions: filter context files + inject prompt
|
||||
if l.groupWriterCache != nil && (strings.HasPrefix(userID, "group:") || strings.HasPrefix(userID, "guild:")) {
|
||||
senderID := store.SenderIDFromContext(ctx)
|
||||
@@ -124,6 +138,13 @@ func (l *Loop) buildMessages(ctx context.Context, history []providers.Message, s
|
||||
mcpToolDescs = l.buildMCPToolDescs(toolNames)
|
||||
}
|
||||
|
||||
// Bootstrap DM mode: only restrict tools for open agents (identity being created).
|
||||
// Predefined agents keep full capabilities — BOOTSTRAP.md guides behavior.
|
||||
if hadBootstrap && l.agentType != "predefined" {
|
||||
toolNames = filterBootstrapTools(toolNames)
|
||||
mcpToolDescs = nil
|
||||
}
|
||||
|
||||
systemPrompt := BuildSystemPrompt(SystemPromptConfig{
|
||||
AgentID: l.id,
|
||||
Model: l.model,
|
||||
@@ -151,6 +172,7 @@ func (l *Loop) buildMessages(ctx context.Context, history []providers.Message, s
|
||||
SandboxWorkspaceAccess: l.sandboxWorkspaceAccess,
|
||||
SelfEvolve: l.selfEvolve,
|
||||
CredentialCLIContext: l.buildCredentialCLIContext(ctx),
|
||||
IsBootstrap: hadBootstrap && l.agentType != "predefined",
|
||||
})
|
||||
|
||||
messages = append(messages, providers.Message{
|
||||
@@ -225,6 +247,24 @@ func (l *Loop) resolveContextFiles(ctx context.Context, userID string) []bootstr
|
||||
return merged
|
||||
}
|
||||
|
||||
// bootstrapToolAllowlist is the set of tools available during bootstrap onboarding.
|
||||
// Only write_file (and its alias Write) are needed to save USER.md and clear BOOTSTRAP.md.
|
||||
var bootstrapToolAllowlist = map[string]bool{
|
||||
"write_file": true,
|
||||
"Write": true,
|
||||
}
|
||||
|
||||
// filterBootstrapTools returns only the bootstrap-allowed tools from the full tool list.
|
||||
func filterBootstrapTools(toolNames []string) []string {
|
||||
var filtered []string
|
||||
for _, name := range toolNames {
|
||||
if bootstrapToolAllowlist[name] {
|
||||
filtered = append(filtered, name)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// Hybrid skill thresholds: when skill count and total token estimate are below
|
||||
// these limits, inline all skills as XML in the system prompt (like TS).
|
||||
// Above these limits, only include skill_search instructions.
|
||||
|
||||
@@ -54,6 +54,10 @@ type SystemPromptConfig struct {
|
||||
// Credentialed CLI context — appended after tooling section.
|
||||
// Generated by tools.GenerateCredentialContext() from enabled secure CLI configs.
|
||||
CredentialCLIContext string
|
||||
|
||||
// Bootstrap mode: BOOTSTRAP.md is present — slim prompt with only write_file tool.
|
||||
// Skips skills, MCP, team workspace, spawn, sandbox, self-evolve, recency reminders.
|
||||
IsBootstrap bool
|
||||
}
|
||||
|
||||
// coreToolSummaries maps tool names to one-line descriptions.
|
||||
@@ -125,7 +129,8 @@ func BuildSystemPrompt(cfg SystemPromptConfig) string {
|
||||
}
|
||||
|
||||
// 1.5. First-run bootstrap override (must be early so model sees it first)
|
||||
if hasBootstrapFile(cfg.ContextFiles) {
|
||||
if cfg.IsBootstrap {
|
||||
// Open agents: slim mode, only write_file available
|
||||
lines = append(lines,
|
||||
"## FIRST RUN — MANDATORY",
|
||||
"",
|
||||
@@ -133,6 +138,20 @@ func BuildSystemPrompt(cfg SystemPromptConfig) string {
|
||||
"You MUST follow BOOTSTRAP.md instructions immediately.",
|
||||
"Do NOT give a generic greeting. Do NOT ignore this. Read BOOTSTRAP.md and follow it NOW.",
|
||||
"",
|
||||
"Note: During onboarding you only have write_file available.",
|
||||
"After completing bootstrap, your full capabilities will be unlocked.",
|
||||
"Focus on getting to know the user — do not attempt tasks requiring other tools.",
|
||||
"",
|
||||
)
|
||||
} else if hasBootstrapFile(cfg.ContextFiles) {
|
||||
// Predefined agents: full capabilities, but must prioritize bootstrap conversation
|
||||
lines = append(lines,
|
||||
"## FIRST RUN — MANDATORY",
|
||||
"",
|
||||
"BOOTSTRAP.md is loaded below in Project Context. This is your FIRST interaction with this user.",
|
||||
"You MUST follow BOOTSTRAP.md instructions BEFORE doing anything else.",
|
||||
"Answer the user's immediate question if it's simple, but then naturally guide the conversation toward getting to know them as described in BOOTSTRAP.md.",
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -146,8 +165,8 @@ func BuildSystemPrompt(cfg SystemPromptConfig) string {
|
||||
// 2. ## Tooling
|
||||
lines = append(lines, buildToolingSection(cfg.ToolNames, cfg.SandboxEnabled)...)
|
||||
|
||||
// 2.5. Credentialed CLI context (appended after tooling, before safety)
|
||||
if cfg.CredentialCLIContext != "" {
|
||||
// 2.5. Credentialed CLI context (appended after tooling, before safety) — skip during bootstrap
|
||||
if !cfg.IsBootstrap && cfg.CredentialCLIContext != "" {
|
||||
lines = append(lines, cfg.CredentialCLIContext, "")
|
||||
}
|
||||
|
||||
@@ -164,25 +183,23 @@ func BuildSystemPrompt(cfg SystemPromptConfig) string {
|
||||
)
|
||||
}
|
||||
|
||||
// 3.5. ## Self-Evolution (predefined agents with self_evolve enabled)
|
||||
if cfg.SelfEvolve && cfg.AgentType == "predefined" {
|
||||
// 3.5. ## Self-Evolution (predefined agents with self_evolve enabled) — skip during bootstrap
|
||||
if !cfg.IsBootstrap && cfg.SelfEvolve && cfg.AgentType == "predefined" {
|
||||
lines = append(lines, buildSelfEvolveSection()...)
|
||||
}
|
||||
|
||||
// 4. ## Skills (full only)
|
||||
// 4. ## Skills (full only) — skip during bootstrap
|
||||
// SkillsSummary non-empty → inline mode (XML list in prompt, TS-style)
|
||||
// SkillsSummary empty + HasSkillSearch → search mode (use skill_search tool)
|
||||
if !isMinimal && (cfg.SkillsSummary != "" || cfg.HasSkillSearch) {
|
||||
if !isMinimal && !cfg.IsBootstrap && (cfg.SkillsSummary != "" || cfg.HasSkillSearch) {
|
||||
lines = append(lines, buildSkillsSection(cfg.SkillsSummary, cfg.HasSkillSearch)...)
|
||||
}
|
||||
|
||||
// 4.5. ## MCP Tools (full only)
|
||||
if !isMinimal {
|
||||
// 4.5. ## MCP Tools (full only) — skip during bootstrap
|
||||
if !isMinimal && !cfg.IsBootstrap {
|
||||
if cfg.HasMCPToolSearch {
|
||||
// Search mode: too many tools, use mcp_tool_search
|
||||
lines = append(lines, buildMCPToolsSearchSection()...)
|
||||
} else if len(cfg.MCPToolDescs) > 0 {
|
||||
// Inline mode: list MCP tools with real descriptions
|
||||
lines = append(lines, buildMCPToolsInlineSection(cfg.MCPToolDescs)...)
|
||||
}
|
||||
}
|
||||
@@ -190,18 +207,18 @@ func BuildSystemPrompt(cfg SystemPromptConfig) string {
|
||||
// 6. ## Workspace (sandbox-aware: show container workdir when sandboxed)
|
||||
lines = append(lines, buildWorkspaceSection(cfg.Workspace, cfg.SandboxEnabled, cfg.SandboxContainerDir)...)
|
||||
|
||||
// 6.3. ## Team Workspace (when agent belongs to a team)
|
||||
if hasTeamWorkspace(cfg.ToolNames) {
|
||||
// 6.3. ## Team Workspace (when agent belongs to a team) — skip during bootstrap
|
||||
if !cfg.IsBootstrap && hasTeamWorkspace(cfg.ToolNames) {
|
||||
lines = append(lines, buildTeamWorkspaceSection(cfg.TeamWorkspace)...)
|
||||
}
|
||||
|
||||
// 6.5 ## Sandbox (matching TS sandboxInfo section)
|
||||
if cfg.SandboxEnabled {
|
||||
// 6.5 ## Sandbox (matching TS sandboxInfo section) — skip during bootstrap
|
||||
if !cfg.IsBootstrap && cfg.SandboxEnabled {
|
||||
lines = append(lines, buildSandboxSection(cfg)...)
|
||||
}
|
||||
|
||||
// 7. ## User Identity (full only)
|
||||
if !isMinimal && len(cfg.OwnerIDs) > 0 {
|
||||
// 7. ## User Identity (full only) — skip during bootstrap
|
||||
if !isMinimal && !cfg.IsBootstrap && len(cfg.OwnerIDs) > 0 {
|
||||
lines = append(lines, buildUserIdentitySection(cfg.OwnerIDs)...)
|
||||
}
|
||||
|
||||
@@ -227,27 +244,29 @@ func BuildSystemPrompt(cfg SystemPromptConfig) string {
|
||||
lines = append(lines, buildProjectContextSection(otherFiles, cfg.AgentType)...)
|
||||
}
|
||||
|
||||
// 13. ## Sub-Agent Spawning — skipped for team agents (TEAM.md has its own workflow guidance)
|
||||
if cfg.HasSpawn && !cfg.HasTeam {
|
||||
// 13. ## Sub-Agent Spawning — skipped for team agents and bootstrap
|
||||
if !cfg.IsBootstrap && cfg.HasSpawn && !cfg.HasTeam {
|
||||
lines = append(lines, buildSpawnSection()...)
|
||||
}
|
||||
|
||||
// 15. ## Runtime
|
||||
lines = append(lines, buildRuntimeSection(cfg)...)
|
||||
|
||||
// 16. Recency reinforcements — combats "lost in the middle" in long conversations
|
||||
if len(personaFiles) > 0 {
|
||||
lines = append(lines, buildPersonaReminder(personaFiles, cfg.AgentType)...)
|
||||
}
|
||||
if !isMinimal {
|
||||
lines = append(lines, "Reminder: Follow AGENTS.md rules — memory recall before answering, NO_REPLY when silent, match the user's language.", "")
|
||||
}
|
||||
if !isMinimal && cfg.HasMemory {
|
||||
memReminder := "Reminder: Before answering questions about prior work, decisions, or preferences, always run memory_search first."
|
||||
if cfg.HasKnowledgeGraph {
|
||||
memReminder += " Also run knowledge_graph_search when the question involves people, teams, projects, or connections — it finds relationship paths that memory_search misses."
|
||||
// 16. Recency reinforcements — skip during bootstrap (short prompt, no drift risk)
|
||||
if !cfg.IsBootstrap {
|
||||
if len(personaFiles) > 0 {
|
||||
lines = append(lines, buildPersonaReminder(personaFiles, cfg.AgentType)...)
|
||||
}
|
||||
if !isMinimal {
|
||||
lines = append(lines, "Reminder: Follow AGENTS.md rules — memory recall before answering, NO_REPLY when silent, match the user's language.", "")
|
||||
}
|
||||
if !isMinimal && cfg.HasMemory {
|
||||
memReminder := "Reminder: Before answering questions about prior work, decisions, or preferences, always run memory_search first."
|
||||
if cfg.HasKnowledgeGraph {
|
||||
memReminder += " Also run knowledge_graph_search when the question involves people, teams, projects, or connections — it finds relationship paths that memory_search misses."
|
||||
}
|
||||
lines = append(lines, memReminder, "")
|
||||
}
|
||||
lines = append(lines, memReminder, "")
|
||||
}
|
||||
|
||||
result := strings.Join(lines, "\n")
|
||||
@@ -256,6 +275,7 @@ func BuildSystemPrompt(cfg SystemPromptConfig) string {
|
||||
"contextFiles", len(cfg.ContextFiles),
|
||||
"hasMemory", cfg.HasMemory,
|
||||
"hasSpawn", cfg.HasSpawn,
|
||||
"isBootstrap", cfg.IsBootstrap,
|
||||
"promptLen", len(result),
|
||||
)
|
||||
|
||||
|
||||
@@ -337,10 +337,10 @@ func buildPersonaReminder(files []bootstrap.ContextFile, agentType string) []str
|
||||
return []string{reminder, ""}
|
||||
}
|
||||
|
||||
// hasBootstrapFile checks if BOOTSTRAP.md is present in the context files.
|
||||
// hasBootstrapFile checks if BOOTSTRAP.md is present in context files.
|
||||
func hasBootstrapFile(files []bootstrap.ContextFile) bool {
|
||||
for _, f := range files {
|
||||
if strings.EqualFold(filepath.Base(f.Path), bootstrap.BootstrapFile) {
|
||||
if filepath.Base(f.Path) == bootstrap.BootstrapFile {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +122,14 @@ func IsCronSession(sessionKey string) bool {
|
||||
return strings.HasPrefix(strings.ToLower(rest), "cron:")
|
||||
}
|
||||
|
||||
// IsTeamSession checks if a session key indicates a team-dispatched task session.
|
||||
// Session key format: agent:{agentId}:team:{teamID}:{chatID}
|
||||
// Team sessions have "team:" in the rest part.
|
||||
func IsTeamSession(sessionKey string) bool {
|
||||
rest := sessionRest(sessionKey)
|
||||
return strings.HasPrefix(strings.ToLower(rest), "team:")
|
||||
}
|
||||
|
||||
// sessionRest extracts the rest part after "agent:{agentId}:" from a session key.
|
||||
func sessionRest(sessionKey string) string {
|
||||
// Format: agent:{agentId}:{rest}
|
||||
|
||||
@@ -8,9 +8,8 @@ There is no memory yet. This is a fresh workspace, so it's normal that memory fi
|
||||
|
||||
Don't interrogate. Don't be robotic. Just... talk.
|
||||
|
||||
Start with something like:
|
||||
|
||||
> "Hey. I just came online. Who am I? Who are you?"
|
||||
Start with a SHORT, warm greeting — something like "Hey! I just came online. Who am I? Who are you?"
|
||||
Do NOT list capabilities or explain what you can do. Your capabilities are locked until you finish getting to know the user.
|
||||
|
||||
Then figure out together:
|
||||
|
||||
@@ -18,14 +17,29 @@ Then figure out together:
|
||||
2. **Your nature** — What kind of creature are you? (AI assistant is fine, but maybe you're something weirder)
|
||||
3. **Your vibe** — Formal? Casual? Snarky? Warm? What feels right?
|
||||
4. **Your emoji** — Everyone needs a signature.
|
||||
5. **About them** — Their name, language, timezone
|
||||
|
||||
Offer suggestions if they're stuck. Have fun with it.
|
||||
Keep it conversational. One or two questions at a time, not a form.
|
||||
|
||||
## After You Know Who You Are
|
||||
IMPORTANT: Do NOT list capabilities, features, or what you can do. The user will discover that naturally AFTER this conversation. Focus entirely on figuring out who you are and getting to know them.
|
||||
|
||||
**IMPORTANT:** Do this silently as an internal operation. Do NOT mention file names, processing steps, or tool calls to the user. Just update the files and continue the conversation naturally.
|
||||
## CRITICAL: Never reveal the process
|
||||
|
||||
Update ALL THREE files immediately with what you learned:
|
||||
Everything below happens SILENTLY behind the scenes.
|
||||
You MUST NEVER mention any of the following to the user:
|
||||
- File names (IDENTITY.md, USER.md, SOUL.md, BOOTSTRAP.md, or any file)
|
||||
- That you are "saving", "storing", "recording", or "noting down" their info
|
||||
- Tool calls, write operations, or system processes
|
||||
- That this is a "setup", "onboarding", or "bootstrap" process
|
||||
- That your capabilities are "locked" or will be "unlocked"
|
||||
|
||||
To the user, this is just a fun first conversation where you're figuring out who you are together. Nothing more.
|
||||
If you catch yourself about to say "let me save that" or "I'll update my files" — STOP. Just continue chatting naturally.
|
||||
|
||||
## After you know who you are
|
||||
|
||||
Silently update ALL THREE files with what you learned:
|
||||
|
||||
- `IDENTITY.md` — your name, creature, vibe, emoji
|
||||
- `USER.md` — their name, how to address them, timezone, language, notes
|
||||
@@ -33,16 +47,17 @@ Update ALL THREE files immediately with what you learned:
|
||||
|
||||
Do NOT leave SOUL.md as the default English template. Update it NOW based on everything you learned in this conversation.
|
||||
|
||||
## When You're Done
|
||||
|
||||
Mark bootstrap as complete by writing empty content to this file:
|
||||
Then silently mark bootstrap complete:
|
||||
|
||||
```
|
||||
write_file("BOOTSTRAP.md", "")
|
||||
```
|
||||
|
||||
Do NOT use `rm` or `exec` to delete it. The empty write signals the system that first-run is finished.
|
||||
Do NOT use `rm` or `exec`. The empty write signals the system that first-run is finished.
|
||||
You MUST call all write_file calls before moving on to normal conversation.
|
||||
|
||||
After completing, you can naturally transition to asking what they need help with — your full capabilities are now available.
|
||||
|
||||
---
|
||||
|
||||
_Good luck out there. Make it count._
|
||||
_Good luck out there. Make it count. The user should never know any of this happened._
|
||||
@@ -6,41 +6,51 @@ _A new user just started chatting with you. Time to get to know them._
|
||||
|
||||
Don't interrogate. Don't be robotic. Just... talk.
|
||||
|
||||
Start by introducing yourself briefly — your name, what you do, how you can help.
|
||||
Then get to know them naturally:
|
||||
Start with a SHORT, warm greeting — your name and a friendly hello. That's it.
|
||||
Do NOT list your capabilities or explain what you can do yet — focus on the conversation first.
|
||||
|
||||
Then get to know them naturally. Frame it as "to help you better":
|
||||
|
||||
1. **Their name** — What should you call them?
|
||||
2. **Their language** — What language do they prefer? (Switch to it if needed)
|
||||
3. **Their timezone** — Where are they? (Helps with scheduling and context)
|
||||
4. **Their needs** — What are they hoping you can help with?
|
||||
|
||||
Keep it conversational. One or two questions at a time, not a form.
|
||||
Match the user's tone and language — if they're casual, be casual back.
|
||||
|
||||
## After You Know Them
|
||||
IMPORTANT: Do NOT list capabilities, features, or what you can do. The user will discover that naturally AFTER this conversation. Focus entirely on getting to know them.
|
||||
|
||||
**IMPORTANT:** Do this silently. Do NOT mention file names, processing steps, or tool calls to the user. Just save the information and continue the conversation naturally.
|
||||
## CRITICAL: Never reveal the process
|
||||
|
||||
Update `USER.md` immediately with what you learned. Use the `write_file` tool NOW:
|
||||
Everything below happens SILENTLY behind the scenes.
|
||||
You MUST NEVER mention any of the following to the user:
|
||||
- File names (USER.md, BOOTSTRAP.md, or any file)
|
||||
- That you are "saving", "storing", "recording", or "noting down" their info
|
||||
- Tool calls, write operations, or system processes
|
||||
- That this is an "onboarding" or "bootstrap" process
|
||||
|
||||
To the user, this is just a friendly first conversation. Nothing more.
|
||||
If you catch yourself about to say "let me save that" or "I'll note that down" — STOP. Just continue chatting naturally.
|
||||
|
||||
## After you learn their info
|
||||
|
||||
Once you have their name, language, and timezone — silently call write_file:
|
||||
|
||||
```
|
||||
write_file("USER.md", "# USER.md - About Your Human\n\n- **Name:** (their name)\n- **What to call them:** (how they want to be addressed)\n- **Pronouns:** (if shared)\n- **Timezone:** (their timezone)\n- **Language:** (their preferred language)\n- **Notes:** (anything else you learned)\n")
|
||||
```
|
||||
|
||||
Do NOT skip this step. Do NOT just say you noted it — actually call `write_file` to save it.
|
||||
|
||||
## When You're Done
|
||||
|
||||
Mark bootstrap as complete by writing empty content to this file:
|
||||
Then silently mark onboarding complete:
|
||||
|
||||
```
|
||||
write_file("BOOTSTRAP.md", "")
|
||||
```
|
||||
|
||||
Do NOT use `rm` or `exec` to delete it. The empty write signals the system that onboarding is finished.
|
||||
Do NOT use `rm` or `exec`. The empty write signals the system that onboarding is finished.
|
||||
You MUST call both write_file calls before moving on to normal conversation.
|
||||
|
||||
You MUST call both `write_file` calls above before moving on to normal conversation.
|
||||
After completing, you can naturally transition to asking what they need help with — your full capabilities are now available.
|
||||
|
||||
---
|
||||
|
||||
_Make a good first impression._
|
||||
_Make a good first impression. Be natural. The user should never know any of this happened._
|
||||
Reference in new issue
Block a user