wtt-connect 0.2.26 → 0.2.28
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.md +46 -7
- package/package.json +1 -1
- package/scripts/install-systemd-user.sh +18 -3
- package/src/adapters/generic-cli.js +56 -1
- package/src/config.js +20 -2
- package/src/runner.js +1 -0
- package/src/runtime-info.js +135 -1
- package/src/service-manager.js +6 -3
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ Implemented production-oriented surfaces:
|
|
|
25
25
|
- `none`
|
|
26
26
|
- `macos-say` using the macOS `say` command, emitted as WAV when upload is enabled
|
|
27
27
|
- HTTP task status update path
|
|
28
|
-
- Permission broker for agent modes;
|
|
28
|
+
- Permission broker for agent modes; Codex, Claude Code, and Gemini default to `yolo` no-approval/full-access mode when bound through `wtt-connect up`
|
|
29
29
|
- Optional WTT media artifact upload path (`/media/sign` → direct upload → `/media/commit`)
|
|
30
30
|
- Agent-generated file artifacts for WTT feed chat (`.docx`, `.pptx`, `.xlsx`, `.pdf`, `.csv`, `.zip`) using explicit final-response markers that are converted into WTT file cards
|
|
31
31
|
- Attachment staging for WTT message media/files; image attachments are passed to adapters that support image inputs, such as Codex with `--image`; Claude Code stays on the Claude Code adapter and returns its own result if the selected model cannot reason over images
|
|
@@ -45,6 +45,7 @@ Install once, then bind each WTT agent identity with one command:
|
|
|
45
45
|
npm install -g wtt-connect
|
|
46
46
|
wtt-connect up codex agent-001 'wtt-tok-001'
|
|
47
47
|
wtt-connect up claude-code agent-002 'wtt-tok-002'
|
|
48
|
+
wtt-connect up gemini agent-003 'wtt-tok-003'
|
|
48
49
|
```
|
|
49
50
|
|
|
50
51
|
`up` writes a profile, installs a background service, enables it for the current user, starts it immediately, and prints status. On Linux it installs a `systemd --user` service; on macOS it installs a LaunchAgent. The npm install step installs package dependencies such as `node-pty`; users should not need to run `npm install` inside a cloned source tree.
|
|
@@ -89,7 +90,7 @@ Important settings:
|
|
|
89
90
|
- `WTT_CONNECT_ADAPTERS=codex,claude-code,cursor,gemini,qoder,opencode,iflow,kimi,pi,acp,devin`
|
|
90
91
|
- `WTT_CONNECT_WORKDIR`
|
|
91
92
|
- `WTT_CONNECT_MODE=full-auto|auto-edit|yolo|suggest`
|
|
92
|
-
-
|
|
93
|
+
- Codex, Claude Code, and Gemini default to `yolo` no-approval/full-access mode when no explicit mode is set. Codex runs with `--dangerously-bypass-approvals-and-sandbox`; Claude Code runs with `--dangerously-skip-permissions --permission-mode bypassPermissions`; Gemini runs with `-y`.
|
|
93
94
|
- `WTT_CONNECT_AGENT_ALIASES=alias1,alias2` for extra @mention names in discussion/collaborative topics; `WTT_AGENT_ID` always works
|
|
94
95
|
- `WTT_CONNECT_ALLOW_YOLO=1` only when intentionally using `yolo`
|
|
95
96
|
- `WTT_CONNECT_STATE_DIR` / `WTT_CONNECT_STORE_FILE` for durable sessions
|
|
@@ -119,6 +120,7 @@ Session continuity:
|
|
|
119
120
|
- `wtt-connect` uses the WTT topic/task id plus adapter and model as the local session key, for example `wtt:topic:<topic_id>:codex:gpt-5.5:high`.
|
|
120
121
|
- Codex stores `codexThreadId` and resumes with `codex exec resume`.
|
|
121
122
|
- Claude Code stores `claudeSessionId` and resumes the same topic with `claude --resume <session_id> -p ...`.
|
|
123
|
+
- Gemini stores `geminiSessionId` and resumes with `gemini --resume <session_id> --output-format stream-json`.
|
|
122
124
|
- Keep `WTT_CONNECT_STATE_DIR`, `WTT_CONNECT_STORE_FILE`, and `WTT_CONNECT_WORKDIR` stable per claimed agent. If they are changed or deleted, the adapter cannot resume previous local sessions.
|
|
123
125
|
|
|
124
126
|
A JSON config may also be supplied:
|
|
@@ -180,6 +182,22 @@ Examples:
|
|
|
180
182
|
wtt-connect up codex agent-alice 'wtt-tok-alice'
|
|
181
183
|
wtt-connect up codex agent-bob 'wtt-tok-bob'
|
|
182
184
|
wtt-connect up claude-code agent-claude 'wtt-tok-claude'
|
|
185
|
+
wtt-connect up gemini agent-gemini 'wtt-tok-gemini'
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Gemini CLI uses Google OAuth by default. Install/authenticate Gemini on the agent host first, then bind it to WTT:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
npm install -g @google/gemini-cli
|
|
192
|
+
gemini
|
|
193
|
+
npm install -g wtt-connect
|
|
194
|
+
wtt-connect up gemini agent-gemini 'wtt-tok-gemini'
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
If the `gemini` binary is not on `PATH`, set `WTT_GEMINI_BIN` in the profile or shell before starting:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
WTT_GEMINI_BIN="$HOME/.local/bin/gemini" wtt-connect up gemini agent-gemini 'wtt-tok-gemini'
|
|
183
201
|
```
|
|
184
202
|
|
|
185
203
|
Each call creates an independent profile and service:
|
|
@@ -196,7 +214,7 @@ On macOS the same profile and state paths are used, while the service file is:
|
|
|
196
214
|
~/Library/LaunchAgents/com.wtt.connect.agent-alice-codex.plist
|
|
197
215
|
```
|
|
198
216
|
|
|
199
|
-
A profile contains exactly one WTT identity: `WTT_AGENT_ID`, `WTT_TOKEN`, adapter, workdir, permission mode, and state directory. Multiple profiles may point at the same local `codex` or `
|
|
217
|
+
A profile contains exactly one WTT identity: `WTT_AGENT_ID`, `WTT_TOKEN`, adapter, workdir, permission mode, and state directory. Multiple profiles may point at the same local `codex`, `claude`, or `gemini` CLI binary; they must not share `WTT_AGENT_ID`, `WTT_TOKEN`, or `WTT_CONNECT_STATE_DIR`.
|
|
200
218
|
|
|
201
219
|
Workdir rules:
|
|
202
220
|
|
|
@@ -208,13 +226,13 @@ Useful flags:
|
|
|
208
226
|
|
|
209
227
|
```bash
|
|
210
228
|
wtt-connect up codex agent-alice 'wtt-tok-alice' \
|
|
211
|
-
--mode yolo \
|
|
212
|
-
--allow-yolo \
|
|
213
229
|
--workdir /data/workspaces/alice \
|
|
214
230
|
--base-url https://www.waxbyte.com \
|
|
215
231
|
--enable-linger
|
|
216
232
|
```
|
|
217
233
|
|
|
234
|
+
`codex`, `claude-code`, and `gemini` already default to `--mode yolo` with `WTT_CONNECT_ALLOW_YOLO=1`. Pass `--mode suggest` or `--mode auto-edit` only when you intentionally want stricter behavior.
|
|
235
|
+
|
|
218
236
|
`--enable-linger` asks systemd to start the Linux user service at boot before an interactive login. Without it, the service starts when the user session starts. On macOS LaunchAgents start at user login; `--enable-linger` is ignored with a warning.
|
|
219
237
|
|
|
220
238
|
Start an already-created profile in the foreground:
|
|
@@ -261,11 +279,22 @@ WTT_CONNECT_ADAPTERS=claude-code
|
|
|
261
279
|
WTT_CONNECT_STATE_DIR=.wtt-connect-claude
|
|
262
280
|
```
|
|
263
281
|
|
|
282
|
+
```dotenv
|
|
283
|
+
# .env.gemini
|
|
284
|
+
WTT_AGENT_ID=agent-gemini
|
|
285
|
+
WTT_TOKEN=***
|
|
286
|
+
WTT_CONNECT_ADAPTER=gemini
|
|
287
|
+
WTT_CONNECT_ADAPTERS=gemini
|
|
288
|
+
WTT_CONNECT_STATE_DIR=.wtt-connect-gemini
|
|
289
|
+
WTT_GEMINI_BIN=gemini
|
|
290
|
+
```
|
|
291
|
+
|
|
264
292
|
Start them separately:
|
|
265
293
|
|
|
266
294
|
```bash
|
|
267
295
|
./start.sh --env-file .env.codex
|
|
268
296
|
./start.sh --env-file .env.claude
|
|
297
|
+
./start.sh --env-file .env.gemini
|
|
269
298
|
```
|
|
270
299
|
|
|
271
300
|
Install them as separate macOS LaunchAgents:
|
|
@@ -273,6 +302,7 @@ Install them as separate macOS LaunchAgents:
|
|
|
273
302
|
```bash
|
|
274
303
|
./scripts/install-launchd.sh --name codex --env-file .env.codex
|
|
275
304
|
./scripts/install-launchd.sh --name claude --env-file .env.claude
|
|
305
|
+
./scripts/install-launchd.sh --name gemini --env-file .env.gemini
|
|
276
306
|
```
|
|
277
307
|
|
|
278
308
|
Install them as separate Linux `systemd --user` services from a source checkout if you are not using npm:
|
|
@@ -280,6 +310,7 @@ Install them as separate Linux `systemd --user` services from a source checkout
|
|
|
280
310
|
```bash
|
|
281
311
|
./scripts/install-agent.sh codex agent-codex '***'
|
|
282
312
|
./scripts/install-agent.sh claude-code agent-claude '***'
|
|
313
|
+
./scripts/install-agent.sh gemini agent-gemini '***'
|
|
283
314
|
```
|
|
284
315
|
|
|
285
316
|
The npm equivalent is preferred for end users:
|
|
@@ -287,6 +318,7 @@ The npm equivalent is preferred for end users:
|
|
|
287
318
|
```bash
|
|
288
319
|
wtt-connect up codex agent-codex '***'
|
|
289
320
|
wtt-connect up claude-code agent-claude '***'
|
|
321
|
+
wtt-connect up gemini agent-gemini '***'
|
|
290
322
|
```
|
|
291
323
|
|
|
292
324
|
Use named options when you need a custom service name, a stricter permission mode, or boot-before-login behavior:
|
|
@@ -306,12 +338,19 @@ Use named options when you need a custom service name, a stricter permission mod
|
|
|
306
338
|
--agent-id agent-claude \
|
|
307
339
|
--token '***' \
|
|
308
340
|
--adapter claude-code \
|
|
309
|
-
--
|
|
341
|
+
--publish-progress \
|
|
342
|
+
--enable-linger
|
|
343
|
+
|
|
344
|
+
./scripts/install-systemd-user.sh \
|
|
345
|
+
--name alice-gemini \
|
|
346
|
+
--agent-id agent-gemini \
|
|
347
|
+
--token '***' \
|
|
348
|
+
--adapter gemini \
|
|
310
349
|
--publish-progress \
|
|
311
350
|
--enable-linger
|
|
312
351
|
```
|
|
313
352
|
|
|
314
|
-
Each invocation writes a separate `.env.<name>`, `.wtt-connect-<name>` state directory, and `wtt-connect-<name>.service`. Multiple WTT agent identities may share the same local `codex` or `
|
|
353
|
+
Each invocation writes a separate `.env.<name>`, `.wtt-connect-<name>` state directory, and `wtt-connect-<name>.service`. Multiple WTT agent identities may share the same local `codex`, `claude`, or `gemini` CLI binary; only the WTT identity and state directory need to be unique.
|
|
315
354
|
|
|
316
355
|
Uninstall one identity with:
|
|
317
356
|
|
package/package.json
CHANGED
|
@@ -28,8 +28,8 @@ Options:
|
|
|
28
28
|
--workdir <dir> Agent working directory (default: wtt-connect root)
|
|
29
29
|
--env-file <file> Env file to write (default: .env.<name>)
|
|
30
30
|
--state-dir <dir> State directory (default: .wtt-connect-<name>)
|
|
31
|
-
--mode <mode> full-auto, auto-edit, suggest, yolo (default:
|
|
32
|
-
--allow-yolo
|
|
31
|
+
--mode <mode> full-auto, auto-edit, suggest, yolo (default: yolo for codex/claude-code/gemini)
|
|
32
|
+
--allow-yolo Allow yolo mode for adapters that do not default to full access
|
|
33
33
|
--publish-progress Publish agent progress/status messages
|
|
34
34
|
--timeout <seconds> Per-run timeout (default: 3600)
|
|
35
35
|
--node-bin <path> node binary (default: command -v node)
|
|
@@ -49,7 +49,7 @@ BASE_URL="https://www.waxbyte.com"
|
|
|
49
49
|
WORKDIR="$ROOT"
|
|
50
50
|
ENV_FILE=""
|
|
51
51
|
STATE_DIR=""
|
|
52
|
-
MODE="${WTT_CONNECT_MODE:-
|
|
52
|
+
MODE="${WTT_CONNECT_MODE:-}"
|
|
53
53
|
ALLOW_YOLO=0
|
|
54
54
|
PUBLISH_PROGRESS=0
|
|
55
55
|
TIMEOUT_SECONDS=3600
|
|
@@ -98,6 +98,21 @@ if [[ "${#POSITIONAL[@]}" -gt 0 ]]; then
|
|
|
98
98
|
fi
|
|
99
99
|
|
|
100
100
|
ADAPTER="${ADAPTER:-codex}"
|
|
101
|
+
case "$ADAPTER" in
|
|
102
|
+
claude) ADAPTER="claude-code" ;;
|
|
103
|
+
cc-connect) ADAPTER="codex" ;;
|
|
104
|
+
esac
|
|
105
|
+
if [[ -z "$MODE" ]]; then
|
|
106
|
+
case "$ADAPTER" in
|
|
107
|
+
codex|claude-code|gemini)
|
|
108
|
+
MODE="yolo"
|
|
109
|
+
ALLOW_YOLO=1
|
|
110
|
+
;;
|
|
111
|
+
*)
|
|
112
|
+
MODE="full-auto"
|
|
113
|
+
;;
|
|
114
|
+
esac
|
|
115
|
+
fi
|
|
101
116
|
if [[ -z "$NAME" && -n "$AGENT_ID" ]]; then
|
|
102
117
|
NAME="$AGENT_ID-$ADAPTER"
|
|
103
118
|
fi
|
|
@@ -144,6 +144,12 @@ export class GenericCliAdapter {
|
|
|
144
144
|
if (context.onProgress) context.onProgress(event).catch(() => {});
|
|
145
145
|
}, this.config.taskTimeoutSeconds * 1000);
|
|
146
146
|
if (result.sessionId) this.store?.patchSession(sessionKey, { [storedKey]: result.sessionId, adapter: this.name });
|
|
147
|
+
const runtimePatch = {
|
|
148
|
+
...(result.model ? { current_model: result.model, model: result.model, model_source: `${this.name}_runtime` } : {}),
|
|
149
|
+
...(result.reasoningEffort ? { reasoning_effort: result.reasoningEffort } : {}),
|
|
150
|
+
...(result.thinkingMode ? { thinking_mode: result.thinkingMode } : {}),
|
|
151
|
+
};
|
|
152
|
+
if (Object.keys(runtimePatch).length) this.store?.patchRuntime(runtimePatch);
|
|
147
153
|
return result.text.trim();
|
|
148
154
|
}
|
|
149
155
|
}
|
|
@@ -166,6 +172,9 @@ function runCli(bin, args, stdinText, cwd, onEvent, timeoutMs) {
|
|
|
166
172
|
let stderr = '';
|
|
167
173
|
let plainStdout = '';
|
|
168
174
|
let sessionId = '';
|
|
175
|
+
let model = '';
|
|
176
|
+
let reasoningEffort = '';
|
|
177
|
+
let thinkingMode = '';
|
|
169
178
|
const texts = [];
|
|
170
179
|
const timer = setTimeout(() => {
|
|
171
180
|
child.kill('SIGTERM');
|
|
@@ -183,6 +192,9 @@ function runCli(bin, args, stdinText, cwd, onEvent, timeoutMs) {
|
|
|
183
192
|
}
|
|
184
193
|
const eventSessionId = extractSessionId(event);
|
|
185
194
|
if (eventSessionId) sessionId = eventSessionId;
|
|
195
|
+
model = extractModel(event) || model;
|
|
196
|
+
reasoningEffort = extractReasoningEffort(event) || reasoningEffort;
|
|
197
|
+
thinkingMode = extractThinkingMode(event) || thinkingMode;
|
|
186
198
|
const text = extractText(event);
|
|
187
199
|
if (text) texts.push({ text, delta: event.delta === true });
|
|
188
200
|
onEvent?.(event);
|
|
@@ -190,12 +202,55 @@ function runCli(bin, args, stdinText, cwd, onEvent, timeoutMs) {
|
|
|
190
202
|
child.on('close', (code) => {
|
|
191
203
|
clearTimeout(timer);
|
|
192
204
|
if (code !== 0) reject(new Error(`${bin} exited ${code}: ${stderr.trim()}`));
|
|
193
|
-
else resolve({ text: collapseText(texts, plainStdout), sessionId });
|
|
205
|
+
else resolve({ text: collapseText(texts, plainStdout), sessionId, model, reasoningEffort, thinkingMode });
|
|
194
206
|
});
|
|
195
207
|
if (stdinText != null) child.stdin.end(stdinText);
|
|
196
208
|
});
|
|
197
209
|
}
|
|
198
210
|
|
|
211
|
+
function extractModel(event) {
|
|
212
|
+
if (!event || typeof event !== 'object') return '';
|
|
213
|
+
for (const key of ['model', 'model_id', 'modelId', 'current_model']) {
|
|
214
|
+
const value = event[key];
|
|
215
|
+
if (typeof value === 'string' && value.trim()) return value.trim();
|
|
216
|
+
}
|
|
217
|
+
const statsModels = event.stats?.models;
|
|
218
|
+
if (statsModels && typeof statsModels === 'object' && !Array.isArray(statsModels)) {
|
|
219
|
+
const entries = Object.entries(statsModels).filter(([name]) => String(name || '').trim());
|
|
220
|
+
if (entries.length) {
|
|
221
|
+
entries.sort((a, b) => modelTokenTotal(b[1]) - modelTokenTotal(a[1]));
|
|
222
|
+
return String(entries[0][0]).trim();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return '';
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function modelTokenTotal(value) {
|
|
229
|
+
if (!value || typeof value !== 'object') return 0;
|
|
230
|
+
return Number(value.total_tokens || value.totalTokens || value.output_tokens || value.outputTokens || 0) || 0;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function extractReasoningEffort(event) {
|
|
234
|
+
const raw = event?.reasoning_effort || event?.reasoningEffort || event?.thinking_effort || event?.thinkingEffort;
|
|
235
|
+
return normalizeReasoning(raw);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function extractThinkingMode(event) {
|
|
239
|
+
const raw = event?.thinking_mode || event?.thinkingMode || event?.thinking || event?.displayThinking;
|
|
240
|
+
if (typeof raw === 'boolean') return raw ? 'high' : 'off';
|
|
241
|
+
return typeof raw === 'string' ? raw.trim() : '';
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function normalizeReasoning(value) {
|
|
245
|
+
const raw = String(value || '').trim().toLowerCase();
|
|
246
|
+
if (!raw) return '';
|
|
247
|
+
if (['off', 'none', 'disabled', 'false', '0'].includes(raw)) return 'off';
|
|
248
|
+
if (['low', 'minimal'].includes(raw)) return 'low';
|
|
249
|
+
if (['medium', 'normal', 'auto'].includes(raw)) return 'medium';
|
|
250
|
+
if (['high', 'full', 'max', 'maximum', 'xhigh'].includes(raw)) return 'high';
|
|
251
|
+
return raw;
|
|
252
|
+
}
|
|
253
|
+
|
|
199
254
|
function collapseText(texts, plainStdout) {
|
|
200
255
|
const filtered = texts
|
|
201
256
|
.map((entry) => ({ text: String(entry?.text || ''), delta: entry?.delta === true }))
|
package/src/config.js
CHANGED
|
@@ -3,6 +3,8 @@ import os from 'node:os';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { parseBool, parseIntEnv } from './env.js';
|
|
5
5
|
|
|
6
|
+
const FULL_ACCESS_DEFAULT_ADAPTERS = new Set(['codex', 'claude-code', 'gemini']);
|
|
7
|
+
|
|
6
8
|
function wsUrl(base, agentId) {
|
|
7
9
|
let b = String(base || '').replace(/\/$/, '');
|
|
8
10
|
if (b.endsWith('/api/v1')) b = b.slice(0, -7);
|
|
@@ -27,6 +29,8 @@ export function loadConfig(argv = {}) {
|
|
|
27
29
|
const token = process.env.WTT_TOKEN || process.env.WTT_AGENT_TOKEN || fileConfig.token || '';
|
|
28
30
|
const adapter = process.env.WTT_CONNECT_ADAPTER || fileConfig.adapter || 'codex';
|
|
29
31
|
const adapters = String(process.env.WTT_CONNECT_ADAPTERS || fileConfig.adapters || adapter).split(',').map((x) => x.trim()).filter(Boolean);
|
|
32
|
+
const defaultMode = defaultModeForAdapters(adapter, adapters);
|
|
33
|
+
const mode = process.env.WTT_CONNECT_MODE || fileConfig.mode || defaultMode;
|
|
30
34
|
const explicitWorkDir = process.env.WTT_CONNECT_WORKDIR || fileConfig.workDir || '';
|
|
31
35
|
const workDir = resolveAgentWorkDir(explicitWorkDir, agentId);
|
|
32
36
|
const stateDir = process.env.WTT_CONNECT_STATE_DIR || fileConfig.stateDir || path.join(workDir, '.wtt-connect');
|
|
@@ -52,8 +56,8 @@ export function loadConfig(argv = {}) {
|
|
|
52
56
|
},
|
|
53
57
|
workDir,
|
|
54
58
|
model: process.env.WTT_CONNECT_MODEL || fileConfig.model || '',
|
|
55
|
-
mode
|
|
56
|
-
allowYolo: parseBool(process.env.WTT_CONNECT_ALLOW_YOLO, fileConfig.allowYolo ??
|
|
59
|
+
mode,
|
|
60
|
+
allowYolo: parseBool(process.env.WTT_CONNECT_ALLOW_YOLO, fileConfig.allowYolo ?? mode === 'yolo'),
|
|
57
61
|
reasoningEffort: process.env.WTT_CONNECT_REASONING_EFFORT || fileConfig.reasoningEffort || 'low',
|
|
58
62
|
codexBin: process.env.WTT_CODEX_BIN || fileConfig.codexBin || 'codex',
|
|
59
63
|
claudeBin: process.env.WTT_CLAUDE_BIN || fileConfig.claudeBin || 'claude',
|
|
@@ -112,6 +116,20 @@ export function loadConfig(argv = {}) {
|
|
|
112
116
|
};
|
|
113
117
|
}
|
|
114
118
|
|
|
119
|
+
function defaultModeForAdapters(adapter, adapters) {
|
|
120
|
+
const names = [adapter, ...(Array.isArray(adapters) ? adapters : [])]
|
|
121
|
+
.map(normalizeAdapter)
|
|
122
|
+
.filter(Boolean);
|
|
123
|
+
return names.some((name) => FULL_ACCESS_DEFAULT_ADAPTERS.has(name)) ? 'yolo' : 'full-auto';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function normalizeAdapter(name) {
|
|
127
|
+
const normalized = String(name || '').trim().toLowerCase();
|
|
128
|
+
if (normalized === 'claude') return 'claude-code';
|
|
129
|
+
if (normalized === 'cc-connect') return 'codex';
|
|
130
|
+
return normalized;
|
|
131
|
+
}
|
|
132
|
+
|
|
115
133
|
export function resolveAgentWorkDir(explicitWorkDir, agentId) {
|
|
116
134
|
if (explicitWorkDir) return path.resolve(String(explicitWorkDir));
|
|
117
135
|
return path.join(os.homedir(), '.wtt-connect', safeAgentPathSegment(agentId));
|
package/src/runner.js
CHANGED
package/src/runtime-info.js
CHANGED
|
@@ -8,6 +8,8 @@ export function buildRuntimeInfo(config, runtimeState = {}) {
|
|
|
8
8
|
const git = gitInfo(workdir);
|
|
9
9
|
const adapter = String(runtimeState.adapter || config.adapter || '').trim();
|
|
10
10
|
const model = runtimeModel(config, adapter, runtimeState);
|
|
11
|
+
const reasoning = runtimeReasoningEffort(config, adapter, runtimeState);
|
|
12
|
+
const thinkingMode = runtimeThinkingMode(config, adapter, runtimeState);
|
|
11
13
|
return {
|
|
12
14
|
kind: 'wtt-connect',
|
|
13
15
|
agent_id: config.agentId,
|
|
@@ -16,7 +18,8 @@ export function buildRuntimeInfo(config, runtimeState = {}) {
|
|
|
16
18
|
...(model ? { model, current_model: model } : {}),
|
|
17
19
|
...(runtimeState.model_source ? { model_source: String(runtimeState.model_source) } : {}),
|
|
18
20
|
...(runtimeState.status ? { runtime_status: String(runtimeState.status) } : {}),
|
|
19
|
-
...(
|
|
21
|
+
...(reasoning ? { reasoning_effort: reasoning } : {}),
|
|
22
|
+
...(thinkingMode ? { thinking_mode: thinkingMode } : {}),
|
|
20
23
|
workdir,
|
|
21
24
|
workdir_name: path.basename(workdir || ''),
|
|
22
25
|
cwd: safeRealpath(process.cwd()),
|
|
@@ -45,9 +48,37 @@ function runtimeModel(config, adapter, runtimeState = {}) {
|
|
|
45
48
|
if (normalizedAdapter === 'claude-code' || normalizedAdapter === 'claude' || normalizedAdapter === 'claude_code') {
|
|
46
49
|
return String(process.env.ANTHROPIC_MODEL || readClaudeConfigModel() || '').trim();
|
|
47
50
|
}
|
|
51
|
+
if (normalizedAdapter === 'gemini') {
|
|
52
|
+
return String(process.env.GEMINI_MODEL || process.env.GEMINI_CLI_MODEL || process.env.GOOGLE_GEMINI_MODEL || readGeminiConfigModel() || '').trim();
|
|
53
|
+
}
|
|
48
54
|
return String(process.env.WTT_CONNECT_MODEL || '').trim();
|
|
49
55
|
}
|
|
50
56
|
|
|
57
|
+
function runtimeReasoningEffort(config, adapter, runtimeState = {}) {
|
|
58
|
+
const fromRun = normalizeReasoning(runtimeState.reasoning_effort || runtimeState.reasoningEffort);
|
|
59
|
+
if (fromRun) return fromRun;
|
|
60
|
+
|
|
61
|
+
const explicit = normalizeReasoning(config.reasoningEffort || config.reasoning_effort);
|
|
62
|
+
if (explicit) return explicit;
|
|
63
|
+
|
|
64
|
+
const normalizedAdapter = String(adapter || config.adapter || '').trim().toLowerCase();
|
|
65
|
+
if (normalizedAdapter === 'codex') return normalizeReasoning(process.env.OPENAI_REASONING_EFFORT || process.env.CODEX_REASONING_EFFORT || readCodexReasoningEffort());
|
|
66
|
+
if (normalizedAdapter === 'gemini') return normalizeReasoning(process.env.GEMINI_THINKING || process.env.GEMINI_THINKING_MODE || readGeminiThinkingMode());
|
|
67
|
+
if (normalizedAdapter === 'claude-code' || normalizedAdapter === 'claude' || normalizedAdapter === 'claude_code') {
|
|
68
|
+
return normalizeReasoning(process.env.CLAUDE_THINKING || process.env.ANTHROPIC_THINKING || readClaudeThinkingMode());
|
|
69
|
+
}
|
|
70
|
+
return normalizeReasoning(process.env.WTT_CONNECT_REASONING_EFFORT);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function runtimeThinkingMode(config, adapter, runtimeState = {}) {
|
|
74
|
+
const value = String(runtimeState.thinking_mode || runtimeState.thinkingMode || '').trim();
|
|
75
|
+
if (value) return value;
|
|
76
|
+
const normalizedAdapter = String(adapter || config.adapter || '').trim().toLowerCase();
|
|
77
|
+
if (normalizedAdapter === 'gemini') return readGeminiThinkingMode();
|
|
78
|
+
if (normalizedAdapter === 'claude-code' || normalizedAdapter === 'claude' || normalizedAdapter === 'claude_code') return readClaudeThinkingMode();
|
|
79
|
+
return '';
|
|
80
|
+
}
|
|
81
|
+
|
|
51
82
|
function readCodexConfigModel() {
|
|
52
83
|
const home = os.homedir();
|
|
53
84
|
const candidates = [
|
|
@@ -61,6 +92,19 @@ function readCodexConfigModel() {
|
|
|
61
92
|
return '';
|
|
62
93
|
}
|
|
63
94
|
|
|
95
|
+
function readCodexReasoningEffort() {
|
|
96
|
+
const home = os.homedir();
|
|
97
|
+
const candidates = [
|
|
98
|
+
path.join(home, '.codex', 'config.toml'),
|
|
99
|
+
path.join(home, '.config', 'codex', 'config.toml'),
|
|
100
|
+
];
|
|
101
|
+
for (const file of candidates) {
|
|
102
|
+
const effort = readTomlStringKey(file, 'model_reasoning_effort') || readTomlStringKey(file, 'reasoning_effort');
|
|
103
|
+
if (effort) return effort;
|
|
104
|
+
}
|
|
105
|
+
return '';
|
|
106
|
+
}
|
|
107
|
+
|
|
64
108
|
function readClaudeConfigModel() {
|
|
65
109
|
const home = os.homedir();
|
|
66
110
|
const candidates = [
|
|
@@ -75,6 +119,50 @@ function readClaudeConfigModel() {
|
|
|
75
119
|
return '';
|
|
76
120
|
}
|
|
77
121
|
|
|
122
|
+
function readClaudeThinkingMode() {
|
|
123
|
+
const home = os.homedir();
|
|
124
|
+
const candidates = [
|
|
125
|
+
path.join(home, '.claude.json'),
|
|
126
|
+
path.join(home, '.claude', 'settings.json'),
|
|
127
|
+
path.join(home, '.config', 'claude', 'settings.json'),
|
|
128
|
+
];
|
|
129
|
+
for (const file of candidates) {
|
|
130
|
+
const mode = readJsonThinkingKey(file);
|
|
131
|
+
if (mode) return mode;
|
|
132
|
+
}
|
|
133
|
+
return '';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function readGeminiConfigModel() {
|
|
137
|
+
const home = os.homedir();
|
|
138
|
+
const candidates = [
|
|
139
|
+
path.join(home, '.gemini', 'settings.json'),
|
|
140
|
+
path.join(home, '.gemini', 'config.json'),
|
|
141
|
+
path.join(home, '.config', 'gemini', 'settings.json'),
|
|
142
|
+
path.join(home, '.config', 'gemini', 'config.json'),
|
|
143
|
+
];
|
|
144
|
+
for (const file of candidates) {
|
|
145
|
+
const model = readJsonModelKey(file);
|
|
146
|
+
if (model) return model;
|
|
147
|
+
}
|
|
148
|
+
return '';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function readGeminiThinkingMode() {
|
|
152
|
+
const home = os.homedir();
|
|
153
|
+
const candidates = [
|
|
154
|
+
path.join(home, '.gemini', 'settings.json'),
|
|
155
|
+
path.join(home, '.gemini', 'config.json'),
|
|
156
|
+
path.join(home, '.config', 'gemini', 'settings.json'),
|
|
157
|
+
path.join(home, '.config', 'gemini', 'config.json'),
|
|
158
|
+
];
|
|
159
|
+
for (const file of candidates) {
|
|
160
|
+
const mode = readJsonThinkingKey(file);
|
|
161
|
+
if (mode) return mode;
|
|
162
|
+
}
|
|
163
|
+
return '';
|
|
164
|
+
}
|
|
165
|
+
|
|
78
166
|
function readJsonModelKey(file) {
|
|
79
167
|
try {
|
|
80
168
|
if (!fs.existsSync(file)) return '';
|
|
@@ -85,8 +173,23 @@ function readJsonModelKey(file) {
|
|
|
85
173
|
}
|
|
86
174
|
}
|
|
87
175
|
|
|
176
|
+
function readJsonThinkingKey(file) {
|
|
177
|
+
try {
|
|
178
|
+
if (!fs.existsSync(file)) return '';
|
|
179
|
+
const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
180
|
+
return findThinkingValue(parsed);
|
|
181
|
+
} catch {
|
|
182
|
+
return '';
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
88
186
|
function findModelValue(value, depth = 0) {
|
|
89
187
|
if (!value || typeof value !== 'object' || depth > 4) return '';
|
|
188
|
+
const modelObj = value.model;
|
|
189
|
+
if (modelObj && typeof modelObj === 'object') {
|
|
190
|
+
const name = modelObj.name;
|
|
191
|
+
if (typeof name === 'string' && name.trim()) return name.trim();
|
|
192
|
+
}
|
|
90
193
|
for (const key of ['model', 'model_id', 'modelId', 'defaultModel', 'default_model']) {
|
|
91
194
|
const raw = value[key];
|
|
92
195
|
if (typeof raw === 'string' && raw.trim()) return raw.trim();
|
|
@@ -98,6 +201,37 @@ function findModelValue(value, depth = 0) {
|
|
|
98
201
|
return '';
|
|
99
202
|
}
|
|
100
203
|
|
|
204
|
+
function findThinkingValue(value, depth = 0) {
|
|
205
|
+
if (!value || typeof value !== 'object' || depth > 6) return '';
|
|
206
|
+
for (const key of ['thinking_mode', 'thinkingMode', 'thinking', 'displayThinking', 'reasoning_effort', 'reasoningEffort', 'thinkingLevel']) {
|
|
207
|
+
const raw = value[key];
|
|
208
|
+
if (typeof raw === 'string' && raw.trim()) return raw.trim();
|
|
209
|
+
if (typeof raw === 'boolean') return raw ? 'high' : 'off';
|
|
210
|
+
}
|
|
211
|
+
const budget = value.thinkingBudget ?? value.thinking_budget;
|
|
212
|
+
if (typeof budget === 'number') {
|
|
213
|
+
if (budget <= 0) return 'off';
|
|
214
|
+
if (budget >= 8192) return 'high';
|
|
215
|
+
if (budget >= 2048) return 'medium';
|
|
216
|
+
return 'low';
|
|
217
|
+
}
|
|
218
|
+
for (const child of Object.values(value)) {
|
|
219
|
+
const found = findThinkingValue(child, depth + 1);
|
|
220
|
+
if (found) return found;
|
|
221
|
+
}
|
|
222
|
+
return '';
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function normalizeReasoning(value) {
|
|
226
|
+
const raw = String(value || '').trim().toLowerCase();
|
|
227
|
+
if (!raw) return '';
|
|
228
|
+
if (['off', 'none', 'disabled', 'false', '0'].includes(raw)) return 'off';
|
|
229
|
+
if (['low', 'minimal'].includes(raw)) return 'low';
|
|
230
|
+
if (['medium', 'normal', 'auto'].includes(raw)) return 'medium';
|
|
231
|
+
if (['high', 'full', 'max', 'maximum', 'xhigh'].includes(raw)) return 'high';
|
|
232
|
+
return raw;
|
|
233
|
+
}
|
|
234
|
+
|
|
101
235
|
function readTomlStringKey(file, key) {
|
|
102
236
|
try {
|
|
103
237
|
if (!fs.existsSync(file)) return '';
|
package/src/service-manager.js
CHANGED
|
@@ -8,6 +8,7 @@ import { resolveAgentWorkDir } from './config.js';
|
|
|
8
8
|
|
|
9
9
|
const DEFAULT_BASE_URL = 'https://www.waxbyte.com';
|
|
10
10
|
const DEFAULT_MODE = 'full-auto';
|
|
11
|
+
const FULL_ACCESS_DEFAULT_ADAPTERS = new Set(['codex', 'claude-code', 'gemini']);
|
|
11
12
|
const VALID_MODES = new Set(['suggest', 'auto-edit', 'full-auto', 'yolo']);
|
|
12
13
|
|
|
13
14
|
export function resolveProfileEnvFile(profile) {
|
|
@@ -23,9 +24,11 @@ export async function up(argv) {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
const profile = sanitizeProfile(argv.profile || argv.name || `${agentId}-${adapter}`);
|
|
26
|
-
const
|
|
27
|
+
const defaultMode = FULL_ACCESS_DEFAULT_ADAPTERS.has(adapter) ? 'yolo' : DEFAULT_MODE;
|
|
28
|
+
const mode = argv.mode || process.env.WTT_CONNECT_MODE || defaultMode;
|
|
27
29
|
if (!VALID_MODES.has(mode)) throw new Error(`invalid --mode: ${mode}`);
|
|
28
|
-
|
|
30
|
+
const allowYolo = mode === 'yolo' && FULL_ACCESS_DEFAULT_ADAPTERS.has(adapter) ? true : Boolean(argv.allowYolo || argv.yes);
|
|
31
|
+
if (mode === 'yolo' && !allowYolo) {
|
|
29
32
|
throw new Error('--mode yolo requires --allow-yolo or --yes');
|
|
30
33
|
}
|
|
31
34
|
|
|
@@ -53,7 +56,7 @@ export async function up(argv) {
|
|
|
53
56
|
WTT_CONNECT_ADAPTERS: adapter,
|
|
54
57
|
WTT_CONNECT_WORKDIR: workDir,
|
|
55
58
|
WTT_CONNECT_MODE: mode,
|
|
56
|
-
WTT_CONNECT_ALLOW_YOLO: mode === 'yolo' ||
|
|
59
|
+
WTT_CONNECT_ALLOW_YOLO: mode === 'yolo' || allowYolo ? '1' : '',
|
|
57
60
|
WTT_CONNECT_ENABLE_CHAT: '1',
|
|
58
61
|
WTT_CONNECT_PUBLISH_PROGRESS: argv.publishProgress ? '1' : '0',
|
|
59
62
|
WTT_CONNECT_TASK_TIMEOUT_SECONDS: String(argv.timeoutSeconds || (argv.timeout ? Math.round(argv.timeout / 1000) : 3600)),
|