@fg-lab/amadeus-qq 1.2.1 → 1.2.2
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/LICENSE +21 -21
- package/README.md +55 -53
- package/README.zh-CN.md +51 -51
- package/REFERENCE.md +49 -49
- package/cordis.patch.yml +6 -6
- package/docs/DATA_MIGRATION.md +26 -26
- package/docs/INSTALL_V1_2.md +22 -22
- package/docs/PUBLISHING.md +28 -26
- package/docs/RELEASE_CHECKLIST.md +23 -23
- package/docs/RELEASE_NOTES.md +38 -38
- package/docs/UPGRADE_POLICY.md +26 -26
- package/docs/verification/IMAGE_READ_FIX_2026-11-07.md +22 -0
- package/docs/verification/LIFE_QQ_BOUNDARY_2026-11-07.md +20 -0
- package/lib/client.js +372 -372
- package/package.json +94 -91
- package/preset.patch.yml +63 -0
- package/scripts/symlink-security.mjs +1 -1
- package/src/account-profile.js +223 -223
- package/src/admin-http.js +361 -361
- package/src/audio.js +428 -428
- package/src/capabilities.js +785 -785
- package/src/client/factory.template.js +82 -82
- package/src/client/logic.js +290 -290
- package/src/config.js +2 -0
- package/src/console/console.css +28 -28
- package/src/console/console.js +3 -0
- package/src/console/index.html +67 -67
- package/src/core.js +21 -4
- package/src/files.js +230 -230
- package/src/index.js +135 -8
- package/src/lease.js +42 -42
- package/src/media.js +66 -3
- package/src/napcat-action-names.js +186 -186
- package/src/native-asr.js +112 -112
- package/src/onebot.js +378 -378
- package/src/outbound-media.js +309 -309
- package/src/owner-workspace.js +394 -394
- package/src/permissions.js +20 -0
- package/src/preset-guard.mjs +16 -2
- package/src/qzone-provider.js +628 -628
- package/src/session-provenance.js +74 -74
- package/src/setup.js +255 -255
- package/src/web-console.js +9 -9
package/src/permissions.js
CHANGED
|
@@ -59,6 +59,9 @@ export const CAPABILITY_CATALOG = Object.freeze([
|
|
|
59
59
|
Object.freeze({ id: 'qq.media.read', implemented: true }),
|
|
60
60
|
Object.freeze({ id: 'qq.owner.workspace', implemented: true }),
|
|
61
61
|
Object.freeze({ id: 'qq.owner.tools', implemented: true }),
|
|
62
|
+
// Explicitly grants only the session-scoped Life read tools. This is not an
|
|
63
|
+
// Owner tool grant and never exposes Life admin/intent mutation surfaces.
|
|
64
|
+
Object.freeze({ id: 'amadeus.life.read', implemented: true }),
|
|
62
65
|
Object.freeze({ id: 'qq.reaction.write', implemented: true }),
|
|
63
66
|
Object.freeze({ id: 'qq.poke', implemented: true }),
|
|
64
67
|
Object.freeze({ id: 'qq.recall', implemented: true }),
|
|
@@ -77,6 +80,8 @@ export const CAPABILITY_CATALOG = Object.freeze([
|
|
|
77
80
|
Object.freeze({ id: 'qq.qzone.publish', implemented: true }),
|
|
78
81
|
Object.freeze({ id: 'dsh.shell', implemented: false }),
|
|
79
82
|
])
|
|
83
|
+
export const LIFE_READ_CAPABILITY = 'amadeus.life.read'
|
|
84
|
+
export const LIFE_READ_TOOLS = Object.freeze(['life_shared_read', 'life_health'])
|
|
80
85
|
const KNOWN_CAPABILITIES = new Set(CAPABILITY_CATALOG.map(item => item.id))
|
|
81
86
|
const DIRECTORY_CAPABILITIES = new Set(['qq.friends.read', 'qq.groups.read'])
|
|
82
87
|
const QZONE_CAPABILITIES = new Set(['qq.qzone.use', 'qq.qzone.read', 'qq.qzone.publish'])
|
|
@@ -143,6 +148,7 @@ export class PermissionEngine {
|
|
|
143
148
|
throw new TypeError('groupDefaultMode cannot be owner-agent: groups cannot be Owner Agent Scenes')
|
|
144
149
|
}
|
|
145
150
|
this.ownerPrivateIds = idSet(raw.ownerPrivateIds ?? [], 'ownerPrivateIds')
|
|
151
|
+
this.lifeReadPrivateIds = idSet(raw.lifeReadPrivateIds ?? [], 'lifeReadPrivateIds')
|
|
146
152
|
this.ownerTools = stringSet(raw.ownerTools ?? [], 'ownerTools')
|
|
147
153
|
this.globalDeny = stringSet(raw.globalDeny ?? [], 'globalDeny')
|
|
148
154
|
this.accountGrants = raw.accountGrants ?? {}
|
|
@@ -197,6 +203,19 @@ export class PermissionEngine {
|
|
|
197
203
|
}
|
|
198
204
|
return decision(true, 'owner-agent')
|
|
199
205
|
}
|
|
206
|
+
if (capability === LIFE_READ_CAPABILITY) {
|
|
207
|
+
// Life tools are session-scoped and read-only, but still require an
|
|
208
|
+
// explicit Scene grant so ordinary QQ chat cannot discover them by
|
|
209
|
+
// default. A configured private allowlist is an equivalent explicit
|
|
210
|
+
// authorization for this narrow read-only capability. The Life module
|
|
211
|
+
// performs the final binding/ACL check.
|
|
212
|
+
if (scene.kind === 'private' && this.lifeReadPrivateIds.has(scene.peerId) && override !== 'deny') {
|
|
213
|
+
return decision(true, 'configured-life-read-private')
|
|
214
|
+
}
|
|
215
|
+
return override === 'allow'
|
|
216
|
+
? decision(true, 'scene-allow')
|
|
217
|
+
: decision(false, override === 'deny' ? 'scene-deny' : 'life-read-explicit')
|
|
218
|
+
}
|
|
200
219
|
if (capability === 'qq.owner.workspace') {
|
|
201
220
|
// The file surface is never implied by owner-agent mode and is never
|
|
202
221
|
// reachable from Safe Chat; the Owner must explicitly allow this Scene.
|
|
@@ -248,3 +267,4 @@ export class PermissionEngine {
|
|
|
248
267
|
return decision(MODE_TO_CAPABILITIES[mode].includes(capability), 'mode')
|
|
249
268
|
}
|
|
250
269
|
}
|
|
270
|
+
|
package/src/preset-guard.mjs
CHANGED
|
@@ -49,6 +49,10 @@ export const OWNER_NATIVE_TOOLS = Object.freeze([
|
|
|
49
49
|
*/
|
|
50
50
|
export const MEMORY_READ_TOOLS = Object.freeze(['memory_search', 'memory_list', 'memory_get'])
|
|
51
51
|
const MEMORY_READ_TOOL_SET = new Set(MEMORY_READ_TOOLS)
|
|
52
|
+
// Session-bound Life views are safe to expose by name; the connector applies
|
|
53
|
+
// the explicit Scene capability at execution time.
|
|
54
|
+
export const LIFE_READ_TOOLS = Object.freeze(['life_shared_read', 'life_health'])
|
|
55
|
+
const LIFE_READ_TOOL_SET = new Set(LIFE_READ_TOOLS)
|
|
52
56
|
|
|
53
57
|
/**
|
|
54
58
|
* Cordis service contract for the trusted-Scene read capability.
|
|
@@ -183,6 +187,8 @@ export function resolveQQPermissionPolicy(config = {}) {
|
|
|
183
187
|
}
|
|
184
188
|
const allowSearch = config.allowSearch === undefined ? false : config.allowSearch
|
|
185
189
|
if (typeof allowSearch !== 'boolean') throw new TypeError('QQ preset allowSearch must be a boolean')
|
|
190
|
+
const allowLifeRead = config.allowLifeRead === undefined ? false : config.allowLifeRead
|
|
191
|
+
if (typeof allowLifeRead !== 'boolean') throw new TypeError('QQ preset allowLifeRead must be a boolean')
|
|
186
192
|
const entry = QQ_PERMISSION_LEVELS[level]
|
|
187
193
|
// Memory readers are intentionally not part of the static preset policy.
|
|
188
194
|
// They are opened only after the live Memory service declares the trusted
|
|
@@ -191,7 +197,7 @@ export function resolveQQPermissionPolicy(config = {}) {
|
|
|
191
197
|
const tools = allowSearch
|
|
192
198
|
? [...toolsForLevel(level), 'web_search']
|
|
193
199
|
: toolsForLevel(level)
|
|
194
|
-
return Object.freeze({ level, levelId: entry.id, tools: Object.freeze(tools), allowSearch })
|
|
200
|
+
return Object.freeze({ level, levelId: entry.id, tools: Object.freeze(tools), allowSearch, allowLifeRead })
|
|
195
201
|
}
|
|
196
202
|
|
|
197
203
|
/**
|
|
@@ -211,7 +217,7 @@ export function apply(ctx, config = {}) {
|
|
|
211
217
|
// policy only when the trusted-Scene contract is present.
|
|
212
218
|
const OPTIONAL_TOOLS = new Set([
|
|
213
219
|
'qq_send_media', 'qq_upload_file', 'qq_qzone', 'qq_account_profile', OWNER_WORKSPACE_TOOL,
|
|
214
|
-
...MEMORY_READ_TOOLS,
|
|
220
|
+
...MEMORY_READ_TOOLS, ...LIFE_READ_TOOLS,
|
|
215
221
|
])
|
|
216
222
|
const OWNER_NATIVE = new Set(OWNER_NATIVE_TOOLS)
|
|
217
223
|
// Host plugins can register tools after this preset starts. Keep the QQ
|
|
@@ -403,6 +409,11 @@ export function apply(ctx, config = {}) {
|
|
|
403
409
|
? undefined
|
|
404
410
|
: 'QQ Memory trusted Scene ACL read contract is unavailable'
|
|
405
411
|
}
|
|
412
|
+
if (LIFE_READ_TOOL_SET.has(tool)) {
|
|
413
|
+
return authorizeCapability(exec, 'amadeus.life.read')
|
|
414
|
+
? undefined
|
|
415
|
+
: 'QQ Session Life read permission denied'
|
|
416
|
+
}
|
|
406
417
|
if (allowed.has(tool)) return undefined
|
|
407
418
|
if (Object.hasOwn(QQ_TOOL_CONTRACT, tool)) {
|
|
408
419
|
return `QQ Session permission level ${policy.level} does not allow ${tool}`
|
|
@@ -439,6 +450,7 @@ export function apply(ctx, config = {}) {
|
|
|
439
450
|
const fresh = schemas
|
|
440
451
|
.map(schema => schema?.name)
|
|
441
452
|
.filter(name => typeof name === 'string' && !MEMORY_READ_TOOL_SET.has(name) &&
|
|
453
|
+
!LIFE_READ_TOOL_SET.has(name) &&
|
|
442
454
|
!allowed.has(name) &&
|
|
443
455
|
!(policy.level === 4 && ownerHostTool(name)) && name !== 'run_code' && !deniedVisible.has(name))
|
|
444
456
|
if (fresh.length === 0) return
|
|
@@ -479,6 +491,7 @@ export function apply(ctx, config = {}) {
|
|
|
479
491
|
const visiblePolicy = [
|
|
480
492
|
...policy.tools,
|
|
481
493
|
...(memoryReadGranted ? MEMORY_READ_TOOLS : []),
|
|
494
|
+
...(policy.allowLifeRead ? LIFE_READ_TOOLS : []),
|
|
482
495
|
...(policy.level === 4
|
|
483
496
|
? [OWNER_WORKSPACE_TOOL, ...[...known].filter(ownerHostTool)]
|
|
484
497
|
: []),
|
|
@@ -577,3 +590,4 @@ export function apply(ctx, config = {}) {
|
|
|
577
590
|
}))
|
|
578
591
|
}
|
|
579
592
|
}
|
|
593
|
+
|