aiterm-mcp 0.27.9 → 0.28.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 +43 -37
- package/README.md +44 -40
- package/dist/agent-resolver.js +14 -5
- package/dist/agent-shared.js +18 -5
- package/dist/aiterm-wait-cli.js +1 -1
- package/dist/core.js +171 -22
- package/dist/index.js +110 -58
- package/dist/vendors/codex.js +2 -1
- package/dist/vendors/cursor.js +393 -0
- package/dist/vendors/grok.js +2 -1
- package/package.json +2 -2
package/dist/agent-shared.js
CHANGED
|
@@ -164,7 +164,18 @@ export function agentLabel(kind) {
|
|
|
164
164
|
? "Grok Build(Composer)"
|
|
165
165
|
: kind === "grok"
|
|
166
166
|
? "Grok Build(Grok)"
|
|
167
|
-
: "
|
|
167
|
+
: kind === "cursor"
|
|
168
|
+
? "Cursor Agent CLI"
|
|
169
|
+
: "Codex";
|
|
170
|
+
}
|
|
171
|
+
export function agentHarness(kind) {
|
|
172
|
+
return kind === "claude"
|
|
173
|
+
? "claude-code"
|
|
174
|
+
: kind === "codex"
|
|
175
|
+
? "codex-cli"
|
|
176
|
+
: kind === "cursor"
|
|
177
|
+
? "cursor-cli"
|
|
178
|
+
: "grok-cli";
|
|
168
179
|
}
|
|
169
180
|
export function subagentInstruction(meta) {
|
|
170
181
|
if (meta.agent_role !== "subagent" ||
|
|
@@ -193,10 +204,12 @@ export function subagentInstruction(meta) {
|
|
|
193
204
|
export function writeScopeLaunchNote(kind, writeScope) {
|
|
194
205
|
return writeScope === undefined
|
|
195
206
|
? ""
|
|
196
|
-
:
|
|
197
|
-
? `\n能力宣言: write_scope=${JSON.stringify(writeScope)}
|
|
198
|
-
|
|
199
|
-
|
|
207
|
+
: kind === "cursor" && writeScope === "read-only"
|
|
208
|
+
? `\n能力宣言: write_scope=${JSON.stringify(writeScope)}。Cursor Agent CLIへ --mode ask を付与し、書込みを実効禁止。`
|
|
209
|
+
: (kind === "codex" || kind === "grok" || kind === "composer") && writeScope === "read-only"
|
|
210
|
+
? `\n能力宣言: write_scope=${JSON.stringify(writeScope)}。${agentLabel(kind)} CLIへ --sandbox read-only を付与し、書込みを実効禁止。` +
|
|
211
|
+
(kind === "codex" ? "" : "MCPツール許可は --always-approve で自動承認(sandbox内のため能力拡大なし)。")
|
|
212
|
+
: `\n能力宣言: write_scope=${JSON.stringify(writeScope)}。パス単位のsandbox allowlistに対応するCLI引数がないため宣言の記録のみ(構造的unsupported)。`;
|
|
200
213
|
}
|
|
201
214
|
export function agentLineageFields(context) {
|
|
202
215
|
return {
|
package/dist/aiterm-wait-cli.js
CHANGED
|
@@ -42,7 +42,7 @@ export function parseArgs(argv) {
|
|
|
42
42
|
if (cursor !== null)
|
|
43
43
|
throw new Error(`--cursor が重複しています。${USAGE}`);
|
|
44
44
|
if (!/^\d+$/.test(v))
|
|
45
|
-
throw new Error(`--cursor は0
|
|
45
|
+
throw new Error(`--cursor は0以上の整数完了境界だけを受理します。${USAGE}`);
|
|
46
46
|
cursor = Number(v);
|
|
47
47
|
}
|
|
48
48
|
else {
|
package/dist/core.js
CHANGED
|
@@ -14,13 +14,15 @@ import { createHash, randomBytes } from "node:crypto";
|
|
|
14
14
|
import * as rtk from "./rtk.js";
|
|
15
15
|
import { AitermError, telemetryOwnedFailure, ownTelemetryFailure } from "./errors.js";
|
|
16
16
|
import { isWin, SOCKDIR, tmuxCommand, loadPtyBufferChunk, pasteBufferBaseArgs, TMUX_EMPTY_CONFIG, attachCommand, normalizePaneCommand, appendMarkSentinel, settlePaneLog, paneCwdArgument, } from "./tmux-runtime.js";
|
|
17
|
-
import { sleep, currentUid, runtimeStateBase, safeStatSize, readFileRange, writeJson0600, createEmpty0600, shq, LAUNCH_ID_RE, AGENT_DONE_POLL_MS, AGENT_EVENT_MAX_BYTES, assertSessionName, agentsDir, agentEventPath, agentMetadataPath, writeAgentMetadata, AGENT_EVENT_TAIL_BYTES, agentLabel, agentLineageFields, } from "./agent-shared.js";
|
|
17
|
+
import { sleep, currentUid, runtimeStateBase, safeStatSize, readFileRange, writeJson0600, createEmpty0600, shq, LAUNCH_ID_RE, AGENT_DONE_POLL_MS, AGENT_EVENT_MAX_BYTES, assertSessionName, agentsDir, agentEventPath, agentMetadataPath, writeAgentMetadata, AGENT_EVENT_TAIL_BYTES, agentLabel, agentHarness, subagentInstruction, agentLineageFields, } from "./agent-shared.js";
|
|
18
18
|
import { GROK_MODEL_DEFAULTS, realGrokHome, resolveAndValidateGrokAuth, assertGrokModelAvailable, grokEventsTranscript, latestGrokCompletion, grokInitializationComplete, observeGrokDone, buildGrokAgentCmd, grokLaunchNote, grokEnvTokens, grokTuiReady, GROK_COMPOSER_MARKER_RE, grokFooterHasConfiguration, grokTranscriptText, createGrokAgentMetadata, } from "./vendors/grok.js";
|
|
19
19
|
import { bindCodexTranscriptSession, latestCodexCompletion, observeCodexDone, buildCodexAgentCmd, codexLaunchNote, codexTuiReady, CODEX_COMPOSER_MARKER_RE, codexModelChoice, codexEffortChoice, codexMoreReasoningChoice, codexTranscriptText, createCodexAgentMetadata, } from "./vendors/codex.js";
|
|
20
20
|
import { OPERATION_ID_RE, CLAUDE_RESULT_MAX_BYTES, CLAUDE_EFFORTS, agentManagedClaudeSettingsPath, agentClaudeResultPath, agentClaudeOperationPath, agentClaudeApprovalReceiptPath, agentClaudeDispatchReceiptPath, validateOperationId, readClaudeResultText, assertClaudeAuthenticationReady, buildClaudeAgentCmd, claudeLaunchNote, claudeTuiReady, CLAUDE_COMPOSER_MARKER_RE, createClaudeAgentMetadata, } from "./vendors/claude.js";
|
|
21
|
+
import { bindCursorTranscriptSession, cursorTurnBoundary, latestCursorCompletion, observeCursorDone, cursorTranscriptText, assertCursorAuthenticationReady, assertCursorModelAvailable, buildCursorAgentCmd, createCursorAgentMetadata, cursorLaunchNote, cursorEffortNavigation, cursorTuiReady, CURSOR_COMPOSER_MARKER_RE, validateCursorModelEffort, } from "./vendors/cursor.js";
|
|
21
22
|
import { resolveAgentBin, resolveThroughlineBin, runThroughlineHandoffContext, isWindowsNativeExecutable, agentBinForPaneShell, resolveWinPaneShell } from "./agent-resolver.js";
|
|
22
23
|
export { AitermError } from "./errors.js";
|
|
23
24
|
export { tmuxSpawnEnv } from "./tmux-runtime.js";
|
|
25
|
+
export { agentHarness } from "./agent-shared.js";
|
|
24
26
|
// 完了検出
|
|
25
27
|
export const DEFAULT_TIMEOUT = 10.0;
|
|
26
28
|
const POLL = 0.25;
|
|
@@ -202,11 +204,12 @@ function cleanupAgentState(name) {
|
|
|
202
204
|
f.endsWith(".claude-settings.json") ||
|
|
203
205
|
f.endsWith(".claude-mcp.json") ||
|
|
204
206
|
f.endsWith(".claude-result.json") ||
|
|
207
|
+
f.endsWith(".cursor-result.json") ||
|
|
205
208
|
f.endsWith(".claude-operation.json") ||
|
|
206
209
|
f.endsWith(".claude-approval.json") ||
|
|
207
210
|
f.endsWith(".claude-dispatch"))
|
|
208
211
|
fs.unlinkSync(p);
|
|
209
|
-
else if (f.endsWith(".codex-home") || f.endsWith(".grok-home") || f.endsWith(".home")) {
|
|
212
|
+
else if (f.endsWith(".codex-home") || f.endsWith(".grok-home") || f.endsWith(".cursor-plugin") || f.endsWith(".home")) {
|
|
210
213
|
fs.rmSync(p, { recursive: true, force: true });
|
|
211
214
|
}
|
|
212
215
|
}
|
|
@@ -923,6 +926,7 @@ export function listSessions() {
|
|
|
923
926
|
return line;
|
|
924
927
|
const agent = [
|
|
925
928
|
`agent=${meta.kind}`,
|
|
929
|
+
`harness=${agentHarness(meta.kind)}`,
|
|
926
930
|
"agent_done=true",
|
|
927
931
|
meta.write_scope === undefined ? null : `write_scope=${JSON.stringify(meta.write_scope)}`,
|
|
928
932
|
meta.vendor_session_id ? `vendor_session_id=${meta.vendor_session_id}` : null,
|
|
@@ -1584,7 +1588,7 @@ function loadAgentMetadata(name) {
|
|
|
1584
1588
|
throw new AitermError(`agent metadata を読めません: ${e.message}`, 2);
|
|
1585
1589
|
}
|
|
1586
1590
|
const m = raw;
|
|
1587
|
-
if ((m.kind !== "claude" && m.kind !== "codex" && m.kind !== "grok" && m.kind !== "composer") ||
|
|
1591
|
+
if ((m.kind !== "claude" && m.kind !== "codex" && m.kind !== "grok" && m.kind !== "composer" && m.kind !== "cursor") ||
|
|
1588
1592
|
m.aiterm_session !== name ||
|
|
1589
1593
|
typeof m.launch_id !== "string" ||
|
|
1590
1594
|
!LAUNCH_ID_RE.test(m.launch_id)) {
|
|
@@ -1655,6 +1659,31 @@ function loadAgentMetadata(name) {
|
|
|
1655
1659
|
codex_home: recordedHome,
|
|
1656
1660
|
};
|
|
1657
1661
|
}
|
|
1662
|
+
if (m.kind === "cursor") {
|
|
1663
|
+
const recordedHome = m.cursor_home;
|
|
1664
|
+
if (m.hook_route !== "shared_cursor_home" ||
|
|
1665
|
+
m.completion_route !== "cursor_transcript" ||
|
|
1666
|
+
typeof recordedHome !== "string" ||
|
|
1667
|
+
!path.isAbsolute(recordedHome)) {
|
|
1668
|
+
throw new AitermError("agent metadata の cursor_home が不正です(絶対パスの記録が必要)", 2);
|
|
1669
|
+
}
|
|
1670
|
+
return {
|
|
1671
|
+
kind: "cursor",
|
|
1672
|
+
aiterm_session: name,
|
|
1673
|
+
launch_id: m.launch_id,
|
|
1674
|
+
event_file: expectedEvent,
|
|
1675
|
+
created_at: typeof m.created_at === "string" ? m.created_at : "",
|
|
1676
|
+
cwd: typeof m.cwd === "string" ? m.cwd : null,
|
|
1677
|
+
...(typeof m.write_scope === "string" ? { write_scope: m.write_scope } : {}),
|
|
1678
|
+
vendor_session_id: typeof m.vendor_session_id === "string" ? m.vendor_session_id : null,
|
|
1679
|
+
initial_prompt: normalizeInitialPromptState(m.initial_prompt),
|
|
1680
|
+
hook_route: "shared_cursor_home",
|
|
1681
|
+
completion_route: "cursor_transcript",
|
|
1682
|
+
...loadAgentLineageFields(m, true),
|
|
1683
|
+
node_platform: process.platform,
|
|
1684
|
+
cursor_home: recordedHome,
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1658
1687
|
// grok_home も codex_home と同じ理由で per-launch の記録値を正とする(席専用 GROK_HOME)。
|
|
1659
1688
|
// 認証正本の照合も記録された home に対して行う——現在の env の home と比較すると、
|
|
1660
1689
|
// 席専用 home で起動した session が別 instance から拒否される。
|
|
@@ -1791,6 +1820,15 @@ function bindCompletedInitialPrompt(meta) {
|
|
|
1791
1820
|
setInitialPromptState(meta, "done");
|
|
1792
1821
|
return;
|
|
1793
1822
|
}
|
|
1823
|
+
if (meta.kind === "cursor" && meta.completion_route === "cursor_transcript") {
|
|
1824
|
+
const done = latestCursorCompletion(meta, readTranscriptLines);
|
|
1825
|
+
if (!done) {
|
|
1826
|
+
throw new AitermError(`agent session '${meta.aiterm_session}' は起動時 prompt の完了待ちです。${agentWaitGuide(meta.aiterm_session)}`, 2);
|
|
1827
|
+
}
|
|
1828
|
+
bindAgentVendorSession(meta, done);
|
|
1829
|
+
setInitialPromptState(meta, "done");
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
1794
1832
|
if ((meta.kind === "grok" || meta.kind === "composer") && meta.completion_route === "grok_transcript") {
|
|
1795
1833
|
if (!latestGrokCompletion(meta, readTranscriptLines)) {
|
|
1796
1834
|
throw new AitermError(`agent session '${meta.aiterm_session}' は起動時 prompt の完了待ちです。${agentWaitGuide(meta.aiterm_session)}`, 2);
|
|
@@ -1828,6 +1866,9 @@ function tryLoadAgentMetadata(name) {
|
|
|
1828
1866
|
function latestAgentDoneEvent(meta, expectedOperationId = null) {
|
|
1829
1867
|
if (meta.kind === "codex")
|
|
1830
1868
|
return latestCodexCompletion(meta, readTranscriptLines);
|
|
1869
|
+
if (meta.kind === "cursor" && meta.completion_route === "cursor_transcript") {
|
|
1870
|
+
return latestCursorCompletion(meta, readTranscriptLines);
|
|
1871
|
+
}
|
|
1831
1872
|
if ((meta.kind === "grok" || meta.kind === "composer") && meta.completion_route === "grok_transcript") {
|
|
1832
1873
|
return latestGrokCompletion(meta, readTranscriptLines);
|
|
1833
1874
|
}
|
|
@@ -1893,6 +1934,14 @@ function recoverAgentVendorSession(meta) {
|
|
|
1893
1934
|
setInitialPromptState(meta, "done");
|
|
1894
1935
|
return;
|
|
1895
1936
|
}
|
|
1937
|
+
if (meta.kind === "cursor") {
|
|
1938
|
+
const transcript = bindCursorTranscriptSession(meta);
|
|
1939
|
+
if (!transcript)
|
|
1940
|
+
return;
|
|
1941
|
+
if (meta.initial_prompt === "pending" && latestCursorCompletion(meta, readTranscriptLines))
|
|
1942
|
+
setInitialPromptState(meta, "done");
|
|
1943
|
+
return;
|
|
1944
|
+
}
|
|
1896
1945
|
const size = safeStatSize(meta.event_file);
|
|
1897
1946
|
if (size === 0)
|
|
1898
1947
|
return;
|
|
@@ -1916,6 +1965,10 @@ function agentCompletionCursor(meta) {
|
|
|
1916
1965
|
const transcript = grokEventsTranscript(meta);
|
|
1917
1966
|
return transcript ? safeStatSize(transcript) : 0;
|
|
1918
1967
|
}
|
|
1968
|
+
if (meta.kind === "cursor" && meta.completion_route === "cursor_transcript") {
|
|
1969
|
+
bindCursorTranscriptSession(meta);
|
|
1970
|
+
return cursorTurnBoundary(meta);
|
|
1971
|
+
}
|
|
1919
1972
|
if (meta.kind !== "codex")
|
|
1920
1973
|
return safeStatSize(meta.event_file);
|
|
1921
1974
|
const transcript = bindCodexTranscriptSession(meta);
|
|
@@ -1929,7 +1982,7 @@ function transcriptUnavailable() {
|
|
|
1929
1982
|
throw new AitermError(`transcript がまだありません。ターン完了後に再取得してください。${agentWaitGuide()}`, 2);
|
|
1930
1983
|
}
|
|
1931
1984
|
function transcriptNotFound(vendor) {
|
|
1932
|
-
throw new AitermError(`最終 assistant メッセージを特定できませんでした(vendor=${vendor})。screen で確認してください。`, 2);
|
|
1985
|
+
throw new AitermError(`最終 assistant メッセージを特定できませんでした(harness=${agentHarness(vendor)}, vendor=${vendor})。screen で確認してください。`, 2);
|
|
1933
1986
|
}
|
|
1934
1987
|
function readTranscriptLines(file) {
|
|
1935
1988
|
try {
|
|
@@ -2001,6 +2054,9 @@ export async function readAgentTranscript(name, o = {}) {
|
|
|
2001
2054
|
transcriptUnavailable();
|
|
2002
2055
|
text = readClaudeResultText(meta, done, operationId, transcriptUnavailable);
|
|
2003
2056
|
}
|
|
2057
|
+
else if (meta.kind === "cursor") {
|
|
2058
|
+
text = cursorTranscriptText(meta, readTranscriptLines, transcriptUnavailable);
|
|
2059
|
+
}
|
|
2004
2060
|
else if (meta.kind === "codex") {
|
|
2005
2061
|
text = codexTranscriptText(meta, turnId, readTranscriptLines, transcriptUnavailable);
|
|
2006
2062
|
}
|
|
@@ -2017,6 +2073,7 @@ export async function readAgentTranscript(name, o = {}) {
|
|
|
2017
2073
|
"agent_transcript",
|
|
2018
2074
|
`vendor=${meta.kind}`,
|
|
2019
2075
|
`turn_id=${turnId ?? "unknown"}`,
|
|
2076
|
+
`harness=${agentHarness(meta.kind)}`,
|
|
2020
2077
|
done?.operation_id ? `operation_id=${done.operation_id}` : null,
|
|
2021
2078
|
`raw_chars=${rawChars}`,
|
|
2022
2079
|
].filter(Boolean).join(" ");
|
|
@@ -2054,6 +2111,7 @@ function agentReadMetadataSuffix(name, screen) {
|
|
|
2054
2111
|
const bits = [
|
|
2055
2112
|
"agent",
|
|
2056
2113
|
`vendor=${meta.kind}`,
|
|
2114
|
+
`harness=${agentHarness(meta.kind)}`,
|
|
2057
2115
|
`initial_prompt=${meta.initial_prompt}`,
|
|
2058
2116
|
`agent_event_seen=${ev ? "true" : "false"}`,
|
|
2059
2117
|
"completion_attribution=none",
|
|
@@ -2145,7 +2203,7 @@ export function detectAgentRateLimit(kind, aitermSession) {
|
|
|
2145
2203
|
return null;
|
|
2146
2204
|
}
|
|
2147
2205
|
// 外部waiterプロセス用の純リーダー観測。lock・PTY・metadata書込・dispatch状態には一切触れない。
|
|
2148
|
-
// Codex
|
|
2206
|
+
// Codex/Cursor/Grokは各harnessのtranscript、Claudeはevent fileを増分走査し、vendor_session_idのbind永続化を
|
|
2149
2207
|
// 行わない(waiterは観測者であって所有者でない)。
|
|
2150
2208
|
export async function observeAgentDone(name, o = {}) {
|
|
2151
2209
|
const meta = loadAgentMetadata(name);
|
|
@@ -2154,12 +2212,15 @@ export async function observeAgentDone(name, o = {}) {
|
|
|
2154
2212
|
throw new AitermError("operation_id はClaude agent sessionだけで使用できます", 2);
|
|
2155
2213
|
}
|
|
2156
2214
|
if (o.cursor != null && (!Number.isInteger(o.cursor) || o.cursor < 0)) {
|
|
2157
|
-
throw new AitermError("cursor は0
|
|
2215
|
+
throw new AitermError("cursor は0以上の整数完了境界で指定してください", 2);
|
|
2158
2216
|
}
|
|
2159
2217
|
const timeout = o.timeout ?? DEFAULT_AGENT_DONE_TIMEOUT;
|
|
2160
2218
|
if (meta.kind === "codex" && meta.completion_route === "codex_transcript") {
|
|
2161
2219
|
return observeCodexDone(meta, timeout, o.cursor, detectAgentRateLimit);
|
|
2162
2220
|
}
|
|
2221
|
+
if (meta.kind === "cursor" && meta.completion_route === "cursor_transcript") {
|
|
2222
|
+
return observeCursorDone(meta, timeout, o.cursor, detectAgentRateLimit);
|
|
2223
|
+
}
|
|
2163
2224
|
if ((meta.kind === "grok" || meta.kind === "composer") && meta.completion_route === "grok_transcript") {
|
|
2164
2225
|
return observeGrokDone(meta, timeout, o.cursor, detectAgentRateLimit);
|
|
2165
2226
|
}
|
|
@@ -2176,6 +2237,7 @@ export async function observeAgentDone(name, o = {}) {
|
|
|
2176
2237
|
session_id: meta.aiterm_session,
|
|
2177
2238
|
launch_id: meta.launch_id,
|
|
2178
2239
|
vendor: meta.kind,
|
|
2240
|
+
harness: agentHarness(meta.kind),
|
|
2179
2241
|
outcome,
|
|
2180
2242
|
operation_id: ev?.operation_id ?? operationId,
|
|
2181
2243
|
vendor_session_id: ev?.vendor_session_id ?? meta.vendor_session_id ?? null,
|
|
@@ -2225,19 +2287,27 @@ function isAgentTuiReady(kind, screen) {
|
|
|
2225
2287
|
return claudeTuiReady(screen);
|
|
2226
2288
|
if (kind === "codex")
|
|
2227
2289
|
return codexTuiReady(screen);
|
|
2290
|
+
if (kind === "cursor")
|
|
2291
|
+
return cursorTuiReady(screen);
|
|
2228
2292
|
return grokTuiReady(screen);
|
|
2229
2293
|
}
|
|
2230
|
-
// Codex/Claude は実行中に「(esc to interrupt)
|
|
2231
|
-
//
|
|
2294
|
+
// Codex/Claude は実行中に「(esc to interrupt)」、Cursor は「Working」+
|
|
2295
|
+
// 「ctrl+c to stop」を表示する(いずれも実機採取)。startup 側の処理(MCP initialize 等)が
|
|
2296
|
+
// 走ったまま古い composer がscrollbackに残る画面は入力受付とみなさない。
|
|
2232
2297
|
// Grok/Composer は busy 表示文字列の実機根拠が未採取のため対象外(誤ブロックで起動不能にしない)。
|
|
2233
|
-
|
|
2234
|
-
|
|
2298
|
+
function isAgentTuiBusy(kind, screen) {
|
|
2299
|
+
if (kind === "cursor")
|
|
2300
|
+
return /ctrl\+c to stop/i.test(screen);
|
|
2301
|
+
if (kind === "codex" || kind === "claude")
|
|
2302
|
+
return /esc to interrupt/i.test(screen);
|
|
2303
|
+
return false;
|
|
2304
|
+
}
|
|
2235
2305
|
// ready gate 用: 入力欄マーカーがあっても busy 表示中は ready と数えない。
|
|
2236
2306
|
// frontend 推定(inferAgentFrontend)は「agent TUI が前面か」を見るだけなので isAgentTuiReady のまま。
|
|
2237
2307
|
function isAgentTuiIdleReady(kind, screen) {
|
|
2238
2308
|
if (!isAgentTuiReady(kind, screen))
|
|
2239
2309
|
return false;
|
|
2240
|
-
return !(
|
|
2310
|
+
return !isAgentTuiBusy(kind, screen);
|
|
2241
2311
|
}
|
|
2242
2312
|
async function waitAgentTuiReadyImpl(kind, sample, sleepFn, opts = {}) {
|
|
2243
2313
|
const timeoutMs = opts.timeoutMs ?? AGENT_TUI_READY_TIMEOUT_MS;
|
|
@@ -2286,11 +2356,21 @@ function agentSubmitResidueTail(text) {
|
|
|
2286
2356
|
return cps.slice(-AGENT_SUBMIT_RESIDUE_TAIL_CHARS).join("");
|
|
2287
2357
|
}
|
|
2288
2358
|
function agentSubmitResidueOnScreen(kind, screen, tail) {
|
|
2359
|
+
// Cursorは送信成立後のactive turnで明示的な停止UIを出す。scrollbackに残った旧「>」を
|
|
2360
|
+
// composerと誤認すると、実際には走っているpromptをsubmit座礁として報告してしまう。
|
|
2361
|
+
if (kind === "cursor" && isAgentTuiBusy(kind, screen))
|
|
2362
|
+
return false;
|
|
2289
2363
|
const lines = screen.split("\n");
|
|
2290
2364
|
// 入力欄マーカーは ready 判定と同じ記号を行頭基準で探す。submit 済みの transcript echo は
|
|
2291
2365
|
// マーカー行より上に出るため、最後のマーカー行以降だけを composer 領域として見る。
|
|
2292
2366
|
// grok/composer は Windows native 描画(`>`・実測 1.0.4)も ready 判定と同様に受ける。
|
|
2293
|
-
const markerRe = kind === "codex"
|
|
2367
|
+
const markerRe = kind === "codex"
|
|
2368
|
+
? CODEX_COMPOSER_MARKER_RE
|
|
2369
|
+
: kind === "claude"
|
|
2370
|
+
? CLAUDE_COMPOSER_MARKER_RE
|
|
2371
|
+
: kind === "cursor"
|
|
2372
|
+
? CURSOR_COMPOSER_MARKER_RE
|
|
2373
|
+
: GROK_COMPOSER_MARKER_RE;
|
|
2294
2374
|
let markerIdx = -1;
|
|
2295
2375
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
2296
2376
|
if (markerRe.test(lines[i])) {
|
|
@@ -2429,7 +2509,7 @@ export async function sendInitialAgentPrompt(name, text, o = {}) {
|
|
|
2429
2509
|
const ready = await waitAgentTuiReady(name, meta, o.ready_timeout ?? AGENT_TUI_READY_TIMEOUT_MS);
|
|
2430
2510
|
if (!ready.ready) {
|
|
2431
2511
|
return {
|
|
2432
|
-
text: `initial_prompt=not_sent vendor=${meta.kind} ready=false samples=${ready.samples}\n` +
|
|
2512
|
+
text: `initial_prompt=not_sent vendor=${meta.kind} ready=false samples=${ready.samples} harness=${agentHarness(meta.kind)}\n` +
|
|
2433
2513
|
`agent session '${name}' の ${agentLabel(meta.kind)} TUI が入力受付状態になりません。prompt は送信していません。`,
|
|
2434
2514
|
event_cursor: null,
|
|
2435
2515
|
submit_residue: null,
|
|
@@ -2450,7 +2530,7 @@ export async function sendInitialAgentPrompt(name, text, o = {}) {
|
|
|
2450
2530
|
}
|
|
2451
2531
|
const residue = await detectAgentSubmitResidue(name, meta.kind, text);
|
|
2452
2532
|
return {
|
|
2453
|
-
text: `initial_prompt=pending vendor=${meta.kind} event_cursor=${startOffset}\n` +
|
|
2533
|
+
text: `initial_prompt=pending vendor=${meta.kind} event_cursor=${startOffset} harness=${agentHarness(meta.kind)}\n` +
|
|
2454
2534
|
`起動時 prompt を送信した。${agentDispatchGuide(name, startOffset)}` +
|
|
2455
2535
|
agentSubmitResidueWarning(name, residue.residue),
|
|
2456
2536
|
event_cursor: startOffset,
|
|
@@ -2543,6 +2623,48 @@ export async function configureAgent(name, opts) {
|
|
|
2543
2623
|
schema: "aiterm.agent-configure-result.v1",
|
|
2544
2624
|
session_id: name,
|
|
2545
2625
|
provider: "claude",
|
|
2626
|
+
harness: agentHarness("claude"),
|
|
2627
|
+
model,
|
|
2628
|
+
reasoning_effort: effort,
|
|
2629
|
+
};
|
|
2630
|
+
}
|
|
2631
|
+
if (meta.kind === "cursor") {
|
|
2632
|
+
validateCursorModelEffort(model, effort);
|
|
2633
|
+
if (!model)
|
|
2634
|
+
throw new AitermError("Cursorのreasoning_effort変更にはmodelも指定してください", 2);
|
|
2635
|
+
const bin = resolveAgentBin("cursor");
|
|
2636
|
+
if (!bin)
|
|
2637
|
+
throw new AitermError("Cursor Agent CLI の CLI が見つかりません", 2);
|
|
2638
|
+
assertCursorModelAvailable(bin, meta.cwd ?? process.cwd(), model, effort);
|
|
2639
|
+
// Cursor TUIの `/model <base model>` はmodelを直接切り替える一方、flatten済みcatalog ID
|
|
2640
|
+
// (例: gpt-5.6-luna-high)を渡すとno matchesのfilter画面へ入る。effortは標準model pickerの
|
|
2641
|
+
// parameter editorで選び、同じsessionと会話contextを保つ。
|
|
2642
|
+
await sendAgentPromptText(name, `/model ${model}`);
|
|
2643
|
+
const modelReady = await waitAgentTuiReady(name, meta, AGENT_TUI_READY_TIMEOUT_MS);
|
|
2644
|
+
if (!modelReady.ready)
|
|
2645
|
+
throw new AitermError("Cursorのmodel変更完了を確認できません", 2);
|
|
2646
|
+
if (effort) {
|
|
2647
|
+
await sendAgentPromptText(name, "/model");
|
|
2648
|
+
await waitForScreenText(name, "Available models");
|
|
2649
|
+
sendMenuChoice(name, "Tab");
|
|
2650
|
+
const parameterScreen = await waitForScreenText(name, "Edit Parameters");
|
|
2651
|
+
const navigation = cursorEffortNavigation(parameterScreen, effort);
|
|
2652
|
+
for (let i = 0; i < navigation.down; i++)
|
|
2653
|
+
sendMenuChoice(name, "Down");
|
|
2654
|
+
sendMenuChoice(name, "Enter");
|
|
2655
|
+
await waitForScreenText(name, `${navigation.label} ✓`);
|
|
2656
|
+
sendMenuChoice(name, "Escape");
|
|
2657
|
+
await waitForScreenText(name, "Available models");
|
|
2658
|
+
sendMenuChoice(name, "Enter");
|
|
2659
|
+
const effortReady = await waitAgentTuiReady(name, meta, AGENT_TUI_READY_TIMEOUT_MS);
|
|
2660
|
+
if (!effortReady.ready)
|
|
2661
|
+
throw new AitermError("Cursorのreasoning effort変更完了を確認できません", 2);
|
|
2662
|
+
}
|
|
2663
|
+
return {
|
|
2664
|
+
schema: "aiterm.agent-configure-result.v1",
|
|
2665
|
+
session_id: name,
|
|
2666
|
+
provider: "cursor",
|
|
2667
|
+
harness: agentHarness("cursor"),
|
|
2546
2668
|
model,
|
|
2547
2669
|
reasoning_effort: effort,
|
|
2548
2670
|
};
|
|
@@ -2564,6 +2686,7 @@ export async function configureAgent(name, opts) {
|
|
|
2564
2686
|
schema: "aiterm.agent-configure-result.v1",
|
|
2565
2687
|
session_id: name,
|
|
2566
2688
|
provider: meta.kind,
|
|
2689
|
+
harness: agentHarness(meta.kind),
|
|
2567
2690
|
model,
|
|
2568
2691
|
reasoning_effort: effort,
|
|
2569
2692
|
};
|
|
@@ -2602,6 +2725,7 @@ export async function configureAgent(name, opts) {
|
|
|
2602
2725
|
schema: "aiterm.agent-configure-result.v1",
|
|
2603
2726
|
session_id: name,
|
|
2604
2727
|
provider: "codex",
|
|
2728
|
+
harness: agentHarness("codex"),
|
|
2605
2729
|
model,
|
|
2606
2730
|
reasoning_effort: effort,
|
|
2607
2731
|
};
|
|
@@ -2636,6 +2760,11 @@ export async function dispatchAgentTurn(name, text, o = {}) {
|
|
|
2636
2760
|
}
|
|
2637
2761
|
}
|
|
2638
2762
|
const startOffset = agentCompletionCursor(meta);
|
|
2763
|
+
// promptなしで起動したCursorは、最初のdispatch時点ではtranscriptとの相関markerをまだ持たない。
|
|
2764
|
+
// その1回だけlaunch contextを加え、以後はbind済みconversationへ通常textだけを送る。
|
|
2765
|
+
const dispatchText = meta.kind === "cursor" && !meta.vendor_session_id
|
|
2766
|
+
? `${subagentInstruction(meta)}\n\n${text}`
|
|
2767
|
+
: text;
|
|
2639
2768
|
if (meta.kind === "claude") {
|
|
2640
2769
|
// durable/anonymousを分岐する前に同じsend preflightを通す。拒否されるpromptの
|
|
2641
2770
|
// receipt/active markerだけを残して、来ないStopを待つ状態を作らない。
|
|
@@ -2646,7 +2775,7 @@ export async function dispatchAgentTurn(name, text, o = {}) {
|
|
|
2646
2775
|
else
|
|
2647
2776
|
reserveAnonymousClaudeTurn(meta);
|
|
2648
2777
|
}
|
|
2649
|
-
send(name,
|
|
2778
|
+
send(name, dispatchText, {
|
|
2650
2779
|
enter: false,
|
|
2651
2780
|
force: o.force,
|
|
2652
2781
|
raw: o.raw,
|
|
@@ -2658,12 +2787,13 @@ export async function dispatchAgentTurn(name, text, o = {}) {
|
|
|
2658
2787
|
// Codex TUI は literal text 投入直後の Enter を取り落とすことがある。agent 経路だけ submit を分離する。
|
|
2659
2788
|
await sleep(AGENT_SUBMIT_DELAY_MS);
|
|
2660
2789
|
sendKey(name, "Enter", { preserveAgentOperation: meta.kind === "claude" });
|
|
2661
|
-
const residue = await detectAgentSubmitResidue(name, meta.kind,
|
|
2790
|
+
const residue = await detectAgentSubmitResidue(name, meta.kind, dispatchText);
|
|
2662
2791
|
return {
|
|
2663
2792
|
schema: "aiterm.agent-dispatch.v1",
|
|
2664
2793
|
session_id: meta.aiterm_session,
|
|
2665
2794
|
launch_id: meta.launch_id,
|
|
2666
2795
|
vendor: meta.kind,
|
|
2796
|
+
harness: agentHarness(meta.kind),
|
|
2667
2797
|
event_cursor: startOffset,
|
|
2668
2798
|
operation_id: operationId,
|
|
2669
2799
|
submit_residue: residue.residue,
|
|
@@ -2777,6 +2907,8 @@ function buildAgentCmd(kind, bin, model, effort, prompt, meta = null) {
|
|
|
2777
2907
|
return buildClaudeAgentCmd(bin, model, effort, prompt, meta);
|
|
2778
2908
|
if (kind === "codex")
|
|
2779
2909
|
return buildCodexAgentCmd(bin, model, effort, prompt, meta);
|
|
2910
|
+
if (kind === "cursor")
|
|
2911
|
+
return buildCursorAgentCmd(bin, model, effort, prompt, meta);
|
|
2780
2912
|
return buildGrokAgentCmd(kind, bin, model, effort, prompt, meta);
|
|
2781
2913
|
}
|
|
2782
2914
|
function agentEnvPrefix(meta, sid, envVars = []) {
|
|
@@ -2799,7 +2931,7 @@ function agentEnvPrefix(meta, sid, envVars = []) {
|
|
|
2799
2931
|
`AITERM_AGENT_LINEAGE=${shq(meta.lineage ?? `host-root>${meta.kind}:${sid}`)}`,
|
|
2800
2932
|
`AITERM_AGENT_DELEGATION_ALLOWED=${shq(meta.delegation_allowed === true ? "true" : "false")}`,
|
|
2801
2933
|
];
|
|
2802
|
-
if (meta.kind === "claude" || meta.kind === "codex")
|
|
2934
|
+
if (meta.kind === "claude" || meta.kind === "codex" || meta.kind === "cursor")
|
|
2803
2935
|
return common;
|
|
2804
2936
|
return [...grokEnvTokens(meta), ...common];
|
|
2805
2937
|
})();
|
|
@@ -2808,6 +2940,8 @@ function agentEnvPrefix(meta, sid, envVars = []) {
|
|
|
2808
2940
|
function buildAgentLaunchNote(kind, model, effort, meta) {
|
|
2809
2941
|
if (kind === "claude")
|
|
2810
2942
|
return claudeLaunchNote(model, effort, meta);
|
|
2943
|
+
if (kind === "cursor")
|
|
2944
|
+
return cursorLaunchNote(model, effort, meta);
|
|
2811
2945
|
if (kind !== "codex")
|
|
2812
2946
|
return grokLaunchNote(kind, model, effort, meta);
|
|
2813
2947
|
return codexLaunchNote(model, effort, meta);
|
|
@@ -2862,6 +2996,8 @@ export function openAgent(kind, opts = {}) {
|
|
|
2862
2996
|
if (effort && kind === "claude" && !CLAUDE_EFFORTS.has(effort)) {
|
|
2863
2997
|
throw new AitermError("Claude Code の reasoning_effort は low/medium/high/xhigh/max のいずれかです", 2);
|
|
2864
2998
|
}
|
|
2999
|
+
if (kind === "cursor")
|
|
3000
|
+
validateCursorModelEffort(model, effort);
|
|
2865
3001
|
const agentDone = !!opts.agent_done;
|
|
2866
3002
|
const launchOperationId = opts.launch_operation_id == null
|
|
2867
3003
|
? null
|
|
@@ -2891,7 +3027,13 @@ export function openAgent(kind, opts = {}) {
|
|
|
2891
3027
|
ownTelemetryFailure("AITERM.VENDOR_LAUNCHER_FAILED", error, 2);
|
|
2892
3028
|
}
|
|
2893
3029
|
if (!bin) {
|
|
2894
|
-
const where = kind === "claude"
|
|
3030
|
+
const where = kind === "claude"
|
|
3031
|
+
? "~/.local/bin/claude"
|
|
3032
|
+
: kind === "codex"
|
|
3033
|
+
? "~/.local/bin/codex"
|
|
3034
|
+
: kind === "cursor"
|
|
3035
|
+
? "~/.local/bin/cursor-agent"
|
|
3036
|
+
: "~/.grok/bin/grok";
|
|
2895
3037
|
ownTelemetryFailure("AITERM.VENDOR_LAUNCHER_FAILED", new AitermError(`${label} の CLI が見つかりません(${where} か PATH が必要)`, 2), 2);
|
|
2896
3038
|
}
|
|
2897
3039
|
// cwd 検証(session を作る前に。cd 失敗はシェル内で静かに死に「起動した」と偽成功を返すため)。
|
|
@@ -2931,6 +3073,8 @@ export function openAgent(kind, opts = {}) {
|
|
|
2931
3073
|
}
|
|
2932
3074
|
if (kind === "claude")
|
|
2933
3075
|
assertClaudeAuthenticationReady(bin);
|
|
3076
|
+
if (kind === "cursor")
|
|
3077
|
+
assertCursorAuthenticationReady(bin);
|
|
2934
3078
|
// Windows の起動コマンドは native psmux pane の Git Bash で走る(WSL 橋・/mnt/c 変換は
|
|
2935
3079
|
// e3f5fc8 で全廃。bin/cwd は Windows パスの forward slash 形のまま渡す)。
|
|
2936
3080
|
// 未検証リスク: npm グローバル導入の codex.cmd/.bat シムの対話 TUI 描画は実 Windows でしか確認
|
|
@@ -2952,6 +3096,9 @@ export function openAgent(kind, opts = {}) {
|
|
|
2952
3096
|
if (requestedModel)
|
|
2953
3097
|
assertGrokModelAvailable(bin, cwd ?? process.cwd(), requestedModel);
|
|
2954
3098
|
}
|
|
3099
|
+
if (kind === "cursor" && model) {
|
|
3100
|
+
assertCursorModelAvailable(bin, cwd ?? process.cwd(), model, effort);
|
|
3101
|
+
}
|
|
2955
3102
|
let sid;
|
|
2956
3103
|
let hint;
|
|
2957
3104
|
try {
|
|
@@ -2972,7 +3119,9 @@ export function openAgent(kind, opts = {}) {
|
|
|
2972
3119
|
? createClaudeAgentMetadata(sid, cwd, opts.prompt ? "pending" : "none", launchOperationId, launchRequestDigest, lineageContext, model, effort)
|
|
2973
3120
|
: kind === "codex"
|
|
2974
3121
|
? createCodexAgentMetadata(sid, cwd, opts.prompt ? "pending" : "none", { model, effort }, writeScope, lineageContext)
|
|
2975
|
-
:
|
|
3122
|
+
: kind === "cursor"
|
|
3123
|
+
? createCursorAgentMetadata(sid, cwd, opts.prompt ? "pending" : "none", writeScope, lineageContext)
|
|
3124
|
+
: createGrokAgentMetadata(kind, sid, cwd, opts.prompt ? "pending" : "none", grokAuthPath, writeScope, lineageContext)
|
|
2976
3125
|
: null;
|
|
2977
3126
|
if (meta)
|
|
2978
3127
|
agentMetadataNegativeCache.delete(sid);
|
|
@@ -3003,7 +3152,7 @@ export function openAgent(kind, opts = {}) {
|
|
|
3003
3152
|
}
|
|
3004
3153
|
throw failure;
|
|
3005
3154
|
}
|
|
3006
|
-
const driveHint = agentDone
|
|
3155
|
+
const driveHint = agentDone
|
|
3007
3156
|
? `TUI の描画には数秒かかる。少し置いてから pty_read(${sid}, screen:true) で画面を読み、` +
|
|
3008
3157
|
`turnはpty_send(${sid}, "...")で送る(自動で非ブロックdispatch=投げっぱなしでよい・完了通知はaiterm-wait)。中断はpty_key(${sid}, "C-c")、` +
|
|
3009
3158
|
`Stopが来ない場合の解除はpty_close(${sid})を使う。`
|
|
@@ -3037,7 +3186,7 @@ export async function openAgentWithInitialPrompt(kind, opts = {}) {
|
|
|
3037
3186
|
}
|
|
3038
3187
|
// v0.16.0: launcher は常に managed(Stop hook つき)で立つ。手動運転したい場合は
|
|
3039
3188
|
// pty_open で素の PTY を開き、vendor CLI を自分で send する。
|
|
3040
|
-
// 第3要素は「起動時点でturnが走っているか」の event_cursor: Grok/Composer の argv prompt は
|
|
3189
|
+
// 第3要素は「起動時点でturnが走っているか」の event_cursor: Grok/Composer/Cursor の argv prompt は
|
|
3041
3190
|
// event file 新規作成直後の起動=境界0、prompt なしの起動は turn なし=null。
|
|
3042
3191
|
if (!prompt || (kind !== "codex" && kind !== "claude")) {
|
|
3043
3192
|
const [sid, hint] = openAgent(kind, {
|
|
@@ -3051,7 +3200,7 @@ export async function openAgentWithInitialPrompt(kind, opts = {}) {
|
|
|
3051
3200
|
write_scope: opts.write_scope,
|
|
3052
3201
|
env_vars: opts.env_vars,
|
|
3053
3202
|
});
|
|
3054
|
-
// argv prompt(grok/composer)は composer を経由しないため submit 座礁観測の対象外。
|
|
3203
|
+
// argv prompt(grok/composer/cursor)は composer を経由しないため submit 座礁観測の対象外。
|
|
3055
3204
|
return [sid, hint, prompt ? 0 : null, null];
|
|
3056
3205
|
}
|
|
3057
3206
|
const [sid, hint] = openAgent(kind, {
|