@nathan33/nao-skill 0.18.3 → 0.18.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/common/intercom-protocol.md +8 -0
- package/.agents/extensions/nao-role-agents.mjs +207 -0
- package/.agents/scripts/nao-fleet.sh +28 -0
- package/.agents/templates/pi-subagents/README.md +56 -0
- package/.agents/templates/pi-subagents/intercom-bridge.md +26 -0
- package/README.md +1 -1
- package/package.json +4 -1
|
@@ -37,6 +37,14 @@ description: pi-intercom 多会话协作协议(常驻引用)
|
|
|
37
37
|
显式指定时由 `NAO_MODEL_WHITELIST` 在**命令行入口解析阶段**即校验,
|
|
38
38
|
不命中直接 `die`,无部分拉起;PM 只需如实转达用户 `-m` 指定。
|
|
39
39
|
|
|
40
|
+
## 会话内子代理(授权与回执归属)
|
|
41
|
+
|
|
42
|
+
- **授权**:角色可在自身 turn 内用 pi-subagents 起**会话内子代理**(scout / researcher / oracle / reviewer / evidence-auditor 等)。默认**只读**;需要写入时须 `worktree: true` 隔离并指定**单一集成者**(父角色),同一 cwd 禁多写。子代理既**不属**舰队会话身份模型 —— 边界与 `status`/`close` 处置**见上文「子代理会话不属舰队会话身份模型」**,此处不重复。
|
|
43
|
+
- **角色口径同源(指针)**:常驻舰队会话 = `nao-fleet.sh` 以 `--append-system-prompt` 注入角色卡;会话内子代理 = 包内扩展(`.agents/extensions/nao-role-agents.mjs`)在 `session_start` 运行时注册 `nao-<role-id>`。两路**同卡同锚**(同一批 `.agents/prompts/*.md` + 同一环境锚点行);细节见该扩展与 `tests/t4/cases/17-role-agents.sh`,此处不复制。
|
|
44
|
+
- **回执归属(硬性)**:子代理的 run 结果**只是证据,不等于回执**;`[编号] done(lite|full)` **只能由父角色**在自行核对后经 intercom `send` 发出(PM 只认父角色 done)。父角色不得把子代理输出直接转贴为回执。
|
|
45
|
+
- **子代理禁令**:子代理**不得以舰队角色名义回执**、**不得发送 `done` 形态消息**、**不得直发 PM**;需决策/阻塞/授权走 `contact_supervisor`(进程内 native 通道,无需 pi-intercom;父侧对应用 `subagent_supervisor`)。
|
|
46
|
+
- **证据格式**:子代理产物须含**命令 + exit code + 例数/文件数(或断言数)+ 路径**,供父角色写入回执「测试」栏;无证据的「完成」不算证据。注入文本见 `@.agents/templates/pi-subagents/intercom-bridge.md`(安装说明见同目录 `README.md`)。
|
|
47
|
+
|
|
40
48
|
## 开工前自检(派发闸门)
|
|
41
49
|
|
|
42
50
|
派发前跑一次:
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// .agents/extensions/nao-role-agents.mjs
|
|
2
|
+
// T527 —— 角色→pi-subagents agent 的单一事实(运行时注册扩展 · E-lite)
|
|
3
|
+
//
|
|
4
|
+
// 契约点(外部公开面,升版漂移由 tests/t4/cases/17 探针 fail-loud):
|
|
5
|
+
// 事件名 pi-subagents:runtime-agent-register:v1(docs/extension-api.md §Runtime agent registration)
|
|
6
|
+
//
|
|
7
|
+
// BR1 零副本 :运行时读 .agents/prompts/*.md + .agents/roles.yaml;无生成物、无静态副本。
|
|
8
|
+
// BR2 同卡同锚 :systemPrompt = 卡正文(去 frontmatter)逐字节 + 环境锚点行(与
|
|
9
|
+
// `.agents/scripts/nao-fleet.sh` build_system_prompt 的 printf 段逐字一致;
|
|
10
|
+
// 唯一双写点,由 cases/17 cross-assert 兜住)。
|
|
11
|
+
// BR3 命名纪律 :nao-<role-id>(6 个)· 不设 alias · 不占 subagent- 前缀。
|
|
12
|
+
// BR4 上下文口径:systemPromptMode=replace + inheritProjectContext=true(否则卡内 AGENTS.md 契约静默丢失)。
|
|
13
|
+
// BR5 韧性 :注册/装配失败仅 warn、绝不 throw;session_start 先 dispose 再注册(幂等)。
|
|
14
|
+
// BR6 逃生阀 :NAO_ROLE_AGENTS=off ⇒ 不注册。
|
|
15
|
+
// BR9 分发面 :扩展在**每个 trusted 会话**加载(非仅舰队会话),前提同 pi.skills。
|
|
16
|
+
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
18
|
+
import { dirname, join, resolve } from "node:path";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
20
|
+
|
|
21
|
+
export const RUNTIME_AGENT_REGISTER_EVENT = "pi-subagents:runtime-agent-register:v1";
|
|
22
|
+
export const RUNTIME_AGENT_REGISTER_VERSION = 1;
|
|
23
|
+
export const ENV_ANCHOR_HEADING = "## 环境锚点";
|
|
24
|
+
export const ROLE_AGENT_PREFIX = "nao-";
|
|
25
|
+
|
|
26
|
+
// 包根 = <pkg>/.agents/extensions/<this> 向上两级
|
|
27
|
+
const EXT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
28
|
+
const PKG_ROOT = resolve(EXT_DIR, "..", "..");
|
|
29
|
+
|
|
30
|
+
function warn(msg) {
|
|
31
|
+
try {
|
|
32
|
+
console.warn(`[nao-role-agents] ${msg}`);
|
|
33
|
+
} catch {
|
|
34
|
+
/* 日志失败不得影响会话 */
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** root 是否是 nao 机制包根(有 .agents/roles.yaml 才算,防把 shim-only 项目当包根) */
|
|
39
|
+
export function hasRoleMechanism(root) {
|
|
40
|
+
try {
|
|
41
|
+
return readFileSync(join(root, ".agents", "roles.yaml"), "utf8").length > 0;
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** 锚点用的 NAO_SKILLS:优先 env;env 指向的不是机制包根则回退扩展自身包根(下游 shim-only 场景) */
|
|
48
|
+
export function resolveSkillsRoot(env = process.env) {
|
|
49
|
+
const fromEnv = env && env.NAO_SKILLS;
|
|
50
|
+
if (fromEnv && hasRoleMechanism(fromEnv)) return resolve(fromEnv);
|
|
51
|
+
return PKG_ROOT;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** 环境锚点行 —— 与 nao-fleet.sh:1068 的 printf 段逐字一致(cases/17 AC3 cross-assert) */
|
|
55
|
+
export function buildAnchorLine(skillsRoot) {
|
|
56
|
+
return `- NAO_SKILLS=${skillsRoot}(nao 机制**包根**;@.agents/... 引用一律以 \`$NAO_SKILLS/.agents/\` 为根解析。注意:项目内 \`.agents/\` 只有 shim,不存机制副本)`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** 拆分卡片:frontmatter 文本 + 正文(正文 = 首块 frontmatter 之后,逐字节保留原样) */
|
|
60
|
+
export function splitCard(raw) {
|
|
61
|
+
if (!raw.startsWith("---\n")) return { frontmatter: "", body: raw };
|
|
62
|
+
const end = raw.indexOf("\n---\n", 3);
|
|
63
|
+
if (end < 0) return { frontmatter: "", body: raw };
|
|
64
|
+
return { frontmatter: raw.slice(4, end + 1), body: raw.slice(end + 5) };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 卡正文(去 frontmatter + 首尾空白)—— pi-subagents registry 要求
|
|
69
|
+
* `systemPrompt.trim() === systemPrompt`(禁首尾空白,实测 error: "must be a non-empty string
|
|
70
|
+
* without leading or trailing whitespace"),故只归一化首尾空白,内部逐字节保留。
|
|
71
|
+
*/
|
|
72
|
+
export function cardBody(raw) {
|
|
73
|
+
const { body } = splitCard(raw);
|
|
74
|
+
return body.replace(/^\n+/, "").replace(/\s+$/, "");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** 取 frontmatter 单行字段(首个命中) */
|
|
78
|
+
export function frontmatterField(frontmatter, key) {
|
|
79
|
+
for (const line of frontmatter.split("\n")) {
|
|
80
|
+
const m = /^([A-Za-z_][A-Za-z0-9_]*):[ \t]*(.*)$/.exec(line);
|
|
81
|
+
if (m && m[1] === key) return m[2];
|
|
82
|
+
}
|
|
83
|
+
return "";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** 严格子集解析 roles.yaml(角色 id 2 空格 / card 4 空格),保持声明顺序 */
|
|
87
|
+
export function parseRolesYaml(text) {
|
|
88
|
+
const roles = [];
|
|
89
|
+
let cur = null;
|
|
90
|
+
for (const line of text.split("\n")) {
|
|
91
|
+
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
|
92
|
+
let m = /^ ([a-z0-9][a-z0-9-]*):[ \t]*$/.exec(line);
|
|
93
|
+
if (m) {
|
|
94
|
+
cur = { role: m[1], card: "" };
|
|
95
|
+
roles.push(cur);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
m = /^ card:[ \t]*(\S+)[ \t]*$/.exec(line);
|
|
99
|
+
if (m && cur) cur.card = m[1];
|
|
100
|
+
}
|
|
101
|
+
return roles.filter((r) => r.role && r.card);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 纯装配:读机制文件 → 6 条 agent 定义(不触碰 pi / 事件总线,便于探针逐字节比对)。
|
|
106
|
+
* @returns {{name:string,role:string,card:string,description:string,definition:object}[]}
|
|
107
|
+
*/
|
|
108
|
+
export function buildRoleAgentDefinitions({ root, skillsRoot } = {}) {
|
|
109
|
+
const base = root || PKG_ROOT;
|
|
110
|
+
const anchorRoot = skillsRoot || base;
|
|
111
|
+
const roles = parseRolesYaml(readFileSync(join(base, ".agents", "roles.yaml"), "utf8"));
|
|
112
|
+
const anchorLine = buildAnchorLine(anchorRoot);
|
|
113
|
+
return roles.map(({ role, card }) => {
|
|
114
|
+
const raw = readFileSync(join(base, ".agents", "prompts", card), "utf8");
|
|
115
|
+
const { frontmatter } = splitCard(raw);
|
|
116
|
+
const description = frontmatterField(frontmatter, "description");
|
|
117
|
+
return {
|
|
118
|
+
name: `${ROLE_AGENT_PREFIX}${role}`,
|
|
119
|
+
role,
|
|
120
|
+
card,
|
|
121
|
+
description,
|
|
122
|
+
definition: {
|
|
123
|
+
description,
|
|
124
|
+
// BR2 同卡同锚:cardBody 逐字节 + 与 nao-fleet.sh 逐字一致的锚点段(`\n## 环境锚点\n<行>`)
|
|
125
|
+
systemPrompt: `${cardBody(raw)}\n${ENV_ANCHOR_HEADING}\n${anchorLine}`,
|
|
126
|
+
systemPromptMode: "replace",
|
|
127
|
+
inheritProjectContext: true,
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** 经事件总线注册(逐条 try/catch;无 owner 即停)。返回 {registrations, errors},绝不 throw。 */
|
|
134
|
+
export function registerRoleAgents(pi, definitions) {
|
|
135
|
+
const registrations = [];
|
|
136
|
+
const errors = [];
|
|
137
|
+
for (const def of definitions) {
|
|
138
|
+
const request = {
|
|
139
|
+
version: RUNTIME_AGENT_REGISTER_VERSION,
|
|
140
|
+
name: def.name,
|
|
141
|
+
definition: def.definition,
|
|
142
|
+
};
|
|
143
|
+
try {
|
|
144
|
+
pi.events.emit(RUNTIME_AGENT_REGISTER_EVENT, request);
|
|
145
|
+
const result = request.result;
|
|
146
|
+
if (result === undefined) {
|
|
147
|
+
errors.push(`${def.name}: pi-subagents 未安装/未就绪(事件无 owner 响应)`);
|
|
148
|
+
break; // 后续同名合同样无人接管,不刷屏
|
|
149
|
+
}
|
|
150
|
+
if (result.ok === false) throw result.error;
|
|
151
|
+
if (!result.ok || !result.registration || typeof result.registration.dispose !== "function") {
|
|
152
|
+
throw new Error("注册结果格式非法(缺 registration.dispose)");
|
|
153
|
+
}
|
|
154
|
+
registrations.push(result.registration);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
errors.push(`${def.name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return { registrations, errors };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** 释放注册(幂等:其 dispose 自身幂等;异常吞掉不 throw) */
|
|
163
|
+
export function disposeRoleAgents(registrations) {
|
|
164
|
+
for (const reg of registrations || []) {
|
|
165
|
+
try {
|
|
166
|
+
reg.dispose();
|
|
167
|
+
} catch {
|
|
168
|
+
/* 清理失败不得影响会话 */
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* pi 扩展工厂(薄 glue):
|
|
175
|
+
* session_start → 先 dispose 再注册(幂等;NAO_ROLE_AGENTS=off 跳过)
|
|
176
|
+
* session_shutdown → dispose
|
|
177
|
+
* 任何失败仅 warn,绝不 throw(最坏 = 无 agent,不伤会话)。
|
|
178
|
+
*/
|
|
179
|
+
export default function naoRoleAgents(pi) {
|
|
180
|
+
let registrations = [];
|
|
181
|
+
const teardown = () => {
|
|
182
|
+
disposeRoleAgents(registrations);
|
|
183
|
+
registrations = [];
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const onSessionStart = () => {
|
|
187
|
+
teardown(); // BR5 幂等:重复 session_start 净 6 条
|
|
188
|
+
if (String(process.env.NAO_ROLE_AGENTS || "").toLowerCase() === "off") return; // BR6
|
|
189
|
+
try {
|
|
190
|
+
const root = resolveSkillsRoot();
|
|
191
|
+
const definitions = buildRoleAgentDefinitions({ root, skillsRoot: root });
|
|
192
|
+
const outcome = registerRoleAgents(pi, definitions);
|
|
193
|
+
registrations = outcome.registrations;
|
|
194
|
+
for (const err of outcome.errors) warn(`注册失败(仅 warn): ${err}`);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
warn(`装配失败(仅 warn): ${error instanceof Error ? error.message : String(error)}`);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
pi.on("session_start", onSessionStart);
|
|
202
|
+
pi.on("session_shutdown", teardown);
|
|
203
|
+
} catch (error) {
|
|
204
|
+
warn(`生命周期挂载失败(仅 warn): ${error instanceof Error ? error.message : String(error)}`);
|
|
205
|
+
}
|
|
206
|
+
return teardown;
|
|
207
|
+
}
|
|
@@ -1397,6 +1397,34 @@ cmd_check() {
|
|
|
1397
1397
|
shopt -u nullglob
|
|
1398
1398
|
(( orphan )) || echo ' ✓ 无孤儿卡'
|
|
1399
1399
|
|
|
1400
|
+
echo "== pi 扩展声明(T527:声明 + 文件存在,防「声明漏写 ⇒ 静默无 agent」)=="
|
|
1401
|
+
if [[ ! -f "$SKILLS_DIR/package.json" ]]; then
|
|
1402
|
+
# 非包根(legacy 仓内真脚本 / 夹具只有 .agents):无 manifest 可核,跳过不判 FAIL
|
|
1403
|
+
echo ' · package.json 缺失(非包根/legacy:跳过 pi.extensions 校验)'
|
|
1404
|
+
elif ! command -v node >/dev/null 2>&1; then
|
|
1405
|
+
printf ' ✗ node 缺失,无法校验 pi.extensions 声明\n'; rc=1
|
|
1406
|
+
else
|
|
1407
|
+
local ext_list="" ext_bad=0 e
|
|
1408
|
+
if ext_list="$(node -e '
|
|
1409
|
+
const p=require(process.argv[1]); const pi=p.pi||{};
|
|
1410
|
+
const ex=pi.extensions;
|
|
1411
|
+
if(!Array.isArray(ex)||ex.length===0) process.exit(3);
|
|
1412
|
+
process.stdout.write(ex.join("\n"));
|
|
1413
|
+
' "$SKILLS_DIR/package.json" 2>/dev/null)"; then
|
|
1414
|
+
while IFS= read -r e; do
|
|
1415
|
+
[[ -n "$e" ]] || continue
|
|
1416
|
+
if [[ ! -e "$SKILLS_DIR/$e" ]]; then
|
|
1417
|
+
printf ' ✗ pi.extensions 声明条目不存在: %s\n' "$e"; ext_bad=1; rc=1
|
|
1418
|
+
fi
|
|
1419
|
+
done <<< "$ext_list"
|
|
1420
|
+
if (( ext_bad == 0 )); then
|
|
1421
|
+
printf ' ✓ pi.extensions 声明 %d 条且文件存在\n' "$(printf '%s\n' "$ext_list" | grep -c .)"
|
|
1422
|
+
fi
|
|
1423
|
+
else
|
|
1424
|
+
printf ' ✗ pi.extensions 未声明或非法(package.json)\n'; rc=1
|
|
1425
|
+
fi
|
|
1426
|
+
fi
|
|
1427
|
+
|
|
1400
1428
|
echo "== 公共规范 =="
|
|
1401
1429
|
for f in output-format.md intercom-protocol.md; do
|
|
1402
1430
|
if [[ -f "$COMMON_DIR/$f" ]]; then printf ' ✓ %s\n' "$f"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# nao 版子代理桥指令(`intercomBridge.instructionFile`)安装说明
|
|
2
|
+
|
|
3
|
+
本目录是 nao 舰队对「会话内子代理」的口径模板,供 pi-subagents 的 `intercomBridge.instructionFile` 使用。模板只做一件事:把**回执归属 / 身份禁令 / 证据格式**三条 nao 语义注入子代理。协议侧对应条款见 `@.agents/common/intercom-protocol.md`「会话内子代理(授权与回执归属)」。
|
|
4
|
+
|
|
5
|
+
- **本批不写任何用户配置文件**:下面步骤由使用者自行执行;本仓不修改 `~/.pi/...`。
|
|
6
|
+
- **零敏感值 / 零绝对家目录路径**:本目录内所有文档只用 `~/...` 或 `$HOME/...` 形式。
|
|
7
|
+
|
|
8
|
+
## 一、安装(两条命令 + 一段配置)
|
|
9
|
+
|
|
10
|
+
1. 把模板复制到 pi-subagents 的配置目录(目录不存在时先建):
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
mkdir -p ~/.pi/agent/extensions/subagent
|
|
14
|
+
cp <本仓>/.agents/templates/pi-subagents/intercom-bridge.md \
|
|
15
|
+
~/.pi/agent/extensions/subagent/nao-intercom-bridge.md
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
2. 在用户级配置 `~/.pi/agent/extensions/subagent/config.json` 中启用(**相对**路径,不写绝对家目录):
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"intercomBridge": {
|
|
23
|
+
"mode": "always",
|
|
24
|
+
"instructionFile": "nao-intercom-bridge.md",
|
|
25
|
+
"resultDelivery": false
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- `instructionFile` 的相对路径从 `~/.pi/agent/extensions/subagent/` 解析,因此与模板放在同一目录即可。
|
|
31
|
+
- `mode` 取值 `off` / `always` / `fork-only`;nao 场景用 `always`(每次子代理都注入)。
|
|
32
|
+
- `resultDelivery` 保持 `false`:nao 不需要外部监听者 ack 结果。
|
|
33
|
+
|
|
34
|
+
3. 生效检查:在 pi 会话内运行 `/subagents-doctor`,确认 `bridge: active` 且指令来自上述文件。
|
|
35
|
+
|
|
36
|
+
## 二、为什么不用 `{orchestratorTarget}` 占位符
|
|
37
|
+
|
|
38
|
+
pi-subagents 的默认模板支持一个「父会话目标」占位符,但 nao 版**刻意不使用**它:
|
|
39
|
+
|
|
40
|
+
- 父会话目标由 `contact_supervisor` 在子代理运行时**自行解析**(native 通道,不依赖 pi-intercom);把父会话名写进模板反而会把启动契约摘要绑到具体父会话上,父会话一变就可能让启动前置校验失败。
|
|
41
|
+
- 目标名写死还会带来「换个 PM 会话名就失效」的可移植性问题。
|
|
42
|
+
- 因此本模板只写「用 `contact_supervisor` 回父角色」,不出现任何目标占位符。
|
|
43
|
+
|
|
44
|
+
## 三、已知行为与排障
|
|
45
|
+
|
|
46
|
+
- **已知低危行为(本批只记录,不调整)**:pi-subagents 0.78 的默认控制通知通道包含 `intercom`,长跑的会话内子代理偶尔会向父会话**本地注入**一条 control 消息。该消息**不经 broker、不会出现在 `intercom list`**,只占一点父会话上下文。nao 暂不改用户配置里的通知通道;如噪音明显,可在用户配置中收紧通知通道。
|
|
47
|
+
- **模板未生效 / 仍看到默认文案**:确认 `config.json` 的键名与 `mode`;确认文件已复制到 `~/.pi/agent/extensions/subagent/` 且文件名与 `instructionFile` 完全一致。
|
|
48
|
+
- **排障入口**:`/subagents-doctor` 会显示桥是否 active、当前 mode 与目标解析结果;先看它,再怀疑配置。
|
|
49
|
+
- **子代理仍直发 PM / 发 `done`**:这属于越权,说明注入未生效或子代理由外部工具另配了 intercom;父角色仍只认自己的 `done`,不采信子代理消息。
|
|
50
|
+
|
|
51
|
+
## 四、口径归属(唯一事实分工)
|
|
52
|
+
|
|
53
|
+
- **语义**(授权 / 回执归属 / 身份禁令 / 证据格式)在 `.agents/common/intercom-protocol.md`。
|
|
54
|
+
- **注入文本**在本文件的同目录 `intercom-bridge.md`。
|
|
55
|
+
- **安装步骤**在本文件。
|
|
56
|
+
- 机械探针对应 `tests/t4/cases/16-intercom-bridge.sh`(校验模板条款齐备、无目标占位符、配置样例接受/拒绝与协议小节四条齐备)。
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# nao 会话内子代理协作口径
|
|
2
|
+
|
|
3
|
+
你正在作为**会话内子代理**运行:为父角色(nao 舰队角色会话)执行一次有界的调研 / 评审 / 侦察 / 实现工作,并把结果交回父角色。以下三条为硬性口径。
|
|
4
|
+
|
|
5
|
+
## 一、回执归属(硬性)
|
|
6
|
+
|
|
7
|
+
- 子代理的 run 结果只是证据,不等于回执。终态回执 `[编号] done(lite|full)` 只能由父角色在自行核对后经 intercom 发出。
|
|
8
|
+
- PM 只认父角色发出的 `done`。你不得代替父角色回执,也不得把「我完成了」当作对 PM 的交付。
|
|
9
|
+
|
|
10
|
+
## 二、身份与消息禁令(硬性)
|
|
11
|
+
|
|
12
|
+
- 不得以舰队角色名义回执:不得以任一舰队角色(`pm` / `arch-designer` / `rd-fe` / `rd-be` / `qa` / `rd-infra`)的名义说话或回执。
|
|
13
|
+
- 不得发送 `done` 形态的消息;不得直接向 PM 发送任何消息。
|
|
14
|
+
- 需要决策、被阻塞、需要授权或确认范围:用 `contact_supervisor({ reason: "need_decision", message: "<问题>" })` 回父角色;有实质进展或计划变更:用 `contact_supervisor({ reason: "progress_update", message: "UPDATE: <摘要>" })`。
|
|
15
|
+
- `contact_supervisor` 是进程内 native 通道(不依赖 pi-intercom),它会自动解析父会话目标,因此本指令不需要、也不会写死父会话名。
|
|
16
|
+
|
|
17
|
+
## 三、证据格式(硬性)
|
|
18
|
+
|
|
19
|
+
- 最终产物必须给出可供父角色写入回执「测试」栏的精确证据:**命令 + exit code + 例数/文件数(或断言数)+ 路径**。
|
|
20
|
+
- 示例:`bash tests/t4/run.sh` exit=0 · 16/16 PASS · 日志路径 `tests/t4/…`。
|
|
21
|
+
- 没有证据的「完成 / 通过」视为未完成;父角色会据此打回,而不是转成回执。
|
|
22
|
+
|
|
23
|
+
## 四、边界
|
|
24
|
+
|
|
25
|
+
- 你这份注入指令只约束「怎么把结果交回父角色」;任务范围、验收口径、是否允许写入均以父角色本次派发的任务文本为准。
|
|
26
|
+
- 不新建任务状态源:nao 的唯一状态源是 `docs/tasks-state.md`,由 PM / 父角色维护,你不写它。
|
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ nao-skills 是一套 **角色卡 + 协作规矩 + 小工具**。装上以后,
|
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
26
|
cd 你的项目
|
|
27
|
-
pi install --local npm:@nathan33/nao-skill@0.18.
|
|
27
|
+
pi install --local npm:@nathan33/nao-skill@0.18.4 # 声明 pin + 把机制包物化到项目内 .pi/npm
|
|
28
28
|
npx @nathan33/nao-skill init # 写入转发 shim + 生成 AGENTS.md
|
|
29
29
|
```
|
|
30
30
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nathan33/nao-skill",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.5",
|
|
4
4
|
"description": "nao 多角色 AI 开发舰队:角色卡 / 技能 / 协作协议 / 工具链,一键接入项目(pi / Claude / 任意 Agent)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,6 +27,9 @@
|
|
|
27
27
|
"skills": [
|
|
28
28
|
"./.agents/skills/nao-fleet/SKILL.md",
|
|
29
29
|
"./.agents/skills/frontend-design/SKILL.md"
|
|
30
|
+
],
|
|
31
|
+
"extensions": [
|
|
32
|
+
"./.agents/extensions/nao-role-agents.mjs"
|
|
30
33
|
]
|
|
31
34
|
},
|
|
32
35
|
"author": "Nathan Lee",
|