154 lines
4.6 KiB
Markdown
154 lines
4.6 KiB
Markdown
# Plugin Contract
|
|
|
|
Status: authoritative for the `alpha 0.6.x` extension API.
|
|
|
|
## Scope
|
|
|
|
JWeBot extensions run for one configured, preselected WeChat group. They do not
|
|
discover groups, choose a destination, switch chats, or send messages directly.
|
|
|
|
The kernel owns:
|
|
|
|
- OCR event ordering and deduplication;
|
|
- the durable ledger and outboxes;
|
|
- acknowledgement ordering;
|
|
- model and approved network credentials;
|
|
- selected-group fingerprint verification;
|
|
- the GUI interaction lock and final WeChat delivery.
|
|
|
|
Extensions return structured intent. The kernel decides whether and when that
|
|
intent becomes a user-visible action.
|
|
|
|
## API Version
|
|
|
|
The current extension API is:
|
|
|
|
```text
|
|
api_version = "1"
|
|
```
|
|
|
|
An unsupported API version fails manifest validation before extension code is
|
|
loaded.
|
|
|
|
## Event Input
|
|
|
|
Extensions receive `jwebot_sdk.Event`, a normalized incoming event containing:
|
|
|
|
- event ID and observation timestamp;
|
|
- untrusted display sender label;
|
|
- bounded message text;
|
|
- confidence values;
|
|
- the current group fingerprint;
|
|
- mention flag and source metadata.
|
|
|
|
The group fingerprint is evidence for kernel delivery safety. It is not a group
|
|
selector and must not be used to discover or address another chat.
|
|
|
|
Display labels are conversational context only. They never grant permission.
|
|
|
|
## Result Output
|
|
|
|
An extension returns `jwebot_sdk.ExtensionResult`:
|
|
|
|
```python
|
|
ExtensionResult(
|
|
reply=ReplyPlan.text("hello"),
|
|
facts=(Fact(kind="topic", text="likes servers"),),
|
|
metadata={"source": "example"},
|
|
)
|
|
```
|
|
|
|
Limits:
|
|
|
|
- reply text: at most 900 characters;
|
|
- facts: at most 12;
|
|
- fact text: at most 120 characters;
|
|
- metadata: at most 16 short string entries.
|
|
|
|
`ReplyPlan.no_reply()` explicitly requests no visible response.
|
|
|
|
The extension cannot select a target group, draft GUI input, or press Send.
|
|
|
|
## Extension Types
|
|
|
|
### Command
|
|
|
|
Registers one or more reviewed slash-command namespaces. It may return one
|
|
reply plan and bounded facts.
|
|
|
|
### Enrichment
|
|
|
|
Consumes a normalized event off the reply-critical path. It may emit bounded
|
|
facts but cannot register commands or send replies by itself.
|
|
|
|
### Scheduled
|
|
|
|
Creates a scheduled intent that re-enters the kernel's ordered event path. It
|
|
never delivers directly.
|
|
|
|
Passive automatic reply extensions are not supported in API version 1.
|
|
|
|
## Manifest
|
|
|
|
Every extension has `plugin.toml`:
|
|
|
|
```toml
|
|
id = "hello-command"
|
|
author = "example"
|
|
description = "Return a local greeting"
|
|
help = ["/hello [name]"]
|
|
version = "1.0.0"
|
|
api_version = "1"
|
|
entrypoint = "hello_command:plugin"
|
|
type = "command"
|
|
|
|
[capabilities]
|
|
commands = ["/hello"]
|
|
storage = false
|
|
network_profiles = []
|
|
```
|
|
|
|
Unknown manifest keys and unknown capability names are rejected.
|
|
`author` is required, bounded, included in release metadata, and reviewed
|
|
against the extension's ownership directory.
|
|
Command plugins must declare bounded `description` and `help` metadata. The two
|
|
fields appear together; help entries begin with `/` and feed the
|
|
Kernel-generated `/功能列表`.
|
|
|
|
## Lifecycle
|
|
|
|
1. CI validates source, tests, manifest, API version, and capabilities.
|
|
2. Review approves the requested command and network namespaces.
|
|
3. CI builds an immutable artifact and canonical release record.
|
|
4. A reviewed publisher signs that exact release record with OpenSSH `sshsig`.
|
|
5. The target verifies the publisher, artifact digest, and API version.
|
|
6. The extension host re-verifies installed content and the publisher signature.
|
|
7. The extension host starts the approved release under resource limits.
|
|
8. The kernel registers approved manifests during startup and rejects conflicts.
|
|
9. The kernel freezes the extension catalog before runtime routing begins.
|
|
10. The kernel supplies a `SingleGroupContext`.
|
|
11. The extension registers only its declared behavior.
|
|
12. Invocation returns an `ExtensionResult` before its deadline.
|
|
13. The kernel validates and orders any resulting action.
|
|
|
|
The local Host, signed activation, and rollback contracts are implemented.
|
|
Provisioning and production enablement on production host remain separate work.
|
|
|
|
Static source validation rejects obvious access to processes, sockets, files,
|
|
dynamic execution, and Python object-introspection escape hatches. This is a CI
|
|
prefilter only. Production activation still requires a separately sandboxed
|
|
extension host with OS-enforced limits.
|
|
|
|
## Non-Negotiable Rules
|
|
|
|
- No GUI access.
|
|
- No direct WeChat send.
|
|
- No chat switching or group enumeration.
|
|
- No raw OCR frame or screenshot access.
|
|
- No direct ledger or SQLite access.
|
|
- No environment or secret reads.
|
|
- No shell, subprocess, SSH, or Docker access.
|
|
- No arbitrary network access.
|
|
- No background thread or process ownership.
|
|
- All behavior requires deterministic tests.
|