@tyhld/conductor 0.12.94 → 0.12.95
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/dist/relay.js +290 -8
- package/package.json +1 -1
package/dist/relay.js
CHANGED
|
@@ -1064,6 +1064,215 @@ export function takeCodexNotice(site, env = process.env) {
|
|
|
1064
1064
|
}
|
|
1065
1065
|
return latest;
|
|
1066
1066
|
}
|
|
1067
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
1068
|
+
// Codex の最後の返事に書いてもらう「機械で読める囲み」(便 cfa9eeec)
|
|
1069
|
+
//
|
|
1070
|
+
// 【なぜ要るか(実測 2026-10-04・codex-cli 0.160.0・便 f8ac8674 / 4c9f5049)】
|
|
1071
|
+
// Codex の現場へ配る便の末尾が Claude 用のままで、Codex にはできない2つを求めていた:
|
|
1072
|
+
// ① ai-kana の MCP(conductor_complete)… Codex にはつながっていない=「利用可能なツールに無い」。
|
|
1073
|
+
// 毎便その断りが報告に入り、結果(outcome・PR 番号・次の独立作業候補)が管制に入らなかった。
|
|
1074
|
+
// ② 作業フォルダの外(~/.local/state/conductor/reports/)への報告ファイル … 囲いが止める
|
|
1075
|
+
// (Read-only file system)。毎回書こうとして失敗していた。
|
|
1076
|
+
// どちらも「道具を増やす」のではなく、【いま既に通っている道】=Codex の notify で届く
|
|
1077
|
+
// 「最後の返事」に機械で読める囲みを書いてもらい、配り役がそれを読んで報告ファイルを組み立てる。
|
|
1078
|
+
//
|
|
1079
|
+
// 【★受け側(管制)の口は1つも変えない】配り役が書く報告ファイルは、職人が書く物とまったく同じ形
|
|
1080
|
+
// (6欄の囲み+本文)。だから完了の道(completeWithReport)・回収の道(sendPendingReportFiles)・
|
|
1081
|
+
// 形の確かめ(reportDelivery)は1行も変わらない。
|
|
1082
|
+
//
|
|
1083
|
+
// 【★囲みが無い・読めないときは今までと同じ動きに倒す】最後の返事をそのまま報告として渡し、
|
|
1084
|
+
// 便を完了にする(実測で通っていた道を塞がない)。
|
|
1085
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
1086
|
+
/** 囲みの始まりの印(★Codex の便の配達文と同じ字面を使う)。 */
|
|
1087
|
+
export const CODEX_REPORT_BEGIN = '=== CONDUCTOR_REPORT ===';
|
|
1088
|
+
/**
|
|
1089
|
+
* 囲みの終わりの印。
|
|
1090
|
+
* ★`CONDUCTOR_REPORT_END` にしない理由: 画面に報告を出させていた昔の目印(`REPORT_END`)を
|
|
1091
|
+
* 字面で含んでしまい、「古い指示が残っていないか」を見る検査と紛れるため(2026-09-07 に廃止した方式)。
|
|
1092
|
+
*/
|
|
1093
|
+
export const CODEX_REPORT_END = '=== END_CONDUCTOR_REPORT ===';
|
|
1094
|
+
/**
|
|
1095
|
+
* 最後の返事から囲みの中身を切り出す(★印の行は含めない)。見つからなければ null。
|
|
1096
|
+
* - 囲みが閉じていなければ、返事の最後までを囲みの中身として読む(途中で切れた返事を救う)。
|
|
1097
|
+
* - 印の行に飾り(``` ・ ** ・ # ・ 引用の >)が付いていても読む(会話の返事は飾られやすい)。
|
|
1098
|
+
* 返り: inner=囲みの中の行/rest=囲みを外した残り(=報告の本文になる)。
|
|
1099
|
+
*/
|
|
1100
|
+
export function sliceCodexReportBlock(reply) {
|
|
1101
|
+
const lines = String(reply ?? '').replace(/\r/g, '').split('\n');
|
|
1102
|
+
const begin = lines.findIndex((l) => isCodexReportMark(l, 'CONDUCTOR_REPORT'));
|
|
1103
|
+
if (begin < 0)
|
|
1104
|
+
return null;
|
|
1105
|
+
let end = -1;
|
|
1106
|
+
for (let i = begin + 1; i < lines.length; i += 1) {
|
|
1107
|
+
if (isCodexReportMark(lines[i], 'END_CONDUCTOR_REPORT')) {
|
|
1108
|
+
end = i;
|
|
1109
|
+
break;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
const inner = lines.slice(begin + 1, end < 0 ? lines.length : end);
|
|
1113
|
+
const rest = [...lines.slice(0, begin), ...(end < 0 ? [] : lines.slice(end + 1))]
|
|
1114
|
+
.join('\n')
|
|
1115
|
+
.trim();
|
|
1116
|
+
return { inner, rest };
|
|
1117
|
+
}
|
|
1118
|
+
/** 印の行か(飾り・前後の `=` を落として、語そのものと突き合わせる)。 */
|
|
1119
|
+
function isCodexReportMark(line, word) {
|
|
1120
|
+
const bare = String(line ?? '')
|
|
1121
|
+
.trim()
|
|
1122
|
+
.replace(/^[`*#>\s]+/, '')
|
|
1123
|
+
.replace(/[`*\s]+$/, '')
|
|
1124
|
+
.replace(/^=+/, '')
|
|
1125
|
+
.replace(/=+$/, '')
|
|
1126
|
+
.trim()
|
|
1127
|
+
.toUpperCase();
|
|
1128
|
+
return bare === word;
|
|
1129
|
+
}
|
|
1130
|
+
/**
|
|
1131
|
+
* 囲みの中身を欄ごとに読む(★読み方は parseReport の囲みと同じ=「key: 値」と「- 」の箇条書き)。
|
|
1132
|
+
* 囲みが無ければ null。
|
|
1133
|
+
*/
|
|
1134
|
+
export function parseCodexReportBlock(reply) {
|
|
1135
|
+
const block = sliceCodexReportBlock(reply);
|
|
1136
|
+
if (block === null)
|
|
1137
|
+
return null;
|
|
1138
|
+
const fields = new Map();
|
|
1139
|
+
const evidence = [];
|
|
1140
|
+
let lastKey = '';
|
|
1141
|
+
for (const line of block.inner) {
|
|
1142
|
+
const bullet = /^\s*[-*]\s+(.*)$/.exec(line);
|
|
1143
|
+
if (bullet !== null) {
|
|
1144
|
+
if (lastKey === 'evidence' && bullet[1].trim() !== '')
|
|
1145
|
+
evidence.push(bullet[1].trim());
|
|
1146
|
+
continue;
|
|
1147
|
+
}
|
|
1148
|
+
const kv = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*:\s*(.*)$/.exec(line);
|
|
1149
|
+
if (kv === null)
|
|
1150
|
+
continue; // 読めない行は捨てる(欄の解釈を壊さない)
|
|
1151
|
+
lastKey = kv[1].toLowerCase();
|
|
1152
|
+
const value = kv[2].trim();
|
|
1153
|
+
if (lastKey === 'evidence') {
|
|
1154
|
+
if (value !== '')
|
|
1155
|
+
evidence.push(value);
|
|
1156
|
+
continue;
|
|
1157
|
+
}
|
|
1158
|
+
fields.set(lastKey, value);
|
|
1159
|
+
}
|
|
1160
|
+
return {
|
|
1161
|
+
instructionId: nonEmpty(fields.get('instruction_id')),
|
|
1162
|
+
site: nonEmpty(fields.get('site')),
|
|
1163
|
+
outcome: pickOutcome(fields.get('outcome')),
|
|
1164
|
+
createdAt: nonEmpty(fields.get('created_at')),
|
|
1165
|
+
evidence,
|
|
1166
|
+
prNumber: pickPrNumber(fields.get('pr_number')),
|
|
1167
|
+
nextWork: nonEmpty(fields.get('next_work')),
|
|
1168
|
+
rest: block.rest,
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* outcome の語を1つだけ取り出す(★2つ以上書かれていたら「読めない」に倒す)。
|
|
1173
|
+
* 配達文の説明文(「pushed か no_change か blocked のどれか1つ」)をそのまま写した返事を
|
|
1174
|
+
* pushed と読んでしまわないため。
|
|
1175
|
+
*/
|
|
1176
|
+
function pickOutcome(raw) {
|
|
1177
|
+
const text = String(raw ?? '');
|
|
1178
|
+
const hit = REPORT_OUTCOMES.filter((o) => new RegExp(`(^|[^a-z_])${o}([^a-z_]|$)`).test(text));
|
|
1179
|
+
return hit.length === 1 ? hit[0] : null;
|
|
1180
|
+
}
|
|
1181
|
+
/** PR 番号(数字だけ)。「なし」「-」など数字が無ければ null。 */
|
|
1182
|
+
function pickPrNumber(raw) {
|
|
1183
|
+
const m = /(\d{1,7})/.exec(String(raw ?? ''));
|
|
1184
|
+
if (m === null)
|
|
1185
|
+
return null;
|
|
1186
|
+
const n = Number(m[1]);
|
|
1187
|
+
return Number.isSafeInteger(n) && n > 0 ? n : null;
|
|
1188
|
+
}
|
|
1189
|
+
/** 囲みの instruction_id が、いま追っている便の物か(頭だけ書かれていても通す)。 */
|
|
1190
|
+
export function codexReportIdMatches(blockId, commandId) {
|
|
1191
|
+
if (blockId === null)
|
|
1192
|
+
return true; // 書かれていない=配り役が知っている便IDで埋める
|
|
1193
|
+
const got = blockId.trim();
|
|
1194
|
+
if (got === commandId)
|
|
1195
|
+
return true;
|
|
1196
|
+
return got.length >= 8 && commandId.startsWith(got);
|
|
1197
|
+
}
|
|
1198
|
+
/**
|
|
1199
|
+
* 囲みから【職人が書く物とまったく同じ形の報告】を組み立てる。読めなければ null。
|
|
1200
|
+
*
|
|
1201
|
+
* 【★読めたと認める条件】囲みが在る/instruction_id が食い違わない/site が食い違わない/
|
|
1202
|
+
* outcome が3つの語のどれか1つ/evidence が1件以上。
|
|
1203
|
+
* ★instruction_id・site は【配り役が知っている値】で書く(職人の写し間違いで別の便の報告にしない)。
|
|
1204
|
+
* 【created_at が無いとき】配り役が受け取った時刻を入れ、入れたことを本文に1行で書く(作り話にしない)。
|
|
1205
|
+
*/
|
|
1206
|
+
export function buildCodexReportText(reply, opts) {
|
|
1207
|
+
const block = parseCodexReportBlock(reply);
|
|
1208
|
+
if (block === null)
|
|
1209
|
+
return null;
|
|
1210
|
+
if (!codexReportIdMatches(block.instructionId, opts.commandId))
|
|
1211
|
+
return null;
|
|
1212
|
+
if (block.site !== null && block.site !== opts.site)
|
|
1213
|
+
return null;
|
|
1214
|
+
if (block.outcome === null)
|
|
1215
|
+
return null;
|
|
1216
|
+
if (block.evidence.length === 0)
|
|
1217
|
+
return null;
|
|
1218
|
+
const filledAt = block.createdAt === null ? new Date(opts.now ?? Date.now()).toISOString() : null;
|
|
1219
|
+
const head = [
|
|
1220
|
+
'---',
|
|
1221
|
+
`instruction_id: ${opts.commandId}`,
|
|
1222
|
+
`site: ${opts.site}`,
|
|
1223
|
+
`outcome: ${block.outcome}`,
|
|
1224
|
+
`created_at: ${block.createdAt ?? filledAt}`,
|
|
1225
|
+
'evidence:',
|
|
1226
|
+
...block.evidence.map((e) => ` - ${e}`),
|
|
1227
|
+
'---',
|
|
1228
|
+
'',
|
|
1229
|
+
];
|
|
1230
|
+
const body = [
|
|
1231
|
+
'(この報告は、職人の最後の返事の囲みから配り役が組み立てました)',
|
|
1232
|
+
`結末(outcome): ${block.outcome}`,
|
|
1233
|
+
`PR番号: ${block.prNumber === null ? 'なし' : block.prNumber}`,
|
|
1234
|
+
`次の独立作業候補: ${block.nextWork ?? '(囲みに書かれていません)'}`,
|
|
1235
|
+
...(filledAt === null ? [] : [`created_at は囲みに無かったので、配り役が受け取った時刻を入れました`]),
|
|
1236
|
+
'',
|
|
1237
|
+
block.rest,
|
|
1238
|
+
];
|
|
1239
|
+
const text = `${head.join('\n')}${body.join('\n')}`.trim();
|
|
1240
|
+
// ★形が整っていない物は書かない(書きかけを完了の材料にしない・便 461be339 と同じ判定を使う)。
|
|
1241
|
+
return reportShapeMissing(text).length === 0 ? text : null;
|
|
1242
|
+
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Codex の最後の返事の囲みを読み、報告ファイル(職人が書く物と同じ形)を置く。
|
|
1245
|
+
* - 既に報告ファイルが在るときは【上書きしない】(職人が書けた現場を壊さない)。
|
|
1246
|
+
* - 囲みが無い・読めない・書けないときは wrote:false=呼び出し側は今までの動きに倒す。
|
|
1247
|
+
*/
|
|
1248
|
+
export function intakeCodexReport(reply, opts) {
|
|
1249
|
+
const env = opts.env;
|
|
1250
|
+
if (reportPhase(opts.commandId, env) !== 'none') {
|
|
1251
|
+
return { wrote: false, file: null, note: '報告ファイルが既にあるので囲みからは作りません' };
|
|
1252
|
+
}
|
|
1253
|
+
const text = buildCodexReportText(reply, { commandId: opts.commandId, site: opts.site, now: opts.now });
|
|
1254
|
+
if (text === null) {
|
|
1255
|
+
return {
|
|
1256
|
+
wrote: false,
|
|
1257
|
+
file: null,
|
|
1258
|
+
note: '最後の返事に機械で読める囲みが無い(または読めない)ので、返事をそのまま報告として渡します',
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1261
|
+
const file = path.join(reportWriteDir(env), reportFileName(opts.commandId));
|
|
1262
|
+
try {
|
|
1263
|
+
mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
1264
|
+
writeFileSync(file, `${text}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
1265
|
+
}
|
|
1266
|
+
catch (err) {
|
|
1267
|
+
return {
|
|
1268
|
+
wrote: false,
|
|
1269
|
+
file: null,
|
|
1270
|
+
note: '囲みは読めましたが報告ファイルを置けませんでした(返事をそのまま報告として渡します): ' +
|
|
1271
|
+
(err instanceof Error ? err.message : String(err)),
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
return { wrote: true, file, note: `最後の返事の囲みから報告ファイルを組み立てました: ${file}` };
|
|
1275
|
+
}
|
|
1067
1276
|
export function sliceForDoneScan(text, anchor, wide) {
|
|
1068
1277
|
const below = sliceBelowAnchor(text, anchor);
|
|
1069
1278
|
if (below !== null)
|
|
@@ -1243,8 +1452,9 @@ export function buildSendText(body, site, anchor, kind = 'claude', context) {
|
|
|
1243
1452
|
? anchor.slice(SEND_ANCHOR_PREFIX.length + 1)
|
|
1244
1453
|
: anchor;
|
|
1245
1454
|
// ★Claude の職人には「便の終え方」の1行だけを付ける(書き方の正本は鉄則の「便の終え方」の節)。
|
|
1246
|
-
// ★Codex
|
|
1247
|
-
|
|
1455
|
+
// ★Codex の職人には Codex にできることだけを頼む全文を付ける(便 cfa9eeec・codexCompletionGuide)。
|
|
1456
|
+
// 鉄則(~/.claude/skills)は Codex には届かないので、全文で渡すのは今までどおり。
|
|
1457
|
+
const guide = kind === 'codex' ? codexCompletionGuide(site, commandId) : shortCompletionLine(site, commandId);
|
|
1248
1458
|
const full = `${body}\n\n${dispatchContextBlock(context)}${guide}` +
|
|
1249
1459
|
// アンカー(管理用ジョブ印)は必ず本文の最後に置く。完了検出の境界として使う。
|
|
1250
1460
|
`(管理用ジョブ印・変更不要: ${anchor})`;
|
|
@@ -1290,9 +1500,14 @@ export function shortCompletionLine(site, commandId) {
|
|
|
1290
1500
|
`完了の知らせ: conductor_complete を commandId="${commandId}" で1回だけ(nextWork は必ず書く)。`);
|
|
1291
1501
|
}
|
|
1292
1502
|
/**
|
|
1293
|
-
*
|
|
1294
|
-
*
|
|
1295
|
-
*
|
|
1503
|
+
* 便の終え方の【全文=正本の物差し】。
|
|
1504
|
+
*
|
|
1505
|
+
* ★この全文は、もう誰にも送らない(便 cfa9eeec)。本物は次の2つ:
|
|
1506
|
+
* ・Claude の職人 … 1行(shortCompletionLine)+鉄則の「便の終え方」の節
|
|
1507
|
+
* ・Codex の職人 … Codex にできることだけを頼む全文(codexCompletionGuide)
|
|
1508
|
+
* ★残してある理由: 「寄せる前の決まりを1つも失っていない」ことを測る物差しだから
|
|
1509
|
+
* (試験 relay-finish-guide-equivalence が、この全文と「1行+鉄則の節」を1つずつ突き合わせる)。
|
|
1510
|
+
* ここに書いてある決まりは、鉄則の「便の終え方」の節と中身を同じに保つ。
|
|
1296
1511
|
*/
|
|
1297
1512
|
export function fullCompletionGuide(site, commandId) {
|
|
1298
1513
|
const marker = `DONE:${site}`;
|
|
@@ -1348,6 +1563,60 @@ export function fullCompletionGuide(site, commandId) {
|
|
|
1348
1563
|
// ★nextWork(次の独立作業候補)は必ず書く(conductor_complete の説明と同じ・便 a5306df2 で両方にそろえた)。
|
|
1349
1564
|
`nextWork(次の独立作業候補)は必ず書いてください(無ければ「なし」と理由)。\n\n`);
|
|
1350
1565
|
}
|
|
1566
|
+
/**
|
|
1567
|
+
* Codex の職人へ付ける完了案内(便 cfa9eeec・★Claude 向けは shortCompletionLine)。
|
|
1568
|
+
*
|
|
1569
|
+
* 【なぜ分けたか(実測 2026-10-04・codex-cli 0.160.0・便 f8ac8674 / 4c9f5049)】
|
|
1570
|
+
* Claude 用の全文は、Codex にできない2つを求めていた=毎便、無駄な失敗になっていた:
|
|
1571
|
+
* ① ai-kana の MCP(完了の知らせの道具)… Codex にはつながっていない=呼べない。
|
|
1572
|
+
* ② 作業フォルダの外への報告ファイル … 囲いが止める(Read-only file system)。
|
|
1573
|
+
* どちらも頼まない。かわりに【いま既に通っている道】=Codex の notify で届く「最後の返事」に、
|
|
1574
|
+
* 機械で読める囲みを書いてもらう。配り役がそれを読んで、職人が書く物と同じ形の報告ファイルを
|
|
1575
|
+
* 組み立て、いまの完了の道へそのまま流す(intakeCodexReport・受け側の口は1つも変えない)。
|
|
1576
|
+
*
|
|
1577
|
+
* 【★.git への書き込み(実測の違い)】便 f8ac8674 は「止められたら回避せず報告」だけを書いていたので、
|
|
1578
|
+
* Codex は承認を求めずに blocked で終えた。便 4c9f5049 で「正規の手順で承認を1回だけ求めてよい」と
|
|
1579
|
+
* 書いたら、承認されて記録(コミット 04c108f)まで通った。その1〜2文をここに入れておく。
|
|
1580
|
+
*
|
|
1581
|
+
* 【★この文に入れない物(試験で固定)】完了の知らせの道具の名前・報告ファイルの置き場。
|
|
1582
|
+
* どちらも Codex にはできない=書くと毎便失敗して報告が汚れる。
|
|
1583
|
+
*/
|
|
1584
|
+
export function codexCompletionGuide(site, commandId) {
|
|
1585
|
+
const marker = `DONE:${site}`;
|
|
1586
|
+
return (
|
|
1587
|
+
// ★確認カードを非エンジニアが読める日本語にする(調査 2987cb8f)。Codex でも確認は同じ形で出る。
|
|
1588
|
+
`作業中にユーザー確認(許可を求めるプロンプト)が出る操作では、その一言説明(description)を` +
|
|
1589
|
+
`英語ではなく日本語で、「プルリクエストを作成する」「テストを実行する」のように` +
|
|
1590
|
+
`「何をするか」が一目で分かる簡潔な形(〜を作成する/実行する/削除する 等)で書いてください` +
|
|
1591
|
+
`(この説明は管制の確認カードにそのまま表示され、非エンジニアのお客さまが読んで許可を判断します)。\n\n` +
|
|
1592
|
+
// ★記録(.git への書き込み)が囲いに止められたときの進み方(実測 f8ac8674 → 4c9f5049)。
|
|
1593
|
+
`記録(git add・git commit)が囲い(サンドボックス)に止められたときは、正規の手順で承認を1回だけ求めてよいです。` +
|
|
1594
|
+
`断られた・求める手段が無かったときは、別のやり方(設定の書き換え・囲いを外す起動・.git を別の場所に作る 等)で` +
|
|
1595
|
+
`通そうとせず、そのまま報告して終えてください。\n\n` +
|
|
1596
|
+
// ★報告は「最後の返事」に書いてもらう(作業フォルダの外へは書けない=囲いが止める)。
|
|
1597
|
+
`作業がすべて完了したら、報告を【この会話の最後の返事】に書いてください` +
|
|
1598
|
+
`(変更ファイル・要点・チェック結果を簡潔に)。` +
|
|
1599
|
+
`★報告を作業フォルダの外のファイルへ書こうとしないでください(囲いが止めます)。\n` +
|
|
1600
|
+
`★最後の返事の先頭に、次の囲みをそのまま付けてください(ここが機械の読み口です。1行に1項目・左の語と「: 」は変えない)。\n` +
|
|
1601
|
+
`1行目: ${CODEX_REPORT_BEGIN}\n` +
|
|
1602
|
+
`2行目: instruction_id: ${commandId}\n` +
|
|
1603
|
+
`3行目: site: ${site}\n` +
|
|
1604
|
+
`4行目: outcome: pushed / no_change / blocked のどれか1つだけを書く` +
|
|
1605
|
+
`(pushed=変更して送った、no_change=変更不要だった、blocked=衝突・要判断・許可待ち等で完了できなかった。★選んだ1語だけを書く)\n` +
|
|
1606
|
+
`5行目: created_at: 書いた日時(例 2026-10-04T18:00:00+09:00)\n` +
|
|
1607
|
+
`6行目: evidence:(この行は「evidence:」だけにして、次の行から「- 」で始めて1行に1つ書く。` +
|
|
1608
|
+
`例「- PR https://github.com/...」「- CI test pass」「- main に入った」。★1件以上必ず書く)\n` +
|
|
1609
|
+
`次の行: pr_number: PR 番号(数字だけ。無ければ なし)\n` +
|
|
1610
|
+
`次の行: next_work: 次の独立作業候補(無ければ「なし」と、なぜ無いかの理由)\n` +
|
|
1611
|
+
`最後の行: ${CODEX_REPORT_END}\n` +
|
|
1612
|
+
`この囲みの下に、いつもどおりの報告本文を書いてください(本文が空だと未完成と見なされます)。\n` +
|
|
1613
|
+
`そして返事のいちばん最後の行に ${marker} とだけ出力してください。\n\n` +
|
|
1614
|
+
// ★道具は呼ばせない(Codex にはつながっていない)。囲みが書けなかったときの倒れ方も書いておく。
|
|
1615
|
+
`★完了の知らせと、管制へ結果(outcome・PR 番号・次の独立作業候補)を入れる作業は、配り役が行います。` +
|
|
1616
|
+
`あなたは上の囲みと ${marker} を書くだけでよく、道具(MCP)を呼ぶ必要はありません。` +
|
|
1617
|
+
`囲みが書けなかったときも、いつもどおり最後の返事に報告を書いて ${marker} で終えてください` +
|
|
1618
|
+
`(配り役がその返事をそのまま報告として受け取ります)。\n\n`);
|
|
1619
|
+
}
|
|
1351
1620
|
// ───────────────────────────────────────────────────────────────────────────
|
|
1352
1621
|
// tmux I/O(失敗は握りつぶして null/false。送らない側に倒すため)
|
|
1353
1622
|
// ───────────────────────────────────────────────────────────────────────────
|
|
@@ -3775,9 +4044,21 @@ export async function relayTick(cfg, state, hooks) {
|
|
|
3775
4044
|
if (kindOf(cfg) === 'codex') {
|
|
3776
4045
|
const notice = takeCodexNotice(cfg.site);
|
|
3777
4046
|
if (notice !== null) {
|
|
4047
|
+
// ★最後の返事の囲みを読み、職人が書く物と同じ形の報告ファイルを組み立てる(便 cfa9eeec)。
|
|
4048
|
+
// 書けたら【いまの完了の道】へそのまま流す(受け側の口は1つも変えない)。
|
|
4049
|
+
// 囲みが無い・読めないときは、今までと同じ動き(返事をそのまま報告にして完了)に倒す。
|
|
4050
|
+
const intake = intakeCodexReport(notice.report, {
|
|
4051
|
+
commandId: state.pending.id,
|
|
4052
|
+
site: cfg.site,
|
|
4053
|
+
});
|
|
4054
|
+
log(`site=${cfg.site} id=${state.pending.id} ${intake.note}`);
|
|
3778
4055
|
const report = notice.report.trim().length > 0 ? notice.report : null;
|
|
3779
|
-
|
|
3780
|
-
|
|
4056
|
+
const written = intake.wrote
|
|
4057
|
+
? await completeWithReport(cfg, state.pending.id, log)
|
|
4058
|
+
: await patchStatus(cfg, state.pending.id, 'done', report);
|
|
4059
|
+
if (written) {
|
|
4060
|
+
log(`site=${cfg.site} id=${state.pending.id} done(Codexの通知` +
|
|
4061
|
+
`${intake.wrote ? '・囲みから報告ファイル' : report != null ? '・報告添付' : ''})`);
|
|
3781
4062
|
// ★終了の事実を同じ項目で届ける(便 915694ac・1-3)。この配り方では実プロセスを
|
|
3782
4063
|
// 中継が持っていないので、終わりは【つかめていない】と伝える(終了コードは null)。
|
|
3783
4064
|
// 届けるのは「中継が完了を確かめた」という事実だけ。★作り話の終了を送らない。
|
|
@@ -3790,7 +4071,8 @@ export async function relayTick(cfg, state, hooks) {
|
|
|
3790
4071
|
exitSignal: null,
|
|
3791
4072
|
reportDelivered: report != null || reportDelivery(state.pending.id).delivered,
|
|
3792
4073
|
processEndObserved: false,
|
|
3793
|
-
detail: '配り役が完了の知らせで確かめた(職人の処理は中継が持っていない)'
|
|
4074
|
+
detail: '配り役が完了の知らせで確かめた(職人の処理は中継が持っていない)' +
|
|
4075
|
+
(intake.wrote ? '/最後の返事の囲みから報告ファイルを組み立てた' : ''),
|
|
3794
4076
|
}, { logLine: log });
|
|
3795
4077
|
state.pending = null;
|
|
3796
4078
|
}
|