aiterm-mcp 0.58.3 → 0.59.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/CHANGELOG.md +98 -1
- package/README.ja.md +15 -5
- package/README.md +14 -4
- package/dist/agent-shared.js +12 -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 +95 -3
- 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/parent-delivery.js +55 -3
- package/dist/process-runtime.js +34 -6
- 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/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;
|
package/dist/parent-delivery.js
CHANGED
|
@@ -64,6 +64,22 @@ async function submitParentAnswer(parent, deliveryId, text) {
|
|
|
64
64
|
const OWNER_IDENTITY_RECHECK_MS = 60_000;
|
|
65
65
|
// owner.jsonの無い保存場所を、作っている途中の持ち主として扱う時間。
|
|
66
66
|
const OWNER_SETUP_GRACE_MS = 60_000;
|
|
67
|
+
// 持ち主が自分の保存場所へ付ける「生きている印」(ADR 0112)。中身は無く、最後に書いた時刻(mtime)だけを使う。
|
|
68
|
+
// 印が新しい持ち主は、OSへ聞かずに生きていると読む。pidを使い回した別のprocessは、この印を書かない。
|
|
69
|
+
// 印の無い持ち主(0.58.3までの版)と、印が古い持ち主だけ、今までどおり開始時刻で照合する。
|
|
70
|
+
// 名前は.jsonで終わらせない(回収は、保存場所の.jsonを配送の記録として引き取る)。
|
|
71
|
+
const OWNER_HEARTBEAT_FILE = "alive";
|
|
72
|
+
const OWNER_HEARTBEAT_MS = 15_000;
|
|
73
|
+
// 持ち主のprocessが同期の処理で塞がる間(最長で十数秒)も印を新しいと読めるよう、書く間隔の4倍にする。
|
|
74
|
+
const OWNER_HEARTBEAT_FRESH_MS = 60_000;
|
|
75
|
+
function ownerHeartbeatAt(directory) {
|
|
76
|
+
try {
|
|
77
|
+
return fs.statSync(path.join(directory, OWNER_HEARTBEAT_FILE)).mtimeMs;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
67
83
|
function processExists(pid) {
|
|
68
84
|
// 0以下はprocess groupを指す。持ち主のpidにはならない。
|
|
69
85
|
if (pid <= 0)
|
|
@@ -129,10 +145,11 @@ export class ParentDeliveryManager {
|
|
|
129
145
|
verifiedOwners = new Map();
|
|
130
146
|
serviceError = null;
|
|
131
147
|
closing = false;
|
|
148
|
+
lastHeartbeat = Number.NEGATIVE_INFINITY;
|
|
132
149
|
constructor(options = {}) {
|
|
133
150
|
this.deps = { observe: observeAnywhere, answer: answerAnywhere, submit: submitParentAnswer,
|
|
134
151
|
verify: verifyParent, processes: readProcessIdentities, exists: processExists,
|
|
135
|
-
now: () => performance.now(), ...options.dependencies };
|
|
152
|
+
now: () => performance.now(), wall: () => Date.now(), ...options.dependencies };
|
|
136
153
|
const stateRoot = ensureStateRoot();
|
|
137
154
|
// 別端末の子の記録はremote-で始まる保存場所へ分け、boundary.remoteを知らない旧版のreaderへ渡さない。
|
|
138
155
|
const prefix = options.remote ? "remote-" : "";
|
|
@@ -414,14 +431,40 @@ export class ParentDeliveryManager {
|
|
|
414
431
|
this.recovering = this.recoverOrphans().finally(() => { this.recovering = null; });
|
|
415
432
|
return this.recovering;
|
|
416
433
|
}
|
|
434
|
+
/** 自分の保存場所の「生きている印」を新しくする。書けない回は次の回へ回す(印が古くなれば、相手は開始時刻で照合する)。 */
|
|
435
|
+
heartbeat() {
|
|
436
|
+
const wall = this.deps.wall();
|
|
437
|
+
if (this.closing || Math.abs(wall - this.lastHeartbeat) < OWNER_HEARTBEAT_MS)
|
|
438
|
+
return;
|
|
439
|
+
const file = path.join(this.ownerDir, OWNER_HEARTBEAT_FILE);
|
|
440
|
+
try {
|
|
441
|
+
const at = new Date(wall);
|
|
442
|
+
try {
|
|
443
|
+
fs.utimesSync(file, at, at);
|
|
444
|
+
}
|
|
445
|
+
catch (error) {
|
|
446
|
+
if (error.code !== "ENOENT")
|
|
447
|
+
throw error;
|
|
448
|
+
fs.writeFileSync(file, "", { mode: 0o600 });
|
|
449
|
+
fs.utimesSync(file, at, at);
|
|
450
|
+
}
|
|
451
|
+
this.lastHeartbeat = wall;
|
|
452
|
+
}
|
|
453
|
+
catch { /* 次の回 */ }
|
|
454
|
+
}
|
|
417
455
|
async recoverOrphans() {
|
|
456
|
+
this.heartbeat();
|
|
418
457
|
// 生存を確かめる相手は、closeしていない他の持ち主だけ。居なければOSへ問い合わせない。
|
|
419
|
-
// この処理は5秒おきに全MCP process
|
|
458
|
+
// この処理は5秒おきに全MCP processで回る。processを起こして確かめると、MCP processの数の掛け算で端末を重くする
|
|
459
|
+
// (Windowsで、23個のMCP processがPowerShellを毎秒4回起こしていた。2026-10-12)。
|
|
420
460
|
const directories = fs.readdirSync(this.active, { withFileTypes: true })
|
|
421
461
|
.filter((directory) => directory.isDirectory() && path.join(this.active, directory.name) !== this.ownerDir);
|
|
422
462
|
const now = this.deps.now();
|
|
463
|
+
const wall = this.deps.wall();
|
|
423
464
|
const fate = new Map();
|
|
424
465
|
const unverified = [];
|
|
466
|
+
// 前の照合を覚えている持ち主。誰かを照合する回に、まとめて照合し直す。
|
|
467
|
+
const remembered = [];
|
|
425
468
|
for (const directory of directories) {
|
|
426
469
|
let owner;
|
|
427
470
|
try {
|
|
@@ -437,14 +480,23 @@ export class ParentDeliveryManager {
|
|
|
437
480
|
fate.set(directory.name, "gone");
|
|
438
481
|
continue;
|
|
439
482
|
}
|
|
483
|
+
const beat = ownerHeartbeatAt(path.join(this.active, directory.name));
|
|
484
|
+
if (beat !== null && Math.abs(wall - beat) < OWNER_HEARTBEAT_FRESH_MS) {
|
|
485
|
+
fate.set(directory.name, "alive");
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
440
488
|
const verified = this.verifiedOwners.get(directory.name);
|
|
441
489
|
if (verified && verified.pid === pid && verified.started_identity === owner.started_identity
|
|
442
|
-
&& now - verified.at < OWNER_IDENTITY_RECHECK_MS)
|
|
490
|
+
&& now - verified.at < OWNER_IDENTITY_RECHECK_MS) {
|
|
443
491
|
fate.set(directory.name, verified.alive ? "alive" : "unverified");
|
|
492
|
+
remembered.push({ name: directory.name, pid, started_identity: owner.started_identity });
|
|
493
|
+
}
|
|
444
494
|
else
|
|
445
495
|
unverified.push({ name: directory.name, pid, started_identity: owner.started_identity });
|
|
446
496
|
}
|
|
447
497
|
if (unverified.length > 0) {
|
|
498
|
+
// 覚えた時刻が持ち主ごとにずれると、1分の間に何度もprocessを起こす。照合する回は、全員を1回で照合し直して時刻を揃える。
|
|
499
|
+
unverified.push(...remembered);
|
|
448
500
|
const processes = this.deps.processes([...new Set(unverified.map((entry) => entry.pid))]);
|
|
449
501
|
for (const entry of unverified) {
|
|
450
502
|
const row = processes.find((candidate) => candidate.pid === entry.pid);
|
package/dist/process-runtime.js
CHANGED
|
@@ -101,17 +101,31 @@ export function readRuntimeProcesses() {
|
|
|
101
101
|
};
|
|
102
102
|
});
|
|
103
103
|
}
|
|
104
|
+
/** そのpidのprocessが今あるか。OSへ直接聞き、processを起こさない。権限が無くて触れないprocessも「ある」と読む。 */
|
|
105
|
+
function pidExists(pid) {
|
|
106
|
+
if (pid <= 0)
|
|
107
|
+
return false;
|
|
108
|
+
try {
|
|
109
|
+
process.kill(pid, 0);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
return error.code === "EPERM";
|
|
114
|
+
}
|
|
115
|
+
}
|
|
104
116
|
/**
|
|
105
117
|
* 指定したpidの開始時刻だけを引く。readRuntimeProcessesと同じ開始時刻を返し、存在しないpidは結果に含めない。
|
|
106
118
|
* process表にpidはあるが開始時刻を読めない時は、started_identityをnullで返す(pidが無いのとは区別する)。
|
|
107
119
|
* 全processのargvを読む一覧取得は、processの多い端末で重い(定期実行から呼ばない)。
|
|
108
120
|
*/
|
|
109
121
|
export function readProcessIdentities(pids) {
|
|
110
|
-
|
|
122
|
+
if (pids.some(pid => !Number.isSafeInteger(pid) || pid < 0))
|
|
123
|
+
throw new AitermError("process照会のpidが不正です", 2);
|
|
124
|
+
// 居ないpidは、OSへ直接聞いて先に除く。全部居なければ、processを起こさない。
|
|
125
|
+
// macOSのpsは、範囲の外のpidが1つ混ざると、居るprocessの分も出さずに誤りで終わる。
|
|
126
|
+
const wanted = [...new Set(pids)].filter(pidExists);
|
|
111
127
|
if (wanted.length === 0)
|
|
112
128
|
return [];
|
|
113
|
-
if (wanted.some(pid => !Number.isSafeInteger(pid) || pid < 0))
|
|
114
|
-
throw new AitermError("process照会のpidが不正です", 2);
|
|
115
129
|
if (!isWin) {
|
|
116
130
|
const result = spawnSync(POSIX_PS, [...POSIX_IDENTITY_ARGS, wanted.join(",")], {
|
|
117
131
|
encoding: "utf8", env: { ...process.env, LC_ALL: "C" }, timeout: 10000,
|
|
@@ -129,11 +143,25 @@ export function readProcessIdentities(pids) {
|
|
|
129
143
|
}
|
|
130
144
|
// CommandLineの読めないprocess(別の権限のserviceなど)も返す。pidが使い回された先を「別のprocess」と確かめるのに要る。
|
|
131
145
|
// 開始時刻を読めない行も、pidがprocess表にある印として返す。
|
|
146
|
+
// WMI(Win32_Process)は使わない。絞っても1回に0.1〜0.3秒かかり、WMIの側のprocess(wmiprvse)のCPUを使う
|
|
147
|
+
// (process 1300個のWindowsで測った 2026-10-12。ADR 0112)。.NETのprocessの読み(OpenProcessとGetProcessTimes)は1個1.5ミリ秒。
|
|
148
|
+
// 開始時刻は同じ値になる(WMIはマイクロ秒で切り捨てた値を返し、どちらも1ミリ秒で切り捨てて使う。888個のprocessで食い違いは0)。
|
|
149
|
+
// 権限が無くて読めないprocessだけ、WMIへ聞き直す。
|
|
132
150
|
const script = [
|
|
133
151
|
...WINDOWS_PROBE_HEADER,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
"}
|
|
152
|
+
"$rows=@()",
|
|
153
|
+
`foreach ($id in @(${wanted.join(",")})) {`,
|
|
154
|
+
" try { $p=[Diagnostics.Process]::GetProcessById($id) } catch [ArgumentException] { continue }",
|
|
155
|
+
" $started=$null",
|
|
156
|
+
" try { $started=$p.StartTime.ToUniversalTime().ToString('o') }",
|
|
157
|
+
" catch [InvalidOperationException] { continue }",
|
|
158
|
+
" catch {",
|
|
159
|
+
" $w=Get-CimInstance Win32_Process -Filter \"ProcessId=$id\" -Property CreationDate",
|
|
160
|
+
" if ($null -eq $w) { continue }",
|
|
161
|
+
" if ($null -ne $w.CreationDate) { $started=$w.CreationDate.ToUniversalTime().ToString('o') }",
|
|
162
|
+
" }",
|
|
163
|
+
" $rows += [ordered]@{ pid=[int]$id; started_identity=$started }",
|
|
164
|
+
"}",
|
|
137
165
|
"ConvertTo-Json -Compress -InputObject $rows",
|
|
138
166
|
].join("\n");
|
|
139
167
|
const result = spawnSync(resolveWindowsPowerShell7(), [...WINDOWS_PROBE_ARGS, Buffer.from(script, "utf16le").toString("base64")], {
|
|
@@ -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
|
}
|