@whyshu/argent-erp 0.2.2 → 0.2.3
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/package.json +1 -1
- package/src/index.js +1 -1
- package/src/office-guard.js +46 -17
package/package.json
CHANGED
package/src/index.js
CHANGED
package/src/office-guard.js
CHANGED
|
@@ -36,28 +36,57 @@ export function apply(ctx) {
|
|
|
36
36
|
try {
|
|
37
37
|
let disposeMask = null
|
|
38
38
|
let lastMask = ''
|
|
39
|
+
/**
|
|
40
|
+
* 重入闸(2026-11-08 修):`ctx.tools.restrict()` 与它的 disposer **都会同步 emit
|
|
41
|
+
* `tools/change`**(见 dsh-scope `ScopedLayers.effect` 的两处 onChange)。
|
|
42
|
+
* 第一次收敛之后才出现的全局工具(典型:MCP 网关在启动后几秒才注册
|
|
43
|
+
* `mcp__argent__erp_*`)会让本函数在「dispose 旧 mask → restrict 新 mask」中途被
|
|
44
|
+
* 同步回调重入;此时 `lastMask` 还没更新 ⇒ 每层都判定"集合变了" ⇒ 无限同步递归
|
|
45
|
+
* (主线程 100% CPU + 堆 OOM,dsh 端口 bound 但 HTTP 不再响应)。
|
|
46
|
+
* 判据:工具名集合没变就只允许一个 reconcile 在跑;本函数自己触发的变化只记 dirty,
|
|
47
|
+
* 等这一轮收敛完再补一轮(保证真有新工具时不会漏收敛)。
|
|
48
|
+
*/
|
|
49
|
+
let reconciling = false
|
|
50
|
+
let dirty = false
|
|
51
|
+
|
|
52
|
+
/** 收敛一轮:按当前全局工具名重算并换 mask。 */
|
|
53
|
+
const applyMask = () => {
|
|
54
|
+
// 全局视图(不传 scope)⇒ 拿到的是"未收敛"的全量工具,不受本作用域 mask 影响
|
|
55
|
+
const all = typeof ctx.tools?.definitions === 'function' ? ctx.tools.definitions() : []
|
|
56
|
+
const names = all.map((d) => (d && d.name) || '')
|
|
57
|
+
const allow = filterErpTools(names)
|
|
58
|
+
// 有 ERP 工具 ⇒ 只放行它们;一个都没有(没装 @whyshu/argent / MCP 未起)⇒ **全部禁掉**,
|
|
59
|
+
// 宁可这个会话无工具,也不让 Agent Team 等全局工具漏进来(边界优先)。
|
|
60
|
+
const mask = allow.length ? { allow } : (names.length ? { deny: names } : null)
|
|
61
|
+
if (!mask) return
|
|
62
|
+
const key = JSON.stringify(mask)
|
|
63
|
+
if (key === lastMask) return
|
|
64
|
+
// 先记账再动 mask:restrict/它的 disposer 同步 emit 时,重入会被闸挡住且只标 dirty;
|
|
65
|
+
// 即便真漏进来一次,key 也已是最新值,不会再次判定"集合变了"。
|
|
66
|
+
lastMask = key
|
|
67
|
+
try { disposeMask?.() } catch { /* noop */ }
|
|
68
|
+
disposeMask = ctx.tools.restrict(mask)
|
|
69
|
+
console.log(allow.length
|
|
70
|
+
? `[argent-office] 工具边界:仅放行 ${allow.length} 个 ERP 只读工具`
|
|
71
|
+
: '[argent-office] 工具边界:未发现 ERP 工具 ⇒ 全部工具禁用(检查 @whyshu/argent 是否安装)')
|
|
72
|
+
}
|
|
39
73
|
|
|
40
74
|
const reconcile = () => {
|
|
75
|
+
if (reconciling) { dirty = true; return }
|
|
76
|
+
reconciling = true
|
|
41
77
|
try {
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const mask = allow.length ? { allow } : (names.length ? { deny: names } : null)
|
|
49
|
-
if (!mask) return
|
|
50
|
-
const key = JSON.stringify(mask)
|
|
51
|
-
if (key === lastMask) return
|
|
52
|
-
try { disposeMask?.() } catch { /* noop */ }
|
|
53
|
-
disposeMask = ctx.tools.restrict(mask)
|
|
54
|
-
lastMask = key
|
|
55
|
-
console.log(allow.length
|
|
56
|
-
? `[argent-office] 工具边界:仅放行 ${allow.length} 个 ERP 只读工具`
|
|
57
|
-
: '[argent-office] 工具边界:未发现 ERP 工具 ⇒ 全部工具禁用(检查 @whyshu/argent 是否安装)')
|
|
78
|
+
// 最多补 20 轮:只有"收敛过程中又出现了新工具"才会再来一轮,正常情况 1 轮结束
|
|
79
|
+
for (let round = 0; round < 20; round++) {
|
|
80
|
+
dirty = false
|
|
81
|
+
applyMask()
|
|
82
|
+
if (!dirty) break
|
|
83
|
+
}
|
|
58
84
|
} catch (e) {
|
|
59
|
-
//
|
|
85
|
+
// 收敛失败不阻塞会话:提示词仍然是第二道边界;清掉 key 让下一次工具变化时重试
|
|
86
|
+
lastMask = ''
|
|
60
87
|
console.warn('[argent-office] 工具边界收敛失败(继续按提示词边界运行):', (e && e.message) || e)
|
|
88
|
+
} finally {
|
|
89
|
+
reconciling = false
|
|
61
90
|
}
|
|
62
91
|
}
|
|
63
92
|
|