wake-bridge 0.9.0-preview.8
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 +201 -0
- package/README.md +222 -0
- package/dist/src/adapters/botlingknows.d.ts +42 -0
- package/dist/src/adapters/botlingknows.js +141 -0
- package/dist/src/adapters/botlingknows.js.map +1 -0
- package/dist/src/adapters/group-chat.d.ts +43 -0
- package/dist/src/adapters/group-chat.js +120 -0
- package/dist/src/adapters/group-chat.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +637 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/connector-catalog.d.ts +17 -0
- package/dist/src/connector-catalog.js +43 -0
- package/dist/src/connector-catalog.js.map +1 -0
- package/dist/src/connectors/botlingknows-mcp.d.ts +15 -0
- package/dist/src/connectors/botlingknows-mcp.js +130 -0
- package/dist/src/connectors/botlingknows-mcp.js.map +1 -0
- package/dist/src/connectors/group-chat-http.d.ts +17 -0
- package/dist/src/connectors/group-chat-http.js +226 -0
- package/dist/src/connectors/group-chat-http.js.map +1 -0
- package/dist/src/connectors/mcp-http-client.d.ts +11 -0
- package/dist/src/connectors/mcp-http-client.js +108 -0
- package/dist/src/connectors/mcp-http-client.js.map +1 -0
- package/dist/src/core.d.ts +170 -0
- package/dist/src/core.js +1720 -0
- package/dist/src/core.js.map +1 -0
- package/dist/src/daemon.d.ts +31 -0
- package/dist/src/daemon.js +605 -0
- package/dist/src/daemon.js.map +1 -0
- package/dist/src/db.d.ts +31 -0
- package/dist/src/db.js +513 -0
- package/dist/src/db.js.map +1 -0
- package/dist/src/event-sdk.d.ts +43 -0
- package/dist/src/event-sdk.js +152 -0
- package/dist/src/event-sdk.js.map +1 -0
- package/dist/src/host-adapter-registry.d.ts +21 -0
- package/dist/src/host-adapter-registry.js +59 -0
- package/dist/src/host-adapter-registry.js.map +1 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +6 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/instance-config.d.ts +46 -0
- package/dist/src/instance-config.js +176 -0
- package/dist/src/instance-config.js.map +1 -0
- package/dist/src/mcp.d.ts +51 -0
- package/dist/src/mcp.js +728 -0
- package/dist/src/mcp.js.map +1 -0
- package/dist/src/operator-control.d.ts +82 -0
- package/dist/src/operator-control.js +220 -0
- package/dist/src/operator-control.js.map +1 -0
- package/dist/src/out-of-process-host.d.ts +37 -0
- package/dist/src/out-of-process-host.js +196 -0
- package/dist/src/out-of-process-host.js.map +1 -0
- package/dist/src/policy-control.d.ts +3 -0
- package/dist/src/policy-control.js +120 -0
- package/dist/src/policy-control.js.map +1 -0
- package/dist/src/release-lifecycle.d.ts +98 -0
- package/dist/src/release-lifecycle.js +378 -0
- package/dist/src/release-lifecycle.js.map +1 -0
- package/dist/src/sdk-error.d.ts +6 -0
- package/dist/src/sdk-error.js +12 -0
- package/dist/src/sdk-error.js.map +1 -0
- package/dist/src/source-adapter.d.ts +32 -0
- package/dist/src/source-adapter.js +164 -0
- package/dist/src/source-adapter.js.map +1 -0
- package/dist/src/source-connector.d.ts +50 -0
- package/dist/src/source-connector.js +358 -0
- package/dist/src/source-connector.js.map +1 -0
- package/dist/src/source-control-client.d.ts +38 -0
- package/dist/src/source-control-client.js +61 -0
- package/dist/src/source-control-client.js.map +1 -0
- package/dist/src/source-ingress.d.ts +16 -0
- package/dist/src/source-ingress.js +47 -0
- package/dist/src/source-ingress.js.map +1 -0
- package/dist/src/source-sdk.d.ts +6 -0
- package/dist/src/source-sdk.js +53 -0
- package/dist/src/source-sdk.js.map +1 -0
- package/dist/src/transport-sdk.d.ts +102 -0
- package/dist/src/transport-sdk.js +222 -0
- package/dist/src/transport-sdk.js.map +1 -0
- package/dist/src/transport.d.ts +23 -0
- package/dist/src/transport.js +71 -0
- package/dist/src/transport.js.map +1 -0
- package/dist/src/types.d.ts +522 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/util.d.ts +26 -0
- package/dist/src/util.js +136 -0
- package/dist/src/util.js.map +1 -0
- package/dist/src/version.d.ts +1 -0
- package/dist/src/version.js +2 -0
- package/dist/src/version.js.map +1 -0
- package/docs/adapters/botlingknows.md +40 -0
- package/docs/adapters/gmail-planned.md +16 -0
- package/docs/adapters/group-chat.md +126 -0
- package/docs/operations/connector-catalog.md +40 -0
- package/docs/operations/operator-control.md +83 -0
- package/docs/operations/out-of-process-host-adapter.md +150 -0
- package/docs/operations/policies.md +106 -0
- package/docs/operations/public-sdk.md +87 -0
- package/docs/operations/release-lifecycle.md +154 -0
- package/docs/operations/source-connectors.md +163 -0
- package/docs/operations/source-ingress.md +108 -0
- package/docs/operations/templates/botlingknows-connector.env.example +5 -0
- package/docs/operations/templates/sources.json.example +15 -0
- package/docs/quickstart.md +143 -0
- package/docs/releases/0.9.0-preview.8.md +44 -0
- package/docs/verification.md +19 -0
- package/examples/policies/botlingknows-conservative.json +44 -0
- package/examples/reference-host-adapter/README.md +41 -0
- package/examples/reference-host-adapter/adapter.mjs +173 -0
- package/examples/reference-host-adapter/host-adapters.json.example +26 -0
- package/package.json +94 -0
- package/schemas/event-v1.schema.json +26 -0
- package/schemas/policy-v1.schema.json +66 -0
package/dist/src/util.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
export const DEFAULT_TIMEZONE = "UTC";
|
|
3
|
+
export function id(prefix) {
|
|
4
|
+
return `${prefix}_${randomUUID()}`;
|
|
5
|
+
}
|
|
6
|
+
export function isoNow(now = new Date()) {
|
|
7
|
+
return now.toISOString();
|
|
8
|
+
}
|
|
9
|
+
export function asDate(value) {
|
|
10
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
11
|
+
if (Number.isNaN(date.getTime()))
|
|
12
|
+
throw new Error(`Invalid date: ${String(value)}`);
|
|
13
|
+
return date;
|
|
14
|
+
}
|
|
15
|
+
export function addMs(value, milliseconds) {
|
|
16
|
+
return new Date(asDate(value).getTime() + milliseconds).toISOString();
|
|
17
|
+
}
|
|
18
|
+
export function sha256(value) {
|
|
19
|
+
return createHash("sha256").update(value).digest("hex");
|
|
20
|
+
}
|
|
21
|
+
export function parseJson(value, fallback) {
|
|
22
|
+
if (typeof value !== "string")
|
|
23
|
+
return value ?? fallback;
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(value);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return fallback;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function stableJson(value) {
|
|
32
|
+
if (value === null || typeof value !== "object")
|
|
33
|
+
return JSON.stringify(value);
|
|
34
|
+
if (Array.isArray(value))
|
|
35
|
+
return `[${value.map(stableJson).join(",")}]`;
|
|
36
|
+
const object = value;
|
|
37
|
+
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stableJson(object[key])}`).join(",")}}`;
|
|
38
|
+
}
|
|
39
|
+
export function getPath(value, path) {
|
|
40
|
+
return path.split(".").reduce((current, key) => {
|
|
41
|
+
if (current && typeof current === "object" && key in current) {
|
|
42
|
+
return current[key];
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}, value);
|
|
46
|
+
}
|
|
47
|
+
export function localParts(date, timezone) {
|
|
48
|
+
const formatter = new Intl.DateTimeFormat("en-CA", {
|
|
49
|
+
timeZone: timezone,
|
|
50
|
+
year: "numeric",
|
|
51
|
+
month: "2-digit",
|
|
52
|
+
day: "2-digit",
|
|
53
|
+
hour: "2-digit",
|
|
54
|
+
minute: "2-digit",
|
|
55
|
+
second: "2-digit",
|
|
56
|
+
hourCycle: "h23",
|
|
57
|
+
});
|
|
58
|
+
const values = Object.fromEntries(formatter.formatToParts(date).map((part) => [part.type, part.value]));
|
|
59
|
+
return {
|
|
60
|
+
date: `${values.year}-${values.month}-${values.day}`,
|
|
61
|
+
hour: Number(values.hour),
|
|
62
|
+
minute: Number(values.minute),
|
|
63
|
+
second: Number(values.second),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export function localMinute(date, timezone) {
|
|
67
|
+
const parts = localParts(date, timezone);
|
|
68
|
+
return parts.hour * 60 + parts.minute;
|
|
69
|
+
}
|
|
70
|
+
export function parseClock(value) {
|
|
71
|
+
const match = /^(\d{1,2}):(\d{2})$/.exec(value.trim());
|
|
72
|
+
if (!match)
|
|
73
|
+
throw new Error(`Invalid local time: ${value}`);
|
|
74
|
+
const hour = Number(match[1]);
|
|
75
|
+
const minute = Number(match[2]);
|
|
76
|
+
if (hour > 23 || minute > 59)
|
|
77
|
+
throw new Error(`Invalid local time: ${value}`);
|
|
78
|
+
return hour * 60 + minute;
|
|
79
|
+
}
|
|
80
|
+
export function clockInWindow(minute, start, end) {
|
|
81
|
+
if (start === end)
|
|
82
|
+
return true;
|
|
83
|
+
if (start < end)
|
|
84
|
+
return minute >= start && minute < end;
|
|
85
|
+
return minute >= start || minute < end;
|
|
86
|
+
}
|
|
87
|
+
export function quietWindowAt(date, config, fallbackTimezone = DEFAULT_TIMEZONE) {
|
|
88
|
+
if (!config?.windows?.length)
|
|
89
|
+
return false;
|
|
90
|
+
const timezone = config.timezone || fallbackTimezone;
|
|
91
|
+
const minute = localMinute(date, timezone);
|
|
92
|
+
return config.windows.some((window) => clockInWindow(minute, parseClock(window.start), parseClock(window.end)));
|
|
93
|
+
}
|
|
94
|
+
/** Find the first minute at which a quiet-hours gate is open again. */
|
|
95
|
+
export function quietEndAfter(date, config, fallbackTimezone = DEFAULT_TIMEZONE) {
|
|
96
|
+
if (!quietWindowAt(date, config, fallbackTimezone))
|
|
97
|
+
return date;
|
|
98
|
+
// A local clock window is at most 24 hours. Scan in minute increments so
|
|
99
|
+
// this remains correct across timezone offsets and midnight windows. Start
|
|
100
|
+
// from an absolute minute boundary so a 01:45:37 observation releases at
|
|
101
|
+
// 03:30:00, never 03:30:37.
|
|
102
|
+
const start = date.getTime();
|
|
103
|
+
const firstBoundary = Math.floor(start / 60_000) * 60_000;
|
|
104
|
+
for (let minute = 1; minute <= 48 * 60 + 2; minute += 1) {
|
|
105
|
+
const candidate = new Date(firstBoundary + minute * 60_000);
|
|
106
|
+
if (!quietWindowAt(candidate, config, fallbackTimezone))
|
|
107
|
+
return candidate;
|
|
108
|
+
}
|
|
109
|
+
return new Date(start + 24 * 60 * 60_000);
|
|
110
|
+
}
|
|
111
|
+
/** Resolve the next occurrence of HH:mm in a timezone using a bounded search. */
|
|
112
|
+
export function nextLocalClock(date, clock, timezone) {
|
|
113
|
+
const target = parseClock(clock);
|
|
114
|
+
const start = date.getTime();
|
|
115
|
+
const firstBoundary = Math.floor(start / 60_000) * 60_000;
|
|
116
|
+
for (let minute = 0; minute <= 48 * 60 + 2; minute += 1) {
|
|
117
|
+
const candidate = new Date(firstBoundary + minute * 60_000);
|
|
118
|
+
if (candidate.getTime() < start)
|
|
119
|
+
continue;
|
|
120
|
+
const parts = localParts(candidate, timezone);
|
|
121
|
+
if (parts.hour * 60 + parts.minute === target && candidate.getTime() >= start) {
|
|
122
|
+
return candidate;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return new Date(start + 24 * 60 * 60_000);
|
|
126
|
+
}
|
|
127
|
+
export function jsonValue(value) {
|
|
128
|
+
return value;
|
|
129
|
+
}
|
|
130
|
+
export function assertFiniteMs(value, field, fallback) {
|
|
131
|
+
const resolved = value ?? fallback;
|
|
132
|
+
if (!Number.isFinite(resolved) || resolved < 0)
|
|
133
|
+
throw new Error(`${field} must be a non-negative finite number`);
|
|
134
|
+
return resolved;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/util.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const RELEASE_VERSION = "0.9.0-preview.8";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# 小机知道通知 Source Connector adapter
|
|
2
|
+
|
|
3
|
+
状态:official optional Source Connector,available but default disabled。通用 connector 代码与 fixture 已实现;
|
|
4
|
+
每个 Agent Space 仍须独立完成 identity verify、from-now bootstrap、无读取副作用检查与真实 UAT 后才可 enable。
|
|
5
|
+
本文不证明某台机器当前已运行,也不能把第一位使用者的安装流程当作项目起源。
|
|
6
|
+
|
|
7
|
+
## 已实现的边界
|
|
8
|
+
|
|
9
|
+
- manifest 声明 `read_side_effects=none`、`credential_custody=connector`、上游 credential breadth `unknown`,以及 connector 对 Bridge 暴露 `notifications.read`;
|
|
10
|
+
- 只调用 `notifications(view=full, mark_read=false)`;
|
|
11
|
+
- raw notification `id` 生成稳定 dedupe key;
|
|
12
|
+
- 当前 provider page limit 为 50;公开 `sources.json.example` 固定使用 50,不能沿用 generic connector 的 100;
|
|
13
|
+
- event 只保存 type、category、稳定 id、resource reference 与 `next_action`,不复制通知正文;
|
|
14
|
+
- opaque 分页 cursor 只用于完成当前扫描,不冒充增量 high-watermark;
|
|
15
|
+
- 一页事件全部 durable 后才 CAS 推进 source checkpoint;崩溃重放由 event dedupe 吸收;
|
|
16
|
+
- 提供显式 `from-now` bootstrap helper,避免上线时偷偷把旧通知当新事件。
|
|
17
|
+
- 独立 connector 从自己的 `BOTLINGKNOWS_MCP_URL` 读取现有 Streamable HTTP MCP 登录,按标准 `initialize → notifications/initialized → tools/call` 调用;Wake Bridge 只持 connector-local bearer。
|
|
18
|
+
- owning agent 自己配置 `BOTLINGKNOWS_MCP_URL` 与非敏感的 `BOTLINGKNOWS_SUBJECT_REF`;connector 在内部从 URL/config 计算 `binding_fingerprint`。Wake Bridge 不发现、不复制、不修改 URL;
|
|
19
|
+
- 上游 URL、路径内身份、响应正文与 connector token 都不会进入 Wake Bridge event/checkpoint;checkpoint 只保存 agent 确认的 subject ref、binding hash 与 opaque cursor。上游身份撤销或 binding 变化投影为结构性 `needs_attention`,不无限重试。
|
|
20
|
+
|
|
21
|
+
## Cursor 语义
|
|
22
|
+
|
|
23
|
+
checkpoint 同时记录:
|
|
24
|
+
|
|
25
|
+
- `high_watermark`:上一次完整扫描确认的最高 notification id;
|
|
26
|
+
- `page_cursor`:尚未扫完时的临时分页位置;
|
|
27
|
+
- `pending_max_id`:本次扫描已经看到的最高 id。
|
|
28
|
+
|
|
29
|
+
只在扫到旧 watermark 或分页结束后,才把 `pending_max_id` 提升为新的 high-watermark。这样 bounded polling 不会因为先提交最新页而漏掉后续旧页中的新事件。
|
|
30
|
+
|
|
31
|
+
## Discover → configure → verify → bootstrap → UAT → enable
|
|
32
|
+
|
|
33
|
+
1. 使用 v0.9 `botlingknows-connector` 安装独立进程。它复用 owning agent 自己配置的 MCP URL;provider 无需修改 server 或签发专用 credential。上游 URL 只进入 connector env,不进入 Wake Bridge daemon、模型上下文或 event。
|
|
34
|
+
2. Connector manifest 如实记录实际上游 credential breadth。若当前 MCP credential 是 broad,就写 `broad` 而不是伪装成 `read_only`;Bridge 只获得 `notifications.read` connector capability。
|
|
35
|
+
3. 用真实调用确认 full view 的字段和排序与 fixture 一致,且 `mark_read=false` 在调用前后不改变 `read_at`。
|
|
36
|
+
4. Owning agent 先用 `attention_source_verify` 核对 subject/binding,再明确使用 `from-now` 或受控 backfill;P0 的 `attention_source_bootstrap/rebind/enable/disable` 不要求手改 SQLite、静态 source JSON 或 LaunchAgent。
|
|
37
|
+
5. 通用 runner、退避、health 与 connector 进程已实现;仍需安装两个 LaunchAgent/env,先用隔离 DB 做 bootstrap + `source-once`,再以一条专用测试通知做 canary。
|
|
38
|
+
6. 真实 canary 只验证 event/claim/receipt,不自动回复、不 mark useful、不改通知 read state。
|
|
39
|
+
|
|
40
|
+
若小机知道未来提供原生 `notifications:read` scope,可以无缝换成更窄的上游授权;这会降低 connector 被攻破时的影响面,但不是首次接入的前置条件。
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Gmail Source Connector
|
|
2
|
+
|
|
3
|
+
状态:planned,not bundled,not enableable。
|
|
4
|
+
|
|
5
|
+
Gmail 出现在 catalog 中仅用于说明预期的 Source Connector 形状;当前发行物没有 Gmail 实现、登录流程或支持承诺。
|
|
6
|
+
|
|
7
|
+
进入 `available` 以前至少需要完成:
|
|
8
|
+
|
|
9
|
+
- 选择并记录合规的 Google OAuth 应用与最小 scope;
|
|
10
|
+
- 定义 message/thread resource reference,不把邮件正文复制进 Wake Bridge;
|
|
11
|
+
- 明确 History API cursor、过期 cursor、初次 from-now bootstrap 和 backfill 语义;
|
|
12
|
+
- 验证读取不会自动 mark read、archive、modify 或发送邮件;
|
|
13
|
+
- 实现 token custody、refresh、revoke、rate limit、offline catch-up 与 `needs_attention`;
|
|
14
|
+
- 使用隔离账号完成真实身份、重启、重复 delivery 和端到端 wake UAT。
|
|
15
|
+
|
|
16
|
+
这些条件完成并经过独立版本验收后,catalog 才能把 Gmail 改为 `available`。
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Group Chat source adapter
|
|
2
|
+
|
|
3
|
+
状态:public SDK reference adapter 与 loopback HTTP connector;从 `0.9.0-preview.6` 起进入 package。
|
|
4
|
+
|
|
5
|
+
本文件属于 Wake Bridge。它描述第一个官方 reference adapter 如何消费独立 Group Chat 项目的公开 event feed;Group Chat 不导入或实现本仓库协议。
|
|
6
|
+
|
|
7
|
+
“第一个官方 reference adapter”只表示它在最初方案中用于验证真实的 mention/reply 垂直链路,不表示它拥有 core
|
|
8
|
+
特权,也不覆盖小机知道用户提问这一项目起源。第三方 source 必须能沿同一 SDK、contract test 与注册路径接入。
|
|
9
|
+
|
|
10
|
+
## 依赖方向
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
Wake Bridge group-chat adapter
|
|
14
|
+
│ consumes a narrow connector surface
|
|
15
|
+
▼
|
|
16
|
+
agent-controlled Group Chat connector
|
|
17
|
+
│ existing principal auth
|
|
18
|
+
▼
|
|
19
|
+
Group Chat versioned event-feed contract
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
adapter 是 anti-corruption layer:验证 Group Chat schema,再转换成通用 WakeEvent input。实现通过
|
|
23
|
+
`wake-bridge/source/group-chat` 发布,只依赖 public source/event contract;Wake Bridge core 不认识 room/message 领域对象,也不持有 Group Chat principal credential。
|
|
24
|
+
|
|
25
|
+
下面列的是这个 reference source 已经具备、connector 可以利用的能力,不是 Wake Bridge 要求所有第三方服务新增的接口。若另一个服务只有 OAuth REST、MCP、webhook 或本地 session,应由它自己的 connector 适配现有能力。
|
|
26
|
+
|
|
27
|
+
## 所需 Group Chat 能力
|
|
28
|
+
|
|
29
|
+
- service consumer credential,绑定一个 member principal 与 room scopes;
|
|
30
|
+
- `read_events(after_event_cursor, limit, wait_ms, types?)`;
|
|
31
|
+
- stable event id、global event cursor、schema version;
|
|
32
|
+
- resource URI/message id/message cursor;
|
|
33
|
+
- 使用相同 principal credential 读取 resource 的 API 或 MCP。
|
|
34
|
+
|
|
35
|
+
adapter 不需要 Group Chat 管理员 credential。
|
|
36
|
+
|
|
37
|
+
## v1 映射
|
|
38
|
+
|
|
39
|
+
| Group Chat event | Wake source/type | coalesce key | 默认 channel hint |
|
|
40
|
+
| --- | --- | --- | --- |
|
|
41
|
+
| `room.mention.created` | `group_chat/mention` | `group:<room_id>` | `group:<room_id>` |
|
|
42
|
+
| `room.reply.created` | `group_chat/reply` | `group:<room_id>` | `group:<room_id>` |
|
|
43
|
+
| `room.message.created` | `group_chat/activity` | `group:<room_id>` | `group:<room_id>` |
|
|
44
|
+
| `room.message.tombstoned` | `group_chat/tombstone` | `group:<room_id>` | `group:<room_id>` |
|
|
45
|
+
| `room.membership.changed` | `group_chat/membership` | `group:<room_id>` | `default` |
|
|
46
|
+
|
|
47
|
+
映射只表达事件类别,不决定 immediate/scheduled/suppress。个人 policy 才拥有这个决定。
|
|
48
|
+
|
|
49
|
+
## WakeEvent input 示例
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"schema_version": 1,
|
|
54
|
+
"occurred_at": "2026-08-26T03:21:00Z",
|
|
55
|
+
"type": "mention",
|
|
56
|
+
"dedupe_key": "event:gev_01...",
|
|
57
|
+
"coalesce_key": "group:room_home",
|
|
58
|
+
"priority_hint": "high",
|
|
59
|
+
"attention_channel_hint": "group:room_home",
|
|
60
|
+
"actor_ref": "group-member:member-b",
|
|
61
|
+
"resource": {
|
|
62
|
+
"uri": "group-chat://room_home/messages/msg_01...",
|
|
63
|
+
"cursor": "184"
|
|
64
|
+
},
|
|
65
|
+
"metadata": {
|
|
66
|
+
"room_id": "room_home",
|
|
67
|
+
"group_event_cursor": 955,
|
|
68
|
+
"group_schema_version": 1,
|
|
69
|
+
"direct": true
|
|
70
|
+
},
|
|
71
|
+
"payload_preview": null
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`priority_hint=high` 只是 adapter 的保守提示,不能绕过个人 policy 或 quiet-hours rule。
|
|
76
|
+
|
|
77
|
+
## Cursor 与事务
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
read group events after local committed cursor
|
|
81
|
+
→ validate every event schema/audience
|
|
82
|
+
→ map to SourceDeliveries
|
|
83
|
+
→ durable insert/dedupe all WakeEvents in the page
|
|
84
|
+
→ revision-CAS the opaque group event cursor
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
进程在 event commit 后、cursor CAS 前崩溃会重读同一页;stable ids 使其幂等。adapter 不向 Group Chat 写 consumer ack,也不推进任何 member last-seen cursor。
|
|
88
|
+
|
|
89
|
+
## 当前证据与集成责任
|
|
90
|
+
|
|
91
|
+
仓内 fixture 验证五类 event 的 v1 schema fence、mention/reply/activity mapping、resource reference、opaque cursor、
|
|
92
|
+
重复页拒绝,并确认 Core 没有 Group Chat 专用分支。Connector tests 另行验证 from-now bootstrap、credential-bound
|
|
93
|
+
identity、cursor catch-up、重复读取幂等和正文不进入 Wake Bridge ledger。
|
|
94
|
+
|
|
95
|
+
接入真实 Group Chat provider 时,维护方仍须验证 provider schema/version、principal scopes、credential revoke、
|
|
96
|
+
offline restart/catch-up、读取副作用和真实 Host Adapter 投递。这些现场能力不由 reference fixture 自动保证。
|
|
97
|
+
|
|
98
|
+
## 醒来后的读取
|
|
99
|
+
|
|
100
|
+
Wake payload 只携带 resource reference。agent 醒来后使用 Group Chat 自己的 MCP:
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
group.get_context / group.get_message
|
|
104
|
+
→ understand authoritative room history
|
|
105
|
+
→ group.mark_seen(actual message cursor)
|
|
106
|
+
→ optional group.post_message
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Wake Bridge 的 `agent_seen` 与 Group Chat 的 `member last_seen` 是不同事实。
|
|
110
|
+
|
|
111
|
+
## 失败语义
|
|
112
|
+
|
|
113
|
+
- Group Chat offline:adapter 记录 source unavailable,保留本地 cursor;其他 sources 继续。
|
|
114
|
+
- schema version unsupported:停止该 source 并 needs_attention,不丢弃/猜测字段。
|
|
115
|
+
- credential revoked:fail closed,不降级为匿名读取。
|
|
116
|
+
- event resource tombstoned:仍可消费 tombstone/audit metadata,不要求正文存在。
|
|
117
|
+
- Wake Bridge offline:Group Chat 正常运行,event log 等待稍后 catch up。
|
|
118
|
+
|
|
119
|
+
## 独立性验收
|
|
120
|
+
|
|
121
|
+
1. Group Chat 仓库不引用 Wake Bridge package、schema 或运行地址。
|
|
122
|
+
2. adapter 可以只靠公开 event-feed fixture 完成 contract tests。
|
|
123
|
+
3. 删除本 adapter,Wake Bridge 的 manual/timer/其他 sources 继续通过。
|
|
124
|
+
4. 停止 Wake Bridge,Group Chat UI/API/MCP/event feed 继续通过自身测试。
|
|
125
|
+
5. Wake Bridge core 不出现 Group Chat 专用 schema、数据库列或调度分支。
|
|
126
|
+
6. 一个只实现公开 source fixtures 的第二 adapter 能通过同一 conformance suite。
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Source Connector catalog
|
|
2
|
+
|
|
3
|
+
状态:current public catalog(2026-09-07)。Catalog 是可发现性清单,不是自动安装器;所有 connector 默认
|
|
4
|
+
`disabled`,未配置时不会登录 provider、创建 checkpoint、轮询或产生 wake。
|
|
5
|
+
|
|
6
|
+
查看机器可读清单:
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
wakebridge connector-catalog
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 当前条目
|
|
13
|
+
|
|
14
|
+
| Connector | 类型 | 可用性 | Bundled | Enable |
|
|
15
|
+
| --- | --- | --- | --- | --- |
|
|
16
|
+
| 小机知道 | official optional | available | yes | 完成 configure → verify identity → from-now bootstrap → UAT 后可显式 enable |
|
|
17
|
+
| Gmail | official optional | planned | no | 不可 enable;见 [planned status](../adapters/gmail-planned.md) |
|
|
18
|
+
| Group Chat fixture | reference | available | yes | 仅用于 contract/conformance 与自建来源参考 |
|
|
19
|
+
|
|
20
|
+
Catalog 的 `availability` 描述发行物能力;某个已配置 source 的 `disabled | enabled | needs_attention` 是运行状态,
|
|
21
|
+
两者不可混用。`available` 也不等于已经登录、已启用或生产 SLA。
|
|
22
|
+
|
|
23
|
+
## 小机知道:首次启用
|
|
24
|
+
|
|
25
|
+
1. 按 [小机知道 Connector](../adapters/botlingknows.md) 启动独立 connector process,并让 provider credential
|
|
26
|
+
只留在该 process。
|
|
27
|
+
2. 将 source 写入私有 `sources.json`,但保持 disabled。
|
|
28
|
+
3. 运行 `attention_source_verify`,由 agent/operator 核对返回的 `subject_ref` 与 `binding_fingerprint`。
|
|
29
|
+
4. 显式执行 `attention_source_bootstrap(mode="from-now")`,避免把历史通知当作新事件。
|
|
30
|
+
5. 用一条专用测试通知验收 cursor、restart、`mark_read=false`、event/claim/wake 与 authoritative resource。
|
|
31
|
+
6. UAT 通过后执行 `attention_source_enable`。失败时保持 disabled 或 `needs_attention`,不要盲目重试登录。
|
|
32
|
+
|
|
33
|
+
命令行环境可用 `source-validate`、`source-bootstrap` 与 `source-once` 完成相同的低层验证。Connector 不携带默认
|
|
34
|
+
唤醒策略;用户需单独安装 policy。仓库提供的
|
|
35
|
+
[`botlingknows-conservative.json`](../../examples/policies/botlingknows-conservative.json) 只是可选 recipe:@mention
|
|
36
|
+
即时但尊重 quiet hours/presence,其余通知排到本地 09:00。安装前应按自己的 attention channel 与节奏修改并 preview。
|
|
37
|
+
这样同一条通知可以由不同 Agent Space 按各自节奏处理。
|
|
38
|
+
|
|
39
|
+
当前 catalog 刻意不包含尚未沟通和验收的第三方来源。添加条目需要先明确公开接口、认证与权限、读取副作用、
|
|
40
|
+
cursor/replay、速率限制、维护责任和真实 UAT,不因“能抓到数据”就自动成为 official connector。
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Operator status and dead-letter recovery
|
|
2
|
+
|
|
3
|
+
状态:introduced in `wake-bridge@0.9.0-preview.3`; current in `0.9.0-preview.8`。
|
|
4
|
+
|
|
5
|
+
这组命令面向单 Agent Space 的 operator,不属于第三方 SDK,也不进入 agent MCP。它只补运行态定位与永久投递失败的受控
|
|
6
|
+
恢复,不替 agent 判断来源工作是否仍需处理。
|
|
7
|
+
|
|
8
|
+
## Secret-free status
|
|
9
|
+
|
|
10
|
+
停止或无法访问 daemon 时,可以直接读取 durable DB:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
wakebridge status --config /absolute/path/wakebridge.config.json
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
这个路径以 observer mode 打开 Core,不回收 expired dispatch lease,也不运行 scheduler。它汇总:
|
|
17
|
+
|
|
18
|
+
- 各 batch state 数量、当前 due 数、expired dispatch leases;
|
|
19
|
+
- failed attempt 的 `error_class` 计数,以及最多 100 条 dead-letter / needs-attention 摘要;
|
|
20
|
+
- endpoint/binding 的 live/stale 数量与 channel generation;
|
|
21
|
+
- durable source checkpoint/control 摘要。
|
|
22
|
+
|
|
23
|
+
离线读取无法观察另一个 daemon 进程内存中的 source supervisor,因此返回 `sources.runtime_observed=false`。运行中应优先请求
|
|
24
|
+
owner-authenticated live endpoint:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
curl --fail-with-body \
|
|
28
|
+
-H "Authorization: Bearer ${WAKEBRIDGE_ADMIN_TOKEN}" \
|
|
29
|
+
http://127.0.0.1:4311/v1/status
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Live status 会加入 source 的 `healthy | backoff | needs_attention | stopped` 状态与已启动 Host Adapter 摘要。Source/host
|
|
33
|
+
credential 不能调用该 endpoint。
|
|
34
|
+
|
|
35
|
+
`health` 语义:
|
|
36
|
+
|
|
37
|
+
- `healthy`:没有当前 queue/source/host degradation;
|
|
38
|
+
- `degraded`:存在可自动恢复的 retry/backoff,或 batch 正等待 endpoint;
|
|
39
|
+
- `needs_attention`:存在 dead letter、needs-attention batch、expired dispatch lease 或 source needs-attention。
|
|
40
|
+
|
|
41
|
+
`ok=false` 与 CLI exit code `2` 只对应 `needs_attention`;`degraded` 仍返回 exit code `0`,但应由监控展示。Status 不返回 route
|
|
42
|
+
address、lease token、session ref、provider error message、source credential 或 owner token。它只保留安全的 error class;详细 provider
|
|
43
|
+
错误仍留在本地 DB/operator evidence 中,不进入结构化 health payload。
|
|
44
|
+
|
|
45
|
+
## Dead-letter retry
|
|
46
|
+
|
|
47
|
+
先读取 dead letter 的 `batch_id` 与当前 `attempt`,修复导致 permanent rejection 的 route、credential 或 provider configuration,
|
|
48
|
+
再显式重开:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
wakebridge batch-retry wb_... \
|
|
52
|
+
--config /absolute/path/wakebridge.config.json \
|
|
53
|
+
--expected-attempt 1 \
|
|
54
|
+
--reason "operator confirmed route repair"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
运行中也可以使用 owner API:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
curl --fail-with-body \
|
|
61
|
+
-X POST http://127.0.0.1:4311/v1/batches/wb_.../retry \
|
|
62
|
+
-H "Authorization: Bearer ${WAKEBRIDGE_ADMIN_TOKEN}" \
|
|
63
|
+
-H "Content-Type: application/json" \
|
|
64
|
+
-d '{"expected_attempt":1,"reason":"operator confirmed route repair"}'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Contract:
|
|
68
|
+
|
|
69
|
+
1. 只接受 `dead_letter`;`needs_attention`、等待 endpoint、已 dispatched/seen 的 batch 不可重开;
|
|
70
|
+
2. `expected_attempt` 是 CAS fence,防止旧命令重开更新后的失败 generation;
|
|
71
|
+
3. 所有 claim 必须仍为 `batched`;已经 consume/dismiss/finalize 的工作不会被复活;
|
|
72
|
+
4. 成功只把 batch 改为 `retry_wait` 并写 `dead_letter → retry_wait` transition,调用本身不 dispatch;
|
|
73
|
+
5. 相同 attempt 的响应丢失后重放返回 `retried=false`,不会重复写 transition;
|
|
74
|
+
6. 后续 canonical daemon scheduler 或 operator 显式 `dispatch` 才创建新 attempt;attempt number 会递增。
|
|
75
|
+
|
|
76
|
+
Reason 会作为本地 transition audit 保存;不要把 credential、正文或其他 secret 写进 reason。命令限制为 1–200 个可打印字符。
|
|
77
|
+
|
|
78
|
+
## 仍未覆盖的 operator hardening
|
|
79
|
+
|
|
80
|
+
本票没有承诺 metrics backend、长期 structured log pipeline、dead-letter bulk replay、任意 export、Linux/systemd、端口扫描或自动
|
|
81
|
+
backup drill。`doctor/release-preflight` 继续负责 runtime/schema/config permission readiness;backup/restore 流程见
|
|
82
|
+
[release lifecycle](release-lifecycle.md)。这些剩余项应按独立风险与验收继续推进,不能因有了 status 就宣称 production operations
|
|
83
|
+
全部完成。
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Out-of-process local Host Adapter protocol
|
|
2
|
+
|
|
3
|
+
状态:current public Host Adapter Contract。所有具体 host/product support 由 adapter 维护者按 host/version 独立声明;
|
|
4
|
+
Wake Bridge package 本身不内置产品支持。
|
|
5
|
+
|
|
6
|
+
这个协议面向已经有 backend agent session、bridge service 或自建 frontend 的宿主。外部 service 保管宿主身份与内部 credential,
|
|
7
|
+
Wake Bridge daemon 只知道一个 opaque session、声明的 capability 与精确 loopback delivery route。
|
|
8
|
+
|
|
9
|
+
它不是动态 plugin loader。Daemon 不 import 第三方代码,也不把 Core、DB、owner token 或 agent memory 交给 adapter。具体宿主实现仍是
|
|
10
|
+
仓库外 integration;通过该协议不等于该宿主进入 Wake Bridge supported-host matrix。
|
|
11
|
+
|
|
12
|
+
## 1. Operator 配置
|
|
13
|
+
|
|
14
|
+
Bootstrap token 只放环境变量:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
export WAKEBRIDGE_EXAMPLE_HOST_TOKEN="<至少 32 字符的随机 token>"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
reference file 只保存 env 名、固定 host identity、允许的 channel 与诚实 capability:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"version": 1,
|
|
25
|
+
"adapters": [
|
|
26
|
+
{
|
|
27
|
+
"id": "example-host-local",
|
|
28
|
+
"adapter_kind": "example_host",
|
|
29
|
+
"adapter_version": "1.0.0",
|
|
30
|
+
"host_kind": "example_host",
|
|
31
|
+
"tested_host_versions": ["your-host/1.0"],
|
|
32
|
+
"token_env": "WAKEBRIDGE_EXAMPLE_HOST_TOKEN",
|
|
33
|
+
"attention_channels": ["life"],
|
|
34
|
+
"capabilities": {
|
|
35
|
+
"warm_resume": true,
|
|
36
|
+
"cold_push": false,
|
|
37
|
+
"exact_live_route": true,
|
|
38
|
+
"requires_live_binding": true,
|
|
39
|
+
"queue_when_busy": true,
|
|
40
|
+
"session_activity_observable": true
|
|
41
|
+
},
|
|
42
|
+
"receipt_upper_bound": "host_accepted"
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
启动 daemon:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
wakebridge daemon \
|
|
52
|
+
--config /absolute/path/wakebridge.config.json \
|
|
53
|
+
--host-adapters /absolute/path/host-adapters.json \
|
|
54
|
+
--host 127.0.0.1 --port 4311
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
启动前可以用同一 env/file 运行 `wakebridge doctor --config ... --host-adapters ...`。Doctor 只输出 adapter kind/version、host kinds、
|
|
58
|
+
experimental tier 与错误摘要,不打印 token;缺失 env、无效 capability 或重复 kind 会令 readiness fail。
|
|
59
|
+
|
|
60
|
+
当前文件只允许 `experimental` out-of-process adapters。`receipt_upper_bound` 只能是 `accepted_to_live_pipe` 或
|
|
61
|
+
`host_accepted`;HTTP 202 不能声明 `agent_completed`。每个 adapter kind 与 credential id 必须唯一。
|
|
62
|
+
|
|
63
|
+
## 2. 外部 service 生命周期
|
|
64
|
+
|
|
65
|
+
仓库外 service 从 `wake-bridge/transport` 导入 `HostSessionClient`。它在确认自己的精确 session 后启动一个 credential-free
|
|
66
|
+
loopback HTTP origin,并为该进程生成独立 route token:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { HostSessionClient } from "wake-bridge/transport";
|
|
70
|
+
|
|
71
|
+
const client = new HostSessionClient({
|
|
72
|
+
base_url: "http://127.0.0.1:4311",
|
|
73
|
+
host_token: process.env.WAKEBRIDGE_EXAMPLE_HOST_TOKEN!,
|
|
74
|
+
adapter_kind: "example_host",
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const registration = await client.open({
|
|
78
|
+
session_ref: "opaque-session-id",
|
|
79
|
+
attention_channel: "life",
|
|
80
|
+
route_origin: "http://127.0.0.1:18790",
|
|
81
|
+
route_token: process.env.EXAMPLE_HOST_ROUTE_TOKEN!,
|
|
82
|
+
});
|
|
83
|
+
const lease = client.lease(registration);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Service 周期性 `renew(lease)`,正常退出时 `close(lease)`。重新 open 同一 session 会执行显式 takeover 并递增 binding generation;旧
|
|
87
|
+
lease、旧 activity 与旧 delivery correlation 随后 fail closed。Route token 不得复用 owner、任一 host bootstrap 或 source token。
|
|
88
|
+
|
|
89
|
+
`HostSessionClient` 只有 open/renew/close/presence/activity/wake-echo 方法,不能 inspect、emit、配置 policy、dispatch 或执行 operator retry。
|
|
90
|
+
|
|
91
|
+
## 3. Delivery endpoint
|
|
92
|
+
|
|
93
|
+
外部 service 实现:
|
|
94
|
+
|
|
95
|
+
```text
|
|
96
|
+
POST /v1/wakes
|
|
97
|
+
Authorization: Bearer <per-process route token>
|
|
98
|
+
X-WakeBridge-Delivery-Nonce: <same nonce as body>
|
|
99
|
+
Content-Type: application/json
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Body 使用 `LOCAL_HOST_PROTOCOL_VERSION=1`:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"protocol_version": 1,
|
|
107
|
+
"attempt_id": "att_...",
|
|
108
|
+
"delivery_nonce": "opaque-one-shot-nonce",
|
|
109
|
+
"wake": {
|
|
110
|
+
"schema_version": 1,
|
|
111
|
+
"instance_id": "agent-a",
|
|
112
|
+
"wake_batch_id": "wb_...",
|
|
113
|
+
"attention_channel": "life",
|
|
114
|
+
"claim_refs": [],
|
|
115
|
+
"binding_generation": 3
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
使用 `validateLocalHostDeliveryRequest()` 验证 body,并另行比较 nonce header。Wake payload 只有 durable reference 与有限 metadata,
|
|
121
|
+
不是 prompt injection channel,也不复制来源正文。
|
|
122
|
+
|
|
123
|
+
响应语义固定:
|
|
124
|
+
|
|
125
|
+
- `202`:adapter 声明的 pipe/host 已接收;Core 只写 `transport_accepted`;
|
|
126
|
+
- `408 | 425 | 429 | 5xx`:retryable;
|
|
127
|
+
- 其他状态:permanent rejection,最终可进入 dead letter;
|
|
128
|
+
- response body 被忽略,不进入 receipt、status 或 error message。
|
|
129
|
+
|
|
130
|
+
Transport 使用有界 timeout、拒绝 redirects,只连接 `127.0.0.1` 或 `::1`。公网 URL、带 userinfo/path/query 的 origin 均在登记前
|
|
131
|
+
拒绝。
|
|
132
|
+
|
|
133
|
+
## 4. Activity 与更高层 receipt
|
|
134
|
+
|
|
135
|
+
若 manifest 声明 `foreground_presence_observable`,adapter 必须只在真实用户 active edge 上调用
|
|
136
|
+
`renewPresence(lease, { observed_by, observation, ttl_ms? })`。普通模型 activity、进程存活、窗口 focus 与 Wake echo 都不能续
|
|
137
|
+
Foreground Presence Lease;Core 会同时核对 capability、endpoint token 与 current binding generation,未声明 observer 的 host
|
|
138
|
+
调用会 fail closed。
|
|
139
|
+
|
|
140
|
+
若 manifest 声明 `session_activity_observable`,外部 service 可以调用 `observeActivity()` 上报真实用户/runner activity,也可以在确认
|
|
141
|
+
delivery nonce 确实进入相同 generation 后调用 `consumeWakeEcho()`。Wake echo 只建立可信 `wake_started` activity,不冒充
|
|
142
|
+
`agent_seen`;batch 的 seen/consumed/acted 仍需 agent/harness 通过既有明确 acknowledgement 完成。
|
|
143
|
+
|
|
144
|
+
## 5. 支持边界
|
|
145
|
+
|
|
146
|
+
这项 contract 使自建 backend、CLI harness、frontend 或私有 runtime 可以独立实现 integration。它不使任何具体 runtime
|
|
147
|
+
自动成为 Wake Bridge 支持对象。Core 不增加产品私有字段、不扫描 conversation、不读取 runtime DB、不选择最近窗口,也不
|
|
148
|
+
承诺换窗后的 agent memory continuity。具体 adapter 必须用自己的真实 host/version 做 conformance 与 field canary。
|
|
149
|
+
|
|
150
|
+
可运行的中立示例见 [Reference Host Adapter](../../examples/reference-host-adapter/README.md)。
|