sintbot 1.4.16 → 1.4.18
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 +62 -0
- package/CHANGELOG.json +62 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/bin/sintbot-v2.js +82 -4
- package/docs/AI-ASSISTED-INSTALL.md +4 -4
- package/docs/install.md +62 -10
- package/docs/zh-CN/AI-ASSISTED-INSTALL.md +4 -4
- package/docs/zh-CN/install.md +62 -10
- package/install.sh +1 -1
- package/package.json +4 -1
- package/src/agent/claudecode.js +41 -3
- package/src/agent/claudeusage.js +239 -5
- package/src/agent/envconflicts.js +74 -0
- package/src/agent/privatecore.js +86 -4
- package/src/agent/privatecore.wasm +0 -0
- package/src/agent/runner.js +9 -1
- package/src/c2configcontrol.js +7 -0
- package/src/c2console.js +3 -0
- package/src/c2install.js +1 -1
- package/src/c2multigroupservice.js +1 -1
- package/src/connect.js +566 -0
- package/src/core/c2runnerinput.js +47 -1
- package/src/core/v2engine.js +211 -11
- package/src/platform/feishu.js +22 -7
- package/src/setupserver.js +447 -0
- package/src/ui/console.html +161 -128
- package/src/ui/serverbase.js +19 -10
- package/src/usage.js +131 -38
package/src/agent/privatecore.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// ABI
|
|
1
|
+
// ABI v37 adapter for the private binary decision core. Manifest assembly still stays in JS.
|
|
2
2
|
const fs = require('node:fs')
|
|
3
3
|
const path = require('node:path')
|
|
4
4
|
const { createHash } = require('node:crypto')
|
|
5
5
|
|
|
6
|
-
const EXPECTED_SHA256 = '
|
|
6
|
+
const EXPECTED_SHA256 = '9705ac11c9ddcd47c9532471603e5c0c21d189ac89e353b33fcea55de91feb51'
|
|
7
7
|
let bytes
|
|
8
8
|
try {
|
|
9
9
|
bytes = fs.readFileSync(path.join(__dirname, 'privatecore.wasm'))
|
|
@@ -26,7 +26,7 @@ try {
|
|
|
26
26
|
} catch (cause) {
|
|
27
27
|
throw new Error('SintBot binary core cannot start on this runtime', { cause })
|
|
28
28
|
}
|
|
29
|
-
if (core.abi_version() !==
|
|
29
|
+
if (core.abi_version() !== 37) throw new Error('SintBot binary core ABI mismatch')
|
|
30
30
|
const encoder = new TextEncoder()
|
|
31
31
|
const decoder = new TextDecoder('utf-8', { fatal: true })
|
|
32
32
|
|
|
@@ -771,6 +771,88 @@ function manifestPolicy(permissionMode, isCodex, allowedTools, disallowedTools)
|
|
|
771
771
|
return value
|
|
772
772
|
}
|
|
773
773
|
|
|
774
|
+
// ABI 36 JSON decisions (sintbot-core docs/ABI36_CONTRACTS.md). Each wrapper only
|
|
775
|
+
// checks the envelope shape the host relies on; the core owns the rules and wording.
|
|
776
|
+
const isObject = value => !!value && typeof value === 'object' && !Array.isArray(value)
|
|
777
|
+
|
|
778
|
+
function connectManifest(basic, userName, version) {
|
|
779
|
+
const value = coreJson('connect_manifest', { basic: !!basic, userName: String(userName || ''), version: String(version || '') }, 'build the Feishu connect manifest')
|
|
780
|
+
if (!isObject(value) || !isObject(value.appPreset) || !isObject(value.addons) ||
|
|
781
|
+
!Array.isArray(value.addons.scopes) || !Array.isArray(value.addons.events) ||
|
|
782
|
+
!Array.isArray(value.addons.callbacks) || typeof value.hint !== 'string') {
|
|
783
|
+
throw new Error('SintBot binary core returned an invalid connect manifest')
|
|
784
|
+
}
|
|
785
|
+
return value
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
function connectPlan(step, { basic = false, status = null, code = null, msg = '', observed = {} } = {}) {
|
|
789
|
+
const value = coreJson('connect_plan', { step, basic: !!basic, status, code, msg: String(msg || ''), observed: isObject(observed) ? observed : {} }, `decide the connect step ${step}`)
|
|
790
|
+
if (!isObject(value) || !['ok', 'stop', 'wait', 'warn'].includes(value.verdict) || typeof value.notice !== 'string' ||
|
|
791
|
+
!(value.next === null || typeof value.next === 'string') ||
|
|
792
|
+
!(value.retryAfterMs === null || Number.isSafeInteger(value.retryAfterMs))) {
|
|
793
|
+
throw new Error('SintBot binary core returned an invalid connect decision')
|
|
794
|
+
}
|
|
795
|
+
return value
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
function connectConfig(input) {
|
|
799
|
+
const value = coreJson('connect_config', input, 'build the Feishu connect configuration')
|
|
800
|
+
if (!isObject(value) || typeof value.ok !== 'boolean' ||
|
|
801
|
+
(value.ok && (!isObject(value.config) || typeof value.readme !== 'string')) ||
|
|
802
|
+
(!value.ok && typeof value.error !== 'string')) {
|
|
803
|
+
throw new Error('SintBot binary core returned an invalid connect configuration')
|
|
804
|
+
}
|
|
805
|
+
return value
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
function qrMatrix(text, ec = 'M') {
|
|
809
|
+
const value = coreJson('qr_matrix', { text: String(text), ec }, 'encode a QR code')
|
|
810
|
+
if (!isObject(value) || !Number.isInteger(value.size) || !Array.isArray(value.rows) ||
|
|
811
|
+
value.rows.length !== value.size || value.rows.some(row => typeof row !== 'string' || row.length !== value.size)) {
|
|
812
|
+
throw new Error('SintBot binary core returned an invalid QR matrix')
|
|
813
|
+
}
|
|
814
|
+
return value
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
function usageParse(provider, source, status, body, now = Date.now()) {
|
|
818
|
+
const value = coreJson('usage_parse', { provider, source, status, body, now }, `parse ${provider} ${source} usage`)
|
|
819
|
+
if (!isObject(value) || typeof value.ok !== 'boolean' ||
|
|
820
|
+
(value.ok && !Array.isArray(value.windows)) || (!value.ok && typeof value.error !== 'string')) {
|
|
821
|
+
throw new Error('SintBot binary core returned an invalid usage result')
|
|
822
|
+
}
|
|
823
|
+
return value
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// ABI 37 (docs/ABI37_CONTRACTS.md in the core): idle compaction rules. The host
|
|
827
|
+
// only spawns, stops and logs; start/skip and settle outcomes are decided here.
|
|
828
|
+
function idleCompactPlan(input) {
|
|
829
|
+
const value = coreJson('idle_compact_plan', input, 'plan idle compaction')
|
|
830
|
+
if (!isObject(value) || typeof value.start !== 'boolean' || !Number.isFinite(value.at) ||
|
|
831
|
+
typeof value.reason !== 'string' || typeof value.keep !== 'string' || !Number.isFinite(value.maxMs)) {
|
|
832
|
+
throw new Error('SintBot binary core returned an invalid idle compaction plan')
|
|
833
|
+
}
|
|
834
|
+
return value
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
function idleCompactSettle(input) {
|
|
838
|
+
const value = coreJson('idle_compact_settle', input, 'settle idle compaction')
|
|
839
|
+
if (!isObject(value) || !['compacted', 'stopped', 'failed'].includes(value.status) ||
|
|
840
|
+
!Number.isFinite(value.fails) || typeof value.notice !== 'string' ||
|
|
841
|
+
(value.ctx !== null && !Number.isFinite(value.ctx)) || (value.restUntil !== null && !Number.isFinite(value.restUntil))) {
|
|
842
|
+
throw new Error('SintBot binary core returned an invalid idle compaction result')
|
|
843
|
+
}
|
|
844
|
+
return value
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
function envConflicts(input) {
|
|
848
|
+
const value = coreJson('env_conflicts', input, 'check environment conflicts')
|
|
849
|
+
if (!isObject(value) || !Array.isArray(value.warnings) ||
|
|
850
|
+
value.warnings.some(w => !isObject(w) || typeof w.key !== 'string' || typeof w.where !== 'string' || typeof w.reason !== 'string')) {
|
|
851
|
+
throw new Error('SintBot binary core returned an invalid environment check')
|
|
852
|
+
}
|
|
853
|
+
return value
|
|
854
|
+
}
|
|
855
|
+
|
|
774
856
|
// A trap or exhausted stack can leave the instance's own stack pointer wrong, and
|
|
775
857
|
// C1 calls hold no state, so the next call simply gets a fresh instance.
|
|
776
858
|
function guarded(fn) {
|
|
@@ -798,7 +880,7 @@ function followupCall(name, value) {
|
|
|
798
880
|
function followupEnabled(group, runner) { return followupCall('followup_policy', { group, runner }) }
|
|
799
881
|
function claudeFollowupStep(state, event) { return followupCall('claude_followup_step', { state, event }) }
|
|
800
882
|
|
|
801
|
-
const entries = { followupEnabled, claudeFollowupStep, classifyError, routeBackend, sandboxTier, appErrorOverride, appNormalizeItem, appStreamTail, appNoticeAction, appDeltaNext, appLandedAction, appCompletionAction, appCompletionNoticeAction, manifestFeatures, manifestHeader, manifestContext, manifestSkills, manifestStyleBoard, manifestImagenAsset, manifestRuntime, manifestPolicy, systemAppendix, codexEffortRejection, wecomGatePrompt, imagenPrompt, turnPrompt, compactEvent, engineQueueStep, engineSlotsStep, engineTurnStep, engineSessionStep, engineHandoffStep, engineIntakeStep, engineLifecycleStep, engineTerminalStep, engineSteerStep, engineRecoveryStep, engineFailoverStep, parseResetFields, chooseResetEpoch, codexBuildPrompt, codexBuildArgs, appUsageFromTotal, settleUsage, appBuildRequest, claudeBaseSkillAllows, claudeBuildArgs, claudePluginAllowRules, claudeNormalizeEvent, codexNormalizeEvent, codexParseError }
|
|
883
|
+
const entries = { followupEnabled, claudeFollowupStep, classifyError, routeBackend, sandboxTier, appErrorOverride, appNormalizeItem, appStreamTail, appNoticeAction, appDeltaNext, appLandedAction, appCompletionAction, appCompletionNoticeAction, manifestFeatures, manifestHeader, manifestContext, manifestSkills, manifestStyleBoard, manifestImagenAsset, manifestRuntime, manifestPolicy, systemAppendix, codexEffortRejection, wecomGatePrompt, imagenPrompt, turnPrompt, compactEvent, engineQueueStep, engineSlotsStep, engineTurnStep, engineSessionStep, engineHandoffStep, engineIntakeStep, engineLifecycleStep, engineTerminalStep, engineSteerStep, engineRecoveryStep, engineFailoverStep, parseResetFields, chooseResetEpoch, codexBuildPrompt, codexBuildArgs, appUsageFromTotal, settleUsage, appBuildRequest, claudeBaseSkillAllows, claudeBuildArgs, claudePluginAllowRules, claudeNormalizeEvent, codexNormalizeEvent, codexParseError, connectManifest, connectPlan, connectConfig, qrMatrix, usageParse, envConflicts, idleCompactPlan, idleCompactSettle }
|
|
802
884
|
for (const name of Object.keys(entries)) entries[name] = guarded(entries[name])
|
|
803
885
|
|
|
804
886
|
module.exports = { ...entries, engineStep, resetBinaryCore, HUMAN_BY_KIND: HUMAN }
|
|
Binary file
|
package/src/agent/runner.js
CHANGED
|
@@ -22,6 +22,14 @@ function runTurn(opts) {
|
|
|
22
22
|
return claudeAgent.runTurn(opts)
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
// 空闲整理只有 Claude 路线有(无头 /compact);GPT ⇒ null = 不支持,引擎只在轮内压缩。
|
|
26
|
+
function compactSession(opts) {
|
|
27
|
+
const route = routeBackend(opts && opts.provider && opts.provider.runner, opts && opts.codexApp, opts && opts.codexSteer, opts && opts.codexProjectDocFallback)
|
|
28
|
+
if (route !== 'claude') return null
|
|
29
|
+
const compact = Number(opts.autocompactTokens ?? opts.provider?.autocompactTokens) || 0
|
|
30
|
+
return claudeAgent.compactSession({ ...opts, autocompactTokens: compact > 0 ? Math.min(1_000_000, Math.max(100_000, Math.floor(compact))) : 0 })
|
|
31
|
+
}
|
|
32
|
+
|
|
25
33
|
const isClosedRunnerResult = value => [claudeAgent, codexAgent, codexApp].some(agent => agent.isClosedRunnerResult(value))
|
|
26
34
|
const isClosedRunnerError = value => [claudeAgent, codexAgent, codexApp].some(agent => agent.isClosedRunnerError(value))
|
|
27
|
-
module.exports = { runTurn, probeVersion: claudeAgent.probeVersion, isClosedRunnerResult, isClosedRunnerError }
|
|
35
|
+
module.exports = { runTurn, compactSession, probeVersion: claudeAgent.probeVersion, isClosedRunnerResult, isClosedRunnerError }
|
package/src/c2configcontrol.js
CHANGED
|
@@ -200,6 +200,13 @@ function createC2ConfigControl({ installation, getBindings, getHeld = () => new
|
|
|
200
200
|
if (!prepared || prepared.raw !== JSON.stringify(raw)) {
|
|
201
201
|
throw new Error('Configuration was not validated against the current service')
|
|
202
202
|
}
|
|
203
|
+
const bindings = getBindings()
|
|
204
|
+
// Stop every affected compactor before mutating any session or file.
|
|
205
|
+
let stopping = false
|
|
206
|
+
for (const chatId of prepared.resets) {
|
|
207
|
+
if (bindings.get(chatId)?.engine?.stopIdleCompact?.(chatId, '会话已重置')) stopping = true
|
|
208
|
+
}
|
|
209
|
+
if (stopping) throw new Error('正在停止会话整理,请稍后再保存设置')
|
|
203
210
|
for (const chatId of prepared.resets) store.resetGroupSession(chatId)
|
|
204
211
|
if (prepared.added.length) {
|
|
205
212
|
for (const route of prepared.added) {
|
package/src/c2console.js
CHANGED
|
@@ -110,6 +110,9 @@ function createC2ConsoleFacade({ getBindings, getCapacity, getConfig, getHeld =
|
|
|
110
110
|
try { assertJournalScope(binding.journal, chatId, { requireSettled: true }) } catch (error) {
|
|
111
111
|
throw new Error(busy, { cause: error })
|
|
112
112
|
}
|
|
113
|
+
if (binding.engine.stopIdleCompact?.(chatId, '会话已重置')) {
|
|
114
|
+
throw new Error('正在停止会话整理,请稍后再开新会话')
|
|
115
|
+
}
|
|
113
116
|
binding.engine.store.resetSessionAndStats(chatId)
|
|
114
117
|
return true
|
|
115
118
|
}
|
package/src/c2install.js
CHANGED
|
@@ -323,6 +323,6 @@ function registerAddedGroups(installation) {
|
|
|
323
323
|
return addedRoutes.map(route => route.chatId)
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
-
module.exports = { registerAddedGroups, loadInstallation, loadInstallationFromRaw, initializeInstallation, assertPrivateDirectory,
|
|
326
|
+
module.exports = { registerAddedGroups, loadInstallation, loadInstallationFromRaw, initializeInstallation, installationStateDir, assertPrivateDirectory,
|
|
327
327
|
assertJournalScope, assertInstallationJournals, assertRunnerExecutable, SUPPORTED_RUNNERS, MANIFEST,
|
|
328
328
|
VALIDATORS }
|
|
@@ -164,7 +164,7 @@ function createC2MultiGroupService({ routes, assertWriterLock,
|
|
|
164
164
|
binding.engine.shutdown()
|
|
165
165
|
await bounded(() => binding.engine.drainDurableV2(drainTimeoutMs), drainTimeoutMs + 100)
|
|
166
166
|
const quiescent = [...binding.engine.rts.values()].every(rt =>
|
|
167
|
-
!rt.v2Controller && !rt.v2NormalRouteActive)
|
|
167
|
+
!rt.v2Controller && !rt.v2NormalRouteActive && !rt.v2Compacting)
|
|
168
168
|
if (!quiescent || !writerHeld() || !route) throw new Error('C2 group fault is not quiescent')
|
|
169
169
|
const turns = activeTurns(binding.journal.snapshot())
|
|
170
170
|
const proofs = turns.length ? binding.engine.durableV2ActiveFaultProofs?.(turns) || null : null
|