my-pi-agent 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/README.md +318 -0
  2. package/package.json +45 -0
  3. package/pyproject.toml +50 -0
  4. package/src/my_agent_core/__init__.py +123 -0
  5. package/src/my_agent_core/agent.py +441 -0
  6. package/src/my_agent_core/background.py +121 -0
  7. package/src/my_agent_core/context.py +505 -0
  8. package/src/my_agent_core/events.py +153 -0
  9. package/src/my_agent_core/extensions/__init__.py +9 -0
  10. package/src/my_agent_core/extensions/core.py +197 -0
  11. package/src/my_agent_core/hooks.py +130 -0
  12. package/src/my_agent_core/loop.py +709 -0
  13. package/src/my_agent_core/main.py +134 -0
  14. package/src/my_agent_core/memory.py +241 -0
  15. package/src/my_agent_core/message_queue.py +110 -0
  16. package/src/my_agent_core/plugins.py +212 -0
  17. package/src/my_agent_core/registry.py +186 -0
  18. package/src/my_agent_core/session/__init__.py +79 -0
  19. package/src/my_agent_core/session/entries.py +197 -0
  20. package/src/my_agent_core/session/jsonl.py +60 -0
  21. package/src/my_agent_core/session/memory.py +137 -0
  22. package/src/my_agent_core/session/session.py +400 -0
  23. package/src/my_agent_core/session/storage.py +245 -0
  24. package/src/my_agent_core/session/store.py +131 -0
  25. package/src/my_agent_core/session/tree.py +86 -0
  26. package/src/my_agent_core/skills.py +149 -0
  27. package/src/my_agent_core/subagent_tasks.py +170 -0
  28. package/src/my_agent_core/subagents.py +148 -0
  29. package/src/my_agent_core/task_store.py +248 -0
  30. package/src/my_agent_core/tool_history.py +189 -0
  31. package/src/my_agent_core/tools/__init__.py +5 -0
  32. package/src/my_agent_core/tools/builtin/__init__.py +5 -0
  33. package/src/my_agent_core/tools/builtin/task.py +30 -0
  34. package/src/my_agent_core/tools/builtin/task_tools.py +215 -0
  35. package/src/my_agent_core/tools/core.py +239 -0
  36. package/src/my_agent_llm/__init__.py +45 -0
  37. package/src/my_agent_llm/auth/__init__.py +46 -0
  38. package/src/my_agent_llm/auth/antigravity.py +209 -0
  39. package/src/my_agent_llm/auth/manager.py +259 -0
  40. package/src/my_agent_llm/auth/quota.py +56 -0
  41. package/src/my_agent_llm/auth/schema.py +94 -0
  42. package/src/my_agent_llm/client.py +116 -0
  43. package/src/my_agent_llm/config.py +17 -0
  44. package/src/my_agent_llm/events.py +84 -0
  45. package/src/my_agent_llm/models.py +195 -0
  46. package/src/my_agent_llm/providers/__init__.py +4 -0
  47. package/src/my_agent_llm/providers/_base.py +94 -0
  48. package/src/my_agent_llm/providers/anthropic.py +298 -0
  49. package/src/my_agent_llm/providers/antigravity.py +480 -0
  50. package/src/my_agent_llm/providers/deepseek.py +196 -0
  51. package/src/my_agent_llm/providers/openai.py +364 -0
  52. package/src/my_agent_llm/providers/registry.py +16 -0
  53. package/src/my_agent_llm/stream.py +218 -0
  54. package/src/my_coding_agent/__init__.py +66 -0
  55. package/src/my_coding_agent/agent.py +208 -0
  56. package/src/my_coding_agent/cli.py +78 -0
  57. package/src/my_coding_agent/file_reference.py +80 -0
  58. package/src/my_coding_agent/macro.py +408 -0
  59. package/src/my_coding_agent/mcp.py +243 -0
  60. package/src/my_coding_agent/mutation_queue.py +37 -0
  61. package/src/my_coding_agent/paths.py +119 -0
  62. package/src/my_coding_agent/permissions.py +84 -0
  63. package/src/my_coding_agent/prompt.py +54 -0
  64. package/src/my_coding_agent/rpc_server.py +2817 -0
  65. package/src/my_coding_agent/settings.py +126 -0
  66. package/src/my_coding_agent/tools/__init__.py +55 -0
  67. package/src/my_coding_agent/tools/base.py +58 -0
  68. package/src/my_coding_agent/tools/bash.py +206 -0
  69. package/src/my_coding_agent/tools/edit.py +226 -0
  70. package/src/my_coding_agent/tools/find.py +118 -0
  71. package/src/my_coding_agent/tools/grep.py +177 -0
  72. package/src/my_coding_agent/tools/ls.py +112 -0
  73. package/src/my_coding_agent/tools/read.py +113 -0
  74. package/src/my_coding_agent/tools/write.py +72 -0
  75. package/tui/README.md +27 -0
  76. package/tui/bin/my-agent.js +98 -0
  77. package/tui/dist/app.d.ts +41 -0
  78. package/tui/dist/app.js +110 -0
  79. package/tui/dist/bridge/event-translator.d.ts +92 -0
  80. package/tui/dist/bridge/event-translator.js +216 -0
  81. package/tui/dist/bridge/kernel-bridge.d.ts +48 -0
  82. package/tui/dist/bridge/kernel-bridge.js +132 -0
  83. package/tui/dist/client.d.ts +63 -0
  84. package/tui/dist/client.js +239 -0
  85. package/tui/dist/components/assistant-message.d.ts +19 -0
  86. package/tui/dist/components/assistant-message.js +90 -0
  87. package/tui/dist/components/compaction-summary-message.d.ts +19 -0
  88. package/tui/dist/components/compaction-summary-message.js +46 -0
  89. package/tui/dist/components/custom-editor.d.ts +18 -0
  90. package/tui/dist/components/custom-editor.js +56 -0
  91. package/tui/dist/components/dynamic-border.d.ts +9 -0
  92. package/tui/dist/components/dynamic-border.js +14 -0
  93. package/tui/dist/components/footer.d.ts +39 -0
  94. package/tui/dist/components/footer.js +199 -0
  95. package/tui/dist/components/header.d.ts +4 -0
  96. package/tui/dist/components/header.js +21 -0
  97. package/tui/dist/components/keys.d.ts +5 -0
  98. package/tui/dist/components/keys.js +12 -0
  99. package/tui/dist/components/login-selector.d.ts +26 -0
  100. package/tui/dist/components/login-selector.js +181 -0
  101. package/tui/dist/components/logout-selector.d.ts +19 -0
  102. package/tui/dist/components/logout-selector.js +88 -0
  103. package/tui/dist/components/model-selector.d.ts +40 -0
  104. package/tui/dist/components/model-selector.js +268 -0
  105. package/tui/dist/components/session-selector.d.ts +54 -0
  106. package/tui/dist/components/session-selector.js +393 -0
  107. package/tui/dist/components/settings-selector.d.ts +24 -0
  108. package/tui/dist/components/settings-selector.js +146 -0
  109. package/tui/dist/components/status-indicator.d.ts +25 -0
  110. package/tui/dist/components/status-indicator.js +60 -0
  111. package/tui/dist/components/theme-selector.d.ts +14 -0
  112. package/tui/dist/components/theme-selector.js +77 -0
  113. package/tui/dist/components/thinking-selector.d.ts +21 -0
  114. package/tui/dist/components/thinking-selector.js +128 -0
  115. package/tui/dist/components/tool-execution.d.ts +31 -0
  116. package/tui/dist/components/tool-execution.js +206 -0
  117. package/tui/dist/components/tree-selector.d.ts +40 -0
  118. package/tui/dist/components/tree-selector.js +173 -0
  119. package/tui/dist/components/user-message-selector.d.ts +21 -0
  120. package/tui/dist/components/user-message-selector.js +103 -0
  121. package/tui/dist/components/user-message.d.ts +5 -0
  122. package/tui/dist/components/user-message.js +15 -0
  123. package/tui/dist/index.d.ts +11 -0
  124. package/tui/dist/index.js +11 -0
  125. package/tui/dist/interactive/chat-viewport.d.ts +19 -0
  126. package/tui/dist/interactive/chat-viewport.js +41 -0
  127. package/tui/dist/interactive/components.d.ts +1 -0
  128. package/tui/dist/interactive/components.js +1 -0
  129. package/tui/dist/interactive/interactive-mode.d.ts +89 -0
  130. package/tui/dist/interactive/interactive-mode.js +1625 -0
  131. package/tui/dist/interactive/theme.d.ts +1 -0
  132. package/tui/dist/interactive/theme.js +1 -0
  133. package/tui/dist/interactive/tui-renderer.d.ts +8 -0
  134. package/tui/dist/interactive/tui-renderer.js +10 -0
  135. package/tui/dist/protocol.d.ts +78 -0
  136. package/tui/dist/protocol.js +1 -0
  137. package/tui/dist/theme/dark.json +54 -0
  138. package/tui/dist/theme/light.json +71 -0
  139. package/tui/dist/theme/theme.d.ts +20 -0
  140. package/tui/dist/theme/theme.js +86 -0
  141. package/tui/package.json +25 -0
