{"repo": "ardumine/hagent", "files": {"Agent/AgentImplement.cs": {"69318a38b4ea4feee28f5edd009b14f03c072610": {"t": 1785745802.3753653, "n": 15186, "h": "4bdc207642d17c49", "c": "using System.Text.Json;\nusing System.Text.Json.Nodes;\nusing HCore.Modules.Base;\n\nnamespace HCore.Packages.Agent;\n\n/// \n/// An interactive agent app. Launched from the shell as agent (optionally\n/// with an initial prompt), it takes over the terminal and runs a chat REPL:\n/// you type, it thinks and uses tools (VFS + shell), it answers, and the\n/// conversation history persists across turns so you can reply to its questions.\n/// Type /exit to quit and return to the shell.\n/// \npublic class AgentImplement : BaseImplement, IAgent, IOneshotCommand\n{\n private const string ApiKeyPath = \"/etc/agent/apikey\";\n private const string ConfigPath = \"/etc/agent/config.json\";\n private const int MaxSteps = 12;\n\n private string[] _args = [];\n\n public void SetArguments(string[] args) => _args = args;\n\n public void Run()\n {\n string apiKey;\n try\n {\n apiKey = LoadApiKey();\n }\n catch (Exception ex)\n {\n Console.WriteLine($\"agent: {ex.Message}\");\n return;\n }\n\n var (model, baseUrl) = LoadConfig();\n var client = new DeepseekClient(apiKey, model, baseUrl);\n var tools = new AgentTools(Vfs, Host, ShellPath());\n var store = new SessionStore(Vfs);\n\n // Parse launch args: `agent [-s|--session ] [initial prompt...]`.\n var (sessionArg, promptArg) = ParseLaunchArgs(_args);\n\n var messages = new JsonArray { Msg(\"system\", SystemPrompt()) };\n string sessionName;\n if (sessionArg is not null && store.Exists(sessionArg))\n {\n sessionName = SessionStore.Sanitize(sessionArg);\n var restored = store.Load(sessionName);\n if (restored is not null)\n {\n foreach (var m in restored) messages.Add(m?.DeepClone());\n Console.WriteLine($\"(restored session '{sessionName}' — {restored.Count} messages)\");\n }\n }\n else\n {\n sessionName = sessionArg is not null ? SessionStore.Sanitize(sessionArg) : SessionStore.NewName();\n }\n\n Console.WriteLine($\"HCore Agent ({model}). Session: {sessionName}. Type your message.\");\n Console.WriteLine(\"Commands: /exit, /reset, /model , /sessions, /save [name], /load , /new [name], /help.\");\n Logger.I($\"agent: interactive session started, model={model}, session={sessionName}\");\n\n // Optional initial prompt passed on the command line: `agent do something`.\n var pending = string.IsNullOrWhiteSpace(promptArg) ? null : promptArg;\n\n while (true)\n {\n // Honor our own kill signal: if this instance was reaped (e.g. someone\n // ran `kill` on us), the kernel cancelled StopToken — end the session\n // instead of lingering as a zombie on the shell's thread.\n if (StopToken.IsCancellationRequested)\n {\n Console.WriteLine(\"agent: this instance was killed — ending session.\");\n break;\n }\n\n string? line;\n if (!string.IsNullOrEmpty(pending))\n {\n line = pending;\n pending = null;\n Console.WriteLine($\"you> {line}\");\n }\n else\n {\n Console.Write(\"you> \");\n line = Console.ReadLine();\n }\n\n if (line is null) break; // EOF / Ctrl-D\n line = line.Trim();\n if (line.Length == 0) continue;\n\n if (line is \"/exit\" or \"/quit\") break;\n if (line == \"/help\")\n {\n Console.WriteLine(\n \"Commands: /exit quit · /reset clear conversation · /model switch model ·\\n\" +\n \" /sessions list saved · /save [name] save now · /load restore ·\\n\" +\n \" /new [name] start fresh session · /help this help.\");\n continue;\n }\n if (line == \"/reset\")\n {\n messages = new JsonArray { Msg(\"system\", SystemPrompt()) };\n Console.WriteLine(\"(conversation reset)\");\n continue;\n }\n if (line == \"/sessions\")\n {\n var names = store.List();\n if (names.Count == 0) Console.WriteLine(\"(no saved sessions)\");\n else foreach (var n in names)\n Console.WriteLine($\" {(n == sessionName ? \"*\" : \" \")} {n} ({store.Count(n)} messages)\");\n continue;\n }\n if (line == \"/save\" || line.StartsWith(\"/save \"))\n {\n var given = line.Length > 5 ? line[5..].Trim() : \"\";\n if (given.Length > 0) sessionName = SessionStore.Sanitize(given);\n store.Save(sessionName, model, messages);\n Console.WriteLine($\"(saved session '{sessionName}')\");\n continue;\n }\n if (line == \"/load\" || line.StartsWith(\"/load \"))\n {\n var given = line.Length > 5 ? line[5..].Trim() : \"\";\n if (given.Length == 0) { Console.WriteLine(\"usage: /load \"); continue; }\n var restored = store.Load(given);\n if (restored is null) { Console.WriteLine($\"(no session '{given}')\"); continue; }\n sessionName = SessionStore.Sanitize(given);\n messages = new JsonArray { Msg(\"system\", SystemPrompt()) };\n foreach (var m in restored) messages.Add(m?.DeepClone());\n Console.WriteLine($\"(loaded session '{sessionName}' — {restored.Count} messages)\");\n continue;\n }\n if (line == \"/new\" || line.StartsWith(\"/new \"))\n {\n var given = line.Length > 4 ? line[4..].Trim() : \"\";\n sessionName = given.Length > 0 ? SessionStore.Sanitize(given) : SessionStore.NewName();\n messages = new JsonArray { Msg(\"system\", SystemPrompt()) };\n Console.WriteLine($\"(new session '{sessionName}')\");\n continue;\n }\n if (line == \"/model\" || line.StartsWith(\"/model \"))\n {\n var requested = line.Length > 6 ? line[6..].Trim() : \"\";\n if (requested.Length == 0) { Console.WriteLine($\"model: {model}\"); continue; }\n model = requested;\n SaveModel(model, baseUrl);\n client = new DeepseekClient(apiKey, model, baseUrl);\n Console.WriteLine($\"(model set to {model})\");\n continue;\n }\n\n // Hot-reload config each turn so edits to /etc/agent/config.json\n // (by you, or by the agent itself via a tool) take effect live.\n var (curModel, curBase) = LoadConfig();\n if (curModel != model || curBase != baseUrl)\n {\n model = curModel;\n baseUrl = curBase;\n client = new DeepseekClient(apiKey, model, baseUrl);\n Console.WriteLine($\"(config reloaded: model={model})\");\n }\n\n messages.Add(Msg(\"user\", line));\n if (!RunTurn(client, tools, messages))\n break; // fatal API error already reported\n\n // Auto-persist the conversation so it survives crashes / kills.\n try { store.Save(sessionName, model, messages); }\n catch (Exception ex) { Logger.W($\"agent: session save failed: {ex.Message}\"); }\n }\n\n Console.WriteLine($\"agent: session '{sessionName}' ended.\");\n Logger.I($\"agent: interactive session '{sessionName}' ended.\");\n }\n\n /// \n /// Drive one user turn: call the model, execute any tool calls, loop until it\n /// produces a plain-text answer (or the step budget runs out). Returns false\n /// only on a fatal API error (caller should stop the session).\n /// \n private bool RunTurn(DeepseekClient client, AgentTools tools, JsonArray messages)\n {\n for (var step = 0; step < MaxSteps; step++)\n {\n JsonNode message;\n try\n {\n message = client.Chat(messages, AgentTools.Schema);\n }\n catch (Exception ex)\n {\n Console.WriteLine($\"agent: request failed: {ex.Message}\");\n Logger.E($\"agent request failed: {ex.Message}\");\n return false;\n }\n\n messages.Add(message.DeepClone());\n\n var toolCalls = message[\"tool_calls\"] as JsonArray;\n if (toolCalls is { Count: > 0 })\n {\n foreach (var call in toolCalls)\n {\n if (call is null) continue;\n var id = call[\"id\"]?.GetValue() ?? \"\";\n var fn = call[\"function\"];\n var name = fn?[\"name\"]?.GetValue() ?? \"\";\n var argsRaw = fn?[\"arguments\"]?.GetValue() ?? \"{}\";\n\n var args = ParseArgs(argsRaw);\n Console.WriteLine($\" [tool] {name}({Compact(argsRaw)})\");\n var result = tools.Invoke(name, args);\n Logger.I($\"agent tool {name} -> {result.Length} chars\");\n\n messages.Add(new JsonObject\n {\n [\"role\"] = \"tool\",\n [\"tool_call_id\"] = id,\n [\"content\"] = result,\n });\n }\n continue;\n }\n\n var content = message[\"content\"]?.GetValue() ?? \"\";\n Console.WriteLine($\"agent> {content.Trim()}\");\n return true;\n }\n\n Console.WriteLine($\"agent: stopped after {MaxSteps} steps without a final answer.\");\n return true;\n }\n\n private string ShellPath()\n {\n var name = InstanceName;\n var slash = name.LastIndexOf('/');\n return slash > 0 ? name[..slash] : \"init/console\";\n }\n\n private string LoadApiKey()\n {\n if (Vfs.Exists(ApiKeyPath))\n {\n var key = Vfs.ReadAllText(ApiKeyPath).Trim();\n if (key.Length > 0) return key;\n }\n\n var env = Environment.GetEnvironmentVariable(\"DEEPSEEK_API_KEY\");\n if (!string.IsNullOrWhiteSpace(env)) return env.Trim();\n\n throw new InvalidOperationException(\n $\"no API key. Put it in {ApiKeyPath} or set DEEPSEEK_API_KEY.\");\n }\n\n private (string model, string baseUrl) LoadConfig()\n {\n var model = \"deepseek-chat\";\n var baseUrl = \"https://api.deepseek.com\";\n try\n {\n if (Vfs.Exists(ConfigPath))\n {\n var doc = JsonNode.Parse(Vfs.ReadAllText(ConfigPath));\n model = doc?[\"model\"]?.GetValue() ?? model;\n baseUrl = doc?[\"baseUrl\"]?.GetValue() ?? baseUrl;\n }\n }\n catch { /* fall back to defaults */ }\n return (model, baseUrl);\n }\n\n private void SaveModel(string model, string baseUrl)\n {\n try\n {\n var cfg = new JsonObject { [\"model\"] = model, [\"baseUrl\"] = baseUrl };\n Vfs.WriteAllText(ConfigPath, cfg.ToJsonString());\n }\n catch (Exception ex)\n {\n Console.WriteLine($\"(warning: could not persist config: {ex.Message})\");\n }\n }\n\n private string SystemPrompt()\n {\n return\n \"You are an autonomous agent running inside HCore, a microkernel with a \" +\n \"virtual filesystem (VFS). You help the user by reasoning and using tools.\\n\" +\n \"\\n\" +\n \"Environment:\\n\" +\n $\"- Your instance name: {InstanceName}\\n\" +\n $\"- Current working directory: {Vfs.WorkingDirectory}\\n\" +\n \"- The VFS layout: /packs (installed packages), /proc (running module \" +\n \"instances, read-only), /etc (config + services), /home, /data.\\n\" +\n \"\\n\" +\n \"Tools: vfs_list(path), vfs_read(path), vfs_write(path, content, append?), \" +\n \"shell(command), pkg_build(name, source). Use vfs_* for direct file access; use \" +\n \"shell to run HShell command lines and interact with modules (ls, cat, spawn, run, \" +\n \"kill, service, hpm, ...). Use pkg_build to CREATE, COMPILE and HOT-LOAD a brand-new \" +\n \"HCore package from C# source on the fly, then 'spawn'/'run' it via shell — a full \" +\n \"write→compile→(read errors)→fix→run→debug loop, all without a reboot. \" +\n \"All paths are VFS paths. Use tools to inspect or act on the system \" +\n \"before answering. When you have the answer, reply in plain text with no tool \" +\n \"call. Be concise.\\n\" +\n \"\\n\" +\n \"This is an ongoing, multi-turn conversation with the user at a terminal. \" +\n \"If a request is ambiguous or you need more information, ask the user a \" +\n \"clarifying question in plain text (no tool call) and wait for their reply.\\n\" +\n \"\\n\" +\n \"About yourself: you run as a one-shot interactive command spawned by the \" +\n \"shell (instance under init/console), NOT as a registered service. So \" +\n $\"'service stop {InstanceName}' or 'service start agent' will NOT affect this \" +\n \"session — there is no 'agent' service. Your model/endpoint come from \" +\n \"/etc/agent/config.json and are hot-reloaded every turn: if you (or the user) \" +\n \"change 'model' there, it takes effect on the next message automatically — no \" +\n \"restart needed. The user can also switch models with the /model command.\";\n }\n\n private static JsonObject Msg(string role, string content)\n => new() { [\"role\"] = role, [\"content\"] = content };\n\n /// \n /// Split argv (argv[0] == command name) into an optional session name\n /// (-s/--session <name>) and the remaining initial prompt.\n /// \n private static (string? session, string? prompt) ParseLaunchArgs(string[] args)\n {\n string? session = null;\n var rest = new List();\n for (var i = 1; i < args.Length; i++)\n {\n if ((args[i] is \"-s\" or \"--session\") && i + 1 < args.Length)\n {\n session = args[++i];\n }\n else\n {\n rest.Add(args[i]);\n }\n }\n var prompt = rest.Count > 0 ? string.Join(' ', rest).Trim() : null;\n return (session, prompt);\n }\n\n private static JsonElement ParseArgs(string raw)\n {\n try\n {\n return JsonDocument.Parse(string.IsNullOrWhiteSpace(raw) ? \"{}\" : raw).RootElement.Clone();\n }\n catch\n {\n return JsonDocument.Parse(\"{}\").RootElement.Clone();\n }\n }\n\n private static string Compact(string s)\n {\n s = s.Replace(\"\\n\", \" \").Replace(\"\\r\", \" \");\n return s.Length > 120 ? s[..120] + \"...\" : s;\n }\n}\n"}, "4dc73b9e7b24ff410e37dce28a9538b5d64450d5": {"t": 1785756662.139189, "n": 15186, "h": "4bdc207642d17c49", "c": "using System.Text.Json;\nusing System.Text.Json.Nodes;\nusing HCore.Modules.Base;\n\nnamespace HCore.Packages.Agent;\n\n/// \n/// An interactive agent app. Launched from the shell as agent (optionally\n/// with an initial prompt), it takes over the terminal and runs a chat REPL:\n/// you type, it thinks and uses tools (VFS + shell), it answers, and the\n/// conversation history persists across turns so you can reply to its questions.\n/// Type /exit to quit and return to the shell.\n/// \npublic class AgentImplement : BaseImplement, IAgent, IOneshotCommand\n{\n private const string ApiKeyPath = \"/etc/agent/apikey\";\n private const string ConfigPath = \"/etc/agent/config.json\";\n private const int MaxSteps = 12;\n\n private string[] _args = [];\n\n public void SetArguments(string[] args) => _args = args;\n\n public void Run()\n {\n string apiKey;\n try\n {\n apiKey = LoadApiKey();\n }\n catch (Exception ex)\n {\n Console.WriteLine($\"agent: {ex.Message}\");\n return;\n }\n\n var (model, baseUrl) = LoadConfig();\n var client = new DeepseekClient(apiKey, model, baseUrl);\n var tools = new AgentTools(Vfs, Host, ShellPath());\n var store = new SessionStore(Vfs);\n\n // Parse launch args: `agent [-s|--session ] [initial prompt...]`.\n var (sessionArg, promptArg) = ParseLaunchArgs(_args);\n\n var messages = new JsonArray { Msg(\"system\", SystemPrompt()) };\n string sessionName;\n if (sessionArg is not null && store.Exists(sessionArg))\n {\n sessionName = SessionStore.Sanitize(sessionArg);\n var restored = store.Load(sessionName);\n if (restored is not null)\n {\n foreach (var m in restored) messages.Add(m?.DeepClone());\n Console.WriteLine($\"(restored session '{sessionName}' — {restored.Count} messages)\");\n }\n }\n else\n {\n sessionName = sessionArg is not null ? SessionStore.Sanitize(sessionArg) : SessionStore.NewName();\n }\n\n Console.WriteLine($\"HCore Agent ({model}). Session: {sessionName}. Type your message.\");\n Console.WriteLine(\"Commands: /exit, /reset, /model , /sessions, /save [name], /load , /new [name], /help.\");\n Logger.I($\"agent: interactive session started, model={model}, session={sessionName}\");\n\n // Optional initial prompt passed on the command line: `agent do something`.\n var pending = string.IsNullOrWhiteSpace(promptArg) ? null : promptArg;\n\n while (true)\n {\n // Honor our own kill signal: if this instance was reaped (e.g. someone\n // ran `kill` on us), the kernel cancelled StopToken — end the session\n // instead of lingering as a zombie on the shell's thread.\n if (StopToken.IsCancellationRequested)\n {\n Console.WriteLine(\"agent: this instance was killed — ending session.\");\n break;\n }\n\n string? line;\n if (!string.IsNullOrEmpty(pending))\n {\n line = pending;\n pending = null;\n Console.WriteLine($\"you> {line}\");\n }\n else\n {\n Console.Write(\"you> \");\n line = Console.ReadLine();\n }\n\n if (line is null) break; // EOF / Ctrl-D\n line = line.Trim();\n if (line.Length == 0) continue;\n\n if (line is \"/exit\" or \"/quit\") break;\n if (line == \"/help\")\n {\n Console.WriteLine(\n \"Commands: /exit quit · /reset clear conversation · /model switch model ·\\n\" +\n \" /sessions list saved · /save [name] save now · /load restore ·\\n\" +\n \" /new [name] start fresh session · /help this help.\");\n continue;\n }\n if (line == \"/reset\")\n {\n messages = new JsonArray { Msg(\"system\", SystemPrompt()) };\n Console.WriteLine(\"(conversation reset)\");\n continue;\n }\n if (line == \"/sessions\")\n {\n var names = store.List();\n if (names.Count == 0) Console.WriteLine(\"(no saved sessions)\");\n else foreach (var n in names)\n Console.WriteLine($\" {(n == sessionName ? \"*\" : \" \")} {n} ({store.Count(n)} messages)\");\n continue;\n }\n if (line == \"/save\" || line.StartsWith(\"/save \"))\n {\n var given = line.Length > 5 ? line[5..].Trim() : \"\";\n if (given.Length > 0) sessionName = SessionStore.Sanitize(given);\n store.Save(sessionName, model, messages);\n Console.WriteLine($\"(saved session '{sessionName}')\");\n continue;\n }\n if (line == \"/load\" || line.StartsWith(\"/load \"))\n {\n var given = line.Length > 5 ? line[5..].Trim() : \"\";\n if (given.Length == 0) { Console.WriteLine(\"usage: /load \"); continue; }\n var restored = store.Load(given);\n if (restored is null) { Console.WriteLine($\"(no session '{given}')\"); continue; }\n sessionName = SessionStore.Sanitize(given);\n messages = new JsonArray { Msg(\"system\", SystemPrompt()) };\n foreach (var m in restored) messages.Add(m?.DeepClone());\n Console.WriteLine($\"(loaded session '{sessionName}' — {restored.Count} messages)\");\n continue;\n }\n if (line == \"/new\" || line.StartsWith(\"/new \"))\n {\n var given = line.Length > 4 ? line[4..].Trim() : \"\";\n sessionName = given.Length > 0 ? SessionStore.Sanitize(given) : SessionStore.NewName();\n messages = new JsonArray { Msg(\"system\", SystemPrompt()) };\n Console.WriteLine($\"(new session '{sessionName}')\");\n continue;\n }\n if (line == \"/model\" || line.StartsWith(\"/model \"))\n {\n var requested = line.Length > 6 ? line[6..].Trim() : \"\";\n if (requested.Length == 0) { Console.WriteLine($\"model: {model}\"); continue; }\n model = requested;\n SaveModel(model, baseUrl);\n client = new DeepseekClient(apiKey, model, baseUrl);\n Console.WriteLine($\"(model set to {model})\");\n continue;\n }\n\n // Hot-reload config each turn so edits to /etc/agent/config.json\n // (by you, or by the agent itself via a tool) take effect live.\n var (curModel, curBase) = LoadConfig();\n if (curModel != model || curBase != baseUrl)\n {\n model = curModel;\n baseUrl = curBase;\n client = new DeepseekClient(apiKey, model, baseUrl);\n Console.WriteLine($\"(config reloaded: model={model})\");\n }\n\n messages.Add(Msg(\"user\", line));\n if (!RunTurn(client, tools, messages))\n break; // fatal API error already reported\n\n // Auto-persist the conversation so it survives crashes / kills.\n try { store.Save(sessionName, model, messages); }\n catch (Exception ex) { Logger.W($\"agent: session save failed: {ex.Message}\"); }\n }\n\n Console.WriteLine($\"agent: session '{sessionName}' ended.\");\n Logger.I($\"agent: interactive session '{sessionName}' ended.\");\n }\n\n /// \n /// Drive one user turn: call the model, execute any tool calls, loop until it\n /// produces a plain-text answer (or the step budget runs out). Returns false\n /// only on a fatal API error (caller should stop the session).\n /// \n private bool RunTurn(DeepseekClient client, AgentTools tools, JsonArray messages)\n {\n for (var step = 0; step < MaxSteps; step++)\n {\n JsonNode message;\n try\n {\n message = client.Chat(messages, AgentTools.Schema);\n }\n catch (Exception ex)\n {\n Console.WriteLine($\"agent: request failed: {ex.Message}\");\n Logger.E($\"agent request failed: {ex.Message}\");\n return false;\n }\n\n messages.Add(message.DeepClone());\n\n var toolCalls = message[\"tool_calls\"] as JsonArray;\n if (toolCalls is { Count: > 0 })\n {\n foreach (var call in toolCalls)\n {\n if (call is null) continue;\n var id = call[\"id\"]?.GetValue() ?? \"\";\n var fn = call[\"function\"];\n var name = fn?[\"name\"]?.GetValue() ?? \"\";\n var argsRaw = fn?[\"arguments\"]?.GetValue() ?? \"{}\";\n\n var args = ParseArgs(argsRaw);\n Console.WriteLine($\" [tool] {name}({Compact(argsRaw)})\");\n var result = tools.Invoke(name, args);\n Logger.I($\"agent tool {name} -> {result.Length} chars\");\n\n messages.Add(new JsonObject\n {\n [\"role\"] = \"tool\",\n [\"tool_call_id\"] = id,\n [\"content\"] = result,\n });\n }\n continue;\n }\n\n var content = message[\"content\"]?.GetValue() ?? \"\";\n Console.WriteLine($\"agent> {content.Trim()}\");\n return true;\n }\n\n Console.WriteLine($\"agent: stopped after {MaxSteps} steps without a final answer.\");\n return true;\n }\n\n private string ShellPath()\n {\n var name = InstanceName;\n var slash = name.LastIndexOf('/');\n return slash > 0 ? name[..slash] : \"init/console\";\n }\n\n private string LoadApiKey()\n {\n if (Vfs.Exists(ApiKeyPath))\n {\n var key = Vfs.ReadAllText(ApiKeyPath).Trim();\n if (key.Length > 0) return key;\n }\n\n var env = Environment.GetEnvironmentVariable(\"DEEPSEEK_API_KEY\");\n if (!string.IsNullOrWhiteSpace(env)) return env.Trim();\n\n throw new InvalidOperationException(\n $\"no API key. Put it in {ApiKeyPath} or set DEEPSEEK_API_KEY.\");\n }\n\n private (string model, string baseUrl) LoadConfig()\n {\n var model = \"deepseek-chat\";\n var baseUrl = \"https://api.deepseek.com\";\n try\n {\n if (Vfs.Exists(ConfigPath))\n {\n var doc = JsonNode.Parse(Vfs.ReadAllText(ConfigPath));\n model = doc?[\"model\"]?.GetValue() ?? model;\n baseUrl = doc?[\"baseUrl\"]?.GetValue() ?? baseUrl;\n }\n }\n catch { /* fall back to defaults */ }\n return (model, baseUrl);\n }\n\n private void SaveModel(string model, string baseUrl)\n {\n try\n {\n var cfg = new JsonObject { [\"model\"] = model, [\"baseUrl\"] = baseUrl };\n Vfs.WriteAllText(ConfigPath, cfg.ToJsonString());\n }\n catch (Exception ex)\n {\n Console.WriteLine($\"(warning: could not persist config: {ex.Message})\");\n }\n }\n\n private string SystemPrompt()\n {\n return\n \"You are an autonomous agent running inside HCore, a microkernel with a \" +\n \"virtual filesystem (VFS). You help the user by reasoning and using tools.\\n\" +\n \"\\n\" +\n \"Environment:\\n\" +\n $\"- Your instance name: {InstanceName}\\n\" +\n $\"- Current working directory: {Vfs.WorkingDirectory}\\n\" +\n \"- The VFS layout: /packs (installed packages), /proc (running module \" +\n \"instances, read-only), /etc (config + services), /home, /data.\\n\" +\n \"\\n\" +\n \"Tools: vfs_list(path), vfs_read(path), vfs_write(path, content, append?), \" +\n \"shell(command), pkg_build(name, source). Use vfs_* for direct file access; use \" +\n \"shell to run HShell command lines and interact with modules (ls, cat, spawn, run, \" +\n \"kill, service, hpm, ...). Use pkg_build to CREATE, COMPILE and HOT-LOAD a brand-new \" +\n \"HCore package from C# source on the fly, then 'spawn'/'run' it via shell — a full \" +\n \"write→compile→(read errors)→fix→run→debug loop, all without a reboot. \" +\n \"All paths are VFS paths. Use tools to inspect or act on the system \" +\n \"before answering. When you have the answer, reply in plain text with no tool \" +\n \"call. Be concise.\\n\" +\n \"\\n\" +\n \"This is an ongoing, multi-turn conversation with the user at a terminal. \" +\n \"If a request is ambiguous or you need more information, ask the user a \" +\n \"clarifying question in plain text (no tool call) and wait for their reply.\\n\" +\n \"\\n\" +\n \"About yourself: you run as a one-shot interactive command spawned by the \" +\n \"shell (instance under init/console), NOT as a registered service. So \" +\n $\"'service stop {InstanceName}' or 'service start agent' will NOT affect this \" +\n \"session — there is no 'agent' service. Your model/endpoint come from \" +\n \"/etc/agent/config.json and are hot-reloaded every turn: if you (or the user) \" +\n \"change 'model' there, it takes effect on the next message automatically — no \" +\n \"restart needed. The user can also switch models with the /model command.\";\n }\n\n private static JsonObject Msg(string role, string content)\n => new() { [\"role\"] = role, [\"content\"] = content };\n\n /// \n /// Split argv (argv[0] == command name) into an optional session name\n /// (-s/--session <name>) and the remaining initial prompt.\n /// \n private static (string? session, string? prompt) ParseLaunchArgs(string[] args)\n {\n string? session = null;\n var rest = new List();\n for (var i = 1; i < args.Length; i++)\n {\n if ((args[i] is \"-s\" or \"--session\") && i + 1 < args.Length)\n {\n session = args[++i];\n }\n else\n {\n rest.Add(args[i]);\n }\n }\n var prompt = rest.Count > 0 ? string.Join(' ', rest).Trim() : null;\n return (session, prompt);\n }\n\n private static JsonElement ParseArgs(string raw)\n {\n try\n {\n return JsonDocument.Parse(string.IsNullOrWhiteSpace(raw) ? \"{}\" : raw).RootElement.Clone();\n }\n catch\n {\n return JsonDocument.Parse(\"{}\").RootElement.Clone();\n }\n }\n\n private static string Compact(string s)\n {\n s = s.Replace(\"\\n\", \" \").Replace(\"\\r\", \" \");\n return s.Length > 120 ? s[..120] + \"...\" : s;\n }\n}\n"}}}}