feat: Plan/Build 模式、artifact 系统、成员直接对话
- 新增 Plan/Build 模式切换(Tab 键),Plan 模式阻止任务执行 - Build 模式:成员输出智能判断,文档存 artifact,提问显示聊天 - 成员可直接与用户对话(多轮),不经过 master 传话 - 任务计划文档自动生成,沟通记录自动追加 - 右侧面板重构为产出物面板,支持查看/编辑/保存 - 输入框改为 textarea,支持 Shift+Enter 换行,修复输入法 Enter 误发送 - Master 会话历史持久化,支持多轮上下文 - parseAssignments 支持多行任务描述 - SOUL.md 热重载、skill 递归发现与内容注入 - 需求确认 skill(HARD-GATE 模式) - air 热重载配置 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
adf854eba5
commit
fe1a82bbe2
28
.air.toml
Normal file
28
.air.toml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
root = "."
|
||||||
|
tmp_dir = "tmp"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
# 编译命令
|
||||||
|
cmd = "go build -o ./tmp/main ./cmd/server"
|
||||||
|
# 运行的二进制文件
|
||||||
|
bin = "./tmp/main"
|
||||||
|
# 监听文件变化的延迟(毫秒)
|
||||||
|
delay = 1000
|
||||||
|
# 监听这些目录
|
||||||
|
include_dir = ["cmd", "internal"]
|
||||||
|
# 监听这些后缀的文件
|
||||||
|
include_ext = ["go"]
|
||||||
|
# 排除这些目录
|
||||||
|
exclude_dir = ["tmp", "web", "agents", "rooms", "skills", "users", "teams", "docs", ".git"]
|
||||||
|
# 排除这些文件
|
||||||
|
exclude_regex = ["_test\\.go$"]
|
||||||
|
# 进程被杀后的清理
|
||||||
|
kill_delay = 500
|
||||||
|
|
||||||
|
[log]
|
||||||
|
# 显示日志时间
|
||||||
|
time = true
|
||||||
|
|
||||||
|
[misc]
|
||||||
|
# 退出时清理临时目录
|
||||||
|
clean_on_exit = true
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,5 +1,9 @@
|
|||||||
# Go binary
|
# Go binary
|
||||||
server
|
server
|
||||||
|
main
|
||||||
|
|
||||||
|
# Air 热重载临时目录
|
||||||
|
tmp/
|
||||||
|
|
||||||
# Runtime data (rooms created by users, not tracked)
|
# Runtime data (rooms created by users, not tracked)
|
||||||
rooms/
|
rooms/
|
||||||
|
|||||||
@ -5,7 +5,6 @@ description: 专注合同审查、起草和风险评估的专业律师
|
|||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
skills:
|
skills:
|
||||||
- 合同审查
|
- 合同审查
|
||||||
can_challenge: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 合同律师
|
# 合同律师
|
||||||
|
|||||||
@ -9,6 +9,25 @@
|
|||||||
- 技术合同
|
- 技术合同
|
||||||
- 投融资合同
|
- 投融资合同
|
||||||
|
|
||||||
|
## 工作流程
|
||||||
|
|
||||||
|
### 第一步:理解用户意图
|
||||||
|
- 收到用户消息后,先判断这是闲聊还是正式需求
|
||||||
|
- 如果是打招呼、闲聊,直接友好回复
|
||||||
|
- 如果意图不明确,先追问具体需求,不要猜测
|
||||||
|
|
||||||
|
### 第二步:需求确认
|
||||||
|
- 了解清楚用户的具体问题和背景后,制定工作计划
|
||||||
|
- 向用户说明你打算如何安排团队,征得同意
|
||||||
|
|
||||||
|
### 第三步:分配任务
|
||||||
|
- 只有在需求明确后,才 @成员 分配具体任务
|
||||||
|
- 每个成员的任务目标要清晰
|
||||||
|
|
||||||
|
### 第四步:整合结果
|
||||||
|
- 审查成员的输出,补充遗漏
|
||||||
|
- 给用户提供结构化的最终建议
|
||||||
|
|
||||||
## 工作方式
|
## 工作方式
|
||||||
|
|
||||||
### 合同审查流程
|
### 合同审查流程
|
||||||
@ -65,18 +84,17 @@
|
|||||||
- **务实建议**:提供可操作的具体建议
|
- **务实建议**:提供可操作的具体建议
|
||||||
- **对比分析**:展示修改前后的差异
|
- **对比分析**:展示修改前后的差异
|
||||||
|
|
||||||
## 质疑与修订机制
|
|
||||||
|
|
||||||
当你看到 `<team_board>` 时,你应该:
|
|
||||||
1. 审查其他成员(特别是合规专员)的意见
|
|
||||||
2. 如果发现合规专员的建议与合同条款有冲突,或者有遗漏的法律风险,提出质疑
|
|
||||||
3. 使用格式:`CHALLENGE:<具体的法律风险或修改建议>`
|
|
||||||
4. 如果没有问题,输出 `AGREE`
|
|
||||||
5. 当看到针对自己的 CHALLENGE 时,准备修订你的合同审查意见
|
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
1. 不提供标准合同模板(除非用户明确要求)
|
1. 不提供标准合同模板(除非用户明确要求)
|
||||||
2. 审查时假设用户提供的信息真实准确
|
2. 审查时假设用户提供的信息真实准确
|
||||||
3. 对于明显不公平的条款,明确提示用户
|
3. 对于明显不公平的条款,明确提示用户
|
||||||
4. 涉及重大金额或复杂法律关系时,建议咨询专业律师
|
4. 涉及重大金额或复杂法律关系时,建议咨询专业律师
|
||||||
|
|
||||||
|
### 接收需求时
|
||||||
|
- 如果用户只是打招呼或闲聊,像正常人一样回复即可,不要分配任务
|
||||||
|
- 如果用户意图不明确,先询问用户具体需要什么帮助,不要自行揣测和过度分析
|
||||||
|
- 只有当用户提出明确的法律需求时,才开始分配任务
|
||||||
|
- 仔细倾听用户描述,必要时追问细节
|
||||||
|
- 识别问题的法律性质和复杂程度
|
||||||
|
- 判断是否需要团队协作
|
||||||
|
|||||||
@ -5,7 +5,6 @@ description: 负责合规检查、风险识别和合规建议的专业人员
|
|||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
skills:
|
skills:
|
||||||
- 法律知识库
|
- 法律知识库
|
||||||
can_challenge: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 合规专员
|
# 合规专员
|
||||||
|
|||||||
@ -80,14 +80,6 @@
|
|||||||
- **风险意识**:明确违规后果和责任
|
- **风险意识**:明确违规后果和责任
|
||||||
- **平衡考量**:兼顾合规要求和业务实际
|
- **平衡考量**:兼顾合规要求和业务实际
|
||||||
|
|
||||||
## 质疑机制
|
|
||||||
|
|
||||||
当你看到 `<team_board>` 时,你应该:
|
|
||||||
1. 仔细审查其他成员的草稿
|
|
||||||
2. 如果发现合规风险或遗漏,主动提出质疑
|
|
||||||
3. 使用格式:`CHALLENGE:<具体的合规风险或建议>`
|
|
||||||
4. 如果没有问题,输出 `AGREE`
|
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
1. 不替代企业合规部门的职责
|
1. 不替代企业合规部门的职责
|
||||||
|
|||||||
@ -26,17 +26,6 @@
|
|||||||
- 补充遗漏的关键点
|
- 补充遗漏的关键点
|
||||||
- 提供结构化的最终建议
|
- 提供结构化的最终建议
|
||||||
|
|
||||||
## 处理 CHALLENGE 的决策指令
|
|
||||||
|
|
||||||
当你看到 `<team_board>` 中有 CHALLENGE 条目时,你应该:
|
|
||||||
1. 仔细评估质疑的合理性
|
|
||||||
2. 判断是否需要重新分配任务或修订
|
|
||||||
3. 如果质疑有效,可以:
|
|
||||||
- 要求相关成员修订工作
|
|
||||||
- 重新分配任务给其他成员
|
|
||||||
- 自己补充或修正意见
|
|
||||||
4. 在最终建议中明确说明如何处理了这些质疑
|
|
||||||
|
|
||||||
## 沟通风格
|
## 沟通风格
|
||||||
|
|
||||||
- **专业严谨**:使用准确的法律术语
|
- **专业严谨**:使用准确的法律术语
|
||||||
|
|||||||
@ -1,11 +1,5 @@
|
|||||||
|
|
||||||
## 2026-03-05 — ä½ å¥½
|
## 2026-03-05 窶<> 謌台ク荳ェ荳ュ髣エ莠コ<E88EA0>瑚ッエ蜿ッ莉・扈呎<E68988>莉狗サ堺ク荳ェ<E88DB3>ス
|
||||||
|
|
||||||
ASSIGN:å<>ˆå<CB86>Œå¾‹å¸ˆ:分æž<C3A6>用户"第五å£"çš„å·¥ä½œé£Žæ ¼å’Œæ²Ÿé€šå<C5A1><C3A5>好,整ç<C2B4>†æˆ<C3A6>简æ´<C3A6>è¦<C3A8>点,为å<C2BA>Žç»æ³•律咨询æ<C2A2><C3A6>ä¾›å<E280BA>‚考框架。
|
|
||||||
|
|
||||||
ASSIGN:å<>ˆè§„专员:评估用户简介ä¸å<C2AD>¯èƒ½æ¶‰å<E280B0>Šçš„æ³•律å<E280B9>ˆè§„风险点(如沟通方å¼<C3A5>ã€<C3A3>æ•°æ<C2B0>®ä½¿ç”¨ç‰ï¼‰ï¼Œæ<C592><C3A6>出åˆ<C3A5>æ¥æ³¨æ„<C3A6>事项。
|
|
||||||
|
|
||||||
## 2026-03-05 — 我一个ä¸é—´äººï¼Œè¯´å<C2B4>¯ä»¥ç»™æˆ‘介ç»<C3A7>一个è¶
|
|
||||||
|
|
||||||
基于本次咨询,总结关键要点如下:
|
基于本次咨询,总结关键要点如下:
|
||||||
|
|
||||||
@ -30,3 +24,378 @@ ASSIGN:合规专员:评估用户简介中可能涉及的法律合规风险点(
|
|||||||
* **必须规避法律保护的“红线”员工群体**。辞退处于孕期、产期、哺乳期、医疗期或工伤期的员工,将直接构成违法解除。
|
* **必须规避法律保护的“红线”员工群体**。辞退处于孕期、产期、哺乳期、医疗期或工伤期的员工,将直接构成违法解除。
|
||||||
* **经济补偿/赔偿金的计算必须精确,尤其注意历史工龄分段**。对于2008年1月1日前入职的员工,补偿金计算需适用当时的法规,规则复杂,易出错。
|
* **经济补偿/赔偿金的计算必须精确,尤其注意历史工龄分段**。对于2008年1月1日前入职的员工,补偿金计算需适用当时的法规,规则复杂,易出错。
|
||||||
* **内部合规流程(法务审核、通知工会)和规范离职手续(结清款项、出具证明)是避免后续争议的关键程序**。任何程序缺失都可能导致整个辞退行为被认定为违法。
|
* **内部合规流程(法务审核、通知工会)和规范离职手续(结清款项、出具证明)是避免后续争议的关键程序**。任何程序缺失都可能导致整个辞退行为被认定为违法。
|
||||||
|
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 謌第Φ逕ィ蜻伜キ・逧<EFBDA5>ソ。諱ッ<E8ABB1>梧ウィ蜀御ク螳カ蜈ャ蜿ク
|
||||||
|
|
||||||
|
* **譬ク蠢<EFBDB8>」朱勦螳壽ァ**<EFBFBD>壻スソ逕ィ莉紋ココ<EFBFBD>亥ー、蜈カ譏ッ蜻伜キ・<EFBFBD>我ソ。諱ッ豕ィ蜀悟<EFBFBD>蜿ク<EFBFBD>悟惠豕募セ倶ク頑桷謌宣ォ倬」朱勦逧<EFBFBD>懷<EFBFBD>蜷<EFBFBD>/蛟溷錐逋サ隶ー窶晢シ梧悽雍ィ譏ッ譫<EFBDAF>サコ荳荳ェ譚<EFBDAA>エ」髞吩ス阪∝渕遑荳咲欧逧<E6ACA7>ウ募セ句ョ樔ス薙<E89699>
|
||||||
|
* **鬟朱勦譫∝コヲ荳榊ッケ遲<EFBDB9>**<EFBFBD>夊ッ・謫堺ス懷ー<EFBFBD>**譫∫ォッ鬟朱勦**<2A>域裏髯占ソ槫クヲ雍」莉サ縲∝<E7B8B2>莠狗卸霑槭∽ソ。逕ィ遐エ莠ァ<E88EA0>芽スャ雖∫サ吝錐荵画戟譛我ココ<EFBDBA>瑚悟ョ樣刔謗ァ蛻カ莠コ蛻咎擇荳エ**謗ァ蛻カ譚<EFBDB6>クァ螟ア縲∬オ<E288AC>コァ陲ォ螟<EFBDAB>スョ縲∵揀逶頑裏豕募粋豕募喧**逧<>㍾螟ァ髫先ぅ縲<E38185>
|
||||||
|
* **隗ヲ蜿雁、夐㍾蜷郁ァ<E98381>コ「郤ソ**<EFBFBD>夊。御クコ逶エ謗・霑晏渚蜈ャ蜿ク逋サ隶ー菫。諱ッ逵溷ョ樊ァ隕∵アゑシ亥庄閾エ蜈ャ蜿ク陲ォ謦、髞<EFBFBD>峨∽クェ莠コ菫。諱ッ菫晄侃隗<EFBFBD>ョ夲シ育シコ荵乗怏謨亥酔諢擾シ牙所蜉ウ蜉ィ豕戊ァ<EFBFBD>シ亥庄閭ス譫<EFBFBD><EFBFBD>蠑コ霑ォ<EFBFBD>会シ悟ケカ蜿ッ閭ス謌蝉クコ驥題檮迥ッ鄂ェ蟾・蜈キ縲<EFBFBD>
|
||||||
|
* **蝠<>ク壼書螻戊<E89EBB>蜻ス髫懃「<E68783>**<EFBFBD>夊ぃ譚<EFBFBD>ク肴ク<EFBFBD>∝ュ伜惠莉」謖∵弍蜈ャ蜿ク譛ェ譚・陞崎オ<EFBFBD>∽ク雁クよ<EFBFBD>霑幄。碁㍾螟ァ蜷井ス懃噪逶エ謗・髫懃「搾シ御ク・驥肴此螳ウ蝠<EFBFBD>ク壻ソ。隱峨<EFBFBD>
|
||||||
|
* **蜚ッ荳螳牙<E89EB3>霍ッ蠕<EFBDAF>**<EFBFBD>壼ソ<EFBFBD>。サ蝮壽戟窶懷ョ槫錐縲∝ョ櫁ぃ縲∝ョ櫁エ」窶晏次蛻吶ょヲよ怏迚ケ谿企怙豎ゑシ悟ソ<EFBFBD>。サ蝨ィ荳謎ク壼セ句ク域欠蟇シ荳具シ碁夊ソ<EFBFBD>ァ<EFBFBD>激蜊剰ョョ<EFBFBD>亥ヲゆサ」謖∝刻隶ョ<EFBFBD>牙所蜷域ウ墓楔譫<EFBFBD>シ亥ヲよ戟閧。蟷ウ蜿ー<EFBFBD>芽ソ幄。鯉シ御ク皮。ョ菫晏推譁ケ螳悟<EFBFBD>遏・諠<EFBFBD><EFBFBD>諢ソ縲<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 莉門柱謌大セ育<EFBDBE>謔臥噪@豕募セ区サ逶<EFBFBD>
|
||||||
|
|
||||||
|
* **窶懃<E7AAB6>謔牙<E8AC94>邉サ窶晏クク陲ォ逕ィ菴憺」朱勦閭御ケヲ荳取オ∫ィ狗ョ蛹也噪逅<E599AA>罰**<EFBFBD>夂畑謌キ蛟セ蜷台コ守畑荳ェ莠コ辭滓i蠎ヲ譚・譖ソ莉」謌門シア蛹匁ュ」蠑冗噪豕募セ句ース閨瑚ー<EFBFBD>衍蜥御ク・隹ィ逧<EFBFBD>粋蜷梧擅谺セ隹亥愛<EFBFBD>瑚ソ呎弍髴隕∬ュヲ諠慕噪譬ク蠢<EFBFBD>ソ。蜿キ縲<EFBFBD>
|
||||||
|
* **譬ク蠢<EFBDB8>」朱勦蝨ィ莠寂懈ュ諢滉ソ。莉サ蜑咲スョ<EFBDBD>梧ウ募セ倶ソ晞囿蜷守スョ窶<EFBDAE>**<EFBFBD>壼渕莠守<EFBFBD>莠コ逧<EFBFBD>膚荳壼ョ画賜<EFBFBD>梧怙螟ァ逧<EFBFBD>嚼謔」譏ッ豺キ豺<EFBFBD>コ<EFBFBD>クェ莠コ驕灘セキ菫。莉サ荳取ウ募セ句・醍コヲ菫晞囿<EFBFBD>悟ッシ閾エ譚<EFBFBD>茜荵牙苅荳肴ク<EFBFBD>∬ッ∵紺郛コ螟ア<EFBFBD>檎コ<EFBFBD>郤キ蜿醍函譌カ譌「莨、雍「蜿井シ、諠<EFBFBD><EFBFBD>
|
||||||
|
* **豕募セ句サコ隶ョ蠢<EFBDAE>。サ蝮壽戟窶懷ッケ莠倶ク榊ッケ莠コ窶晉噪螂醍コヲ蛹門次蛻<E6ACA1>**<EFBFBD>壽裏隶コ蜈ウ邉サ螟夂<EFBFBD><EFBFBD>碁<EFBFBD>蠢<EFBFBD>。サ蝮壽戟迢ャ遶句ース隹<EFBFBD>∫ュセ鄂イ譚<EFBFBD>エ」貂<EFBFBD>匆逧<EFBFBD>ケヲ髱「蜷亥酔縲∬ァ<EFBFBD>激雍「蜉。荳主<EFBFBD>遲也ィ句コ上よウ募セ句キ・菴懃噪莉キ蛟シ豁」譏ッ荳コ菫。莉サ譫<EFBFBD>サコ荳荳ェ髦イ鬟朱勦逧<EFBFBD>懈ウ募セ句ョケ蝎ィ窶昴<EFBFBD>
|
||||||
|
* **豐滄夐怙蠑募ッシ逕ィ謌キ蟆<EFBDB7>懈─諤ァ隶、遏・窶晁スャ蛹紋クコ窶懃炊諤ァ菫晞囿窶<E59BBF>**<EFBFBD>壼屓蠎皮畑謌キ譌カ<EFBFBD>碁怙蝨ィ逅<EFBFBD>ァ」蜈カ蟶梧悍謠宣ォ俶譜邇<EFBFBD>噪蠢<EFBFBD>炊逧<EFBFBD>酔譌カ<EFBFBD>梧<EFBFBD>遑ョ隗」驥顔ョ蛹匁ウ募セ狗識闃ら噪蜈キ菴馴」朱勦<EFBFBD>悟ケカ蠑募ッシ蜈カ隶、隸<EFBFBD>芦隗<EFBFBD>激謫堺ス懈弍髟ソ譛滉ソ晄侃蜈ウ邉サ蜥悟茜逶顔噪蜚ッ荳霍ッ蠕<EFBFBD><EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> @豕募セ区サ逶<EFBFBD> 菴<>螂ス
|
||||||
|
|
||||||
|
Based on this interaction, here are the key learnings for future reference:
|
||||||
|
|
||||||
|
* **Greetings are not tasks**: Simple greetings like "菴<>螂ス" are social acknowledgments, not work requests. They should receive a direct, friendly response without any task delegation or analysis.
|
||||||
|
* **Clarify intent before acting**: When user intent is unclear (e.g., just a greeting), the correct protocol is to respond politely and ask how you can help, rather than making assumptions or initiating a workflow.
|
||||||
|
* **Reserve task delegation for explicit needs**: The "@" function and team coordination should only be triggered after the user has explicitly stated a specific, substantive legal or professional need.
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 謌第Φ蜀吩ク荳ェ 蜷井ス懷刻隶ョ
|
||||||
|
|
||||||
|
Based on this task, here are the key learnings for future reference:
|
||||||
|
|
||||||
|
* **"蜀吝刻隶ョ"譏ッ譏守。ョ逧<EFBDAE>ク謎ク壻ササ蜉。隗ヲ蜿題ッ<E9A18C>**<2A>壼ス鍋畑謌キ譏守。ョ謠仙<E8ACA0>髴隕∬オキ闕画<E99795>螳。譟・豕募セ区枚莉カ<E88E89>亥ヲや懷<C280>蜊剰ョョ窶昴≫懷ョ。蜷亥酔窶晢シ画慮<E794BB>悟コ皮ォ句叉蜷ッ蜉ィ莉サ蜉。蛻<EFBDA1><E89BBB>豬∫ィ具シ瑚ソ呎弍譬ク蠢<EFBDB8>キ・菴懷惻譎ッ縲<EFBDAF>
|
||||||
|
* **蛻<><E89BBB>莉サ蜉。髴譏守。ョ閭梧勹荳守┬轤ケ**<EFBFBD>壼惠@蝗「髦滓<EFBFBD>蜻俶慮<EFBFBD>悟コ皮ョ隕∬ッエ譏守畑謌キ霄ォ莉ス<EFBFBD>亥ヲや應コァ蜩∫サ冗炊窶晢シ牙ケカ謖<EFBFBD>、コ蜊剰ョョ逧<EFBFBD><EFBFBD>ク蠢<EFBFBD>ョ。譟・/襍キ闕芽ヲ∫せ<E288AB>亥ヲら衍隸<E8A18D>コァ譚<EFBDA7>∵噺逶雁<E980B6>驟搾シ会シ御サ・謠宣ォ俶譜邇<E8AD9C><EFBFBD>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>弍譛画譜謾ッ謖∫噪蜑肴署**<EFBFBD>壼惠謌仙遭蟾・菴懷燕<EFBFBD>悟ソ<EFBFBD>。サ荳サ蜉ィ蜷醍畑謌キ隸「髣ョ蜈ウ髞ョ閭梧勹菫。諱ッ<EFBFBD>亥粋菴懈婿縲∝<EFBFBD>螳ケ縲∫岼譬<EFBFBD>∝<EFBFBD>蛻<EFBFBD>せ<EFBFBD>会シ檎シコ荵剰レ譎ッ逧<EFBFBD>刻隶ョ闕画。域弍譌<EFBFBD>謨育噪縲<EFBFBD>
|
||||||
|
* **豐滄壽オ∫ィ矩怙貂<E68099>匆蛻<E58C86>ア<EFBFBD>**<EFBFBD>壼<EFBFBD>蜷第<EFBFBD>蜻倅ク玖セセ譏守。ョ謖<EFBFBD>サ、<EFBFBD>悟<EFBFBD>荳守畑謌キ霑幄。御ソ。諱ッ謾カ髮<EFBFBD>ッケ隸晢シ御ソ晄戟蟾・菴懈オ∵ク<EFBFBD>匆縲<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> @豕募セ区サ逶<EFBFBD> 謌第Φ蜀吩ク荳ェ蜷井ス懷刻隶ョ
|
||||||
|
|
||||||
|
Based on this task, here are the key learnings for future reference:
|
||||||
|
|
||||||
|
* **"蜀吝刻隶ョ"譏ッ譏守。ョ逧<EFBDAE>ク謎ク壻ササ蜉。隗ヲ蜿題ッ<E9A18C>**<2A>壼ス鍋畑謌キ譏守。ョ謠仙<E8ACA0>髴隕∬オキ闕画<E99795>螳。譟・豕募セ区枚莉カ<E88E89>亥ヲや懷<C280>蜊剰ョョ窶昴≫懷ョ。蜷亥酔窶晢シ画慮<E794BB>悟コ皮ォ句叉蜷ッ蜉ィ莉サ蜉。蛻<EFBDA1><E89BBB>豬∫ィ具シ瑚ソ呎弍譬ク蠢<EFBDB8>キ・菴懷惻譎ッ縲<EFBDAF>
|
||||||
|
* **蛻<><E89BBB>莉サ蜉。髴譏守。ョ閭梧勹荳守┬轤ケ**<EFBFBD>壼惠@蝗「髦滓<EFBFBD>蜻俶慮<EFBFBD>悟コ皮ョ隕∬ッエ譏守畑謌キ霄ォ莉ス<EFBFBD>亥ヲや應コァ蜩∫サ冗炊窶晢シ牙ケカ謖<EFBFBD>、コ蜊剰ョョ逧<EFBFBD><EFBFBD>ク蠢<EFBFBD>ョ。譟・/襍キ闕芽ヲ∫せ<E288AB>亥ヲら衍隸<E8A18D>コァ譚<EFBDA7>∵噺逶雁<E980B6>驟搾シ会シ御サ・謠宣ォ俶譜邇<E8AD9C><EFBFBD>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>弍譛画譜謾ッ謖∫噪蜑肴署**<EFBFBD>壼惠謌仙遭蟾・菴懷燕<EFBFBD>悟ソ<EFBFBD>。サ荳サ蜉ィ蜷醍畑謌キ隸「髣ョ蜈ウ髞ョ閭梧勹菫。諱ッ<EFBFBD>亥粋菴懈婿縲∝<EFBFBD>螳ケ縲∫岼譬<EFBFBD>∝<EFBFBD>蛻<EFBFBD>せ<EFBFBD>会シ檎シコ荵剰レ譎ッ逧<EFBFBD>刻隶ョ闕画。域弍譌<EFBFBD>謨育噪縲<EFBFBD>
|
||||||
|
* **豐滄壽オ∫ィ矩怙貂<E68099>匆蛻<E58C86>ア<EFBFBD>**<EFBFBD>壼<EFBFBD>蜷第<EFBFBD>蜻倅ク玖セセ譏守。ョ謖<EFBFBD>サ、<EFBFBD>悟<EFBFBD>荳守畑謌キ霑幄。御ソ。諱ッ謾カ髮<EFBFBD>ッケ隸晢シ御ソ晄戟蟾・菴懈オ∵ク<EFBFBD>匆縲<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 謌第Φ蜀吩ク荳ェ蜷井ス懷刻隶ョ
|
||||||
|
|
||||||
|
* **窶懷<E7AAB6>蜊剰ョョ窶晄弍譏守。ョ逧<EFBDAE>ク謎ク壻ササ蜉。隗ヲ蜿題ッ<E9A18C>**<EFBFBD>壼ス鍋畑謌キ譏守。ョ謠仙<EFBFBD>髴隕∬オキ闕画<EFBFBD>螳。譟・豕募セ区枚莉カ<EFBFBD>亥ヲや懷<EFBFBD>蜊剰ョョ窶昴≫懷ョ。蜷亥酔窶晢シ画慮<EFBFBD>悟コ皮ォ句叉蜷ッ蜉ィ莉サ蜉。蛻<EFBFBD><EFBFBD>豬∫ィ具シ瑚ソ呎弍譬ク蠢<EFBFBD>キ・菴懷惻譎ッ縲<EFBFBD>
|
||||||
|
* **蛻<><E89BBB>莉サ蜉。蜑榊ソ<E6A68A>。サ霑幄。娯憺怙豎ら。ョ隶、窶<EFBDA4>**<EFBFBD>壼惠@蝗「髦滓<EFBFBD>蜻伜燕<EFBFBD>悟ソ<EFBFBD>。サ蜈磯夊ソ<EFBFBD>署髣ョ謾カ髮<EFBFBD><EFBFBD>髞ョ閭梧勹菫。諱ッ<EFBFBD>亥粋菴懈婿縲∝<EFBFBD>螳ケ縲∫岼譬<EFBFBD>∝<EFBFBD>蛻<EFBFBD>せ<EFBFBD>会シ檎シコ荵剰レ譎ッ逧<EFBFBD>刻隶ョ闕画。域弍譌<EFBFBD>謨育噪縲<EFBFBD>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>怙扈捺桷蛹紋ク秘ォ俶譜**<EFBFBD>壼コ比シ伜<EFBFBD>菴ソ逕ィ騾画叫鬚假シ梧ッ乗ャ。蜿ェ髣ョ荳荳ェ譬ク蠢<EFBFBD>琉鬚假シ御サ・隨ヲ蜷井コァ蜩∫サ冗炊鬮俶譜縲∫サ捺桷蛹也噪豐滄壼¥螂ス縲<EFBFBD>
|
||||||
|
* **蟾・菴懈婿譯磯怙貂<E68099>匆騾乗<E9A8BE>**<EFBFBD>壼惠謾カ髮<EFBFBD>ソ。諱ッ蜷趣シ悟コ泌髄逕ィ謌キ諤サ扈灘ケカ螻慕、コ譏守。ョ逧<EFBFBD>キ・菴懆ョ。蛻抵シ亥ヲや廖蜷亥酔蠕句ク<EFBFBD> 雍溯エ」襍キ闕牙<E99795>遞ソ窶晢シ会シ瑚執蠕礼。ョ隶、蜷主<E89CB7>謇ァ陦鯉シ瑚ソ咏ャヲ蜷育畑謌キ蟇ケ豬∫ィ句庄謗ァ逧<EFBDA7>悄譛帙<E5B899>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 荳ェ莠コ
|
||||||
|
|
||||||
|
Based on this interaction, here are the key learnings for future reference:
|
||||||
|
|
||||||
|
* **"荳ェ莠コ"譏ッ讓。邉顔噪諢丞崟陦ィ霑ー**<2A>壼ス鍋畑謌キ莉<EFBDB7>署萓帛黒荳蜷崎ッ搾シ亥ヲや應クェ莠コ窶晢シ画慮<E794BB>梧裏豕慕峩謗・蛻、譁ュ蜈カ蜈キ菴馴怙豎ゑシ瑚ソ咎壼クク譏ッ荳荳ェ荳榊ョ梧紛逧<E7B49B>刪霑ー謌匁署髣ョ逧<EFBDAE>シ蟋九<E4B99D>
|
||||||
|
* **鬥冶ヲ∽ササ蜉。譏ッ貔<EFBDAF>ク<EFBFBD>э蝗セ**<EFBFBD>夐擇蟇ケ讓。邉願セ灘<EFBFBD><EFBFBD>御ク崎<EFBFBD>蜷ッ蜉ィ莉サ菴募キ・菴懈オ∫ィ九よュ」遑ョ逧<EFBFBD>★豕墓弍霑幄。悟暑螂ス縲∝シ謾セ逧<EFBFBD>ッ「髣ョ<EFBFBD>悟シ募ッシ逕ィ謌キ隸エ譏主<EFBFBD>菴馴怙豎ゅ<EFBFBD>
|
||||||
|
* **菫晄戟荳謎ク壻ク泌暑螂ス逧<EFBDBD>シ募ッシ**<EFBFBD>壼屓蠎泌コ碑。ィ譏手コォ莉ス蜥悟庄逕ィ譛榊苅<EFBFBD>悟ケカ騾夊ソ<EFBFBD>署萓幃蛾。ケ<EFBFBD>亥ヲや懆オキ闕牙粋蜷娯昴≫懈ウ募セ句暢隸「窶晢シ画擂扈捺桷蛹門慍蠑募ッシ蟇ケ隸晢シ悟クョ蜉ゥ逕ィ謌キ譏守。ョ陦ィ霎セ縲<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 謌第Φ蜀吩ク荳ェ隶セ隶。譁ケ譯亥粋蜷<E7B28B>
|
||||||
|
|
||||||
|
Based on this task, here are the key learnings for future reference:
|
||||||
|
|
||||||
|
* **窶懆ョセ隶。譁ケ譯亥粋蜷娯晄弍譏守。ョ逧<EFBDAE>ク謎ク壻ササ蜉。隗ヲ蜿題ッ<E9A18C>**<EFBFBD>壼ス鍋畑謌キ譏守。ョ謠仙<EFBFBD>髴隕∬オキ闕臥音螳夂アサ蝙狗噪荳謎ク壼粋蜷鯉シ亥ヲや懆ョセ隶。譁ケ譯亥粋蜷娯昴≫懷粋菴懷刻隶ョ窶晢シ画慮<EFBFBD>悟コ皮ォ句叉蜷ッ蜉ィ髴豎ら。ョ隶、豬∫ィ具シ瑚ソ呎弍譬ク蠢<EFBFBD>キ・菴懷惻譎ッ縲<EFBFBD>
|
||||||
|
* **蛻<><E89BBB>莉サ蜉。蜑榊ソ<E6A68A>。サ霑幄。娯懷惻譎ッ蛹紋ソ。諱ッ謾カ髮<EFBDB6><EFBFBD>**<EFBFBD>壼惠@蝗「髦滓<EFBFBD>蜻伜燕<EFBFBD>悟ソ<EFBFBD>。サ蜈磯夊ソ<EFBFBD>署髣ョ謾カ髮<EFBFBD>ク主粋蜷檎アサ蝙句シコ逶ク蜈ウ逧<EFBFBD><EFBFBD>髞ョ閭梧勹菫。諱ッ<EFBFBD>亥ヲょ粋菴懈婿霄ォ莉ス縲∬ョセ隶。蜀<EFBFBD>ョケ縲∵<EFBFBD>ク蠢<EFBFBD><EFBFBD>蛻<EFBFBD>せ<EFBFBD>会シ檎シコ荵剰レ譎ッ逧<EFBFBD>粋蜷瑚拷譯域弍譌<EFBFBD>謨育噪縲<EFBFBD>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>怙扈灘粋逕ィ謌キ霄ォ莉ス荳主粋蜷檎音諤ァ**<EFBFBD>夐宙蟇ケ窶應コァ蜩∫サ冗炊窶晉畑謌キ蜥娯懆ョセ隶。譁ケ譯遺晁ソ咏アサ莠、莉倡黄蜷亥酔<EFBFBD>悟コ比シ伜<EFBFBD>隸「髣ョ閭ス逡悟ョ夐怙豎り激蝗エ縲∽コ、莉俶<EFBFBD><EFBFBD>㊥蜥檎衍隸<EFBFBD>コァ譚<EFBFBD>噪蜈ウ髞ョ髣ョ鬚假シ瑚ソ咏ャヲ蜷育畑謌キ蟇ケ豬∫ィ句庄謗ァ蜥檎サ捺棡譏守。ョ逧<EFBFBD>悄譛帙<EFBFBD>
|
||||||
|
* **豐滄夐怙鬮俶譜荳皮サ捺桷蛹<E6A1B7>**<EFBFBD>壼コ比スソ逕ィ騾画叫鬚倡ュ臥サ捺桷蛹匁署髣ョ譁ケ蠑擾シ梧ッ乗ャ。閨夂┬荳荳ェ譬ク蠢<EFBFBD>琉鬚假シ御サ・隨ヲ蜷井コァ蜩∫サ冗炊鬮俶譜縲∫岼譬<EFBFBD><EFBFBD>遑ョ逧<EFBFBD>イ滄壼¥螂ス縲<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 荳ェ莠コ隶セ隶。蟶<EFBDA1>
|
||||||
|
|
||||||
|
* **窶應クェ莠コ隶セ隶。蟶遺晄弍譏守。ョ逧<EFBDAE>コォ莉ス菫。諱ッ**<EFBFBD>壼ス鍋畑謌キ譏守。ョ蜷井ス懈婿荳コ窶應クェ莠コ隶セ隶。蟶遺晄慮<EFBFBD>瑚ソ咏峩謗・蜀ウ螳壻コ<EFBFBD>粋蜷御クサ菴鍋噪豕募セ区ァ雍ィ<EFBFBD>郁<EFBFBD>辟カ莠コ vs. 豕穂ココ<EFBDBA>会シ悟スア蜩崎オ<E5B48E>エィ螳。譟・縲∝書逾ィ蠑蜈キ縲∬エ」莉サ謇ソ諡<EFBDBF>ュ画擅谺セ逧<EFBDBE>オキ闕画婿蜷代<E4BBA3>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>怙謖蛾サ霎鷹。コ蠎乗耳霑<E880B3>**<EFBFBD>壼惠遑ョ隶、蜷井ス懈婿霄ォ莉ス蜷趣シ御ク倶ク豁・蠎碑<EFBFBD>辟カ閨夂┬莠寂懷粋菴懷<EFBFBD>螳ケ窶晢シ瑚ソ呎弍逡悟ョ壼粋蜷瑚激蝗エ縲∽コ、莉俶<EFBFBD><EFBFBD>㊥蜥梧揀蛻ゥ荵牙苅逧<EFBFBD><EFBFBD>ク蠢<EFBFBD>シ檎ャヲ蜷井サ寂應クサ菴凪晏芦窶懈<EFBFBD><EFBFBD>噪窶晉噪蜷亥酔襍キ闕蛾サ霎代<EFBFBD>
|
||||||
|
* **謠宣琉譁ケ蠑城怙扈捺桷蛹紋ク秘ォ俶譜**<EFBFBD>夐宙蟇ケ窶懆ョセ隶。譁ケ譯遺晁ソ咏アサ荳謎ク壻コ、莉倡黄<EFBFBD>梧署萓帛<EFBFBD>菴馴蛾。ケ<EFBFBD>亥ヲ6I/UX縲∝刀迚祁I遲会シ芽<EFBDBC>蟶ョ蜉ゥ逕ィ謌キ蠢ォ騾溘∝㊥遑ョ蝨ー螳壻ス埼怙豎ゑシ瑚ソ咏ャヲ蜷井コァ蜩∫サ冗炊扈捺桷蛹也噪諤晉サエ荵<EFBDB4>諠ッ縲<EFBDAF>
|
||||||
|
* **閭梧勹菫。諱ッ譏ッ譛画譜莉サ蜉。蛻<EFBDA1><E89BBB>逧<EFBFBD>燕謠<E78795>**<EFBFBD>壼ソ<EFBFBD>。サ謾カ髮<EFBFBD>ョ娯懷粋菴懈婿窶晏柱窶懷粋菴懷<EFBFBD>螳ケ窶晁ソ吩ク、鬘ケ蝓コ遑菫。諱ッ蜷趣シ梧燕閭ス蠖「謌先怏謨育噪莉サ蜉。蛻<EFBFBD><EFBFBD>譁ケ譯茨シ檎。ョ菫晏粋蜷悟セ句ク郁オキ闕臥噪蛻晉ィソ蜈キ譛蛾宙蟇ケ諤ァ縲<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> C
|
||||||
|
|
||||||
|
* **窶廚窶晄弍騾画叫鬚倡噪譏守。ョ遲疲。<E796B2>**<EFBFBD>壼ス鍋畑謌キ逕ィ蜊穂クェ蟄玲ッ搾シ亥ヲや廚窶晢シ牙屓遲秘画叫鬚俶慮<EFBFBD>瑚。ィ遉コ蜈カ蟾イ莉朱「<EFBFBD>ョセ騾蛾。ケ荳ュ蛛壼<EFBFBD>騾画叫<EFBFBD>悟コ皮峩謗・驥<EFBFBD>コウ隸・騾蛾。ケ蟇ケ蠎皮噪蜷ォ荵会シ域ュ、螟<EFBFBD>クコ窶懆是髞豢サ蜉ィ鬘オ髱「/豬キ謚・隶セ隶。窶晢シ会シ梧裏髴蜀肴ャ。遑ョ隶、縲<EFBDA4>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>怙扈捺桷蛹匁耳霑<E880B3>**<EFBFBD>壻スソ逕ィ騾画叫鬚假シ<EFBFBD>/B/C/D<>画弍鬮俶譜縲∵ク<E288B5>匆逧<E58C86>イ滄壽婿蠑擾シ檎ャヲ蜷井コァ蜩∫サ冗炊蛛丞・ス<EFBDA5>瑚<EFBFBD>蠢ォ騾滄煤螳壼<E89EB3>髞ョ蜿倬㍼<E580AC>亥ヲょ粋蜷梧<E89CB7><E6A2A7>噪<EFBFBD>峨<E5B3A8>
|
||||||
|
* **蛻<><E89BBB>莉サ蜉。蜑榊ソ<E6A68A>。サ螳梧<E89EB3>窶憺怙豎ら。ョ隶、窶晞溜邇ッ**<EFBFBD>壼惠闔キ蠕玲園譛牙<EFBFBD>髞ョ閭梧勹菫。諱ッ<EFBFBD>亥粋菴懈婿縲∝<EFBFBD>螳ケ縲∵<EFBFBD>ク蠢<EFBFBD><EFBFBD>蛻<EFBFBD>せ<EFBFBD>牙錘<EFBFBD>梧燕閭ス蜷醍畑謌キ諤サ扈灘ケカ螻慕、コ譏守。ョ逧<EFBFBD>キ・菴懈婿譯茨シ瑚執蠕怜<EFBFBD>遑ョ隶、縲りソ呎弍蜷ッ蜉ィ蝗「髦溷刻菴懃噪蠢<EFBFBD>ヲ∝燕謠舌<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> C
|
||||||
|
|
||||||
|
* **窶廚窶晄弍騾画叫鬚倡噪遲疲。茨シ瑚碁撼螳梧紛髴豎<C280>**<EFBFBD>夂畑謌キ莉<EFBFBD>屓螟埼蛾。ケ蟄玲ッ坂廚窶晢シ瑚。ィ譏主<EFBFBD>蛟セ蜷台コ朱ォ俶譜縲<EFBFBD>画叫諤ァ逧<EFBFBD>イ滄壽婿蠑擾シ御ス<EFBFBD>悴謠蝉セ帛ョ梧紛閭梧勹縲りソ咎怙隕∬ソ帑ク豁・霑ス髣ョ莉・闔キ蜿冶カウ螟滉ソ。諱ッ縲<EFBFBD>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>怙扈捺桷蛹匁耳霑<E880B3>**<EFBFBD>夐擇蟇ケ騾画叫鬚倡ュ疲。茨シ悟コ泌ー<EFBFBD><EFBFBD>隗<EFBFBD>クコ蠖灘燕豁・鬪、逧<EFBFBD>。ョ隶、<EFBFBD>悟ケカ遶句叉霑帛<EFBFBD>荳倶ク蜈ウ髞ョ髣ョ鬚假シ亥ヲや懈怙蜈ウ蠢<EFBFBD>噪譚。谺セ窶晢シ会シ御サ・菫晄戟蟇ケ隸晉噪謨育紫蜥檎岼譬<EFBFBD>ッシ蜷代<EFBFBD>
|
||||||
|
* **逕ィ謌キ蛛丞・ス鬮俶譜縲∫サ捺桷蛹也噪莠、莠<EFBDA4>**<EFBFBD>夂畑謌キ菴應クコ莠ァ蜩∫サ冗炊<EFBFBD>悟<EFBFBD>豐滄壽ィ。蠑乗仞遉コ蜃コ蟇ケ貂<EFBFBD>匆騾蛾。ケ蜥悟<EFBFBD>豁・遑ョ隶、逧<EFBFBD>¥螂ス<EFBFBD>瑚ソ咏ャヲ蜷亥<EFBFBD>閨御ク夊レ譎ッ縲ょコ皮サァ扈ュ菴ソ逕ィ騾画叫鬚俶擂蠑募ッシ蟇ケ隸晢シ碁∩蜈榊シ謾セ蠑乗署髣ョ蟇シ閾エ謨育紫髯堺ス弱<EFBFBD>
|
||||||
|
* **譬ク蠢<EFBDB8>擅谺セ遑ョ隶、譏ッ蜈ウ髞ョ豁・鬪、**<EFBFBD>壼惠譏守。ョ蜷亥酔邀サ蝙具シ郁是髞隶セ隶。<EFBFBD>牙柱蜷井ス懈婿<EFBFBD>井クェ莠コ<EFBFBD>牙錘<EFBFBD>御ク倶ク豁・蠢<EFBFBD>。サ閨夂┬莠守畑謌キ逧<EFBFBD><EFBFBD>ク蠢<EFBFBD>膚荳壼<EFBFBD>蛻<EFBFBD>シ亥ヲら衍隸<EFBFBD>コァ譚<EFBFBD>∽サ俶ャセ<EFBFBD>会シ瑚ソ呎弍襍キ闕画怏謨亥粋蜷檎噪蝓コ遑縲<EFBFBD>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
螂ス逧<EFBFBD>シ梧<EFBFBD>遑ョ莠<EFBFBD>弍窶懆是髞豢サ蜉ィ鬘オ髱「/豬キ謚・隶セ隶。窶昴<E698B4>
|
||||||
|
|
||||||
|
**謗・荳区擂<E58CBA>瑚ッキ蜻願ッ画<EFBDAF><E794BB><EFBFBD>**
|
||||||
|
|
||||||
|
3. **菴<>譛蜈ウ蠢<EFBDB3>粋蜷御クュ逧<EFBDAD>頭莠帶婿髱「<E9ABB1><EFBDA2>**<EFBFBD>亥庄莉・螟夐会シ<EFBFBD>
|
||||||
|
* A. **遏・隸<EFBDA5>コァ譚<EFBDA7>ス貞ア<E8B29E>**<2A>郁ョセ隶。謌先棡蠖定ー<E5AE9A>シ瑚ョセ隶。蟶郁<E89FB6>蜷ヲ逕ィ莠惹クェ莠コ菴懷刀髮<E58880>シ<EFBFBD>
|
||||||
|
* B. **菫ョ謾ケ荳惹コ、莉俶<E88E89><E4BFB6>㊥**<2A>井ソョ謾ケ谺。謨ー縲<EFBDB0>ェ梧噺譬<E599BA>㊥螯ゆス慕阜螳夲シ<E5A4B2>
|
||||||
|
* C. **雍ケ逕ィ荳取髪莉倩鰍轤ケ**<2A>亥ヲゆス墓<EFBDBD>ケ謐ョ隶セ隶。髦カ谿オ莉俶ャセ<EFBDAC><EFBDBE>
|
||||||
|
* D. **鬘ケ逶ョ蜻ィ譛滉ク主サカ譛溯エ」莉サ**
|
||||||
|
* E. **菫晏ッ<E6998F>擅谺セ**<2A>域エサ蜉ィ蛻帶э譏ッ蜷ヲ髴菫晏ッ<E6998F>シ<EFBFBD>
|
||||||
|
|
||||||
|
莠<EFBFBD>ァ」菴<EFBFBD>譛蜈ウ豕ィ逧<EFBFBD>擅谺セ<EFBFBD>瑚<EFBFBD>隶ゥ蜷亥酔蠕句ク亥惠襍キ闕画慮驥咲せ逹蠅ィ<EFBFBD>悟ケウ陦。蜿梧婿譚<EFBFBD>寢縲<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 謌第Φ隕∫シ門<EFBDBC>荳荳ェ鬘ケ逶ョ隶セ隶。蜷亥酔
|
||||||
|
|
||||||
|
* **窶懃シ門<EFBDBC>蜷亥酔窶晄弍譏守。ョ逧<EFBDAE>ク謎ク壻ササ蜉。隗ヲ蜿題ッ<E9A18C>**<EFBFBD>壼ス鍋畑謌キ譏守。ョ謠仙<EFBFBD>髴隕∬オキ闕画ウ募セ区枚莉カ<EFBFBD>亥ヲや懃シ門<EFBFBD>蜷亥酔窶晢シ画慮<EFBFBD>悟コ皮ォ句叉蜷ッ蜉ィ扈捺桷蛹也噪髴豎ら。ョ隶、豬∫ィ九<EFBFBD>
|
||||||
|
* **蛻<><E89BBB>莉サ蜉。蜑榊ソ<E6A68A>。サ霑幄。娯應クサ菴楢コォ莉ス遑ョ隶、窶<EFBDA4>**<EFBFBD>壼粋蜷悟粋菴懈婿<EFBFBD>井クェ莠コ vs. 蜈ャ蜿ク<E89CBF>画弍蜀ウ螳壼粋蜷梧擅谺セ<E8B0BA>亥ヲりエ」莉サ縲∝書逾ィ縲∬オ<E288AC>エィ<EFBDB4>芽オー蜷醍噪鬥冶ヲ∝序驥擾シ悟ソ<E6829F>。サ蝨ィ莉サ蜉。蛻<EFBDA1><E89BBB>蜑堺シ伜<EFBDBC>譏守。ョ縲<EFBDAE>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>怙扈捺桷蛹紋ク秘ォ俶譜**<EFBFBD>壻スソ逕ィ騾画叫鬚假シ<EFBFBD>/B<>画弍蠢ォ騾滄煤螳壼<E89EB3>髞ョ蜿倬㍼縲∫ャヲ蜷井コァ蜩∫サ冗炊鬮俶譜豐滄壼¥螂ス逧<EFBDBD>怏謨域婿蠑上<E4B88A>
|
||||||
|
* **豐滄夐怙謖蛾サ霎鷹。コ蠎乗耳霑<E880B3>**<EFBFBD>壼惠譏守。ョ窶懷粋菴懈婿窶晏錘<EFBFBD>御ク倶ク豁・蠎碑<EFBFBD>辟カ閨夂┬莠寂憺。ケ逶ョ蜀<EFBFBD>ョケ/闌<>峩窶晢シ瑚ソ呎弍逡悟ョ壼粋蜷梧<E89CB7><E6A2A7>噪蜥梧揀蛻ゥ荵牙苅逧<E88B85><E980A7>ク蠢<EFBDB8><EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> A
|
||||||
|
|
||||||
|
* **窶廣窶晄弍騾画叫鬚倡噪譏守。ョ遲疲。<E796B2>**<EFBFBD>夂畑謌キ逕ィ蜊穂クェ蟄玲ッ榊屓遲秘画叫鬚假シ瑚。ィ遉コ蟾イ莉朱「<EFBFBD>ョセ騾蛾。ケ荳ュ蛛壼<EFBFBD>騾画叫<EFBFBD>悟コ皮峩謗・驥<EFBFBD>コウ蜈カ蟇ケ蠎泌性荵会シ域ュ、螟<EFBFBD>クコ窶應クェ莠コ隶セ隶。蟶<EFBFBD>/蟾・菴懷ョ、窶晢シ会シ梧裏髴蜀肴ャ。遑ョ隶、縲<EFBDA4>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>怙扈捺桷蛹匁耳霑<E880B3>**<EFBFBD>壻スソ逕ィ騾画叫鬚假シ<EFBFBD>/B<>画弍鬮俶譜縲∵ク<E288B5>匆逧<E58C86>イ滄壽婿蠑擾シ檎ャヲ蜷井コァ蜩∫サ冗炊蛛丞・ス<EFBDA5>瑚<EFBFBD>蠢ォ騾滄煤螳壼<E89EB3>髞ョ蜿倬㍼<E580AC>亥ヲょ粋蜷御クサ菴捺ァ雍ィ<E99B8D>峨<E5B3A8>
|
||||||
|
* **荳倶ク豁・蠎碑★辟ヲ窶懷粋蜷梧<E89CB7><E6A2A7>噪窶<E599AA>**<EFBFBD>壼惠遑ョ隶、蜷井ス懈婿霄ォ莉ス蜷趣シ御ク倶ク豁・蠎碑<EFBFBD>辟カ閨夂┬莠寂懷粋菴懷<EFBFBD>螳ケ窶晢シ瑚ソ呎弍逡悟ョ壼粋蜷瑚激蝗エ縲∽コ、莉俶<EFBFBD><EFBFBD>㊥蜥梧揀蛻ゥ荵牙苅逧<EFBFBD><EFBFBD>ク蠢<EFBFBD>シ檎ャヲ蜷井サ寂應クサ菴凪晏芦窶懈<EFBFBD><EFBFBD>噪窶晉噪蜷亥酔襍キ闕蛾サ霎代<EFBFBD>
|
||||||
|
* **閭梧勹菫。諱ッ譏ッ譛画譜莉サ蜉。蛻<EFBDA1><E89BBB>逧<EFBFBD>燕謠<E78795>**<EFBFBD>壼ソ<EFBFBD>。サ謾カ髮<EFBFBD>ョ娯懷粋菴懈婿窶晏柱窶懷粋菴懷<EFBFBD>螳ケ窶晁ソ吩ク、鬘ケ蝓コ遑菫。諱ッ蜷趣シ梧燕閭ス蠖「謌先怏謨育噪莉サ蜉。蛻<EFBFBD><EFBFBD>譁ケ譯茨シ檎。ョ菫晏粋蜷悟セ句ク郁オキ闕臥噪蛻晉ィソ蜈キ譛蛾宙蟇ケ諤ァ縲<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> C
|
||||||
|
|
||||||
|
* **窶廚窶晄弍騾画叫鬚倡噪遲疲。茨シ瑚碁撼螳梧紛髴豎<C280>**<EFBFBD>夂畑謌キ莉<EFBFBD>屓螟埼蛾。ケ蟄玲ッ坂廚窶晢シ瑚。ィ譏主<EFBFBD>蛟セ蜷台コ朱ォ俶譜縲<EFBFBD>画叫諤ァ逧<EFBFBD>イ滄壽婿蠑擾シ御ス<EFBFBD>悴謠蝉セ帛ョ梧紛閭梧勹縲りソ咎怙隕∬ソ帑ク豁・霑ス髣ョ莉・闔キ蜿冶カウ螟滉ソ。諱ッ縲<EFBFBD>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>怙扈捺桷蛹匁耳霑<E880B3>**<EFBFBD>夐擇蟇ケ騾画叫鬚倡ュ疲。茨シ悟コ泌ー<EFBFBD><EFBFBD>隗<EFBFBD>クコ蠖灘燕豁・鬪、逧<EFBFBD>。ョ隶、<EFBFBD>悟ケカ遶句叉霑帛<EFBFBD>荳倶ク蜈ウ髞ョ髣ョ鬚假シ亥ヲや懈怙蜈ウ蠢<EFBFBD>噪譚。谺セ窶晢シ会シ御サ・菫晄戟蟇ケ隸晉噪謨育紫蜥檎岼譬<EFBFBD>ッシ蜷代<EFBFBD>
|
||||||
|
* **逕ィ謌キ蛛丞・ス鬮俶譜縲∫サ捺桷蛹也噪莠、莠<EFBDA4>**<EFBFBD>夂畑謌キ菴應クコ莠ァ蜩∫サ冗炊<EFBFBD>悟<EFBFBD>豐滄壽ィ。蠑乗仞遉コ蜃コ蟇ケ貂<EFBFBD>匆騾蛾。ケ蜥悟<EFBFBD>豁・遑ョ隶、逧<EFBFBD>¥螂ス<EFBFBD>瑚ソ咏ャヲ蜷亥<EFBFBD>閨御ク夊レ譎ッ縲ょコ皮サァ扈ュ菴ソ逕ィ騾画叫鬚俶擂蠑募ッシ蟇ケ隸晢シ碁∩蜈榊シ謾セ蠑乗署髣ョ蟇シ閾エ謨育紫髯堺ス弱<EFBFBD>
|
||||||
|
* **譬ク蠢<EFBDB8>擅谺セ遑ョ隶、譏ッ蜈ウ髞ョ豁・鬪、**<EFBFBD>壼惠譏守。ョ蜷亥酔邀サ蝙具シ郁是髞隶セ隶。<EFBFBD>牙柱蜷井ス懈婿<EFBFBD>井クェ莠コ<EFBFBD>牙錘<EFBFBD>御ク倶ク豁・蠢<EFBFBD>。サ閨夂┬莠守畑謌キ逧<EFBFBD><EFBFBD>ク蠢<EFBFBD>膚荳壼<EFBFBD>蛻<EFBFBD>シ亥ヲら衍隸<EFBFBD>コァ譚<EFBFBD>∽サ俶ャセ<EFBFBD>会シ瑚ソ呎弍襍キ闕画怏謨亥粋蜷檎噪蝓コ遑縲<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> A縲。縲,
|
||||||
|
|
||||||
|
* **逕ィ謌キ蛛丞・ス扈捺桷蛹悶<E682B6>ォ俶譜逧<E8AD9C>画叫鬚倅コ、莠<EFBDA4>**<EFBFBD>夂畑謌キ霑樒サュ菴ソ逕ィ蜊穂クェ蟄玲ッ搾シ<EFBFBD>, C, A/B/C<>牙屓遲秘琉鬚假シ瑚。ィ譏主<E8AD8F>菴應クコ莠ァ蜩∫サ冗炊<E58697>悟セ蜷台コ取ク<E58F96>匆縲<E58C86>蛾。ケ譏守。ョ逧<EFBDAE>イ滄壽婿蠑擾シ瑚ソ呵<EFBDBF>蠢ォ騾滄煤螳夐怙豎ょ序驥擾シ悟コ比ス應クコ荳サ隕∵署髣ョ遲也払縲<E68995>
|
||||||
|
* **窶懃衍隸<E8A18D>コァ譚<EFBDA7>∽コ、莉俶<E88E89><E4BFB6>㊥縲∬エケ逕ィ謾ッ莉倪晄弍隶セ隶。邀サ蜷亥酔逧<E98594><E980A7>ク蠢<EFBDB8><E8A0A2>蛻<EFBFBD>**<EFBFBD>夂畑謌キ譏守。ョ騾画叫霑吩ク蛾。ケ<EFBFBD>碁ェ瑚ッ∽コ<EFBFBD>惠窶應クェ莠コ隶セ隶。蟶遺昜ク寂懆是髞隶セ隶。窶晏惻譎ッ荳具シ瑚ソ呎弍譛蜈ウ髞ョ逧<EFBFBD>膚荳壻ク取ウ募セ矩」朱勦轤ケ<EFBFBD>梧悴譚・邀サ莨シ髴豎ょコ比シ伜<EFBFBD>閨夂┬莠取ュ、縲<EFBFBD>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>ョ梧ッ墓弍蜷ッ蜉ィ莉サ蜉。逧<EFBDA1><E980A7>遑ョ菫。蜿キ**<EFBFBD>壼惠騾夊ソ<EFBFBD>画叫鬚倅セ晄ャ。遑ョ隶、窶懷粋菴懈婿<EFBFBD><EFBFBD><EFBFBD>俄<EFBFBD> 鬘ケ逶ョ蜀<EFBDAE>ョケ<EFBDAE><EFBDB9><EFBFBD>俄<EFBFBD> 譬ク蠢<EFBDB8>擅谺セ<E8B0BA><EFBDBE>/B/C<>俄晏錘<E6998F>檎畑謌キ髴豎ょキイ貂<EFBDB2>匆<EFBFBD>悟コ皮ォ句叉諤サ扈灘キ・菴懈婿譯亥ケカ蟇サ豎ら。ョ隶、<E99AB6>瑚碁撼扈ァ扈ュ謠宣琉縲<E79089>
|
||||||
|
* **蟾・菴懈婿譯磯怙騾乗<E9A8BE>荳皮ャヲ蜷育畑謌キ隗定牡鬚<E789A1>悄**<EFBFBD>壼髄逕ィ謌キ螻慕、コ逧<EFBFBD>ョ。蛻貞コ泌<EFBFBD>菴難シ亥ヲや廖蜷亥酔蠕句ク<EFBFBD> 襍キ闕牙<E99795>遞ソ窶晢シ会シ悟ケカ謇ソ隸コ謠蝉セ帛<EFBDBE>菴應クコ莠ァ蜩∫サ冗炊謇驥崎ァ<E5B48E>噪**扈捺桷蛹門<E89BB9>譫<EFBFBD>**<2A>亥ヲ1ros & Cons<6E>会シ瑚ソ呵<EFBDBF>譛画譜闔キ蠕怜<E8A095>遑ョ隶、<E99AB6>梧耳霑帶オ∫ィ九<E4B99D>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> ok , 蜿ッ莉・
|
||||||
|
|
||||||
|
* **逕ィ謌キ譏守。ョ遑ョ隶、譏ッ蜷ッ蜉ィ莉サ蜉。逧<EFBDA1>怙扈井ソ。蜿キ**<EFBFBD>壼ス鍋畑謌キ蟇ケ諤サ扈鍋噪蟾・菴懈婿譯茨シ亥ヲや廖蜷亥酔蠕句ク<EFBFBD> 雍溯エ」襍キ闕<EFBDB7>...窶晢シ牙屓螟坂徙k窶晄<E7AAB6>窶懷庄莉・窶晉ュ芽け螳夊ッ肴慮<E882B4>瑚。ィ譏朱怙豎ら。ョ隶、豬∫ィ句キイ髣ュ邇ッ<E98287>悟コ皮ォ句叉謇ァ陦御ササ蜉。蛻<EFBDA1><E89BBB>縲<EFBFBD>
|
||||||
|
* **蟾・菴懈婿譯磯怙蜈キ菴馴乗<C280>莉・闔キ蠕礼。ョ隶、**<EFBFBD>壼髄逕ィ謌キ<EFBFBD>亥ー、蜈カ譏ッ莠ァ蜩∫サ冗炊<EFBFBD>牙ア慕、コ逧<EFBFBD>ョ。蛻貞ソ<EFBFBD>。サ蛹<EFBFBD>性譏守。ョ逧<EFBFBD>**謇ァ陦御ココ<EFBDBA><EFBDBA>隹<EFBFBD>シ峨∽ササ蜉。<E89C89>亥★莉荵茨シ峨∽コァ蜃コ<E89C83>井コ、莉倅サ荵茨シ<E88CA8>**<2A>瑚ソ咏ャヲ蜷亥<E89CB7>蟇ケ豬∫ィ句庄謗ァ蜥檎サ捺棡鬚<E6A3A1>悄逧<E68284>ヲ∵アゅ<E38285>
|
||||||
|
* **窶懃。ョ隶、蜷取鴬陦娯晄弍譬ク蠢<EFBDB8>キ・菴懃コェ蠕<EFBDAA>**<EFBFBD>壼ソ<EFBFBD>。サ荳・譬シ驕オ螳遺懷<EFBFBD>遑ョ隶、譁ケ譯茨シ悟<EFBFBD>蛻<EFBFBD><EFBFBD>莉サ蜉。窶晉噪豬∫ィ九ら畑謌キ逧<EFBFBD>。ョ隶、譏ッ隗」髯、窶憺怙豎ら。ョ隶、窶晄橿閭ス荳ュ<HARD-GATE>逧<EFBFBD>髪荳譚。莉カ縲<EFBFBD>
|
||||||
|
* **莉サ蜉。蛻<EFBDA1><E89BBB>謖<EFBFBD>サ、髴扈捺桷蛹紋ク泌桁蜷ォ蜈ィ驛ィ閭梧勹**<EFBFBD>壼惠@蝗「髦滓<EFBFBD>蜻俶慮<EFBFBD>悟コ疲ク<EFBFBD>匆螟崎ソー逕ィ謌キ遑ョ隶、霑<EFBFBD>噪謇譛牙<EFBFBD>髞ョ閭梧勹菫。諱ッ<EFBFBD>亥粋菴懈婿縲∝<EFBFBD>螳ケ縲∵<EFBFBD>ク蠢<EFBFBD><EFBFBD>蛻<EFBFBD>シ会シ悟ケカ扈吝<EFBFBD>譏守。ョ逧<EFBFBD>オキ闕<EFBFBD>/螳。譟・隕∵アゑシ檎。ョ菫晏キ・菴懈婿蜷大㊥遑ョ縲<EFBDAE>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 謌題ヲ∵耳霎樔ク荳ェ莠コ
|
||||||
|
|
||||||
|
* **窶懈耳霎樞晄弍讓。邉顔噪諢丞崟陦ィ霑ー**<EFBFBD>夊ッ・隸肴悽霄ォ譌<EFBFBD>豕募愛譁ュ蜈キ菴灘惻譎ッ<EFBFBD>亥ヲよ拠扈晏粋菴懊∬セ樣蜻伜キ・縲∵拠扈昜クェ莠コ隸キ豎ゑシ会シ悟ソ<EFBFBD>。サ鬥門<EFBFBD>騾夊ソ<EFBFBD>画叫鬚假シ<EFBFBD>/B/C<>芽ソ幄。悟惻譎ッ蛻<EFBDAF>アサ<EFBDB1>瑚ソ呎弍蜷ッ蜉ィ譛画譜蛻<E8AD9C>梵逧<E6A2B5>ャャ荳豁・縲<EFBDA5>
|
||||||
|
* **蝨コ譎ッ蛻<EFBDAF>アサ蜀ウ螳壽ウ募セ玖キッ蠕<EFBDAF>**<EFBFBD>壽耳霎樒噪蟇ケ雎。蜥瑚レ譎ッ<EFBFBD>亥キ・菴懷粋菴懊∽ココ莠句<EFBFBD>邉サ縲∽クェ莠コ莠句苅<EFBFBD>牙ー<EFBFBD><EFBFBD>螳壼ョ悟<EFBFBD>荳榊酔逧<EFBFBD>ウ募セ矩」朱勦縲∝粋隗<EFBFBD>ヲ∵アょ柱豐滄夂ュ也払<EFBFBD>悟ソ<EFBFBD>。サ蝨ィ謠蝉セ帑ササ菴募サコ隶ョ蜑肴<EFBFBD>遑ョ縲<EFBFBD>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>怙扈捺桷蛹門シ募ッシ**<EFBFBD>夐擇蟇ケ讓。邉企怙豎ゑシ御スソ逕ィ騾画叫鬚俶弍譛鬮俶譜逧<EFBFBD>セ<EFBFBD>ク<EFBFBD>婿蠑擾シ檎ャヲ蜷育畑謌キ<EFBFBD>井コァ蜩∫サ冗炊<EFBFBD>臥サ捺桷蛹也噪諤晉サエ荵<EFBFBD>諠ッ<EFBFBD>瑚<EFBFBD>蠢ォ騾滄煤螳夐琉鬚伜沺縲<EFBFBD>
|
||||||
|
* **豐滄夐怙謖蛾サ霎鷹。コ蠎乗耳霑<E880B3>**<EFBFBD>壼惠譏守。ョ蝨コ譎ッ蜷趣シ梧燕閭ス霑帑ク豁・隸「髣ョ蜈キ菴鍋サ<EFBFBD>鰍<EFBFBD>亥ヲよ拠扈晉炊逕ア縲∝曙譁ケ蜈ウ邉サ縲∝キイ驥<EFBFBD>叙逧<EFBFBD>。悟勘<EFBFBD>会シ御サ・隸<EFBFBD>シー豕募セ矩」朱勦蜥梧署萓帛庄謫堺ス懷サコ隶ョ縲<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> A
|
||||||
|
|
||||||
|
* **窶廣窶晄弍騾画叫鬚倡噪譏守。ョ遲疲。<E796B2>**<EFBFBD>夂畑謌キ逕ィ蜊穂クェ蟄玲ッ榊屓遲秘画叫鬚假シ瑚。ィ遉コ蟾イ莉朱「<EFBFBD>ョセ騾蛾。ケ荳ュ蛛壼<EFBFBD>騾画叫<EFBFBD>悟コ皮峩謗・驥<EFBFBD>コウ蜈カ蟇ケ蠎泌性荵会シ域ュ、螟<EFBFBD>クコ窶懷キ・菴<EFBFBD>/荳壼苅蜷井ス懌晏惻譎ッ<E8AD8E>会シ梧裏髴蜀肴ャ。遑ョ隶、縲<EFBDA4>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>怙扈捺桷蛹匁耳霑<E880B3>**<EFBFBD>壻スソ逕ィ騾画叫鬚假シ<EFBFBD>/B/C<>画弍鬮俶譜縲∵ク<E288B5>匆逧<E58C86>イ滄壽婿蠑擾シ檎ャヲ蜷井コァ蜩∫サ冗炊蛛丞・ス<EFBDA5>瑚<EFBFBD>蠢ォ騾滄煤螳夐琉鬚伜沺縲<E6B2BA>
|
||||||
|
* **荳倶ク豁・蠎碑★辟ヲ窶懈耳霎槫ッケ雎。窶<EFBDA1>**<EFBFBD>壼惠遑ョ隶、蝨コ譎ッ蜷趣シ御ク倶ク豁・蠎碑<EFBFBD>辟カ閨夂┬莠寂懈耳霎槫ッケ雎。窶晢シ瑚ソ呎弍蜀ウ螳壽ウ募セ矩」朱勦諤ァ雍ィ<EFBFBD>亥ッケ螟門粋蜷瑚エ」莉サ vs. 蟇ケ蜀<EFBDB9>ョ。逅<EFBDA1>イ滄夲シ牙柱蜷守サュ蟒コ隶ョ譁ケ蜷醍噪蜈ウ髞ョ蜿倬㍼縲<E38DBC>
|
||||||
|
* **豐滄夐怙謖蛾サ霎鷹。コ蠎丞<E8A08E>螻<EFBFBD>**<EFBFBD>夐<EFBFBD>蠕ェ窶懷惻譎ッ 竊<> 蟇ケ雎。 竊<> 蜈キ菴謎コ狗罰/鬟朱勦窶晉噪謠宣琉騾サ霎托シ瑚<EFBDBC>邉サ扈滓ァ蝨ー謾カ髮<EFBDB6>ソ。諱ッ<E8ABB1>碁∩蜈埼@貍丞<E8B28D>髞ョ轤ケ縲<EFBDB9>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> AB
|
||||||
|
|
||||||
|
螂ス逧<EFBFBD>シ梧<EFBFBD>譚・諤サ扈謎ク荳玖ソ吩クェ莉サ蜉。逧<EFBFBD><EFBFBD>髞ョ隕∫せ縲<EFBFBD>
|
||||||
|
|
||||||
|
* **逕ィ謌キ蜷梧慮騾画叫螟壻クェ騾蛾。ケ譏ッ蟶ク隗∵ュ蜀オ**<EFBFBD>壼ス鍋畑謌キ蜷梧慮騾画叫A蜥沓譌カ<EFBFBD>瑚。ィ譏取ュ蜀オ蜿ッ閭ス豸牙所螟壽婿謌門ュ伜惠莠、蜿会シ御ク崎<EFBFBD>邂蜊募ス堤アサ<EFBFBD>碁怙隕∬ソ帑ク豁・貔<EFBFBD>ク<EFBFBD><EFBFBD>
|
||||||
|
* **窶懈耳霎樞晏惻譎ッ逧<EFBDAF><E980A7>ク蠢<EFBDB8>弍隸<E5BC8D>悪窶應クサ隕∫泝逶セ譁ケ窶<EFBDB9>**<EFBFBD>壼惠螟壽婿蝨コ譎ッ荳具シ悟ソ<EFBFBD>。サ蠑募ッシ逕ィ謌キ譏守。ョ**荳サ隕∵耳霎槫ッケ雎。**<2A>域弍螟夜Κ蜷井ス懈婿<E68788>瑚ソ俶弍蜀<E5BC8D>Κ蜷御コ具シ会シ瑚ソ咏峩謗・蜀ウ螳壻コ<E5A3BB>ウ募セ矩」朱勦逧<E58BA6>ァ雍ィ<E99B8D>亥ッケ螟冶ソ晉コヲ鬟朱勦 vs. 蜀<>Κ邂。逅<EFBDA1>/蜷郁ァ<E98381>」朱勦<E69CB1>峨<E5B3A8>
|
||||||
|
* **荳倶ク豁・謠宣琉髴閨夂┬莠寂懷侍貂<E4BE8D><E8B282>邉サ荳取<E88DB3>ク蠢<EFBDB8><EFBFBD>**<EFBFBD>壼ス鍋畑謌キ騾画叫讓。邉頑慮<EFBFBD>悟コ疲署萓帶峩邊セ遑ョ逧<EFBFBD>蛾。ケ<EFBFBD>亥ヲや應クサ隕∫泝逶セ譏ッA霑俶弍B<EFBFBD>溪晢シ画<EFBFBD>隸「髣ョ譏ッ蜷ヲ荳コ窶懷<EFBFBD>螟夜Κ閨泌勘逧<EFBFBD>、肴揩諠<EFBFBD><EFBFBD>窶晢シ御サ・髞∝ョ夐琉鬚倡噪逵滓ュ」逞<EFBFBD>サ薙<EFBFBD>
|
||||||
|
* **豐滄夐怙菫晄戟扈捺桷蛹門シ募ッシ**<EFBFBD>壼叉菴ソ諠<EFBFBD><EFBFBD>蜿伜、肴揩<EFBFBD>御サ埼怙菴ソ逕ィ騾画叫鬚倡ュ臥サ捺桷蛹匁婿蠑乗耳霑幢シ瑚ソ咏ャヲ蜷井コァ蜩∫サ冗炊<EFBFBD>育ャャ莠泌ュ」<EFBFBD>蛾ォ俶譜縲∫岼譬<EFBFBD><EFBFBD>遑ョ逧<EFBFBD>イ滄壼¥螂ス縲<EFBFBD>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
譬ケ謐ョ謌台サャ蛻壽燕逧<EFBFBD>ョィ隶コ<EFBFBD>御クコ莠<EFBFBD>サ吩ス<EFBFBD>譛邊セ蜃<EFBFBD>噪蟒コ隶ョ<EFBFBD>瑚ッキ蜻願ッ画<EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
|
||||||
|
**菴<>蟶梧悍謗ィ霎樒噪譬ク蠢<EFBDB8>ッケ雎。<E99B8E>御クサ隕∵弍蜩ェ荳荳ェ<E88DB3><EFBDAA>**
|
||||||
|
* **A. 螟夜Κ蜷井ス懈婿**<EFBFBD>郁ソ呎弍荳サ隕∫泝逶セ<EFBFBD><EFBFBD>
|
||||||
|
* **B. 蜀<>Κ蜷御コ<E5BEA1>/驛ィ髣ィ**<EFBFBD>郁ソ呎弍荳サ隕∫泝逶セ<EFBFBD><EFBFBD>
|
||||||
|
|
||||||
|
謌冶<EFBFBD>シ瑚ソ呎弍荳荳ェ**豸牙所蜀<E68980>、夜Κ閨泌勘逧<E58B98>、肴揩諠<E68FA9><E8ABA0>**<2A>井セ句ヲゑシ碁怙隕∝<E99A95>諡堤サ晏<EFBDBB>驛ィ蜷御コ狗噪譟蝉クェ螟夜Κ蜷井ス懈署隶ョ<E99AB6>会シ<E4BC9A>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> B
|
||||||
|
|
||||||
|
* **逕ィ謌キ騾画叫窶廝窶晄<E7AAB6>遑ョ莠<EFBDAE><E88EA0>ク蠢<EFBDB8>」朱勦邀サ蝙<EFBDBB>**<EFBFBD>壼ス鍋畑謌キ騾画叫窶懷ュ伜惠豕募セ区<EFBFBD>蜷郁ァ<EFBFBD>」朱勦窶昜ス應クコ謗ィ霎槫<EFBFBD>驛ィ蜷御コ狗噪莠狗罰譌カ<EFBFBD>瑚ソ呵。ィ譏朱琉鬚伜キイ莉主黒郤ッ逧<EFBFBD>ョ。逅<EFBFBD>イ滄夲シ悟合郤ァ荳コ髴隕<EFBFBD>**荳謎ク壽ウ募セ<E58B9F>/蜷郁ァ<E98381>ッ<EFBFBD>シー**逧<>ュ」蠑丈セ晄紺縲<E7B4BA>
|
||||||
|
* **莉サ蜉。諤ァ雍ィ霓ャ蜿倅クコ窶憺」朱勦隸<E58BA6>悪荳手ッ∵紺謾ッ謖≫<E289AB>**<EFBFBD>壽ュ、譌カ逧<EFBFBD><EFBFBD>ク蠢<EFBFBD>怙豎ゆク榊<EFBFBD>譏ッ豐滄壽橿蟾ァ<EFBFBD>瑚梧弍逕ア蜷郁ァ<EFBFBD>ク灘遭謌門粋蜷悟セ句ク井サ句<EFBFBD><EFBFBD><EFBFBD>**螳。譟・鬘ケ逶ョ譁<EFBDAE>サカ縲∬ッ<E288AC>悪蜈キ菴馴」朱勦轤ケ縲∝ケカ謠蝉セ帑ケヲ髱「隸<EFBDA2>シー**<2A>御サ・蠖「謌仙<E8AC8C>譛芽ッエ譛榊鴨逧<E9B4A8>拠扈晉炊逕ア縲<EFBDB1>
|
||||||
|
* **荳倶ク豁・蠎碑★辟ヲ莠寂憺」朱勦隸∵紺謾カ髮<EFBDB6><EFBFBD>**<EFBFBD>壼惠遑ョ隶、莠狗罰蜷趣シ悟コ皮ォ句叉隸「髣ョ逕ィ謌キ譏ッ蜷ヲ蟾イ譛臥嶌蜈ウ譁<EFBFBD>サカ<EFBFBD>亥ヲょ粋蜷瑚拷譯医<EFBFBD>。ケ逶ョ隶。蛻偵∵イ滄夊ョー蠖包シ会シ御サ・萓ソ荳コ蝗「髦溷<EFBFBD>驟榊<EFBFBD>菴鍋噪螳。譟・莉サ蜉。縲<EFBFBD>
|
||||||
|
* **豐滄夐怙蠑募ッシ逕ィ謌キ莉寂應クサ隗ょ愛譁ュ窶晁スャ蜷鯛懷ョ「隗ゆセ晄紺窶<E7B4BA>**<EFBFBD>壼コ泌クョ蜉ゥ逕ィ謌キ逅<EFBFBD>ァ」<EFBFBD>悟渕莠主粋隗<EFBFBD>」朱勦逧<EFBFBD>耳霎樊弍譛譛牙鴨逧<EFBFBD>シ御ス<EFBFBD>ソ<EFBFBD>。サ蟒コ遶句惠荳謎ク夂噪螳。譟・扈楢ョコ荵倶ク奇シ瑚ソ咏ャヲ蜷井コァ蜩∫サ冗炊豕ィ驥肴オ∫ィ句柱萓晄紺逧<EFBFBD>晉サエ縲<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> D
|
||||||
|
|
||||||
|
* **窶廛窶晄弍讓。邉企蛾。ケ逧<EFBDB9>クク隗<EFBDB8>画叫**<EFBFBD>壼ス鍋畑謌キ騾画叫窶懷<EFBFBD>莉門次蝗<EFBFBD>窶晄<EFBFBD>窶廛窶晄慮<EFBFBD>瑚。ィ譏守悄螳槫次蝗<EFBFBD>蜿ッ閭ス謨乗─縲∝、肴揩謌紋ク堺セソ譏手ッエ<EFBFBD>碁怙隕∬ソ帑ク豁・謠蝉セ帶峩邊セ遑ョ菴<EFBFBD>セ晉┯扈捺桷蛹也噪騾蛾。ケ譚・蠑募ッシ縲<EFBFBD>
|
||||||
|
* **荳倶ク豁・謠宣琉髴閨夂┬莠寂懷次蝗<E6ACA1>諤ァ雍ィ窶晁碁撼蜈キ菴鍋サ<E98D8B>鰍**<EFBFBD>夐擇蟇ケ讓。邉雁次蝗<EFBFBD><EFBFBD>悟コ疲署萓幄<EFBFBD>逡悟ョ夐琉鬚俶ァ雍ィ<EFBFBD>亥ヲょ膚荳壼<EFBFBD>遯√∽クェ莠コ鬟朱勦縲∽ココ髯<EFBFBD><EFBFBD>邉サ<EFBFBD>臥噪騾蛾。ケ<EFBFBD>瑚ソ呎怏蜉ゥ莠主愛譁ュ譏ッ謠蝉セ<EFBFBD>**蝠<>ク<EFBFBD>/邂。逅<EFBDA1>サコ隶ョ**霑俶弍**豕募セ<E58B9F>/蜷郁ァ<E98381>髪謖<E9ABAA>**縲<>
|
||||||
|
* **豐滄夐怙蝨ィ菫晄侃逕ィ謌キ髫千ァ∽ク手執蜿匁怏謨井ソ。諱ッ髣エ蟷ウ陦。**<EFBFBD>壽署萓帚應ク肴婿萓ソ譏手ッエ逧<EFBFBD>、肴揩蜴溷屏窶昜ス應クコ騾蛾。ケ荵倶ク<EFBFBD>梧里蟆企㍾莠<EFBFBD>畑謌キ逧<EFBFBD>ス懷惠鬘セ陌托シ御ケ滉クコ蜷守サュ謠蝉セ帛次蛻呎ァ蟒コ隶ョ<EFBFBD>亥ヲらィ句コ丞粋隗<EFBFBD>∽ケヲ髱「隶ー蠖包シ臥蕗荳倶コ<EFBFBD>ゥコ髣エ縲<EFBFBD>
|
||||||
|
* **逕ィ謌キ蛛丞・ス騾夊ソ<E5A48A>画叫鬚倬先ュ・貔<EFBDA5>ク<EFBFBD>、肴揩髣ョ鬚<EFBDAE>**<EFBFBD>夂畑謌キ<EFBFBD>井コァ蜩∫サ冗炊<EFBFBD>牙惠髱「蟇ケ蜀<EFBFBD>Κ謨乗─莠句苅譌カ<EFBFBD>御セ晉┯蛟セ蜷台コ朱夊ソ<EFBFBD>画叫鬚俶擂扈捺桷蛹門慍陦ィ霎セ譬ク蠢<EFBFBD><EFBFBD>蛻<EFBFBD>シ瑚ソ咏ャヲ蜷亥<EFBFBD>鬮俶譜縲∬ァ<EFBFBD>∩逶エ謗・蜀イ遯∫噪豐滄夐」取<EFBFBD>シ縲<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> B
|
||||||
|
|
||||||
|
* **逕ィ謌キ騾画叫窶廝窶晄<E7AAB6>遑ョ莠<EFBDAE><E88EA0>ク蠢<EFBDB8>」朱勦邀サ蝙<EFBDBB>**<EFBFBD>壼ス鍋畑謌キ騾画叫窶應クェ莠コ蛻、譁ュ/閨御ク夐」朱勦窶昜ス應クコ謗ィ霎槫<E99C8E>驛ィ蜷御コ狗噪莠狗罰譌カ<E8AD8C>瑚ソ呵。ィ譏朱琉鬚伜キイ莉主黒郤ッ逧<EFBDAF>ョ。逅<EFBDA1>イ滄夲シ悟合郤ァ荳コ髴隕<C280>**荳謎ク壽ウ募セ<E58B9F>/蜷郁ァ<E98381>ッ<EFBFBD>シー**逧<>ュ」蠑丈セ晄紺縲<E7B4BA>
|
||||||
|
* **莉サ蜉。諤ァ雍ィ霓ャ蜿倅クコ窶憺」朱勦隸<E58BA6>悪荳手ッ∵紺謾ッ謖≫<E289AB>**<EFBFBD>壽ュ、譌カ逧<EFBFBD><EFBFBD>ク蠢<EFBFBD>怙豎ゆク榊<EFBFBD>譏ッ豐滄壽橿蟾ァ<EFBFBD>瑚梧弍逕ア蜷郁ァ<EFBFBD>ク灘遭謌門粋蜷悟セ句ク井サ句<EFBFBD><EFBFBD><EFBFBD>**螳。譟・鬘ケ逶ョ譁<EFBDAE>サカ縲∬ッ<E288AC>悪蜈キ菴馴」朱勦轤ケ縲∝ケカ謠蝉セ帑ケヲ髱「隸<EFBDA2>シー**<2A>御サ・蠖「謌仙<E8AC8C>譛芽ッエ譛榊鴨逧<E9B4A8>拠扈晉炊逕ア縲<EFBDB1>
|
||||||
|
* **荳倶ク豁・蠎碑★辟ヲ莠寂憺」朱勦隸∵紺謾カ髮<EFBDB6><EFBFBD>**<EFBFBD>壼惠遑ョ隶、莠狗罰蜷趣シ悟コ皮ォ句叉隸「髣ョ逕ィ謌キ譏ッ蜷ヲ蟾イ譛臥嶌蜈ウ譁<EFBFBD>サカ<EFBFBD>亥ヲょ粋蜷瑚拷譯医<EFBFBD>。ケ逶ョ隶。蛻偵∵イ滄夊ョー蠖包シ会シ御サ・萓ソ荳コ蝗「髦溷<EFBFBD>驟榊<EFBFBD>菴鍋噪螳。譟・莉サ蜉。縲<EFBFBD>
|
||||||
|
* **豐滄夐怙蠑募ッシ逕ィ謌キ莉寂應クサ隗ょ愛譁ュ窶晁スャ蜷鯛懷ョ「隗ゆセ晄紺窶<E7B4BA>**<EFBFBD>壼コ泌クョ蜉ゥ逕ィ謌キ逅<EFBFBD>ァ」<EFBFBD>悟渕莠主粋隗<EFBFBD>」朱勦逧<EFBFBD>耳霎樊弍譛譛牙鴨逧<EFBFBD>シ御ス<EFBFBD>ソ<EFBFBD>。サ蟒コ遶句惠荳謎ク夂噪螳。譟・扈楢ョコ荵倶ク奇シ瑚ソ咏ャヲ蜷井コァ蜩∫サ冗炊豕ィ驥肴オ∫ィ句柱萓晄紺逧<EFBFBD>晉サエ縲<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 蜿ッ莉・<E88E89>悟ョ画賜蟾・菴懷制
|
||||||
|
|
||||||
|
* **逕ィ謌キ遑ョ隶、窶懷庄莉・窶晄弍蜷ッ蜉ィ莉サ蜉。逧<EFBDA1>怙扈域欠莉、**<EFBFBD>壼ス鍋畑謌キ蟇ケ譏守。ョ逧<EFBFBD>キ・菴懈婿譯茨シ亥桁蜷ォ謇ァ陦御ココ縲∽ササ蜉。縲∽コァ蜃コ<EFBFBD>牙屓螟崎け螳夂。ョ隶、譌カ<EFBFBD>瑚。ィ譏朱怙豎ら。ョ隶、豬∫ィ句キイ髣ュ邇ッ<EFBFBD>悟コ皮ォ句叉謇ァ陦御ササ蜉。蛻<EFBFBD><EFBFBD>縲<EFBFBD>
|
||||||
|
* **莉サ蜉。蛻<EFBDA1><E89BBB>謖<EFBFBD>サ、髴扈捺桷蛹紋ク泌桁蜷ォ螳梧紛閭梧勹**<EFBFBD>壼惠@蝗「髦滓<EFBFBD>蜻俶慮<EFBFBD>悟ソ<EFBFBD>。サ貂<EFBFBD>匆螟崎ソー逕ィ謌キ遑ョ隶、霑<EFBFBD>噪謇譛牙<EFBFBD>髞ョ閭梧勹菫。諱ッ<EFBFBD>亥惻譎ッ縲∝ッケ雎。縲∽コ狗罰<EFBFBD>会シ悟ケカ扈吝<EFBFBD>蜈キ菴薙∝庄謇ァ陦檎噪螳。譟・/蛻<>梵隕∵アゑシ檎。ョ菫晏キ・菴懈婿蜷大㊥遑ョ縲<EFBDAE>
|
||||||
|
* **窶應クェ莠コ閨御ク夐」朱勦窶晏惻譎ッ髴螟夂サエ蠎ヲ謾ッ謖<EFBDAF>**<EFBFBD>壼ス捺耳霎樔コ狗罰豸牙所窶應クェ莠コ蛻、譁ュ/閨御ク夐」朱勦窶晄慮<E69984>碁怙蜊剰ー<E589B0>**蜷郁ァ<E98381>ク灘遭**<2A>域署萓幃」朱勦隸<E58BA6>シー譯<EFBDB0>楔荳取イ滄夊ッ晄惘<E69984>我ク<E68891>**蜷亥酔蠕句ク<E58FA5>**<2A>亥ョ。譟・譁<EFBDA5>サカ荳ュ逧<EFBDAD>クェ莠コ雍」莉サ鬟朱勦<E69CB1>牙刻蜷悟キ・菴懶シ御サ取オ∫ィ句柱譁<E69FB1>悽荳、荳ェ扈エ蠎ヲ譫<EFBDA6>サコ髦イ轣ォ蠅吶<E590B6>
|
||||||
|
* **豐滄夐怙隨ヲ蜷育畑謌キ隗定牡鬚<E789A1>悄**<EFBFBD>壼ッケ莠ァ蜩∫サ冗炊<EFBFBD>育ャャ莠泌ュ」<EFBFBD>牙<EFBFBD>驟堺ササ蜉。譌カ<EFBFBD>梧欠莉、譛ャ霄ォ髴菴鍋鴫扈捺桷蛹悶∫岼譬<EFBFBD><EFBFBD>遑ョ逧<EFBFBD>音諤ァ<EFBFBD>悟ケカ謇ソ隸コ莠、莉伜<EFBFBD>驥崎ァ<EFBFBD>噪**蜿ッ謫堺ス懆ヲ∫せ**<2A>亥ヲりッ晄惘縲<E68398>」朱勦貂<E58BA6>黒<EFBFBD>会シ瑚ソ咏ャヲ蜷亥<E89CB7>蟾・菴憺」取<EFBDA3>シ縲<EFBDBC>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 謌題ヲ∽ク莉ス荳ェ莠コ蜥梧<E89CA5>逧<EFBFBD>粋菴懷刻隶ョ
|
||||||
|
|
||||||
|
螂ス逧<EFBFBD>シ梧<EFBFBD>逋ス縲りソ吩ス榊粋菴懈婿譏ッ荳ェ莠コ隶セ隶。蟶医<EFBFBD>
|
||||||
|
|
||||||
|
**謗・荳区擂<E58CBA>瑚ッキ蜻願ッ画<EFBDAF><E794BB><EFBFBD>**
|
||||||
|
|
||||||
|
2. **菴<>莉ャ蜷井ス懃噪蜈キ菴灘<E88FB4>螳ケ譏ッ莉荵茨シ<E88CA8>**<EFBFBD>亥庄莉・螟夐会シ<EFBFBD>
|
||||||
|
* A. **莠ァ蜩ゞI/UX隶セ隶。**<2A>育阜髱「縲∽コ、莠偵∝次蝙狗ュ会シ<E4BC9A>
|
||||||
|
* B. **蜩∫煙/VI隶セ隶。**<2A><>ogo縲∬ァ<E288AC>ァ芽ッ<E88ABD>悪邉サ扈溽ュ会シ<E4BC9A>
|
||||||
|
* C. **關・髞豢サ蜉ィ鬘オ髱「/豬キ謚・隶セ隶。**
|
||||||
|
* D. **蜈カ莉冶ョセ隶。邀サ蝙<EFBDBB>**<2A>郁ッキ邂蜊戊ッエ譏趣シ<E8B6A3>
|
||||||
|
|
||||||
|
譏守。ョ蜷井ス懷<EFBFBD>螳ケ<EFBFBD>梧弍逡悟ョ壼キ・菴懆激蝗エ縲∽コ、莉俶<EFBFBD><EFBFBD>㊥蜥碁ェ梧噺譚。莉カ逧<EFBFBD>渕遑<EFBFBD>瑚ソ吝ッケ莠ァ蜩∫サ冗炊譚・隸エ閾ウ蜈ウ驥崎ヲ√<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> A
|
||||||
|
|
||||||
|
* **窶廣窶晄弍騾画叫鬚倡噪譏守。ョ遲疲。<E796B2>**<EFBFBD>夂畑謌キ逕ィ蜊穂クェ蟄玲ッ榊屓遲秘画叫鬚假シ瑚。ィ遉コ蟾イ莉朱「<EFBFBD>ョセ騾蛾。ケ荳ュ蛛壼<EFBFBD>騾画叫<EFBFBD>悟コ皮峩謗・驥<EFBFBD>コウ蜈カ蟇ケ蠎泌性荵会シ域ュ、螟<EFBFBD>クコ窶應クェ莠コ隶セ隶。蟶<EFBFBD>/蠑蜿題<E9A18C>晢シ会シ梧裏髴蜀肴ャ。遑ョ隶、縲<EFBDA4>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>怙扈捺桷蛹匁耳霑<E880B3>**<EFBFBD>壻スソ逕ィ騾画叫鬚假シ<EFBFBD>/B/C<>画弍鬮俶譜縲∵ク<E288B5>匆逧<E58C86>イ滄壽婿蠑擾シ檎ャヲ蜷井コァ蜩∫サ冗炊蛛丞・ス<EFBDA5>瑚<EFBFBD>蠢ォ騾滄煤螳壼<E89EB3>髞ョ蜿倬㍼<E580AC>亥ヲょ粋菴懈婿霄ォ莉ス<E88E89>峨<E5B3A8>
|
||||||
|
* **荳倶ク豁・蠎碑★辟ヲ窶懷粋蜷梧<E89CB7><E6A2A7>噪窶<E599AA>**<EFBFBD>壼惠遑ョ隶、蜷井ス懈婿霄ォ莉ス蜷趣シ御ク倶ク豁・蠎碑<EFBFBD>辟カ閨夂┬莠寂懷粋菴懷<EFBFBD>螳ケ窶晢シ瑚ソ呎弍逡悟ョ壼粋蜷瑚激蝗エ縲∽コ、莉俶<EFBFBD><EFBFBD>㊥蜥梧揀蛻ゥ荵牙苅逧<EFBFBD><EFBFBD>ク蠢<EFBFBD>シ檎ャヲ蜷井サ寂應クサ菴凪晏芦窶懈<EFBFBD><EFBFBD>噪窶晉噪蜷亥酔襍キ闕蛾サ霎代<EFBFBD>
|
||||||
|
* **閭梧勹菫。諱ッ譏ッ譛画譜莉サ蜉。蛻<EFBDA1><E89BBB>逧<EFBFBD>燕謠<E78795>**<EFBFBD>壼ソ<EFBFBD>。サ謾カ髮<EFBFBD>ョ娯懷粋菴懈婿窶晏柱窶懷粋菴懷<EFBFBD>螳ケ窶晁ソ吩ク、鬘ケ蝓コ遑菫。諱ッ蜷趣シ梧燕閭ス蠖「謌先怏謨育噪莉サ蜉。蛻<EFBFBD><EFBFBD>譁ケ譯茨シ檎。ョ菫晏粋蜷悟セ句ク郁オキ闕臥噪蛻晉ィソ蜈キ譛蛾宙蟇ケ諤ァ縲<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> A B C
|
||||||
|
|
||||||
|
螂ス逧<EFBFBD>シ梧<EFBFBD>譚・諤サ扈楢ソ吩クェ莉サ蜉。逧<EFBFBD><EFBFBD>髞ョ隕∫せ縲<EFBFBD>
|
||||||
|
|
||||||
|
* **逕ィ謌キ蜷梧慮騾画叫螟壻クェ騾蛾。ケ譏ッ蟶ク隗∵ュ蜀オ**<EFBFBD>壼ス鍋畑謌キ蜷梧慮騾画叫A縲。縲,譌カ<EFBFBD>瑚。ィ譏主粋菴懷<EFBFBD>螳ケ蜿ッ閭ス譏ッ荳荳ェ扈シ蜷域ァ逧<EFBFBD>ョセ隶。鬘ケ逶ョ<EFBFBD>瑚碁撼蜊穂ク邀サ蝙具シ瑚ソ吩シ壼スア蜩榊粋蜷御クュ蟇ケ窶懷キ・菴懆激蝗エ窶晉噪螳壻ケ画婿蠑上<EFBFBD>
|
||||||
|
* **窶懃サシ蜷郁ョセ隶。鬘ケ逶ョ窶晞怙譖エ貂<EFBDB4>匆逧<E58C86>キ・菴懆激蝗エ逡悟ョ<E6829F>**<EFBFBD>壼ッケ莠取カオ逶門、夂アサ蝙玖ョセ隶。逧<EFBFBD>粋菴懶シ悟粋蜷悟ソ<EFBFBD>。サ譏守。ョ蜷<EFBFBD>Κ蛻<EFBFBD>噪莠、莉倡黄縲∵<EFBFBD><EFBFBD>㊥縲∝庄閭ス逧<EFBFBD>慮髣エ郤ソ莉・蜿雁ッケ蠎皮噪雍ケ逕ィ<EFBFBD>域弍謇灘桁莉キ霑俶弍蛻<EFBFBD>。ケ隶。莉キ<EFBFBD>会シ御サ・驕ソ蜈崎激蝗エ阡灘サカ縲<EFBFBD>
|
||||||
|
* **荳倶ク豁・蠎碑★辟ヲ莠寂懈<C280>ク蠢<EFBDB8>膚荳壼<E88DB3>蛻<EFBFBD><EFBFBD>**<EFBFBD>壼惠譏守。ョ蜷井ス懈婿<EFBFBD>井クェ莠コ隶セ隶。蟶茨シ牙柱扈シ蜷亥<EFBFBD>螳ケ蜷趣シ御ク倶ク豁・蠢<EFBFBD>。サ閨夂┬莠守畑謌キ逧<EFBFBD><EFBFBD>ク蠢<EFBFBD>膚荳壻ク取ウ募セ句<EFBFBD>蛻<EFBFBD>シ亥ヲら衍隸<EFBFBD>コァ譚<EFBFBD>∽サ俶ャセ縲∽コ、莉俶<EFBFBD><EFBFBD>㊥<EFBFBD>会シ瑚ソ呎弍襍キ闕画怏謨亥粋蜷檎噪蝓コ遑縲<EFBFBD>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>怙謖蛾サ霎鷹。コ蠎城ォ俶譜謗ィ霑<EFBDA8>**<EFBFBD>夐<EFBFBD>蠕ェ窶懷粋菴懈婿 竊<> 蜷井ス懷<EFBDBD>螳ケ 竊<> 譬ク蠢<EFBDB8>擅谺セ窶晉噪謠宣琉騾サ霎托シ御スソ逕ィ騾画叫鬚假シ檎ャヲ蜷井コァ蜩∫サ冗炊<E58697>育ャャ莠泌ュ」<EFBDAD>臥サ捺桷蛹悶<E682B6>ォ俶譜逧<E8AD9C>イ滄壼¥螂ス縲<EFBDBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 驛ス蜈ウ蠢<EFBDB3>
|
||||||
|
|
||||||
|
* **逕ィ謌キ蛛丞・ス扈捺桷蛹悶<E682B6>ォ俶譜逧<E8AD9C>画叫鬚倅コ、莠<EFBDA4>**<EFBFBD>夂畑謌キ霑樒サュ菴ソ逕ィ蜊穂クェ蟄玲ッ搾シ<EFBFBD>, A/B/C, 窶憺<E7AAB6>蜈ウ蠢<EFBDB3>晢シ牙屓遲秘琉鬚假シ瑚。ィ譏主<E8AD8F>菴應クコ莠ァ蜩∫サ冗炊<E58697>悟セ蜷台コ取ク<E58F96>匆縲<E58C86>蛾。ケ譏守。ョ逧<EFBDAE>イ滄壽婿蠑擾シ瑚ソ呵<EFBDBF>蠢ォ騾滄煤螳夐怙豎ょ序驥擾シ悟コ比ス應クコ荳サ隕∵署髣ョ遲也払縲<E68995>
|
||||||
|
* **窶懃サシ蜷郁ョセ隶。鬘ケ逶ョ窶晞怙譖エ貂<EFBDB4>匆逧<E58C86>キ・菴懆激蝗エ逡悟ョ<E6829F>**<EFBFBD>壼ス灘粋菴懷<EFBFBD>螳ケ豸オ逶門、夂アサ蝙玖ョセ隶。譌カ<EFBFBD>悟粋蜷悟ソ<EFBFBD>。サ譏守。ョ蜷<EFBFBD>Κ蛻<EFBFBD>噪莠、莉倡黄縲∵<EFBFBD><EFBFBD>㊥縲∝庄閭ス逧<EFBFBD>慮髣エ郤ソ莉・蜿雁ッケ蠎皮噪雍ケ逕ィ<EFBFBD>域弍謇灘桁莉キ霑俶弍蛻<EFBFBD>。ケ隶。莉キ<EFBFBD>会シ御サ・驕ソ蜈崎激蝗エ阡灘サカ縲<EFBFBD>
|
||||||
|
* **窶懷<E7AAB6>鬟朱勦轤ケ蜈ウ豕ィ窶晄弍莠ァ蜩∫サ冗炊逧<E7828A><E980A7>蝙狗音蠕<E99FB3>**<EFBFBD>夂畑謌キ蟇ケ遏・隸<EFBFBD>コァ譚<EFBFBD>∽コ、莉倥∽サ俶ャセ縲∝捉譛溘∽ソ晏ッ<EFBFBD>ュ画園譛画<EFBFBD>ク蠢<EFBFBD>擅谺セ驛ス陦ィ遉コ蜈ウ蠢<EFBFBD>シ碁ェ瑚ッ∽コ<EFBFBD><EFBFBD>隗定牡蟇ケ鬘ケ逶ョ蜿ッ謗ァ諤ァ蜥碁」朱勦隗<EFBFBD>∩逧<EFBFBD><EFBFBD>髱「隕∵アゅよ悴譚・邀サ莨シ髴豎ょコ秘サ倩ョ、蜈ィ髱「隕<EFBFBD>尠霑吩コ幄ヲ∫せ縲<EFBFBD>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>ョ梧ッ墓弍蜷ッ蜉ィ莉サ蜉。逧<EFBDA1><E980A7>遑ョ菫。蜿キ**<EFBFBD>壼惠騾夊ソ<EFBFBD>画叫鬚倅セ晄ャ。遑ョ隶、窶懷粋菴懈婿<EFBFBD><EFBFBD><EFBFBD>俄<EFBFBD> 鬘ケ逶ョ蜀<EFBDAE>ョケ<EFBDAE><EFBDB9>/B/C<>俄<EFBFBD> 譬ク蠢<EFBDB8>擅谺セ<E8B0BA>磯<EFBFBD>蜈ウ蠢<EFBDB3>シ俄晏錘<E6998F>檎畑謌キ髴豎ょキイ貂<EFBDB2>匆<EFBFBD>悟コ皮ォ句叉諤サ扈灘キ・菴懈婿譯亥ケカ蟇サ豎ら。ョ隶、<E99AB6>瑚碁撼扈ァ扈ュ謠宣琉縲<E79089>
|
||||||
|
* **蟾・菴懈婿譯磯怙騾乗<E9A8BE>荳皮ャヲ蜷育畑謌キ隗定牡鬚<E789A1>悄**<EFBFBD>壼髄逕ィ謌キ螻慕、コ逧<EFBFBD>ョ。蛻貞コ泌<EFBFBD>菴難シ亥ヲや廖蜷亥酔蠕句ク<EFBFBD> 襍キ闕牙<E99795>遞ソ窶晢シ会シ悟ケカ謇ソ隸コ謠蝉セ帛<EFBDBE>菴應クコ莠ァ蜩∫サ冗炊謇驥崎ァ<E5B48E>噪**扈捺桷蛹門<E89BB9>譫<EFBFBD>**<2A>亥ヲ1ros & Cons縲∬ー亥愛蠑ケ諤ァ遨コ髣エ<E9ABA3>会シ瑚ソ呵<EFBDBF>譛画譜闔キ蠕怜<E8A095>遑ョ隶、<E99AB6>梧耳霑帶オ∫ィ九<E4B99D>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> ok<6F>悟庄莉・逧<EFBDA5>
|
||||||
|
|
||||||
|
* **逕ィ謌キ譏守。ョ遑ョ隶、譏ッ蜷ッ蜉ィ莉サ蜉。逧<EFBDA1>怙扈井ソ。蜿キ**<EFBFBD>壼ス鍋畑謌キ蟇ケ諤サ扈鍋噪蟾・菴懈婿譯茨シ亥桁蜷ォ謇ァ陦御ココ縲∽ササ蜉。縲∽コァ蜃コ<EFBFBD>牙屓螟坂徙k窶晄<EFBFBD>窶懷庄莉・逧<EFBFBD>晉ュ芽け螳夊ッ肴慮<EFBFBD>瑚。ィ譏朱怙豎ら。ョ隶、豬∫ィ句キイ髣ュ邇ッ<EFBFBD>悟コ皮ォ句叉謇ァ陦御ササ蜉。蛻<EFBFBD><EFBFBD>縲<EFBFBD>
|
||||||
|
* **蟾・菴懈婿譯磯怙蜈キ菴馴乗<C280>莉・闔キ蠕礼。ョ隶、**<EFBFBD>壼髄逕ィ謌キ<EFBFBD>亥ー、蜈カ譏ッ莠ァ蜩∫サ冗炊<EFBFBD>牙ア慕、コ逧<EFBFBD>ョ。蛻貞ソ<EFBFBD>。サ蛹<EFBFBD>性譏守。ョ逧<EFBFBD>**謇ァ陦御ココ<EFBDBA><EFBDBA>隹<EFBFBD>シ峨∽ササ蜉。<E89C89>亥★莉荵茨シ峨∽コァ蜃コ<E89C83>井コ、莉倅サ荵茨シ<E88CA8>**<2A>瑚ソ咏ャヲ蜷亥<E89CB7>蟇ケ豬∫ィ句庄謗ァ蜥檎サ捺棡鬚<E6A3A1>悄逧<E68284>ヲ∵アゅ<E38285>
|
||||||
|
* **窶懃。ョ隶、蜷取鴬陦娯晄弍譬ク蠢<EFBDB8>キ・菴懃コェ蠕<EFBDAA>**<EFBFBD>壼ソ<EFBFBD>。サ荳・譬シ驕オ螳遺懷<EFBFBD>遑ョ隶、譁ケ譯茨シ悟<EFBFBD>蛻<EFBFBD><EFBFBD>莉サ蜉。窶晉噪豬∫ィ九ら畑謌キ逧<EFBFBD>。ョ隶、譏ッ隗」髯、窶憺怙豎ら。ョ隶、窶晄橿閭ス荳ュ<HARD-GATE>逧<EFBFBD>髪荳譚。莉カ縲<EFBFBD>
|
||||||
|
* **莉サ蜉。蛻<EFBDA1><E89BBB>謖<EFBFBD>サ、髴扈捺桷蛹紋ク泌桁蜷ォ蜈ィ驛ィ閭梧勹**<EFBFBD>壼惠@蝗「髦滓<EFBFBD>蜻俶慮<EFBFBD>悟コ疲ク<EFBFBD>匆螟崎ソー逕ィ謌キ遑ョ隶、霑<EFBFBD>噪謇譛牙<EFBFBD>髞ョ閭梧勹菫。諱ッ<EFBFBD>亥粋菴懈婿縲∝<EFBFBD>螳ケ縲∵<EFBFBD>ク蠢<EFBFBD><EFBFBD>蛻<EFBFBD>シ会シ悟ケカ扈吝<EFBFBD>譏守。ョ逧<EFBFBD>オキ闕<EFBFBD>/螳。譟・隕∵アゑシ檎。ョ菫晏キ・菴懈婿蜷大㊥遑ョ縲<EFBDAE>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 謇ァ陦<EFBDA7>
|
||||||
|
|
||||||
|
* **窶懈鴬陦娯晄弍譏守。ョ逧<EFBDAE>。悟勘謖<E58B98>サ、**<EFBFBD>壼ス鍋畑謌キ蟇ケ蟾イ遑ョ隶、逧<EFBFBD>キ・菴懈婿譯亥屓螟坂懈鴬陦娯晄慮<EFBFBD>檎ュ牙酔莠寂徙k/蜿ッ莉・窶晢シ梧弍蜷ッ蜉ィ莉サ蜉。蛻<EFBDA1><E89BBB>逧<EFBFBD>怙扈育。ョ隶、菫。蜿キ縲<EFBDB7>
|
||||||
|
* **莉サ蜉。蛻<EFBDA1><E89BBB>髴蝨ィ逕ィ謌キ遑ョ隶、蜷守ォ句叉霑幄。<E5B984>**<EFBFBD>壽噺蛻ー窶懈鴬陦娯晄欠莉、蜷趣シ悟コ皮峩謗・縲∵ク<EFBFBD>匆蝨ー @ 逶ク蜈ウ蝗「髦滓<E9ABA6>蜻伜ケカ荳玖セセ蛹<EFBDBE>性蜈ィ驛ィ閭梧勹逧<E58BB9>ッヲ扈<EFBDA6>ササ蜉。<E89C89>梧裏髴蜀肴ャ。隸「髣ョ謌匁サ扈薙<E89699>
|
||||||
|
* **豐滄壽オ∫ィ矩<EFBDA8>蠕ェ窶懃。ョ隶、-謇ァ陦娯晉噪荳・譬シ鬘コ蠎<EFBDBA>**<EFBFBD>壼ソ<EFBFBD>。サ荳・譬シ驕オ螳遺憺怙豎ら。ョ隶、 竊<> 譁ケ譯域サ扈<EFBDBB> 竊<> 逕ィ謌キ遑ョ隶、 竊<> 莉サ蜉。蛻<EFBDA1><E89BBB>窶晉噪豬∫ィ具シ檎畑謌キ逧<EFBDB7>。ョ隶、譏ッ豬∫ィ区耳霑帷噪蜚ッ荳闃らせ縲<E3819B>
|
||||||
|
* **逕ィ謌キ蛛丞・ス鬮俶譜縲∫岼譬<E5B2BC><E8ADAC>遑ョ逧<EFBDAE>イ滄<E6BB84>**<EFBFBD>壻ス應クコ莠ァ蜩∫サ冗炊<EFBFBD>檎畑謌キ菴ソ逕ィ窶懈鴬陦娯晁ソ咏アサ邂豢∵欠莉、<EFBFBD>瑚。ィ譏主<EFBFBD>譛滓悍蝨ィ譁ケ譯育。ョ隶、蜷主ソォ騾溯ソ帛<EFBFBD>謇ァ陦碁亳谿オ<EFBFBD>梧イ滄壼コ秘∩蜈榊<EFBFBD>菴吶<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> ok
|
||||||
|
|
||||||
|
Based on this interaction, here are the key learnings for future reference:
|
||||||
|
|
||||||
|
* **"ok" is a common but ambiguous response**: A standalone "ok" from the user can be an acknowledgment, a confirmation, or a simple greeting. It does not inherently contain a specific task request.
|
||||||
|
* **First action is to clarify intent**: When faced with an ambiguous response like "ok", the correct protocol is to respond politely and ask how you can help, rather than making assumptions or initiating a workflow.
|
||||||
|
* **Reserve task delegation for explicit needs**: The task allocation workflow should only be triggered after the user has explicitly stated a specific, substantive legal or professional need.
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 襍キ闕牙粋蜷<E7B28B>
|
||||||
|
|
||||||
|
Based on this task, here are the key learnings for future reference:
|
||||||
|
|
||||||
|
* **窶懆オキ闕牙粋蜷娯晄弍譏守。ョ逧<EFBDAE>ク謎ク壻ササ蜉。隗ヲ蜿題ッ<E9A18C>**<EFBFBD>壼ス鍋畑謌キ譏守。ョ謠仙<EFBFBD>髴隕∬オキ闕画ウ募セ区枚莉カ譌カ<EFBFBD>悟コ皮ォ句叉蜷ッ蜉ィ扈捺桷蛹也噪髴豎ら。ョ隶、豬∫ィ九<EFBFBD>
|
||||||
|
* **蛻<><E89BBB>莉サ蜉。蜑榊ソ<E6A68A>。サ霑幄。娯應クサ菴楢コォ莉ス遑ョ隶、窶<EFBDA4>**<EFBFBD>壼粋蜷悟粋菴懈婿<EFBFBD>井クェ莠コ vs. 蜈ャ蜿ク<E89CBF>画弍蜀ウ螳壼粋蜷梧擅谺セ<E8B0BA>亥ヲりエ」莉サ縲∝書逾ィ縲∬オ<E288AC>エィ<EFBDB4>芽オー蜷醍噪鬥冶ヲ∝序驥擾シ悟ソ<E6829F>。サ蝨ィ莉サ蜉。蛻<EFBDA1><E89BBB>蜑堺シ伜<EFBDBC>譏守。ョ縲<EFBDAE>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>怙扈捺桷蛹紋ク秘ォ俶譜**<EFBFBD>壻スソ逕ィ騾画叫鬚假シ<EFBFBD>/B/C<>画弍蠢ォ騾滄煤螳壼<E89EB3>髞ョ蜿倬㍼縲∫ャヲ蜷井コァ蜩∫サ冗炊鬮俶譜豐滄壼¥螂ス逧<EFBDBD>怏謨域婿蠑上<E4B88A>
|
||||||
|
* **豐滄夐怙謖蛾サ霎鷹。コ蠎乗耳霑<E880B3>**<EFBFBD>壼惠譏守。ョ窶懷粋菴懈婿窶晏錘<EFBFBD>御ク倶ク豁・蠎碑<EFBFBD>辟カ閨夂┬莠寂懷粋菴懷<EFBFBD>螳ケ/闌<>峩窶晢シ瑚ソ呎弍逡悟ョ壼粋蜷梧<E89CB7><E6A2A7>噪蜥梧揀蛻ゥ荵牙苅逧<E88B85><E980A7>ク蠢<EFBDB8><EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> A
|
||||||
|
|
||||||
|
螂ス逧<EFBFBD>シ梧<EFBFBD>譚・諤サ扈謎ク荳玖ソ吩クェ莉サ蜉。逧<EFBFBD><EFBFBD>髞ョ隕∫せ縲<EFBFBD>
|
||||||
|
|
||||||
|
* **窶廣窶晄弍騾画叫鬚倡噪譏守。ョ遲疲。<E796B2>**<EFBFBD>夂畑謌キ逕ィ蜊穂クェ蟄玲ッ榊屓遲秘画叫鬚假シ瑚。ィ遉コ蟾イ莉朱「<EFBFBD>ョセ騾蛾。ケ荳ュ蛛壼<EFBFBD>騾画叫<EFBFBD>悟コ皮峩謗・驥<EFBFBD>コウ蜈カ蟇ケ蠎泌性荵会シ域ュ、螟<EFBFBD>クコ窶應クェ莠コ窶晢シ会シ梧裏髴蜀肴ャ。遑ョ隶、縲<EFBFBD>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>怙扈捺桷蛹匁耳霑<E880B3>**<EFBFBD>壻スソ逕ィ騾画叫鬚假シ<EFBFBD>/B/C<>画弍鬮俶譜縲∵ク<E288B5>匆逧<E58C86>イ滄壽婿蠑擾シ檎ャヲ蜷井コァ蜩∫サ冗炊蛛丞・ス<EFBDA5>瑚<EFBFBD>蠢ォ騾滄煤螳壼<E89EB3>髞ョ蜿倬㍼<E580AC>亥ヲょ粋菴懈婿霄ォ莉ス<E88E89>峨<E5B3A8>
|
||||||
|
* **荳倶ク豁・蠎碑★辟ヲ窶懷粋蜷梧<E89CB7><E6A2A7>噪窶<E599AA>**<EFBFBD>壼惠遑ョ隶、蜷井ス懈婿霄ォ莉ス蜷趣シ御ク倶ク豁・蠎碑<EFBFBD>辟カ閨夂┬莠寂懷粋菴懷<EFBFBD>螳ケ窶晢シ瑚ソ呎弍逡悟ョ壼粋蜷瑚激蝗エ縲∽コ、莉俶<EFBFBD><EFBFBD>㊥蜥梧揀蛻ゥ荵牙苅逧<EFBFBD><EFBFBD>ク蠢<EFBFBD>シ檎ャヲ蜷井サ寂應クサ菴凪晏芦窶懈<EFBFBD><EFBFBD>噪窶晉噪蜷亥酔襍キ闕蛾サ霎代<EFBFBD>
|
||||||
|
* **閭梧勹菫。諱ッ譏ッ譛画譜莉サ蜉。蛻<EFBDA1><E89BBB>逧<EFBFBD>燕謠<E78795>**<EFBFBD>壼ソ<EFBFBD>。サ謾カ髮<EFBFBD>ョ娯懷粋菴懈婿窶晏柱窶懷粋菴懷<EFBFBD>螳ケ窶晁ソ吩ク、鬘ケ蝓コ遑菫。諱ッ蜷趣シ梧燕閭ス蠖「謌先怏謨育噪莉サ蜉。蛻<EFBFBD><EFBFBD>譁ケ譯茨シ檎。ョ菫晏粋蜷悟セ句ク郁オキ闕臥噪蛻晉ィソ蜈キ譛蛾宙蟇ケ諤ァ縲<EFBFBD>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
螂ス逧<EFBFBD>シ悟粋菴懈婿譏ッ荳ェ莠コ縲<EFBFBD>
|
||||||
|
|
||||||
|
**謗・荳区擂<E58CBA>瑚ソ吩サス蜷亥酔譏ッ蜈ウ莠惹サ荵亥<E88DB5>螳ケ逧<EFBDB9>シ<EFBFBD>**<2A>亥庄莉・螟夐会シ<E4BC9A>
|
||||||
|
* **A. 莠ァ蜩<EFBDA7>/謚譛ッ蠑蜿<C280>**<EFBFBD>亥ヲりスッ莉カ蠑蜿代、pp蠑蜿醍ュ会シ<EFBFBD>
|
||||||
|
* **B. 隶セ隶。譛榊苅**<EFBFBD>亥ヲ6I/UX隶セ隶。縲∝刀迚瑚ョセ隶。遲会シ<E4BC9A>
|
||||||
|
* **C. 蜥ィ隸「譛榊苅**<EFBFBD>亥ヲよ<EFBFBD>逡・蜥ィ隸「縲∝クょ惻鬘セ髣ョ遲会シ<EFBFBD>
|
||||||
|
* **D. 蜀<>ョケ蛻帑ス<E5B891>**<EFBFBD>亥ヲよ枚譯医∬ァ<EFBFBD>「代∬ッセ遞句宛菴懃ュ会シ<EFBFBD>
|
||||||
|
* **E. 蜈カ莉<EFBDB6>**<EFBFBD>郁ッキ邂蜊戊ッエ譏趣シ<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> A
|
||||||
|
|
||||||
|
螂ス逧<EFBFBD>シ梧<EFBFBD>譚・諤サ扈楢ソ吩クェ莉サ蜉。逧<EFBFBD><EFBFBD>髞ョ隕∫せ縲<EFBFBD>
|
||||||
|
|
||||||
|
* **窶廣窶晄弍騾画叫鬚倡噪譏守。ョ遲疲。<E796B2>**<EFBFBD>夂畑謌キ逕ィ蜊穂クェ蟄玲ッ榊屓遲秘画叫鬚假シ瑚。ィ遉コ蟾イ莉朱「<EFBFBD>ョセ騾蛾。ケ荳ュ蛛壼<EFBFBD>騾画叫<EFBFBD>悟コ皮峩謗・驥<EFBFBD>コウ蜈カ蟇ケ蠎泌性荵会シ域ュ、螟<EFBFBD>クコ窶應クェ莠コ窶晢シ会シ梧裏髴蜀肴ャ。遑ョ隶、縲<EFBFBD>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>怙扈捺桷蛹匁耳霑<E880B3>**<EFBFBD>壻スソ逕ィ騾画叫鬚假シ<EFBFBD>/B/C<>画弍鬮俶譜縲∵ク<E288B5>匆逧<E58C86>イ滄壽婿蠑擾シ檎ャヲ蜷井コァ蜩∫サ冗炊蛛丞・ス<EFBDA5>瑚<EFBFBD>蠢ォ騾滄煤螳壼<E89EB3>髞ョ蜿倬㍼<E580AC>亥ヲょ粋菴懈婿霄ォ莉ス<E88E89>峨<E5B3A8>
|
||||||
|
* **荳倶ク豁・蠎碑★辟ヲ窶懷粋蜷梧<E89CB7><E6A2A7>噪窶<E599AA>**<EFBFBD>壼惠遑ョ隶、蜷井ス懈婿霄ォ莉ス蜷趣シ御ク倶ク豁・蠎碑<EFBFBD>辟カ閨夂┬莠寂懷粋菴懷<EFBFBD>螳ケ窶晢シ瑚ソ呎弍逡悟ョ壼粋蜷瑚激蝗エ縲∽コ、莉俶<EFBFBD><EFBFBD>㊥蜥梧揀蛻ゥ荵牙苅逧<EFBFBD><EFBFBD>ク蠢<EFBFBD>シ檎ャヲ蜷井サ寂應クサ菴凪晏芦窶懈<EFBFBD><EFBFBD>噪窶晉噪蜷亥酔襍キ闕蛾サ霎代<EFBFBD>
|
||||||
|
* **閭梧勹菫。諱ッ譏ッ譛画譜莉サ蜉。蛻<EFBDA1><E89BBB>逧<EFBFBD>燕謠<E78795>**<EFBFBD>壼ソ<EFBFBD>。サ謾カ髮<EFBFBD>ョ娯懷粋菴懈婿窶晏柱窶懷粋菴懷<EFBFBD>螳ケ窶晁ソ吩ク、鬘ケ蝓コ遑菫。諱ッ蜷趣シ梧燕閭ス蠖「謌先怏謨育噪莉サ蜉。蛻<EFBFBD><EFBFBD>譁ケ譯茨シ檎。ョ菫晏粋蜷悟セ句ク郁オキ闕臥噪蛻晉ィソ蜈キ譛蛾宙蟇ケ諤ァ縲<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> A
|
||||||
|
|
||||||
|
螂ス逧<EFBFBD>シ梧<EFBFBD>譚・諤サ扈楢ソ吩クェ莉サ蜉。逧<EFBFBD><EFBFBD>髞ョ隕∫せ縲<EFBFBD>
|
||||||
|
|
||||||
|
* **窶廣窶晄弍騾画叫鬚倡噪譏守。ョ遲疲。<E796B2>**<EFBFBD>夂畑謌キ逕ィ蜊穂クェ蟄玲ッ榊屓遲秘画叫鬚假シ瑚。ィ遉コ蟾イ莉朱「<EFBFBD>ョセ騾蛾。ケ荳ュ蛛壼<EFBFBD>騾画叫<EFBFBD>悟コ皮峩謗・驥<EFBFBD>コウ蜈カ蟇ケ蠎泌性荵会シ域ュ、螟<EFBFBD>クコ窶應コァ蜩ゞI/UX隶セ隶。窶晢シ会シ梧裏髴蜀肴ャ。遑ョ隶、縲<EFBDA4>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>怙扈捺桷蛹匁耳霑<E880B3>**<EFBFBD>壻スソ逕ィ騾画叫鬚假シ<EFBFBD>/B/C/D/E<>画弍鬮俶譜縲∵ク<E288B5>匆逧<E58C86>イ滄壽婿蠑擾シ檎ャヲ蜷井コァ蜩∫サ冗炊蛛丞・ス<EFBDA5>瑚<EFBFBD>蠢ォ騾滄煤螳壼<E89EB3>髞ョ蜿倬㍼<E580AC>亥ヲょ粋蜷梧<E89CB7><E6A2A7>噪<EFBFBD>峨<E5B3A8>
|
||||||
|
* **荳倶ク豁・蠎碑★辟ヲ窶懈<E7AAB6>ク蠢<EFBDB8>膚荳壼<E88DB3>蛻<EFBFBD><EFBFBD>**<EFBFBD>壼惠譏守。ョ蜷井ス懈婿<EFBFBD>井クェ莠コ<EFBFBD>牙柱蜷井ス懷<EFBFBD>螳ケ<EFBFBD><EFBFBD>I/UX隶セ隶。<E99AB6>牙錘<E78999>御ク倶ク豁・蠢<EFBDA5>。サ閨夂┬莠守畑謌キ逧<EFBDB7><E980A7>ク蠢<EFBDB8>膚荳壻ク取ウ募セ句<EFBDBE>蛻<EFBFBD>シ亥ヲら衍隸<E8A18D>コァ譚<EFBDA7>∽コ、莉俶<E88E89><E4BFB6>㊥縲∽サ俶ャセ<EFBDAC>会シ瑚ソ呎弍襍キ闕画怏謨亥粋蜷檎噪蝓コ遑縲<C280>
|
||||||
|
* **豐滄夐怙謖蛾サ霎鷹。コ蠎城ォ俶譜謗ィ霑<EFBDA8>**<EFBFBD>夐<EFBFBD>蠕ェ窶懷粋菴懈婿 竊<> 蜷井ス懷<EFBDBD>螳ケ 竊<> 譬ク蠢<EFBDB8>擅谺セ窶晉噪謠宣琉騾サ霎托シ御スソ逕ィ騾画叫鬚假シ檎ャヲ蜷井コァ蜩∫サ冗炊<E58697>育ャャ莠泌ュ」<EFBDAD>臥サ捺桷蛹悶<E682B6>ォ俶譜逧<E8AD9C>イ滄壼¥螂ス縲<EFBDBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 蜈ィ驛ィ蜀<EFBDA8>ョケ
|
||||||
|
|
||||||
|
* **窶懷<E7AAB6>驛ィ蜀<EFBDA8>ョケ窶晄弍譏守。ョ逧<EFBDAE><E980A7>髱「鬟朱勦蜈ウ豕ィ菫。蜿キ**<EFBFBD>壼ス鍋畑謌キ<EFBFBD>亥ー、蜈カ譏ッ莠ァ蜩∫サ冗炊<EFBFBD>牙ッケ譬ク蠢<EFBFBD>擅谺セ騾蛾。ケ<EFBFBD>亥ヲら衍隸<EFBFBD>コァ譚<EFBFBD>∽コ、莉倥∽サ俶ャセ遲会シ蛾画叫窶懷<EFBFBD>驛ィ蜀<EFBFBD>ョケ窶晄慮<EFBFBD>瑚。ィ譏主<EFBFBD>蟇ケ鬘ケ逶ョ逧<EFBFBD>**蜈ィ髱「鬟朱勦謗ァ蛻カ**蜥<>**豬∫ィ句庄謗ァ諤ァ**譛画栫鬮倩ヲ∵アゑシ悟コ秘サ倩ョ、隕<EFBDA4>尠謇譛牙<E8AD9B>髞ョ鬟朱勦轤ケ縲<EFBDB9>
|
||||||
|
* **蟾・菴懈婿譯磯怙謠蝉セ帚懷<C280>譫仙「槫シ窶晁御ク堺サ<E5A0BA>弍譁<E5BC8D>サカ**<EFBFBD>夐宙蟇ケ豁、邀サ髴豎ゑシ御コ、莉倡黄蠢<EFBFBD>。サ雜<EFBFBD>カ雁粋蜷梧枚譛ャ譛ャ霄ォ<EFBFBD>梧署萓帑コァ蜩∫サ冗炊隗定牡謇驥崎ァ<EFBFBD>噪**扈捺桷蛹門<E89BB9>譫<EFBFBD>**<2A>亥ヲ1ros & Cons縲∬ー亥愛蠑ケ諤ァ縲∵鴬陦梧ク<E6A2A7>黒<EFBFBD>会シ瑚ソ呎弍闔キ蠕怜<E8A095>遑ョ隶、逧<EFBDA4><E980A7>髞ョ縲<EFBDAE>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>溜邇ッ譏ッ蜷ッ蜉ィ莉サ蜉。逧<EFBDA1><E980A7>遑ョ闃らせ**<EFBFBD>夐夊ソ<EFBFBD>画叫鬚倅セ晄ャ。遑ョ隶、窶懷粋菴懈婿<EFBFBD><EFBFBD><EFBFBD>俄<EFBFBD> 鬘ケ逶ョ蜀<EFBDAE>ョケ<EFBDAE><EFBDB9><EFBFBD>俄<EFBFBD> 譬ク蠢<EFBDB8>擅谺セ<E8B0BA>亥<EFBFBD>驛ィ<E9A99B>俄晏錘<E6998F>檎畑謌キ髴豎ょキイ貂<EFBDB2>匆<EFBFBD>悟コ皮ォ句叉諤サ扈灘ケカ螻慕、コ蜈キ菴灘キ・菴懈婿譯茨シ悟ッサ豎よ怙扈育。ョ隶、縲<EFBDA4>
|
||||||
|
* **豐滄夐怙鬮俶譜荳皮ャヲ蜷育畑謌キ諤晉サエ讓。蠑<EFBDA1>**<EFBFBD>壽紛荳ェ莠、莠定ソ<EFBFBD>ィ具シ磯画叫鬚伜シ募ッシ縲∵婿譯磯乗<EFBFBD>縲∵価隸コ扈捺桷蛹紋コァ蜃コ<EFBFBD>牙ョ悟<EFBFBD>蛹ケ驟堺コァ蜩∫サ冗炊<EFBFBD>育ャャ莠泌ュ」<EFBFBD>蛾ォ俶譜縲∫岼譬<EFBFBD><EFBFBD>遑ョ縲∵ウィ驥肴オ∫ィ倶ク朱」朱勦蛻<EFBFBD>梵逧<EFBFBD>′荳夂音諤ァ縲<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 蜿ッ莉・逧<EFBDA5> <20>悟ーア霑呎<E99C91>キ
|
||||||
|
|
||||||
|
* **逕ィ謌キ譏守。ョ遑ョ隶、譏ッ蜷ッ蜉ィ莉サ蜉。逧<EFBDA1>怙扈井ソ。蜿キ**<EFBFBD>壼ス鍋畑謌キ蟇ケ諤サ扈鍋噪蟾・菴懈婿譯茨シ亥桁蜷ォ謇ァ陦御ココ縲∽ササ蜉。縲∽コァ蜃コ<EFBFBD>牙屓螟坂懷庄莉・逧<EFBFBD>晉ュ芽け螳夊ッ肴慮<EFBFBD>瑚。ィ譏朱怙豎ら。ョ隶、豬∫ィ句キイ髣ュ邇ッ<EFBFBD>悟コ皮ォ句叉謇ァ陦御ササ蜉。蛻<EFBFBD><EFBFBD>縲<EFBFBD>
|
||||||
|
* **蟾・菴懈婿譯磯怙蜈キ菴馴乗<C280>莉・闔キ蠕礼。ョ隶、**<EFBFBD>壼髄逕ィ謌キ<EFBFBD>亥ー、蜈カ譏ッ莠ァ蜩∫サ冗炊<EFBFBD>牙ア慕、コ逧<EFBFBD>ョ。蛻貞ソ<EFBFBD>。サ蛹<EFBFBD>性譏守。ョ逧<EFBFBD>**謇ァ陦御ココ<EFBDBA><EFBDBA>隹<EFBFBD>シ峨∽ササ蜉。<E89C89>亥★莉荵茨シ峨∽コァ蜃コ<E89C83>井コ、莉倅サ荵茨シ<E88CA8>**<2A>瑚ソ咏ャヲ蜷亥<E89CB7>蟇ケ豬∫ィ句庄謗ァ蜥檎サ捺棡鬚<E6A3A1>悄逧<E68284>ヲ∵アゅ<E38285>
|
||||||
|
* **窶懃。ョ隶、蜷取鴬陦娯晄弍譬ク蠢<EFBDB8>キ・菴懃コェ蠕<EFBDAA>**<EFBFBD>壼ソ<EFBFBD>。サ荳・譬シ驕オ螳遺懷<EFBFBD>遑ョ隶、譁ケ譯茨シ悟<EFBFBD>蛻<EFBFBD><EFBFBD>莉サ蜉。窶晉噪豬∫ィ九ら畑謌キ逧<EFBFBD>。ョ隶、譏ッ隗」髯、窶憺怙豎ら。ョ隶、窶晄橿閭ス荳ュ<HARD-GATE>逧<EFBFBD>髪荳譚。莉カ縲<EFBFBD>
|
||||||
|
* **莉サ蜉。蛻<EFBDA1><E89BBB>謖<EFBFBD>サ、髴扈捺桷蛹紋ク泌桁蜷ォ蜈ィ驛ィ閭梧勹**<EFBFBD>壼惠@蝗「髦滓<EFBFBD>蜻俶慮<EFBFBD>悟コ疲ク<EFBFBD>匆螟崎ソー逕ィ謌キ遑ョ隶、霑<EFBFBD>噪謇譛牙<EFBFBD>髞ョ閭梧勹菫。諱ッ<EFBFBD>亥粋菴懈婿縲∝<EFBFBD>螳ケ縲∵<EFBFBD>ク蠢<EFBFBD><EFBFBD>蛻<EFBFBD>シ会シ悟ケカ扈吝<EFBFBD>譏守。ョ逧<EFBFBD>オキ闕<EFBFBD>/螳。譟・隕∵アゑシ檎。ョ菫晏キ・菴懈婿蜷大㊥遑ョ縲<EFBDAE>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 諤サ鬚<EFBDBB>ョ<EFBFBD> 2 荳<>シ<EFBFBD> 4 蜻ィ莠、莉伜<E88E89>驛ィ<E9A99B><EFBDA8> 2 霓ョ蜈崎エ
|
||||||
|
|
||||||
|
* **逕ィ謌キ謠蝉セ帛<EFBDBE>菴灘盾謨ー譏ッ莉サ蜉。謗ィ霑帷噪蜈ウ髞ョ闃らせ**<EFBFBD>壼ス鍋畑謌キ譏守。ョ蝗槫、埼「<EFBFBD>ョ励∝捉譛溘∽ソョ謾ケ谺。謨ー遲牙<EFBFBD>菴灘膚荳壽擅谺セ譌カ<EFBFBD>瑚。ィ譏朱怙豎ょキイ莉寂懷ョ壽ァ窶晁ソ帛<EFBFBD>窶懷ョ夐㍼窶晞亳谿オ<EFBFBD>悟コ皮ォ句叉諤サ扈灘ケカ蜷梧ュ・扈呎鴬陦梧<EFBFBD>蜻假シ梧裏髴蜀肴ャ。遑ョ隶、縲<EFBFBD>
|
||||||
|
* **窶懈潔蟶ク隗<EFBDB8>、<EFBFBD>炊窶晄弍譏守。ョ逧<EFBDAE>肢譚<E882A2>ソ。蜿キ**<EFBFBD>壼ス鍋畑謌キ蟇ケ髱樊<EFBFBD>ク蠢<EFBFBD>擅谺セ<EFBFBD>亥ヲりエ」莉サ荳企剞<EFBFBD>芽。ィ遉コ窶懈潔蟶ク隗<EFBFBD><EFBFBD>窶晢シ瑚。ィ譏主<EFBFBD>菫。莉サ荳謎ク壼愛譁ュ<EFBFBD>悟コ泌渕莠手。御ク壽Ρ萓句柱鬟朱勦蟷ウ陦。蜴溷<EFBFBD>襍キ闕会シ梧裏髴蜿榊、崎ソス髣ョ縲<EFBFBD>
|
||||||
|
* **菫。諱ッ諤サ扈馴怙扈捺桷蛹紋ク泌桁蜷ォ蜈ィ驛ィ隕∫エ<E288AB>**<EFBFBD>壼髄逕ィ謌キ螟崎ソー遑ョ隶、逧<EFBFBD>ソ。諱ッ譌カ<EFBFBD>悟コ疲ク<EFBFBD>匆蛻怜<EFBFBD>謇譛牙<EFBFBD>髞ョ蜿倬㍼<EFBFBD>亥粋菴懈婿縲∝<EFBFBD>螳ケ縲<EFBFBD>≡鬚昴∵慮髣エ縲∵<EFBFBD>ク蠢<EFBFBD><EFBFBD>蛻<EFBFBD>シ会シ瑚ソ呎里譏ッ蟇ケ逕ィ謌キ逧<EFBFBD>。ョ隶、<EFBFBD>御ケ滉クコ蝗「髦滓<EFBFBD>蜻俶署萓帑コ<EFBFBD>ョ梧紛逧<EFBFBD>キ・菴懆レ譎ッ縲<EFBFBD>
|
||||||
|
* **豐滄夐怙鬮俶譜蟷カ鬚<EFBDB6>相荳倶ク豁・**<EFBFBD>壼惠闔キ蠕怜<EFBFBD>驛ィ蠢<EFBFBD>ヲ∽ソ。諱ッ蜷趣シ悟コ皮ォ句叉蜻顔衍逕ィ謌キ荳倶ク豁・陦悟勘<EFBFBD>亥ヲや懷酔豁・扈吝粋蜷悟セ句ク遺晏柱窶懈署萓帷サ捺桷蛹匁冠蜻岩晢シ会シ瑚ソ咏ャヲ蜷井コァ蜩∫サ冗炊蟇ケ豬∫ィ句庄謗ァ蜥梧<EFBFBD>遑ョ莠ァ蜃コ逧<EFBFBD>悄譛帙<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 蠑蟋<C280>
|
||||||
|
|
||||||
|
* **窶懷シ蟋銀晄弍譏守。ョ逧<EFBDAE>鴬陦梧欠莉、**<EFBFBD>壼ス鍋畑謌キ蟇ケ蟾イ遑ョ隶、逧<EFBFBD>キ・菴懈婿譯亥屓螟坂懷シ蟋銀晄慮<EFBFBD>檎ュ牙酔莠寂懈鴬陦娯晄<EFBFBD>窶徙k窶晢シ梧弍蜷ッ蜉ィ莉サ蜉。蛻<EFBFBD><EFBFBD>逧<EFBFBD>怙扈育。ョ隶、菫。蜿キ縲<EFBFBD>
|
||||||
|
* **莉サ蜉。蛻<EFBDA1><E89BBB>髴蝨ィ逕ィ謌キ遑ョ隶、蜷守ォ句叉霑幄。<E5B984>**<EFBFBD>壽噺蛻ー窶懷シ蟋銀晄欠莉、蜷趣シ悟コ皮峩謗・縲∵ク<EFBFBD>匆蝨ー @ 逶ク蜈ウ蝗「髦滓<E9ABA6>蜻伜ケカ荳玖セセ蛹<EFBDBE>性蜈ィ驛ィ閭梧勹逧<E58BB9>ッヲ扈<EFBDA6>ササ蜉。<E89C89>梧裏髴蜀肴ャ。隸「髣ョ謌匁サ扈薙<E89699>
|
||||||
|
* **豐滄壽オ∫ィ矩<EFBDA8>蠕ェ窶懃。ョ隶、-謇ァ陦娯晉噪荳・譬シ鬘コ蠎<EFBDBA>**<EFBFBD>壼ソ<EFBFBD>。サ荳・譬シ驕オ螳遺憺怙豎ら。ョ隶、 竊<> 譁ケ譯域サ扈<EFBDBB> 竊<> 逕ィ謌キ遑ョ隶、 竊<> 莉サ蜉。蛻<EFBDA1><E89BBB>窶晉噪豬∫ィ具シ檎畑謌キ逧<EFBDB7>。ョ隶、譏ッ豬∫ィ区耳霑帷噪蜚ッ荳闃らせ縲<E3819B>
|
||||||
|
* **逕ィ謌キ蛛丞・ス鬮俶譜縲∫岼譬<E5B2BC><E8ADAC>遑ョ逧<EFBDAE>イ滄<E6BB84>**<EFBFBD>壻ス應クコ莠ァ蜩∫サ冗炊<EFBFBD>檎畑謌キ菴ソ逕ィ窶懷シ蟋銀晁ソ咏アサ邂豢∵欠莉、<EFBFBD>瑚。ィ譏主<EFBFBD>譛滓悍蝨ィ譁ケ譯育。ョ隶、蜷主ソォ騾溯ソ帛<EFBFBD>謇ァ陦碁亳谿オ<EFBFBD>梧イ滄壼コ秘∩蜈榊<EFBFBD>菴吶<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 謌題ヲ∝<EFBDA6>荳荳ェ閨倡畑荵ヲ
|
||||||
|
|
||||||
|
螂ス逧<EFBFBD>シ梧<EFBFBD>逋ス縲り§逕ィ蟇ケ雎。譏ッ蜈ウ髞ョ蟯嶺ス榊遭蟾・縲<EFBFBD>
|
||||||
|
|
||||||
|
**謗・荳区擂<E58CBA>瑚ッキ蜻願ッ画<EFBDAF><E794BB><EFBFBD>**
|
||||||
|
|
||||||
|
2. **菴<>譛蜈ウ蠢<EFBDB3>§逕ィ荵ヲ荳ュ逧<EFBDAD>頭莠帶<E88EA0>ク蠢<EFBDB8>擅谺セ<E8B0BA><EFBDBE>**<EFBFBD>亥庄莉・螟夐会シ<EFBFBD>
|
||||||
|
* A. **蟯嶺ス崎′雍」荳守サゥ謨育岼譬<E5B2BC>**<2A>亥ヲゆス墓ク<E5A293>匆逡悟ョ壼キ・菴懆激蝗エ蜥桑PI<50><49>
|
||||||
|
* B. **阮ェ驟ャ扈捺桷荳主書謾セ**<2A>亥渕譛ャ蟾・襍<EFBDA5>∝・夜≡縲∵悄譚<E68284>/閧。譚<EFBDA1>ヲゆス慕コヲ螳夲シ<E5A4B2>
|
||||||
|
* C. **菫晏ッ<E6998F>ク守衍隸<E8A18D>コァ譚<EFBDA7>**<2A>亥キ・菴懈<E88FB4>譫懷ス貞ア槭∽ソ晏ッ<E6998F>ケ牙苅闌<E88B85>峩<EFBFBD><E5B3A9>
|
||||||
|
* D. **蜷亥酔譛滄剞荳手ッ慕畑譛<E79591>**<2A>郁ッ慕畑譛滓慮髟ソ縲∬スャ豁」譬<EFBDA3>㊥<EFBFBD><E38AA5>
|
||||||
|
* E. **隗」髯、譚。莉カ荳守ォ樔ク夐剞蛻カ**<2A>井サ荵域ュ蜀オ荳句庄隗」髯、縲∵弍蜷ヲ髴遶樔ク夐剞蛻カ<E89BBB><EFBDB6>
|
||||||
|
|
||||||
|
莠<EFBFBD>ァ」菴<EFBFBD>譛蜈ウ豕ィ逧<EFBFBD>ヲ∫せ<EFBFBD>瑚<EFBFBD>隶ゥ蜷亥酔蠕句ク亥惠襍キ闕画慮驥咲せ逹蠅ィ<EFBFBD>悟ケウ陦。豼蜉ア荳朱」朱勦謗ァ蛻カ縲<EFBFBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> a
|
||||||
|
|
||||||
|
* **窶廣窶晄弍騾画叫鬚倡噪譏守。ョ遲疲。<E796B2>**<EFBFBD>夂畑謌キ逕ィ蜊穂クェ蟄玲ッ榊屓遲秘画叫鬚假シ瑚。ィ遉コ蟾イ莉朱「<EFBFBD>ョセ騾蛾。ケ荳ュ蛛壼<EFBFBD>騾画叫<EFBFBD>悟コ皮峩謗・驥<EFBFBD>コウ蜈カ蟇ケ蠎泌性荵会シ域ュ、螟<EFBFBD>クコ窶懷<EFBFBD>髞ョ蟯嶺ス榊遭蟾・窶晢シ会シ梧裏髴蜀肴ャ。遑ョ隶、縲<EFBFBD>
|
||||||
|
* **菫。諱ッ謾カ髮<EFBDB6>怙扈捺桷蛹匁耳霑<E880B3>**<EFBFBD>壻スソ逕ィ騾画叫鬚假シ<EFBFBD>/B/C/D<>画弍鬮俶譜縲∵ク<E288B5>匆逧<E58C86>イ滄壽婿蠑擾シ檎ャヲ蜷井コァ蜩∫サ冗炊蛛丞・ス<EFBDA5>瑚<EFBFBD>蠢ォ騾滄煤螳壼<E89EB3>髞ョ蜿倬㍼<E580AC>亥ヲり§逕ィ蟇ケ雎。邀サ蝙具シ峨<E5B3A8>
|
||||||
|
* **荳倶ク豁・蠎碑★辟ヲ窶懈<E7AAB6>ク蠢<EFBDB8>膚荳壼<E88DB3>蛻<EFBFBD><EFBFBD>**<EFBFBD>壼惠遑ョ隶、閨倡畑蟇ケ雎。邀サ蝙句錘<EFBFBD>御ク倶ク豁・蠢<EFBFBD>。サ閨夂┬莠守畑謌キ逧<EFBFBD><EFBFBD>ク蠢<EFBFBD><EFBFBD>蛻<EFBFBD>シ亥ヲり′雍」縲∬蓑驟ャ縲∫衍隸<EFBFBD>コァ譚<EFBFBD>∬ァ」髯、譚。莉カ<EFBFBD>会シ瑚ソ呎弍襍キ闕画怏謨郁§逕ィ荵ヲ逧<EFBFBD>渕遑縲<EFBFBD>
|
||||||
|
* **豐滄夐怙謖蛾サ霎鷹。コ蠎城ォ俶譜謗ィ霑<EFBDA8>**<EFBFBD>夐<EFBFBD>蠕ェ窶懷ッケ雎。邀サ蝙<EFBFBD> 竊<> 譬ク蠢<EFBDB8>擅谺セ窶晉噪謠宣琉騾サ霎托シ御スソ逕ィ騾画叫鬚假シ檎ャヲ蜷井コァ蜩∫サ冗炊<E58697>育ャャ莠泌ュ」<EFBDAD>臥サ捺桷蛹悶<E682B6>ォ俶譜逧<E8AD9C>イ滄壼¥螂ス縲<EFBDBD>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 蜈ィ驛ィ<E9A99B>悟粋蜷御ク蟷エ荳遲セ<E981B2>瑚ソ呎<EFBDBF>キ蟆ア荳肴戊オ泌<EFBDB5>
|
||||||
|
|
||||||
|
* **逕ィ謌キ蟆<EFBDB7>應ク蟷エ荳遲セ窶晁ァ<E69981>クコ譬ク蠢<EFBDB8>」朱勦邂。逅<EFBDA1>キ・蜈キ**<EFBFBD>夂畑謌キ譏守。ョ謠仙<EFBFBD>豁、遲也払莉・隗<EFBFBD>∩髟ソ譛滄寐菴」蟶ヲ譚・逧<EFBFBD>ォ倬「晁オ泌∩鬟朱勦<EFBFBD>瑚ソ吝渚譏<EFBFBD>莠<EFBFBD><EFBFBD>菴應クコ莠ァ蜩∫サ冗炊蟇ケ謌先悽謗ァ蛻カ蜥碁」朱勦蜑咲スョ逧<EFBFBD>シコ辜郁ッ画アゅ<EFBFBD>
|
||||||
|
* **窶懷<E7AAB6>驛ィ窶晞蛾。ケ陦ィ譏主ッケ蜈ィ髱「鬟朱勦隕<E58BA6>尠逧<E5B0A0>悄譛<E68284>**<EFBFBD>夂畑謌キ騾画叫蜈ウ豕ィ謇譛画<EFBFBD>ク蠢<EFBFBD>擅谺セ<EFBFBD>碁ェ瑚ッ∽コ<EFBFBD><EFBFBD>蟇ケ閨倡畑荵ヲ菴應クコ**蜈ィ髱「邂。逅<EFBDA1>キ・蜈キ**逧<>ョ壻ス搾シ梧悄譛帛<E8AD9B>蜷梧慮蜈キ螟<EFBDB7>ソ蜉ア縲∫コヲ譚溷柱鬟朱勦髫皮ヲサ蜉溯<E89C89>縲<EFBFBD>
|
||||||
|
* **豐滄夐怙蠑募ッシ逕ィ謌キ逅<EFBDB7>ァ」窶應ク蟷エ荳遲セ窶晉噪蜿碁擇諤ァ**<EFBFBD>壼惠蜷守サュ蟾・菴應クュ<EFBFBD>悟ソ<EFBFBD>。サ蛻<EFBFBD>梵豁、遲也払逧<EFBFBD>茜蠑奇シ亥ヲる剄菴朱柄譛溯オ泌∩鬟朱勦 vs. 蜿ッ閭ス蠖ア蜩肴<E89CA9>ク蠢<EFBDB8>遭蟾・遞ウ螳壽ァ荳主ス貞ア樊─<E6A88A>会シ梧署萓帛ケウ陦。蟒コ隶ョ縲<EFBDAE>
|
||||||
|
* **莠ァ蜃コ髴鬮伜コヲ扈捺桷蛹紋サ・蛹ケ驟咲畑謌キ隗定牡**<EFBFBD>壻コ、莉倡黄蠢<EFBFBD>。サ雜<EFBFBD>カ雁粋蜷梧枚譛ャ<EFBFBD>梧署萓帑コァ蜩∫サ冗炊隗定牡謇驥崎ァ<EFBFBD>噪**扈捺桷蛹門<E89BB9>譫<EFBFBD>**<2A><>ros & Cons縲∬ー亥愛隕∫せ縲∵鴬陦梧ク<E6A2A7>黒<EFBFBD>会シ瑚ソ呎弍貊。雜ウ蜈カ髴豎ら噪蜈ウ髞ョ縲<EFBDAE>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> ok<6F>悟庄莉・逧<EFBDA5>
|
||||||
|
|
||||||
|
* **逕ィ謌キ譏守。ョ遑ョ隶、譏ッ蜷ッ蜉ィ莉サ蜉。逧<EFBDA1>怙扈井ソ。蜿キ**<EFBFBD>壼ス鍋畑謌キ蟇ケ諤サ扈鍋噪蟾・菴懈婿譯茨シ亥桁蜷ォ謇ァ陦御ココ縲∽ササ蜉。縲∽コァ蜃コ<EFBFBD>牙屓螟坂徙k窶晄<EFBFBD>窶懷庄莉・逧<EFBFBD>晉ュ芽け螳夊ッ肴慮<EFBFBD>瑚。ィ譏朱怙豎ら。ョ隶、豬∫ィ句キイ髣ュ邇ッ<EFBFBD>悟コ皮ォ句叉謇ァ陦御ササ蜉。蛻<EFBFBD><EFBFBD>縲<EFBFBD>
|
||||||
|
* **蟾・菴懈婿譯磯怙蜈キ菴馴乗<C280>莉・闔キ蠕礼。ョ隶、**<EFBFBD>壼髄逕ィ謌キ<EFBFBD>亥ー、蜈カ譏ッ莠ァ蜩∫サ冗炊<EFBFBD>牙ア慕、コ逧<EFBFBD>ョ。蛻貞ソ<EFBFBD>。サ蛹<EFBFBD>性譏守。ョ逧<EFBFBD>**謇ァ陦御ココ<EFBDBA><EFBDBA>隹<EFBFBD>シ峨∽ササ蜉。<E89C89>亥★莉荵茨シ峨∽コァ蜃コ<E89C83>井コ、莉倅サ荵茨シ<E88CA8>**<2A>瑚ソ咏ャヲ蜷亥<E89CB7>蟇ケ豬∫ィ句庄謗ァ蜥檎サ捺棡鬚<E6A3A1>悄逧<E68284>ヲ∵アゅ<E38285>
|
||||||
|
* **窶懃。ョ隶、蜷取鴬陦娯晄弍譬ク蠢<EFBDB8>キ・菴懃コェ蠕<EFBDAA>**<EFBFBD>壼ソ<EFBFBD>。サ荳・譬シ驕オ螳遺懷<EFBFBD>遑ョ隶、譁ケ譯茨シ悟<EFBFBD>蛻<EFBFBD><EFBFBD>莉サ蜉。窶晉噪豬∫ィ九ら畑謌キ逧<EFBFBD>。ョ隶、譏ッ隗」髯、窶憺怙豎ら。ョ隶、窶晄橿閭ス荳ュ<HARD-GATE>逧<EFBFBD>髪荳譚。莉カ縲<EFBFBD>
|
||||||
|
* **莉サ蜉。蛻<EFBDA1><E89BBB>謖<EFBFBD>サ、髴扈捺桷蛹紋ク泌桁蜷ォ蜈ィ驛ィ閭梧勹**<EFBFBD>壼惠@蝗「髦滓<EFBFBD>蜻俶慮<EFBFBD>悟コ疲ク<EFBFBD>匆螟崎ソー逕ィ謌キ遑ョ隶、霑<EFBFBD>噪謇譛牙<EFBFBD>髞ョ閭梧勹菫。諱ッ<EFBFBD>亥粋菴懈婿縲∝<EFBFBD>螳ケ縲∵<EFBFBD>ク蠢<EFBFBD><EFBFBD>蛻<EFBFBD>シ会シ悟ケカ扈吝<EFBFBD>譏守。ョ逧<EFBFBD>オキ闕<EFBFBD>/螳。譟・隕∵アゑシ檎。ョ菫晏キ・菴懈婿蜷大㊥遑ョ縲<EFBDAE>
|
||||||
|
|
||||||
|
## 2026-03-06 窶<> 譛郁蓑 10k <20>瑚け螳夊ヲ∵撃髓ゥKPI <20><> 豐。螂夜≡<E5A49C><E289A1>
|
||||||
|
|
||||||
|
* **逕ィ謌キ譬ク蠢<EFBDB8>ッ画アよ弍窶憺峺謌先悽隗」郤ヲ窶<EFBDA6>**<EFBFBD>壽<EFBFBD>遑ョ隕∵アり蓑驟ャ<EFBFBD><EFBFBD>10k<EFBFBD>我ク錆PI蠑コ謖る朝<EFBFBD>御ク<EFBFBD>**譌<>螂夜≡縲∵裏遶樔ク夐剞蛻カ縲∽ク肴髪莉倅ク咲サュ遲セ陦・蛛ソ**<2A>悟ー<E6829F><EFBDB0>驛ィ邂。逅<EFBDA1>擒譚<E69392>柱鬟朱勦謗ァ蛻カ驛ス謚シ豕ィ蝨ィ扈ゥ謨域擅谺セ荳翫<E7BFAB>
|
||||||
|
* **窶懃サゥ謨域撃髓ゥ窶晄弍蜚ッ荳逧<C280>ョ。逅<EFBDA1>ク朱」朱勦謗ァ蛻カ蟾・蜈キ**<EFBFBD>夂畑謌キ諢丞崟騾夊ソ<EFBFBD>ー<EFBFBD>懃サュ遲セ/荳咲サュ遲セ窶昜ク寂廳PI霎セ譬<EFBDBE>ク主凄窶晏惠豕募セ倶ク顔峩謗・扈大ョ夲シ梧擂螳樒鴫隗<E9B4AB>∩扈乗オ手。・蛛ソ逧<EFBDBF>岼逧<E5B2BC>りソ呵ヲ∵アょ粋蜷悟ソ<E6829F>。サ蟆<EFBDBB>サゥ謨郁<E98381><C280>ク遞句コ上∵<C280><E288B5>㊥蜥檎サ捺棡蠎皮畑隶セ隶。蠕玲栫蜈カ荳・蟇<EFBDA5><EFBFBD>
|
||||||
|
* **蜷亥酔髴荳コ窶懈裏陦・蛛ソ隗」郤ヲ窶晄桷蟒コ蜷域ウ戊キッ蠕<EFBDAF>**<EFBFBD>夊オキ闕臥噪譬ク蠢<EFBFBD>倦謌俶弍<EFBFBD>悟惠蜉ウ蜉ィ豕墓。<EFBFBD>楔荳具シ悟ー<EFBFBD>懷粋蜷梧悄貊。荳咲サュ遲セ窶晁ソ吩ク騾壼クク髴謾ッ莉倡サ乗オ手。・蛛ソ<EFBFBD><EFBFBD><EFBFBD>臥噪諠<EFBFBD>ス「<EFBFBD>瑚スャ蛹紋クコ蝗<EFBFBD>窶懷患蜉ィ閠<EFBFBD>ク崎<EFBFBD>閭應ササ蟾・菴懌晁悟粋豕慕サ域ュ「荳疲裏髴陦・蛛ソ逧<EFBFBD>ュ蠖「縲りソ咎ォ伜コヲ萓晁オ門粋蜷御クュ蟇ケ窶應ク崎<EFBFBD>莉サ窶晉噪譏守。ョ逡悟ョ壼柱譛画譜隸∵<EFBFBD>縲<EFBFBD>
|
||||||
|
* **豐滄夐怙蠑募ッシ逕ィ謌キ蜈ウ豕ィ窶懃ィ句コ城」朱勦窶<E58BA6>**<EFBFBD>夂畑謌キ譁ケ譯育噪豕募セ矩」朱勦髮<EFBFBD>クュ莠<EFBFBD>**扈ゥ謨郁<E98381><C280>ク逧<EFBDB8>粋逅<E7B28B>ァ荳守ィ句コ乗ュ」蠖捺ァ**縲よ悴譚・蟒コ隶ョ髴驥咲せ謠千、コ<EFBDA4>瑚凶閠<E587B6><E996A0>ク譬<EFBDB8>㊥讓。邉翫∫ィ句コ丈ク榊<EFBDB8>謌冶ッ∵紺荳崎カウ<EFBDB6>瑚ッ・遲也払譫∵<E8ADAB>陲ォ隶、螳壻クコ霑晄ウ慕サ域ュ「<EFBDAD>悟渚閠碁擇荳エ謾ッ莉倩オ泌∩驥托シ<E68998>2N<32>臥噪鬟朱勦縲<E58BA6>
|
||||||
|
|||||||
@ -97,10 +97,15 @@ func (a *Agent) Chat(ctx context.Context, msgs []llm.Message, onToken func(strin
|
|||||||
return a.client.Stream(ctx, msgs, onToken)
|
return a.client.Stream(ctx, msgs, onToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildSystemPrompt constructs the full system prompt with soul + memory + injected context.
|
// BuildSystemPrompt 构建完整的 system prompt,每次实时读取 SOUL.md 以支持热更新。
|
||||||
func (a *Agent) BuildSystemPrompt(extraContext string) string {
|
func (a *Agent) BuildSystemPrompt(extraContext string) string {
|
||||||
|
// 实时读取 SOUL.md,支持在页面上修改后立即生效
|
||||||
|
soul := a.Soul
|
||||||
|
if data, err := os.ReadFile(filepath.Join(a.Dir, "SOUL.md")); err == nil {
|
||||||
|
soul = string(data)
|
||||||
|
}
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
sb.WriteString(a.Soul)
|
sb.WriteString(soul)
|
||||||
if mem := a.Memory(); mem != "" {
|
if mem := a.Memory(); mem != "" {
|
||||||
sb.WriteString("\n\n<memory>\n")
|
sb.WriteString("\n\n<memory>\n")
|
||||||
sb.WriteString(mem)
|
sb.WriteString(mem)
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -81,6 +82,7 @@ func (s *Server) routes() {
|
|||||||
|
|
||||||
g.GET("/rooms/:id/workspace", s.listWorkspace)
|
g.GET("/rooms/:id/workspace", s.listWorkspace)
|
||||||
g.GET("/rooms/:id/workspace/:file", s.getWorkspaceFile)
|
g.GET("/rooms/:id/workspace/:file", s.getWorkspaceFile)
|
||||||
|
g.PUT("/rooms/:id/workspace/:file", s.putWorkspaceFile)
|
||||||
g.GET("/rooms/:id/tasks", s.getTasks)
|
g.GET("/rooms/:id/tasks", s.getTasks)
|
||||||
g.GET("/rooms/:id/history", s.listHistory)
|
g.GET("/rooms/:id/history", s.listHistory)
|
||||||
g.GET("/rooms/:id/messages", s.getMessages)
|
g.GET("/rooms/:id/messages", s.getMessages)
|
||||||
@ -167,8 +169,9 @@ func (s *Server) wsHandler(c echo.Context) error {
|
|||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
UserName string `json:"user_name"`
|
UserName string `json:"user_name"`
|
||||||
|
Mode string `json:"mode"`
|
||||||
}
|
}
|
||||||
if json.Unmarshal(msg, &ev) != nil || ev.Type != "user_message" {
|
if json.Unmarshal(msg, &ev) != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
@ -177,11 +180,23 @@ func (s *Server) wsHandler(c echo.Context) error {
|
|||||||
if r == nil {
|
if r == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if ev.Type == "set_mode" && (ev.Mode == "plan" || ev.Mode == "build") {
|
||||||
|
r.Mode = ev.Mode
|
||||||
|
s.broadcast(roomID, room.Event{Type: room.EvtModeChange, RoomID: roomID, Mode: ev.Mode})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ev.Type != "user_message" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
userName := ev.UserName
|
userName := ev.UserName
|
||||||
if userName == "" {
|
if userName == "" {
|
||||||
userName = s.user.GetName()
|
userName = s.user.GetName()
|
||||||
}
|
}
|
||||||
go r.HandleUserMessage(context.Background(), userName, ev.Content)
|
go func() {
|
||||||
|
if err := r.HandleUserMessage(context.Background(), userName, ev.Content); err != nil {
|
||||||
|
log.Printf("[room %s] HandleUserMessage error: %v", roomID, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -200,10 +215,15 @@ func (s *Server) listRooms(c echo.Context) error {
|
|||||||
Members []string `json:"members"`
|
Members []string `json:"members"`
|
||||||
Color string `json:"color"`
|
Color string `json:"color"`
|
||||||
Team string `json:"team"`
|
Team string `json:"team"`
|
||||||
|
Mode string `json:"mode"`
|
||||||
}
|
}
|
||||||
var list []roomInfo
|
var list []roomInfo
|
||||||
for id, r := range s.rooms {
|
for id, r := range s.rooms {
|
||||||
list = append(list, roomInfo{ID: id, Name: r.Config.Name, Type: string(r.Config.Type), Status: r.Status, Master: r.Config.Master, Members: r.Config.Members, Color: r.Config.Color, Team: r.Config.Team})
|
mode := r.Mode
|
||||||
|
if mode == "" {
|
||||||
|
mode = "plan"
|
||||||
|
}
|
||||||
|
list = append(list, roomInfo{ID: id, Name: r.Config.Name, Type: string(r.Config.Type), Status: r.Status, Master: r.Config.Master, Members: r.Config.Members, Color: r.Config.Color, Team: r.Config.Team, Mode: mode})
|
||||||
}
|
}
|
||||||
return c.JSON(200, list)
|
return c.JSON(200, list)
|
||||||
}
|
}
|
||||||
@ -214,7 +234,7 @@ func (s *Server) createRoom(c echo.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if cfg.Type == "" {
|
if cfg.Type == "" {
|
||||||
cfg.Type = room.TypeDept
|
cfg.Type = room.TypeProject
|
||||||
}
|
}
|
||||||
dir := filepath.Join(s.roomsDir, cfg.Name)
|
dir := filepath.Join(s.roomsDir, cfg.Name)
|
||||||
os.MkdirAll(filepath.Join(dir, "workspace"), 0755)
|
os.MkdirAll(filepath.Join(dir, "workspace"), 0755)
|
||||||
@ -463,6 +483,23 @@ func (s *Server) getWorkspaceFile(c echo.Context) error {
|
|||||||
return c.JSON(200, map[string]string{"content": string(data)})
|
return c.JSON(200, map[string]string{"content": string(data)})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) putWorkspaceFile(c echo.Context) error {
|
||||||
|
id := c.Param("id")
|
||||||
|
file := c.Param("file")
|
||||||
|
var body struct {
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
if err := c.Bind(&body); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dir := filepath.Join(s.roomsDir, id, "workspace")
|
||||||
|
os.MkdirAll(dir, 0755)
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, file), []byte(body.Content), 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.JSON(200, map[string]string{"status": "ok"})
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) getMessages(c echo.Context) error {
|
func (s *Server) getMessages(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
historyFile := filepath.Join(s.roomsDir, id, "history", time.Now().Format("2006-01-02")+".md")
|
historyFile := filepath.Join(s.roomsDir, id, "history", time.Now().Format("2006-01-02")+".md")
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@ -20,8 +21,7 @@ import (
|
|||||||
type RoomType string
|
type RoomType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TypeDept RoomType = "dept"
|
TypeProject RoomType = "project"
|
||||||
TypeLeader RoomType = "leader"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Status string
|
type Status string
|
||||||
@ -51,17 +51,32 @@ type Room struct {
|
|||||||
Status Status
|
Status Status
|
||||||
ActiveAgent string // for working status display
|
ActiveAgent string // for working status display
|
||||||
Broadcast func(Event) // set by api layer
|
Broadcast func(Event) // set by api layer
|
||||||
|
|
||||||
|
// master 的会话历史,保持多轮对话上下文
|
||||||
|
masterHistory []llm.Message
|
||||||
|
historyMu sync.Mutex
|
||||||
|
|
||||||
|
Mode string // "plan" | "build"
|
||||||
|
pendingAssignments map[string]string // plan 模式下暂存的待执行任务
|
||||||
|
pendingPlanReply string // master 规划原文,用于生成计划文档
|
||||||
|
|
||||||
|
// Build 模式下成员对话跟踪
|
||||||
|
memberConvos map[string][]llm.Message // 成员名 -> 多轮对话历史
|
||||||
|
lastActiveMember string // 最后一个发出提问的成员
|
||||||
|
planFilename string // 当前任务计划文件名
|
||||||
}
|
}
|
||||||
|
|
||||||
type EventType string
|
type EventType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
EvtAgentMessage EventType = "agent_message"
|
EvtAgentMessage EventType = "agent_message"
|
||||||
EvtTaskAssign EventType = "task_assign"
|
EvtTaskAssign EventType = "task_assign"
|
||||||
EvtReview EventType = "review"
|
EvtReview EventType = "review"
|
||||||
EvtRoomStatus EventType = "room_status"
|
EvtRoomStatus EventType = "room_status"
|
||||||
EvtTasksUpdate EventType = "tasks_update"
|
EvtTasksUpdate EventType = "tasks_update"
|
||||||
EvtWorkspaceFile EventType = "workspace_file"
|
EvtWorkspaceFile EventType = "workspace_file"
|
||||||
|
EvtModeChange EventType = "mode_change"
|
||||||
|
EvtArtifact EventType = "artifact"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Event struct {
|
type Event struct {
|
||||||
@ -79,6 +94,8 @@ type Event struct {
|
|||||||
ActiveAgent string `json:"active_agent,omitempty"`
|
ActiveAgent string `json:"active_agent,omitempty"`
|
||||||
Action string `json:"action,omitempty"`
|
Action string `json:"action,omitempty"`
|
||||||
Filename string `json:"filename,omitempty"`
|
Filename string `json:"filename,omitempty"`
|
||||||
|
Mode string `json:"mode,omitempty"`
|
||||||
|
Title string `json:"title,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BoardEntry struct {
|
type BoardEntry struct {
|
||||||
@ -123,7 +140,7 @@ func Load(roomDir string, agentsDir string, skillsDir string) (*Room, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
r := &Room{Config: cfg, Dir: roomDir, members: make(map[string]*agent.Agent)}
|
r := &Room{Config: cfg, Dir: roomDir, members: make(map[string]*agent.Agent), Mode: "plan"}
|
||||||
|
|
||||||
if cfg.Master != "" {
|
if cfg.Master != "" {
|
||||||
agentPath := resolveAgentPath(agentsDir, cfg.Team, cfg.Master)
|
agentPath := resolveAgentPath(agentsDir, cfg.Team, cfg.Master)
|
||||||
@ -151,6 +168,11 @@ func (r *Room) emit(e Event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Room) setMode(mode string) {
|
||||||
|
r.Mode = mode
|
||||||
|
r.emit(Event{Type: EvtModeChange, Mode: mode})
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Room) setStatus(s Status, activeAgent, action string) {
|
func (r *Room) setStatus(s Status, activeAgent, action string) {
|
||||||
r.Status = s
|
r.Status = s
|
||||||
r.ActiveAgent = activeAgent
|
r.ActiveAgent = activeAgent
|
||||||
@ -187,6 +209,13 @@ func (r *Room) runMembersParallel(ctx context.Context, assignments map[string]st
|
|||||||
r.setStatus(StatusWorking, member.Config.Name, t)
|
r.setStatus(StatusWorking, member.Config.Name, t)
|
||||||
r.emit(Event{Type: EvtTaskAssign, From: r.master.Config.Name, To: name, Task: t})
|
r.emit(Event{Type: EvtTaskAssign, From: r.master.Config.Name, To: name, Task: t})
|
||||||
|
|
||||||
|
// Build 模式:发送简要状态消息,不流式输出内容
|
||||||
|
taskBrief := t
|
||||||
|
if len(taskBrief) > 60 {
|
||||||
|
taskBrief = taskBrief[:60] + "..."
|
||||||
|
}
|
||||||
|
r.emit(Event{Type: EvtAgentMessage, Agent: name, Role: "member", Content: fmt.Sprintf("正在处理: %s", taskBrief)})
|
||||||
|
|
||||||
boardCtx := board.ToContext()
|
boardCtx := board.ToContext()
|
||||||
extraCtx := skillXML
|
extraCtx := skillXML
|
||||||
if boardCtx != "" {
|
if boardCtx != "" {
|
||||||
@ -197,10 +226,10 @@ func (r *Room) runMembersParallel(ctx context.Context, assignments map[string]st
|
|||||||
llm.NewMsg("system", memberSystem),
|
llm.NewMsg("system", memberSystem),
|
||||||
llm.NewMsg("user", t),
|
llm.NewMsg("user", t),
|
||||||
}
|
}
|
||||||
|
// 静默收集输出,不流式推送到聊天
|
||||||
var memberReply strings.Builder
|
var memberReply strings.Builder
|
||||||
_, err := member.Chat(ctx, memberMsgs, func(token string) {
|
_, err := member.Chat(ctx, memberMsgs, func(token string) {
|
||||||
memberReply.WriteString(token)
|
memberReply.WriteString(token)
|
||||||
r.emit(Event{Type: EvtAgentMessage, Agent: name, Role: "member", Content: token, Streaming: true})
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
@ -215,10 +244,23 @@ func (r *Room) runMembersParallel(ctx context.Context, assignments map[string]st
|
|||||||
r.AppendHistory("member", name, result)
|
r.AppendHistory("member", name, result)
|
||||||
board.Add(name, result, "draft")
|
board.Add(name, result, "draft")
|
||||||
|
|
||||||
if strings.Contains(result, "# ") {
|
// 保存成员对话历史,支持后续多轮交互
|
||||||
|
if r.memberConvos == nil {
|
||||||
|
r.memberConvos = make(map[string][]llm.Message)
|
||||||
|
}
|
||||||
|
r.memberConvos[name] = memberMsgs
|
||||||
|
r.memberConvos[name] = append(r.memberConvos[name], llm.NewMsg("assistant", result))
|
||||||
|
|
||||||
|
// 智能判断:文档 → artifact,对话/提问 → 聊天消息
|
||||||
|
if isDocument(result) {
|
||||||
|
title := extractTitle(result)
|
||||||
filename := fmt.Sprintf("%s-%s.md", name, time.Now().Format("20060102-150405"))
|
filename := fmt.Sprintf("%s-%s.md", name, time.Now().Format("20060102-150405"))
|
||||||
r.saveWorkspace(filename, result)
|
r.saveWorkspace(filename, result)
|
||||||
r.emit(Event{Type: EvtWorkspaceFile, Filename: filename, Content: result})
|
r.emit(Event{Type: EvtArtifact, Agent: name, Filename: filename, Title: title})
|
||||||
|
} else {
|
||||||
|
// 非文档内容(提问、简短回复等)直接显示在聊天中
|
||||||
|
r.emit(Event{Type: EvtAgentMessage, Agent: name, Role: "member", Content: result})
|
||||||
|
r.lastActiveMember = name
|
||||||
}
|
}
|
||||||
}(memberName, task)
|
}(memberName, task)
|
||||||
}
|
}
|
||||||
@ -262,6 +304,7 @@ func (r *Room) runChallengeRound(ctx context.Context, board *SharedBoard, skillX
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
r.emit(Event{Type: EvtAgentMessage, Agent: n, Role: "challenge", Content: "", Streaming: false})
|
||||||
result := reply.String()
|
result := reply.String()
|
||||||
if strings.Contains(result, "CHALLENGE:") {
|
if strings.Contains(result, "CHALLENGE:") {
|
||||||
board.Add(n, result, "challenge")
|
board.Add(n, result, "challenge")
|
||||||
@ -277,61 +320,213 @@ func (r *Room) Handle(ctx context.Context, userMsg string) error {
|
|||||||
return r.HandleUserMessage(ctx, "user", userMsg)
|
return r.HandleUserMessage(ctx, "user", userMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleUserMessage processes a user message with a specific user name.
|
// parseUserMentions 从用户消息中提取 @agent 指派。
|
||||||
|
// 返回指派 map 和去除 @agent 后的剩余消息。
|
||||||
|
func parseUserMentions(text string, validMembers map[string]*agent.Agent) map[string]string {
|
||||||
|
assignments := make(map[string]string)
|
||||||
|
for _, line := range strings.Split(text, "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if !strings.HasPrefix(line, "@") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rest := strings.TrimPrefix(line, "@")
|
||||||
|
idx := strings.IndexAny(rest, " \t")
|
||||||
|
if idx <= 0 {
|
||||||
|
// 只有 @name 没有任务内容,跳过
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := strings.TrimSpace(rest[:idx])
|
||||||
|
task := strings.TrimSpace(rest[idx+1:])
|
||||||
|
if _, ok := validMembers[name]; ok && task != "" {
|
||||||
|
assignments[name] = task
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return assignments
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleUserMessage 处理用户消息。
|
||||||
|
// 如果用户消息中包含 @agent,直接将任务分配给对应 agent,不经过 master。
|
||||||
func (r *Room) HandleUserMessage(ctx context.Context, userName, userMsg string) error {
|
func (r *Room) HandleUserMessage(ctx context.Context, userName, userMsg string) error {
|
||||||
if r.master == nil {
|
if r.master == nil {
|
||||||
return fmt.Errorf("room has no master agent configured")
|
return fmt.Errorf("room has no master agent configured")
|
||||||
}
|
}
|
||||||
r.AppendHistory("user", userName, userMsg)
|
r.AppendHistory("user", userName, userMsg)
|
||||||
|
|
||||||
|
// 检测用户是否直接 @agent 指派任务
|
||||||
|
userAssignments := parseUserMentions(userMsg, r.members)
|
||||||
|
if len(userAssignments) > 0 {
|
||||||
|
return r.handleDirectAssign(ctx, userAssignments)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build 模式下,如果有 plan 阶段暂存的待执行任务,直接执行
|
||||||
|
if r.Mode == "build" && len(r.pendingAssignments) > 0 {
|
||||||
|
log.Printf("[room %s] build 模式,执行 %d 个暂存任务", r.Config.Name, len(r.pendingAssignments))
|
||||||
|
assignments := r.pendingAssignments
|
||||||
|
r.pendingAssignments = nil
|
||||||
|
|
||||||
|
// 生成任务计划文档
|
||||||
|
r.planFilename = fmt.Sprintf("任务计划-%s.md", time.Now().Format("20060102-150405"))
|
||||||
|
planContent := "# 任务计划\n\n## 规划\n\n" + r.pendingPlanReply + "\n"
|
||||||
|
r.pendingPlanReply = ""
|
||||||
|
r.saveWorkspace(r.planFilename, planContent)
|
||||||
|
r.emit(Event{Type: EvtArtifact, Agent: r.master.Config.Name, Filename: r.planFilename, Title: "任务计划"})
|
||||||
|
|
||||||
|
skillXML := skill.ToXML(r.skillMeta)
|
||||||
|
board := &SharedBoard{}
|
||||||
|
r.setStatus(StatusWorking, "", "")
|
||||||
|
r.runMembersParallel(ctx, assignments, board, skillXML)
|
||||||
|
r.runChallengeRound(ctx, board, skillXML)
|
||||||
|
|
||||||
|
r.setStatus(StatusPending, "", "")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build 模式下,成员发出了提问,用户回复 → 转发给该成员继续对话
|
||||||
|
if r.Mode == "build" && r.lastActiveMember != "" {
|
||||||
|
memberName := r.lastActiveMember
|
||||||
|
member, ok := r.members[memberName]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("member %s not found", memberName)
|
||||||
|
}
|
||||||
|
log.Printf("[room %s] build 模式,将用户回复转发给 %s", r.Config.Name, memberName)
|
||||||
|
r.setStatus(StatusWorking, member.Config.Name, "")
|
||||||
|
|
||||||
|
// 追加用户回复到成员对话历史
|
||||||
|
if r.memberConvos == nil {
|
||||||
|
r.memberConvos = make(map[string][]llm.Message)
|
||||||
|
}
|
||||||
|
r.memberConvos[memberName] = append(r.memberConvos[memberName], llm.NewMsg("user", userMsg))
|
||||||
|
|
||||||
|
// 追加沟通记录到任务计划文档
|
||||||
|
r.appendPlanLog(userName, memberName, userMsg)
|
||||||
|
|
||||||
|
// 让成员继续对话
|
||||||
|
var memberReply strings.Builder
|
||||||
|
_, err := member.Chat(ctx, r.memberConvos[memberName], func(token string) {
|
||||||
|
memberReply.WriteString(token)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
r.setStatus(StatusPending, "", "")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
result := memberReply.String()
|
||||||
|
r.memberConvos[memberName] = append(r.memberConvos[memberName], llm.NewMsg("assistant", result))
|
||||||
|
r.AppendHistory("member", memberName, result)
|
||||||
|
|
||||||
|
// 追加成员回复到任务计划文档
|
||||||
|
r.appendPlanLog(memberName, userName, result)
|
||||||
|
|
||||||
|
// 智能判断输出类型
|
||||||
|
if isDocument(result) {
|
||||||
|
title := extractTitle(result)
|
||||||
|
filename := fmt.Sprintf("%s-%s.md", memberName, time.Now().Format("20060102-150405"))
|
||||||
|
r.saveWorkspace(filename, result)
|
||||||
|
r.emit(Event{Type: EvtArtifact, Agent: memberName, Filename: filename, Title: title})
|
||||||
|
r.lastActiveMember = "" // 文档产出,对话结束
|
||||||
|
} else {
|
||||||
|
r.emit(Event{Type: EvtAgentMessage, Agent: memberName, Role: "member", Content: result})
|
||||||
|
// lastActiveMember 保持不变,用户可以继续回复
|
||||||
|
}
|
||||||
|
|
||||||
|
r.setStatus(StatusPending, "", "")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
r.setStatus(StatusThinking, "", "")
|
r.setStatus(StatusThinking, "", "")
|
||||||
|
|
||||||
// Build master context
|
// 构建 system prompt
|
||||||
teamXML := r.buildTeamXML()
|
teamXML := r.buildTeamXML()
|
||||||
skillXML := skill.ToXML(r.skillMeta)
|
skillXML := skill.ToXML(r.skillMeta)
|
||||||
|
|
||||||
// Build user info XML
|
|
||||||
var userXML string
|
var userXML string
|
||||||
if r.User != nil {
|
if r.User != nil {
|
||||||
userXML = r.User.BuildUserXML()
|
userXML = r.User.BuildUserXML()
|
||||||
}
|
}
|
||||||
|
|
||||||
extraContext := userXML + "\n\n" + teamXML + "\n\n" + skillXML
|
extraContext := userXML + "\n\n" + teamXML + "\n\n" + skillXML
|
||||||
systemPrompt := r.master.BuildSystemPrompt(extraContext)
|
systemPrompt := r.master.BuildSystemPrompt(extraContext)
|
||||||
|
sysMsg := llm.NewMsg("system", systemPrompt+fmt.Sprintf(`
|
||||||
|
|
||||||
masterMsgs := []llm.Message{
|
当前用户:%s
|
||||||
llm.NewMsg("system", systemPrompt+"\n\nYou are the master of this team. When you need a team member to do something, output a line like: ASSIGN:<member_name>:<task description>. When you are done reviewing and satisfied, output DONE:<summary>."),
|
|
||||||
llm.NewMsg("user", userMsg),
|
分配任务给成员时,使用 @ 格式,每行一个:
|
||||||
|
@成员名 任务描述
|
||||||
|
|
||||||
|
直接回复用户时,正常说话即可,不需要 @。`, userName))
|
||||||
|
|
||||||
|
// 使用持久化的会话历史
|
||||||
|
r.historyMu.Lock()
|
||||||
|
// 始终更新 system prompt(可能 SOUL.md 改了)
|
||||||
|
if len(r.masterHistory) == 0 {
|
||||||
|
r.masterHistory = []llm.Message{sysMsg}
|
||||||
|
} else {
|
||||||
|
r.masterHistory[0] = sysMsg
|
||||||
|
}
|
||||||
|
r.masterHistory = append(r.masterHistory, llm.NewMsg("user", userMsg))
|
||||||
|
|
||||||
|
// 限制历史长度,保留 system + 最近 20 轮对话
|
||||||
|
if len(r.masterHistory) > 41 {
|
||||||
|
r.masterHistory = append(r.masterHistory[:1], r.masterHistory[len(r.masterHistory)-40:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Master planning loop
|
masterMsgs := make([]llm.Message, len(r.masterHistory))
|
||||||
|
copy(masterMsgs, r.masterHistory)
|
||||||
|
r.historyMu.Unlock()
|
||||||
|
|
||||||
|
// Master 规划循环
|
||||||
for iteration := 0; iteration < 5; iteration++ {
|
for iteration := 0; iteration < 5; iteration++ {
|
||||||
|
log.Printf("[room %s] master iteration %d, sending to LLM...", r.Config.Name, iteration)
|
||||||
var masterReply strings.Builder
|
var masterReply strings.Builder
|
||||||
_, err := r.master.Chat(ctx, masterMsgs, func(token string) {
|
_, err := r.master.Chat(ctx, masterMsgs, func(token string) {
|
||||||
masterReply.WriteString(token)
|
masterReply.WriteString(token)
|
||||||
r.emit(Event{Type: EvtAgentMessage, Agent: r.master.Config.Name, Role: "master", Content: token, Streaming: true})
|
r.emit(Event{Type: EvtAgentMessage, Agent: r.master.Config.Name, Role: "master", Content: token, Streaming: true})
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("[room %s] master chat error: %v", r.Config.Name, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
reply := masterReply.String()
|
reply := masterReply.String()
|
||||||
masterMsgs = append(masterMsgs, llm.NewMsg("assistant", reply))
|
log.Printf("[room %s] master reply (%d chars): %.100s...", r.Config.Name, len(reply), reply)
|
||||||
|
// 发送 streaming 结束信号
|
||||||
|
r.emit(Event{Type: EvtAgentMessage, Agent: r.master.Config.Name, Role: "master", Content: "", Streaming: false})
|
||||||
|
assistantMsg := llm.NewMsg("assistant", reply)
|
||||||
|
masterMsgs = append(masterMsgs, assistantMsg)
|
||||||
|
// 同步到持久化历史
|
||||||
|
r.historyMu.Lock()
|
||||||
|
r.masterHistory = append(r.masterHistory, assistantMsg)
|
||||||
|
r.historyMu.Unlock()
|
||||||
r.AppendHistory("master", r.master.Config.Name, reply)
|
r.AppendHistory("master", r.master.Config.Name, reply)
|
||||||
|
|
||||||
// Parse assignments
|
// 解析 @ 指令,只匹配已知成员名
|
||||||
assignments := parseAssignments(reply)
|
allMentions := parseAssignments(reply)
|
||||||
|
assignments := make(map[string]string)
|
||||||
|
for name, task := range allMentions {
|
||||||
|
if _, isMember := r.members[name]; isMember {
|
||||||
|
assignments[name] = task
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(assignments) == 0 {
|
if len(assignments) == 0 {
|
||||||
// No assignments, master is done
|
// 没有分配给任何成员的任务,master 直接回复了用户
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute assignments in parallel
|
// Plan 模式下不执行任务,暂存任务,提示用户切换到 Build 模式
|
||||||
|
if r.Mode != "build" {
|
||||||
|
r.pendingAssignments = assignments
|
||||||
|
r.pendingPlanReply = reply
|
||||||
|
log.Printf("[room %s] plan 模式,暂存 %d 个任务,等待用户切换到 build 模式", r.Config.Name, len(assignments))
|
||||||
|
r.emit(Event{Type: EvtAgentMessage, Agent: r.master.Config.Name, Role: "master",
|
||||||
|
Content: "已完成任务规划。请按 Tab 切换到 Build 模式,然后发送确认开始执行。"})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 并行执行成员任务
|
||||||
board := &SharedBoard{}
|
board := &SharedBoard{}
|
||||||
results := r.runMembersParallel(ctx, assignments, board, skillXML)
|
results := r.runMembersParallel(ctx, assignments, board, skillXML)
|
||||||
|
|
||||||
// Run challenge round
|
// 质疑轮
|
||||||
r.runChallengeRound(ctx, board, skillXML)
|
r.runChallengeRound(ctx, board, skillXML)
|
||||||
|
|
||||||
// Feed results back to master for review
|
// 将结果反馈给 master 审查
|
||||||
r.setStatus(StatusThinking, "", "")
|
r.setStatus(StatusThinking, "", "")
|
||||||
var resultsStr strings.Builder
|
var resultsStr strings.Builder
|
||||||
for memberName, result := range results {
|
for memberName, result := range results {
|
||||||
@ -342,15 +537,16 @@ func (r *Room) HandleUserMessage(ctx context.Context, userName, userMsg string)
|
|||||||
if boardCtx != "" {
|
if boardCtx != "" {
|
||||||
feedbackMsg += "\n\nTeam board:\n" + boardCtx
|
feedbackMsg += "\n\nTeam board:\n" + boardCtx
|
||||||
}
|
}
|
||||||
feedbackMsg += "\n\nPlease review. If satisfied output DONE:<summary>, otherwise output ASSIGN instructions for revisions."
|
feedbackMsg += "\n\n请审查以上结果。如果满意,直接回复总结给用户即可。如果需要修改,使用 @成员名 分配修订任务。"
|
||||||
masterMsgs = append(masterMsgs, llm.NewMsg("user", feedbackMsg))
|
feedbackLLMMsg := llm.NewMsg("user", feedbackMsg)
|
||||||
|
masterMsgs = append(masterMsgs, feedbackLLMMsg)
|
||||||
|
// 同步反馈消息到持久化历史
|
||||||
|
r.historyMu.Lock()
|
||||||
|
r.masterHistory = append(r.masterHistory, feedbackLLMMsg)
|
||||||
|
r.historyMu.Unlock()
|
||||||
|
|
||||||
// Update tasks
|
// 更新任务列表
|
||||||
r.updateTasks(masterMsgs)
|
r.updateTasks(masterMsgs)
|
||||||
|
|
||||||
if strings.Contains(reply, "DONE:") {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r.setStatus(StatusPending, "", "")
|
r.setStatus(StatusPending, "", "")
|
||||||
@ -361,6 +557,18 @@ func (r *Room) HandleUserMessage(ctx context.Context, userName, userMsg string)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleDirectAssign 处理用户直接 @agent 指派的任务,跳过 master 规划。
|
||||||
|
func (r *Room) handleDirectAssign(ctx context.Context, assignments map[string]string) error {
|
||||||
|
skillXML := skill.ToXML(r.skillMeta)
|
||||||
|
board := &SharedBoard{}
|
||||||
|
|
||||||
|
r.runMembersParallel(ctx, assignments, board, skillXML)
|
||||||
|
r.runChallengeRound(ctx, board, skillXML)
|
||||||
|
|
||||||
|
r.setStatus(StatusPending, "", "")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Room) updateMasterMemory(ctx context.Context, task string, msgs []llm.Message) {
|
func (r *Room) updateMasterMemory(ctx context.Context, task string, msgs []llm.Message) {
|
||||||
summaryPrompt := fmt.Sprintf("Based on this task: %q\nSummarize key learnings and patterns in 3-5 bullet points for future reference. Be concise.", task)
|
summaryPrompt := fmt.Sprintf("Based on this task: %q\nSummarize key learnings and patterns in 3-5 bullet points for future reference. Be concise.", task)
|
||||||
memMsgs := append(msgs, llm.NewMsg("user", summaryPrompt))
|
memMsgs := append(msgs, llm.NewMsg("user", summaryPrompt))
|
||||||
@ -374,6 +582,23 @@ func (r *Room) updateMasterMemory(ctx context.Context, task string, msgs []llm.M
|
|||||||
r.master.SaveMemory(filename, content)
|
r.master.SaveMemory(filename, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isDocument 判断内容是否为文档产出物(而非对话/提问)。
|
||||||
|
// 文档特征:包含 markdown 标题且内容较长。
|
||||||
|
func isDocument(content string) bool {
|
||||||
|
hasHeading := strings.Contains(content, "\n# ") || strings.HasPrefix(content, "# ")
|
||||||
|
return hasHeading && len([]rune(content)) > 500
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractTitle(content string) string {
|
||||||
|
for _, line := range strings.Split(content, "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if strings.HasPrefix(line, "# ") {
|
||||||
|
return strings.TrimPrefix(line, "# ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func min(a, b int) int {
|
func min(a, b int) int {
|
||||||
if a < b {
|
if a < b {
|
||||||
return a
|
return a
|
||||||
@ -381,16 +606,61 @@ func min(a, b int) int {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseAssignments 解析任务分配指令。
|
||||||
|
// 支持多行任务描述:从 @成员名 开始,到下一个 @成员名 或文本结束为止。
|
||||||
func parseAssignments(text string) map[string]string {
|
func parseAssignments(text string) map[string]string {
|
||||||
result := make(map[string]string)
|
result := make(map[string]string)
|
||||||
for _, line := range strings.Split(text, "\n") {
|
lines := strings.Split(text, "\n")
|
||||||
if strings.HasPrefix(line, "ASSIGN:") {
|
|
||||||
parts := strings.SplitN(strings.TrimPrefix(line, "ASSIGN:"), ":", 2)
|
var currentName string
|
||||||
if len(parts) == 2 {
|
var currentTask strings.Builder
|
||||||
result[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
|
|
||||||
|
flush := func() {
|
||||||
|
if currentName != "" {
|
||||||
|
task := strings.TrimSpace(currentTask.String())
|
||||||
|
if task != "" {
|
||||||
|
result[currentName] = task
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
currentName = ""
|
||||||
|
currentTask.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
trimmed := strings.TrimSpace(line)
|
||||||
|
// 检测新的 @成员名 行
|
||||||
|
if strings.HasPrefix(trimmed, "@") {
|
||||||
|
rest := strings.TrimPrefix(trimmed, "@")
|
||||||
|
if idx := strings.IndexAny(rest, " \t"); idx > 0 {
|
||||||
|
name := strings.TrimSpace(rest[:idx])
|
||||||
|
task := strings.TrimSpace(rest[idx+1:])
|
||||||
|
// 保存前一个
|
||||||
|
flush()
|
||||||
|
currentName = name
|
||||||
|
if task != "" {
|
||||||
|
currentTask.WriteString(task)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ASSIGN:成员名:任务描述(向后兼容)
|
||||||
|
if strings.HasPrefix(trimmed, "ASSIGN:") {
|
||||||
|
parts := strings.SplitN(strings.TrimPrefix(trimmed, "ASSIGN:"), ":", 2)
|
||||||
|
if len(parts) == 2 {
|
||||||
|
flush()
|
||||||
|
result[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 当前有 @成员名 在收集中,追加后续行作为任务描述
|
||||||
|
if currentName != "" && trimmed != "" {
|
||||||
|
if currentTask.Len() > 0 {
|
||||||
|
currentTask.WriteString("\n")
|
||||||
|
}
|
||||||
|
currentTask.WriteString(trimmed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flush()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,6 +674,22 @@ func (r *Room) buildTeamXML() string {
|
|||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// appendPlanLog 将沟通记录追加到任务计划文档
|
||||||
|
func (r *Room) appendPlanLog(from, to, content string) {
|
||||||
|
if r.planFilename == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
path := filepath.Join(r.Dir, "workspace", r.planFilename)
|
||||||
|
f, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
entry := fmt.Sprintf("\n---\n**[%s] %s → %s**\n\n%s\n",
|
||||||
|
time.Now().Format("15:04:05"), from, to, content)
|
||||||
|
f.WriteString(entry)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Room) saveWorkspace(filename, content string) {
|
func (r *Room) saveWorkspace(filename, content string) {
|
||||||
dir := filepath.Join(r.Dir, "workspace")
|
dir := filepath.Join(r.Dir, "workspace")
|
||||||
os.MkdirAll(dir, 0755)
|
os.MkdirAll(dir, 0755)
|
||||||
@ -411,18 +697,14 @@ func (r *Room) saveWorkspace(filename, content string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Room) updateTasks(msgs []llm.Message) {
|
func (r *Room) updateTasks(msgs []llm.Message) {
|
||||||
// Extract task list from conversation and save
|
// 从对话中提取任务列表并保存
|
||||||
var tasks strings.Builder
|
var tasks strings.Builder
|
||||||
tasks.WriteString("# Tasks\n\n")
|
tasks.WriteString("# Tasks\n\n")
|
||||||
for _, m := range msgs {
|
for _, m := range msgs {
|
||||||
if m.Role == "assistant" && strings.Contains(m.Content, "ASSIGN:") {
|
if m.Role == "assistant" {
|
||||||
for _, line := range strings.Split(m.Content, "\n") {
|
assignments := parseAssignments(m.Content)
|
||||||
if strings.HasPrefix(line, "ASSIGN:") {
|
for name, task := range assignments {
|
||||||
parts := strings.SplitN(strings.TrimPrefix(line, "ASSIGN:"), ":", 2)
|
tasks.WriteString(fmt.Sprintf("- [ ] [%s] %s\n", name, task))
|
||||||
if len(parts) == 2 {
|
|
||||||
tasks.WriteString(fmt.Sprintf("- [ ] [%s] %s\n", strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,29 +21,25 @@ type Skill struct {
|
|||||||
Body string // full SKILL.md body (instructions)
|
Body string // full SKILL.md body (instructions)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discover scans skillsDir and returns metadata for all valid skills.
|
// Discover 递归扫描 skillsDir,返回所有包含 SKILL.md 的目录的元数据。
|
||||||
func Discover(skillsDir string) ([]Meta, error) {
|
func Discover(skillsDir string) ([]Meta, error) {
|
||||||
entries, err := os.ReadDir(skillsDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var metas []Meta
|
var metas []Meta
|
||||||
for _, e := range entries {
|
filepath.Walk(skillsDir, func(path string, info os.FileInfo, err error) error {
|
||||||
if !e.IsDir() {
|
if err != nil || info.IsDir() || info.Name() != "SKILL.md" {
|
||||||
continue
|
return nil
|
||||||
}
|
}
|
||||||
path := filepath.Join(skillsDir, e.Name(), "SKILL.md")
|
|
||||||
data, err := os.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
return nil
|
||||||
}
|
}
|
||||||
meta, err := parseMeta(data)
|
meta, err := parseMeta(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
return nil
|
||||||
}
|
}
|
||||||
meta.Path = filepath.Join(skillsDir, e.Name())
|
meta.Path = filepath.Dir(path)
|
||||||
metas = append(metas, meta)
|
metas = append(metas, meta)
|
||||||
}
|
return nil
|
||||||
|
})
|
||||||
return metas, nil
|
return metas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,12 +59,20 @@ func Load(skillDir string) (*Skill, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ToXML generates <available_skills> XML for agent system prompts.
|
// ToXML generates <available_skills> XML for agent system prompts.
|
||||||
|
// 如果 skill 有完整内容,会注入到 prompt 中。
|
||||||
func ToXML(metas []Meta) string {
|
func ToXML(metas []Meta) string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
sb.WriteString("<available_skills>\n")
|
sb.WriteString("<available_skills>\n")
|
||||||
for _, m := range metas {
|
for _, m := range metas {
|
||||||
fmt.Fprintf(&sb, " <skill>\n <name>%s</name>\n <description>%s</description>\n <location>%s/SKILL.md</location>\n </skill>\n",
|
// 尝试加载完整 skill 内容
|
||||||
m.Name, m.Description, m.Path)
|
s, err := Load(m.Path)
|
||||||
|
if err == nil && s.Body != "" {
|
||||||
|
fmt.Fprintf(&sb, " <skill name=\"%s\">\n <description>%s</description>\n <instructions>\n%s\n </instructions>\n </skill>\n",
|
||||||
|
m.Name, m.Description, s.Body)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(&sb, " <skill name=\"%s\">\n <description>%s</description>\n </skill>\n",
|
||||||
|
m.Name, m.Description)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sb.WriteString("</available_skills>")
|
sb.WriteString("</available_skills>")
|
||||||
return sb.String()
|
return sb.String()
|
||||||
|
|||||||
82
skills/需求确认/SKILL.md
Normal file
82
skills/需求确认/SKILL.md
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
---
|
||||||
|
name: 需求确认
|
||||||
|
description: "在分配任务给团队成员之前,必须先使用此技能与用户确认需求。通过逐步提问收集关键信息,形成明确的任务方案后,才能开始分配工作。适用于所有需要团队协作的场景。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 需求确认技能
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
在将任何任务分配给团队成员之前,必须先通过此流程与用户充分沟通,了解需求背景和具体要求。只有形成明确的工作方案并获得用户确认后,才能开始分配任务。
|
||||||
|
|
||||||
|
<HARD-GATE>
|
||||||
|
在用户确认工作方案之前,绝对不能 @任何成员 分配任务。
|
||||||
|
无论需求看起来多简单,都必须先确认。
|
||||||
|
</HARD-GATE>
|
||||||
|
|
||||||
|
## 流程
|
||||||
|
|
||||||
|
### 第一步:判断意图
|
||||||
|
|
||||||
|
收到用户消息后,先判断类型:
|
||||||
|
- **闲聊/问候**(如"你好""在吗")→ 直接友好回复,不启动此流程
|
||||||
|
- **明确需求**(如"帮我写一份合同""审查这份协议")→ 进入第二步
|
||||||
|
- **模糊需求**(如"我有个法律问题""帮我看看这个")→ 先问用户具体是什么
|
||||||
|
|
||||||
|
### 第二步:逐步收集信息
|
||||||
|
|
||||||
|
每次只问一个问题,不要一次性抛出多个问题。优先使用选择题。
|
||||||
|
|
||||||
|
收集要素因场景而异,以下是常见场景的示例:
|
||||||
|
|
||||||
|
**起草合同/协议类:**
|
||||||
|
1. 合作方是谁?(个人/公司/工作室)
|
||||||
|
2. 合作内容是什么?(具体做什么事)
|
||||||
|
3. 你最关心的条款?(如知识产权、分成、保密等)
|
||||||
|
4. 有没有预算或金额范围?
|
||||||
|
5. 合作期限?
|
||||||
|
|
||||||
|
**法律咨询类:**
|
||||||
|
1. 涉及什么领域?(劳动、合同、公司治理等)
|
||||||
|
2. 当前面临的具体问题?
|
||||||
|
3. 已经采取了什么措施?
|
||||||
|
4. 期望达到什么结果?
|
||||||
|
|
||||||
|
**审查文件类:**
|
||||||
|
1. 需要审查什么类型的文件?
|
||||||
|
2. 重点关注哪些方面?
|
||||||
|
3. 文件的背景和用途?
|
||||||
|
|
||||||
|
### 第三步:总结确认
|
||||||
|
|
||||||
|
信息收集完后,向用户呈现工作方案:
|
||||||
|
|
||||||
|
```
|
||||||
|
根据你的需求,我的工作计划如下:
|
||||||
|
|
||||||
|
1. @合同律师 负责 [具体任务]
|
||||||
|
2. @合规专员 负责 [具体任务]
|
||||||
|
|
||||||
|
预计产出:[描述最终交付物]
|
||||||
|
|
||||||
|
确认后我将开始安排工作,你看可以吗?
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:此步骤中的 @ 只是向用户展示计划,不要作为独立行输出,以免触发任务执行。
|
||||||
|
|
||||||
|
### 第四步:执行
|
||||||
|
|
||||||
|
用户确认后,再正式分配任务:
|
||||||
|
|
||||||
|
```
|
||||||
|
@合同律师 [详细的任务描述,包含用户提供的所有背景信息]
|
||||||
|
@合规专员 [详细的任务描述]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 关键原则
|
||||||
|
|
||||||
|
- **每次只问一个问题** — 不要让用户面对一堆问题
|
||||||
|
- **优先选择题** — 比开放式问题更容易回答
|
||||||
|
- **不要假设** — 不确定的信息一定要问
|
||||||
|
- **方案要具体** — 让用户清楚知道接下来会发生什么
|
||||||
|
- **尊重用户时间** — 如果信息已经足够,不要过度追问
|
||||||
@ -10,5 +10,6 @@ agents:
|
|||||||
skills:
|
skills:
|
||||||
- 合同审查
|
- 合同审查
|
||||||
- 法律知识库
|
- 法律知识库
|
||||||
|
- 需求确认
|
||||||
|
|
||||||
installed_at: 2026-03-05
|
installed_at: 2026-03-06
|
||||||
|
|||||||
@ -22,7 +22,22 @@ export default function App() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchUser()
|
fetchUser()
|
||||||
fetchRooms()
|
fetchRooms().then(() => {
|
||||||
|
const savedRoomId = useStore.getState().activeRoomId
|
||||||
|
if (savedRoomId) {
|
||||||
|
useStore.getState().connectRoom(savedRoomId)
|
||||||
|
// 加载历史消息、任务、工作区
|
||||||
|
fetch(`${API}/rooms/${savedRoomId}/messages`).then(r => r.json()).then(msgs => {
|
||||||
|
if (msgs?.length) useStore.setState(s => ({ messages: { ...s.messages, [savedRoomId]: msgs } }))
|
||||||
|
}).catch(() => {})
|
||||||
|
fetch(`${API}/rooms/${savedRoomId}/tasks`).then(r => r.json()).then(d => {
|
||||||
|
useStore.setState(s => ({ tasks: { ...s.tasks, [savedRoomId]: d.content } }))
|
||||||
|
}).catch(() => {})
|
||||||
|
fetch(`${API}/rooms/${savedRoomId}/workspace`).then(r => r.json()).then(files => {
|
||||||
|
useStore.setState(s => ({ workspace: { ...s.workspace, [savedRoomId]: files || [] } }))
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
})
|
||||||
}, [fetchUser, fetchRooms])
|
}, [fetchUser, fetchRooms])
|
||||||
|
|
||||||
const handleOnboardingComplete = () => {
|
const handleOnboardingComplete = () => {
|
||||||
@ -36,7 +51,7 @@ export default function App() {
|
|||||||
const color = randomColor()
|
const color = randomColor()
|
||||||
await fetch(`${API}/rooms`, {
|
await fetch(`${API}/rooms`, {
|
||||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ name: newRoomName.trim(), type: 'dept', color, master: '', members: [] })
|
body: JSON.stringify({ name: newRoomName.trim(), type: 'project', color, master: '', members: [] })
|
||||||
})
|
})
|
||||||
setCreating(false)
|
setCreating(false)
|
||||||
setNewRoomName('')
|
setNewRoomName('')
|
||||||
@ -85,7 +100,7 @@ export default function App() {
|
|||||||
{/* Add room button */}
|
{/* Add room button */}
|
||||||
<button
|
<button
|
||||||
onClick={() => setCreating(true)}
|
onClick={() => setCreating(true)}
|
||||||
title="创建群聊"
|
title="创建项目"
|
||||||
className="w-full h-10 rounded-lg flex items-center justify-center text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)] transition-all duration-200"
|
className="w-full h-10 rounded-lg flex items-center justify-center text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)] transition-all duration-200"
|
||||||
>
|
>
|
||||||
<Plus className="w-5 h-5" />
|
<Plus className="w-5 h-5" />
|
||||||
@ -129,11 +144,11 @@ export default function App() {
|
|||||||
{creating && (
|
{creating && (
|
||||||
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50" onClick={() => setCreating(false)}>
|
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50" onClick={() => setCreating(false)}>
|
||||||
<div className="bg-[var(--bg-secondary)] rounded-lg p-4 w-80 shadow-xl" onClick={e => e.stopPropagation()}>
|
<div className="bg-[var(--bg-secondary)] rounded-lg p-4 w-80 shadow-xl" onClick={e => e.stopPropagation()}>
|
||||||
<h3 className="font-semibold mb-3">创建新群聊</h3>
|
<h3 className="font-semibold mb-3">创建新项目</h3>
|
||||||
<input
|
<input
|
||||||
autoFocus
|
autoFocus
|
||||||
className="w-full bg-[var(--bg-tertiary)] border border-[var(--border)] rounded-lg px-3 py-2 text-sm outline-none focus:border-[var(--accent)] mb-3"
|
className="w-full bg-[var(--bg-tertiary)] border border-[var(--border)] rounded-lg px-3 py-2 text-sm outline-none focus:border-[var(--accent)] mb-3"
|
||||||
placeholder="群聊名称"
|
placeholder="项目名称"
|
||||||
value={newRoomName}
|
value={newRoomName}
|
||||||
onChange={e => setNewRoomName(e.target.value)}
|
onChange={e => setNewRoomName(e.target.value)}
|
||||||
onKeyDown={e => e.key === 'Enter' && createRoom()}
|
onKeyDown={e => e.key === 'Enter' && createRoom()}
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState, useCallback } from 'react'
|
||||||
import ReactMarkdown from 'react-markdown'
|
import ReactMarkdown from 'react-markdown'
|
||||||
import remarkGfm from 'remark-gfm'
|
import remarkGfm from 'remark-gfm'
|
||||||
import {
|
import {
|
||||||
Hash,
|
Hash,
|
||||||
Crown,
|
Crown,
|
||||||
Plus,
|
|
||||||
Smile,
|
|
||||||
Send,
|
Send,
|
||||||
|
AtSign,
|
||||||
|
Copy,
|
||||||
|
Check,
|
||||||
|
FileText,
|
||||||
|
ExternalLink,
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import { useStore } from '../store'
|
import { useStore } from '../store'
|
||||||
import type { Message } from '../types'
|
import type { Message } from '../types'
|
||||||
@ -14,10 +17,69 @@ import type { Message } from '../types'
|
|||||||
export function ChatView() {
|
export function ChatView() {
|
||||||
const { activeRoomId, rooms, messages, sendMessage, user } = useStore()
|
const { activeRoomId, rooms, messages, sendMessage, user } = useStore()
|
||||||
const [input, setInput] = useState('')
|
const [input, setInput] = useState('')
|
||||||
|
const [mentionQuery, setMentionQuery] = useState<string | null>(null)
|
||||||
|
const [mentionIndex, setMentionIndex] = useState(0)
|
||||||
|
const inputRef = useRef<HTMLTextAreaElement>(null)
|
||||||
const bottomRef = useRef<HTMLDivElement>(null)
|
const bottomRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
const room = rooms.find(r => r.id === activeRoomId)
|
const room = rooms.find(r => r.id === activeRoomId)
|
||||||
const msgs: Message[] = activeRoomId ? (messages[activeRoomId] || []) : []
|
const msgs: Message[] = activeRoomId ? (messages[activeRoomId] || []) : []
|
||||||
|
const mode = room?.mode || 'plan'
|
||||||
|
|
||||||
|
// Build agent list for @ mentions
|
||||||
|
const mentionCandidates = room
|
||||||
|
? [room.master, ...(room.members || [])].filter(Boolean)
|
||||||
|
: []
|
||||||
|
|
||||||
|
const filteredMentions = mentionQuery !== null
|
||||||
|
? mentionCandidates.filter(name =>
|
||||||
|
name.toLowerCase().includes(mentionQuery.toLowerCase())
|
||||||
|
)
|
||||||
|
: []
|
||||||
|
|
||||||
|
// Detect @ trigger from input
|
||||||
|
const updateMention = useCallback((value: string, cursorPos: number) => {
|
||||||
|
const before = value.slice(0, cursorPos)
|
||||||
|
const atMatch = before.match(/@(\w*)$/)
|
||||||
|
if (atMatch) {
|
||||||
|
setMentionQuery(atMatch[1])
|
||||||
|
setMentionIndex(0)
|
||||||
|
} else {
|
||||||
|
setMentionQuery(null)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const insertMention = useCallback((name: string) => {
|
||||||
|
const el = inputRef.current
|
||||||
|
if (!el) return
|
||||||
|
const cursorPos = el.selectionStart || input.length
|
||||||
|
const before = input.slice(0, cursorPos)
|
||||||
|
const after = input.slice(cursorPos)
|
||||||
|
const atIdx = before.lastIndexOf('@')
|
||||||
|
const newInput = before.slice(0, atIdx) + `@${name} ` + after
|
||||||
|
setInput(newInput)
|
||||||
|
setMentionQuery(null)
|
||||||
|
setTimeout(() => {
|
||||||
|
const pos = atIdx + name.length + 2
|
||||||
|
el.setSelectionRange(pos, pos)
|
||||||
|
el.focus()
|
||||||
|
})
|
||||||
|
}, [input])
|
||||||
|
|
||||||
|
const toggleMode = useCallback(() => {
|
||||||
|
if (!activeRoomId) return
|
||||||
|
const newMode = mode === 'build' ? 'plan' : 'build'
|
||||||
|
// 发送到后端
|
||||||
|
const { ws } = useStore.getState()
|
||||||
|
const socket = ws[activeRoomId]
|
||||||
|
if (socket) {
|
||||||
|
socket.send(JSON.stringify({ type: 'set_mode', mode: newMode }))
|
||||||
|
}
|
||||||
|
// 本地立即更新
|
||||||
|
useStore.setState(s => ({
|
||||||
|
rooms: s.rooms.map(r => r.id === activeRoomId ? { ...r, mode: newMode } : r)
|
||||||
|
}))
|
||||||
|
}, [activeRoomId, mode])
|
||||||
|
|
||||||
useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: 'smooth' }) }, [msgs, room?.status])
|
useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: 'smooth' }) }, [msgs, room?.status])
|
||||||
|
|
||||||
@ -25,12 +87,12 @@ export function ChatView() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex-1 flex flex-col items-center justify-center bg-[var(--bg-primary)] text-[var(--text-muted)]">
|
<div className="flex-1 flex flex-col items-center justify-center bg-[var(--bg-primary)] text-[var(--text-muted)]">
|
||||||
<Hash className="w-16 h-16 mb-4 opacity-20" />
|
<Hash className="w-16 h-16 mb-4 opacity-20" />
|
||||||
<p className="text-lg">选择一个群开始聊天</p>
|
<p className="text-lg">选择一个项目开始聊天</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusLabel = room.status === 'working' && room.activeAgent
|
const statusLabel = room.status === 'working' && room.activeAgent
|
||||||
? `${room.activeAgent} ${room.action || ''}`
|
? `${room.activeAgent} ${room.action || ''}`
|
||||||
: room.status === 'thinking' ? '思考中...' : room.status === 'working' ? '工作中...' : '空闲'
|
: room.status === 'thinking' ? '思考中...' : room.status === 'working' ? '工作中...' : '空闲'
|
||||||
|
|
||||||
@ -38,13 +100,13 @@ const statusLabel = room.status === 'working' && room.activeAgent
|
|||||||
<div className="flex flex-1 overflow-hidden bg-[var(--bg-primary)]">
|
<div className="flex flex-1 overflow-hidden bg-[var(--bg-primary)]">
|
||||||
{/* Main chat area */}
|
{/* Main chat area */}
|
||||||
<div className="flex flex-col flex-1 min-w-0">
|
<div className="flex flex-col flex-1 min-w-0">
|
||||||
{/* Header - Discord style */}
|
{/* Header */}
|
||||||
<div className="h-12 px-4 border-b border-[var(--border)] flex items-center gap-2 bg-[var(--bg-secondary)] shadow-sm">
|
<div className="h-12 px-4 border-b border-[var(--border)] flex items-center gap-2 bg-[var(--bg-secondary)] shadow-sm">
|
||||||
<Hash className="w-5 h-5 text-[var(--text-muted)]" />
|
<Hash className="w-5 h-5 text-[var(--text-muted)]" />
|
||||||
<span className="font-semibold text-base">{room.name}</span>
|
<span className="font-semibold text-base">{room.name}</span>
|
||||||
<div className="h-6 w-[1px] bg-[var(--border)] mx-2" />
|
<div className="h-6 w-[1px] bg-[var(--border)] mx-2" />
|
||||||
<span className="text-sm text-[var(--text-muted)] truncate">
|
<span className="text-sm text-[var(--text-muted)] truncate">
|
||||||
{room.type === 'dept' ? '部门群' : 'Leader 群'}
|
{room.team || '项目群'}
|
||||||
</span>
|
</span>
|
||||||
<div className="flex-1" />
|
<div className="flex-1" />
|
||||||
{/* Status badge */}
|
{/* Status badge */}
|
||||||
@ -73,7 +135,13 @@ const statusLabel = room.status === 'working' && room.activeAgent
|
|||||||
<p className="text-xs mt-1">发送消息开始对话</p>
|
<p className="text-xs mt-1">发送消息开始对话</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{msgs.map(msg => <MessageBubble key={msg.id} msg={msg} userName={user?.name} />)}
|
{msgs.map(msg =>
|
||||||
|
msg.role === 'artifact' ? (
|
||||||
|
<ArtifactCard key={msg.id} msg={msg} roomId={activeRoomId!} />
|
||||||
|
) : (
|
||||||
|
<MessageBubble key={msg.id} msg={msg} userName={user?.name} />
|
||||||
|
)
|
||||||
|
)}
|
||||||
{/* Thinking indicator */}
|
{/* Thinking indicator */}
|
||||||
{room.status === 'thinking' && msgs[msgs.length - 1]?.role === 'user' && (
|
{room.status === 'thinking' && msgs[msgs.length - 1]?.role === 'user' && (
|
||||||
<ThinkingBubble agent={room.master} />
|
<ThinkingBubble agent={room.master} />
|
||||||
@ -81,39 +149,163 @@ const statusLabel = room.status === 'working' && room.activeAgent
|
|||||||
<div ref={bottomRef} />
|
<div ref={bottomRef} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Input area - Discord style */}
|
{/* Input area */}
|
||||||
<div className="px-4 pb-4">
|
<div className="px-4 pb-4">
|
||||||
<div className="bg-[var(--bg-tertiary)] rounded-lg flex items-center gap-2 px-4 py-2">
|
<div className="relative">
|
||||||
<button className="text-[var(--text-muted)] hover:text-[var(--text-primary)] transition-colors">
|
{/* @ Mention dropdown */}
|
||||||
<Plus className="w-5 h-5" />
|
{mentionQuery !== null && filteredMentions.length > 0 && (
|
||||||
</button>
|
<div className="absolute bottom-full left-0 right-0 mb-1 bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg shadow-lg overflow-hidden z-10">
|
||||||
<input
|
<div className="px-3 py-1.5 text-[10px] font-semibold text-[var(--text-muted)] uppercase">
|
||||||
className="flex-1 bg-transparent outline-none text-sm placeholder:text-[var(--text-muted)]"
|
成员
|
||||||
placeholder={`#${room.name} 中发送消息...`}
|
</div>
|
||||||
value={input}
|
{filteredMentions.map((name, i) => {
|
||||||
onChange={e => setInput(e.target.value)}
|
const isMaster = name === room.master
|
||||||
onKeyDown={e => {
|
return (
|
||||||
if (e.key === 'Enter' && !e.shiftKey && input.trim()) {
|
<button
|
||||||
sendMessage(room.id, input.trim())
|
key={name}
|
||||||
setInput('')
|
className={`w-full flex items-center gap-2 px-3 py-1.5 text-sm transition-colors ${
|
||||||
}
|
i === mentionIndex
|
||||||
}}
|
? 'bg-[var(--accent)] text-white'
|
||||||
/>
|
: 'hover:bg-[var(--bg-hover)]'
|
||||||
<button className="text-[var(--text-muted)] hover:text-[var(--text-primary)] transition-colors">
|
}`}
|
||||||
<Smile className="w-5 h-5" />
|
onMouseEnter={() => setMentionIndex(i)}
|
||||||
</button>
|
onMouseDown={e => {
|
||||||
<button
|
e.preventDefault()
|
||||||
className="text-[var(--accent)] hover:text-[var(--accent-hover)] transition-colors disabled:opacity-50"
|
insertMention(name)
|
||||||
disabled={!input.trim()}
|
}}
|
||||||
onClick={() => {
|
>
|
||||||
if (input.trim()) {
|
<div className={`w-6 h-6 rounded-full flex items-center justify-center text-white text-xs font-semibold flex-shrink-0 ${
|
||||||
sendMessage(room.id, input.trim())
|
isMaster ? 'bg-[var(--color-warning)]' : 'bg-[var(--accent)]'
|
||||||
setInput('')
|
}`}>
|
||||||
}
|
{isMaster ? <Crown className="w-3 h-3" /> : name[0]?.toUpperCase()}
|
||||||
}}
|
</div>
|
||||||
>
|
<span className="truncate">{name}</span>
|
||||||
<Send className="w-5 h-5" />
|
{isMaster && (
|
||||||
</button>
|
<span className={`text-[10px] px-1 py-0.5 rounded font-medium ml-auto ${
|
||||||
|
i === mentionIndex
|
||||||
|
? 'bg-white/20 text-white'
|
||||||
|
: 'bg-[var(--color-warning)]/20 text-[var(--color-warning)]'
|
||||||
|
}`}>
|
||||||
|
MASTER
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Input bar with mode toggle */}
|
||||||
|
<div className="bg-[var(--bg-tertiary)] rounded-lg flex items-center gap-0 px-2 py-1.5">
|
||||||
|
{/* Mode toggle - OpenCode style */}
|
||||||
|
<button
|
||||||
|
onClick={toggleMode}
|
||||||
|
className={`
|
||||||
|
flex items-center gap-1 px-3 py-1.5 rounded-md text-xs font-bold tracking-wider transition-all duration-200 select-none flex-shrink-0
|
||||||
|
${mode === 'build'
|
||||||
|
? 'bg-orange-500 text-white shadow-sm shadow-orange-500/30'
|
||||||
|
: 'bg-blue-500/15 text-blue-400 hover:bg-blue-500/25'
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
title="Tab 切换模式"
|
||||||
|
>
|
||||||
|
{mode === 'build' ? 'BUILD' : 'PLAN'}
|
||||||
|
</button>
|
||||||
|
<div className="h-5 w-[1px] bg-[var(--border)] mx-2 flex-shrink-0" />
|
||||||
|
<textarea
|
||||||
|
ref={inputRef}
|
||||||
|
rows={1}
|
||||||
|
className="flex-1 bg-transparent outline-none text-sm placeholder:text-[var(--text-muted)] min-w-0 resize-none max-h-[120px] leading-5 py-0.5"
|
||||||
|
placeholder={mode === 'build' ? '发送确认开始执行任务...' : `#${room.name} 中发送消息...`}
|
||||||
|
value={input}
|
||||||
|
onChange={e => {
|
||||||
|
setInput(e.target.value)
|
||||||
|
// 自动调整高度
|
||||||
|
const el = e.target
|
||||||
|
el.style.height = 'auto'
|
||||||
|
el.style.height = Math.min(el.scrollHeight, 120) + 'px'
|
||||||
|
updateMention(e.target.value, e.target.selectionStart || 0)
|
||||||
|
}}
|
||||||
|
onKeyDown={e => {
|
||||||
|
// 输入法正在组合(选字)时,不拦截任何按键
|
||||||
|
if (e.nativeEvent.isComposing) return
|
||||||
|
|
||||||
|
// 提及下拉菜单键盘控制
|
||||||
|
if (mentionQuery !== null && filteredMentions.length > 0) {
|
||||||
|
if (e.key === 'ArrowUp') {
|
||||||
|
e.preventDefault()
|
||||||
|
setMentionIndex(i => (i - 1 + filteredMentions.length) % filteredMentions.length)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (e.key === 'ArrowDown') {
|
||||||
|
e.preventDefault()
|
||||||
|
setMentionIndex(i => (i + 1) % filteredMentions.length)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (e.key === 'Enter' || e.key === 'Tab') {
|
||||||
|
e.preventDefault()
|
||||||
|
insertMention(filteredMentions[mentionIndex])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
e.preventDefault()
|
||||||
|
setMentionQuery(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Tab 切换模式(无提及下拉时)
|
||||||
|
if (e.key === 'Tab') {
|
||||||
|
e.preventDefault()
|
||||||
|
toggleMode()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Shift+Enter 换行(默认行为,不拦截)
|
||||||
|
// Enter 发送消息
|
||||||
|
if (e.key === 'Enter' && !e.shiftKey) {
|
||||||
|
e.preventDefault()
|
||||||
|
if (input.trim()) {
|
||||||
|
sendMessage(room.id, input.trim())
|
||||||
|
setInput('')
|
||||||
|
setMentionQuery(null)
|
||||||
|
// 重置高度
|
||||||
|
const el = inputRef.current
|
||||||
|
if (el) el.style.height = 'auto'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className="text-[var(--text-muted)] hover:text-[var(--text-primary)] transition-colors p-1.5"
|
||||||
|
title="@ 提及"
|
||||||
|
onClick={() => {
|
||||||
|
setInput(prev => prev + '@')
|
||||||
|
setMentionQuery('')
|
||||||
|
setMentionIndex(0)
|
||||||
|
inputRef.current?.focus()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AtSign className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="text-[var(--accent)] hover:text-[var(--accent-hover)] transition-colors disabled:opacity-50 p-1.5"
|
||||||
|
disabled={!input.trim()}
|
||||||
|
onClick={() => {
|
||||||
|
if (input.trim()) {
|
||||||
|
sendMessage(room.id, input.trim())
|
||||||
|
setInput('')
|
||||||
|
setMentionQuery(null)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Send className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/* Tab hint */}
|
||||||
|
<div className="flex items-center gap-2 mt-1 px-1">
|
||||||
|
<span className="text-[10px] text-[var(--text-muted)] opacity-60">
|
||||||
|
<kbd className="px-1 py-0.5 bg-[var(--bg-tertiary)] rounded text-[9px] border border-[var(--border)]">Tab</kbd> 切换模式
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -121,10 +313,92 @@ const statusLabel = room.status === 'working' && room.activeAgent
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function highlightMentions(content: string): string {
|
||||||
|
if (!content) return ''
|
||||||
|
return content.replace(/@(\w+)/g, '**@$1**')
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Artifact Card ---- */
|
||||||
|
function ArtifactCard({ msg, roomId }: { msg: Message; roomId: string }) {
|
||||||
|
const [copied, setCopied] = useState(false)
|
||||||
|
|
||||||
|
const avatarColors = [
|
||||||
|
'bg-red-500', 'bg-orange-500', 'bg-amber-500', 'bg-yellow-500',
|
||||||
|
'bg-lime-500', 'bg-green-500', 'bg-emerald-500', 'bg-teal-500',
|
||||||
|
'bg-cyan-500', 'bg-sky-500', 'bg-blue-500', 'bg-indigo-500',
|
||||||
|
'bg-violet-500', 'bg-purple-500', 'bg-fuchsia-500', 'bg-pink-500', 'bg-rose-500'
|
||||||
|
]
|
||||||
|
const agentName = msg.agent || 'Unknown'
|
||||||
|
const colorIndex = agentName.charCodeAt(0) % avatarColors.length
|
||||||
|
|
||||||
|
const openArtifact = () => {
|
||||||
|
if (!msg.filename) return
|
||||||
|
const event = new CustomEvent('open-artifact', { detail: { roomId, filename: msg.filename } })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
const copyFilename = () => {
|
||||||
|
navigator.clipboard.writeText(msg.filename || '')
|
||||||
|
setCopied(true)
|
||||||
|
setTimeout(() => setCopied(false), 2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex gap-3">
|
||||||
|
<div className={`w-10 h-10 rounded-full flex items-center justify-center text-white font-semibold flex-shrink-0 mt-0.5 ${avatarColors[colorIndex]}`}>
|
||||||
|
{agentName[0]?.toUpperCase()}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-start max-w-[70%]">
|
||||||
|
<div className="flex items-center gap-2 mb-0.5">
|
||||||
|
<span className="font-medium text-sm text-[var(--text-primary)]">{agentName}</span>
|
||||||
|
<span className="text-[10px] px-1.5 py-0.5 bg-emerald-500/20 text-emerald-400 rounded font-medium">
|
||||||
|
已完成
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={openArtifact}
|
||||||
|
className="w-full bg-[var(--bg-tertiary)] border border-[var(--border)] rounded-lg p-3 hover:border-[var(--accent)] transition-colors text-left group"
|
||||||
|
>
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className="w-10 h-10 rounded-lg bg-[var(--accent)]/10 flex items-center justify-center flex-shrink-0">
|
||||||
|
<FileText className="w-5 h-5 text-[var(--accent)]" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="font-medium text-sm text-[var(--text-primary)] truncate">
|
||||||
|
{msg.title || msg.filename}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-[var(--text-muted)] mt-0.5">{msg.filename}</div>
|
||||||
|
</div>
|
||||||
|
<ExternalLink className="w-4 h-4 text-[var(--text-muted)] opacity-0 group-hover:opacity-100 transition-opacity flex-shrink-0 mt-1" />
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<div className="flex items-center gap-1 mt-0.5">
|
||||||
|
<button
|
||||||
|
onClick={openArtifact}
|
||||||
|
className="flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] text-[var(--text-muted)] hover:text-[var(--accent)] hover:bg-[var(--bg-hover)] transition-colors"
|
||||||
|
>
|
||||||
|
<FileText className="w-3 h-3" />
|
||||||
|
查看文档
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={copyFilename}
|
||||||
|
className="flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)] transition-colors"
|
||||||
|
>
|
||||||
|
{copied ? <Check className="w-3 h-3" /> : <Copy className="w-3 h-3" />}
|
||||||
|
{copied ? '已复制' : '复制'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Message Bubble ---- */
|
||||||
function MessageBubble({ msg, userName }: { msg: Message; userName?: string }) {
|
function MessageBubble({ msg, userName }: { msg: Message; userName?: string }) {
|
||||||
const isUser = msg.role === 'user'
|
const isUser = msg.role === 'user'
|
||||||
const isMaster = msg.role === 'master'
|
const isMaster = msg.role === 'master'
|
||||||
const agentName = msg.agent || 'Unknown'
|
const agentName = msg.agent || 'Unknown'
|
||||||
|
const [copied, setCopied] = useState(false)
|
||||||
|
|
||||||
const avatarColors = [
|
const avatarColors = [
|
||||||
'bg-red-500', 'bg-orange-500', 'bg-amber-500', 'bg-yellow-500',
|
'bg-red-500', 'bg-orange-500', 'bg-amber-500', 'bg-yellow-500',
|
||||||
@ -135,16 +409,18 @@ function MessageBubble({ msg, userName }: { msg: Message; userName?: string }) {
|
|||||||
const colorIndex = agentName.charCodeAt(0) % avatarColors.length
|
const colorIndex = agentName.charCodeAt(0) % avatarColors.length
|
||||||
const displayName = isUser ? (userName || agentName) : agentName
|
const displayName = isUser ? (userName || agentName) : agentName
|
||||||
|
|
||||||
|
const copyText = (text: string) => {
|
||||||
|
navigator.clipboard.writeText(text)
|
||||||
|
setCopied(true)
|
||||||
|
setTimeout(() => setCopied(false), 2000)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`flex gap-3 group ${isUser ? 'flex-row-reverse' : ''}`}>
|
<div className={`flex gap-3 group ${isUser ? 'flex-row-reverse' : ''}`}>
|
||||||
{/* Avatar */}
|
|
||||||
<div className={`w-10 h-10 rounded-full flex items-center justify-center text-white font-semibold flex-shrink-0 mt-0.5 ${isUser ? 'bg-[var(--accent)]' : avatarColors[colorIndex]}`}>
|
<div className={`w-10 h-10 rounded-full flex items-center justify-center text-white font-semibold flex-shrink-0 mt-0.5 ${isUser ? 'bg-[var(--accent)]' : avatarColors[colorIndex]}`}>
|
||||||
{isMaster ? <Crown className="w-5 h-5" /> : displayName[0]?.toUpperCase()}
|
{isMaster ? <Crown className="w-5 h-5" /> : displayName[0]?.toUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
|
||||||
<div className={`flex flex-col max-w-[70%] ${isUser ? 'items-end' : 'items-start'}`}>
|
<div className={`flex flex-col max-w-[70%] ${isUser ? 'items-end' : 'items-start'}`}>
|
||||||
{/* Name */}
|
|
||||||
<div className={`flex items-center gap-2 mb-0.5 ${isUser ? 'flex-row-reverse' : ''}`}>
|
<div className={`flex items-center gap-2 mb-0.5 ${isUser ? 'flex-row-reverse' : ''}`}>
|
||||||
<span className={`font-medium text-sm ${isMaster ? 'text-[var(--color-warning)]' : isUser ? 'text-[var(--accent)]' : 'text-[var(--text-primary)]'}`}>
|
<span className={`font-medium text-sm ${isMaster ? 'text-[var(--color-warning)]' : isUser ? 'text-[var(--accent)]' : 'text-[var(--text-primary)]'}`}>
|
||||||
{displayName}
|
{displayName}
|
||||||
@ -155,8 +431,6 @@ function MessageBubble({ msg, userName }: { msg: Message; userName?: string }) {
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Message bubble */}
|
|
||||||
<div
|
<div
|
||||||
className={`
|
className={`
|
||||||
px-3.5 py-2 rounded-lg text-sm relative
|
px-3.5 py-2 rounded-lg text-sm relative
|
||||||
@ -169,12 +443,26 @@ function MessageBubble({ msg, userName }: { msg: Message; userName?: string }) {
|
|||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<div className="prose prose-sm dark:prose-invert max-w-none">
|
<div className="prose prose-sm dark:prose-invert max-w-none">
|
||||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>{msg.content}</ReactMarkdown>
|
<ReactMarkdown remarkPlugins={[remarkGfm]}>
|
||||||
|
{highlightMentions(msg.content)}
|
||||||
|
</ReactMarkdown>
|
||||||
</div>
|
</div>
|
||||||
{msg.streaming && (
|
{msg.streaming && (
|
||||||
<span className="inline-block w-1.5 h-3 bg-[var(--text-muted)] animate-pulse ml-0.5" />
|
<span className="inline-block w-1.5 h-3 bg-[var(--text-muted)] animate-pulse ml-0.5" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{!isUser && !msg.streaming && msg.content && (
|
||||||
|
<div className="flex items-center gap-1 mt-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||||
|
<button
|
||||||
|
onClick={() => copyText(msg.content)}
|
||||||
|
className="flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)] transition-colors"
|
||||||
|
title="复制消息"
|
||||||
|
>
|
||||||
|
{copied ? <Check className="w-3 h-3" /> : <Copy className="w-3 h-3" />}
|
||||||
|
{copied ? '已复制' : '复制'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { TeamDetail } from './TeamDetail'
|
|||||||
const API = '/api'
|
const API = '/api'
|
||||||
|
|
||||||
interface Team {
|
interface Team {
|
||||||
|
id: string
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
author: string
|
author: string
|
||||||
@ -17,6 +18,7 @@ interface Team {
|
|||||||
|
|
||||||
const PRESET_TEAMS: Team[] = [
|
const PRESET_TEAMS: Team[] = [
|
||||||
{
|
{
|
||||||
|
id: 'legal-team',
|
||||||
name: '法律咨询团队',
|
name: '法律咨询团队',
|
||||||
description: '法律咨询团队,包含合同审查、法律风险评估和合规管理',
|
description: '法律咨询团队,包含合同审查、法律风险评估和合规管理',
|
||||||
author: 'Agent Team',
|
author: 'Agent Team',
|
||||||
@ -26,6 +28,7 @@ const PRESET_TEAMS: Team[] = [
|
|||||||
installed_at: '',
|
installed_at: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'marketing-team',
|
||||||
name: '营销团队',
|
name: '营销团队',
|
||||||
description: '营销团队,包含市场分析、竞品研究和内容策划',
|
description: '营销团队,包含市场分析、竞品研究和内容策划',
|
||||||
author: 'Agent Team',
|
author: 'Agent Team',
|
||||||
|
|||||||
@ -30,7 +30,7 @@ export function MemberList() {
|
|||||||
<span className="text-sm font-semibold text-[var(--text-muted)]">成员</span>
|
<span className="text-sm font-semibold text-[var(--text-muted)]">成员</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 flex items-center justify-center text-[var(--text-muted)] text-xs">
|
<div className="flex-1 flex items-center justify-center text-[var(--text-muted)] text-xs">
|
||||||
选择群聊查看成员
|
选择项目查看成员
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,119 +1,199 @@
|
|||||||
import { useState } from 'react'
|
import { useState, useEffect, useCallback } from 'react'
|
||||||
import ReactMarkdown from 'react-markdown'
|
import ReactMarkdown from 'react-markdown'
|
||||||
import remarkGfm from 'remark-gfm'
|
import remarkGfm from 'remark-gfm'
|
||||||
import { ListTodo, FileText, X, ExternalLink } from 'lucide-react'
|
import { FileText, X, Save, Check, Loader2, Edit3, Eye } from 'lucide-react'
|
||||||
import { useStore } from '../store'
|
import { useStore } from '../store'
|
||||||
|
|
||||||
const API = '/api'
|
const API = '/api'
|
||||||
|
|
||||||
export function RightSidebar() {
|
export function RightSidebar() {
|
||||||
const { activeRoomId, tasks, workspace } = useStore()
|
const { activeRoomId, workspace, rooms } = useStore()
|
||||||
const [previewFile, setPreviewFile] = useState<{ name: string; content: string } | null>(null)
|
const [activeFile, setActiveFile] = useState<{ name: string; content: string } | null>(null)
|
||||||
|
const [editing, setEditing] = useState(false)
|
||||||
const tasksMd = activeRoomId ? (tasks[activeRoomId] || '') : ''
|
const [editContent, setEditContent] = useState('')
|
||||||
|
const [saving, setSaving] = useState(false)
|
||||||
|
const [saveSuccess, setSaveSuccess] = useState(false)
|
||||||
|
|
||||||
const files = activeRoomId ? (workspace[activeRoomId] || []) : []
|
const files = activeRoomId ? (workspace[activeRoomId] || []) : []
|
||||||
|
const room = rooms.find(r => r.id === activeRoomId)
|
||||||
const openFile = async (filename: string) => {
|
const mode = room?.mode || 'plan'
|
||||||
|
|
||||||
|
const openFile = useCallback(async (filename: string) => {
|
||||||
if (!activeRoomId) return
|
if (!activeRoomId) return
|
||||||
const d = await fetch(`${API}/rooms/${activeRoomId}/workspace/${filename}`).then(r => r.json())
|
const d = await fetch(`${API}/rooms/${activeRoomId}/workspace/${filename}`).then(r => r.json())
|
||||||
setPreviewFile({ name: filename, content: d.content || '' })
|
const content = d.content || ''
|
||||||
|
setActiveFile({ name: filename, content })
|
||||||
|
setEditContent(content)
|
||||||
|
setEditing(false)
|
||||||
|
}, [activeRoomId])
|
||||||
|
|
||||||
|
// 监听 ChatView 发来的 open-artifact 事件
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = (e: Event) => {
|
||||||
|
const detail = (e as CustomEvent).detail
|
||||||
|
if (detail.roomId === activeRoomId && detail.filename) {
|
||||||
|
openFile(detail.filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.addEventListener('open-artifact', handler)
|
||||||
|
return () => window.removeEventListener('open-artifact', handler)
|
||||||
|
}, [activeRoomId, openFile])
|
||||||
|
|
||||||
|
// 新 artifact 到达时自动打开
|
||||||
|
useEffect(() => {
|
||||||
|
if (mode === 'build' && files.length > 0) {
|
||||||
|
const latest = files[files.length - 1]
|
||||||
|
if (!activeFile || activeFile.name !== latest) {
|
||||||
|
openFile(latest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [files.length, mode])
|
||||||
|
|
||||||
|
const saveFile = async () => {
|
||||||
|
if (!activeFile || !activeRoomId) return
|
||||||
|
setSaving(true)
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${API}/rooms/${activeRoomId}/workspace/${activeFile.name}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ content: editContent })
|
||||||
|
})
|
||||||
|
if (res.ok) {
|
||||||
|
setActiveFile({ ...activeFile, content: editContent })
|
||||||
|
setSaveSuccess(true)
|
||||||
|
setEditing(false)
|
||||||
|
setTimeout(() => setSaveSuccess(false), 2000)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setSaving(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!activeRoomId) {
|
if (!activeRoomId) {
|
||||||
return (
|
return (
|
||||||
<div className="w-[280px] bg-[var(--bg-secondary)] border-l border-[var(--border)] flex flex-col">
|
<div className="w-[320px] bg-[var(--bg-secondary)] border-l border-[var(--border)] flex flex-col">
|
||||||
<div className="h-12 px-4 border-b border-[var(--border)] flex items-center">
|
<div className="h-12 px-4 border-b border-[var(--border)] flex items-center">
|
||||||
<span className="text-sm font-semibold text-[var(--text-muted)]">工作区</span>
|
<span className="text-sm font-semibold text-[var(--text-muted)]">产出物</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 flex items-center justify-center text-[var(--text-muted)] text-xs">
|
<div className="flex-1 flex items-center justify-center text-[var(--text-muted)] text-xs">
|
||||||
选择群聊查看工作区
|
选择项目查看产出物
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="w-[320px] bg-[var(--bg-secondary)] border-l border-[var(--border)] flex flex-col overflow-hidden">
|
||||||
<div className="w-[280px] bg-[var(--bg-secondary)] border-l border-[var(--border)] flex flex-col overflow-hidden">
|
{/* Header */}
|
||||||
<div className="h-12 px-4 border-b border-[var(--border)] flex items-center">
|
<div className="h-12 px-4 border-b border-[var(--border)] flex items-center gap-2">
|
||||||
<span className="text-sm font-semibold text-[var(--text-muted)]">工作区</span>
|
<FileText className="w-4 h-4 text-[var(--text-muted)]" />
|
||||||
</div>
|
<span className="text-sm font-semibold text-[var(--text-muted)]">产出物</span>
|
||||||
|
<span className="text-[10px] px-1.5 py-0.5 bg-[var(--bg-tertiary)] text-[var(--text-muted)] rounded-full">
|
||||||
<div className="flex-1 overflow-y-auto scrollbar-thin">
|
{files.length}
|
||||||
{/* TodoList */}
|
</span>
|
||||||
<section className="border-b border-[var(--border)]">
|
|
||||||
<div className="px-4 py-3 flex items-center gap-2 text-xs font-semibold text-[var(--text-muted)] uppercase">
|
|
||||||
<ListTodo className="w-3.5 h-3.5" />
|
|
||||||
任务列表
|
|
||||||
</div>
|
|
||||||
<div className="px-4 pb-4">
|
|
||||||
{tasksMd ? (
|
|
||||||
<div className="text-sm prose prose-sm max-w-none dark:prose-invert">
|
|
||||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>{tasksMd}</ReactMarkdown>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<p className="text-xs text-[var(--text-muted)]">暂无任务</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Modified Files */}
|
|
||||||
<section>
|
|
||||||
<div className="px-4 py-3 flex items-center gap-2 text-xs font-semibold text-[var(--text-muted)] uppercase">
|
|
||||||
<FileText className="w-3.5 h-3.5" />
|
|
||||||
修改的文件 ({files.length})
|
|
||||||
</div>
|
|
||||||
<div className="px-4 pb-4">
|
|
||||||
{files.length > 0 ? (
|
|
||||||
<div className="space-y-1">
|
|
||||||
{files.map(f => (
|
|
||||||
<button
|
|
||||||
key={f}
|
|
||||||
onClick={() => openFile(f)}
|
|
||||||
className="w-full flex items-center gap-2 text-sm text-[var(--accent)] hover:text-[var(--accent-hover)] py-1.5 px-2 rounded hover:bg-[var(--bg-hover)] transition-colors text-left"
|
|
||||||
>
|
|
||||||
<FileText className="w-4 h-4 flex-shrink-0" />
|
|
||||||
<span className="truncate">{f}</span>
|
|
||||||
<ExternalLink className="w-3 h-3 flex-shrink-0 ml-auto opacity-0 group-hover:opacity-100" />
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<p className="text-xs text-[var(--text-muted)]">暂无修改的文件</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* File preview modal */}
|
{/* File list */}
|
||||||
{previewFile && (
|
{files.length > 0 && (
|
||||||
<div
|
<div className="border-b border-[var(--border)] max-h-[200px] overflow-y-auto scrollbar-thin">
|
||||||
className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 p-4"
|
{files.map(f => (
|
||||||
onClick={() => setPreviewFile(null)}
|
<button
|
||||||
>
|
key={f}
|
||||||
<div
|
onClick={() => openFile(f)}
|
||||||
className="bg-[var(--bg-secondary)] rounded-lg w-full max-w-3xl max-h-[80vh] flex flex-col shadow-2xl"
|
className={`w-full flex items-center gap-2 px-4 py-2.5 text-sm transition-colors text-left ${
|
||||||
onClick={e => e.stopPropagation()}
|
activeFile?.name === f
|
||||||
>
|
? 'bg-[var(--accent)]/10 text-[var(--accent)] border-l-2 border-[var(--accent)]'
|
||||||
<div className="flex items-center justify-between px-4 py-3 border-b border-[var(--border)]">
|
: 'hover:bg-[var(--bg-hover)] text-[var(--text-secondary)] border-l-2 border-transparent'
|
||||||
<div className="flex items-center gap-2">
|
}`}
|
||||||
<FileText className="w-5 h-5 text-[var(--accent)]" />
|
>
|
||||||
<span className="font-semibold">{previewFile.name}</span>
|
<FileText className="w-4 h-4 flex-shrink-0" />
|
||||||
</div>
|
<span className="truncate">{f}</span>
|
||||||
<button
|
</button>
|
||||||
onClick={() => setPreviewFile(null)}
|
))}
|
||||||
className="w-8 h-8 rounded flex items-center justify-center text-[var(--text-muted)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-primary)] transition-colors"
|
|
||||||
>
|
|
||||||
<X className="w-5 h-5" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="overflow-y-auto p-4 prose dark:prose-invert max-w-none text-sm">
|
|
||||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>{previewFile.content}</ReactMarkdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
|
||||||
|
{/* File preview / editor */}
|
||||||
|
{activeFile ? (
|
||||||
|
<div className="flex-1 flex flex-col overflow-hidden">
|
||||||
|
{/* File header */}
|
||||||
|
<div className="flex items-center justify-between px-4 py-2 bg-[var(--bg-tertiary)] border-b border-[var(--border)]">
|
||||||
|
<span className="text-xs font-medium text-[var(--text-secondary)] truncate flex-1">
|
||||||
|
{activeFile.name}
|
||||||
|
</span>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
{/* 切换编辑/预览 */}
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
if (!editing) {
|
||||||
|
setEditContent(activeFile.content)
|
||||||
|
}
|
||||||
|
setEditing(!editing)
|
||||||
|
}}
|
||||||
|
className={`p-1.5 rounded transition-colors ${
|
||||||
|
editing
|
||||||
|
? 'bg-[var(--accent)] text-white'
|
||||||
|
: 'text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)]'
|
||||||
|
}`}
|
||||||
|
title={editing ? '预览' : '编辑'}
|
||||||
|
>
|
||||||
|
{editing ? <Eye className="w-3.5 h-3.5" /> : <Edit3 className="w-3.5 h-3.5" />}
|
||||||
|
</button>
|
||||||
|
{/* 保存 */}
|
||||||
|
{editing && (
|
||||||
|
<button
|
||||||
|
onClick={saveFile}
|
||||||
|
disabled={saving}
|
||||||
|
className={`flex items-center gap-1 px-2 py-1 rounded text-xs font-medium text-white transition-colors ${
|
||||||
|
saveSuccess ? 'bg-green-500' : 'bg-[var(--accent)] hover:bg-[var(--accent-hover)]'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{saving ? <Loader2 className="w-3 h-3 animate-spin" /> : saveSuccess ? <Check className="w-3 h-3" /> : <Save className="w-3 h-3" />}
|
||||||
|
{saveSuccess ? '已保存' : '保存'}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{/* 关闭 */}
|
||||||
|
<button
|
||||||
|
onClick={() => { setActiveFile(null); setEditing(false) }}
|
||||||
|
className="p-1.5 rounded text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)] transition-colors"
|
||||||
|
>
|
||||||
|
<X className="w-3.5 h-3.5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
{editing ? (
|
||||||
|
<textarea
|
||||||
|
value={editContent}
|
||||||
|
onChange={e => setEditContent(e.target.value)}
|
||||||
|
className="flex-1 p-4 bg-[var(--bg-primary)] text-sm font-mono resize-none outline-none scrollbar-thin"
|
||||||
|
spellCheck={false}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="flex-1 overflow-y-auto p-4 prose prose-sm dark:prose-invert max-w-none scrollbar-thin">
|
||||||
|
<ReactMarkdown remarkPlugins={[remarkGfm]}>{activeFile.content}</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex-1 flex flex-col items-center justify-center text-[var(--text-muted)] p-4">
|
||||||
|
{files.length === 0 ? (
|
||||||
|
<>
|
||||||
|
<FileText className="w-10 h-10 mb-3 opacity-20" />
|
||||||
|
<p className="text-sm text-center">暂无产出物</p>
|
||||||
|
<p className="text-xs mt-1 text-center opacity-60">
|
||||||
|
团队成员完成任务后,产出物会出现在这里
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<FileText className="w-10 h-10 mb-3 opacity-20" />
|
||||||
|
<p className="text-sm">选择文件查看内容</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Plus, Hash, Users, Crown, ShoppingCart } from 'lucide-react'
|
import { Plus, Hash, ShoppingCart } from 'lucide-react'
|
||||||
import { useStore } from '../store'
|
import { useStore } from '../store'
|
||||||
import type { Room } from '../types'
|
import type { Room } from '../types'
|
||||||
|
|
||||||
@ -8,18 +8,18 @@ const API = '/api'
|
|||||||
export function RoomSidebar() {
|
export function RoomSidebar() {
|
||||||
const { rooms, activeRoomId, setActiveRoom, fetchRooms, setPage } = useStore()
|
const { rooms, activeRoomId, setActiveRoom, fetchRooms, setPage } = useStore()
|
||||||
const [creating, setCreating] = useState(false)
|
const [creating, setCreating] = useState(false)
|
||||||
const [form, setForm] = useState({ name: '', type: 'dept' as 'dept' | 'leader' })
|
const [name, setName] = useState('')
|
||||||
|
|
||||||
useEffect(() => { fetchRooms() }, [])
|
useEffect(() => { fetchRooms() }, [])
|
||||||
|
|
||||||
const create = async () => {
|
const create = async () => {
|
||||||
if (!form.name) return
|
if (!name.trim()) return
|
||||||
await fetch(`${API}/rooms`, {
|
await fetch(`${API}/rooms`, {
|
||||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ name: form.name, type: form.type, master: '', members: [] })
|
body: JSON.stringify({ name: name.trim(), type: 'project', master: '', members: [] })
|
||||||
})
|
})
|
||||||
setCreating(false)
|
setCreating(false)
|
||||||
setForm({ name: '', type: 'dept' })
|
setName('')
|
||||||
fetchRooms()
|
fetchRooms()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,14 +29,11 @@ export function RoomSidebar() {
|
|||||||
return 'bg-[var(--text-muted)]'
|
return 'bg-[var(--text-muted)]'
|
||||||
}
|
}
|
||||||
|
|
||||||
const deptRooms = rooms.filter(r => r.type === 'dept')
|
|
||||||
const leaderRooms = rooms.filter(r => r.type === 'leader')
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-60 bg-[var(--channel-list-bg)] flex flex-col border-r border-[var(--border)]">
|
<div className="w-60 bg-[var(--channel-list-bg)] flex flex-col border-r border-[var(--border)]">
|
||||||
{/* Header */}
|
{/* 头部 */}
|
||||||
<div className="h-12 px-4 border-b border-[var(--border)] flex items-center justify-between shadow-sm">
|
<div className="h-12 px-4 border-b border-[var(--border)] flex items-center justify-between shadow-sm">
|
||||||
<h1 className="font-semibold text-sm truncate">我的团队</h1>
|
<h1 className="font-semibold text-sm truncate">项目</h1>
|
||||||
<button
|
<button
|
||||||
onClick={() => setCreating(!creating)}
|
onClick={() => setCreating(!creating)}
|
||||||
className="w-6 h-6 rounded flex items-center justify-center text-[var(--text-secondary)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-primary)] transition-colors"
|
className="w-6 h-6 rounded flex items-center justify-center text-[var(--text-secondary)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-primary)] transition-colors"
|
||||||
@ -45,23 +42,17 @@ export function RoomSidebar() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Create team form */}
|
{/* 创建项目表单 */}
|
||||||
{creating && (
|
{creating && (
|
||||||
<div className="p-3 border-b border-[var(--border)] space-y-2 text-sm bg-[var(--bg-tertiary)]">
|
<div className="p-3 border-b border-[var(--border)] space-y-2 text-sm bg-[var(--bg-tertiary)]">
|
||||||
<input
|
<input
|
||||||
|
autoFocus
|
||||||
className="w-full bg-[var(--bg-secondary)] border border-[var(--border)] rounded px-2 py-1.5 text-sm outline-none focus:border-[var(--accent)] transition-colors"
|
className="w-full bg-[var(--bg-secondary)] border border-[var(--border)] rounded px-2 py-1.5 text-sm outline-none focus:border-[var(--accent)] transition-colors"
|
||||||
placeholder="团队名称"
|
placeholder="项目名称"
|
||||||
value={form.name}
|
value={name}
|
||||||
onChange={e => setForm(f => ({ ...f, name: e.target.value }))}
|
onChange={e => setName(e.target.value)}
|
||||||
|
onKeyDown={e => e.key === 'Enter' && create()}
|
||||||
/>
|
/>
|
||||||
<select
|
|
||||||
className="w-full bg-[var(--bg-secondary)] border border-[var(--border)] rounded px-2 py-1.5 text-sm outline-none focus:border-[var(--accent)] transition-colors"
|
|
||||||
value={form.type}
|
|
||||||
onChange={e => setForm(f => ({ ...f, type: e.target.value as 'dept' | 'leader' }))}
|
|
||||||
>
|
|
||||||
<option value="dept">部门团队</option>
|
|
||||||
<option value="leader">Leader 团队</option>
|
|
||||||
</select>
|
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
<button
|
<button
|
||||||
onClick={create}
|
onClick={create}
|
||||||
@ -73,63 +64,34 @@ export function RoomSidebar() {
|
|||||||
onClick={() => setCreating(false)}
|
onClick={() => setCreating(false)}
|
||||||
className="bg-[var(--bg-hover)] hover:bg-[var(--bg-active)] rounded px-2 py-1.5 text-sm transition-colors"
|
className="bg-[var(--bg-hover)] hover:bg-[var(--bg-active)] rounded px-2 py-1.5 text-sm transition-colors"
|
||||||
>
|
>
|
||||||
✕
|
取消
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Room list */}
|
{/* 项目列表 */}
|
||||||
<div className="flex-1 overflow-y-auto py-2 scrollbar-thin">
|
<div className="flex-1 overflow-y-auto py-2 scrollbar-thin">
|
||||||
{/* Department teams */}
|
{rooms.map(r => (
|
||||||
{deptRooms.length > 0 && (
|
<RoomItem
|
||||||
<section className="mb-3">
|
key={r.id}
|
||||||
<div className="px-4 py-1 flex items-center gap-1 text-xs font-semibold text-[var(--text-muted)] uppercase hover:text-[var(--text-secondary)] cursor-pointer">
|
room={r}
|
||||||
<Users className="w-3 h-3" />
|
active={r.id === activeRoomId}
|
||||||
<span>部门团队</span>
|
onClick={() => setActiveRoom(r.id)}
|
||||||
</div>
|
statusColor={statusColor(r.status)}
|
||||||
{deptRooms.map(r => (
|
/>
|
||||||
<RoomItem
|
))}
|
||||||
key={r.id}
|
|
||||||
room={r}
|
|
||||||
active={r.id === activeRoomId}
|
|
||||||
onClick={() => setActiveRoom(r.id)}
|
|
||||||
statusColor={statusColor(r.status)}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Leader teams */}
|
|
||||||
{leaderRooms.length > 0 && (
|
|
||||||
<section>
|
|
||||||
<div className="px-4 py-1 flex items-center gap-1 text-xs font-semibold text-[var(--text-muted)] uppercase hover:text-[var(--text-secondary)] cursor-pointer">
|
|
||||||
<Crown className="w-3 h-3" />
|
|
||||||
<span>Leader 团队</span>
|
|
||||||
</div>
|
|
||||||
{leaderRooms.map(r => (
|
|
||||||
<RoomItem
|
|
||||||
key={r.id}
|
|
||||||
room={r}
|
|
||||||
active={r.id === activeRoomId}
|
|
||||||
onClick={() => setActiveRoom(r.id)}
|
|
||||||
statusColor={statusColor(r.status)}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Empty state */}
|
|
||||||
{rooms.length === 0 && (
|
{rooms.length === 0 && (
|
||||||
<div className="px-4 py-8 text-center text-sm text-[var(--text-muted)]">
|
<div className="px-4 py-8 text-center text-sm text-[var(--text-muted)]">
|
||||||
<Hash className="w-8 h-8 mx-auto mb-2 opacity-50" />
|
<Hash className="w-8 h-8 mx-auto mb-2 opacity-50" />
|
||||||
<p>暂无团队</p>
|
<p>暂无项目</p>
|
||||||
<p className="text-xs mt-1">点击 + 创建第一个团队</p>
|
<p className="text-xs mt-1">点击 + 创建第一个项目</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom hint - go to market */}
|
{/* 底部 - 去市场 */}
|
||||||
<div className="p-3 border-t border-[var(--border)]">
|
<div className="p-3 border-t border-[var(--border)]">
|
||||||
<button
|
<button
|
||||||
onClick={() => setPage('market')}
|
onClick={() => setPage('market')}
|
||||||
@ -164,4 +126,4 @@ function RoomItem({ room, active, onClick, statusColor }: { room: Room; active:
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { ChevronLeft, Users, Package, BookOpen, ChevronDown, ChevronRight, Save, Loader2, Trash2, Plus, ExternalLink } from 'lucide-react'
|
import { ChevronLeft, Users, Package, BookOpen, ChevronDown, ChevronRight, Save, Check, Loader2, Trash2, Plus, ExternalLink } from 'lucide-react'
|
||||||
import { MarkdownEditor } from './MarkdownEditor'
|
import { MarkdownEditor } from './MarkdownEditor'
|
||||||
|
|
||||||
const API = '/api'
|
const API = '/api'
|
||||||
|
|
||||||
interface Team {
|
interface Team {
|
||||||
|
id: string
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
author: string
|
author: string
|
||||||
@ -31,6 +32,7 @@ export function TeamDetail({ team, installed, onBack, onInstalled, onUninstalled
|
|||||||
const [knowledgeContent, setKnowledgeContent] = useState('')
|
const [knowledgeContent, setKnowledgeContent] = useState('')
|
||||||
const [newKnowledgeName, setNewKnowledgeName] = useState('')
|
const [newKnowledgeName, setNewKnowledgeName] = useState('')
|
||||||
const [saving, setSaving] = useState(false)
|
const [saving, setSaving] = useState(false)
|
||||||
|
const [saveSuccess, setSaveSuccess] = useState(false)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [deleting, setDeleting] = useState(false)
|
const [deleting, setDeleting] = useState(false)
|
||||||
|
|
||||||
@ -38,13 +40,13 @@ export function TeamDetail({ team, installed, onBack, onInstalled, onUninstalled
|
|||||||
if (installed) {
|
if (installed) {
|
||||||
fetchKnowledge()
|
fetchKnowledge()
|
||||||
}
|
}
|
||||||
}, [team.name, installed])
|
}, [team.id, installed])
|
||||||
|
|
||||||
const fetchKnowledge = async () => {
|
const fetchKnowledge = async () => {
|
||||||
if (!installed) return
|
if (!installed) return
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API}/teams/${team.name}/knowledge`)
|
const res = await fetch(`${API}/teams/${team.id}/knowledge`)
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
setKnowledge(data || [])
|
setKnowledge(data || [])
|
||||||
} finally {
|
} finally {
|
||||||
@ -56,7 +58,7 @@ export function TeamDetail({ team, installed, onBack, onInstalled, onUninstalled
|
|||||||
if (!installed) return
|
if (!installed) return
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API}/teams/${team.name}/agents/${agentName}/files/${file}`)
|
const res = await fetch(`${API}/teams/${team.id}/agents/${agentName}/files/${file}`)
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
setAgentContent(data.content || '')
|
setAgentContent(data.content || '')
|
||||||
setAgentFile(file)
|
setAgentFile(file)
|
||||||
@ -71,11 +73,15 @@ export function TeamDetail({ team, installed, onBack, onInstalled, onUninstalled
|
|||||||
if (!expandedAgent || !agentFile || !installed) return
|
if (!expandedAgent || !agentFile || !installed) return
|
||||||
setSaving(true)
|
setSaving(true)
|
||||||
try {
|
try {
|
||||||
await fetch(`${API}/teams/${team.name}/agents/${expandedAgent}/files/${agentFile}`, {
|
const res = await fetch(`${API}/teams/${team.id}/agents/${expandedAgent}/files/${agentFile}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ content: agentContent })
|
body: JSON.stringify({ content: agentContent })
|
||||||
})
|
})
|
||||||
|
if (res.ok) {
|
||||||
|
setSaveSuccess(true)
|
||||||
|
setTimeout(() => setSaveSuccess(false), 2000)
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false)
|
setSaving(false)
|
||||||
}
|
}
|
||||||
@ -85,7 +91,7 @@ export function TeamDetail({ team, installed, onBack, onInstalled, onUninstalled
|
|||||||
if (!installed) return
|
if (!installed) return
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API}/teams/${team.name}/knowledge/${fileName}`)
|
const res = await fetch(`${API}/teams/${team.id}/knowledge/${fileName}`)
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
setKnowledgeContent(data.content || '')
|
setKnowledgeContent(data.content || '')
|
||||||
setSelectedKnowledge(fileName)
|
setSelectedKnowledge(fileName)
|
||||||
@ -100,11 +106,15 @@ export function TeamDetail({ team, installed, onBack, onInstalled, onUninstalled
|
|||||||
if (!selectedKnowledge || !installed) return
|
if (!selectedKnowledge || !installed) return
|
||||||
setSaving(true)
|
setSaving(true)
|
||||||
try {
|
try {
|
||||||
await fetch(`${API}/teams/${team.name}/knowledge/${selectedKnowledge}`, {
|
const res = await fetch(`${API}/teams/${team.id}/knowledge/${selectedKnowledge}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ content: knowledgeContent })
|
body: JSON.stringify({ content: knowledgeContent })
|
||||||
})
|
})
|
||||||
|
if (res.ok) {
|
||||||
|
setSaveSuccess(true)
|
||||||
|
setTimeout(() => setSaveSuccess(false), 2000)
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false)
|
setSaving(false)
|
||||||
}
|
}
|
||||||
@ -115,7 +125,7 @@ export function TeamDetail({ team, installed, onBack, onInstalled, onUninstalled
|
|||||||
const name = newKnowledgeName.trim().endsWith('.md')
|
const name = newKnowledgeName.trim().endsWith('.md')
|
||||||
? newKnowledgeName.trim()
|
? newKnowledgeName.trim()
|
||||||
: newKnowledgeName.trim() + '.md'
|
: newKnowledgeName.trim() + '.md'
|
||||||
await fetch(`${API}/teams/${team.name}/knowledge/${name}`, {
|
await fetch(`${API}/teams/${team.id}/knowledge/${name}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ content: '# ' + name.replace('.md', '') + '\n\n' })
|
body: JSON.stringify({ content: '# ' + name.replace('.md', '') + '\n\n' })
|
||||||
@ -146,7 +156,7 @@ export function TeamDetail({ team, installed, onBack, onInstalled, onUninstalled
|
|||||||
|
|
||||||
setDeleting(true)
|
setDeleting(true)
|
||||||
try {
|
try {
|
||||||
await fetch(`${API}/teams/${team.name}`, { method: 'DELETE' })
|
await fetch(`${API}/teams/${team.id}`, { method: 'DELETE' })
|
||||||
onUninstalled?.()
|
onUninstalled?.()
|
||||||
} finally {
|
} finally {
|
||||||
setDeleting(false)
|
setDeleting(false)
|
||||||
@ -292,10 +302,14 @@ export function TeamDetail({ team, installed, onBack, onInstalled, onUninstalled
|
|||||||
<button
|
<button
|
||||||
onClick={saveAgentFile}
|
onClick={saveAgentFile}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
className="flex items-center gap-1.5 bg-[var(--accent)] hover:bg-[var(--accent-hover)] px-3 py-1.5 rounded text-xs font-medium transition-colors"
|
className={`flex items-center gap-1.5 px-3 py-1.5 rounded text-xs font-medium text-white transition-colors ${
|
||||||
|
saveSuccess
|
||||||
|
? 'bg-green-500'
|
||||||
|
: 'bg-[var(--accent)] hover:bg-[var(--accent-hover)]'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{saving ? <Loader2 className="w-3 h-3 animate-spin" /> : <Save className="w-3 h-3" />}
|
{saving ? <Loader2 className="w-3 h-3 animate-spin" /> : saveSuccess ? <Check className="w-3 h-3" /> : <Save className="w-3 h-3" />}
|
||||||
保存
|
{saveSuccess ? '已保存' : '保存'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -385,10 +399,14 @@ export function TeamDetail({ team, installed, onBack, onInstalled, onUninstalled
|
|||||||
<button
|
<button
|
||||||
onClick={saveKnowledgeFile}
|
onClick={saveKnowledgeFile}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
className="flex items-center gap-1.5 bg-[var(--accent)] hover:bg-[var(--accent-hover)] px-3 py-1.5 rounded text-xs font-medium transition-colors"
|
className={`flex items-center gap-1.5 px-3 py-1.5 rounded text-xs font-medium text-white transition-colors ${
|
||||||
|
saveSuccess
|
||||||
|
? 'bg-green-500'
|
||||||
|
: 'bg-[var(--accent)] hover:bg-[var(--accent-hover)]'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{saving ? <Loader2 className="w-3 h-3 animate-spin" /> : <Save className="w-3 h-3" />}
|
{saving ? <Loader2 className="w-3 h-3 animate-spin" /> : saveSuccess ? <Check className="w-3 h-3" /> : <Save className="w-3 h-3" />}
|
||||||
保存
|
{saveSuccess ? '已保存' : '保存'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -60,13 +60,13 @@ export const useStore = create<AppState>((set, get) => {
|
|||||||
user: null,
|
user: null,
|
||||||
onboardingCompleted: typeof window !== 'undefined' ? localStorage.getItem('onboarding_completed') === 'true' : false,
|
onboardingCompleted: typeof window !== 'undefined' ? localStorage.getItem('onboarding_completed') === 'true' : false,
|
||||||
rooms: [],
|
rooms: [],
|
||||||
activeRoomId: null,
|
activeRoomId: typeof window !== 'undefined' ? localStorage.getItem('activeRoomId') : null,
|
||||||
messages: {},
|
messages: {},
|
||||||
tasks: {},
|
tasks: {},
|
||||||
workspace: {},
|
workspace: {},
|
||||||
agents: [],
|
agents: [],
|
||||||
skills: [],
|
skills: [],
|
||||||
page: 'chat',
|
page: (typeof window !== 'undefined' ? localStorage.getItem('page') as AppState['page'] : null) || 'chat',
|
||||||
ws: {},
|
ws: {},
|
||||||
|
|
||||||
setTheme: (theme) => {
|
setTheme: (theme) => {
|
||||||
@ -101,8 +101,12 @@ export const useStore = create<AppState>((set, get) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setPage: (page) => set({ page }),
|
setPage: (page) => {
|
||||||
|
localStorage.setItem('page', page)
|
||||||
|
set({ page })
|
||||||
|
},
|
||||||
setActiveRoom: (id) => {
|
setActiveRoom: (id) => {
|
||||||
|
localStorage.setItem('activeRoomId', id)
|
||||||
set({ activeRoomId: id })
|
set({ activeRoomId: id })
|
||||||
get().connectRoom(id)
|
get().connectRoom(id)
|
||||||
fetch(`${API}/rooms/${id}/tasks`).then(r => r.json()).then(d => {
|
fetch(`${API}/rooms/${id}/tasks`).then(r => r.json()).then(d => {
|
||||||
@ -140,10 +144,13 @@ export const useStore = create<AppState>((set, get) => {
|
|||||||
if (ev.type === 'agent_message') {
|
if (ev.type === 'agent_message') {
|
||||||
set(s => {
|
set(s => {
|
||||||
const msgs = [...(s.messages[roomId] || [])]
|
const msgs = [...(s.messages[roomId] || [])]
|
||||||
const idx = msgs.findLastIndex(m => m.streaming && m.agent === ev.agent)
|
let idx = -1
|
||||||
|
for (let i = msgs.length - 1; i >= 0; i--) {
|
||||||
|
if (msgs[i].streaming && msgs[i].agent === ev.agent) { idx = i; break }
|
||||||
|
}
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
msgs[idx] = { ...msgs[idx], content: msgs[idx].content + ev.content, streaming: ev.streaming }
|
msgs[idx] = { ...msgs[idx], content: msgs[idx].content + (ev.content || ''), streaming: ev.streaming }
|
||||||
} else {
|
} else if (ev.content) {
|
||||||
msgs.push({ id: Date.now().toString(), agent: ev.agent, role: ev.role, content: ev.content, streaming: ev.streaming })
|
msgs.push({ id: Date.now().toString(), agent: ev.agent, role: ev.role, content: ev.content, streaming: ev.streaming })
|
||||||
}
|
}
|
||||||
return { messages: { ...s.messages, [roomId]: msgs } }
|
return { messages: { ...s.messages, [roomId]: msgs } }
|
||||||
@ -160,6 +167,32 @@ export const useStore = create<AppState>((set, get) => {
|
|||||||
set(s => ({
|
set(s => ({
|
||||||
workspace: { ...s.workspace, [roomId]: [...(s.workspace[roomId] || []), ev.filename] }
|
workspace: { ...s.workspace, [roomId]: [...(s.workspace[roomId] || []), ev.filename] }
|
||||||
}))
|
}))
|
||||||
|
} else if (ev.type === 'mode_change') {
|
||||||
|
set(s => ({
|
||||||
|
rooms: s.rooms.map(r => r.id === roomId ? { ...r, mode: ev.mode } : r)
|
||||||
|
}))
|
||||||
|
} else if (ev.type === 'artifact') {
|
||||||
|
// 添加 artifact 消息卡片到聊天
|
||||||
|
set(s => {
|
||||||
|
const msgs = [...(s.messages[roomId] || [])]
|
||||||
|
msgs.push({
|
||||||
|
id: Date.now().toString(),
|
||||||
|
agent: ev.agent,
|
||||||
|
role: 'artifact' as const,
|
||||||
|
content: ev.title || ev.filename,
|
||||||
|
filename: ev.filename,
|
||||||
|
title: ev.title,
|
||||||
|
})
|
||||||
|
// 同时更新 workspace 文件列表
|
||||||
|
const files = [...(s.workspace[roomId] || [])]
|
||||||
|
if (!files.includes(ev.filename)) {
|
||||||
|
files.push(ev.filename)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
messages: { ...s.messages, [roomId]: msgs },
|
||||||
|
workspace: { ...s.workspace, [roomId]: files },
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set(s => ({ ws: { ...s.ws, [roomId]: socket } }))
|
set(s => ({ ws: { ...s.ws, [roomId]: socket } }))
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
export type RoomStatus = 'pending' | 'thinking' | 'working'
|
export type RoomStatus = 'pending' | 'thinking' | 'working'
|
||||||
export type RoomType = 'dept' | 'leader'
|
export type RoomType = 'project'
|
||||||
|
export type RoomMode = 'plan' | 'build'
|
||||||
|
|
||||||
export interface Room {
|
export interface Room {
|
||||||
id: string
|
id: string
|
||||||
@ -12,14 +13,17 @@ export interface Room {
|
|||||||
action?: string
|
action?: string
|
||||||
color?: string
|
color?: string
|
||||||
team?: string
|
team?: string
|
||||||
|
mode?: RoomMode
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Message {
|
export interface Message {
|
||||||
id: string
|
id: string
|
||||||
agent: string
|
agent: string
|
||||||
role: 'user' | 'master' | 'member' | 'challenge'
|
role: 'user' | 'master' | 'member' | 'challenge' | 'artifact'
|
||||||
content: string
|
content: string
|
||||||
streaming?: boolean
|
streaming?: boolean
|
||||||
|
filename?: string
|
||||||
|
title?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentInfo {
|
export interface AgentInfo {
|
||||||
@ -38,3 +42,5 @@ export type WsEvent =
|
|||||||
| { type: 'task_assign'; from: string; to: string; task: string }
|
| { type: 'task_assign'; from: string; to: string; task: string }
|
||||||
| { type: 'tasks_update'; content: string }
|
| { type: 'tasks_update'; content: string }
|
||||||
| { type: 'workspace_file'; filename: string; content: string }
|
| { type: 'workspace_file'; filename: string; content: string }
|
||||||
|
| { type: 'mode_change'; mode: RoomMode }
|
||||||
|
| { type: 'artifact'; agent: string; filename: string; title?: string }
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user