@autono/pinbox-core 0.1.0

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.
Files changed (48) hide show
  1. package/README.md +17 -0
  2. package/dist/auth/verify.d.ts +15 -0
  3. package/dist/auth/verify.js +2 -0
  4. package/dist/connectors/github.d.ts +5 -0
  5. package/dist/connectors/github.js +49 -0
  6. package/dist/connectors/index.d.ts +37 -0
  7. package/dist/connectors/index.js +103 -0
  8. package/dist/context-BHcEpzVb.js +30 -0
  9. package/dist/delivery/openclaw.d.ts +10 -0
  10. package/dist/delivery/openclaw.js +50 -0
  11. package/dist/delivery/resume.d.ts +23 -0
  12. package/dist/delivery/resume.js +102 -0
  13. package/dist/delivery/router.d.ts +2 -0
  14. package/dist/delivery/router.js +262 -0
  15. package/dist/delivery/webhook.d.ts +9 -0
  16. package/dist/delivery/webhook.js +52 -0
  17. package/dist/do.d.ts +67 -0
  18. package/dist/do.js +713 -0
  19. package/dist/hub-QYz6OqYQ.js +328 -0
  20. package/dist/hub-server.d.ts +44 -0
  21. package/dist/hub-server.js +251 -0
  22. package/dist/hub.d.ts +125 -0
  23. package/dist/hub.js +2 -0
  24. package/dist/markdown.d.ts +14 -0
  25. package/dist/markdown.js +87 -0
  26. package/dist/payload-m1DbRWDD.d.ts +6 -0
  27. package/dist/poll-BrcAuaAz.js +252 -0
  28. package/dist/proc-BMp_pbPS.js +42 -0
  29. package/dist/router-D3dDjIaD.d.ts +62 -0
  30. package/dist/schema-BOTmn5SM.d.ts +297 -0
  31. package/dist/schema.d.ts +2 -0
  32. package/dist/schema.js +121 -0
  33. package/dist/schema.json +403 -0
  34. package/dist/sessions-CJdMBH3C.d.ts +47 -0
  35. package/dist/sessions-DrCVTMfI.js +129 -0
  36. package/dist/sessions.d.ts +2 -0
  37. package/dist/sessions.js +2 -0
  38. package/dist/store-DHbwWu93.d.ts +105 -0
  39. package/dist/store-DM8MjB8M.js +479 -0
  40. package/dist/store.d.ts +2 -0
  41. package/dist/store.js +3 -0
  42. package/dist/types-BLNQb7MH.d.ts +28 -0
  43. package/dist/verify-BDt9d9Np.js +55 -0
  44. package/dist/ws-protocol.d.ts +61 -0
  45. package/dist/ws-protocol.js +51 -0
  46. package/dist/ws.d.ts +7 -0
  47. package/dist/ws.js +1 -0
  48. package/package.json +104 -0
