@easynet/agent-runtime 1.0.3 → 1.0.4
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/.github/workflows/ci.yml +9 -24
- package/.github/workflows/release.yml +14 -35
- package/agent-runtime/.github/workflows/ci.yml +69 -0
- package/agent-runtime/.github/workflows/release.yml +118 -0
- package/agent-runtime/.releaserc.cjs +26 -0
- package/agent-runtime/config/agent.deep.yaml +25 -0
- package/agent-runtime/config/agent.react.yaml +24 -0
- package/agent-runtime/example/basic-usage.ts +49 -0
- package/agent-runtime/package-lock.json +7740 -0
- package/agent-runtime/package.json +49 -0
- package/agent-runtime/pnpm-lock.yaml +3712 -0
- package/agent-runtime/scripts/resolve-deps.js +54 -0
- package/agent-runtime/src/agents/deep-agent.ts +165 -0
- package/agent-runtime/src/agents/react-agent.helpers.ts +227 -0
- package/agent-runtime/src/agents/react-agent.ts +584 -0
- package/{src → agent-runtime/src/agents}/sub-agent.ts +2 -2
- package/agent-runtime/src/cli/args.ts +15 -0
- package/agent-runtime/src/cli/event-listener.ts +162 -0
- package/agent-runtime/src/cli/interactive.ts +144 -0
- package/agent-runtime/src/cli/runtime.ts +31 -0
- package/agent-runtime/src/cli/spinner.ts +23 -0
- package/agent-runtime/src/cli/terminal-render.ts +322 -0
- package/agent-runtime/src/cli/types.ts +33 -0
- package/agent-runtime/src/cli.ts +134 -0
- package/agent-runtime/src/config/helpers.ts +179 -0
- package/agent-runtime/src/config/index.ts +245 -0
- package/agent-runtime/src/config/types.ts +62 -0
- package/agent-runtime/src/core/context.ts +266 -0
- package/agent-runtime/src/index.ts +55 -0
- package/agent-runtime/tsconfig.json +18 -0
- package/apps/imessagebot/README.md +38 -0
- package/apps/imessagebot/config/.agent/cache/easynet/agent-tool-buildin/0.0.45/README.md +33 -0
- package/apps/imessagebot/config/.agent/cache/easynet/agent-tool-buildin/0.0.45/package-lock.json +15257 -0
- package/apps/imessagebot/config/.agent/cache/easynet/agent-tool-buildin/0.0.45/package.json +55 -0
- package/apps/imessagebot/config/agents/deep/agent.yaml +31 -0
- package/apps/imessagebot/config/agents/react/agent.yaml +58 -0
- package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.43/README.md +33 -0
- package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.43/package-lock.json +15457 -0
- package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.43/package.json +55 -0
- package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.46/README.md +33 -0
- package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.46/package-lock.json +15257 -0
- package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.46/package.json +62 -0
- package/apps/imessagebot/config/agents/shared/memory.yaml +31 -0
- package/apps/imessagebot/config/agents/shared/model.yaml +23 -0
- package/apps/imessagebot/config/agents/shared/tool.yaml +13 -0
- package/apps/imessagebot/config/app.yaml +14 -0
- package/apps/imessagebot/package-lock.json +53695 -0
- package/apps/imessagebot/package.json +41 -0
- package/apps/imessagebot/pnpm-lock.yaml +1589 -0
- package/apps/imessagebot/scripts/resolve-deps.js +41 -0
- package/apps/imessagebot/scripts/test-llm.mjs +27 -0
- package/apps/imessagebot/scripts/validate-tools-config.mjs +174 -0
- package/apps/imessagebot/src/config.ts +76 -0
- package/apps/imessagebot/src/context.ts +35 -0
- package/apps/imessagebot/src/index.ts +17 -0
- package/apps/imessagebot/tsconfig.json +18 -0
- package/apps/itermbot/.github/workflows/ci.yml +61 -0
- package/apps/itermbot/.github/workflows/release.yml +80 -0
- package/apps/itermbot/.releaserc.cjs +26 -0
- package/apps/itermbot/README.md +82 -0
- package/apps/itermbot/config/app.yaml +29 -0
- package/apps/itermbot/config/tsconfig.json +18 -0
- package/apps/itermbot/macos_disk_usage_agent_plan.md +244 -0
- package/apps/itermbot/package-lock.json +53697 -0
- package/apps/itermbot/package.json +57 -0
- package/apps/itermbot/pnpm-lock.yaml +3966 -0
- package/apps/itermbot/scripts/patch-buildin-cache.sh +25 -0
- package/apps/itermbot/scripts/resolve-deps.js +41 -0
- package/apps/itermbot/scripts/test-llm.mjs +32 -0
- package/apps/itermbot/skills/command-explain-and-guard/SKILL.md +39 -0
- package/apps/itermbot/skills/command-explain-and-guard/handler.js +86 -0
- package/apps/itermbot/skills/disk-usage-investigate/SKILL.md +44 -0
- package/apps/itermbot/skills/disk-usage-investigate/handler.js +12 -0
- package/apps/itermbot/skills/gpu-ssh-monitor/SKILL.md +64 -0
- package/apps/itermbot/skills/repo-triage/SKILL.md +40 -0
- package/apps/itermbot/skills/repo-triage/handler.js +56 -0
- package/apps/itermbot/skills/test-failure-diagnose/SKILL.md +43 -0
- package/apps/itermbot/skills/test-failure-diagnose/handler.js +107 -0
- package/apps/itermbot/src/config.ts +95 -0
- package/apps/itermbot/src/context.ts +35 -0
- package/apps/itermbot/src/index.ts +223 -0
- package/apps/itermbot/src/iterm/session-hint.ts +40 -0
- package/apps/itermbot/src/iterm/target-routing.ts +419 -0
- package/apps/itermbot/src/startup/colors.ts +317 -0
- package/apps/itermbot/src/startup/diagnostics.ts +97 -0
- package/apps/itermbot/src/startup/ui.ts +141 -0
- package/config/agent.deep.yaml +25 -0
- package/config/agent.react.yaml +24 -0
- package/dist/agents/deep-agent.d.ts +37 -0
- package/dist/agents/deep-agent.d.ts.map +1 -0
- package/dist/agents/deep-agent.js +115 -0
- package/dist/agents/deep-agent.js.map +1 -0
- package/dist/agents/react-agent.d.ts +40 -0
- package/dist/agents/react-agent.d.ts.map +1 -0
- package/dist/agents/react-agent.helpers.d.ts +40 -0
- package/dist/agents/react-agent.helpers.d.ts.map +1 -0
- package/dist/agents/react-agent.helpers.js +196 -0
- package/dist/agents/react-agent.helpers.js.map +1 -0
- package/dist/agents/react-agent.js +400 -0
- package/dist/agents/react-agent.js.map +1 -0
- package/dist/agents/sub-agent.d.ts +34 -0
- package/dist/agents/sub-agent.d.ts.map +1 -0
- package/dist/agents/sub-agent.js +53 -0
- package/dist/agents/sub-agent.js.map +1 -0
- package/dist/cli/args.d.ts +8 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +9 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/event-listener.d.ts +3 -0
- package/dist/cli/event-listener.d.ts.map +1 -0
- package/dist/cli/event-listener.js +131 -0
- package/dist/cli/event-listener.js.map +1 -0
- package/dist/cli/interactive.d.ts +4 -0
- package/dist/cli/interactive.d.ts.map +1 -0
- package/dist/cli/interactive.js +118 -0
- package/dist/cli/interactive.js.map +1 -0
- package/dist/cli/runtime.d.ts +8 -0
- package/dist/cli/runtime.d.ts.map +1 -0
- package/dist/cli/runtime.js +27 -0
- package/dist/cli/runtime.js.map +1 -0
- package/dist/cli/spinner.d.ts +2 -0
- package/dist/cli/spinner.d.ts.map +1 -0
- package/dist/cli/spinner.js +22 -0
- package/dist/cli/spinner.js.map +1 -0
- package/dist/cli/terminal-render.d.ts +7 -0
- package/dist/cli/terminal-render.d.ts.map +1 -0
- package/dist/cli/terminal-render.js +282 -0
- package/dist/cli/terminal-render.js.map +1 -0
- package/dist/cli/types.d.ts +29 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +3 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli.d.ts +4 -41
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +84 -588
- package/dist/cli.js.map +1 -1
- package/dist/config/helpers.d.ts +6 -0
- package/dist/config/helpers.d.ts.map +1 -0
- package/dist/config/helpers.js +164 -0
- package/dist/config/helpers.js.map +1 -0
- package/dist/config/index.d.ts +15 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +160 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/types.d.ts +57 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/context.d.ts +8 -69
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +44 -24
- package/dist/context.js.map +1 -1
- package/dist/core/context.d.ts +66 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +149 -0
- package/dist/core/context.js.map +1 -0
- package/dist/deep-agent.d.ts +5 -2
- package/dist/deep-agent.d.ts.map +1 -1
- package/dist/deep-agent.js +44 -11
- package/dist/deep-agent.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/middleware/malformed-tool-call-middleware.d.ts +8 -0
- package/dist/middleware/malformed-tool-call-middleware.d.ts.map +1 -0
- package/dist/middleware/malformed-tool-call-middleware.js +191 -0
- package/dist/middleware/malformed-tool-call-middleware.js.map +1 -0
- package/dist/react-agent.d.ts +2 -2
- package/dist/react-agent.d.ts.map +1 -1
- package/dist/react-agent.js +28 -9
- package/dist/react-agent.js.map +1 -1
- package/package.json +1 -1
- package/scripts/resolve-deps.js +54 -0
- package/src/agents/deep-agent.ts +165 -0
- package/src/agents/react-agent.helpers.ts +227 -0
- package/src/agents/react-agent.ts +584 -0
- package/src/agents/sub-agent.ts +82 -0
- package/src/cli/args.ts +15 -0
- package/src/cli/event-listener.ts +162 -0
- package/src/cli/interactive.ts +144 -0
- package/src/cli/runtime.ts +31 -0
- package/src/cli/spinner.ts +23 -0
- package/src/cli/terminal-render.ts +322 -0
- package/src/cli/types.ts +33 -0
- package/src/cli.ts +91 -702
- package/src/config/helpers.ts +179 -0
- package/src/config/index.ts +245 -0
- package/src/config/types.ts +62 -0
- package/src/core/context.ts +266 -0
- package/src/index.ts +13 -11
- package/src/middleware/malformed-tool-call-middleware.ts +239 -0
- package/src/types/markdown-it-terminal.d.ts +4 -0
- package/src/types/marked-terminal.d.ts +16 -0
- package/dist/config.d.ts +0 -86
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -84
- package/dist/config.js.map +0 -1
- package/src/config.ts +0 -177
- package/src/context.ts +0 -247
- package/src/deep-agent.ts +0 -104
- package/src/react-agent.ts +0 -576
- /package/{src → agent-runtime/src/middleware}/malformed-tool-call-middleware.ts +0 -0
- /package/{src → agent-runtime/src/types}/markdown-it-terminal.d.ts +0 -0
- /package/{src → agent-runtime/src/types}/marked-terminal.d.ts +0 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import type { BotContext } from "@easynet/agent-runtime";
|
|
2
|
+
import {
|
|
3
|
+
itermListCurrentWindowSessions,
|
|
4
|
+
itermRename,
|
|
5
|
+
itermSetSessionColors,
|
|
6
|
+
itermSplitPane,
|
|
7
|
+
} from "@easynet/agent-tool-buildin/iterm";
|
|
8
|
+
import {
|
|
9
|
+
CHAT_BG,
|
|
10
|
+
CHAT_FG,
|
|
11
|
+
TARGET_BG,
|
|
12
|
+
TARGET_FG,
|
|
13
|
+
captureSessionColorsByIdSync,
|
|
14
|
+
restoreSessionColorsSync,
|
|
15
|
+
type SessionColorSnapshot,
|
|
16
|
+
} from "../startup/colors.js";
|
|
17
|
+
|
|
18
|
+
interface SessionInfo {
|
|
19
|
+
windowId?: number;
|
|
20
|
+
tabIndex?: number;
|
|
21
|
+
sessionId?: string;
|
|
22
|
+
isCurrentSession?: boolean;
|
|
23
|
+
isCurrentTab?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface TargetRoutingState {
|
|
27
|
+
chatSessionId: string | null;
|
|
28
|
+
chatWindowId: number | null;
|
|
29
|
+
chatTabIndex: number | null;
|
|
30
|
+
targetSessionId: string | null;
|
|
31
|
+
windowId: number | null;
|
|
32
|
+
tabIndex: number | null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type ToolLike = {
|
|
36
|
+
name?: unknown;
|
|
37
|
+
invoke?: (args: unknown) => Promise<unknown>;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export function enableDynamicTargetRouting(
|
|
41
|
+
ctx: BotContext,
|
|
42
|
+
state: TargetRoutingState,
|
|
43
|
+
options: {
|
|
44
|
+
intervalMs?: number;
|
|
45
|
+
chatTitle?: string;
|
|
46
|
+
targetTitle?: string;
|
|
47
|
+
} = {},
|
|
48
|
+
): () => void {
|
|
49
|
+
const intervalMs = options.intervalMs ?? 2000;
|
|
50
|
+
const chatTitle = options.chatTitle ?? "iTermBot Chat";
|
|
51
|
+
const targetTitle = options.targetTitle ?? "iTermBot Target";
|
|
52
|
+
const unpatchFns: Array<() => void> = [];
|
|
53
|
+
let announcedTarget = state.targetSessionId;
|
|
54
|
+
const originalColors = new Map<string, SessionColorSnapshot>();
|
|
55
|
+
const verboseRoutingLog = process.env.ITERMBOT_ROUTING_LOG === "1";
|
|
56
|
+
|
|
57
|
+
const announceRoutingChange = (message: string): void => {
|
|
58
|
+
// Avoid breaking readline prompt in interactive TTY mode.
|
|
59
|
+
if (!verboseRoutingLog) return;
|
|
60
|
+
console.error(message);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const isLikelyRoutingError = (error: unknown): boolean => {
|
|
64
|
+
const message =
|
|
65
|
+
error instanceof Error
|
|
66
|
+
? error.message
|
|
67
|
+
: typeof error === "string"
|
|
68
|
+
? error
|
|
69
|
+
: "";
|
|
70
|
+
if (!message) return false;
|
|
71
|
+
const lower = message.toLowerCase();
|
|
72
|
+
return (
|
|
73
|
+
lower.includes("session") ||
|
|
74
|
+
lower.includes("window") ||
|
|
75
|
+
lower.includes("tab") ||
|
|
76
|
+
lower.includes("not found") ||
|
|
77
|
+
lower.includes("missing value") ||
|
|
78
|
+
lower.includes("iterm")
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const setSessionTitle = async (
|
|
83
|
+
sessionId: string,
|
|
84
|
+
windowId: number | null,
|
|
85
|
+
tabIndex: number | null,
|
|
86
|
+
sessionName: string,
|
|
87
|
+
): Promise<void> => {
|
|
88
|
+
if (!sessionId) return;
|
|
89
|
+
if (typeof windowId !== "number" || typeof tabIndex !== "number") return;
|
|
90
|
+
try {
|
|
91
|
+
await itermRename({
|
|
92
|
+
windowId,
|
|
93
|
+
tabIndex,
|
|
94
|
+
sessionId,
|
|
95
|
+
sessionName,
|
|
96
|
+
});
|
|
97
|
+
} catch {
|
|
98
|
+
// non-fatal
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const setSessionColors = async (
|
|
103
|
+
sessionId: string,
|
|
104
|
+
windowId: number | null,
|
|
105
|
+
tabIndex: number | null,
|
|
106
|
+
colors: { backgroundHex: string; foregroundHex: string },
|
|
107
|
+
): Promise<void> => {
|
|
108
|
+
if (!sessionId) return;
|
|
109
|
+
if (typeof windowId !== "number" || typeof tabIndex !== "number") return;
|
|
110
|
+
try {
|
|
111
|
+
await itermSetSessionColors({
|
|
112
|
+
windowId,
|
|
113
|
+
tabIndex,
|
|
114
|
+
sessionId,
|
|
115
|
+
backgroundHex: colors.backgroundHex,
|
|
116
|
+
foregroundHex: colors.foregroundHex,
|
|
117
|
+
});
|
|
118
|
+
} catch {
|
|
119
|
+
// non-fatal
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const rememberOriginalColors = (
|
|
124
|
+
sessionId: string,
|
|
125
|
+
windowId: number | null,
|
|
126
|
+
tabIndex: number | null,
|
|
127
|
+
): void => {
|
|
128
|
+
if (!sessionId) return;
|
|
129
|
+
if (originalColors.has(sessionId)) return;
|
|
130
|
+
if (typeof windowId !== "number" || typeof tabIndex !== "number") return;
|
|
131
|
+
const snapshot = captureSessionColorsByIdSync({
|
|
132
|
+
windowId,
|
|
133
|
+
tabIndex,
|
|
134
|
+
sessionId,
|
|
135
|
+
});
|
|
136
|
+
if (!snapshot) return;
|
|
137
|
+
originalColors.set(sessionId, snapshot);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const restoreOriginalColors = (sessionId: string | null): void => {
|
|
141
|
+
if (!sessionId) return;
|
|
142
|
+
const snapshot = originalColors.get(sessionId);
|
|
143
|
+
if (!snapshot) return;
|
|
144
|
+
try {
|
|
145
|
+
restoreSessionColorsSync([snapshot]);
|
|
146
|
+
} catch {
|
|
147
|
+
// non-fatal
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const enforceTitles = async (): Promise<void> => {
|
|
152
|
+
if (state.chatSessionId) {
|
|
153
|
+
await setSessionTitle(state.chatSessionId, state.chatWindowId, state.chatTabIndex, chatTitle);
|
|
154
|
+
await setSessionColors(state.chatSessionId, state.chatWindowId, state.chatTabIndex, {
|
|
155
|
+
backgroundHex: CHAT_BG,
|
|
156
|
+
foregroundHex: CHAT_FG,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
if (state.targetSessionId) {
|
|
160
|
+
rememberOriginalColors(state.targetSessionId, state.windowId, state.tabIndex);
|
|
161
|
+
await setSessionTitle(state.targetSessionId, state.windowId, state.tabIndex, targetTitle);
|
|
162
|
+
await setSessionColors(state.targetSessionId, state.windowId, state.tabIndex, {
|
|
163
|
+
backgroundHex: TARGET_BG,
|
|
164
|
+
foregroundHex: TARGET_FG,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const chooseFallbackTarget = (sessions: SessionInfo[], chatSessionId: string | null): SessionInfo | null => {
|
|
170
|
+
const bySameTab = sessions.find((s) =>
|
|
171
|
+
Boolean(s.sessionId) &&
|
|
172
|
+
s.sessionId !== chatSessionId &&
|
|
173
|
+
typeof state.chatTabIndex === "number" &&
|
|
174
|
+
s.tabIndex === state.chatTabIndex,
|
|
175
|
+
);
|
|
176
|
+
if (bySameTab) return bySameTab;
|
|
177
|
+
|
|
178
|
+
const byCurrentTab = sessions.find((s) =>
|
|
179
|
+
Boolean(s.sessionId) &&
|
|
180
|
+
s.sessionId !== chatSessionId &&
|
|
181
|
+
s.isCurrentTab,
|
|
182
|
+
);
|
|
183
|
+
if (byCurrentTab) return byCurrentTab;
|
|
184
|
+
|
|
185
|
+
const byAny = sessions.find((s) =>
|
|
186
|
+
Boolean(s.sessionId) &&
|
|
187
|
+
s.sessionId !== chatSessionId,
|
|
188
|
+
);
|
|
189
|
+
return byAny ?? null;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const refreshTargetForCommand = async (opts: {
|
|
193
|
+
allowSplit: boolean;
|
|
194
|
+
adoptFocusedNonChat: boolean;
|
|
195
|
+
}): Promise<{
|
|
196
|
+
changed: boolean;
|
|
197
|
+
autoCreated: boolean;
|
|
198
|
+
}> => {
|
|
199
|
+
const { result } = await itermListCurrentWindowSessions();
|
|
200
|
+
const sessions = (result.sessions ?? []) as SessionInfo[];
|
|
201
|
+
if (sessions.length === 0) {
|
|
202
|
+
return { changed: false, autoCreated: false };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const current = sessions.find((s) => s.isCurrentSession && s.sessionId) ?? null;
|
|
206
|
+
const chatSession =
|
|
207
|
+
sessions.find((s) => s.sessionId && state.chatSessionId && s.sessionId === state.chatSessionId) ??
|
|
208
|
+
current ??
|
|
209
|
+
sessions.find((s) => Boolean(s.sessionId)) ??
|
|
210
|
+
null;
|
|
211
|
+
|
|
212
|
+
if (chatSession?.sessionId) {
|
|
213
|
+
const prevChatSessionId = state.chatSessionId;
|
|
214
|
+
state.chatSessionId = chatSession.sessionId;
|
|
215
|
+
if (typeof chatSession.windowId === "number") state.chatWindowId = chatSession.windowId;
|
|
216
|
+
if (typeof chatSession.tabIndex === "number") state.chatTabIndex = chatSession.tabIndex;
|
|
217
|
+
if (prevChatSessionId !== state.chatSessionId) {
|
|
218
|
+
state.targetSessionId = null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (opts.adoptFocusedNonChat && current?.sessionId && current.sessionId !== state.chatSessionId) {
|
|
223
|
+
const changed = state.targetSessionId !== current.sessionId;
|
|
224
|
+
state.targetSessionId = current.sessionId;
|
|
225
|
+
if (typeof current.windowId === "number") state.windowId = current.windowId;
|
|
226
|
+
if (typeof current.tabIndex === "number") state.tabIndex = current.tabIndex;
|
|
227
|
+
return { changed, autoCreated: false };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const currentTarget = sessions.find((s) => s.sessionId && s.sessionId === state.targetSessionId) ?? null;
|
|
231
|
+
const targetIsValid = Boolean(
|
|
232
|
+
currentTarget?.sessionId &&
|
|
233
|
+
(!state.chatSessionId || currentTarget.sessionId !== state.chatSessionId),
|
|
234
|
+
);
|
|
235
|
+
if (targetIsValid) {
|
|
236
|
+
if (currentTarget && typeof currentTarget.windowId === "number") state.windowId = currentTarget.windowId;
|
|
237
|
+
if (currentTarget && typeof currentTarget.tabIndex === "number") state.tabIndex = currentTarget.tabIndex;
|
|
238
|
+
return { changed: false, autoCreated: false };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const fallback = chooseFallbackTarget(sessions, state.chatSessionId);
|
|
242
|
+
if (fallback?.sessionId) {
|
|
243
|
+
const changed = state.targetSessionId !== fallback.sessionId;
|
|
244
|
+
state.targetSessionId = fallback.sessionId;
|
|
245
|
+
if (typeof fallback.windowId === "number") state.windowId = fallback.windowId;
|
|
246
|
+
if (typeof fallback.tabIndex === "number") state.tabIndex = fallback.tabIndex;
|
|
247
|
+
return { changed, autoCreated: false };
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (!opts.allowSplit || !chatSession?.sessionId) {
|
|
251
|
+
return { changed: false, autoCreated: false };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const split = await itermSplitPane({
|
|
255
|
+
windowId: typeof chatSession.windowId === "number" ? chatSession.windowId : undefined,
|
|
256
|
+
tabIndex: typeof chatSession.tabIndex === "number" ? chatSession.tabIndex : undefined,
|
|
257
|
+
sessionId: chatSession.sessionId,
|
|
258
|
+
direction: "vertical",
|
|
259
|
+
activate: false,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const splitSessionId = typeof split?.result?.sessionId === "string" ? split.result.sessionId : "";
|
|
263
|
+
if (splitSessionId && splitSessionId !== chatSession.sessionId) {
|
|
264
|
+
const changed = state.targetSessionId !== splitSessionId;
|
|
265
|
+
state.targetSessionId = splitSessionId;
|
|
266
|
+
if (typeof split.result.windowId === "number") state.windowId = split.result.windowId;
|
|
267
|
+
if (typeof split.result.tabIndex === "number") state.tabIndex = split.result.tabIndex;
|
|
268
|
+
return { changed, autoCreated: true };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const refreshed = await itermListCurrentWindowSessions();
|
|
272
|
+
const refreshedSessions = (refreshed.result.sessions ?? []) as SessionInfo[];
|
|
273
|
+
const refreshedFallback = chooseFallbackTarget(refreshedSessions, state.chatSessionId);
|
|
274
|
+
if (refreshedFallback?.sessionId) {
|
|
275
|
+
const changed = state.targetSessionId !== refreshedFallback.sessionId;
|
|
276
|
+
state.targetSessionId = refreshedFallback.sessionId;
|
|
277
|
+
if (typeof refreshedFallback.windowId === "number") state.windowId = refreshedFallback.windowId;
|
|
278
|
+
if (typeof refreshedFallback.tabIndex === "number") state.tabIndex = refreshedFallback.tabIndex;
|
|
279
|
+
return { changed, autoCreated: true };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return { changed: false, autoCreated: false };
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// Initial titles
|
|
286
|
+
void enforceTitles();
|
|
287
|
+
|
|
288
|
+
for (const t of ctx.tools as unknown as ToolLike[]) {
|
|
289
|
+
if (!t || typeof t.name !== "string" || typeof t.invoke !== "function") continue;
|
|
290
|
+
if (!t.name.includes("itermRunCommandInSession")) continue;
|
|
291
|
+
|
|
292
|
+
const originalInvoke = t.invoke.bind(t);
|
|
293
|
+
t.invoke = async (args: unknown): Promise<unknown> => {
|
|
294
|
+
const baseArgs =
|
|
295
|
+
args && typeof args === "object"
|
|
296
|
+
? { ...(args as Record<string, unknown>) }
|
|
297
|
+
: ({} as Record<string, unknown>);
|
|
298
|
+
|
|
299
|
+
const applyRoutingArgs = (target: Record<string, unknown>): void => {
|
|
300
|
+
if (state.targetSessionId) target.sessionId = state.targetSessionId;
|
|
301
|
+
if (typeof state.windowId === "number") target.windowId = state.windowId;
|
|
302
|
+
if (typeof state.tabIndex === "number") target.tabIndex = state.tabIndex;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
try {
|
|
306
|
+
// Fast path: avoid iTerm preflight on every command.
|
|
307
|
+
// Only preflight when routing state is empty.
|
|
308
|
+
if (!state.targetSessionId) {
|
|
309
|
+
const previousTarget = state.targetSessionId;
|
|
310
|
+
const ensureResult = await refreshTargetForCommand({
|
|
311
|
+
allowSplit: true,
|
|
312
|
+
adoptFocusedNonChat: true,
|
|
313
|
+
});
|
|
314
|
+
if (ensureResult.changed) {
|
|
315
|
+
await enforceTitles();
|
|
316
|
+
if (previousTarget && previousTarget !== state.targetSessionId) {
|
|
317
|
+
restoreOriginalColors(previousTarget);
|
|
318
|
+
}
|
|
319
|
+
if (state.targetSessionId !== announcedTarget) {
|
|
320
|
+
announcedTarget = state.targetSessionId;
|
|
321
|
+
if (ensureResult.autoCreated) {
|
|
322
|
+
announceRoutingChange("Session routing updated: target panel auto-created and linked.");
|
|
323
|
+
} else {
|
|
324
|
+
announceRoutingChange("Session routing updated: target panel linked.");
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
} catch {
|
|
330
|
+
// keep previous target when preflight fails
|
|
331
|
+
}
|
|
332
|
+
applyRoutingArgs(baseArgs);
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
return await originalInvoke(baseArgs);
|
|
336
|
+
} catch (error) {
|
|
337
|
+
if (!isLikelyRoutingError(error)) {
|
|
338
|
+
throw error;
|
|
339
|
+
}
|
|
340
|
+
// Slow path fallback: routing may be stale (target pane closed/moved).
|
|
341
|
+
const previousTarget = state.targetSessionId;
|
|
342
|
+
try {
|
|
343
|
+
const ensureResult = await refreshTargetForCommand({
|
|
344
|
+
allowSplit: true,
|
|
345
|
+
adoptFocusedNonChat: true,
|
|
346
|
+
});
|
|
347
|
+
if (ensureResult.changed) {
|
|
348
|
+
await enforceTitles();
|
|
349
|
+
if (previousTarget && previousTarget !== state.targetSessionId) {
|
|
350
|
+
restoreOriginalColors(previousTarget);
|
|
351
|
+
}
|
|
352
|
+
if (state.targetSessionId !== announcedTarget) {
|
|
353
|
+
announcedTarget = state.targetSessionId;
|
|
354
|
+
if (ensureResult.autoCreated) {
|
|
355
|
+
announceRoutingChange("Session routing updated: target panel auto-created and linked.");
|
|
356
|
+
} else {
|
|
357
|
+
announceRoutingChange("Session routing updated: target panel linked.");
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
} catch {
|
|
362
|
+
// If reroute fails, fall through and surface original error.
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const retryArgs = { ...baseArgs };
|
|
366
|
+
applyRoutingArgs(retryArgs);
|
|
367
|
+
return originalInvoke(retryArgs);
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
unpatchFns.push(() => {
|
|
372
|
+
t.invoke = originalInvoke;
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
let stopped = false;
|
|
377
|
+
let polling = false;
|
|
378
|
+
const timer = setInterval(() => {
|
|
379
|
+
if (stopped || polling) return;
|
|
380
|
+
polling = true;
|
|
381
|
+
void (async () => {
|
|
382
|
+
try {
|
|
383
|
+
const previousTarget = state.targetSessionId;
|
|
384
|
+
const ensured = await refreshTargetForCommand({
|
|
385
|
+
allowSplit: false,
|
|
386
|
+
adoptFocusedNonChat: true,
|
|
387
|
+
});
|
|
388
|
+
if (ensured.changed) {
|
|
389
|
+
await enforceTitles();
|
|
390
|
+
if (previousTarget && previousTarget !== state.targetSessionId) {
|
|
391
|
+
restoreOriginalColors(previousTarget);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (state.targetSessionId === announcedTarget) return;
|
|
395
|
+
announcedTarget = state.targetSessionId;
|
|
396
|
+
announceRoutingChange("Session routing updated: target panel linked.");
|
|
397
|
+
} catch {
|
|
398
|
+
// keep previous target when polling fails
|
|
399
|
+
} finally {
|
|
400
|
+
polling = false;
|
|
401
|
+
}
|
|
402
|
+
})();
|
|
403
|
+
}, intervalMs);
|
|
404
|
+
|
|
405
|
+
return () => {
|
|
406
|
+
stopped = true;
|
|
407
|
+
clearInterval(timer);
|
|
408
|
+
if (originalColors.size > 0) {
|
|
409
|
+
try {
|
|
410
|
+
restoreSessionColorsSync(Array.from(originalColors.values()));
|
|
411
|
+
} catch {
|
|
412
|
+
// non-fatal
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
for (const unpatch of unpatchFns) {
|
|
416
|
+
unpatch();
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
}
|