@@ -0,0 +1,119 @@
1
+ """AgentPaths: 统一管理全局用户目录与项目本地资源的路径调度中心。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import re
7
+ from dataclasses import dataclass, field
8
+ from hashlib import sha256
9
+ from pathlib import Path
10
+
11
+ __all__ = ["AgentPaths"]
12
+
13
+
14
+ @dataclass(frozen=True, slots=True)
15
+ class AgentPaths:
16
+ """集中解析与管理 my-pi-agent 的全局与项目级路径。"""
17
+
18
+ home: Path = field(
19
+ default_factory=lambda: Path(os.environ.get("MY_AGENT_HOME") or (Path.home() / ".my-pi-agent")).resolve()
20
+ )
21
+ agents_home: Path = field(default_factory=lambda: (Path.home() / ".agents").resolve())
22
+
23
+ # ── 全局资源路径 ──
24
+ @property
25
+ def auth_path(self) -> Path:
26
+ return self.home / "auth.json"
27
+
28
+ @property
29
+ def auth_lock_path(self) -> Path:
30
+ return self.home / "auth.json.lock"
31
+
32
+ @property
33
+ def settings_path(self) -> Path:
34
+ return self.home / "settings.json"
35
+
36
+ @property
37
+ def sessions_dir(self) -> Path:
38
+ return self.home / "sessions"
39
+
40
+ @property
41
+ def skills_dir(self) -> Path:
42
+ return self.home / "skills"
43
+
44
+ @property
45
+ def prompts_dir(self) -> Path:
46
+ return self.home / "prompts"
47
+
48
+ @property
49
+ def themes_dir(self) -> Path:
50
+ return self.home / "themes"
51
+
52
+ @property
53
+ def extensions_dir(self) -> Path:
54
+ return self.home / "extensions"
55
+
56
+ @property
57
+ def logs_dir(self) -> Path:
58
+ return self.home / "logs"
59
+
60
+ # ── 项目局部路径 ──
61
+ def project_agent_dir(self, cwd: Path) -> Path:
62
+ return cwd / ".my-pi-agent"
63
+
64
+ def project_settings_path(self, cwd: Path) -> Path:
65
+ return self.project_agent_dir(cwd) / "settings.json"
66
+
67
+ def project_skills_dir(self, cwd: Path) -> Path:
68
+ return self.project_agent_dir(cwd) / "skills"
69
+
70
+ def project_agents_skills_dir(self, cwd: Path) -> Path:
71
+ return cwd / ".agents" / "skills"
72
+
73
+ # ── 会话分区映射算法 (Tau Slug + Hash 优化版) ──
74
+ def project_session_dir(self, cwd: Path) -> Path:
75
+ """根据项目 cwd 计算全局唯一的 sessions/<slug>-<hash> 目录。"""
76
+ resolved = cwd.resolve()
77
+ digest = sha256(str(resolved).encode("utf-8")).hexdigest()[:6]
78
+ slug = self._slugify_path(resolved)
79
+ target = self.sessions_dir / f"{slug}-{digest}"
80
+ target.mkdir(parents=True, exist_ok=True)
81
+ return target
82
+
83
+ def default_session_path(self, cwd: Path) -> Path:
84
+ return self.project_session_dir(cwd) / "default.jsonl"
85
+
86
+ def ensure_directories(self) -> None:
87
+ """初次启动自动建巢,静默初始化目录骨架。"""
88
+ for d in (
89
+ self.home,
90
+ self.sessions_dir,
91
+ self.skills_dir,
92
+ self.prompts_dir,
93
+ self.themes_dir,
94
+ self.extensions_dir,
95
+ self.logs_dir,
96
+ ):
97
+ d.mkdir(parents=True, exist_ok=True)
98
+
99
+ @staticmethod
100
+ def _slugify_path(path: Path, max_length: int = 48) -> str:
101
+ parts = [p for p in path.parts if p not in (path.anchor, "")]
102
+ try:
103
+ rel = path.relative_to(Path.home())
104
+ parts = ["home", *rel.parts]
105
+ except ValueError:
106
+ pass
107
+ normalized = [clean for p in parts if (clean := re.sub(r"[^a-zA-Z0-9._-]+", "-", p).strip(".-_").lower())]
108
+ slug = "-".join(normalized)
109
+ if len(slug) <= max_length:
110
+ return slug or "project"
111
+
112
+ suffix_parts: list[str] = []
113
+ cur_len = 0
114
+ for p in reversed(normalized):
115
+ if cur_len + len(p) + 1 > max_length:
116
+ break
117
+ suffix_parts.append(p)
118
+ cur_len += len(p) + 1
119
+ return "-".join(reversed(suffix_parts)) or slug[-max_length:].strip("-") or "project"
@@ -0,0 +1,84 @@
1
+ """业务权限门禁(PermissionGate):基于 ToolCallHook 实现的无侵入安全审批门禁。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import inspect
6
+ from collections.abc import Awaitable, Callable
7
+ from dataclasses import dataclass, field
8
+ from typing import Any, Literal
9
+
10
+ from my_agent_core.hooks import HookResult, ToolCallHook
11
+
12
+ PermissionMode = Literal["review", "autonomous", "yolo", "strict"]
13
+ READONLY_TOOLS = frozenset({"read", "grep", "find"})
14
+ SAFE_BASH_PREFIXES = (
15
+ "git status",
16
+ "git diff",
17
+ "git log",
18
+ "pytest",
19
+ "python -m pytest",
20
+ "uv run",
21
+ )
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class PermissionRequest:
26
+ """权限审批请求对象。"""
27
+
28
+ action: str
29
+ target: str
30
+ details: dict[str, Any] = field(default_factory=dict)
31
+ preview: str | None = None
32
+
33
+
34
+ class PermissionGate:
35
+ """基于 ToolCallHook 实现的无侵入安全审批门禁。"""
36
+
37
+ def __init__(
38
+ self,
39
+ mode: PermissionMode = "review",
40
+ confirm_callback: Callable[[PermissionRequest], Awaitable[bool] | bool] | None = None,
41
+ ) -> None:
42
+ self.mode: PermissionMode = "autonomous" if mode == "yolo" else mode
43
+ self.confirm_callback = confirm_callback
44
+
45
+ async def __call__(self, hook: ToolCallHook) -> HookResult:
46
+ if self.mode == "autonomous":
47
+ return HookResult()
48
+
49
+ tool_name = hook.tool_name
50
+ args = hook.args or {}
51
+
52
+ # 1. 只读工具放行 (除非 strict 模式)
53
+ if self.mode != "strict" and tool_name in READONLY_TOOLS:
54
+ return HookResult()
55
+
56
+ # 2. 安全 Shell 命令放行
57
+ if tool_name == "bash" and self.mode == "review":
58
+ cmd = str(args.get("command", "")).strip()
59
+ if any(cmd.startswith(p) for p in SAFE_BASH_PREFIXES):
60
+ return HookResult()
61
+
62
+ # 3. 发起用户交互审查
63
+ if not self.confirm_callback:
64
+ # 无交互回调时默认放行
65
+ return HookResult()
66
+
67
+ target = str(args.get("path") or args.get("command") or tool_name)
68
+ preview = None
69
+ if tool_name in ("write", "edit"):
70
+ preview = args.get("content") or str(args.get("edits", ""))
71
+
72
+ req = PermissionRequest(
73
+ action=tool_name,
74
+ target=target,
75
+ details=args,
76
+ preview=preview,
77
+ )
78
+
79
+ res = self.confirm_callback(req)
80
+ approved = await res if inspect.isawaitable(res) else res
81
+ if not approved:
82
+ return HookResult(block=True, reason=f"用户拒绝了工具 [{tool_name}] 的执行请求。")
83
+
84
+ return HookResult()
@@ -0,0 +1,54 @@
1
+ """专业编码系统提示词与上下文自动注入。"""
2
+
3
+ import logging
4
+ from pathlib import Path
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+ DEFAULT_CODING_INSTRUCTIONS = """You are an expert coding assistant operating inside the codebase.
9
+
10
+ Core Engineering Principles:
11
+ 1. Read Before Edit: Always inspect file contents with 'read' before calling 'edit'. Never guess line numbers or code blocks.
12
+ 2. Surgical Edits: Keep edits concise and provide sufficient context in 'oldText' to ensure a unique match.
13
+ 3. Verification Before Completion: Run tests or linters using 'bash' to verify changes before declaring work complete.
14
+ 4. Minimal Changes: Do not add unsolicited refactorings, comments, or unnecessary abstractions.
15
+ """
16
+
17
+
18
+ def build_default_coding_prompt(workspace: Path | str) -> str:
19
+ """构建专业编码系统提示词,自动扫描并注入项目指导文件。
20
+
21
+ Args:
22
+ workspace: 工作区目录路径(支持 Path 或 str)。
23
+
24
+ Returns:
25
+ 包含工程规范、工作区路径以及项目上下文的完整系统提示词。
26
+ """
27
+ workspace_path = Path(workspace).resolve()
28
+ sections = [
29
+ DEFAULT_CODING_INSTRUCTIONS,
30
+ f"Workspace Directory: {workspace_path}",
31
+ ]
32
+
33
+ # 扫描并注入项目指导文件
34
+ context_files = ["AGENTS.md", "CLAUDE.md", "README.md"]
35
+ injected_contexts = []
36
+ for fname in context_files:
37
+ fpath = workspace_path / fname
38
+ if fpath.is_file():
39
+ try:
40
+ content = fpath.read_text(encoding="utf-8", errors="replace")
41
+ injected_contexts.append(
42
+ f'<project_instructions path="{fpath}">\n{content}\n</project_instructions>'
43
+ )
44
+ except Exception:
45
+ logger.debug("Failed to read context file: %s", fpath, exc_info=True)
46
+
47
+ if injected_contexts:
48
+ sections.append(
49
+ "<project_context>\n"
50
+ + "\n\n".join(injected_contexts)
51
+ + "\n</project_context>"
52
+ )
53
+
54
+ return "\n\n".join(sections)