aiterm-mcp 0.58.4 → 0.59.1
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/CHANGELOG.md +90 -1
- package/README.ja.md +15 -5
- package/README.md +14 -4
- package/dist/agent-shared.js +48 -1
- package/dist/core.js +868 -106
- package/dist/cursor-parent-receive.js +2 -1
- package/dist/fast-mode.js +22 -0
- package/dist/harnesses/claude.js +137 -19
- package/dist/harnesses/codex.js +126 -22
- package/dist/harnesses/cursor.js +96 -12
- package/dist/harnesses/grok.js +159 -2
- package/dist/index.js +41 -7
- package/dist/live-node.js +17 -0
- package/dist/model-catalog.js +1 -0
- package/dist/parent-delivery-cli.js +3 -2
- package/dist/runtime-error-report.js +2 -1
- package/dist/runtime-error-store.js +3 -2
- package/dist/user-config.js +138 -0
- package/docs/DESIGN.md +11 -0
- package/docs/RELEASE.md +5 -0
- package/package.json +2 -2
package/dist/harnesses/cursor.js
CHANGED
|
@@ -8,6 +8,7 @@ import { randomBytes } from "node:crypto";
|
|
|
8
8
|
import { AitermError } from "../errors.js";
|
|
9
9
|
import { spawnAgentControlCommand } from "../agent-resolver.js";
|
|
10
10
|
import { checkedCatalog } from "../model-catalog.js";
|
|
11
|
+
import { FastModeError } from "../fast-mode.js";
|
|
11
12
|
import { AGENT_DONE_POLL_MS, CURSOR_SCREEN_POLL_MS, pollGate, AGENT_EVENT_MAX_BYTES, agentEventPath, agentHarness, agentLineageFields, agentMetadataPath, createEmpty0600, readFileRange, safeStatSize, shq, sleep, subagentInstruction, writeAgentMetadata, writeScopeLaunchNote, } from "../agent-shared.js";
|
|
12
13
|
const CURSOR_TRANSCRIPT_MATCH_MAX_BYTES = 1024 * 1024;
|
|
13
14
|
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;
|
|
@@ -350,10 +351,15 @@ export function validateCursorModelEffort(model, effort) {
|
|
|
350
351
|
throw new AitermError("Cursor CLIのmodelへ既にparameter overrideがあります。reasoning_effortとの二重指定はできません", 2);
|
|
351
352
|
}
|
|
352
353
|
}
|
|
353
|
-
export function cursorModelArgument(model, effort) {
|
|
354
|
+
export function cursorModelArgument(model, effort, fast = false) {
|
|
354
355
|
if (!model)
|
|
355
356
|
return null;
|
|
356
|
-
|
|
357
|
+
// fastは完成形のIDの末尾(`<model>[-<effort>]-fast`)。そのIDが一覧にあるかは assertCursorModelAvailable が見る。
|
|
358
|
+
return `${model}${effort ? `-${effort}` : ""}${fast ? "-fast" : ""}`;
|
|
359
|
+
}
|
|
360
|
+
/** modelへ直に書かれたIDが、fast版の完成形か(`composer-2.5-fast`、`gpt-5.3-codex-low-fast`)。 */
|
|
361
|
+
export function cursorModelIsFastVariant(model) {
|
|
362
|
+
return !!model && model.endsWith("-fast");
|
|
357
363
|
}
|
|
358
364
|
const CURSOR_AUTH_TIMEOUT_MS = 5_000;
|
|
359
365
|
const CURSOR_MODELS_TIMEOUT_MS = 5_000;
|
|
@@ -426,11 +432,15 @@ export function cursorModelChoices(bin, cwd) {
|
|
|
426
432
|
export function cursorCatalogFromLines(lines) {
|
|
427
433
|
const choices = new Map();
|
|
428
434
|
let defaultModel = null;
|
|
435
|
+
// fast: true で起こす時に渡すID。鍵は `${model}` と `${model}-${effort}`、値は一覧にある `-fast` 付きの完成形。
|
|
436
|
+
const fastIds = {};
|
|
429
437
|
for (const line of lines) {
|
|
430
438
|
const parsed = cursorBaseModel(line.id);
|
|
431
439
|
if (!parsed)
|
|
432
440
|
continue;
|
|
433
441
|
const choice = choices.get(parsed.base) ?? { efforts: new Set(), label: null };
|
|
442
|
+
if (parsed.fast)
|
|
443
|
+
fastIds[parsed.effort ? `${parsed.base}-${parsed.effort}` : parsed.base] = line.id;
|
|
434
444
|
// -fast版だけにあるeffortは `${model}-${effort}` の連結で作れないので数えない。
|
|
435
445
|
if (parsed.effort && !parsed.fast)
|
|
436
446
|
choice.efforts.add(parsed.effort);
|
|
@@ -452,13 +462,19 @@ export function cursorCatalogFromLines(lines) {
|
|
|
452
462
|
efforts: CURSOR_SELECTABLE_EFFORTS.filter((effort) => choice.efforts.has(effort)),
|
|
453
463
|
default_effort: null,
|
|
454
464
|
hidden: false,
|
|
465
|
+
// そのmodelのどれかの組(effortなし、またはeffort付き)にfast版がある。組ごとの有無は起動時に一覧で確かめる。
|
|
466
|
+
fast: Object.keys(fastIds).some((key) => key === id || key.startsWith(`${id}-`)),
|
|
455
467
|
})),
|
|
468
|
+
fast_ids: fastIds,
|
|
456
469
|
});
|
|
457
470
|
}
|
|
458
|
-
export function assertCursorModelAvailable(bin, cwd, model, effort) {
|
|
459
|
-
const effective = cursorModelArgument(model, effort);
|
|
471
|
+
export function assertCursorModelAvailable(bin, cwd, model, effort, fast = false) {
|
|
472
|
+
const effective = cursorModelArgument(model, effort, fast);
|
|
460
473
|
const models = cursorModelCatalog(bin, cwd);
|
|
461
|
-
|
|
474
|
+
if (fast && !models.includes(effective)) {
|
|
475
|
+
throw new FastModeError("FAST_MODE_NOT_SUPPORTED", `Cursor の一覧に ${JSON.stringify(effective)} がありません(model=${model}${effort ? `、reasoning_effort=${effort}` : ""} のfast版)。起動していません。`);
|
|
476
|
+
}
|
|
477
|
+
const available = effort || fast ? models.includes(effective) : models.includes(model) || models.some((id) => id.startsWith(`${model}-`));
|
|
462
478
|
if (!available) {
|
|
463
479
|
throw new AitermError(`Cursor model catalog に ${JSON.stringify(effective)} がありません。` +
|
|
464
480
|
"modelとreasoning_effortから作る正規IDが存在しないため、別modelへfallbackせず中止しました", 2);
|
|
@@ -468,7 +484,7 @@ export function cursorEffortNavigation(screen, effort) {
|
|
|
468
484
|
const label = CURSOR_EFFORT_LABELS[effort.toLowerCase()];
|
|
469
485
|
if (!label)
|
|
470
486
|
throw new AitermError(`Cursorのreasoning_effort ${JSON.stringify(effort)} は未対応です`, 2);
|
|
471
|
-
const choices = screen
|
|
487
|
+
const choices = cursorParameterLines(screen).flatMap((line) => {
|
|
472
488
|
const match = line.match(/^\s*(→\s*)?[●○◯]\s+(.+?)(?:\s+✓)?\s*$/);
|
|
473
489
|
return match ? [{ selected: !!match[1], label: match[2] }] : [];
|
|
474
490
|
});
|
|
@@ -496,10 +512,10 @@ export function assertCursorAuthenticationReady(bin) {
|
|
|
496
512
|
"通常端末で `agent status` が成功することを確認してください。", 2);
|
|
497
513
|
}
|
|
498
514
|
/** 起動argv(先頭が実行ファイル)。paneのshellに合わせた引用は呼び出し側が行う。 */
|
|
499
|
-
export function cursorAgentArgv(bin, model, effort, prompt, meta) {
|
|
515
|
+
export function cursorAgentArgv(bin, model, effort, prompt, meta, fast = false) {
|
|
500
516
|
validateCursorModelEffort(model, effort);
|
|
501
517
|
const parts = [bin];
|
|
502
|
-
const modelArg = cursorModelArgument(model, effort);
|
|
518
|
+
const modelArg = cursorModelArgument(model, effort, fast);
|
|
503
519
|
if (modelArg)
|
|
504
520
|
parts.push("--model", modelArg);
|
|
505
521
|
// managed agentは人の対話承認を待てない。Cursor公式の無人運転フラグで
|
|
@@ -511,9 +527,9 @@ export function cursorAgentArgv(bin, model, effort, prompt, meta) {
|
|
|
511
527
|
parts.push(meta ? cursorPromptWithLineage(meta, prompt) : prompt);
|
|
512
528
|
return parts;
|
|
513
529
|
}
|
|
514
|
-
export function buildCursorAgentCmd(bin, model, effort, prompt, meta) {
|
|
530
|
+
export function buildCursorAgentCmd(bin, model, effort, prompt, meta, fast = false) {
|
|
515
531
|
// 固定の語彙(--force、--mode ask等)は引用しない。実行ファイル、model、promptだけを引用する。
|
|
516
|
-
const argv = cursorAgentArgv(bin, model, effort, prompt, meta);
|
|
532
|
+
const argv = cursorAgentArgv(bin, model, effort, prompt, meta, fast);
|
|
517
533
|
return argv.map((part, index) => index === 0 || argv[index - 1] === "--model" || (prompt !== null && index === argv.length - 1) ? shq(part) : part).join(" ");
|
|
518
534
|
}
|
|
519
535
|
/**
|
|
@@ -551,8 +567,11 @@ const CURSOR_START_PROMPT_MARKER_RE = /(?:^|\n)\s*(?:→|->)\s*Plan,\s*search,\s
|
|
|
551
567
|
export function cursorTuiReady(screen) {
|
|
552
568
|
if (CURSOR_FOLLOWUP_MARKER_RE.test(screen))
|
|
553
569
|
return true;
|
|
554
|
-
|
|
555
|
-
|
|
570
|
+
// 入力欄の案内(Plan, search, build anything)はCursorの入力欄だけが出す文なので、見出し(Cursor Agent)が画面の外へ流れた後も
|
|
571
|
+
// 入力待ちと読む。まだ番を流していない席で `/model` の選ぶ画面を開いて閉じると、見出しは流れて戻らない(2026.10.01)。
|
|
572
|
+
if (CURSOR_START_PROMPT_MARKER_RE.test(screen))
|
|
573
|
+
return true;
|
|
574
|
+
return /Cursor Agent/i.test(screen) && CURSOR_COMPOSER_MARKER_RE.test(screen);
|
|
556
575
|
}
|
|
557
576
|
// 利用上限に達したCursorは、入力欄も戻さずに説明を出して止まる(2026.09.26-dd393fe、macOSで実測)。
|
|
558
577
|
// transcriptには、user turnを残さずにturn_ended(status "error")だけを書く事がある(実記録で8件)。その時は完了の条件
|
|
@@ -642,3 +661,68 @@ export function cursorAuthPane(screen) {
|
|
|
642
661
|
return { url: authUrl(screen, ["cursor.com", "www.cursor.com", "auth.cursor.com"]),
|
|
643
662
|
user_code: null, input_required: false, message: null };
|
|
644
663
|
}
|
|
664
|
+
/**
|
|
665
|
+
* `/model` の画面の今の姿を、画面の最後の行(操作の案内)で読む。list は選ぶ画面、parameters はパラメータの画面、null は閉じている。
|
|
666
|
+
* Cursorは選ぶ画面を閉じても前の行を消さないので(上に残る)、文字の有無では今の姿が分からない。
|
|
667
|
+
*/
|
|
668
|
+
export function cursorPickerView(screen) {
|
|
669
|
+
const last = screen.split("\n").map((line) => line.trim()).filter(Boolean).at(-1) ?? "";
|
|
670
|
+
if (/Enter to select\s*•\s*Esc to go back/.test(last))
|
|
671
|
+
return "parameters";
|
|
672
|
+
if (/Enter to select\s*•\s*Tab to edit/.test(last))
|
|
673
|
+
return "list";
|
|
674
|
+
return null;
|
|
675
|
+
}
|
|
676
|
+
/** 今開いているパラメータの画面の行(最後の「Edit Parameters」の見出しより後)。見出しが無ければ画面の全部。 */
|
|
677
|
+
function cursorParameterLines(screen) {
|
|
678
|
+
const lines = screen.split("\n");
|
|
679
|
+
let title = lines.length - 1;
|
|
680
|
+
while (title >= 0 && !lines[title].includes("Edit Parameters"))
|
|
681
|
+
title--;
|
|
682
|
+
return lines.slice(title + 1);
|
|
683
|
+
}
|
|
684
|
+
/** 今開いているパラメータの画面の選べる行。cursor は選択中(→)の行、label は印と✓を除いた名前(Low・High・Fast 等)。 */
|
|
685
|
+
export function cursorParameterRows(screen) {
|
|
686
|
+
return cursorParameterLines(screen).flatMap((line) => {
|
|
687
|
+
const match = line.trim().match(/^(→\s*)?[●○◯◉]\s+(.+?)(?:\s+✓)?\s*$/u);
|
|
688
|
+
return match ? [{ label: match[2], cursor: !!match[1] }] : [];
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
/** 今開いているパラメータの画面で、そのeffortの行に選択済みの印(✓)が付いているか。 */
|
|
692
|
+
export function cursorEffortSelected(screen, label) {
|
|
693
|
+
return cursorParameterLines(screen).some((line) => new RegExp(`[●○◯]\\s+${label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s+✓\\s*$`).test(line));
|
|
694
|
+
}
|
|
695
|
+
// ---- ファスト。Cursorは `/model` のパラメータの画面(Edit Parameters)に「Fast」の行がある(2026.10.01)。
|
|
696
|
+
// 入っていない: 「◯ Fast」 / 入っている: 「◉ Fast」。行が無いmodelはfastへ切り替えられない。
|
|
697
|
+
/** パラメータの画面のFastの行。state は今の入り切り、down は選択中(→)の行からFastの行までの下矢印の数。 */
|
|
698
|
+
export function cursorFastParameter(screen) {
|
|
699
|
+
if (!screen.includes("Edit Parameters"))
|
|
700
|
+
return null;
|
|
701
|
+
// 画面は行ごとに横へずれて描かれる事がある。行の中身だけを見る。
|
|
702
|
+
const rows = cursorParameterLines(screen).map((line) => line.trim()).filter((line) => /^(?:→\s*)?[●○◯◉]\s+\S/u.test(line));
|
|
703
|
+
const fast = rows.findIndex((line) => /^(?:→\s*)?[◯◉]\s+Fast\s*$/u.test(line));
|
|
704
|
+
const selected = rows.findIndex((line) => line.startsWith("→"));
|
|
705
|
+
if (fast < 0 || selected < 0)
|
|
706
|
+
return null;
|
|
707
|
+
return { state: rows[fast].includes("◉"), down: fast - selected };
|
|
708
|
+
}
|
|
709
|
+
/** 見出しのmodelの表示(「Codex 5.3 Low Fast」)が、ファストが入っている形か。 */
|
|
710
|
+
export function cursorHeaderShowsFast(screen) {
|
|
711
|
+
// 見出しは「<modelの表示> <権限の表示>」の1行。権限の表示は Run Everything/Ask 等(画面の幅で後ろが切れる事がある)。
|
|
712
|
+
const header = screen.split("\n").map((line) => line.trim())
|
|
713
|
+
.filter((line) => /\S\s{2,}(?:Run|Auto-Run|Ask|Plan|Manual)\b/.test(line)).at(-1);
|
|
714
|
+
if (!header)
|
|
715
|
+
return null;
|
|
716
|
+
return /\bFast\s{2,}/.test(header);
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Cursorが、起動の --model と `/model` の選択のたびに、利用者の ~/.cursor/cli-config.json へ書く項目(次の会話の既定のmodelとパラメータ)。
|
|
720
|
+
* 書く版と書かない版がある(2026.10.01-14929f9は書く。fastを入れた選択なら `selectedModel.parameters` に fast: true が残る)。
|
|
721
|
+
* 席だけの切り替えにするため、切り替えの直前の値へ戻す。
|
|
722
|
+
*/
|
|
723
|
+
export const CURSOR_DEFAULT_MODEL_KEYS = [
|
|
724
|
+
"model", "hasChangedDefaultModel", "modelParameters", "selectedModel", "modelSelectionHistory", "maxMode",
|
|
725
|
+
];
|
|
726
|
+
export function cursorUserConfigPath(cursorHome) {
|
|
727
|
+
return path.join(cursorHome, "cli-config.json");
|
|
728
|
+
}
|
package/dist/harnesses/grok.js
CHANGED
|
@@ -117,16 +117,41 @@ export function grokCatalogFromInitialize(stdout, summary = "") {
|
|
|
117
117
|
// 候補に無い既定値は黙って捨てず、checkedCatalogで形式異常にする。
|
|
118
118
|
default_effort: efforts.length > 0 ? defaultEffort : null,
|
|
119
119
|
hidden: false,
|
|
120
|
+
fast: false,
|
|
120
121
|
};
|
|
121
122
|
});
|
|
123
|
+
// Grokのfastは別のmodel(表示名が「<元の表示名> Fast」。1.0.50: Grok 4.7 と Grok 4.7 Fast=grok-4.7-build-fast)。
|
|
124
|
+
// fast版は別のmodelとして並べず、元のmodelの fast: true にまとめる。元のmodelが一覧に無いfast版は、そのまま1つのmodelとして残す。
|
|
125
|
+
const fastIds = grokFastVariants(models);
|
|
126
|
+
const variants = new Set(Object.values(fastIds));
|
|
127
|
+
const current = typeof state.currentModelId === "string" ? state.currentModelId : null;
|
|
128
|
+
const baseOf = (id) => Object.keys(fastIds).find((base) => fastIds[base] === id) ?? id;
|
|
122
129
|
return checkedCatalog("Grok", {
|
|
123
130
|
source: "grok agent stdio initialize (_meta.modelState)",
|
|
124
131
|
harness_version: typeof meta.agentVersion === "string" ? meta.agentVersion : null,
|
|
125
|
-
default_model:
|
|
132
|
+
default_model: baseOf(current),
|
|
126
133
|
adapter_efforts: {},
|
|
127
|
-
models
|
|
134
|
+
models: models.filter((model) => !variants.has(model.id))
|
|
135
|
+
.map((model) => ({ ...model, fast: model.id in fastIds })),
|
|
136
|
+
fast_ids: fastIds,
|
|
128
137
|
});
|
|
129
138
|
}
|
|
139
|
+
/** 元のmodel ID → fast版のmodel ID。表示名が「<元の表示名> Fast」のmodelを、その元のmodelのfast版と読む。 */
|
|
140
|
+
export function grokFastVariants(models) {
|
|
141
|
+
const byName = new Map(models.flatMap((model) => model.display_name ? [[model.display_name, model.id]] : []));
|
|
142
|
+
const pairs = {};
|
|
143
|
+
for (const model of models) {
|
|
144
|
+
const match = model.display_name?.match(/^(.+) Fast$/);
|
|
145
|
+
const base = match ? byName.get(match[1]) : undefined;
|
|
146
|
+
if (base && base !== model.id)
|
|
147
|
+
pairs[base] = model.id;
|
|
148
|
+
}
|
|
149
|
+
return pairs;
|
|
150
|
+
}
|
|
151
|
+
/** model IDが、一覧の上でほかのmodelのfast版なら、その元のmodel ID。 */
|
|
152
|
+
export function grokFastBase(catalog, model) {
|
|
153
|
+
return Object.keys(catalog.fast_ids ?? {}).find((base) => catalog.fast_ids?.[base] === model) ?? null;
|
|
154
|
+
}
|
|
130
155
|
export function assertGrokModelAvailable(bin, cwd, model) {
|
|
131
156
|
const models = grokModelCatalog(bin, cwd);
|
|
132
157
|
if (!models.includes(model)) {
|
|
@@ -630,3 +655,135 @@ export function grokAuthPane(screen) {
|
|
|
630
655
|
}
|
|
631
656
|
return { url, user_code: embedded, input_required: false, message: null };
|
|
632
657
|
}
|
|
658
|
+
// ---- ファスト。Grokのfastは別のmodel。Grokは、起動の --model と、動いている席の `/model <ID> [effort]`・`/effort` で、
|
|
659
|
+
// 利用者のconfig.tomlの [models] の default と default_reasoning_effort を書き換える(1.0.50で確かめた。「✓ Default model: …」)。
|
|
660
|
+
// 席だけの切り替えにするため、切り替えの直前の値へ戻す。
|
|
661
|
+
const GROK_DEFAULT_KEYS = ["default", "default_reasoning_effort"];
|
|
662
|
+
/** [models] の節の中の `<key> = …` の行の番号。節や行が無ければnull。 */
|
|
663
|
+
function grokModelsLine(rows, key) {
|
|
664
|
+
const start = rows.findIndex((row) => /^\s*\[models\]\s*(?:#.*)?$/.test(row));
|
|
665
|
+
if (start < 0)
|
|
666
|
+
return null;
|
|
667
|
+
for (let index = start + 1; index < rows.length && !/^\s*\[/.test(rows[index]); index++) {
|
|
668
|
+
if (new RegExp(`^\\s*${key}\\s*=`).test(rows[index]))
|
|
669
|
+
return index;
|
|
670
|
+
}
|
|
671
|
+
return null;
|
|
672
|
+
}
|
|
673
|
+
export function readGrokModelDefaults(configPath) {
|
|
674
|
+
let body;
|
|
675
|
+
try {
|
|
676
|
+
body = fs.readFileSync(configPath, "utf8");
|
|
677
|
+
}
|
|
678
|
+
catch {
|
|
679
|
+
body = null;
|
|
680
|
+
}
|
|
681
|
+
return grokModelDefaultsFromBody(body);
|
|
682
|
+
}
|
|
683
|
+
/** config.tomlの中身(読めなかった時はnull)から、[models] の既定を読む。 */
|
|
684
|
+
export function grokModelDefaultsFromBody(body) {
|
|
685
|
+
if (body === null)
|
|
686
|
+
return { body: null, default: { present: false, value: null }, default_reasoning_effort: { present: false, value: null } };
|
|
687
|
+
const rows = body.split(/\r?\n/);
|
|
688
|
+
const pick = (key) => {
|
|
689
|
+
const line = grokModelsLine(rows, key);
|
|
690
|
+
if (line === null)
|
|
691
|
+
return { present: false, value: null };
|
|
692
|
+
const value = rows[line].match(/=\s*"([^"\\]*)"\s*(?:#.*)?$/);
|
|
693
|
+
return { present: true, value: value ? value[1] : null };
|
|
694
|
+
};
|
|
695
|
+
return { body, default: pick("default"), default_reasoning_effort: pick("default_reasoning_effort") };
|
|
696
|
+
}
|
|
697
|
+
function withoutGrokDefaults(body) {
|
|
698
|
+
const rows = body.split(/\r?\n/);
|
|
699
|
+
for (const key of GROK_DEFAULT_KEYS) {
|
|
700
|
+
const line = grokModelsLine(rows, key);
|
|
701
|
+
if (line !== null)
|
|
702
|
+
rows.splice(line, 1);
|
|
703
|
+
}
|
|
704
|
+
return rows.join("\n");
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Grokがconfig.tomlへ書いた [models] の既定(default・default_reasoning_effort)を、切り替えの直前の値へ戻す。
|
|
708
|
+
* 切り替えの前後で、ほかの行が同じなら前の中身をそのまま書き戻す。ほかの行も変わっていたら、既定の行だけを直す。
|
|
709
|
+
* 前の中身を読めなかった時、元の値が読めない書き方だった時、今のfileに [models] の節が無い時は、その項目に触らない。
|
|
710
|
+
*/
|
|
711
|
+
export function restoreGrokModelDefaults(configPath, before) {
|
|
712
|
+
if (before.body === null)
|
|
713
|
+
return;
|
|
714
|
+
let now;
|
|
715
|
+
try {
|
|
716
|
+
now = fs.readFileSync(configPath, "utf8");
|
|
717
|
+
}
|
|
718
|
+
catch {
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
if (now === before.body)
|
|
722
|
+
return;
|
|
723
|
+
const eol = now.includes("\r\n") ? "\r\n" : "\n";
|
|
724
|
+
const rows = now.split(/\r?\n/);
|
|
725
|
+
for (const key of GROK_DEFAULT_KEYS) {
|
|
726
|
+
const pin = before[key];
|
|
727
|
+
if (pin.present && pin.value === null)
|
|
728
|
+
continue;
|
|
729
|
+
const line = grokModelsLine(rows, key);
|
|
730
|
+
if (pin.present) {
|
|
731
|
+
const wanted = `${key} = ${JSON.stringify(pin.value)}`;
|
|
732
|
+
if (line !== null)
|
|
733
|
+
rows[line] = wanted;
|
|
734
|
+
else {
|
|
735
|
+
const header = rows.findIndex((row) => /^\s*\[models\]\s*(?:#.*)?$/.test(row));
|
|
736
|
+
if (header >= 0)
|
|
737
|
+
rows.splice(header + 1, 0, wanted);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
else if (line !== null) {
|
|
741
|
+
rows.splice(line, 1);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
const edited = rows.join(eol);
|
|
745
|
+
if (edited === now)
|
|
746
|
+
return;
|
|
747
|
+
// ほかの行が前と同じなら、前の中身(空白や並びもそのまま)を書き戻す。
|
|
748
|
+
const next = withoutGrokDefaults(edited) === withoutGrokDefaults(before.body) ? before.body : edited;
|
|
749
|
+
const temp = `${configPath}.aiterm-restore-${process.pid}`;
|
|
750
|
+
fs.writeFileSync(temp, next, { mode: fs.statSync(configPath).mode & 0o777 });
|
|
751
|
+
try {
|
|
752
|
+
fs.renameSync(temp, configPath);
|
|
753
|
+
}
|
|
754
|
+
catch (error) {
|
|
755
|
+
// Windowsでは、ほかのprocessがfileを開いている間、置き換えに失敗する事がある。途中のfileを残さず、呼び手に次の回で試させる。
|
|
756
|
+
try {
|
|
757
|
+
fs.unlinkSync(temp);
|
|
758
|
+
}
|
|
759
|
+
catch { /* 残っても中身は前の値 */ }
|
|
760
|
+
throw error;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* 足元の枠の行の「<modelの表示> (<effort>)」から、modelの表示(「Grok 4.7 Fast」)を読む。権限の表示(· always-approve)は、付く時と付かない時がある。
|
|
765
|
+
* 切り替えの知らせ(Switched to Grok 4.7 Fast (high effort))は、かっこの中に空白があるので拾わない。
|
|
766
|
+
*/
|
|
767
|
+
export function grokFooterModelName(screen) {
|
|
768
|
+
let name = null;
|
|
769
|
+
for (const line of screen.split("\n")) {
|
|
770
|
+
const match = line.match(/(Grok [^()·│╯╰─]+?) \([a-z-]+\)/);
|
|
771
|
+
if (match)
|
|
772
|
+
name = match[1].trim();
|
|
773
|
+
}
|
|
774
|
+
return name;
|
|
775
|
+
}
|
|
776
|
+
/** 足元の「Grok 4.7 Fast (high)」から、今のmodelを一覧のIDで返す。base は元のmodel、fast はfast版かどうか。 */
|
|
777
|
+
export function grokModelFromFooter(screen, catalog) {
|
|
778
|
+
const name = grokFooterModelName(screen);
|
|
779
|
+
if (!name)
|
|
780
|
+
return null;
|
|
781
|
+
const fastIds = catalog.fast_ids ?? {};
|
|
782
|
+
for (const model of catalog.models) {
|
|
783
|
+
if (model.display_name === name)
|
|
784
|
+
return { id: model.id, base: model.id, fast: false };
|
|
785
|
+
if (fastIds[model.id] && `${model.display_name} Fast` === name)
|
|
786
|
+
return { id: fastIds[model.id], base: model.id, fast: true };
|
|
787
|
+
}
|
|
788
|
+
return null;
|
|
789
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -834,15 +834,27 @@ registerRemoteAwareTool("agent_auth", {
|
|
|
834
834
|
return fail(error);
|
|
835
835
|
}
|
|
836
836
|
});
|
|
837
|
+
// ファスト(高速の応答。ADR 0114)。断りの符号は4つのharnessで共通で、文の先頭に付く。
|
|
838
|
+
const fastModeErrorCodes = ["FAST_MODE_NOT_SUPPORTED", "FAST_MODE_UNAVAILABLE", "FAST_MODE_MODEL_CONFLICT", "FAST_MODE_UNCONFIRMED"];
|
|
839
|
+
const fastModeFailureSchema = z.object({
|
|
840
|
+
code: z.enum(fastModeErrorCodes),
|
|
841
|
+
message: z.string(),
|
|
842
|
+
});
|
|
843
|
+
const fastModeDesc = "ファスト(harnessの高速の応答。費用や利用量が増える)。席は必ず通常の速度で起きる。trueはその席をfastへ切り替え、falseは通常の速度にする" +
|
|
844
|
+
"(fastに対応していないmodelでもfalseは受ける)。切り替えは席だけに留め、harnessが利用者の既定へ書いた分は切り替えの直前の値へ戻す。" +
|
|
845
|
+
"断りは文の先頭の符号で分かる: FAST_MODE_NOT_SUPPORTED(そのmodelは対応していない)、FAST_MODE_UNAVAILABLE(アカウントや契約で使えない)、" +
|
|
846
|
+
"FAST_MODE_MODEL_CONFLICT(modelへfast版のIDを書いてfalseを渡した)、FAST_MODE_UNCONFIRMED(切り替えた後の状態を確かめられない)。";
|
|
837
847
|
registerRemoteAwareTool("agent_configure", {
|
|
838
|
-
description: "起動済みのClaude/Codex/Grok/Cursor agent sessionを再起動せず、会話contextを保ったままmodel/reasoning effortを変更する。" +
|
|
848
|
+
description: "起動済みのClaude/Codex/Grok/Cursor agent sessionを再起動せず、会話contextを保ったままmodel/reasoning effort/fastを変更する。" +
|
|
839
849
|
"各harnessのCLI標準model操作を使う。Cursorのreasoning_effort変更はmodelと同時指定する。" +
|
|
840
850
|
"Claude Codeの席では /model の選ぶ画面の「この会話だけ」(s)で替え、端末の ~/.claude/settings.json の既定は書き換えない。" +
|
|
841
|
-
"その選び方が無い版、選ぶ画面に無いmodel、そのmodelで選べないeffortは、何も変えずに断る。"
|
|
851
|
+
"その選び方が無い版、選ぶ画面に無いmodel、そのmodelで選べないeffortは、何も変えずに断る。" +
|
|
852
|
+
"fastだけの指定も受ける。" + fastModeDesc,
|
|
842
853
|
inputSchema: {
|
|
843
854
|
session_id: z.string().regex(/^[A-Za-z0-9_-]{1,64}$/),
|
|
844
855
|
model: z.string().min(1).nullish().describe("変更後のmodel。省略時はmodelを変更しない"),
|
|
845
856
|
reasoning_effort: z.string().min(1).nullish().describe("変更後のreasoning effort。省略時はeffortを変更しない"),
|
|
857
|
+
fast: z.boolean().nullish().describe("変更後のfast。省略時は、その席の今の状態を変えない"),
|
|
846
858
|
},
|
|
847
859
|
outputSchema: {
|
|
848
860
|
schema: z.literal("aiterm.agent-configure-result.v1"),
|
|
@@ -851,10 +863,11 @@ registerRemoteAwareTool("agent_configure", {
|
|
|
851
863
|
harness: z.enum(["claude-code", "codex-cli", "grok-cli", "cursor-cli"]),
|
|
852
864
|
model: z.string().nullable(),
|
|
853
865
|
reasoning_effort: z.string().nullable(),
|
|
866
|
+
fast: z.boolean().nullable().optional().describe("切り替えた後の、席のfastの状態。Aitermが確かめた事の無い席(前の版が起こした席)はnull"),
|
|
854
867
|
},
|
|
855
|
-
}, async ({ session_id, model, reasoning_effort }) => {
|
|
868
|
+
}, async ({ session_id, model, reasoning_effort, fast }) => {
|
|
856
869
|
try {
|
|
857
|
-
const result = await core.configureAgent(session_id, { model, reasoning_effort });
|
|
870
|
+
const result = await core.configureAgent(session_id, { model, reasoning_effort, fast });
|
|
858
871
|
return {
|
|
859
872
|
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
860
873
|
structuredContent: { ...result },
|
|
@@ -870,11 +883,14 @@ const agentModelChoiceSchema = z.object({
|
|
|
870
883
|
efforts: z.array(z.string()).describe("このmodelで選べるreasoning_effort。空ならeffort非対応"),
|
|
871
884
|
default_effort: z.string().nullable(),
|
|
872
885
|
hidden: z.boolean().describe("harnessが一覧で隠すmodel(Codexのinclude_hidden時だけtrueがある)"),
|
|
886
|
+
fast: z.boolean().describe("このharness・このmodelでfastへ切り替えられるか(agent_launch/agent_configureのfast: true)。" +
|
|
887
|
+
"fastが別のmodel IDのharness(Cursor・Grok)は、fast版を別のmodelとして並べず、元のmodelのここがtrueになる"),
|
|
873
888
|
});
|
|
874
889
|
registerRemoteAwareTool("agent_models", {
|
|
875
890
|
description: "harnessが今返すmodelとreasoning effortの候補を、そのharnessの公式の一覧から取得する。promptもturnも送らず推論を消費しない。" +
|
|
876
891
|
"Codexはapp-server model/list、Claude Codeはstream-json initializeのmodels、Grokはagent stdio initializeのmodelState、" +
|
|
877
892
|
"Cursorはcursor-agent modelsを素のmodel IDとeffortへ分けたもの。返るidとeffortsはagent_launch/agent_configureへそのまま渡せる。" +
|
|
893
|
+
"modelごとのfastは、そのmodelでfastへ切り替えられるか(harnessが返す値)。fast_unavailableは、アカウントの都合でどのmodelでも使えない時の訳。" +
|
|
878
894
|
"取得不能はMODEL_CATALOG_UNAVAILABLE、形式異常はMODEL_CATALOG_INVALIDのエラーで返し、別の一覧へfallbackしない。" +
|
|
879
895
|
"remoteを付けると別端末のharnessの候補を返す。",
|
|
880
896
|
inputSchema: {
|
|
@@ -890,6 +906,7 @@ registerRemoteAwareTool("agent_models", {
|
|
|
890
906
|
default_model: z.string().nullable().describe("model省略時にharnessが使うmodel。一覧のIDで表せなければnull"),
|
|
891
907
|
efforts: z.array(z.string()).describe("全modelのeffortの和"),
|
|
892
908
|
adapter_efforts: z.record(z.string(), z.string()).describe("harnessの一覧には無く、Aitermのadapterが足したeffortと理由"),
|
|
909
|
+
fast_unavailable: fastModeFailureSchema.nullable().optional().describe("このアカウント・この端末では、どのmodelでもfastを使えない時の訳(harnessが先に答えられる時だけ。今はClaude Code)。使える・分からない時はnull"),
|
|
893
910
|
models: z.array(agentModelChoiceSchema),
|
|
894
911
|
},
|
|
895
912
|
}, async ({ harness, cwd, include_hidden }) => {
|
|
@@ -907,6 +924,11 @@ registerRemoteAwareTool("agent_models", {
|
|
|
907
924
|
});
|
|
908
925
|
// 新規APIの選択軸はharness。旧4 launcher名は移行期間中の薄いaliasとして同じ実装へ流す。
|
|
909
926
|
const kindForHarness = (harness) => harness === "claude-code" ? "claude" : harness === "codex-cli" ? "codex" : harness === "cursor-cli" ? "cursor" : "grok";
|
|
927
|
+
const fastModeLaunchDesc = "ファスト(harnessの高速の応答。費用や利用量が増える)。省略とfalseは通常の速度で起こす(利用者の設定にfastが残っていても通常)。" +
|
|
928
|
+
"trueは通常で起こしてから、その席をfastへ切り替える。modelがfastに対応していない時(FAST_MODE_NOT_SUPPORTED)と、" +
|
|
929
|
+
"modelへfast版のIDを書いてfalseを渡した時(FAST_MODE_MODEL_CONFLICT)は起動を断る。アカウントの都合で使えない時・確かめられない時は、" +
|
|
930
|
+
"起動を断らずに通常の速度で起こし、返りの fast を false、fast_error に訳を入れる。" +
|
|
931
|
+
"modelへfast版のIDを直に書いて省略した時だけ、そのIDのまま起こす(返りの fast は true)。";
|
|
910
932
|
const agentModelDesc = (kind) => kind === "claude"
|
|
911
933
|
? "起動モデル(例: claude-sonnet-4-6)。省略時はClaude CLI既定"
|
|
912
934
|
: kind === "codex"
|
|
@@ -966,7 +988,7 @@ async function launchRemoteAgent(target, harness, args, extra) {
|
|
|
966
988
|
}
|
|
967
989
|
async function launchAgent(kind, args, extra) {
|
|
968
990
|
const supportsWriteScope = kind !== "claude";
|
|
969
|
-
const { prompt, image, throughline_source_session, throughline_supplement_file, model, reasoning_effort, env_vars, cwd, session_name, launch_operation_id, write_scope, trust_project } = args;
|
|
991
|
+
const { prompt, image, throughline_source_session, throughline_supplement_file, model, reasoning_effort, fast, env_vars, cwd, session_name, launch_operation_id, write_scope, trust_project } = args;
|
|
970
992
|
let delivery = null;
|
|
971
993
|
try {
|
|
972
994
|
if (!supportsWriteScope && write_scope !== undefined) {
|
|
@@ -975,12 +997,13 @@ async function launchAgent(kind, args, extra) {
|
|
|
975
997
|
const initialPrompt = image && image.length > 0 ? core.attachImages(prompt ?? "", image) : prompt ?? undefined;
|
|
976
998
|
if (initialPrompt || throughline_source_session)
|
|
977
999
|
delivery = await deliveryForRequest(extra);
|
|
978
|
-
const [sid, hint, eventCursor, submitResidue, initialDelivery, startup] = await core.openAgentWithInitialPrompt(kind, {
|
|
1000
|
+
const [sid, hint, eventCursor, submitResidue, initialDelivery, startup, fastState] = await core.openAgentWithInitialPrompt(kind, {
|
|
979
1001
|
prompt: initialPrompt,
|
|
980
1002
|
throughline_source_session,
|
|
981
1003
|
throughline_supplement_file,
|
|
982
1004
|
model: model ?? undefined,
|
|
983
1005
|
reasoning_effort: reasoning_effort ?? undefined,
|
|
1006
|
+
fast: fast ?? undefined,
|
|
984
1007
|
env_vars,
|
|
985
1008
|
trust_project,
|
|
986
1009
|
before_send: delivery?.before_send,
|
|
@@ -1001,6 +1024,8 @@ async function launchAgent(kind, args, extra) {
|
|
|
1001
1024
|
submit_residue: submitResidue,
|
|
1002
1025
|
initial_prompt: initialDelivery,
|
|
1003
1026
|
startup,
|
|
1027
|
+
fast: fastState.fast,
|
|
1028
|
+
fast_error: fastState.fast_error,
|
|
1004
1029
|
...(supportsWriteScope && write_scope !== undefined
|
|
1005
1030
|
? {
|
|
1006
1031
|
write_scope,
|
|
@@ -1012,8 +1037,11 @@ async function launchAgent(kind, args, extra) {
|
|
|
1012
1037
|
};
|
|
1013
1038
|
// 開始未確認(submitted_unconfirmed)は誤りにしない。sessionは立ち、promptは入力欄を離れ、配送も登録済み。
|
|
1014
1039
|
// 誤りで返すとClaude Code親は受け口のhookを走らせず、子の回答が届かない(ADR 0093)。状態はinitial_promptと本文で伝える。
|
|
1040
|
+
const fastLine = fastState.fast_error
|
|
1041
|
+
? `\nfast: false(通常の速度で起こした)。${fastState.fast_error.message}`
|
|
1042
|
+
: fast === true ? `\nfast: ${fastState.fast}` : "";
|
|
1015
1043
|
return {
|
|
1016
|
-
content: [{ type: "text", text: `session_id: ${sid}\n${hint}${deliveryIdLine(delivery)}` }],
|
|
1044
|
+
content: [{ type: "text", text: `session_id: ${sid}\n${hint}${fastLine}${deliveryIdLine(delivery)}` }],
|
|
1017
1045
|
structuredContent: structured,
|
|
1018
1046
|
};
|
|
1019
1047
|
}
|
|
@@ -1073,6 +1101,7 @@ function registerAgentTool(toolName, kind, desc) {
|
|
|
1073
1101
|
model: z.string().nullish().describe(agentModelDesc(kind)),
|
|
1074
1102
|
// CLI/model側の値集合が版で変わるため公開enumでは縛らない(core側も同方針)。
|
|
1075
1103
|
reasoning_effort: z.string().nullish().describe(agentEffortDesc(kind)),
|
|
1104
|
+
fast: z.boolean().nullish().describe(fastModeLaunchDesc),
|
|
1076
1105
|
env_vars: z.array(z.string()).optional().describe("起動したagentへ現在のMCP processから継承する環境変数名。値はtool引数へ渡さない"),
|
|
1077
1106
|
trust_project: z.boolean().optional().describe("対象projectを信頼し、既知のworkspace・project hooks・MCP初期同意を起動中に進める"),
|
|
1078
1107
|
cwd: z.string().nullish().describe("作業ディレクトリ(対象リポのルート等・任意)"),
|
|
@@ -1097,6 +1126,8 @@ function registerAgentTool(toolName, kind, desc) {
|
|
|
1097
1126
|
// 初回prompt dispatch後のsubmit座礁観測(additive)。true=composerに残存を確認(submit未成立の疑い)/
|
|
1098
1127
|
// false=残存を観測せず(submit成立の保証ではない)/ null=promptなし・判定不能。
|
|
1099
1128
|
submit_residue: z.boolean().nullable(),
|
|
1129
|
+
fast: z.boolean().optional().describe("起こした後の、席のfastの状態"),
|
|
1130
|
+
fast_error: fastModeFailureSchema.nullable().optional().describe("fastを頼まれたが、通常の速度で起こした時の訳"),
|
|
1100
1131
|
...writeScopeOutputSchema,
|
|
1101
1132
|
},
|
|
1102
1133
|
}, async (args, extra) => launchAgent(kind, args, extra));
|
|
@@ -1115,6 +1146,7 @@ server.registerTool("agent_launch", {
|
|
|
1115
1146
|
throughline_supplement_file: z.string().min(1).optional().describe("Throughline 0.10.8以降へそのまま渡すproject束縛済み長期記憶・知識の補足JSON path"),
|
|
1116
1147
|
model: z.string().nullish().describe("harnessが選ぶモデル。provider名ではなくlive catalog上のmodel ID"),
|
|
1117
1148
|
reasoning_effort: z.string().nullish().describe("harness adapterが標準CLI表現へ変換する思考レベル。Cursorではmodel同時指定が必要"),
|
|
1149
|
+
fast: z.boolean().nullish().describe(fastModeLaunchDesc),
|
|
1118
1150
|
env_vars: z.array(z.string()).optional().describe("現在のMCP processから継承する環境変数名"),
|
|
1119
1151
|
trust_project: z.boolean().optional().describe("対象projectを信頼し、既知のworkspace・project hooks・MCP初期同意を起動中に進める"),
|
|
1120
1152
|
cwd: z.string().nullish().describe("作業ディレクトリ(絶対パス・任意)"),
|
|
@@ -1138,6 +1170,8 @@ server.registerTool("agent_launch", {
|
|
|
1138
1170
|
wait_command: z.string().nullable(),
|
|
1139
1171
|
parent_delivery: parentDeliveryOutputSchema.optional(),
|
|
1140
1172
|
submit_residue: z.boolean().nullable(),
|
|
1173
|
+
fast: z.boolean().optional().describe("起こした後の、席のfastの状態(この項目を返さない版の別端末では無い)"),
|
|
1174
|
+
fast_error: fastModeFailureSchema.nullable().optional().describe("fastを頼まれたが、通常の速度で起こした時の訳"),
|
|
1141
1175
|
write_scope: z.string().optional(),
|
|
1142
1176
|
write_scope_enforcement: z.enum(["enforced_read_only", "declaration_only_unsupported"]).optional(),
|
|
1143
1177
|
},
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
/**
|
|
3
|
+
* このprocessのNodeを、後からでも起こせる場所で返す。自分でNodeを起こす時と、呼び手へ「起こす先」として渡す時に使う。
|
|
4
|
+
* `process.execPath`は起きた時の実体で、長く動くprocessでは後から消える事がある(Homebrewは版ごとの場所へ入れ、更新で前の版を消す。
|
|
5
|
+
* 2026-10-12、MacでNodeが26.10.0_1から26.11.1へ上がり、前の版から起きていたMCP process 19個が、無いNodeを起こそうとして黙って失敗していた)。
|
|
6
|
+
* Homebrewの時は同じformulaのopt(更新でも残る)を返す。実体が消えていて代わりも無い時は、PATHから探させる(`node`)。
|
|
7
|
+
* aiterm-steer-deliveryの同じ名前の関数と同じ決まり(0.4.6〜)。
|
|
8
|
+
*/
|
|
9
|
+
export function liveNodeExecutable(executable = process.execPath, exists = existsSync) {
|
|
10
|
+
const brew = /^(.*)\/Cellar\/(node(?:@\d+)?)\/[^/]+\/bin\/node$/.exec(executable);
|
|
11
|
+
if (brew) {
|
|
12
|
+
const opt = `${brew[1]}/opt/${brew[2]}/bin/node`;
|
|
13
|
+
if (exists(opt))
|
|
14
|
+
return opt;
|
|
15
|
+
}
|
|
16
|
+
return exists(executable) ? executable : "node";
|
|
17
|
+
}
|
package/dist/model-catalog.js
CHANGED
|
@@ -50,6 +50,7 @@ export function agentModelsResult(harness, catalog) {
|
|
|
50
50
|
default_model: catalog.default_model,
|
|
51
51
|
efforts: sortEfforts(catalog.models.flatMap(model => model.efforts)),
|
|
52
52
|
adapter_efforts: catalog.adapter_efforts,
|
|
53
|
+
fast_unavailable: catalog.fast_unavailable ?? null,
|
|
53
54
|
models: catalog.models,
|
|
54
55
|
};
|
|
55
56
|
}
|
|
@@ -22,6 +22,7 @@ import { createRequire } from "node:module";
|
|
|
22
22
|
import { fileURLToPath } from "node:url";
|
|
23
23
|
import { realCodexHome, readCodexWakeResult } from "aiterm-steer-delivery";
|
|
24
24
|
import { AITERM_PROFILE } from "./steer-profile.js";
|
|
25
|
+
import { liveNodeExecutable } from "./live-node.js";
|
|
25
26
|
import { checkCodexParent, submitCodexParentAnswer, withCodexReceiver } from "./codex-parent-receiver.js";
|
|
26
27
|
import { codexHookDeliveryState, codexHookDirectory, codexInputDirectory } from "./codex-hook-state.js";
|
|
27
28
|
export const PARENT_DELIVERY_SCHEMA = "aiterm.parent-delivery.v1";
|
|
@@ -134,7 +135,7 @@ async function deliveryState(target, delivery) {
|
|
|
134
135
|
* アプリ配下のprocessは、PATHが細い事がある(2026-10-07、素のsshの環境からの呼び出しで起きた)。
|
|
135
136
|
* nodeの場所がPATHに無い時だけ頭へ足す。既にあれば、並びも中身も変えない。
|
|
136
137
|
*/
|
|
137
|
-
export function pathWithNode(env, node =
|
|
138
|
+
export function pathWithNode(env, node = liveNodeExecutable(), platform = process.platform) {
|
|
138
139
|
const windows = platform === "win32";
|
|
139
140
|
// Windowsの環境変数名は大文字小文字を区別しない(`Path`で入っている事が多い)。
|
|
140
141
|
const key = Object.keys(env).find(name => windows ? name.toLowerCase() === "path" : name === "PATH") ?? "PATH";
|
|
@@ -146,7 +147,7 @@ export function pathWithNode(env, node = process.execPath, platform = process.pl
|
|
|
146
147
|
}
|
|
147
148
|
/** この命令の場所と版。製品側の入口(aiterm-steer-delivery)が、呼べる相手かどうかを確かめるのに使う。 */
|
|
148
149
|
export function providerDescription() {
|
|
149
|
-
return { version: pkg.version, node:
|
|
150
|
+
return { version: pkg.version, node: liveNodeExecutable(), cli: fileURLToPath(import.meta.url) };
|
|
150
151
|
}
|
|
151
152
|
export async function runParentDelivery(argv) {
|
|
152
153
|
const [kind, command, ...args] = argv;
|
|
@@ -7,6 +7,7 @@ import { createRequire } from "node:module";
|
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
import { defaultRuntimeErrorReportPaths, readBoundedFile, runtimeErrorReportingStatus } from "./runtime-error-os.js";
|
|
10
|
+
import { liveNodeExecutable } from "./live-node.js";
|
|
10
11
|
import { RuntimeErrorStore } from "./runtime-error-store.js";
|
|
11
12
|
const pkg = createRequire(import.meta.url)("../package.json");
|
|
12
13
|
const WORKER = fileURLToPath(new URL("./runtime-error-worker.js", import.meta.url));
|
|
@@ -252,7 +253,7 @@ export function triggerRuntimeErrorReport(env = process.env) {
|
|
|
252
253
|
try {
|
|
253
254
|
if (runtimeErrorReportingStatus(defaultRuntimeErrorReportPaths().reportingConfigPath) !== "enabled")
|
|
254
255
|
return false;
|
|
255
|
-
const child = spawn(
|
|
256
|
+
const child = spawn(liveNodeExecutable(), [WORKER, "report"], { stdio: "ignore", detached: true, windowsHide: true, env });
|
|
256
257
|
child.once("error", () => { });
|
|
257
258
|
child.unref();
|
|
258
259
|
return true;
|
|
@@ -4,6 +4,7 @@ import * as fs from "node:fs";
|
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import { defaultRuntimeErrorReportPaths, runtimeErrorReportingStatus } from "./runtime-error-os.js";
|
|
7
|
+
import { liveNodeExecutable } from "./live-node.js";
|
|
7
8
|
import { defaultRuntimeErrorPaths, windowsPrivateDaclCommand, expectedHostProfiles, readBoundedFile, processStartIdentity, forceKill } from "./runtime-error-os.js";
|
|
8
9
|
export { defaultRuntimeErrorPaths, windowsPrivateDaclCommand, windowsPrivateDaclVerifyCommand } from "./runtime-error-os.js";
|
|
9
10
|
import { fileURLToPath } from "node:url";
|
|
@@ -673,7 +674,7 @@ export function recordRuntimeError(code, options = {}) {
|
|
|
673
674
|
fixedStoreFailure(stderr);
|
|
674
675
|
} };
|
|
675
676
|
try {
|
|
676
|
-
const child = spawn(
|
|
677
|
+
const child = spawn(liveNodeExecutable(), [options.workerPath ?? WORKER, "record", code], {
|
|
677
678
|
stdio: "ignore", windowsHide: true, env: process.env,
|
|
678
679
|
});
|
|
679
680
|
const timer = setTimeout(() => { report(); forceKill(child); }, options.timeoutMs ?? defaultWorkerTimeoutMs("record"));
|
|
@@ -707,7 +708,7 @@ export async function runtimeErrorStoreDiagnostic(options = {}) {
|
|
|
707
708
|
} };
|
|
708
709
|
let child;
|
|
709
710
|
try {
|
|
710
|
-
child = spawn(
|
|
711
|
+
child = spawn(liveNodeExecutable(), [options.workerPath ?? WORKER, "diagnostic"], {
|
|
711
712
|
stdio: ["ignore", "pipe", "ignore"], windowsHide: true, env: process.env,
|
|
712
713
|
});
|
|
713
714
|
}
|