sintbot 1.4.14 → 1.4.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.en.json +38 -0
- package/CHANGELOG.json +38 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/bin/sintbot-v2.js +87 -2
- package/docs/AI-ASSISTED-INSTALL.md +2 -2
- package/docs/install.md +5 -5
- package/docs/zh-CN/AI-ASSISTED-INSTALL.md +2 -2
- package/docs/zh-CN/install.md +5 -5
- package/install.sh +1 -1
- package/package.json +8 -3
- package/src/agent/claudecode.js +5 -2
- package/src/agent/claudemodels.js +79 -8
- package/src/agent/codexapp.js +5 -3
- package/src/agent/codexcode.js +6 -4
- package/src/agent/modelwindows.js +10 -3
- package/src/agent/runner.js +5 -0
- package/src/c2configcontrol.js +29 -18
- package/src/c2console.js +22 -6
- package/src/c2multigroupservice.js +22 -2
- package/src/config.js +18 -8
- package/src/core/brain.js +117 -0
- package/src/core/c2inputrejection.js +19 -6
- package/src/core/c2runnerinput.js +10 -5
- package/src/core/modelcmd.js +132 -0
- package/src/core/modelname.js +61 -0
- package/src/core/v2engine.js +304 -47
- package/src/groupbrain.js +135 -0
- package/src/modelcatalog.js +122 -0
- package/src/platform/feishu.js +17 -0
- package/src/platform/modelcard.js +103 -0
- package/src/ui/console.html +182 -48
- package/src/ui/serverbase.js +173 -18
- package/src/modelcontext.js +0 -13
package/CHANGELOG.en.json
CHANGED
|
@@ -1,4 +1,42 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "1.4.16",
|
|
4
|
+
"date": "2026-11-11",
|
|
5
|
+
"name": "Per-group model and thinking depth",
|
|
6
|
+
"items": [
|
|
7
|
+
{
|
|
8
|
+
"t": "Choose model and depth per group",
|
|
9
|
+
"d": "Pick the backend, model and thinking depth on each group card in the console. Model lists come live from the local Codex and Claude Code. Subscription model cards no longer carry a model or depth; a third-party endpoint keeps its model on the model card."
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"t": "Group command /model",
|
|
13
|
+
"d": "Send /model for a tap-to-pick card and confirm to apply, or send /model high, /model <model>, or /model <backend> <model> <depth>. A model or depth change on the same backend continues the conversation; a backend change starts a new one. Changes wait until the group is idle; receipts and the audit log record who made them."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"t": "Existing settings migrate automatically",
|
|
17
|
+
"d": "After the upgrade connects, each model card's model and depth move into its groups with the same effective values and no session reset, after a configuration backup. Custom Codex context windows are checked against each group's chosen model."
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"version": "1.4.15",
|
|
23
|
+
"date": "2026-11-11",
|
|
24
|
+
"name": "Simpler model settings",
|
|
25
|
+
"items": [
|
|
26
|
+
{
|
|
27
|
+
"t": "Configure the Codex context window",
|
|
28
|
+
"d": "Choose the executor default or a custom window on each model card. Validate known limits and distinguish requested settings from runtime reports."
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"t": "Keep compaction settings per model",
|
|
32
|
+
"d": "Remove global inheritance. Choose the executor default or a custom value. Existing explicit global values become independent settings on existing models; new models use the executor default."
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"t": "Streamline model configuration",
|
|
36
|
+
"d": "Group model and reasoning, context and compaction, and connection settings. Show numeric inputs only when needed and collapse capacity details. Model trials now use the model compaction setting."
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
},
|
|
2
40
|
{
|
|
3
41
|
"version": "1.4.14",
|
|
4
42
|
"date": "2026-11-11",
|
package/CHANGELOG.json
CHANGED
|
@@ -1,4 +1,42 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "1.4.16",
|
|
4
|
+
"date": "2026-11-11",
|
|
5
|
+
"name": "按群选型号与思考深度",
|
|
6
|
+
"items": [
|
|
7
|
+
{
|
|
8
|
+
"t": "型号与深度按群选",
|
|
9
|
+
"d": "控制台「群 · 项目」卡上选后端、具体型号和思考深度,型号清单取自本机 Codex / Claude Code 的实时数据;订阅登录的模型卡不再设型号和深度,第三方端点的型号仍在模型卡上。"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"t": "群命令 /model",
|
|
13
|
+
"d": "发 /model 打开点选卡,选好点「确认更新」生效;也可直接发 /model high、/model 型号、/model 后端 型号 深度。同一后端换型号或调深度接着当前对话,换后端开新对话;都要等群空闲,回执和审计记下是谁改的。"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"t": "原有设置自动迁移",
|
|
17
|
+
"d": "升级连上后,模型卡上原有的型号和深度写进各群,生效值不变、会话不重置;改前自动备份配置。Codex 自定义上下文窗口按各群所选型号的上限校验。"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"version": "1.4.15",
|
|
23
|
+
"date": "2026-11-11",
|
|
24
|
+
"name": "模型设置简化",
|
|
25
|
+
"items": [
|
|
26
|
+
{
|
|
27
|
+
"t": "独立设置 Codex 上下文窗口",
|
|
28
|
+
"d": "在模型卡片选择执行器默认或自定义窗口,保存时校验已知上限;窗口设置与实际运行回报分开展示。"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"t": "统一逐模型压缩设置",
|
|
32
|
+
"d": "取消全局继承,只保留执行器默认与自定义;旧显式全局值转为原有模型的独立设置,新模型使用执行器默认。"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"t": "精简模型配置交互",
|
|
36
|
+
"d": "型号推理、上下文压缩、连接登录分组;数值按需显示,容量说明可展开。测试入口同步采用模型的压缩设置。"
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
},
|
|
2
40
|
{
|
|
3
41
|
"version": "1.4.14",
|
|
4
42
|
"date": "2026-11-11",
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ SintBot is distributed under the [PolyForm Shield License 1.0.0](./LICENSE).
|
|
|
31
31
|
<p align="center"><img src="https://raw.githubusercontent.com/sintlab/sintbot/main/docs/assets/how-it-works.svg" alt="Group (Slack, Feishu or WeCom) → SintBot on your machine → Claude Code or Codex in the project directory → the model chosen for that group; progress and results flow back to the group" width="880"></p>
|
|
32
32
|
|
|
33
33
|
- **One group, one project.** Each group gets its own project directory, session, queue, permissions and scheduled tasks.
|
|
34
|
-
- **Same agent, any brain.**
|
|
34
|
+
- **Same agent, any brain.** Pick the backend, model and thinking depth with `/model` in the chat. A model or depth change continues the conversation; a backend change starts a new one, while project files and shared notes in `CLAUDE.md` and `AGENTS.md` carry over, so save important decisions in the project first.
|
|
35
35
|
- **Stays on your machine.** Projects, sessions, memory and logs live on your disk and never pass through a SintBot server. Prompts and the code the model reads still go to the provider you chose.
|
|
36
36
|
- **No public IP.** Every platform uses an outbound long connection (Socket Mode on Slack). One daemon can serve Slack, Feishu and WeCom at the same time.
|
|
37
37
|
|
package/README.zh-CN.md
CHANGED
|
@@ -31,7 +31,7 @@ SintBot 按 [PolyForm Shield 1.0.0 许可](./LICENSE) 分发。
|
|
|
31
31
|
<p align="center"><img src="https://raw.githubusercontent.com/sintlab/sintbot/main/docs/assets/how-it-works-zh.svg" alt="群(飞书、Slack 或企业微信)→ 你机器上的 SintBot → 项目目录里的 Claude Code 或 Codex → 这个群选的模型;进度与结果回到群里" width="880"></p>
|
|
32
32
|
|
|
33
33
|
- **一群一项目。** 每个群各有自己的项目目录、会话、排队、权限和定时任务。
|
|
34
|
-
- **同一套 agent,任意大脑。** 群里发 `/model`
|
|
34
|
+
- **同一套 agent,任意大脑。** 群里发 `/model` 点选后端、型号和思考深度。换型号或调深度接着当前对话;换后端会开新对话,项目文件及 `CLAUDE.md`、`AGENTS.md` 中的共享记录可以延续,重要决定先写进项目。
|
|
35
35
|
- **数据留在本机。** 项目、会话、记忆、日志都在你自己的硬盘上,不经过任何 SintBot 服务器。提示词和模型读到的代码照常发给你选的模型服务商。
|
|
36
36
|
- **不要公网 IP。** 每个平台都是机器主动往外连的长连接(Slack 用 Socket Mode)。一个守护进程可以同时接飞书、Slack、企业微信。
|
|
37
37
|
|
package/bin/sintbot-v2.js
CHANGED
|
@@ -48,6 +48,50 @@ async function startService(rest) {
|
|
|
48
48
|
return startMultiGroupService({ installation, preflightOnly, uiEnabled, uiHost, uiPort })
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
// Model and depth used to live on model cards; they now belong to each group.
|
|
52
|
+
// Once every group is connected (a console upgrade can no longer roll back
|
|
53
|
+
// onto this configuration), copy each card's value into the groups that have
|
|
54
|
+
// not chosen and remove it from the card, in one validated write. Effective
|
|
55
|
+
// values do not change, so no session resets. The file is backed up first;
|
|
56
|
+
// a group still running is retried later.
|
|
57
|
+
function scheduleCardDefaultsMigration({ service, configControl, updater, cfg, stateDir, log,
|
|
58
|
+
delayMs = 15_000, tries = 40 } = {}) {
|
|
59
|
+
const fs = require('node:fs')
|
|
60
|
+
const { fillGroupPicks, stripCardDefaults } = require('../src/groupbrain')
|
|
61
|
+
const pending = () => {
|
|
62
|
+
const probe = JSON.parse(fs.readFileSync(cfg._path, 'utf8'))
|
|
63
|
+
const filled = fillGroupPicks(probe).changed
|
|
64
|
+
return stripCardDefaults(probe).changed || filled
|
|
65
|
+
}
|
|
66
|
+
try { if (!pending()) return } catch (error) { log(`card defaults migration skipped: ${error?.message || String(error)}`); return }
|
|
67
|
+
let left = tries
|
|
68
|
+
const attempt = () => {
|
|
69
|
+
left -= 1
|
|
70
|
+
try {
|
|
71
|
+
if (!service.serving || service.held.size || service.inMaintenance ||
|
|
72
|
+
updater?.status?.()?.job?.state === 'switching') throw new Error('service not settled')
|
|
73
|
+
if (!pending()) return
|
|
74
|
+
const dir = path.join(stateDir, 'config-backups')
|
|
75
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 })
|
|
76
|
+
const backup = path.join(dir, `${new Date().toISOString().replace(/[:.]/g, '-')}-group-model.json`)
|
|
77
|
+
fs.copyFileSync(cfg._path, backup)
|
|
78
|
+
fs.chmodSync(backup, 0o600)
|
|
79
|
+
let summary = null
|
|
80
|
+
configControl.writeConfig(raw => {
|
|
81
|
+
const filled = fillGroupPicks(raw)
|
|
82
|
+
const stripped = stripCardDefaults(raw)
|
|
83
|
+
summary = { groups: filled.groups.length, providers: stripped.providers }
|
|
84
|
+
})
|
|
85
|
+
log(`model and depth moved from model cards into groups: ${summary.groups} group(s), cards ${summary.providers.join(', ') || '-'}; backup ${path.basename(backup)}`)
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (left > 0) { const t = setTimeout(attempt, delayMs * 4); t.unref?.() }
|
|
88
|
+
else log(`card defaults migration not applied: ${error?.message || String(error)}`)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const timer = setTimeout(attempt, delayMs)
|
|
92
|
+
timer.unref?.()
|
|
93
|
+
}
|
|
94
|
+
|
|
51
95
|
async function startMultiGroupService({ installation, preflightOnly, uiEnabled = false, uiHost, uiPort }) {
|
|
52
96
|
const { stateDir, cfg, routes } = installation
|
|
53
97
|
const { assertPrivateDirectory, assertJournalScope } = require('../src/c2install')
|
|
@@ -72,6 +116,21 @@ async function startMultiGroupService({ installation, preflightOnly, uiEnabled =
|
|
|
72
116
|
const issues = require('../src/core/issues').createIssues(path.join(stateDir, 'issues.jsonl'),
|
|
73
117
|
{ withFileLock: require('../src/core/store').withFileLock,
|
|
74
118
|
log: message => process.stderr.write(`C2 multi-group · ${message}\n`) })
|
|
119
|
+
// Backend · model · depth: one live model list per process (also used by the
|
|
120
|
+
// console), and every effective change is audited.
|
|
121
|
+
const modelCatalog = require('../src/modelcatalog').createModelCatalog({
|
|
122
|
+
codexstatus: require('../src/agent/codexstatus'), claudemodels: require('../src/agent/claudemodels'),
|
|
123
|
+
claudeBin: cfg.agent?.bin || 'claude', cacheFile: path.join(stateDir, 'model-lists.json'),
|
|
124
|
+
log: message => process.stderr.write(`C2 multi-group · ${message}\n`) })
|
|
125
|
+
const groupBrainMod = require('../src/groupbrain')
|
|
126
|
+
const { buildModelCard } = require('../src/platform/modelcard')
|
|
127
|
+
const audit = require('../src/core/ledger').createLedger(path.join(stateDir, 'audit.jsonl'),
|
|
128
|
+
message => process.stderr.write(`C2 multi-group · ${message}\n`))
|
|
129
|
+
const setGroupBrain = (chatId, change, { by = '', source = 'console' } = {}) => {
|
|
130
|
+
const result = configControl.setGroupBrain(chatId, change)
|
|
131
|
+
if (result?.changed) audit.append(groupBrainMod.auditRow(chatId, result, { source, by }))
|
|
132
|
+
return result
|
|
133
|
+
}
|
|
75
134
|
const runner = require('../src/agent/runner')
|
|
76
135
|
const validators = { validateState: validatePersistentState,
|
|
77
136
|
validateReceipts: validatePersistentReceipts, validateRelation: validatePersistentRelation }
|
|
@@ -173,6 +232,18 @@ async function startMultiGroupService({ installation, preflightOnly, uiEnabled =
|
|
|
173
232
|
// Faults in a shared resource stop every group; others hold one group.
|
|
174
233
|
sharedFault: () => capacity.snapshot().closed ? 'model concurrency closed' : null,
|
|
175
234
|
log: message => process.stderr.write(`C2 multi-group · ${message}\n`),
|
|
235
|
+
// /model card taps: the engine decides, the reply replaces the card.
|
|
236
|
+
onModelCard: async (event, binding) => {
|
|
237
|
+
let who = event.operator?.name || ''
|
|
238
|
+
const adapter = liveTransport?.platform.forChat(event.chatId)
|
|
239
|
+
if (!who && adapter?.nameOf && event.operator?.openId) { try { who = (await adapter.nameOf(event.operator.openId)) || '' } catch {} }
|
|
240
|
+
const out = await binding.engine.handleModelCard({ chatId: event.chatId, who,
|
|
241
|
+
value: event.action.value || {}, option: event.action.option })
|
|
242
|
+
const resp = {}
|
|
243
|
+
if (out?.toast) resp.toast = out.toast
|
|
244
|
+
if (out?.state) resp.card = { type: 'raw', data: buildModelCard(out.state) }
|
|
245
|
+
return Object.keys(resp).length ? resp : undefined
|
|
246
|
+
},
|
|
176
247
|
onFatal: (error, result) => {
|
|
177
248
|
process.stderr.write(`C2 multi-group failure: ${error?.message || String(error)}\n`)
|
|
178
249
|
finish(result)
|
|
@@ -250,8 +321,14 @@ async function startMultiGroupService({ installation, preflightOnly, uiEnabled =
|
|
|
250
321
|
},
|
|
251
322
|
durableV2SwitchProvider: (chatId, name) => {
|
|
252
323
|
if (chatId !== route.chatId) throw new Error('Model switch targets another group')
|
|
253
|
-
|
|
324
|
+
setGroupBrain(chatId, { provider: name }, { source: 'command' })
|
|
325
|
+
},
|
|
326
|
+
durableV2SetBrain: (chatId, change, meta) => {
|
|
327
|
+
if (chatId !== route.chatId) throw new Error('Model change targets another group')
|
|
328
|
+
return setGroupBrain(chatId, change, meta)
|
|
254
329
|
},
|
|
330
|
+
durableV2ModelCatalog: (name, provider) => modelCatalog(name, provider),
|
|
331
|
+
durableV2ModelCard: state => buildModelCard(state),
|
|
255
332
|
durableV2UsageProbe: usageProbe, durableV2Issues: issues,
|
|
256
333
|
durableV2Version: require('../package.json').version,
|
|
257
334
|
durableV2DataDir: stateDir,
|
|
@@ -301,7 +378,7 @@ async function startMultiGroupService({ installation, preflightOnly, uiEnabled =
|
|
|
301
378
|
getPlatform: chatId => liveTransport?.platform.forChat(chatId) || null,
|
|
302
379
|
routeMessage: (msg, adapter) => service.routeMessage(msg, adapter),
|
|
303
380
|
isServing: () => service.serving, isFrozen: () => service.inMaintenance,
|
|
304
|
-
assertWriterLock, ...configControl, updater,
|
|
381
|
+
assertWriterLock, ...configControl, setGroupBrain, modelCatalog, updater,
|
|
305
382
|
log: message => process.stderr.write(`Console · ${message}\n`) })
|
|
306
383
|
await new Promise((resolve, reject) => {
|
|
307
384
|
ui.server.once('error', reject)
|
|
@@ -337,6 +414,14 @@ async function startMultiGroupService({ installation, preflightOnly, uiEnabled =
|
|
|
337
414
|
// group is connected; then the daily version check starts.
|
|
338
415
|
if (!started.held?.length) updater?.confirmStarted()
|
|
339
416
|
void updater?.start()
|
|
417
|
+
// Model lists are probed once in the background shortly after startup, so
|
|
418
|
+
// a later /model or console group card does not wait for a CLI handshake.
|
|
419
|
+
const runners = new Set(Object.values(cfg.providers || {}).filter(p => p && !p.hidden)
|
|
420
|
+
.map(p => p.runner === 'codex' ? 'codex' : !p.baseUrl ? 'claude' : '').filter(Boolean))
|
|
421
|
+
const warm = setTimeout(() => { void modelCatalog.warm([...runners]).catch(() => {}) }, 30_000)
|
|
422
|
+
warm.unref?.()
|
|
423
|
+
scheduleCardDefaultsMigration({ service, configControl, updater, cfg, stateDir,
|
|
424
|
+
log: message => process.stderr.write(`SintBot · ${message}\n`) })
|
|
340
425
|
} catch (error) {
|
|
341
426
|
ui?.close()
|
|
342
427
|
const stopped = await service?.stop('startup-failed', 1)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Install SintBot 1.4.
|
|
1
|
+
# Install SintBot 1.4.16 with an AI assistant
|
|
2
2
|
|
|
3
3
|
English · [简体中文](./zh-CN/AI-ASSISTED-INSTALL.md)
|
|
4
4
|
|
|
@@ -15,5 +15,5 @@ Use the terminal on the Mac that will run SintBot. A cloud shell cannot install
|
|
|
15
15
|
## Copyable request
|
|
16
16
|
|
|
17
17
|
```text
|
|
18
|
-
Install SintBot 1.4.
|
|
18
|
+
Install SintBot 1.4.16 on this Mac using the one-line installation guide. Preserve existing services, configuration, state and Codex or Claude Code account data. Do not expose credentials. Verify the actual installed executable and binary core, configure the Feishu groups with their runners, and initialize only clean new state. Run the foreground preflight and an authorized read-only group test, check the result, then verify clean stop/restart. Stop temporary processes when finished, and distinguish package installation from successful group acceptance.
|
|
19
19
|
```
|
package/docs/install.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Install SintBot 1.4.
|
|
1
|
+
# Install SintBot 1.4.16
|
|
2
2
|
|
|
3
3
|
English · [简体中文](./zh-CN/install.md)
|
|
4
4
|
|
|
@@ -10,13 +10,13 @@ You need a Mac, Node.js 22 or newer, and projects stored on that Mac. Install an
|
|
|
10
10
|
curl -fsSL https://sintbot.com/install.sh | bash
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
The installer checks the environment, installs `sintbot@1.4.
|
|
13
|
+
The installer checks the environment, installs `sintbot@1.4.16` into `~/.local/share/sintbot`, and verifies the entry, built-in core and Feishu module. Continue when you see `Installed and verified SintBot 1.4.16`. No sudo is needed.
|
|
14
14
|
|
|
15
15
|
To preview, append `-s -- --dry-run`. To choose another location, set `SINTBOT_INSTALL_DIR` and adjust the executable paths below.
|
|
16
16
|
|
|
17
17
|
## 2. Connect Feishu
|
|
18
18
|
|
|
19
|
-
In the [Feishu Open Platform](https://open.feishu.cn/), create a custom app, enable the bot and the permissions to receive and reply to messages, choose long connection for event subscription and subscribe to message receipt. Publish the app, add the bot to the groups you will use, and copy each group's `oc_…` chat ID.
|
|
19
|
+
In the [Feishu Open Platform](https://open.feishu.cn/), create a custom app, enable the bot and the permissions to receive and reply to messages, choose long connection for event subscription and subscribe to message receipt. For callbacks, also choose long connection and subscribe to card action callbacks (the tap-to-pick `/model` card needs them). Publish the app, add the bot to the groups you will use, and copy each group's `oc_…` chat ID.
|
|
20
20
|
|
|
21
21
|
Enter the app credentials in the default macOS terminal (the secret is not echoed):
|
|
22
22
|
|
|
@@ -94,7 +94,7 @@ Any text message is a task. Replies arrive in a card that appears once the model
|
|
|
94
94
|
| `/skip` | Cancel only the current task; queued messages run next and the conversation is kept |
|
|
95
95
|
| `/stop` | Stop the current task and cancel queued ones; the conversation is kept |
|
|
96
96
|
| `/new` | Start a new conversation, forgetting the previous one (only while the group is idle) |
|
|
97
|
-
| `/model` |
|
|
97
|
+
| `/model` | Opens a tap-to-pick card: choose the backend, model and thinking depth, then confirm. You can also send `/model high`, `/model <model>` or `/model <backend>`. A model or depth change continues the conversation; a backend change starts a new one. Only while the group is idle |
|
|
98
98
|
| `/hud` | Quota of the current model and usage of this conversation |
|
|
99
99
|
| `/issue text` | Log feedback; review and close it in the console overview |
|
|
100
100
|
| `/help` | List commands, with this group's project and model |
|
|
@@ -107,7 +107,7 @@ When the reply card moves below a confirmed follow-up, its existing text is show
|
|
|
107
107
|
|
|
108
108
|
## 6. Console
|
|
109
109
|
|
|
110
|
-
Model
|
|
110
|
+
Model and thinking depth are chosen per group: on the group card, the Model row picks the backend and model and the next row picks the depth, from lists read live from the local Codex and Claude Code. Subscription model cards no longer carry a model or depth; a third-party endpoint keeps its model on the model card. After the upgrade, each model card's existing model and depth move into its groups with the same effective values and no session reset, after a configuration backup. Model cards distinguish requested settings from runtime reports; expand Capacity and runtime details for reference limits. Codex context and per-model compaction each offer Executor default or Custom. Numeric inputs appear only for Custom. Changes apply next turn without resetting the session; known catalog context limits are enforced. Compaction means a threshold for Codex and a window for Claude Code, and may start earlier. Explicit legacy global compaction values are converted into independent settings for existing models, preserving per-model overrides. New models use executor defaults. Reading does not rewrite the file; the converted settings are persisted on the next normal configuration save.
|
|
111
111
|
|
|
112
112
|
The overview combines group projects and concurrency. Each row shows the runner, state and elapsed time, project folder, model and configured reasoning effort, and the latest reported context usage. Select a group to view its tasks, then expand a task for tools, reply and usage. Context is a session sample, not cumulative token consumption; unknown capacity and older samples are labeled. Row icons provide console messages, stop and new-session controls according to the current group state.
|
|
113
113
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# 用 AI 助手安装 SintBot 1.4.
|
|
1
|
+
# 用 AI 助手安装 SintBot 1.4.16
|
|
2
2
|
|
|
3
3
|
[English](../AI-ASSISTED-INSTALL.md) · 简体中文
|
|
4
4
|
|
|
@@ -15,5 +15,5 @@
|
|
|
15
15
|
## 可复制的请求
|
|
16
16
|
|
|
17
17
|
```text
|
|
18
|
-
按一行安装指南,在这台 Mac 安装 SintBot 1.4.
|
|
18
|
+
按一行安装指南,在这台 Mac 安装 SintBot 1.4.16。保留已有服务、配置、状态和 Codex 或 Claude Code 账号数据,不输出凭据。验证实际入口与二进制核心,配置飞书群及各群的执行器,只对全新状态初始化。运行前台预检和已授权的只读群测试,对照结果,再验证正常停止与重启。完成后停止临时进程,并区分程序包安装与群验收结果。
|
|
19
19
|
```
|
package/docs/zh-CN/install.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# 安装 SintBot 1.4.
|
|
1
|
+
# 安装 SintBot 1.4.16
|
|
2
2
|
|
|
3
3
|
[English](../install.md) · 简体中文
|
|
4
4
|
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
curl -fsSL https://sintbot.com/install.sh | bash
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
安装器会自动检查环境,把 `sintbot@1.4.
|
|
13
|
+
安装器会自动检查环境,把 `sintbot@1.4.16` 装到 `~/.local/share/sintbot`,并验证程序入口、内置核心和飞书模块。看到 `Installed and verified SintBot 1.4.16` 就可以继续。不需要 sudo。
|
|
14
14
|
|
|
15
15
|
可选预览:在命令末尾加 `-s -- --dry-run`。要自定义安装位置,设置 `SINTBOT_INSTALL_DIR` 环境变量,并同步修改后面的可执行文件路径。
|
|
16
16
|
|
|
17
17
|
## 2. 连接飞书
|
|
18
18
|
|
|
19
|
-
在 [飞书开放平台](https://open.feishu.cn/)
|
|
19
|
+
在 [飞书开放平台](https://open.feishu.cn/) 创建企业自建应用,启用机器人、接收和回复消息的权限,事件订阅选择长连接并订阅消息接收;回调订阅同样选择长连接接收回调并订阅卡片回传交互(群里点选 `/model` 卡片要用)。发布应用后,把机器人加入要使用的群,取得每个群的 `oc_…` 群 ID。
|
|
20
20
|
|
|
21
21
|
在 macOS 默认终端中输入应用凭据(密钥输入时不显示):
|
|
22
22
|
|
|
@@ -94,7 +94,7 @@ SINTBOT="$HOME/.local/share/sintbot/bin/sintbot"
|
|
|
94
94
|
| `/skip` | 只放弃当前任务,排队的接着处理,之前的对话保留 |
|
|
95
95
|
| `/stop` | 停止当前任务,连同排队的一起取消;之前的对话保留 |
|
|
96
96
|
| `/new` | 忘掉之前的对话,开始新的一段(只在本群空闲时生效) |
|
|
97
|
-
| `/model` |
|
|
97
|
+
| `/model` | 打开点选卡,选后端、型号和思考深度后点「确认更新」;也可直接发 `/model high`、`/model 型号`、`/model 后端`。换型号或调深度接着当前对话,换后端开始新的一段对话;只在本群空闲时生效 |
|
|
98
98
|
| `/hud` | 当前模型的额度与本段对话的用量 |
|
|
99
99
|
| `/issue 一句话` | 记下问题反馈,在控制台概览里查看和标记处理 |
|
|
100
100
|
| `/help` | 查看可用命令,以及本群的项目和模型 |
|
|
@@ -107,7 +107,7 @@ Codex 和 Claude Code 的忙时跟进默认开启;`steer:false` 改为排队
|
|
|
107
107
|
|
|
108
108
|
## 6. 控制台
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
型号与思考深度按群选:在「群 · 项目」卡的「模型」行选后端和型号,下一行选深度,清单取自本机 Codex / Claude Code 的实时数据;订阅登录的模型卡上不再设型号和深度,第三方端点的型号仍在模型卡上设置。升级后,模型卡上原有的型号和深度会自动写进各群,生效值不变、会话不重置,改前备份配置。模型卡区分设置值与运行回报,容量依据可在「容量与运行信息」中查看。Codex 上下文窗口和各模型自动压缩都只提供「执行器默认 / 自定义」,仅自定义时显示数值输入;从下轮生效,保留当前会话。Codex 自定义窗口不能超过已知目录上限。压缩值对 Codex 表示阈值,对 Claude Code 表示窗口,实际可能提前触发。旧配置中的全局压缩值会转换为原有各模型的独立值,已有逐模型设置优先;新模型默认交给执行器,不继续继承旧全局值。读取不改磁盘,下一次正常保存配置时写入转换结果。
|
|
111
111
|
|
|
112
112
|
概览合并群项目与并发信息,每行显示执行器、状态与耗时、项目目录、模型和配置的思考深度、最近上报的上下文用量。选择群后查看对应任务,展开任务查看工具、回复和用量。上下文是当前会话采样,不是累计消耗;容量未知或历史采样会明确标注。群行图标按当前状态提供插话、停止与新会话操作。
|
|
113
113
|
|
package/install.sh
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# The function is parsed in full before installation starts, including when piped.
|
|
3
3
|
sintbot_install() (
|
|
4
4
|
set -eu
|
|
5
|
-
version=1.4.
|
|
5
|
+
version=1.4.16
|
|
6
6
|
prefix="${SINTBOT_INSTALL_DIR:-$HOME/.local/share/sintbot}"
|
|
7
7
|
fail() { printf 'SintBot: %s\n' "$*" >&2; exit 1; }
|
|
8
8
|
case "${1:-}" in
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sintbot",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.16",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Drive your Mac projects from Feishu groups with Codex or Claude Code, using a built-in agent core.",
|
|
6
6
|
"bin": {
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"src/agent/codexapp.js",
|
|
52
52
|
"src/agent/codexcode.js",
|
|
53
53
|
"src/agent/codexstatus.js",
|
|
54
|
-
"src/agent/modelwindows.js",
|
|
55
54
|
"src/agent/errtag.js",
|
|
55
|
+
"src/agent/modelwindows.js",
|
|
56
56
|
"src/agent/privatecore.js",
|
|
57
57
|
"src/agent/privatecore.wasm",
|
|
58
58
|
"src/agent/runner.js",
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
"src/config.js",
|
|
72
72
|
"src/consolesay.js",
|
|
73
73
|
"src/consoletrials.js",
|
|
74
|
+
"src/core/brain.js",
|
|
74
75
|
"src/core/c2cardeffectbridge.js",
|
|
75
76
|
"src/core/c2cardterminalpath.js",
|
|
76
77
|
"src/core/c2durableingress.js",
|
|
@@ -104,6 +105,8 @@
|
|
|
104
105
|
"src/core/issues.js",
|
|
105
106
|
"src/core/ledger.js",
|
|
106
107
|
"src/core/mdtable.js",
|
|
108
|
+
"src/core/modelcmd.js",
|
|
109
|
+
"src/core/modelname.js",
|
|
107
110
|
"src/core/proxy.js",
|
|
108
111
|
"src/core/redact.js",
|
|
109
112
|
"src/core/store.js",
|
|
@@ -114,10 +117,12 @@
|
|
|
114
117
|
"src/design.js",
|
|
115
118
|
"src/feishucli.js",
|
|
116
119
|
"src/gitrepo.js",
|
|
120
|
+
"src/groupbrain.js",
|
|
117
121
|
"src/leftover.js",
|
|
118
|
-
"src/
|
|
122
|
+
"src/modelcatalog.js",
|
|
119
123
|
"src/platform/board.js",
|
|
120
124
|
"src/platform/feishu.js",
|
|
125
|
+
"src/platform/modelcard.js",
|
|
121
126
|
"src/platform/multi.js",
|
|
122
127
|
"src/platform/noticecard.js",
|
|
123
128
|
"src/platform/noticesend.js",
|
package/src/agent/claudecode.js
CHANGED
|
@@ -55,7 +55,7 @@ function durableToolResults(ev) {
|
|
|
55
55
|
// replaces provider.baseUrl for ANTHROPIC_BASE_URL — used to point `claude` at
|
|
56
56
|
// the local vision strip-proxy for text-only providers. The auth
|
|
57
57
|
// token is unchanged: the proxy forwards it verbatim to the real upstream.
|
|
58
|
-
function buildEnv(provider = {}, baseUrlOverride, extraEnv) {
|
|
58
|
+
function buildEnv(provider = {}, baseUrlOverride, extraEnv, effort = '') {
|
|
59
59
|
const env = { ...process.env }
|
|
60
60
|
|
|
61
61
|
// The spawn env is defined ENTIRELY by the provider:
|
|
@@ -83,6 +83,8 @@ function buildEnv(provider = {}, baseUrlOverride, extraEnv) {
|
|
|
83
83
|
delete env.CLAUDECODE
|
|
84
84
|
|
|
85
85
|
if (provider.env) Object.assign(env, provider.env)
|
|
86
|
+
// The group's own thinking depth wins over a card-level value.
|
|
87
|
+
if (effort) env.CLAUDE_CODE_EFFORT_LEVEL = effort
|
|
86
88
|
// Engine-injected non-provider context (e.g. SBOT_CHAT_ID for the feishu base skill).
|
|
87
89
|
// Applied last but namespaced (SBOT_*), so it never collides with provider ANTHROPIC_* env.
|
|
88
90
|
if (extraEnv) Object.assign(env, extraEnv)
|
|
@@ -137,6 +139,7 @@ function runTurn({
|
|
|
137
139
|
prompt,
|
|
138
140
|
provider = {},
|
|
139
141
|
model,
|
|
142
|
+
effort = '',
|
|
140
143
|
workdir,
|
|
141
144
|
sessionId,
|
|
142
145
|
permissionMode,
|
|
@@ -238,7 +241,7 @@ function runTurn({
|
|
|
238
241
|
const { file, spawnArgs, fallback } = resolveSpawn(bin, args)
|
|
239
242
|
if (fallback) log(`配置的 agent 路径 '${bin}' 不存在,本轮改用 ${fallback} —— 请把配置改回正确路径`) // 兜底只救急,配置仍是错的
|
|
240
243
|
const child = spawnFn(file, spawnArgs, {
|
|
241
|
-
env: buildEnv(provider, baseUrlOverride, extraEnv),
|
|
244
|
+
env: buildEnv(provider, baseUrlOverride, extraEnv, effort),
|
|
242
245
|
cwd: workdir || process.cwd(),
|
|
243
246
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
244
247
|
windowsHide: true,
|
|
@@ -17,15 +17,16 @@ const fs = require('node:fs')
|
|
|
17
17
|
const os = require('node:os')
|
|
18
18
|
const path = require('node:path')
|
|
19
19
|
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
20
|
+
// Fallback candidates, used only when the live list from the CLI handshake
|
|
21
|
+
// has never been obtained. Names follow core/modelname.js display names.
|
|
22
|
+
// Ids carry no date suffix and use hyphens for versions (claude-opus-5-5).
|
|
23
23
|
const BUILTIN = [
|
|
24
24
|
{ id: '', name: '默认(跟随 Claude Code)', note: '推荐 · 不指定型号' },
|
|
25
|
-
{ id: 'claude-opus-5', name: '
|
|
26
|
-
{ id: 'claude-opus-
|
|
27
|
-
{ id: 'claude-sonnet-5', name: '
|
|
28
|
-
{ id: 'claude-haiku-
|
|
25
|
+
{ id: 'claude-opus-5-5', name: 'Opus 5.5', note: '最新 Opus · 复杂改码 / 长链路' },
|
|
26
|
+
{ id: 'claude-opus-5', name: 'Opus 5', note: '上一代 Opus' },
|
|
27
|
+
{ id: 'claude-sonnet-5-5', name: 'Sonnet 5.5', note: '速度与智能平衡' },
|
|
28
|
+
{ id: 'claude-haiku-5-5', name: 'Haiku 5.5', note: '最快最省 · 简单任务' },
|
|
29
|
+
{ id: 'claude-opus-4-8', name: 'Opus 4.8', note: '更早一代 Opus · 额度池独立' },
|
|
29
30
|
]
|
|
30
31
|
|
|
31
32
|
// CLI 登录后写在 ~/.claude.json 的账号额外可选模型(本机这台是 Fable 5)。
|
|
@@ -57,4 +58,74 @@ function listModels({ loggedIn, home } = {}) {
|
|
|
57
58
|
return { loggedIn: true, models: mergeModels(extra), source: extra.length ? 'account' : 'builtin' }
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
|
|
61
|
+
// Live list: Claude Code's own initialize handshake. Start
|
|
62
|
+
// `claude -p --input-format stream-json --output-format stream-json`, send one
|
|
63
|
+
// control_request { subtype: 'initialize' }; the response's models are the
|
|
64
|
+
// CLI's /model menu for this machine and account: official display names,
|
|
65
|
+
// descriptions and the depths each model supports. No question is asked and
|
|
66
|
+
// no turn starts, so no quota is used, and the login token is never read.
|
|
67
|
+
// Only for subscription cards: endpoint/model variables are removed first so a
|
|
68
|
+
// third-party endpoint exported in the service shell cannot redirect it.
|
|
69
|
+
// Unavailable (signed out, CLI too old, timeout) ⇒ null; callers then use the
|
|
70
|
+
// last saved list or the built-in candidates.
|
|
71
|
+
function parseInitModels(models) {
|
|
72
|
+
if (!Array.isArray(models)) return null
|
|
73
|
+
const out = [], aliasOf = {}
|
|
74
|
+
let recommended = ''
|
|
75
|
+
const seen = new Set()
|
|
76
|
+
for (const m of models) {
|
|
77
|
+
if (!m || typeof m.value !== 'string' || !m.value) continue
|
|
78
|
+
const id = typeof m.resolvedModel === 'string' && m.resolvedModel ? m.resolvedModel : m.value
|
|
79
|
+
if (m.value === 'default') { recommended = id; continue }
|
|
80
|
+
// An alias (opus → claude-opus-5-5) is stored as the concrete model, so a
|
|
81
|
+
// later CLI upgrade does not silently move the group to another generation.
|
|
82
|
+
if (m.value !== id) aliasOf[m.value] = id
|
|
83
|
+
if (seen.has(id)) continue
|
|
84
|
+
seen.add(id)
|
|
85
|
+
const efforts = Array.isArray(m.supportedEffortLevels) ? m.supportedEffortLevels.filter((e) => typeof e === 'string') : []
|
|
86
|
+
out.push({ id, name: String(m.displayName || '').trim() || id, note: String(m.description || '').slice(0, 60), efforts, effort: m.supportsEffort !== false })
|
|
87
|
+
}
|
|
88
|
+
if (!out.length) return null
|
|
89
|
+
return { models: out, aliasOf, recommended: recommended || out[0].id }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function probeModels(bin = 'claude', { timeoutMs = 20000, cwd = os.tmpdir(), spawnFn = null } = {}) {
|
|
93
|
+
return new Promise((resolve) => {
|
|
94
|
+
const spawn = spawnFn || require('node:child_process').spawn
|
|
95
|
+
const { resolveSpawn } = require('./winexec')
|
|
96
|
+
const env = { ...process.env }
|
|
97
|
+
delete env.ANTHROPIC_BASE_URL; delete env.ANTHROPIC_AUTH_TOKEN; delete env.ANTHROPIC_MODEL; delete env.CLAUDECODE
|
|
98
|
+
let child, buf = '', settled = false, timer = null
|
|
99
|
+
const done = (v) => {
|
|
100
|
+
if (settled) return
|
|
101
|
+
settled = true
|
|
102
|
+
clearTimeout(timer)
|
|
103
|
+
try { child.stdin.end() } catch {}
|
|
104
|
+
try { child.kill() } catch {}
|
|
105
|
+
resolve(v)
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const { file, spawnArgs } = resolveSpawn(bin, ['-p', '--input-format', 'stream-json', '--output-format', 'stream-json', '--verbose'])
|
|
109
|
+
child = spawn(file, spawnArgs, { cwd, env, stdio: ['pipe', 'pipe', 'ignore'], windowsHide: true })
|
|
110
|
+
} catch { return resolve(null) }
|
|
111
|
+
timer = setTimeout(() => done(null), timeoutMs)
|
|
112
|
+
if (timer.unref) timer.unref()
|
|
113
|
+
child.on('error', () => done(null))
|
|
114
|
+
child.on('close', () => done(null))
|
|
115
|
+
child.stdout.on('data', (d) => {
|
|
116
|
+
buf += d.toString('utf8')
|
|
117
|
+
let i
|
|
118
|
+
while ((i = buf.indexOf('\n')) >= 0) {
|
|
119
|
+
const line = buf.slice(0, i); buf = buf.slice(i + 1)
|
|
120
|
+
let o; try { o = JSON.parse(line) } catch { continue }
|
|
121
|
+
if (o && o.type === 'control_response') {
|
|
122
|
+
const r = o.response && (o.response.response || o.response)
|
|
123
|
+
return done(parseInitModels(r && r.models))
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
try { child.stdin.write(JSON.stringify({ type: 'control_request', request_id: 'sintbot-models', request: { subtype: 'initialize' } }) + '\n') } catch { done(null) }
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
module.exports = { listModels, mergeModels, parseExtraModels, readExtraModels, BUILTIN, parseInitModels, probeModels }
|
package/src/agent/codexapp.js
CHANGED
|
@@ -64,7 +64,7 @@ function itemEvent(method, item) {
|
|
|
64
64
|
// App Server 的请求和通知共用一条 stdout 流;单轮独立进程使关闭成为续接边界。
|
|
65
65
|
function runTurn(opts = {}) {
|
|
66
66
|
const {
|
|
67
|
-
bin = 'codex', prompt, provider = {}, model, workdir, sessionId,
|
|
67
|
+
bin = 'codex', prompt, provider = {}, model, effort: wantEffort, workdir, sessionId,
|
|
68
68
|
permissionMode, autocompactTokens, appendSystemPrompt, codexApp, codexProjectDocFallback, extraEnv,
|
|
69
69
|
onEvent, onDurableEvent, onSteerReady, onSteerLanded, onTurnAccepted, onSpawnFailed, signal, log = () => {},
|
|
70
70
|
softStopGraceMs = 10000, spawnFn = spawn,
|
|
@@ -78,6 +78,7 @@ function runTurn(opts = {}) {
|
|
|
78
78
|
delete env.CLAUDECODE
|
|
79
79
|
delete env.OPENAI_API_KEY // 只使用机器现有的 ChatGPT 订阅登录态,与 codex exec 一致。
|
|
80
80
|
const args = ['app-server', '-c', 'approval_policy=never']
|
|
81
|
+
if (Number.isSafeInteger(provider.contextWindowTokens) && provider.contextWindowTokens > 0) args.push('-c', `model_context_window=${provider.contextWindowTokens}`)
|
|
81
82
|
if (autocompactTokens > 0) args.push('-c', `model_auto_compact_token_limit=${autocompactTokens}`)
|
|
82
83
|
const { file, spawnArgs, fallback } = resolveSpawn(bin, args)
|
|
83
84
|
if (fallback) log(`配置的 agent 路径 '${bin}' 不存在,本轮改用 ${fallback}`)
|
|
@@ -101,7 +102,8 @@ function runTurn(opts = {}) {
|
|
|
101
102
|
let childCloseObserved = false
|
|
102
103
|
let settled = false
|
|
103
104
|
let completed = false
|
|
104
|
-
|
|
105
|
+
const configuredEffort = wantEffort || provider.reasoningEffort || ''
|
|
106
|
+
let effort = configuredEffort
|
|
105
107
|
let retriedEffort = false
|
|
106
108
|
let pendingTurnStart = false
|
|
107
109
|
let acceptedTurnId = null
|
|
@@ -349,7 +351,7 @@ function runTurn(opts = {}) {
|
|
|
349
351
|
else if (action === 3) { aborted = true; lastResult = null }
|
|
350
352
|
else lastResult = { type: 'result', subtype: 'success', text: lastAgentText, usage: totalUsage, usageCumulative: !!totalUsage,
|
|
351
353
|
contextTokens, contextWindowTokens, contextCompacted }
|
|
352
|
-
if (lastResult && retriedEffort) lastResult.effortFellBack =
|
|
354
|
+
if (lastResult && retriedEffort) lastResult.effortFellBack = configuredEffort
|
|
353
355
|
if (lastResult) emit(lastResult)
|
|
354
356
|
// 让服务端自行落盘退出;收到完成通知就强杀会破坏下一轮 resume。
|
|
355
357
|
try { child.stdin.end() } catch {}
|
package/src/agent/codexcode.js
CHANGED
|
@@ -83,8 +83,10 @@ function buildPrompt(prompt, appendSystemPrompt, isResume) {
|
|
|
83
83
|
// 2026-07-07): resume takes `resume <id>`, REJECTS `--sandbox` (use `-c sandbox_mode=`)
|
|
84
84
|
// and `--cd` (relies on process cwd); fresh accepts `--sandbox` + `--cd`. Prompt rides
|
|
85
85
|
// on stdin (trailing `-`), dodging Windows argv mangling of arbitrary chat text.
|
|
86
|
-
function buildArgs({ sessionId, model, workdir, permissionMode, reasoningEffort, autocompactTokens }) {
|
|
87
|
-
|
|
86
|
+
function buildArgs({ sessionId, model, workdir, permissionMode, reasoningEffort, autocompactTokens, contextWindowTokens }) {
|
|
87
|
+
const args = codexBuildArgs({ sessionId, model, workdir, permissionMode, reasoningEffort, autocompactTokens })
|
|
88
|
+
if (Number.isSafeInteger(contextWindowTokens) && contextWindowTokens > 0) args.unshift('-c', `model_context_window=${contextWindowTokens}`)
|
|
89
|
+
return args
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
// A codex turn.failed / error result whose offending param is the reasoning effort —
|
|
@@ -106,7 +108,7 @@ function isEffortRejection(result) {
|
|
|
106
108
|
* @returns {Promise<import('../core/interfaces').RunTurnResult>}
|
|
107
109
|
*/
|
|
108
110
|
function runTurn(opts = {}) {
|
|
109
|
-
const wantEffort = (opts.provider && opts.provider.reasoningEffort) || ''
|
|
111
|
+
const wantEffort = opts.effort || (opts.provider && opts.provider.reasoningEffort) || ''
|
|
110
112
|
if (!wantEffort) return runOnce(opts, '')
|
|
111
113
|
// Attempt 1 with the configured effort. Hold back only ERROR result events so a
|
|
112
114
|
// recoverable effort-rejection can't finalize the turn early (the engine finalizes on
|
|
@@ -191,7 +193,7 @@ function runOnce(opts, effort) {
|
|
|
191
193
|
// so no collision with codex's own env. read-only sandbox can't run the command anyway.
|
|
192
194
|
if (extraEnv) Object.assign(env, extraEnv)
|
|
193
195
|
|
|
194
|
-
const args = buildArgs({ sessionId, model: model || provider.model, workdir, permissionMode, reasoningEffort: effort, autocompactTokens })
|
|
196
|
+
const args = buildArgs({ sessionId, model: model || provider.model, workdir, permissionMode, reasoningEffort: effort, autocompactTokens, contextWindowTokens: provider.contextWindowTokens })
|
|
195
197
|
const { file, spawnArgs, fallback } = resolveSpawn(bin, args)
|
|
196
198
|
if (fallback) log(`配置的 agent 路径 '${bin}' 不存在,本轮改用 ${fallback} —— 请把配置改回正确路径`) // 与 claude runner 同一条兜底
|
|
197
199
|
const child = spawnFn(file, spawnArgs, {
|