@researai/deepscientist 1.5.4 → 1.5.6
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/bin/ds.js +56 -4
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/deepscientist/__init__.py +1 -1
- package/src/deepscientist/prompts/builder.py +1 -1
- package/src/deepscientist/quest/service.py +2 -2
- package/src/prompts/connectors/qq.md +3 -0
- package/src/prompts/system.md +2 -1
- package/src/tui/package.json +1 -1
package/bin/ds.js
CHANGED
|
@@ -36,7 +36,7 @@ const pythonCommands = new Set([
|
|
|
36
36
|
const UPDATE_PACKAGE_NAME = String(packageJson.name || '@researai/deepscientist').trim() || '@researai/deepscientist';
|
|
37
37
|
const UPDATE_CHECK_TTL_MS = 12 * 60 * 60 * 1000;
|
|
38
38
|
|
|
39
|
-
const optionsWithValues = new Set(['--home', '--host', '--port', '--quest-id', '--mode']);
|
|
39
|
+
const optionsWithValues = new Set(['--home', '--host', '--port', '--quest-id', '--mode', '--proxy']);
|
|
40
40
|
|
|
41
41
|
function printLauncherHelp() {
|
|
42
42
|
console.log(`DeepScientist launcher
|
|
@@ -52,6 +52,7 @@ Usage:
|
|
|
52
52
|
ds --tui
|
|
53
53
|
ds --both
|
|
54
54
|
ds --host 0.0.0.0 --port 21000
|
|
55
|
+
ds --host 0.0.0.0 --port 21000 --proxy http://127.0.0.1:58887
|
|
55
56
|
ds --stop
|
|
56
57
|
ds --restart
|
|
57
58
|
ds --status
|
|
@@ -71,6 +72,7 @@ Launcher flags:
|
|
|
71
72
|
--restart Restart the managed daemon
|
|
72
73
|
--home <path> Use a custom DeepScientist home
|
|
73
74
|
--here Use the current working directory as DeepScientist home
|
|
75
|
+
--proxy <url> Use an outbound HTTP/WS proxy for npm and Python runtime traffic
|
|
74
76
|
--quest-id <id> Open the TUI on one quest directly
|
|
75
77
|
|
|
76
78
|
Update:
|
|
@@ -115,6 +117,35 @@ function expandUserPath(rawPath) {
|
|
|
115
117
|
return normalized;
|
|
116
118
|
}
|
|
117
119
|
|
|
120
|
+
function normalizeProxyUrl(rawValue) {
|
|
121
|
+
const value = String(rawValue || '').trim();
|
|
122
|
+
return value || null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function applyLauncherProxy(proxyUrl) {
|
|
126
|
+
const normalized = normalizeProxyUrl(proxyUrl);
|
|
127
|
+
if (!normalized) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
for (const key of ['HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'http_proxy', 'https_proxy', 'all_proxy']) {
|
|
131
|
+
process.env[key] = normalized;
|
|
132
|
+
}
|
|
133
|
+
for (const key of ['NO_PROXY', 'no_proxy']) {
|
|
134
|
+
const current = String(process.env[key] || '').trim();
|
|
135
|
+
const values = current
|
|
136
|
+
.split(',')
|
|
137
|
+
.map((item) => item.trim())
|
|
138
|
+
.filter(Boolean);
|
|
139
|
+
for (const host of ['127.0.0.1', 'localhost', '::1', '0.0.0.0']) {
|
|
140
|
+
if (!values.includes(host)) {
|
|
141
|
+
values.push(host);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
process.env[key] = values.join(',');
|
|
145
|
+
}
|
|
146
|
+
return normalized;
|
|
147
|
+
}
|
|
148
|
+
|
|
118
149
|
function updateStatePath(home) {
|
|
119
150
|
return path.join(home, 'runtime', 'update-state.json');
|
|
120
151
|
}
|
|
@@ -842,6 +873,7 @@ function parseLauncherArgs(argv) {
|
|
|
842
873
|
let host = null;
|
|
843
874
|
let port = null;
|
|
844
875
|
let home = null;
|
|
876
|
+
let proxy = null;
|
|
845
877
|
let stop = false;
|
|
846
878
|
let restart = false;
|
|
847
879
|
let openBrowser = null;
|
|
@@ -870,6 +902,7 @@ function parseLauncherArgs(argv) {
|
|
|
870
902
|
else if (arg === '--host' && args[index + 1]) host = args[++index];
|
|
871
903
|
else if (arg === '--port' && args[index + 1]) port = Number(args[++index]);
|
|
872
904
|
else if (arg === '--home' && args[index + 1]) home = path.resolve(args[++index]);
|
|
905
|
+
else if (arg === '--proxy' && args[index + 1]) proxy = args[++index];
|
|
873
906
|
else if (arg === '--quest-id' && args[index + 1]) questId = args[++index];
|
|
874
907
|
else if (arg === '--mode' && args[index + 1]) mode = normalizeMode(args[++index]);
|
|
875
908
|
else if (arg === '--help' || arg === '-h') return { help: true };
|
|
@@ -882,6 +915,7 @@ function parseLauncherArgs(argv) {
|
|
|
882
915
|
host,
|
|
883
916
|
port,
|
|
884
917
|
home,
|
|
918
|
+
proxy,
|
|
885
919
|
stop,
|
|
886
920
|
restart,
|
|
887
921
|
status,
|
|
@@ -944,6 +978,7 @@ function parseUpdateArgs(argv) {
|
|
|
944
978
|
let home = null;
|
|
945
979
|
let host = null;
|
|
946
980
|
let port = null;
|
|
981
|
+
let proxy = null;
|
|
947
982
|
let restartDaemon = null;
|
|
948
983
|
let skipUpdateCheck = false;
|
|
949
984
|
|
|
@@ -962,6 +997,7 @@ function parseUpdateArgs(argv) {
|
|
|
962
997
|
else if (arg === '--home' && args[index + 1]) home = path.resolve(args[++index]);
|
|
963
998
|
else if (arg === '--host' && args[index + 1]) host = args[++index];
|
|
964
999
|
else if (arg === '--port' && args[index + 1]) port = Number(args[++index]);
|
|
1000
|
+
else if (arg === '--proxy' && args[index + 1]) proxy = args[++index];
|
|
965
1001
|
else if (arg === '--help' || arg === '-h') return { help: true };
|
|
966
1002
|
else if (!arg.startsWith('--')) return null;
|
|
967
1003
|
}
|
|
@@ -979,6 +1015,7 @@ function parseUpdateArgs(argv) {
|
|
|
979
1015
|
home,
|
|
980
1016
|
host,
|
|
981
1017
|
port,
|
|
1018
|
+
proxy,
|
|
982
1019
|
restartDaemon,
|
|
983
1020
|
skipUpdateCheck,
|
|
984
1021
|
};
|
|
@@ -2819,7 +2856,7 @@ function readConfiguredUiAddressFromFile(home, fallbackHost, fallbackPort) {
|
|
|
2819
2856
|
}
|
|
2820
2857
|
}
|
|
2821
2858
|
|
|
2822
|
-
async function startDaemon(home, runtimePython, host, port) {
|
|
2859
|
+
async function startDaemon(home, runtimePython, host, port, proxy = null) {
|
|
2823
2860
|
const browserUrl = browserUiUrl(host, port);
|
|
2824
2861
|
const daemonBindUrl = bindUiUrl(host, port);
|
|
2825
2862
|
const state = readDaemonState(home);
|
|
@@ -2860,7 +2897,18 @@ async function startDaemon(home, runtimePython, host, port) {
|
|
|
2860
2897
|
const daemonId = crypto.randomUUID();
|
|
2861
2898
|
const child = spawn(
|
|
2862
2899
|
runtimePython,
|
|
2863
|
-
[
|
|
2900
|
+
[
|
|
2901
|
+
'-m',
|
|
2902
|
+
'deepscientist.cli',
|
|
2903
|
+
'--home',
|
|
2904
|
+
home,
|
|
2905
|
+
...(normalizeProxyUrl(proxy) ? ['--proxy', normalizeProxyUrl(proxy)] : []),
|
|
2906
|
+
'daemon',
|
|
2907
|
+
'--host',
|
|
2908
|
+
host,
|
|
2909
|
+
'--port',
|
|
2910
|
+
String(port),
|
|
2911
|
+
],
|
|
2864
2912
|
{
|
|
2865
2913
|
cwd: repoRoot,
|
|
2866
2914
|
detached: true,
|
|
@@ -2998,6 +3046,7 @@ async function updateMain(rawArgs) {
|
|
|
2998
3046
|
}
|
|
2999
3047
|
|
|
3000
3048
|
const home = options.home || resolveHome(rawArgs);
|
|
3049
|
+
applyLauncherProxy(options.proxy);
|
|
3001
3050
|
ensureDir(home);
|
|
3002
3051
|
|
|
3003
3052
|
if (options.background && options.yes && !options.worker) {
|
|
@@ -3225,6 +3274,7 @@ async function launcherMain(rawArgs) {
|
|
|
3225
3274
|
}
|
|
3226
3275
|
|
|
3227
3276
|
const home = options.home || resolveHome(rawArgs);
|
|
3277
|
+
applyLauncherProxy(options.proxy);
|
|
3228
3278
|
ensureDir(home);
|
|
3229
3279
|
|
|
3230
3280
|
if (options.stop) {
|
|
@@ -3281,7 +3331,7 @@ async function launcherMain(rawArgs) {
|
|
|
3281
3331
|
step(4, 4, 'Starting local daemon and UI surfaces');
|
|
3282
3332
|
let started;
|
|
3283
3333
|
try {
|
|
3284
|
-
started = await startDaemon(home, runtimePython, host, port);
|
|
3334
|
+
started = await startDaemon(home, runtimePython, host, port, options.proxy);
|
|
3285
3335
|
} catch (error) {
|
|
3286
3336
|
if (handleCodexPreflightFailure(error)) return true;
|
|
3287
3337
|
throw error;
|
|
@@ -3366,6 +3416,8 @@ module.exports = {
|
|
|
3366
3416
|
legacyVenvRootPath,
|
|
3367
3417
|
resolveUvBinary,
|
|
3368
3418
|
resolveHome,
|
|
3419
|
+
parseLauncherArgs,
|
|
3420
|
+
normalizeProxyUrl,
|
|
3369
3421
|
parseMigrateArgs,
|
|
3370
3422
|
useEditableProjectInstall,
|
|
3371
3423
|
compareVersions,
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -745,7 +745,7 @@ class PromptBuilder:
|
|
|
745
745
|
"- response_pattern: say what changed -> say what it means -> say what happens next",
|
|
746
746
|
"- interaction_protocol: first message may be plain conversation; after that, treat artifact.interact threads and mailbox polls as the main continuity spine across TUI, web, and connectors",
|
|
747
747
|
"- mailbox_protocol: artifact.interact(include_recent_inbound_messages=True) is the queued human-message mailbox; when it returns user text, treat that input as higher priority than background subtasks until it has been acknowledged",
|
|
748
|
-
"- acknowledgment_protocol: after artifact.interact returns any human message, immediately
|
|
748
|
+
"- acknowledgment_protocol: after artifact.interact returns any human message, immediately send one substantive artifact.interact(...) follow-up; if the active connector runtime already emitted a transport-level receipt acknowledgement, do not send a redundant receipt-only message; if answerable, answer directly, otherwise state the short plan, nearest checkpoint, and that the current background subtask is paused",
|
|
749
749
|
"- progress_protocol: emit artifact.interact(kind='progress', reply_mode='threaded', ...) at real human-meaningful checkpoints; if no natural checkpoint appears during active user-relevant work, send a concise keepalive before you drift beyond roughly 10 to 30 tool calls without a user-visible update",
|
|
750
750
|
"- smoke_then_detach_protocol: for baseline reproduction, main experiments, and analysis experiments, first validate the command path with a bounded smoke test; once the smoke test passes, launch the real long run with bash_exec(mode='detach', ...) and usually leave timeout_seconds unset rather than guessing a fake deadline",
|
|
751
751
|
"- progress_first_monitoring_protocol: when supervising a long-running bash_exec session, judge health by forward progress rather than by whether the final artifact has already appeared within a short window",
|
|
@@ -2813,8 +2813,8 @@ class QuestService:
|
|
|
2813
2813
|
),
|
|
2814
2814
|
self.localized_copy(
|
|
2815
2815
|
quest_root=quest_root,
|
|
2816
|
-
zh="-
|
|
2817
|
-
en="- Immediately
|
|
2816
|
+
zh="- 立即发送一条有实际内容的 follow-up artifact.interact(...);如果当前 connector 的运行时已经替你发过即时回执,就不要再重复发送一条只有“已收到/处理中”的确认。",
|
|
2817
|
+
en="- Immediately send one substantive follow-up artifact.interact(...); if the active connector runtime already sent the transport-level receipt acknowledgement, do not send a redundant receipt-only message such as 'received' or 'processing'.",
|
|
2818
2818
|
),
|
|
2819
2819
|
self.localized_copy(
|
|
2820
2820
|
quest_root=quest_root,
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
- connector_contract_id: qq
|
|
4
4
|
- connector_contract_scope: loaded only when QQ is the active or bound external connector for this quest
|
|
5
5
|
- connector_contract_goal: use `artifact.interact(...)` as the main durable user-visible thread on QQ instead of exposing raw internal runner or tool chatter
|
|
6
|
+
- qq_runtime_ack_rule: the QQ bridge itself emits the immediate transport-level receipt acknowledgement before the model turn starts
|
|
7
|
+
- qq_no_duplicate_ack_rule: do not waste your first model response or first `artifact.interact(...)` call on a redundant receipt-only acknowledgement such as "received", "已收到", or "I am processing" when the bridge already sent that
|
|
6
8
|
- qq_reply_style: keep QQ replies concise, milestone-first, respectful, and easy to scan on a phone
|
|
7
9
|
- qq_reply_length_rule: for ordinary QQ progress updates, normally use only 2 to 4 short sentences, or 3 short bullets at most
|
|
8
10
|
- qq_summary_first_rule: start with the conclusion the user cares about, then what it means, then the next action
|
|
@@ -16,6 +18,7 @@
|
|
|
16
18
|
- qq_default_text_rule: plain text is the default and safest QQ mode
|
|
17
19
|
- qq_absolute_path_rule: when you request native QQ image or file delivery via an attachment `path`, prefer an absolute path
|
|
18
20
|
- qq_failure_rule: if `artifact.interact(...)` returns `attachment_issues` or `delivery_results` errors, treat that as a real delivery failure and adapt before assuming the user received the media
|
|
21
|
+
- qq_first_followup_rule: after a new inbound QQ message, your first substantive follow-up should either answer directly or give the first meaningful checkpoint and next action, not a second bare acknowledgement
|
|
19
22
|
|
|
20
23
|
## QQ Runtime Capabilities
|
|
21
24
|
|
package/src/prompts/system.md
CHANGED
|
@@ -1027,7 +1027,8 @@ For `artifact.interact(...)` specifically:
|
|
|
1027
1027
|
- ordinary user-facing progress updates should read like a short collaborator message, not like a monitoring transcript, execution diary, or internal postmortem
|
|
1028
1028
|
- when `artifact.interact(...)` returns queued user requirements, treat that mailbox payload as the latest user instruction bundle
|
|
1029
1029
|
- if queued user requirements were returned, treat them as higher priority than the current background subtask until you have acknowledged them
|
|
1030
|
-
- immediately follow a non-empty mailbox poll with
|
|
1030
|
+
- immediately follow a non-empty mailbox poll with one substantive `artifact.interact(...)` follow-up update
|
|
1031
|
+
- if the active connector runtime already emitted a transport-level receipt acknowledgement before your turn, do not send a redundant receipt-only update such as "received" or "processing"
|
|
1031
1032
|
- if the request is directly answerable, answer it in that immediate follow-up update
|
|
1032
1033
|
- otherwise say the current subtask is being paused, give a short execution plan plus nearest report-back point, then complete the user request first
|
|
1033
1034
|
- after completing that interrupting user request, send another `artifact.interact(...)` update with the full result before resuming older work
|