aiterm-mcp 0.21.3 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +23 -13
- package/README.md +37 -26
- package/dist/core.js +404 -266
- package/dist/index.js +18 -10
- package/dist/runtime-error-store.js +8 -4
- package/package.json +1 -1
package/dist/core.js
CHANGED
|
@@ -11,7 +11,7 @@ import { spawnSync } from "node:child_process";
|
|
|
11
11
|
import * as fs from "node:fs";
|
|
12
12
|
import * as path from "node:path";
|
|
13
13
|
import * as os from "node:os";
|
|
14
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
14
|
+
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
15
15
|
import { fileURLToPath } from "node:url";
|
|
16
16
|
import * as rtk from "./rtk.js";
|
|
17
17
|
import { recordRuntimeError } from "./runtime-error-store.js";
|
|
@@ -48,6 +48,8 @@ const NON_POSIX_MARK_SHELLS = new Set(["fish", "csh", "tcsh"]);
|
|
|
48
48
|
const MARK_DONE_RE = /<<<AITERM_DONE rc=[0-9]+>>>/;
|
|
49
49
|
const LAUNCH_ID_RE = /^[0-9a-f]{32}$/;
|
|
50
50
|
const OPERATION_ID_RE = /^sha256:[0-9a-f]{64}$/;
|
|
51
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
52
|
+
const AGENT_LINEAGE_RE = /^[A-Za-z0-9_.:-]+(?:>[A-Za-z0-9_.:-]+)*$/;
|
|
51
53
|
const AGENT_DONE_POLL_MS = 100;
|
|
52
54
|
const AGENT_DONE_SETTLE_MIN_MS = 250;
|
|
53
55
|
const AGENT_DONE_SCREEN_SETTLE_POLL_MS = 100;
|
|
@@ -57,6 +59,7 @@ const AGENT_SUBMIT_DELAY_MS = 250;
|
|
|
57
59
|
const AGENT_EVENT_MAX_BYTES = 1024 * 1024;
|
|
58
60
|
const AGENT_EVENT_TAIL_BYTES = 64 * 1024;
|
|
59
61
|
const CODEX_TRANSCRIPT_INCREMENT_MAX_BYTES = 16 * 1024 * 1024;
|
|
62
|
+
const GROK_TRANSCRIPT_INCREMENT_MAX_BYTES = 16 * 1024 * 1024;
|
|
60
63
|
const AGENT_METADATA_NEGATIVE_CACHE_TTL_MS = 2_000;
|
|
61
64
|
const AGENT_TUI_READY_TIMEOUT_MS = 30_000;
|
|
62
65
|
const AGENT_TUI_READY_POLL_MS = 500;
|
|
@@ -437,24 +440,6 @@ function agentClaudeDispatchReceiptPath(name, launchId, operationId) {
|
|
|
437
440
|
const validated = validateOperationId(operationId);
|
|
438
441
|
return path.join(agentsDir(), `${name}.${launchId}.${validated.slice("sha256:".length)}.claude-dispatch`);
|
|
439
442
|
}
|
|
440
|
-
function agentManagedCodexHomePath(name, launchId) {
|
|
441
|
-
assertSessionName(name);
|
|
442
|
-
if (!LAUNCH_ID_RE.test(launchId))
|
|
443
|
-
throw new AitermError(`launch_id が不正です: ${launchId}`, 2);
|
|
444
|
-
return path.join(agentsDir(), `${name}.${launchId}.codex-home`);
|
|
445
|
-
}
|
|
446
|
-
function agentManagedGrokHomePath(name, launchId) {
|
|
447
|
-
assertSessionName(name);
|
|
448
|
-
if (!LAUNCH_ID_RE.test(launchId))
|
|
449
|
-
throw new AitermError(`launch_id が不正です: ${launchId}`, 2);
|
|
450
|
-
return path.join(agentsDir(), `${name}.${launchId}.grok-home`);
|
|
451
|
-
}
|
|
452
|
-
function agentManagedGrokUserHomePath(name, launchId) {
|
|
453
|
-
assertSessionName(name);
|
|
454
|
-
if (!LAUNCH_ID_RE.test(launchId))
|
|
455
|
-
throw new AitermError(`launch_id が不正です: ${launchId}`, 2);
|
|
456
|
-
return path.join(agentsDir(), `${name}.${launchId}.home`);
|
|
457
|
-
}
|
|
458
443
|
function existingAgentsDir() {
|
|
459
444
|
if (typeof process.getuid !== "function")
|
|
460
445
|
return null;
|
|
@@ -494,6 +479,7 @@ function cleanupAgentState(name) {
|
|
|
494
479
|
f.endsWith(".events.jsonl") ||
|
|
495
480
|
f.endsWith(".wait.lock") ||
|
|
496
481
|
f.endsWith(".claude-settings.json") ||
|
|
482
|
+
f.endsWith(".claude-mcp.json") ||
|
|
497
483
|
f.endsWith(".claude-result.json") ||
|
|
498
484
|
f.endsWith(".claude-operation.json") ||
|
|
499
485
|
f.endsWith(".claude-approval.json") ||
|
|
@@ -953,7 +939,7 @@ function assertManagedClaudeCredentialCommandNotSent(name, text) {
|
|
|
953
939
|
const normalized = text.replace(PASTE_MARKERS_RE, "").replace(ANSI_RE, "").replace(CTRL_RE, "").trim();
|
|
954
940
|
if (!/^\/(?:login|logout)$/i.test(normalized))
|
|
955
941
|
return;
|
|
956
|
-
throw new AitermError("
|
|
942
|
+
throw new AitermError("aiterm相関付きClaude sessionでは共有認証を変更する /login と /logout を送信できません。" +
|
|
957
943
|
"認証操作は通常端末で一度だけ行い、必要ならこのsessionをcloseして起動し直してください。", 2);
|
|
958
944
|
}
|
|
959
945
|
export function send(name, text, o = {}) {
|
|
@@ -979,7 +965,7 @@ export function send(name, text, o = {}) {
|
|
|
979
965
|
// 拒否した呼び出しが古いmarkを消したり偽のmarkを残すと、後続readが存在しない
|
|
980
966
|
// sentinelを待つため、公開上の拒否は副作用ゼロでなければならない。
|
|
981
967
|
if (!o.preserveAgentOperation && managedClaudeOperation(name) !== undefined) {
|
|
982
|
-
throw new AitermError("
|
|
968
|
+
throw new AitermError("aiterm相関付きClaude agent sessionへの通常送信はturn境界を失うため拒否します。" +
|
|
983
969
|
"pty_send(forceなし=自動dispatch)を使うか、通常対話へ切り替えるならsessionをcloseしてpty_openから手動起動し直してください。", 2);
|
|
984
970
|
}
|
|
985
971
|
writeLastcmd(name, text); // read rtk の reducer 分類用(書換/mark 前の素のコマンド)
|
|
@@ -1064,7 +1050,7 @@ export function sendKey(name, key, o = {}) {
|
|
|
1064
1050
|
throw new AitermError(`session '${name}' が無い`, 2);
|
|
1065
1051
|
const k = KEYMAP[key.toLowerCase()] ?? key;
|
|
1066
1052
|
if (!o.preserveAgentOperation && managedClaudeOperation(name) !== undefined && k !== "C-c") {
|
|
1067
|
-
throw new AitermError("
|
|
1053
|
+
throw new AitermError("aiterm相関付きClaude agent sessionではturn相関を壊さないC-cだけをpty_keyで送れます。" +
|
|
1068
1054
|
"他の対話操作はpty_send(自動dispatch)、終了はpty_closeを使ってください。", 2);
|
|
1069
1055
|
}
|
|
1070
1056
|
tmux("send-keys", "-t", name, k);
|
|
@@ -1354,6 +1340,7 @@ export function killAll() {
|
|
|
1354
1340
|
f.endsWith(".events.jsonl") ||
|
|
1355
1341
|
f.endsWith(".wait.lock") ||
|
|
1356
1342
|
f.endsWith(".claude-settings.json") ||
|
|
1343
|
+
f.endsWith(".claude-mcp.json") ||
|
|
1357
1344
|
f.endsWith(".claude-result.json") ||
|
|
1358
1345
|
f.endsWith(".claude-operation.json") ||
|
|
1359
1346
|
f.endsWith(".claude-dispatch") ||
|
|
@@ -1376,12 +1363,80 @@ export function killAll() {
|
|
|
1376
1363
|
agentMetadataNegativeCache.clear();
|
|
1377
1364
|
return "killed all sessions on this socket";
|
|
1378
1365
|
}
|
|
1366
|
+
function readAgentLineageSeed() {
|
|
1367
|
+
const role = process.env.AITERM_AGENT_ROLE;
|
|
1368
|
+
const session = process.env.AITERM_AGENT_SESSION_ID;
|
|
1369
|
+
const depthRaw = process.env.AITERM_AGENT_DEPTH;
|
|
1370
|
+
const lineage = process.env.AITERM_AGENT_LINEAGE;
|
|
1371
|
+
const delegationAllowed = process.env.AITERM_AGENT_DELEGATION_ALLOWED;
|
|
1372
|
+
const nested = [role, session, depthRaw, lineage, delegationAllowed].some((value) => value !== undefined);
|
|
1373
|
+
if (!nested) {
|
|
1374
|
+
return { parentSessionId: "host-root", delegationDepth: 1, lineagePrefix: "host-root" };
|
|
1375
|
+
}
|
|
1376
|
+
if (role !== "subagent" ||
|
|
1377
|
+
delegationAllowed !== "true" ||
|
|
1378
|
+
!session ||
|
|
1379
|
+
!/^[A-Za-z0-9_-]{1,64}$/.test(session) ||
|
|
1380
|
+
!depthRaw ||
|
|
1381
|
+
!/^\d+$/.test(depthRaw) ||
|
|
1382
|
+
!lineage ||
|
|
1383
|
+
lineage.length > 4096 ||
|
|
1384
|
+
!AGENT_LINEAGE_RE.test(lineage)) {
|
|
1385
|
+
throw new AitermError("継承したAITERM sub-agent lineage環境が不正です", 2);
|
|
1386
|
+
}
|
|
1387
|
+
const parentDepth = Number(depthRaw);
|
|
1388
|
+
if (!Number.isSafeInteger(parentDepth) || parentDepth < 1 || parentDepth >= 1_000_000) {
|
|
1389
|
+
throw new AitermError("継承したAITERM_AGENT_DEPTHが不正です", 2);
|
|
1390
|
+
}
|
|
1391
|
+
return { parentSessionId: session, delegationDepth: parentDepth + 1, lineagePrefix: lineage };
|
|
1392
|
+
}
|
|
1393
|
+
function createAgentLineageContext(kind, sessionId, seed) {
|
|
1394
|
+
const lineage = `${seed.lineagePrefix}>${kind}:${sessionId}`;
|
|
1395
|
+
if (lineage.length > 4096 || !AGENT_LINEAGE_RE.test(lineage)) {
|
|
1396
|
+
throw new AitermError("AITERM sub-agent lineageが上限または形式に違反しました", 2);
|
|
1397
|
+
}
|
|
1398
|
+
return {
|
|
1399
|
+
agentRole: "subagent",
|
|
1400
|
+
parentSessionId: seed.parentSessionId,
|
|
1401
|
+
delegationDepth: seed.delegationDepth,
|
|
1402
|
+
lineage,
|
|
1403
|
+
delegationAllowed: true,
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
function agentLineageFields(context) {
|
|
1407
|
+
return {
|
|
1408
|
+
agent_role: context.agentRole,
|
|
1409
|
+
parent_session_id: context.parentSessionId,
|
|
1410
|
+
delegation_depth: context.delegationDepth,
|
|
1411
|
+
lineage: context.lineage,
|
|
1412
|
+
delegation_allowed: context.delegationAllowed,
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
function subagentInstruction(meta) {
|
|
1416
|
+
if (meta.agent_role !== "subagent" ||
|
|
1417
|
+
!meta.parent_session_id ||
|
|
1418
|
+
!Number.isSafeInteger(meta.delegation_depth) ||
|
|
1419
|
+
!meta.lineage ||
|
|
1420
|
+
meta.delegation_allowed !== true) {
|
|
1421
|
+
throw new AitermError("sub-agent instructionに必要なlineage metadataがありません", 2);
|
|
1422
|
+
}
|
|
1423
|
+
return [
|
|
1424
|
+
"<aiterm_subagent_context>",
|
|
1425
|
+
"あなたはaitermから起動されたsub-agentであり、root agentではありません。",
|
|
1426
|
+
`AITERM_AGENT_LAUNCH_ID=${meta.launch_id}`,
|
|
1427
|
+
`role=${meta.agent_role}`,
|
|
1428
|
+
`parent_session_id=${meta.parent_session_id}`,
|
|
1429
|
+
`delegation_depth=${meta.delegation_depth}`,
|
|
1430
|
+
`lineage=${meta.lineage}`,
|
|
1431
|
+
"delegation_allowed=true",
|
|
1432
|
+
"任務の所有権を保ち、結果を親へ返してください。必要なら追加のsub-agentへ委譲してよいです。",
|
|
1433
|
+
"ただし、同じ任務全体を同型agentへ反射的に丸投げして自己複製ループを作らないでください。",
|
|
1434
|
+
"</aiterm_subagent_context>",
|
|
1435
|
+
].join("\n");
|
|
1436
|
+
}
|
|
1379
1437
|
const DEFAULT_AGENT_DONE_TIMEOUT = 600;
|
|
1380
1438
|
const agentMetadataNegativeCache = new Map();
|
|
1381
1439
|
let agentTuiReadyStableSamplesTestOverride = null;
|
|
1382
|
-
function grokHookScriptPath() {
|
|
1383
|
-
return path.join(path.dirname(fileURLToPath(import.meta.url)), "grok-stop-hook.js");
|
|
1384
|
-
}
|
|
1385
1440
|
function claudeHookScriptPath() {
|
|
1386
1441
|
return path.join(path.dirname(fileURLToPath(import.meta.url)), "claude-stop-hook.js");
|
|
1387
1442
|
}
|
|
@@ -1464,36 +1519,6 @@ function createEmpty0600NoFollow(p) {
|
|
|
1464
1519
|
function realCodexHome() {
|
|
1465
1520
|
return process.env.CODEX_HOME || path.join(process.env.HOME ?? os.homedir(), ".codex");
|
|
1466
1521
|
}
|
|
1467
|
-
// 引数で model/effort を渡された時は managed config のピンを上書きする(端末 config の
|
|
1468
|
-
// model/model_reasoning_effort ピン——例: effort=ultra は proactive 自動委譲 ON——が対話子へ
|
|
1469
|
-
// 黙って波及するのを防ぐ)。TOML の top-level キーは最初のテーブルヘッダより前にしか置けないため、
|
|
1470
|
-
// 既存行の除去は先頭領域に限定し、上書き行はファイル先頭へ置く。
|
|
1471
|
-
function applyCodexConfigOverrides(body, overrides) {
|
|
1472
|
-
const lines = [];
|
|
1473
|
-
if (overrides.model)
|
|
1474
|
-
lines.push(`model = ${JSON.stringify(overrides.model)}`);
|
|
1475
|
-
if (overrides.effort)
|
|
1476
|
-
lines.push(`model_reasoning_effort = ${JSON.stringify(overrides.effort)}`);
|
|
1477
|
-
if (!lines.length)
|
|
1478
|
-
return body;
|
|
1479
|
-
if (body == null)
|
|
1480
|
-
return lines.join("\n") + "\n";
|
|
1481
|
-
const rows = body.split(/\r?\n/);
|
|
1482
|
-
let firstTable = rows.findIndex((l) => /^\s*\[/.test(l));
|
|
1483
|
-
if (firstTable === -1)
|
|
1484
|
-
firstTable = rows.length;
|
|
1485
|
-
const head = rows.slice(0, firstTable).filter((l) => {
|
|
1486
|
-
if (overrides.model && /^\s*(?:"model"|'model'|model)\s*=/.test(l))
|
|
1487
|
-
return false;
|
|
1488
|
-
if (overrides.effort && /^\s*(?:"model_reasoning_effort"|'model_reasoning_effort'|model_reasoning_effort)\s*=/.test(l))
|
|
1489
|
-
return false;
|
|
1490
|
-
return true;
|
|
1491
|
-
});
|
|
1492
|
-
let out = [...lines, ...head, ...rows.slice(firstTable)].join("\n");
|
|
1493
|
-
if (!out.endsWith("\n"))
|
|
1494
|
-
out += "\n";
|
|
1495
|
-
return out;
|
|
1496
|
-
}
|
|
1497
1522
|
function readCodexConfigPins(configPath) {
|
|
1498
1523
|
let body;
|
|
1499
1524
|
try {
|
|
@@ -1518,7 +1543,7 @@ function readCodexConfigPins(configPath) {
|
|
|
1518
1543
|
};
|
|
1519
1544
|
return { model: pick("model"), effort: pick("model_reasoning_effort") };
|
|
1520
1545
|
}
|
|
1521
|
-
function
|
|
1546
|
+
function codexConfigSummary(configPath) {
|
|
1522
1547
|
let body;
|
|
1523
1548
|
try {
|
|
1524
1549
|
body = fs.readFileSync(configPath, "utf8");
|
|
@@ -1547,93 +1572,9 @@ function managedCodexConfigSummary(configPath) {
|
|
|
1547
1572
|
bits.push(`approval_policy=${approvalPolicy}`);
|
|
1548
1573
|
if (sandboxMode)
|
|
1549
1574
|
bits.push(`sandbox_mode=${sandboxMode}`);
|
|
1550
|
-
return
|
|
1551
|
-
}
|
|
1552
|
-
// Custom agent definitions are configuration, not mutable Codex state. Copy only direct
|
|
1553
|
-
// agents/*.toml entries into the per-launch home so role discovery matches the source home while
|
|
1554
|
-
// sessions/cache remain isolated. Source symlinks are resolved and their contents are snapshotted;
|
|
1555
|
-
// the managed home never points back to the source definition.
|
|
1556
|
-
function snapshotCodexAgentDefinitions(srcHome, managedHome) {
|
|
1557
|
-
const srcDir = path.join(srcHome, "agents");
|
|
1558
|
-
let srcDirSt;
|
|
1559
|
-
try {
|
|
1560
|
-
srcDirSt = fs.statSync(srcDir);
|
|
1561
|
-
}
|
|
1562
|
-
catch (e) {
|
|
1563
|
-
if (e.code === "ENOENT")
|
|
1564
|
-
return;
|
|
1565
|
-
throw e;
|
|
1566
|
-
}
|
|
1567
|
-
if (!srcDirSt.isDirectory()) {
|
|
1568
|
-
throw new AitermError(`Codex agents が directory ではありません: ${srcDir}`, 2);
|
|
1569
|
-
}
|
|
1570
|
-
const names = fs.readdirSync(srcDir).filter((name) => name.endsWith(".toml")).sort();
|
|
1571
|
-
if (!names.length)
|
|
1572
|
-
return;
|
|
1573
|
-
const dstDir = path.join(managedHome, "agents");
|
|
1574
|
-
fs.mkdirSync(dstDir, { mode: 0o700 });
|
|
1575
|
-
fs.chmodSync(dstDir, 0o700);
|
|
1576
|
-
for (const name of names) {
|
|
1577
|
-
const src = path.join(srcDir, name);
|
|
1578
|
-
const resolved = fs.realpathSync(src);
|
|
1579
|
-
const st = fs.statSync(resolved);
|
|
1580
|
-
if (!st.isFile()) {
|
|
1581
|
-
throw new AitermError(`Codex agent definition が通常ファイルではありません: ${src}`, 2);
|
|
1582
|
-
}
|
|
1583
|
-
writeText0600(path.join(dstDir, name), fs.readFileSync(resolved, "utf8"));
|
|
1584
|
-
}
|
|
1585
|
-
}
|
|
1586
|
-
function createManagedCodexHome(name, launchId, overrides = {}) {
|
|
1587
|
-
const srcHome = realCodexHome();
|
|
1588
|
-
let srcSt;
|
|
1589
|
-
try {
|
|
1590
|
-
srcSt = fs.statSync(srcHome);
|
|
1591
|
-
}
|
|
1592
|
-
catch {
|
|
1593
|
-
throw new AitermError(`Codex home が見つかりません: ${srcHome}`, 2);
|
|
1594
|
-
}
|
|
1595
|
-
if (!srcSt.isDirectory())
|
|
1596
|
-
throw new AitermError(`Codex home が directory ではありません: ${srcHome}`, 2);
|
|
1597
|
-
const managedHome = agentManagedCodexHomePath(name, launchId);
|
|
1598
|
-
fs.mkdirSync(managedHome, { recursive: false, mode: 0o700 });
|
|
1599
|
-
fs.chmodSync(managedHome, 0o700);
|
|
1600
|
-
const authSrc = path.join(srcHome, "auth.json");
|
|
1601
|
-
try {
|
|
1602
|
-
const st = fs.statSync(authSrc);
|
|
1603
|
-
if (!st.isFile())
|
|
1604
|
-
throw new AitermError(`Codex auth.json が通常ファイルではありません: ${authSrc}`, 2);
|
|
1605
|
-
}
|
|
1606
|
-
catch (e) {
|
|
1607
|
-
if (e.code === "ENOENT") {
|
|
1608
|
-
throw new AitermError(`Codex auth.json が見つかりません。先に codex login が必要です: ${srcHome}`, 2);
|
|
1609
|
-
}
|
|
1610
|
-
throw e;
|
|
1611
|
-
}
|
|
1612
|
-
fs.symlinkSync(authSrc, path.join(managedHome, "auth.json"));
|
|
1613
|
-
const configSrc = path.join(srcHome, "config.toml");
|
|
1614
|
-
let configBody = null;
|
|
1615
|
-
try {
|
|
1616
|
-
const st = fs.statSync(configSrc);
|
|
1617
|
-
if (st.isFile())
|
|
1618
|
-
configBody = fs.readFileSync(configSrc, "utf8");
|
|
1619
|
-
}
|
|
1620
|
-
catch (e) {
|
|
1621
|
-
if (e.code !== "ENOENT")
|
|
1622
|
-
throw e;
|
|
1623
|
-
}
|
|
1624
|
-
const configOut = applyCodexConfigOverrides(configBody, overrides);
|
|
1625
|
-
if (configOut != null) {
|
|
1626
|
-
const configDst = path.join(managedHome, "config.toml");
|
|
1627
|
-
fs.writeFileSync(configDst, configOut, { mode: 0o600 });
|
|
1628
|
-
fs.chmodSync(configDst, 0o600);
|
|
1629
|
-
}
|
|
1630
|
-
snapshotCodexAgentDefinitions(srcHome, managedHome);
|
|
1631
|
-
// Codex 自身の rollout transcript に task_complete が永続化されるため、完了検出用の
|
|
1632
|
-
// Stop hook は作らない。hook と transcript の二重正本、および hook failure の単一障害点を
|
|
1633
|
-
// ここでなくす。
|
|
1634
|
-
return managedHome;
|
|
1575
|
+
return `共有 config: ${bits.join(" / ")}`;
|
|
1635
1576
|
}
|
|
1636
|
-
function
|
|
1577
|
+
function createClaudeCorrelationSettings(name, launchId) {
|
|
1637
1578
|
const hookScript = claudeHookScriptPath();
|
|
1638
1579
|
if (!fs.existsSync(hookScript)) {
|
|
1639
1580
|
throw new AitermError(`Claude Stop hook wrapper が見つかりません。npm run build を実行してください: ${hookScript}`, 2);
|
|
@@ -1713,64 +1654,6 @@ function resolveAndValidateGrokAuth(srcHome) {
|
|
|
1713
1654
|
fs.closeSync(fd);
|
|
1714
1655
|
}
|
|
1715
1656
|
}
|
|
1716
|
-
function writeManagedGrokConfig(grokHome) {
|
|
1717
|
-
fs.mkdirSync(path.join(grokHome, "hooks"), { recursive: false, mode: 0o700 });
|
|
1718
|
-
writeText0600(path.join(grokHome, "config.toml"), [
|
|
1719
|
-
"[cli]",
|
|
1720
|
-
"auto_update = false",
|
|
1721
|
-
"",
|
|
1722
|
-
"[features]",
|
|
1723
|
-
"remote_fetch = false",
|
|
1724
|
-
"managed_config = false",
|
|
1725
|
-
"",
|
|
1726
|
-
"[compat.claude]",
|
|
1727
|
-
"skills = false",
|
|
1728
|
-
"rules = false",
|
|
1729
|
-
"agents = false",
|
|
1730
|
-
"mcps = false",
|
|
1731
|
-
"hooks = false",
|
|
1732
|
-
"",
|
|
1733
|
-
"[compat.cursor]",
|
|
1734
|
-
"skills = false",
|
|
1735
|
-
"rules = false",
|
|
1736
|
-
"agents = false",
|
|
1737
|
-
"mcps = false",
|
|
1738
|
-
"hooks = false",
|
|
1739
|
-
"",
|
|
1740
|
-
].join("\n"));
|
|
1741
|
-
}
|
|
1742
|
-
function createManagedGrokHome(name, launchId, authPath) {
|
|
1743
|
-
const grokHome = agentManagedGrokHomePath(name, launchId);
|
|
1744
|
-
const fakeHome = agentManagedGrokUserHomePath(name, launchId);
|
|
1745
|
-
fs.mkdirSync(grokHome, { recursive: false, mode: 0o700 });
|
|
1746
|
-
fs.mkdirSync(fakeHome, { recursive: false, mode: 0o700 });
|
|
1747
|
-
fs.chmodSync(grokHome, 0o700);
|
|
1748
|
-
fs.chmodSync(fakeHome, 0o700);
|
|
1749
|
-
fs.symlinkSync(grokHome, path.join(fakeHome, ".grok"));
|
|
1750
|
-
const hookScript = grokHookScriptPath();
|
|
1751
|
-
if (!fs.existsSync(hookScript)) {
|
|
1752
|
-
throw new AitermError(`Grok Stop hook wrapper が見つかりません。npm run build を実行してください: ${hookScript}`, 2);
|
|
1753
|
-
}
|
|
1754
|
-
writeManagedGrokConfig(grokHome);
|
|
1755
|
-
writeJson0600(path.join(grokHome, "hooks", "aiterm-stop.json"), {
|
|
1756
|
-
hooks: {
|
|
1757
|
-
Stop: [
|
|
1758
|
-
{
|
|
1759
|
-
hooks: [
|
|
1760
|
-
{
|
|
1761
|
-
type: "command",
|
|
1762
|
-
command: nodeHookCommand(hookScript),
|
|
1763
|
-
timeout: 10,
|
|
1764
|
-
},
|
|
1765
|
-
],
|
|
1766
|
-
},
|
|
1767
|
-
],
|
|
1768
|
-
},
|
|
1769
|
-
});
|
|
1770
|
-
// Grok 0.2.87 は compat false でも ~/.claude/plugins の hook file を拾う。HOME を一時化して
|
|
1771
|
-
// plugin/hook source を完全に 0 にする。実 HOME は必要なら hook/agent 側で参照できるよう env で渡す。
|
|
1772
|
-
return { grokHome, home: fakeHome, authPath };
|
|
1773
|
-
}
|
|
1774
1657
|
function writeAgentMetadata(meta) {
|
|
1775
1658
|
writeJson0600(agentMetadataPath(meta.aiterm_session, meta.launch_id), meta);
|
|
1776
1659
|
}
|
|
@@ -1905,7 +1788,7 @@ function parseClaudeApprovalScreen(screen) {
|
|
|
1905
1788
|
question = i;
|
|
1906
1789
|
}
|
|
1907
1790
|
if (question < 0) {
|
|
1908
|
-
throw new AitermError("
|
|
1791
|
+
throw new AitermError("aiterm相関付きClaudeの承認UIを現在画面で確認できません(Do you want to proceed? がありません)", 2);
|
|
1909
1792
|
}
|
|
1910
1793
|
const choices = [];
|
|
1911
1794
|
const seen = new Set();
|
|
@@ -1924,13 +1807,13 @@ function parseClaudeApprovalScreen(screen) {
|
|
|
1924
1807
|
if (!decision)
|
|
1925
1808
|
continue;
|
|
1926
1809
|
if (!Number.isSafeInteger(index) || index < 1 || seen.has(decision)) {
|
|
1927
|
-
throw new AitermError("
|
|
1810
|
+
throw new AitermError("aiterm相関付きClaudeの承認UI選択肢が一意に解釈できません", 2);
|
|
1928
1811
|
}
|
|
1929
1812
|
seen.add(decision);
|
|
1930
1813
|
choices.push({ decision, index, label });
|
|
1931
1814
|
}
|
|
1932
1815
|
if (!seen.has("approve_once") || !seen.has("deny")) {
|
|
1933
|
-
throw new AitermError("
|
|
1816
|
+
throw new AitermError("aiterm相関付きClaudeの承認UIに安全な単発Yes/No選択肢を確認できません", 2);
|
|
1934
1817
|
}
|
|
1935
1818
|
return {
|
|
1936
1819
|
promptDigest: `sha256:${createHash("sha256").update(canonical, "utf8").digest("hex")}`,
|
|
@@ -1940,7 +1823,7 @@ function parseClaudeApprovalScreen(screen) {
|
|
|
1940
1823
|
function assertExpectedClaudeOperation(meta, expectedOperationId) {
|
|
1941
1824
|
const active = readClaudeOperationMarker(meta);
|
|
1942
1825
|
if (!active)
|
|
1943
|
-
throw new AitermError("
|
|
1826
|
+
throw new AitermError("aiterm相関付きClaudeに未解決のactive operationがありません", 2);
|
|
1944
1827
|
if (active.operationId !== expectedOperationId) {
|
|
1945
1828
|
const actual = active.operationId ?? "operation_idなし";
|
|
1946
1829
|
const expected = expectedOperationId ?? "operation_idなし";
|
|
@@ -1954,7 +1837,7 @@ export function runClaudeApproval({ action, session_id: name, operation_id: oper
|
|
|
1954
1837
|
throw new AitermError(`session '${name}' が無い`, 2);
|
|
1955
1838
|
const meta = loadAgentMetadata(name);
|
|
1956
1839
|
if (meta.kind !== "claude")
|
|
1957
|
-
throw new AitermError("claude_approvalは
|
|
1840
|
+
throw new AitermError("claude_approvalはaiterm相関付きClaude agent sessionだけで使用できます", 2);
|
|
1958
1841
|
const operationId = operationIdInput == null ? null : validateOperationId(operationIdInput);
|
|
1959
1842
|
if (action === "inspect") {
|
|
1960
1843
|
if (approvalChoice != null || observedPromptDigest != null) {
|
|
@@ -2220,13 +2103,13 @@ function liveWaitLocks(name) {
|
|
|
2220
2103
|
}
|
|
2221
2104
|
return out;
|
|
2222
2105
|
}
|
|
2223
|
-
function createClaudeAgentMetadata(name, cwd, initialPrompt, launchOperationId, launchRequestDigest) {
|
|
2106
|
+
function createClaudeAgentMetadata(name, cwd, initialPrompt, launchOperationId, launchRequestDigest, lineageContext) {
|
|
2224
2107
|
const launchId = randomBytes(16).toString("hex");
|
|
2225
2108
|
const eventFile = agentEventPath(name, launchId);
|
|
2226
2109
|
const resultFile = agentClaudeResultPath(name, launchId);
|
|
2227
2110
|
createEmpty0600NoFollow(eventFile);
|
|
2228
2111
|
createEmpty0600NoFollow(resultFile);
|
|
2229
|
-
const claudeSettings =
|
|
2112
|
+
const claudeSettings = createClaudeCorrelationSettings(name, launchId);
|
|
2230
2113
|
const meta = {
|
|
2231
2114
|
kind: "claude",
|
|
2232
2115
|
aiterm_session: name,
|
|
@@ -2234,11 +2117,12 @@ function createClaudeAgentMetadata(name, cwd, initialPrompt, launchOperationId,
|
|
|
2234
2117
|
event_file: eventFile,
|
|
2235
2118
|
created_at: new Date().toISOString(),
|
|
2236
2119
|
cwd,
|
|
2237
|
-
vendor_session_id:
|
|
2120
|
+
vendor_session_id: randomUUID(),
|
|
2238
2121
|
initial_prompt: initialPrompt,
|
|
2239
2122
|
launch_operation_id: launchOperationId,
|
|
2240
2123
|
launch_request_digest: launchRequestDigest,
|
|
2241
|
-
hook_route: "
|
|
2124
|
+
hook_route: "shared_claude_settings",
|
|
2125
|
+
...agentLineageFields(lineageContext),
|
|
2242
2126
|
node_platform: process.platform,
|
|
2243
2127
|
claude_settings: claudeSettings,
|
|
2244
2128
|
result_file: resultFile,
|
|
@@ -2246,11 +2130,11 @@ function createClaudeAgentMetadata(name, cwd, initialPrompt, launchOperationId,
|
|
|
2246
2130
|
writeAgentMetadata(meta);
|
|
2247
2131
|
return meta;
|
|
2248
2132
|
}
|
|
2249
|
-
function createCodexAgentMetadata(name, cwd, initialPrompt, overrides = {}, writeScope) {
|
|
2133
|
+
function createCodexAgentMetadata(name, cwd, initialPrompt, overrides = {}, writeScope, lineageContext) {
|
|
2250
2134
|
const launchId = randomBytes(16).toString("hex");
|
|
2251
2135
|
const eventFile = agentEventPath(name, launchId);
|
|
2252
2136
|
createEmpty0600NoFollow(eventFile);
|
|
2253
|
-
const codexHome =
|
|
2137
|
+
const codexHome = realCodexHome();
|
|
2254
2138
|
const meta = {
|
|
2255
2139
|
kind: "codex",
|
|
2256
2140
|
aiterm_session: name,
|
|
@@ -2261,19 +2145,20 @@ function createCodexAgentMetadata(name, cwd, initialPrompt, overrides = {}, writ
|
|
|
2261
2145
|
...(writeScope === undefined ? {} : { write_scope: writeScope }),
|
|
2262
2146
|
vendor_session_id: null,
|
|
2263
2147
|
initial_prompt: initialPrompt,
|
|
2264
|
-
hook_route: "
|
|
2148
|
+
hook_route: "shared_codex_home",
|
|
2265
2149
|
completion_route: "codex_transcript",
|
|
2150
|
+
...(lineageContext ? agentLineageFields(lineageContext) : {}),
|
|
2266
2151
|
node_platform: process.platform,
|
|
2267
2152
|
codex_home: codexHome,
|
|
2268
2153
|
};
|
|
2269
2154
|
writeAgentMetadata(meta);
|
|
2270
2155
|
return meta;
|
|
2271
2156
|
}
|
|
2272
|
-
function createGrokAgentMetadata(kind, name, cwd, initialPrompt, authPath, writeScope) {
|
|
2157
|
+
function createGrokAgentMetadata(kind, name, cwd, initialPrompt, authPath, writeScope, lineageContext) {
|
|
2273
2158
|
const launchId = randomBytes(16).toString("hex");
|
|
2274
2159
|
const eventFile = agentEventPath(name, launchId);
|
|
2275
2160
|
createEmpty0600NoFollow(eventFile);
|
|
2276
|
-
const
|
|
2161
|
+
const grokHome = realGrokHome();
|
|
2277
2162
|
const meta = {
|
|
2278
2163
|
kind,
|
|
2279
2164
|
aiterm_session: name,
|
|
@@ -2282,17 +2167,45 @@ function createGrokAgentMetadata(kind, name, cwd, initialPrompt, authPath, write
|
|
|
2282
2167
|
created_at: new Date().toISOString(),
|
|
2283
2168
|
cwd,
|
|
2284
2169
|
...(writeScope === undefined ? {} : { write_scope: writeScope }),
|
|
2285
|
-
vendor_session_id:
|
|
2170
|
+
vendor_session_id: randomUUID(),
|
|
2286
2171
|
initial_prompt: initialPrompt,
|
|
2287
|
-
hook_route: "
|
|
2172
|
+
hook_route: "shared_grok_home",
|
|
2173
|
+
completion_route: "grok_transcript",
|
|
2174
|
+
...(lineageContext ? agentLineageFields(lineageContext) : {}),
|
|
2288
2175
|
node_platform: process.platform,
|
|
2289
|
-
grok_home:
|
|
2290
|
-
|
|
2291
|
-
grok_auth_path: managed.authPath,
|
|
2176
|
+
grok_home: grokHome,
|
|
2177
|
+
grok_auth_path: authPath,
|
|
2292
2178
|
};
|
|
2293
2179
|
writeAgentMetadata(meta);
|
|
2294
2180
|
return meta;
|
|
2295
2181
|
}
|
|
2182
|
+
function loadAgentLineageFields(m, required) {
|
|
2183
|
+
const present = m.agent_role !== undefined ||
|
|
2184
|
+
m.parent_session_id !== undefined ||
|
|
2185
|
+
m.delegation_depth !== undefined ||
|
|
2186
|
+
m.lineage !== undefined ||
|
|
2187
|
+
m.delegation_allowed !== undefined;
|
|
2188
|
+
if (!present && !required)
|
|
2189
|
+
return {};
|
|
2190
|
+
if (m.agent_role !== "subagent" ||
|
|
2191
|
+
typeof m.parent_session_id !== "string" ||
|
|
2192
|
+
!/^[A-Za-z0-9_-]{1,64}$/.test(m.parent_session_id) ||
|
|
2193
|
+
!Number.isSafeInteger(m.delegation_depth) ||
|
|
2194
|
+
m.delegation_depth < 1 ||
|
|
2195
|
+
typeof m.lineage !== "string" ||
|
|
2196
|
+
m.lineage.length > 4096 ||
|
|
2197
|
+
!AGENT_LINEAGE_RE.test(m.lineage) ||
|
|
2198
|
+
m.delegation_allowed !== true) {
|
|
2199
|
+
throw new AitermError("agent metadata のsub-agent lineageが不正です", 2);
|
|
2200
|
+
}
|
|
2201
|
+
return agentLineageFields({
|
|
2202
|
+
agentRole: "subagent",
|
|
2203
|
+
parentSessionId: m.parent_session_id,
|
|
2204
|
+
delegationDepth: m.delegation_depth,
|
|
2205
|
+
lineage: m.lineage,
|
|
2206
|
+
delegationAllowed: true,
|
|
2207
|
+
});
|
|
2208
|
+
}
|
|
2296
2209
|
function loadAgentMetadata(name) {
|
|
2297
2210
|
assertSessionName(name);
|
|
2298
2211
|
const dir = agentsDir();
|
|
@@ -2300,7 +2213,7 @@ function loadAgentMetadata(name) {
|
|
|
2300
2213
|
.readdirSync(dir)
|
|
2301
2214
|
.filter((f) => f.startsWith(`${name}.`) && f.endsWith(".agent.json"));
|
|
2302
2215
|
if (files.length === 0) {
|
|
2303
|
-
throw new AitermError(`session '${name}' は agent_done 管理セッションではありません。claude_agent/codex_agent 等の launcher
|
|
2216
|
+
throw new AitermError(`session '${name}' は agent_done 管理セッションではありません。claude_agent/codex_agent 等の launcher で起動してください。`, 2);
|
|
2304
2217
|
}
|
|
2305
2218
|
if (files.length !== 1) {
|
|
2306
2219
|
throw new AitermError(`session '${name}' の agent metadata が複数あります。閉じて起動し直してください。`, 2);
|
|
@@ -2328,8 +2241,10 @@ function loadAgentMetadata(name) {
|
|
|
2328
2241
|
const expectedResult = agentClaudeResultPath(name, m.launch_id);
|
|
2329
2242
|
const launchOperationId = m.launch_operation_id ?? null;
|
|
2330
2243
|
const launchRequestDigest = m.launch_request_digest ?? null;
|
|
2331
|
-
if (m.hook_route !== "
|
|
2244
|
+
if (m.hook_route !== "shared_claude_settings" ||
|
|
2332
2245
|
m.claude_settings !== expectedSettings ||
|
|
2246
|
+
typeof m.vendor_session_id !== "string" ||
|
|
2247
|
+
!UUID_RE.test(m.vendor_session_id) ||
|
|
2333
2248
|
m.result_file !== expectedResult ||
|
|
2334
2249
|
((launchOperationId === null) !== (launchRequestDigest === null)) ||
|
|
2335
2250
|
(launchOperationId !== null && !OPERATION_ID_RE.test(launchOperationId)) ||
|
|
@@ -2344,19 +2259,20 @@ function loadAgentMetadata(name) {
|
|
|
2344
2259
|
created_at: typeof m.created_at === "string" ? m.created_at : "",
|
|
2345
2260
|
cwd: typeof m.cwd === "string" ? m.cwd : null,
|
|
2346
2261
|
...(typeof m.write_scope === "string" ? { write_scope: m.write_scope } : {}),
|
|
2347
|
-
vendor_session_id:
|
|
2262
|
+
vendor_session_id: m.vendor_session_id,
|
|
2348
2263
|
initial_prompt: normalizeInitialPromptState(m.initial_prompt),
|
|
2349
2264
|
launch_operation_id: launchOperationId,
|
|
2350
2265
|
launch_request_digest: launchRequestDigest,
|
|
2351
|
-
hook_route: "
|
|
2266
|
+
hook_route: "shared_claude_settings",
|
|
2267
|
+
...loadAgentLineageFields(m, true),
|
|
2352
2268
|
node_platform: process.platform,
|
|
2353
2269
|
claude_settings: expectedSettings,
|
|
2354
2270
|
result_file: expectedResult,
|
|
2355
2271
|
};
|
|
2356
2272
|
}
|
|
2357
2273
|
if (m.kind === "codex") {
|
|
2358
|
-
const expectedHome =
|
|
2359
|
-
if (m.hook_route !== "
|
|
2274
|
+
const expectedHome = realCodexHome();
|
|
2275
|
+
if (m.hook_route !== "shared_codex_home" || m.completion_route !== "codex_transcript" || m.codex_home !== expectedHome) {
|
|
2360
2276
|
throw new AitermError("agent metadata の path が現在の secure state root と一致しません", 2);
|
|
2361
2277
|
}
|
|
2362
2278
|
return {
|
|
@@ -2369,15 +2285,19 @@ function loadAgentMetadata(name) {
|
|
|
2369
2285
|
...(typeof m.write_scope === "string" ? { write_scope: m.write_scope } : {}),
|
|
2370
2286
|
vendor_session_id: typeof m.vendor_session_id === "string" ? m.vendor_session_id : null,
|
|
2371
2287
|
initial_prompt: normalizeInitialPromptState(m.initial_prompt),
|
|
2372
|
-
hook_route: "
|
|
2373
|
-
|
|
2288
|
+
hook_route: "shared_codex_home",
|
|
2289
|
+
completion_route: "codex_transcript",
|
|
2290
|
+
...loadAgentLineageFields(m, true),
|
|
2374
2291
|
node_platform: process.platform,
|
|
2375
2292
|
codex_home: expectedHome,
|
|
2376
2293
|
};
|
|
2377
2294
|
}
|
|
2378
|
-
const expectedGrokHome =
|
|
2379
|
-
|
|
2380
|
-
|
|
2295
|
+
const expectedGrokHome = realGrokHome();
|
|
2296
|
+
if (m.hook_route !== "shared_grok_home" ||
|
|
2297
|
+
m.completion_route !== "grok_transcript" ||
|
|
2298
|
+
m.grok_home !== expectedGrokHome ||
|
|
2299
|
+
typeof m.vendor_session_id !== "string" ||
|
|
2300
|
+
!UUID_RE.test(m.vendor_session_id)) {
|
|
2381
2301
|
throw new AitermError("agent metadata の path が現在の secure state root と一致しません", 2);
|
|
2382
2302
|
}
|
|
2383
2303
|
const expectedAuthPath = resolveAndValidateGrokAuth(realGrokHome());
|
|
@@ -2392,12 +2312,13 @@ function loadAgentMetadata(name) {
|
|
|
2392
2312
|
created_at: typeof m.created_at === "string" ? m.created_at : "",
|
|
2393
2313
|
cwd: typeof m.cwd === "string" ? m.cwd : null,
|
|
2394
2314
|
...(typeof m.write_scope === "string" ? { write_scope: m.write_scope } : {}),
|
|
2395
|
-
vendor_session_id:
|
|
2315
|
+
vendor_session_id: m.vendor_session_id,
|
|
2396
2316
|
initial_prompt: normalizeInitialPromptState(m.initial_prompt),
|
|
2397
|
-
hook_route: "
|
|
2317
|
+
hook_route: "shared_grok_home",
|
|
2318
|
+
completion_route: "grok_transcript",
|
|
2319
|
+
...loadAgentLineageFields(m, true),
|
|
2398
2320
|
node_platform: process.platform,
|
|
2399
2321
|
grok_home: expectedGrokHome,
|
|
2400
|
-
home: expectedHome,
|
|
2401
2322
|
grok_auth_path: expectedAuthPath,
|
|
2402
2323
|
};
|
|
2403
2324
|
}
|
|
@@ -2503,7 +2424,10 @@ function bindCompletedInitialPrompt(meta) {
|
|
|
2503
2424
|
setInitialPromptState(meta, "done");
|
|
2504
2425
|
return;
|
|
2505
2426
|
}
|
|
2506
|
-
if (meta.
|
|
2427
|
+
if ((meta.kind === "grok" || meta.kind === "composer") && meta.completion_route === "grok_transcript") {
|
|
2428
|
+
if (!latestGrokCompletion(meta)) {
|
|
2429
|
+
throw new AitermError(`agent session '${meta.aiterm_session}' は起動時 prompt の完了待ちです。${agentWaitGuide(meta.aiterm_session)}`, 2);
|
|
2430
|
+
}
|
|
2507
2431
|
setInitialPromptState(meta, "done");
|
|
2508
2432
|
return;
|
|
2509
2433
|
}
|
|
@@ -2537,6 +2461,9 @@ function tryLoadAgentMetadata(name) {
|
|
|
2537
2461
|
function latestAgentDoneEvent(meta, expectedOperationId = null) {
|
|
2538
2462
|
if (meta.kind === "codex")
|
|
2539
2463
|
return latestCodexCompletion(meta);
|
|
2464
|
+
if ((meta.kind === "grok" || meta.kind === "composer") && meta.completion_route === "grok_transcript") {
|
|
2465
|
+
return latestGrokCompletion(meta);
|
|
2466
|
+
}
|
|
2540
2467
|
const size = safeStatSize(meta.event_file);
|
|
2541
2468
|
if (size === 0)
|
|
2542
2469
|
return null;
|
|
@@ -2672,10 +2599,52 @@ function listCodexTranscripts(codexHome) {
|
|
|
2672
2599
|
}
|
|
2673
2600
|
};
|
|
2674
2601
|
visit(sessionsDir);
|
|
2675
|
-
// managed CODEX_HOME では root TUI の rollout が最初に作られる。後から同じ home に
|
|
2676
|
-
// sub-agent rollout が増えても root を取り違えないよう、未bind時は最古を選べる順にする。
|
|
2677
2602
|
return files.sort();
|
|
2678
2603
|
}
|
|
2604
|
+
function codexTranscriptMatchesLaunch(file, meta) {
|
|
2605
|
+
const createdAt = Date.parse(meta.created_at);
|
|
2606
|
+
try {
|
|
2607
|
+
const st = fs.statSync(file);
|
|
2608
|
+
if (Number.isFinite(createdAt) && st.mtimeMs + 5_000 < createdAt)
|
|
2609
|
+
return false;
|
|
2610
|
+
}
|
|
2611
|
+
catch {
|
|
2612
|
+
return false;
|
|
2613
|
+
}
|
|
2614
|
+
const size = Math.min(safeStatSize(file), 1024 * 1024);
|
|
2615
|
+
if (size === 0)
|
|
2616
|
+
return false;
|
|
2617
|
+
const marker = `AITERM_AGENT_LAUNCH_ID=${meta.launch_id}`;
|
|
2618
|
+
let rootCli = false;
|
|
2619
|
+
let launchMarker = false;
|
|
2620
|
+
for (const line of readFileRange(file, 0, size).toString("utf8").split("\n")) {
|
|
2621
|
+
if (!line.trim())
|
|
2622
|
+
continue;
|
|
2623
|
+
let record;
|
|
2624
|
+
try {
|
|
2625
|
+
record = JSON.parse(line);
|
|
2626
|
+
}
|
|
2627
|
+
catch {
|
|
2628
|
+
continue;
|
|
2629
|
+
}
|
|
2630
|
+
if (record?.type === "session_meta") {
|
|
2631
|
+
rootCli = record?.payload?.originator === "codex-tui" && record?.payload?.source === "cli";
|
|
2632
|
+
if (!rootCli)
|
|
2633
|
+
return false;
|
|
2634
|
+
}
|
|
2635
|
+
if (record?.type === "response_item" &&
|
|
2636
|
+
record?.payload?.type === "message" &&
|
|
2637
|
+
record?.payload?.role === "developer" &&
|
|
2638
|
+
Array.isArray(record?.payload?.content)) {
|
|
2639
|
+
launchMarker ||= record.payload.content.some((item) => (item?.type === "input_text" || item?.type === "output_text") &&
|
|
2640
|
+
typeof item?.text === "string" &&
|
|
2641
|
+
item.text.includes(marker));
|
|
2642
|
+
}
|
|
2643
|
+
if (rootCli && launchMarker)
|
|
2644
|
+
return true;
|
|
2645
|
+
}
|
|
2646
|
+
return false;
|
|
2647
|
+
}
|
|
2679
2648
|
function codexTranscriptSessionId(file) {
|
|
2680
2649
|
const size = Math.min(safeStatSize(file), AGENT_EVENT_TAIL_BYTES);
|
|
2681
2650
|
if (size === 0)
|
|
@@ -2701,6 +2670,13 @@ function codexRootTranscript(meta) {
|
|
|
2701
2670
|
return null;
|
|
2702
2671
|
if (meta.vendor_session_id)
|
|
2703
2672
|
return findLatestCodexTranscript(meta.codex_home, meta.vendor_session_id);
|
|
2673
|
+
if (meta.hook_route === "shared_codex_home") {
|
|
2674
|
+
const matches = listCodexTranscripts(meta.codex_home).filter((file) => codexTranscriptMatchesLaunch(file, meta));
|
|
2675
|
+
if (matches.length > 1) {
|
|
2676
|
+
throw new AitermError("共有CODEX_HOMEに同じlaunch markerのroot rolloutが複数あります。sessionを閉じて起動し直してください。", 2);
|
|
2677
|
+
}
|
|
2678
|
+
return matches[0] ?? null;
|
|
2679
|
+
}
|
|
2704
2680
|
return listCodexTranscripts(meta.codex_home)[0] ?? null;
|
|
2705
2681
|
}
|
|
2706
2682
|
function bindCodexTranscriptSession(meta) {
|
|
@@ -2752,7 +2728,84 @@ function latestCodexCompletion(meta) {
|
|
|
2752
2728
|
}
|
|
2753
2729
|
return latest;
|
|
2754
2730
|
}
|
|
2731
|
+
function grokSessionDirectory(meta) {
|
|
2732
|
+
if ((meta.kind !== "grok" && meta.kind !== "composer") || !meta.grok_home || !meta.vendor_session_id)
|
|
2733
|
+
return null;
|
|
2734
|
+
const cwd = meta.cwd ?? process.cwd();
|
|
2735
|
+
return path.join(meta.grok_home, "sessions", encodeURIComponent(cwd), meta.vendor_session_id);
|
|
2736
|
+
}
|
|
2737
|
+
function grokEventsTranscript(meta) {
|
|
2738
|
+
const dir = grokSessionDirectory(meta);
|
|
2739
|
+
return dir ? path.join(dir, "events.jsonl") : null;
|
|
2740
|
+
}
|
|
2741
|
+
function grokCompletionEvent(meta, record) {
|
|
2742
|
+
if ((meta.kind !== "grok" && meta.kind !== "composer") ||
|
|
2743
|
+
record?.type !== "turn_ended" ||
|
|
2744
|
+
(record?.outcome !== "completed" && record?.outcome !== "cancelled"))
|
|
2745
|
+
return null;
|
|
2746
|
+
const turnId = typeof record?.ts === "string" || typeof record?.ts === "number" ? String(record.ts) : null;
|
|
2747
|
+
return {
|
|
2748
|
+
type: "agent_done",
|
|
2749
|
+
vendor: meta.kind,
|
|
2750
|
+
aiterm_session: meta.aiterm_session,
|
|
2751
|
+
launch_id: meta.launch_id,
|
|
2752
|
+
vendor_session_id: meta.vendor_session_id,
|
|
2753
|
+
turn_id: turnId,
|
|
2754
|
+
operation_id: null,
|
|
2755
|
+
reason: `Grok transcript turn_ended:${record.outcome}`,
|
|
2756
|
+
done_status: "turn_done",
|
|
2757
|
+
stop_hook_active: false,
|
|
2758
|
+
at: typeof record?.ts === "string" ? record.ts : new Date().toISOString(),
|
|
2759
|
+
};
|
|
2760
|
+
}
|
|
2761
|
+
function latestGrokCompletion(meta) {
|
|
2762
|
+
const transcript = grokEventsTranscript(meta);
|
|
2763
|
+
if (!transcript || !fs.existsSync(transcript))
|
|
2764
|
+
return null;
|
|
2765
|
+
let latest = null;
|
|
2766
|
+
for (const line of readTranscriptLines(transcript)) {
|
|
2767
|
+
if (!line.trim())
|
|
2768
|
+
continue;
|
|
2769
|
+
try {
|
|
2770
|
+
latest = grokCompletionEvent(meta, JSON.parse(line)) ?? latest;
|
|
2771
|
+
}
|
|
2772
|
+
catch {
|
|
2773
|
+
// 末尾書込み中のlineは次の観測で完結してから読む。
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
return latest;
|
|
2777
|
+
}
|
|
2778
|
+
function grokInitializationComplete(meta) {
|
|
2779
|
+
const transcript = grokEventsTranscript(meta);
|
|
2780
|
+
if (!transcript || !fs.existsSync(transcript))
|
|
2781
|
+
return false;
|
|
2782
|
+
const size = safeStatSize(transcript);
|
|
2783
|
+
const from = Math.max(0, size - GROK_TRANSCRIPT_INCREMENT_MAX_BYTES);
|
|
2784
|
+
const lines = readFileRange(transcript, from, size).toString("utf8").split("\n");
|
|
2785
|
+
if (from > 0)
|
|
2786
|
+
lines.shift();
|
|
2787
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
2788
|
+
const line = lines[index].trim();
|
|
2789
|
+
if (!line)
|
|
2790
|
+
continue;
|
|
2791
|
+
try {
|
|
2792
|
+
const record = JSON.parse(line);
|
|
2793
|
+
if (record?.type === "mcp_init_completed")
|
|
2794
|
+
return true;
|
|
2795
|
+
if (record?.type === "mcp_init_started")
|
|
2796
|
+
return false;
|
|
2797
|
+
}
|
|
2798
|
+
catch {
|
|
2799
|
+
// 末尾書込み中や無関係な破損lineはready判定を進めない。
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
return false;
|
|
2803
|
+
}
|
|
2755
2804
|
function agentCompletionCursor(meta) {
|
|
2805
|
+
if ((meta.kind === "grok" || meta.kind === "composer") && meta.completion_route === "grok_transcript") {
|
|
2806
|
+
const transcript = grokEventsTranscript(meta);
|
|
2807
|
+
return transcript ? safeStatSize(transcript) : 0;
|
|
2808
|
+
}
|
|
2756
2809
|
if (meta.kind !== "codex")
|
|
2757
2810
|
return safeStatSize(meta.event_file);
|
|
2758
2811
|
const transcript = bindCodexTranscriptSession(meta);
|
|
@@ -3116,6 +3169,80 @@ async function observeCodexDone(meta, timeout, requestedCursor) {
|
|
|
3116
3169
|
await sleep(AGENT_DONE_POLL_MS);
|
|
3117
3170
|
}
|
|
3118
3171
|
}
|
|
3172
|
+
async function observeGrokDone(meta, timeout, requestedCursor) {
|
|
3173
|
+
const metadataFile = agentMetadataPath(meta.aiterm_session, meta.launch_id);
|
|
3174
|
+
const transcript = grokEventsTranscript(meta);
|
|
3175
|
+
if (!transcript)
|
|
3176
|
+
throw new AitermError("Grok transcriptのsession相関情報がありません", 2);
|
|
3177
|
+
const startOffset = requestedCursor ?? safeStatSize(transcript);
|
|
3178
|
+
let cursor = startOffset;
|
|
3179
|
+
let carry = "";
|
|
3180
|
+
let malformedEvents = 0;
|
|
3181
|
+
let discardLeadingFragment = false;
|
|
3182
|
+
let initializedBoundary = false;
|
|
3183
|
+
const deadline = performance.now() + timeout * 1000;
|
|
3184
|
+
const observation = (outcome, ev = null) => ({
|
|
3185
|
+
schema: "aiterm.agent-wait-result.v1",
|
|
3186
|
+
session_id: meta.aiterm_session,
|
|
3187
|
+
launch_id: meta.launch_id,
|
|
3188
|
+
vendor: meta.kind,
|
|
3189
|
+
outcome,
|
|
3190
|
+
operation_id: null,
|
|
3191
|
+
vendor_session_id: meta.vendor_session_id,
|
|
3192
|
+
turn_id: ev?.turn_id ?? null,
|
|
3193
|
+
malformed_events: malformedEvents,
|
|
3194
|
+
at: ev?.at ?? null,
|
|
3195
|
+
});
|
|
3196
|
+
for (;;) {
|
|
3197
|
+
if (!fs.existsSync(metadataFile))
|
|
3198
|
+
return observation("closed");
|
|
3199
|
+
if (fs.existsSync(transcript)) {
|
|
3200
|
+
if (!initializedBoundary) {
|
|
3201
|
+
if (cursor > 0) {
|
|
3202
|
+
const previous = readFileRange(transcript, cursor - 1, cursor).toString("utf8");
|
|
3203
|
+
discardLeadingFragment = previous !== "\n";
|
|
3204
|
+
}
|
|
3205
|
+
initializedBoundary = true;
|
|
3206
|
+
}
|
|
3207
|
+
const size = safeStatSize(transcript);
|
|
3208
|
+
if (size < cursor) {
|
|
3209
|
+
throw new AitermError("Grok transcript が完了待機中に短くなりました。該当セッションを閉じて起動し直してください。", 2);
|
|
3210
|
+
}
|
|
3211
|
+
if (size - startOffset > GROK_TRANSCRIPT_INCREMENT_MAX_BYTES) {
|
|
3212
|
+
throw new AitermError("Grok transcript のturn増分が大きすぎます。該当セッションを閉じて起動し直してください。", 2);
|
|
3213
|
+
}
|
|
3214
|
+
if (size > cursor) {
|
|
3215
|
+
carry += readFileRange(transcript, cursor, size).toString("utf8");
|
|
3216
|
+
cursor = size;
|
|
3217
|
+
const parts = carry.split("\n");
|
|
3218
|
+
carry = parts.pop() ?? "";
|
|
3219
|
+
if (discardLeadingFragment && parts.length > 0) {
|
|
3220
|
+
parts.shift();
|
|
3221
|
+
discardLeadingFragment = false;
|
|
3222
|
+
}
|
|
3223
|
+
for (const line of parts) {
|
|
3224
|
+
if (!line.trim())
|
|
3225
|
+
continue;
|
|
3226
|
+
if (Buffer.byteLength(line, "utf8") > AGENT_EVENT_MAX_BYTES) {
|
|
3227
|
+
malformedEvents++;
|
|
3228
|
+
continue;
|
|
3229
|
+
}
|
|
3230
|
+
try {
|
|
3231
|
+
const done = grokCompletionEvent(meta, JSON.parse(line));
|
|
3232
|
+
if (done)
|
|
3233
|
+
return observation("done", done);
|
|
3234
|
+
}
|
|
3235
|
+
catch {
|
|
3236
|
+
malformedEvents++;
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
if (performance.now() >= deadline)
|
|
3242
|
+
return observation(timeout === 0 ? "running" : "timeout");
|
|
3243
|
+
await sleep(AGENT_DONE_POLL_MS);
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3119
3246
|
// 外部waiterプロセス用の純リーダー観測。lock・PTY・metadata書込・dispatch状態には一切触れない。
|
|
3120
3247
|
// Codexはrollout transcript、他vendorはevent fileを増分走査し、vendor_session_idのbind永続化を
|
|
3121
3248
|
// 行わない(waiterは観測者であって所有者でない)。
|
|
@@ -3132,6 +3259,9 @@ export async function observeAgentDone(name, o = {}) {
|
|
|
3132
3259
|
if (meta.kind === "codex" && meta.completion_route === "codex_transcript") {
|
|
3133
3260
|
return observeCodexDone(meta, timeout, o.cursor);
|
|
3134
3261
|
}
|
|
3262
|
+
if ((meta.kind === "grok" || meta.kind === "composer") && meta.completion_route === "grok_transcript") {
|
|
3263
|
+
return observeGrokDone(meta, timeout, o.cursor);
|
|
3264
|
+
}
|
|
3135
3265
|
const metadataFile = agentMetadataPath(meta.aiterm_session, meta.launch_id);
|
|
3136
3266
|
// 境界の優先順: dispatch receipt の event_cursor(起動順序に依存しない)→ operation相関
|
|
3137
3267
|
// (operation_idの一意性で先頭から全走査できる)→ waiter起動時EOF(waiter先行起動が前提)。
|
|
@@ -3190,7 +3320,10 @@ function isAgentTuiReady(kind, screen) {
|
|
|
3190
3320
|
if (kind === "codex") {
|
|
3191
3321
|
return screen.includes("OpenAI Codex") && /(^|\n)\s*[›>]/.test(screen);
|
|
3192
3322
|
}
|
|
3193
|
-
|
|
3323
|
+
// Grok Build 0.2.117 は起動完了後に製品名を消し、model footerだけを残す。
|
|
3324
|
+
// Composerも同じfrontendでmodel名だけが異なるため、両方をvendor UIの根拠にする。
|
|
3325
|
+
const grokFrontend = screen.includes("Grok Build") || /\b(?:Grok|Composer)\s+[\w.()-]+/.test(screen);
|
|
3326
|
+
return grokFrontend && /(^|\n|\s)❯/.test(screen);
|
|
3194
3327
|
}
|
|
3195
3328
|
// Codex/Claude は実行中に「(esc to interrupt)」を表示する(実機採取)。startup 側の処理
|
|
3196
3329
|
// (MCP initialize 等)が走ったまま composer だけ描画されている画面は入力受付とみなさない。
|
|
@@ -3229,7 +3362,11 @@ async function waitAgentTuiReadyImpl(kind, sample, sleepFn, opts = {}) {
|
|
|
3229
3362
|
}
|
|
3230
3363
|
}
|
|
3231
3364
|
async function waitAgentTuiReady(name, meta, timeoutMs = AGENT_TUI_READY_TIMEOUT_MS) {
|
|
3232
|
-
return waitAgentTuiReadyImpl(meta.kind, () =>
|
|
3365
|
+
return waitAgentTuiReadyImpl(meta.kind, () => {
|
|
3366
|
+
if ((meta.kind === "grok" || meta.kind === "composer") && !grokInitializationComplete(meta))
|
|
3367
|
+
return "";
|
|
3368
|
+
return captureScreen(name, AGENT_TUI_READY_LINES);
|
|
3369
|
+
}, sleep, { timeoutMs });
|
|
3233
3370
|
}
|
|
3234
3371
|
async function waitAgentTuiReadyByKind(name, kind, timeoutMs = AGENT_TUI_READY_TIMEOUT_MS) {
|
|
3235
3372
|
return waitAgentTuiReadyImpl(kind, () => captureScreen(name, AGENT_TUI_READY_LINES), sleep, { timeoutMs });
|
|
@@ -3433,9 +3570,10 @@ export async function dispatchAgentTurn(name, text, o = {}) {
|
|
|
3433
3570
|
throw new AitermError("operation_id はClaude agent sessionだけで使用できます", 2);
|
|
3434
3571
|
}
|
|
3435
3572
|
bindCompletedInitialPrompt(meta);
|
|
3436
|
-
// Codexはbind済みのfollow-upでも毎回idleを確認してからtranscript
|
|
3437
|
-
//
|
|
3438
|
-
|
|
3573
|
+
// Codex/Grok/Composerはbind済みのfollow-upでも毎回idleを確認してからtranscript境界を切る。
|
|
3574
|
+
// Grok/Composerはsession IDが起動前から既知でも、共有MCPの初期化完了前には送信しない。
|
|
3575
|
+
// 同じcursorへ複数turnを帰属させる余地や、初期化中TUIへの早送信を作らない。
|
|
3576
|
+
if (meta.kind !== "claude" || !meta.vendor_session_id) {
|
|
3439
3577
|
const ready = await waitAgentTuiReady(name, meta, o.ready_timeout ?? AGENT_TUI_READY_TIMEOUT_MS);
|
|
3440
3578
|
if (!ready.ready) {
|
|
3441
3579
|
throw new AitermError(`agent session '${name}' の ${agentLabel(meta.kind)} TUI が入力受付状態になりません。文字列は送信していません。` +
|
|
@@ -3484,7 +3622,7 @@ export async function runClaudeOperation({ session_id: name, action, operation_i
|
|
|
3484
3622
|
const operationId = validateOperationId(operationIdInput);
|
|
3485
3623
|
const meta = loadAgentMetadata(name);
|
|
3486
3624
|
if (meta.kind !== "claude")
|
|
3487
|
-
throw new AitermError("claude_turnは
|
|
3625
|
+
throw new AitermError("claude_turnはaiterm相関付きClaude agent sessionだけで使用できます", 2);
|
|
3488
3626
|
let dispatchReceipt = null;
|
|
3489
3627
|
if (action === "issue") {
|
|
3490
3628
|
if (typeof text !== "string" || text.length === 0) {
|
|
@@ -3595,7 +3733,7 @@ function assertClaudeAuthenticationReady(bin) {
|
|
|
3595
3733
|
status.loggedIn === false) {
|
|
3596
3734
|
throw new AitermError("Claude Codeの認証を利用できません。sessionは作成していません。" +
|
|
3597
3735
|
"通常端末で `claude doctor` を実行し、Keychain/credential storeを直してから一度だけ `claude auth login` を実行してください。" +
|
|
3598
|
-
"
|
|
3736
|
+
"aiterm相関付きClaude session内で /login を繰り返さないでください。", 2);
|
|
3599
3737
|
}
|
|
3600
3738
|
const timedOut = result.error && result.error.code === "ETIMEDOUT";
|
|
3601
3739
|
throw new AitermError(`Claude Codeの認証状態を起動前に確認できません${timedOut ? "(5秒でtimeout)" : ""}。sessionは作成していません。` +
|
|
@@ -3638,7 +3776,7 @@ function buildAgentCmd(kind, bin, model, effort, prompt, meta = null) {
|
|
|
3638
3776
|
const parts = [shq(bin)];
|
|
3639
3777
|
if (kind === "claude") {
|
|
3640
3778
|
if (meta?.kind === "claude") {
|
|
3641
|
-
parts.push("--setting-sources", shq(""), "--settings", shq(meta.claude_settings ?? ""));
|
|
3779
|
+
parts.push("--setting-sources", shq("user,project,local"), "--settings", shq(meta.claude_settings ?? ""), "--session-id", shq(meta.vendor_session_id ?? ""), "--append-system-prompt", shq(subagentInstruction(meta)));
|
|
3642
3780
|
}
|
|
3643
3781
|
if (model)
|
|
3644
3782
|
parts.push("--model", shq(model));
|
|
@@ -3650,12 +3788,14 @@ function buildAgentCmd(kind, bin, model, effort, prompt, meta = null) {
|
|
|
3650
3788
|
// launcher自身が実効能力壁を作る。パス説明はCodex CLIに同等のallowlist引数がないため宣言のまま残す。
|
|
3651
3789
|
if (meta?.kind === "codex" && meta.write_scope === "read-only")
|
|
3652
3790
|
parts.push("--sandbox", "read-only");
|
|
3653
|
-
// model/effort
|
|
3654
|
-
// config.toml も同値で上書き済み(applyCodexConfigOverrides)。
|
|
3791
|
+
// model/effort は共有configを書き換えず、CLI引数で明示して起動単位に優先する。
|
|
3655
3792
|
if (model)
|
|
3656
3793
|
parts.push("-m", shq(model));
|
|
3657
3794
|
if (effort)
|
|
3658
3795
|
parts.push("-c", `model_reasoning_effort=${shq(effort)}`);
|
|
3796
|
+
if (meta?.kind === "codex" && meta.hook_route === "shared_codex_home") {
|
|
3797
|
+
parts.push("-c", `developer_instructions=${shq(subagentInstruction(meta))}`);
|
|
3798
|
+
}
|
|
3659
3799
|
}
|
|
3660
3800
|
else {
|
|
3661
3801
|
// grok / composer は同じ grok CLI をモデル違いで起動。--effort は headless(grok -p)専用で
|
|
@@ -3664,6 +3804,9 @@ function buildAgentCmd(kind, bin, model, effort, prompt, meta = null) {
|
|
|
3664
3804
|
if (meta?.kind === "grok" || meta?.kind === "composer")
|
|
3665
3805
|
parts.push("--no-alt-screen");
|
|
3666
3806
|
parts.push("--model", shq(model ?? GROK_MODEL_DEFAULTS[kind]));
|
|
3807
|
+
if ((meta?.kind === "grok" || meta?.kind === "composer") && meta.hook_route === "shared_grok_home") {
|
|
3808
|
+
parts.push("--session-id", shq(meta.vendor_session_id ?? ""), "--rules", shq(subagentInstruction(meta)));
|
|
3809
|
+
}
|
|
3667
3810
|
if ((meta?.kind === "grok" || meta?.kind === "composer") && prompt)
|
|
3668
3811
|
parts.push("--verbatim");
|
|
3669
3812
|
}
|
|
@@ -3679,29 +3822,21 @@ function agentEnvPrefix(meta, sid) {
|
|
|
3679
3822
|
`AITERM_SESSION_ID=${shq(sid)}`,
|
|
3680
3823
|
`AITERM_AGENT_SESSION_ID=${shq(sid)}`,
|
|
3681
3824
|
`AITERM_AGENT_LAUNCH_ID=${shq(meta.launch_id)}`,
|
|
3825
|
+
`AITERM_AGENT_ROLE=${shq(meta.agent_role ?? "subagent")}`,
|
|
3826
|
+
`AITERM_AGENT_PARENT_SESSION_ID=${shq(meta.parent_session_id ?? "host-root")}`,
|
|
3827
|
+
`AITERM_AGENT_DEPTH=${shq(String(meta.delegation_depth ?? 1))}`,
|
|
3828
|
+
`AITERM_AGENT_LINEAGE=${shq(meta.lineage ?? `host-root>${meta.kind}:${sid}`)}`,
|
|
3829
|
+
`AITERM_AGENT_DELEGATION_ALLOWED=${shq(meta.delegation_allowed === true ? "true" : "false")}`,
|
|
3682
3830
|
];
|
|
3683
3831
|
if (meta.kind === "claude") {
|
|
3684
3832
|
return common.join(" ") + " ";
|
|
3685
3833
|
}
|
|
3686
3834
|
if (meta.kind === "codex") {
|
|
3687
|
-
return
|
|
3835
|
+
return common.join(" ") + " ";
|
|
3688
3836
|
}
|
|
3689
3837
|
return [
|
|
3690
|
-
`HOME=${shq(meta.home ?? "")}`,
|
|
3691
|
-
`GROK_HOME=${shq(meta.grok_home ?? "")}`,
|
|
3692
3838
|
...(meta.grok_auth_path ? [`GROK_AUTH_PATH=${shq(meta.grok_auth_path)}`] : []),
|
|
3693
3839
|
"GROK_DISABLE_AUTOUPDATER=1",
|
|
3694
|
-
"GROK_CLAUDE_HOOKS_ENABLED=false",
|
|
3695
|
-
"GROK_CURSOR_HOOKS_ENABLED=false",
|
|
3696
|
-
"GROK_CLAUDE_SKILLS_ENABLED=false",
|
|
3697
|
-
"GROK_CURSOR_SKILLS_ENABLED=false",
|
|
3698
|
-
"GROK_CLAUDE_RULES_ENABLED=false",
|
|
3699
|
-
"GROK_CURSOR_RULES_ENABLED=false",
|
|
3700
|
-
"GROK_CLAUDE_AGENTS_ENABLED=false",
|
|
3701
|
-
"GROK_CURSOR_AGENTS_ENABLED=false",
|
|
3702
|
-
"GROK_CLAUDE_MCPS_ENABLED=false",
|
|
3703
|
-
"GROK_CURSOR_MCPS_ENABLED=false",
|
|
3704
|
-
`AITERM_REAL_HOME=${shq(process.env.HOME ?? os.homedir())}`,
|
|
3705
3840
|
...common,
|
|
3706
3841
|
].join(" ") + " ";
|
|
3707
3842
|
}
|
|
@@ -3746,7 +3881,7 @@ function buildAgentLaunchNote(kind, model, effort, meta) {
|
|
|
3746
3881
|
(effectiveEffort === "ultra"
|
|
3747
3882
|
? "⚠ effort=ultra は max 推論+proactive 自動委譲 ON(子エージェント自動生成・使用量急増に注意)。"
|
|
3748
3883
|
: "");
|
|
3749
|
-
const summary = meta?.kind === "codex" && meta.codex_home ?
|
|
3884
|
+
const summary = meta?.kind === "codex" && meta.codex_home ? codexConfigSummary(configPath) : "";
|
|
3750
3885
|
return (summary ? `${launch}\n${summary}\n` : launch) + writeScopeNote;
|
|
3751
3886
|
}
|
|
3752
3887
|
function claudeLaunchRequestDigest({ sessionName, model, effort, cwd, agentDone, }) {
|
|
@@ -3824,6 +3959,8 @@ export function openAgent(kind, opts = {}) {
|
|
|
3824
3959
|
throw new AitermError("launch_operation_id付きlaunchにpromptは指定できません", 2);
|
|
3825
3960
|
}
|
|
3826
3961
|
}
|
|
3962
|
+
// 継承lineageの破損はsession作成前にfail loudする。root callerにはhost-root/depth 1を割り当てる。
|
|
3963
|
+
const lineageSeed = readAgentLineageSeed();
|
|
3827
3964
|
let bin;
|
|
3828
3965
|
try {
|
|
3829
3966
|
bin = resolveAgentBin(kind);
|
|
@@ -3894,12 +4031,13 @@ export function openAgent(kind, opts = {}) {
|
|
|
3894
4031
|
}
|
|
3895
4032
|
let launchNote = "";
|
|
3896
4033
|
try {
|
|
4034
|
+
const lineageContext = createAgentLineageContext(kind, sid, lineageSeed);
|
|
3897
4035
|
const meta = agentDone
|
|
3898
4036
|
? kind === "claude"
|
|
3899
|
-
? createClaudeAgentMetadata(sid, cwd, opts.prompt ? "pending" : "none", launchOperationId, launchRequestDigest)
|
|
4037
|
+
? createClaudeAgentMetadata(sid, cwd, opts.prompt ? "pending" : "none", launchOperationId, launchRequestDigest, lineageContext)
|
|
3900
4038
|
: kind === "codex"
|
|
3901
|
-
? createCodexAgentMetadata(sid, cwd, opts.prompt ? "pending" : "none", { model, effort }, writeScope)
|
|
3902
|
-
: createGrokAgentMetadata(kind, sid, cwd, opts.prompt ? "pending" : "none", grokAuthPath, writeScope)
|
|
4039
|
+
? createCodexAgentMetadata(sid, cwd, opts.prompt ? "pending" : "none", { model, effort }, writeScope, lineageContext)
|
|
4040
|
+
: createGrokAgentMetadata(kind, sid, cwd, opts.prompt ? "pending" : "none", grokAuthPath, writeScope, lineageContext)
|
|
3903
4041
|
: null;
|
|
3904
4042
|
if (meta)
|
|
3905
4043
|
agentMetadataNegativeCache.delete(sid);
|
|
@@ -3939,7 +4077,7 @@ export function openAgent(kind, opts = {}) {
|
|
|
3939
4077
|
return [
|
|
3940
4078
|
sid,
|
|
3941
4079
|
`${label} を session ${sid} で起動した。${launchNote}${agentDone ? "agent_done 待機が有効。" : ""}` +
|
|
3942
|
-
`${agentDone
|
|
4080
|
+
`${agentDone ? " 通常のproject/user環境を共有し、aiterm所有の完了相関とsub-agent lineageだけを加算。" : ""}\n${hint}\n` +
|
|
3943
4081
|
driveHint +
|
|
3944
4082
|
`起動直後に増分 pty_read すると空/半描画になり得るので screen:true を使う。`,
|
|
3945
4083
|
];
|
|
@@ -3947,7 +4085,7 @@ export function openAgent(kind, opts = {}) {
|
|
|
3947
4085
|
export async function openAgentWithInitialPrompt(kind, opts = {}) {
|
|
3948
4086
|
const prompt = opts.prompt ?? null;
|
|
3949
4087
|
if (opts.launch_operation_id != null && prompt !== null) {
|
|
3950
|
-
throw new AitermError("launch_operation_idはpromptなしの
|
|
4088
|
+
throw new AitermError("launch_operation_idはpromptなしのClaude相関launchだけで指定できます", 2);
|
|
3951
4089
|
}
|
|
3952
4090
|
// v0.16.0: launcher は常に managed(Stop hook つき)で立つ。手動運転したい場合は
|
|
3953
4091
|
// pty_open で素の PTY を開き、vendor CLI を自分で send する。
|