@@ -0,0 +1,262 @@
1
+ import { PinSchema, ThreadMessageSchema } from "../schema.js";
2
+ import { n as buildReplyPrompt, t as buildInjectionContext } from "../context-BHcEpzVb.js";
3
+ //#region src/delivery/hooks.ts
4
+ /** Agents whose hook systems can register + pull (research §1: shared hooks schema). */
5
+ const HOOK_CAPABLE_AGENTS = /* @__PURE__ */ new Set([
6
+ "claude",
7
+ "codex",
8
+ "hermes"
9
+ ]);
10
+ function createHooksAdapter() {
11
+ return {
12
+ name: "hooks",
13
+ matches(session) {
14
+ return session.endedAt === void 0 && HOOK_CAPABLE_AGENTS.has(session.agent);
15
+ },
16
+ async deliver() {}
17
+ };
18
+ }
19
+ //#endregion
20
+ //#region src/delivery/router.ts
21
+ const PULL_ADAPTERS = /* @__PURE__ */ new Set(["hooks"]);
22
+ const NO_ADAPTER = "none";
23
+ const DEFAULT_HOOKS_ESCALATE_MS = 6e5;
24
+ /** Escalation window for pull rows: PINBOX_HOOKS_ESCALATE_MS, default 10 min. */
25
+ function hooksEscalateMs() {
26
+ const raw = typeof process === "undefined" ? void 0 : process.env["PINBOX_HOOKS_ESCALATE_MS"];
27
+ if (raw === void 0) return DEFAULT_HOOKS_ESCALATE_MS;
28
+ const parsed = Number.parseInt(raw, 10);
29
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_HOOKS_ESCALATE_MS;
30
+ }
31
+ const BACKOFF_BASE_MS = 3e4;
32
+ const BACKOFF_FACTOR = 4;
33
+ const BACKOFF_CAP_MS = 9e5;
34
+ var DeliveryRouter = class {
35
+ store;
36
+ adapters;
37
+ chain = Promise.resolve();
38
+ constructor(opts) {
39
+ this.store = opts.store;
40
+ this.adapters = [...opts.adapters];
41
+ }
42
+ /** THE single entry point; never throws — failures land in the queue. */
43
+ dispatch(event) {
44
+ return this.enqueueWork(() => this.dispatchOne(event));
45
+ }
46
+ /**
47
+ * Runs on hub wake + a coarse unref'd interval: (a) boot/cursor reconciliation,
48
+ * (b) retry/escalate due pending rows, (c) assign unassigned rows to the active
49
+ * session. Never rejects.
50
+ */
51
+ drainDue(now) {
52
+ return this.enqueueWork(() => this.drainNow(now ?? (/* @__PURE__ */ new Date()).toISOString()));
53
+ }
54
+ enqueueWork(work) {
55
+ const run = this.chain.then(work).catch(() => {});
56
+ this.chain = run;
57
+ return run;
58
+ }
59
+ async dispatchOne(event) {
60
+ if (event.seq <= this.store.deliveries.lastEventSeq()) return;
61
+ let target;
62
+ try {
63
+ target = this.route(event);
64
+ } catch {
65
+ target = { kind: "skip" };
66
+ }
67
+ if (target.kind === "skip") {
68
+ this.store.deliveries.enqueue({
69
+ eventSeq: event.seq,
70
+ sessionId: null,
71
+ adapter: NO_ADAPTER,
72
+ dueAt: null,
73
+ status: "skipped"
74
+ });
75
+ return;
76
+ }
77
+ if (target.kind === "unassigned") {
78
+ this.store.deliveries.enqueue({
79
+ eventSeq: event.seq,
80
+ sessionId: null,
81
+ adapter: NO_ADAPTER,
82
+ dueAt: null
83
+ });
84
+ return;
85
+ }
86
+ await this.attemptNew(event, target.session, /* @__PURE__ */ new Date());
87
+ }
88
+ /**
89
+ * Deliverable events are pin.created and thread.message with role human|mirror
90
+ * (rule 3: agent-authored events are never delivered back). Everything else —
91
+ * pin.resolved, and later event types — is skipped-by-design.
92
+ */
93
+ route(event) {
94
+ if (event.type === "pin.created") {
95
+ const pin = PinSchema.parse(event.payload);
96
+ return this.bindTarget(pin);
97
+ }
98
+ if (event.type === "thread.message") {
99
+ const message = ThreadMessageSchema.parse(event.payload);
100
+ if (message.role === "agent") return { kind: "skip" };
101
+ const pin = this.store.getPin(message.pinId);
102
+ if (pin === null) return { kind: "skip" };
103
+ return this.bindTarget(pin);
104
+ }
105
+ return { kind: "skip" };
106
+ }
107
+ bindTarget(pin) {
108
+ const { sessions } = this.store;
109
+ if (pin.agentSession !== void 0) return {
110
+ kind: "session",
111
+ session: sessions.findByRef(pin.agentSession) ?? sessions.register(pin.agentSession)
112
+ };
113
+ const active = sessions.active();
114
+ if (active === null) return { kind: "unassigned" };
115
+ this.store.bindSession(pin.id, sessionRefOf(active));
116
+ return {
117
+ kind: "session",
118
+ session: active
119
+ };
120
+ }
121
+ async attemptNew(event, session, now) {
122
+ const { deliveries } = this.store;
123
+ const adapter = await this.selectAdapter(this.adapters, session);
124
+ if (adapter === null) {
125
+ deliveries.enqueue({
126
+ eventSeq: event.seq,
127
+ sessionId: session.id,
128
+ adapter: NO_ADAPTER,
129
+ dueAt: null
130
+ });
131
+ return;
132
+ }
133
+ if (PULL_ADAPTERS.has(adapter.name)) {
134
+ const dueAt = new Date(now.getTime() + hooksEscalateMs()).toISOString();
135
+ const row = deliveries.enqueue({
136
+ eventSeq: event.seq,
137
+ sessionId: session.id,
138
+ adapter: adapter.name,
139
+ dueAt
140
+ });
141
+ try {
142
+ await adapter.deliver(event, session);
143
+ } catch (cause) {
144
+ this.recordFailure(row, cause, now);
145
+ }
146
+ return;
147
+ }
148
+ const row = deliveries.enqueue({
149
+ eventSeq: event.seq,
150
+ sessionId: session.id,
151
+ adapter: adapter.name,
152
+ dueAt: null
153
+ });
154
+ try {
155
+ await adapter.deliver(event, session);
156
+ deliveries.markDelivered(row.id);
157
+ } catch (cause) {
158
+ this.recordFailure(row, cause, now);
159
+ }
160
+ }
161
+ async drainNow(now) {
162
+ const { store } = this;
163
+ for (const event of store.eventsAfter(store.deliveries.lastEventSeq())) await this.dispatchOne(event);
164
+ for (const row of store.deliveries.due(now)) {
165
+ if (row.sessionId === null) continue;
166
+ try {
167
+ await this.retryRow(row, now);
168
+ } catch {}
169
+ }
170
+ const active = store.sessions.active();
171
+ if (active !== null) for (const row of store.deliveries.unassigned()) try {
172
+ await this.claimRow(row, active, now);
173
+ } catch {}
174
+ }
175
+ async retryRow(row, now) {
176
+ const { deliveries, sessions } = this.store;
177
+ const event = this.eventOf(row);
178
+ if (event === null) {
179
+ deliveries.markFailed(row.id, `E_DELIVERY: event ${row.eventSeq} missing from log`, null);
180
+ return;
181
+ }
182
+ const session = row.sessionId === null ? null : sessions.get(row.sessionId);
183
+ if (session === null) {
184
+ deliveries.markFailed(row.id, `E_SESSION_GONE: session ${row.sessionId} unknown`, null);
185
+ return;
186
+ }
187
+ const adapter = await this.selectAdapter(this.rotatedAfter(row.adapter), session);
188
+ if (adapter === null) return;
189
+ if (PULL_ADAPTERS.has(adapter.name)) return;
190
+ try {
191
+ await adapter.deliver(event, session);
192
+ deliveries.markDelivered(row.id);
193
+ } catch (cause) {
194
+ this.recordFailure(row, cause, new Date(Date.parse(now)));
195
+ }
196
+ }
197
+ async claimRow(row, active, now) {
198
+ const event = this.eventOf(row);
199
+ if (event === null) {
200
+ this.store.deliveries.markFailed(row.id, `E_DELIVERY: event ${row.eventSeq} missing from log`, null);
201
+ return;
202
+ }
203
+ this.store.deliveries.assign(row.id, active.id);
204
+ const pinId = pinIdOfEvent(event);
205
+ if (pinId !== null) try {
206
+ this.store.bindSession(pinId, sessionRefOf(active));
207
+ } catch {}
208
+ await this.retryRow({
209
+ ...row,
210
+ sessionId: active.id
211
+ }, now);
212
+ }
213
+ async selectAdapter(candidates, session) {
214
+ for (const adapter of candidates) try {
215
+ if (await adapter.matches(session)) return adapter;
216
+ } catch {}
217
+ return null;
218
+ }
219
+ rotatedAfter(name) {
220
+ const index = this.adapters.findIndex((adapter) => adapter.name === name);
221
+ if (index < 0) return [...this.adapters];
222
+ return [...this.adapters.slice(index + 1), ...this.adapters.slice(0, index + 1)];
223
+ }
224
+ recordFailure(row, cause, now) {
225
+ const message = cause instanceof Error ? cause.message : String(cause);
226
+ const sessionGone = message.startsWith("E_SESSION_GONE");
227
+ const attempts = row.attempts;
228
+ if (sessionGone || attempts >= 4) {
229
+ const lastError = sessionGone ? message : `E_DELIVERY: ${message}`;
230
+ this.store.deliveries.markFailed(row.id, lastError, null);
231
+ return;
232
+ }
233
+ const backoffMs = Math.min(BACKOFF_BASE_MS * BACKOFF_FACTOR ** attempts, BACKOFF_CAP_MS);
234
+ const retryAt = new Date(now.getTime() + backoffMs).toISOString();
235
+ this.store.deliveries.markFailed(row.id, message, retryAt);
236
+ }
237
+ eventOf(row) {
238
+ const event = this.store.eventsAfter(row.eventSeq - 1).at(0);
239
+ return event !== void 0 && event.seq === row.eventSeq ? event : null;
240
+ }
241
+ };
242
+ function sessionRefOf(session) {
243
+ return {
244
+ agent: session.agent,
245
+ key: session.key,
246
+ ...session.cwd !== void 0 ? { cwd: session.cwd } : {}
247
+ };
248
+ }
249
+ /** pin.created payloads are the Pin (`id`); thread.message payloads carry `pinId`. */
250
+ function pinIdOfEvent(event) {
251
+ if (event.type === "pin.created") {
252
+ const parsed = PinSchema.safeParse(event.payload);
253
+ return parsed.success ? parsed.data.id : null;
254
+ }
255
+ if (event.type === "thread.message") {
256
+ const parsed = ThreadMessageSchema.safeParse(event.payload);
257
+ return parsed.success ? parsed.data.pinId : null;
258
+ }
259
+ return null;
260
+ }
261
+ //#endregion
262
+ export { DEFAULT_HOOKS_ESCALATE_MS, DeliveryRouter, HOOK_CAPABLE_AGENTS, buildInjectionContext, buildReplyPrompt, createHooksAdapter, hooksEscalateMs };
@@ -0,0 +1,9 @@
1
+ import { n as DeliveryAdapter } from "../router-D3dDjIaD.js";
2
+ //#region src/delivery/webhook.d.ts
3
+ type WebhookConfig = {
4
+ url: string;
5
+ secret: string;
6
+ };
7
+ declare function createWebhookAdapter(config: WebhookConfig): DeliveryAdapter;
8
+ //#endregion
9
+ export { WebhookConfig, createWebhookAdapter };
@@ -0,0 +1,52 @@
1
+ //#region src/delivery/webhook.ts
2
+ function createWebhookAdapter(config) {
3
+ return {
4
+ name: "webhook",
5
+ matches() {
6
+ return true;
7
+ },
8
+ async deliver(event, session) {
9
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
10
+ const body = JSON.stringify({
11
+ event,
12
+ session: {
13
+ id: session.id,
14
+ agent: session.agent,
15
+ key: session.key
16
+ }
17
+ });
18
+ const signature = await hmacHex(config.secret, `${timestamp}.${body}`);
19
+ let response;
20
+ try {
21
+ response = await fetch(config.url, {
22
+ method: "POST",
23
+ headers: {
24
+ "content-type": "application/json",
25
+ "x-pinbox-timestamp": timestamp,
26
+ "x-pinbox-event": event.type,
27
+ "x-pinbox-signature": `sha256=${signature}`
28
+ },
29
+ body
30
+ });
31
+ } catch (cause) {
32
+ const detail = cause instanceof Error ? cause.message : String(cause);
33
+ throw new Error(`webhook POST ${config.url} failed: ${detail}`);
34
+ }
35
+ if (!response.ok) {
36
+ await response.arrayBuffer().catch(() => void 0);
37
+ throw new Error(`webhook POST ${config.url} returned ${response.status}`);
38
+ }
39
+ }
40
+ };
41
+ }
42
+ async function hmacHex(secret, payload) {
43
+ const encoder = new TextEncoder();
44
+ const key = await crypto.subtle.importKey("raw", encoder.encode(secret), {
45
+ name: "HMAC",
46
+ hash: "SHA-256"
47
+ }, false, ["sign"]);
48
+ const bytes = new Uint8Array(await crypto.subtle.sign("HMAC", key, encoder.encode(payload)));
49
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
50
+ }
51
+ //#endregion
52
+ export { createWebhookAdapter };
package/dist/do.d.ts ADDED
@@ -0,0 +1,67 @@
1
+ import { a as Pin, f as SessionRef, m as ThreadMessage, r as Link, t as Attachment } from "./schema-BOTmn5SM.js";
2
+ import { r as SessionStore } from "./sessions-CJdMBH3C.js";
3
+ import { a as LinkStore, c as PinStore, i as DeliveryStore, l as StoredEvent, t as CursorStore } from "./store-DHbwWu93.js";
4
+ import { Broadcaster } from "./ws.js";
5
+ import { DurableObjectState, R2Bucket, WebSocket } from "@cloudflare/workers-types";
6
+ //#region src/do-store.d.ts
7
+ interface DoPinStore extends PinStore {
8
+ addThreadMessage(pinId: string, role: "human" | "agent" | "mirror", text: string, opts?: {
9
+ origin?: string;
10
+ attachments?: Attachment[];
11
+ }): ThreadMessage;
12
+ /** @throws NotFoundError @throws ConflictError — trailing commit param is additive */
13
+ resolvePin(id: string, by: "human" | "agent", note?: string, commit?: string): Pin;
14
+ subscribe(listener: (event: StoredEvent) => void): () => void;
15
+ readonly sessions: SessionStore;
16
+ readonly deliveries: DeliveryStore;
17
+ /** Persists the routing binding into pin.agentSession. No event. @throws NotFoundError */
18
+ bindSession(pinId: string, ref: SessionRef): Pin;
19
+ verifyPin(id: string, outcome: "accepted" | "reopened"): Pin;
20
+ searchPins(query: string): Pin[];
21
+ readonly cursors: CursorStore;
22
+ addLink(pinId: string, link: Link): Pin;
23
+ readonly links: LinkStore;
24
+ }
25
+ //#endregion
26
+ //#region src/do.d.ts
27
+ type PinboxDoEnv = {
28
+ PINBOX_TOKEN?: string;
29
+ AUTH_STRATEGY?: "none" | "token" | "jwt";
30
+ ALLOW_UNAUTHENTICATED?: string;
31
+ JWT_ISSUER?: string;
32
+ JWT_JWKS_URL?: string;
33
+ JWT_AUDIENCE?: string;
34
+ MEDIA?: R2Bucket;
35
+ R2_ACCOUNT_ID?: string;
36
+ R2_BUCKET?: string;
37
+ R2_ACCESS_KEY_ID?: string;
38
+ R2_SECRET_ACCESS_KEY?: string;
39
+ };
40
+ declare class DoBroadcaster implements Broadcaster {
41
+ private readonly state;
42
+ constructor(state: DurableObjectState);
43
+ publish(topic: string, data: string): void;
44
+ subscriberCount(topic: string): number;
45
+ }
46
+ declare class PinboxHubDO {
47
+ readonly store: DoPinStore;
48
+ readonly broadcaster: DoBroadcaster;
49
+ readonly topic: string;
50
+ private readonly ctx;
51
+ private readonly env;
52
+ private readonly strategy;
53
+ private readonly handler;
54
+ constructor(ctx: DurableObjectState, env: PinboxDoEnv);
55
+ fetch(req: Request): Promise<Response>;
56
+ private intercept;
57
+ webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): void;
58
+ webSocketClose(_ws: WebSocket, _code: number, _reason: string, _wasClean: boolean): void;
59
+ alarm(): Promise<void>;
60
+ private upgrade;
61
+ private protocolError;
62
+ private unauthorized;
63
+ private createAttachment;
64
+ private serveMedia;
65
+ }
66
+ //#endregion
67
+ export { DoBroadcaster, PinboxDoEnv, PinboxHubDO };