release: public alpha 0.6.0 baseline
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user