@autono/pinbox-core 0.1.0 → 0.8.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.
- package/dist/connectors/github.d.ts +1 -1
- package/dist/connectors/index.d.ts +3 -3
- package/dist/delivery/openclaw.d.ts +2 -2
- package/dist/delivery/resume.d.ts +2 -2
- package/dist/delivery/router.d.ts +1 -1
- package/dist/delivery/router.js +1 -260
- package/dist/delivery/webhook.d.ts +1 -1
- package/dist/do.d.ts +15 -3
- package/dist/do.js +54 -3
- package/dist/{hub-QYz6OqYQ.js → hub-I67BuX9S.js} +5 -3
- package/dist/hub-server.d.ts +2 -2
- package/dist/hub-server.js +1 -1
- package/dist/hub.d.ts +7 -2
- package/dist/hub.js +1 -1
- package/dist/markdown.d.ts +1 -1
- package/dist/payload-BLDLdUIh.d.ts +6 -0
- package/dist/router-B8EOto30.js +261 -0
- package/dist/{router-D3dDjIaD.d.ts → router-DBAEQNfs.d.ts} +3 -3
- package/dist/{schema-BOTmn5SM.d.ts → schema-HSZFobyd.d.ts} +31 -1
- package/dist/schema.d.ts +2 -2
- package/dist/schema.js +33 -2
- package/dist/schema.json +22 -0
- package/dist/{sessions-CJdMBH3C.d.ts → sessions-CHtzoBF6.d.ts} +1 -1
- package/dist/sessions.d.ts +1 -1
- package/dist/{store-DHbwWu93.d.ts → store-DPp8xMdp.d.ts} +3 -2
- package/dist/{store-DM8MjB8M.js → store-qtk4G3q4.js} +1 -0
- package/dist/store.d.ts +1 -1
- package/dist/store.js +1 -1
- package/dist/{types-BLNQb7MH.d.ts → types-Bu7YuW6a.d.ts} +1 -1
- package/dist/ws-protocol.d.ts +1 -1
- package/package.json +1 -1
- package/dist/payload-m1DbRWDD.d.ts +0 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as ConnectorTransport, t as Connector } from "../types-
|
|
1
|
+
import { r as ConnectorTransport, t as Connector } from "../types-Bu7YuW6a.js";
|
|
2
2
|
//#region src/connectors/github.d.ts
|
|
3
3
|
declare function createGithubConnector(transport: ConnectorTransport): Connector;
|
|
4
4
|
//#endregion
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as RemoteStatus, i as RemoteComment, n as ConnectorEvents, r as ConnectorTransport, t as Connector } from "../types-
|
|
3
|
-
import { c as PinStore } from "../store-
|
|
1
|
+
import { g as ThreadMessage } from "../schema-HSZFobyd.js";
|
|
2
|
+
import { a as RemoteStatus, i as RemoteComment, n as ConnectorEvents, r as ConnectorTransport, t as Connector } from "../types-Bu7YuW6a.js";
|
|
3
|
+
import { c as PinStore } from "../store-DPp8xMdp.js";
|
|
4
4
|
//#region src/connectors/mirror.d.ts
|
|
5
5
|
declare function createConnectorEvents(store: PinStore, pinId: string): ConnectorEvents;
|
|
6
6
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as GetPin } from "../payload-
|
|
2
|
-
import { n as DeliveryAdapter } from "../router-
|
|
1
|
+
import { t as GetPin } from "../payload-BLDLdUIh.js";
|
|
2
|
+
import { n as DeliveryAdapter } from "../router-DBAEQNfs.js";
|
|
3
3
|
//#region src/delivery/openclaw.d.ts
|
|
4
4
|
declare function createOpenclawAdapter(opts?: {
|
|
5
5
|
command?: string[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as GetPin } from "../payload-
|
|
2
|
-
import { n as DeliveryAdapter } from "../router-
|
|
1
|
+
import { t as GetPin } from "../payload-BLDLdUIh.js";
|
|
2
|
+
import { n as DeliveryAdapter } from "../router-DBAEQNfs.js";
|
|
3
3
|
//#region src/delivery/resume.d.ts
|
|
4
4
|
type ResumeCommand = (key: string, prompt: string) => string[];
|
|
5
5
|
declare const RESUME_COMMANDS: Record<string, ResumeCommand>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as HOOK_CAPABLE_AGENTS, c as buildReplyPrompt, i as hooksEscalateMs, n as DeliveryAdapter, o as createHooksAdapter, r as DeliveryRouter, s as buildInjectionContext, t as DEFAULT_HOOKS_ESCALATE_MS } from "../router-
|
|
1
|
+
import { a as HOOK_CAPABLE_AGENTS, c as buildReplyPrompt, i as hooksEscalateMs, n as DeliveryAdapter, o as createHooksAdapter, r as DeliveryRouter, s as buildInjectionContext, t as DEFAULT_HOOKS_ESCALATE_MS } from "../router-DBAEQNfs.js";
|
|
2
2
|
export { DEFAULT_HOOKS_ESCALATE_MS, DeliveryAdapter, DeliveryRouter, HOOK_CAPABLE_AGENTS, buildInjectionContext, buildReplyPrompt, createHooksAdapter, hooksEscalateMs };
|
package/dist/delivery/router.js
CHANGED
|
@@ -1,262 +1,3 @@
|
|
|
1
|
-
import { PinSchema, ThreadMessageSchema } from "../schema.js";
|
|
2
1
|
import { n as buildReplyPrompt, t as buildInjectionContext } from "../context-BHcEpzVb.js";
|
|
3
|
-
|
|
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
|
|
2
|
+
import { a as createHooksAdapter, i as HOOK_CAPABLE_AGENTS, n as DeliveryRouter, r as hooksEscalateMs, t as DEFAULT_HOOKS_ESCALATE_MS } from "../router-B8EOto30.js";
|
|
262
3
|
export { DEFAULT_HOOKS_ESCALATE_MS, DeliveryRouter, HOOK_CAPABLE_AGENTS, buildInjectionContext, buildReplyPrompt, createHooksAdapter, hooksEscalateMs };
|
package/dist/do.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { r as SessionStore } from "./sessions-
|
|
3
|
-
import { a as LinkStore, c as PinStore, i as DeliveryStore, l as StoredEvent, t as CursorStore } from "./store-
|
|
1
|
+
import { a as Link, g as ThreadMessage, m as SessionRef, r as Attachment, s as Pin, t as AppliedEdit } from "./schema-HSZFobyd.js";
|
|
2
|
+
import { r as SessionStore } from "./sessions-CHtzoBF6.js";
|
|
3
|
+
import { a as LinkStore, c as PinStore, i as DeliveryStore, l as StoredEvent, t as CursorStore } from "./store-DPp8xMdp.js";
|
|
4
4
|
import { Broadcaster } from "./ws.js";
|
|
5
5
|
import { DurableObjectState, R2Bucket, WebSocket } from "@cloudflare/workers-types";
|
|
6
6
|
//#region src/do-store.d.ts
|
|
@@ -8,6 +8,7 @@ interface DoPinStore extends PinStore {
|
|
|
8
8
|
addThreadMessage(pinId: string, role: "human" | "agent" | "mirror", text: string, opts?: {
|
|
9
9
|
origin?: string;
|
|
10
10
|
attachments?: Attachment[];
|
|
11
|
+
edit?: AppliedEdit;
|
|
11
12
|
}): ThreadMessage;
|
|
12
13
|
/** @throws NotFoundError @throws ConflictError — trailing commit param is additive */
|
|
13
14
|
resolvePin(id: string, by: "human" | "agent", note?: string, commit?: string): Pin;
|
|
@@ -28,6 +29,8 @@ type PinboxDoEnv = {
|
|
|
28
29
|
PINBOX_TOKEN?: string;
|
|
29
30
|
AUTH_STRATEGY?: "none" | "token" | "jwt";
|
|
30
31
|
ALLOW_UNAUTHENTICATED?: string;
|
|
32
|
+
WEBHOOK_URL?: string;
|
|
33
|
+
WEBHOOK_SECRET?: string;
|
|
31
34
|
JWT_ISSUER?: string;
|
|
32
35
|
JWT_JWKS_URL?: string;
|
|
33
36
|
JWT_AUDIENCE?: string;
|
|
@@ -51,12 +54,21 @@ declare class PinboxHubDO {
|
|
|
51
54
|
private readonly env;
|
|
52
55
|
private readonly strategy;
|
|
53
56
|
private readonly handler;
|
|
57
|
+
/** Undefined when no adapter is configured — the hub then stores pins and delivers nothing. */
|
|
58
|
+
private readonly router;
|
|
54
59
|
constructor(ctx: DurableObjectState, env: PinboxDoEnv);
|
|
55
60
|
fetch(req: Request): Promise<Response>;
|
|
56
61
|
private intercept;
|
|
57
62
|
webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): void;
|
|
58
63
|
webSocketClose(_ws: WebSocket, _code: number, _reason: string, _wasClean: boolean): void;
|
|
64
|
+
/**
|
|
65
|
+
* Retry/escalation for the delivery queue. DO alarms, never cron (deep-dive §1.14): the alarm
|
|
66
|
+
* survives eviction, so a webhook receiver that is down when a pin lands is retried after this
|
|
67
|
+
* DO has been unloaded and woken again.
|
|
68
|
+
*/
|
|
59
69
|
alarm(): Promise<void>;
|
|
70
|
+
/** Re-arm while work remains. Setting an alarm that already exists is a no-op. */
|
|
71
|
+
private scheduleDrain;
|
|
60
72
|
private upgrade;
|
|
61
73
|
private protocolError;
|
|
62
74
|
private unauthorized;
|
package/dist/do.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { AttachmentSchema, PinInputSchema, PinSchema, SessionRefSchema, ThreadMessageSchema } from "./schema.js";
|
|
2
2
|
import { a as NotFoundError, i as ConflictError, o as newId, t as SessionSchema } from "./sessions-DrCVTMfI.js";
|
|
3
|
-
import { t as MIGRATIONS } from "./store-
|
|
4
|
-
import { n as createHubHandler, o as ok, r as err } from "./hub-
|
|
3
|
+
import { t as MIGRATIONS } from "./store-qtk4G3q4.js";
|
|
4
|
+
import { n as createHubHandler, o as ok, r as err } from "./hub-I67BuX9S.js";
|
|
5
5
|
import { ClientHelloSchema, WS_CLOSE_PROTOCOL, WS_CLOSE_UNAUTHORIZED, WS_TOKEN_SUBPROTOCOL_PREFIX, encodeWsEvent } from "./ws-protocol.js";
|
|
6
|
+
import { n as DeliveryRouter } from "./router-B8EOto30.js";
|
|
7
|
+
import { createWebhookAdapter } from "./delivery/webhook.js";
|
|
6
8
|
import { i as verifyJwt, n as verifyNone, r as verifyToken } from "./verify-BDt9d9Np.js";
|
|
7
9
|
//#region src/do-store-registries.ts
|
|
8
10
|
function parseSession(json) {
|
|
@@ -250,6 +252,7 @@ var DoSqlitePinStore = class {
|
|
|
250
252
|
text,
|
|
251
253
|
...opts?.origin !== void 0 ? { origin: opts.origin } : {},
|
|
252
254
|
...opts?.attachments !== void 0 ? { attachments: opts.attachments } : {},
|
|
255
|
+
...opts?.edit !== void 0 ? { edit: opts.edit } : {},
|
|
253
256
|
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
254
257
|
});
|
|
255
258
|
return this.mutate(() => {
|
|
@@ -556,6 +559,28 @@ var DoBroadcaster = class {
|
|
|
556
559
|
return this.state.getWebSockets(topic).length;
|
|
557
560
|
}
|
|
558
561
|
};
|
|
562
|
+
/** Retry cadence for the delivery queue. Coarse: the receiver being down is not urgent. */
|
|
563
|
+
const DRAIN_INTERVAL_MS = 3e4;
|
|
564
|
+
/** Later than any due_at this hub can write, so `due()` answers "anything pending?". */
|
|
565
|
+
const FAR_FUTURE = "9999-12-31T23:59:59.999Z";
|
|
566
|
+
/**
|
|
567
|
+
* The cloud adapter set: `[webhook]` when configured, nothing otherwise.
|
|
568
|
+
*
|
|
569
|
+
* Returning undefined rather than an empty router is deliberate — a router with no adapters
|
|
570
|
+
* still writes pending rows, and nothing would ever drain them.
|
|
571
|
+
*/
|
|
572
|
+
function buildRouter(store, env) {
|
|
573
|
+
const url = env.WEBHOOK_URL;
|
|
574
|
+
const secret = env.WEBHOOK_SECRET;
|
|
575
|
+
if (url === void 0 || url === "" || secret === void 0 || secret === "") return void 0;
|
|
576
|
+
return new DeliveryRouter({
|
|
577
|
+
store,
|
|
578
|
+
adapters: [createWebhookAdapter({
|
|
579
|
+
url,
|
|
580
|
+
secret
|
|
581
|
+
})]
|
|
582
|
+
});
|
|
583
|
+
}
|
|
559
584
|
var PinboxHubDO = class {
|
|
560
585
|
store;
|
|
561
586
|
broadcaster;
|
|
@@ -564,6 +589,8 @@ var PinboxHubDO = class {
|
|
|
564
589
|
env;
|
|
565
590
|
strategy;
|
|
566
591
|
handler;
|
|
592
|
+
/** Undefined when no adapter is configured — the hub then stores pins and delivers nothing. */
|
|
593
|
+
router;
|
|
567
594
|
constructor(ctx, env) {
|
|
568
595
|
this.ctx = ctx;
|
|
569
596
|
this.env = env;
|
|
@@ -577,6 +604,14 @@ var PinboxHubDO = class {
|
|
|
577
604
|
..."verify" in this.strategy ? { verify: this.strategy.verify } : {}
|
|
578
605
|
});
|
|
579
606
|
this.store.subscribe((event) => this.broadcaster.publish(this.topic, encodeWsEvent(event)));
|
|
607
|
+
this.router = buildRouter(this.store, env);
|
|
608
|
+
if (this.router !== void 0) {
|
|
609
|
+
const router = this.router;
|
|
610
|
+
this.store.subscribe((event) => {
|
|
611
|
+
router.dispatch(event).then(() => this.scheduleDrain());
|
|
612
|
+
});
|
|
613
|
+
this.scheduleDrain();
|
|
614
|
+
}
|
|
580
615
|
ctx.setWebSocketAutoResponse(new WebSocketRequestResponsePair("ping", "pong"));
|
|
581
616
|
}
|
|
582
617
|
async fetch(req) {
|
|
@@ -634,7 +669,23 @@ var PinboxHubDO = class {
|
|
|
634
669
|
}));
|
|
635
670
|
}
|
|
636
671
|
webSocketClose(_ws, _code, _reason, _wasClean) {}
|
|
637
|
-
|
|
672
|
+
/**
|
|
673
|
+
* Retry/escalation for the delivery queue. DO alarms, never cron (deep-dive §1.14): the alarm
|
|
674
|
+
* survives eviction, so a webhook receiver that is down when a pin lands is retried after this
|
|
675
|
+
* DO has been unloaded and woken again.
|
|
676
|
+
*/
|
|
677
|
+
async alarm() {
|
|
678
|
+
if (this.router === void 0) return;
|
|
679
|
+
await this.router.drainDue();
|
|
680
|
+
await this.scheduleDrain();
|
|
681
|
+
}
|
|
682
|
+
/** Re-arm while work remains. Setting an alarm that already exists is a no-op. */
|
|
683
|
+
async scheduleDrain() {
|
|
684
|
+
if (this.router === void 0) return;
|
|
685
|
+
if (this.store.deliveries.due(FAR_FUTURE).length === 0) return;
|
|
686
|
+
if (await this.ctx.storage.getAlarm() !== null) return;
|
|
687
|
+
await this.ctx.storage.setAlarm(Date.now() + DRAIN_INTERVAL_MS);
|
|
688
|
+
}
|
|
638
689
|
async upgrade(req, url, verify) {
|
|
639
690
|
let token = url.searchParams.get("token");
|
|
640
691
|
let acceptedProtocol;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AttachmentSchema, PinInputSchema, SessionRefSchema } from "./schema.js";
|
|
1
|
+
import { AppliedEditSchema, AttachmentSchema, PinInputSchema, SessionRefSchema } from "./schema.js";
|
|
2
2
|
import { a as NotFoundError, i as ConflictError, o as newId } from "./sessions-DrCVTMfI.js";
|
|
3
3
|
import { t as POLL_OPEN_MS } from "./poll-BrcAuaAz.js";
|
|
4
4
|
import { t as buildInjectionContext } from "./context-BHcEpzVb.js";
|
|
@@ -178,7 +178,8 @@ const ThreadPostSchema = z.object({
|
|
|
178
178
|
]),
|
|
179
179
|
text: z.string().min(1),
|
|
180
180
|
attachments: z.array(AttachmentSchema).optional(),
|
|
181
|
-
origin: z.string().optional()
|
|
181
|
+
origin: z.string().optional(),
|
|
182
|
+
edit: AppliedEditSchema.optional()
|
|
182
183
|
});
|
|
183
184
|
const ResolvePostSchema = z.object({
|
|
184
185
|
by: z.enum(["human", "agent"]),
|
|
@@ -260,7 +261,8 @@ async function routePinItem(req, url, opts) {
|
|
|
260
261
|
const body = ThreadPostSchema.parse(await readJson(req));
|
|
261
262
|
const messageOpts = {
|
|
262
263
|
...body.attachments === void 0 ? {} : { attachments: body.attachments },
|
|
263
|
-
...body.origin === void 0 ? {} : { origin: body.origin }
|
|
264
|
+
...body.origin === void 0 ? {} : { origin: body.origin },
|
|
265
|
+
...body.edit === void 0 ? {} : { edit: body.edit }
|
|
264
266
|
};
|
|
265
267
|
const hasOpts = Object.keys(messageOpts).length > 0;
|
|
266
268
|
return ok(201, store.addThreadMessage(threadPinId, body.role, body.text, hasOpts ? messageOpts : void 0));
|
package/dist/hub-server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { c as PinStore } from "./store-
|
|
1
|
+
import { r as Attachment } from "./schema-HSZFobyd.js";
|
|
2
|
+
import { c as PinStore } from "./store-DPp8xMdp.js";
|
|
3
3
|
import { HubOptions } from "./hub.js";
|
|
4
4
|
//#region src/attachments.d.ts
|
|
5
5
|
interface AttachmentSink {
|
package/dist/hub-server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as registerAttachmentSink, n as createHubHandler } from "./hub-
|
|
1
|
+
import { c as registerAttachmentSink, n as createHubHandler } from "./hub-I67BuX9S.js";
|
|
2
2
|
import { ClientHelloSchema, WS_CLOSE_PROTOCOL, WS_CLOSE_UNAUTHORIZED, WS_TOKEN_SUBPROTOCOL_PREFIX, encodeWsEvent } from "./ws-protocol.js";
|
|
3
3
|
//#region src/attachments-local.ts
|
|
4
4
|
const EXTENSIONS = {
|
package/dist/hub.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as Connector } from "./types-
|
|
2
|
-
import { c as PinStore } from "./store-
|
|
1
|
+
import { t as Connector } from "./types-Bu7YuW6a.js";
|
|
2
|
+
import { c as PinStore } from "./store-DPp8xMdp.js";
|
|
3
3
|
//#region src/hub.d.ts
|
|
4
4
|
type Identity = {
|
|
5
5
|
userId: string;
|
|
@@ -35,6 +35,10 @@ declare function mustGetPin(store: PinStore, id: string): {
|
|
|
35
35
|
width: number;
|
|
36
36
|
height: number;
|
|
37
37
|
} | undefined;
|
|
38
|
+
spot?: {
|
|
39
|
+
x: number;
|
|
40
|
+
y: number;
|
|
41
|
+
} | undefined;
|
|
38
42
|
fixed?: boolean | undefined;
|
|
39
43
|
anchor?: string | undefined;
|
|
40
44
|
source?: {
|
|
@@ -48,6 +52,7 @@ declare function mustGetPin(store: PinStore, id: string): {
|
|
|
48
52
|
aria?: Record<string, string> | undefined;
|
|
49
53
|
nearbyText?: string | undefined;
|
|
50
54
|
selectedText?: string | undefined;
|
|
55
|
+
textRuns?: string[] | undefined;
|
|
51
56
|
} | undefined;
|
|
52
57
|
} | undefined;
|
|
53
58
|
move?: {
|
package/dist/hub.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as mustGetPin, i as match, n as createHubHandler, o as ok, r as err, s as readJson, t as BodyNotJsonError } from "./hub-
|
|
1
|
+
import { a as mustGetPin, i as match, n as createHubHandler, o as ok, r as err, s as readJson, t as BodyNotJsonError } from "./hub-I67BuX9S.js";
|
|
2
2
|
export { BodyNotJsonError, createHubHandler, err, match, mustGetPin, ok, readJson };
|
package/dist/markdown.d.ts
CHANGED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { PinSchema, ThreadMessageSchema } from "./schema.js";
|
|
2
|
+
//#region src/delivery/hooks.ts
|
|
3
|
+
/** Agents whose hook systems can register + pull (research §1: shared hooks schema). */
|
|
4
|
+
const HOOK_CAPABLE_AGENTS = /* @__PURE__ */ new Set([
|
|
5
|
+
"claude",
|
|
6
|
+
"codex",
|
|
7
|
+
"hermes"
|
|
8
|
+
]);
|
|
9
|
+
function createHooksAdapter() {
|
|
10
|
+
return {
|
|
11
|
+
name: "hooks",
|
|
12
|
+
matches(session) {
|
|
13
|
+
return session.endedAt === void 0 && HOOK_CAPABLE_AGENTS.has(session.agent);
|
|
14
|
+
},
|
|
15
|
+
async deliver() {}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/delivery/router.ts
|
|
20
|
+
const PULL_ADAPTERS = /* @__PURE__ */ new Set(["hooks"]);
|
|
21
|
+
const NO_ADAPTER = "none";
|
|
22
|
+
const DEFAULT_HOOKS_ESCALATE_MS = 6e5;
|
|
23
|
+
/** Escalation window for pull rows: PINBOX_HOOKS_ESCALATE_MS, default 10 min. */
|
|
24
|
+
function hooksEscalateMs() {
|
|
25
|
+
const raw = typeof process === "undefined" ? void 0 : process.env["PINBOX_HOOKS_ESCALATE_MS"];
|
|
26
|
+
if (raw === void 0) return DEFAULT_HOOKS_ESCALATE_MS;
|
|
27
|
+
const parsed = Number.parseInt(raw, 10);
|
|
28
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_HOOKS_ESCALATE_MS;
|
|
29
|
+
}
|
|
30
|
+
const BACKOFF_BASE_MS = 3e4;
|
|
31
|
+
const BACKOFF_FACTOR = 4;
|
|
32
|
+
const BACKOFF_CAP_MS = 9e5;
|
|
33
|
+
var DeliveryRouter = class {
|
|
34
|
+
store;
|
|
35
|
+
adapters;
|
|
36
|
+
chain = Promise.resolve();
|
|
37
|
+
constructor(opts) {
|
|
38
|
+
this.store = opts.store;
|
|
39
|
+
this.adapters = [...opts.adapters];
|
|
40
|
+
}
|
|
41
|
+
/** THE single entry point; never throws — failures land in the queue. */
|
|
42
|
+
dispatch(event) {
|
|
43
|
+
return this.enqueueWork(() => this.dispatchOne(event));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Runs on hub wake + a coarse unref'd interval: (a) boot/cursor reconciliation,
|
|
47
|
+
* (b) retry/escalate due pending rows, (c) assign unassigned rows to the active
|
|
48
|
+
* session. Never rejects.
|
|
49
|
+
*/
|
|
50
|
+
drainDue(now) {
|
|
51
|
+
return this.enqueueWork(() => this.drainNow(now ?? (/* @__PURE__ */ new Date()).toISOString()));
|
|
52
|
+
}
|
|
53
|
+
enqueueWork(work) {
|
|
54
|
+
const run = this.chain.then(work).catch(() => {});
|
|
55
|
+
this.chain = run;
|
|
56
|
+
return run;
|
|
57
|
+
}
|
|
58
|
+
async dispatchOne(event) {
|
|
59
|
+
if (event.seq <= this.store.deliveries.lastEventSeq()) return;
|
|
60
|
+
let target;
|
|
61
|
+
try {
|
|
62
|
+
target = this.route(event);
|
|
63
|
+
} catch {
|
|
64
|
+
target = { kind: "skip" };
|
|
65
|
+
}
|
|
66
|
+
if (target.kind === "skip") {
|
|
67
|
+
this.store.deliveries.enqueue({
|
|
68
|
+
eventSeq: event.seq,
|
|
69
|
+
sessionId: null,
|
|
70
|
+
adapter: NO_ADAPTER,
|
|
71
|
+
dueAt: null,
|
|
72
|
+
status: "skipped"
|
|
73
|
+
});
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (target.kind === "unassigned") {
|
|
77
|
+
this.store.deliveries.enqueue({
|
|
78
|
+
eventSeq: event.seq,
|
|
79
|
+
sessionId: null,
|
|
80
|
+
adapter: NO_ADAPTER,
|
|
81
|
+
dueAt: null
|
|
82
|
+
});
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
await this.attemptNew(event, target.session, /* @__PURE__ */ new Date());
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Deliverable events are pin.created and thread.message with role human|mirror
|
|
89
|
+
* (rule 3: agent-authored events are never delivered back). Everything else —
|
|
90
|
+
* pin.resolved, and later event types — is skipped-by-design.
|
|
91
|
+
*/
|
|
92
|
+
route(event) {
|
|
93
|
+
if (event.type === "pin.created") {
|
|
94
|
+
const pin = PinSchema.parse(event.payload);
|
|
95
|
+
return this.bindTarget(pin);
|
|
96
|
+
}
|
|
97
|
+
if (event.type === "thread.message") {
|
|
98
|
+
const message = ThreadMessageSchema.parse(event.payload);
|
|
99
|
+
if (message.role === "agent") return { kind: "skip" };
|
|
100
|
+
const pin = this.store.getPin(message.pinId);
|
|
101
|
+
if (pin === null) return { kind: "skip" };
|
|
102
|
+
return this.bindTarget(pin);
|
|
103
|
+
}
|
|
104
|
+
return { kind: "skip" };
|
|
105
|
+
}
|
|
106
|
+
bindTarget(pin) {
|
|
107
|
+
const { sessions } = this.store;
|
|
108
|
+
if (pin.agentSession !== void 0) return {
|
|
109
|
+
kind: "session",
|
|
110
|
+
session: sessions.findByRef(pin.agentSession) ?? sessions.register(pin.agentSession)
|
|
111
|
+
};
|
|
112
|
+
const active = sessions.active();
|
|
113
|
+
if (active === null) return { kind: "unassigned" };
|
|
114
|
+
this.store.bindSession(pin.id, sessionRefOf(active));
|
|
115
|
+
return {
|
|
116
|
+
kind: "session",
|
|
117
|
+
session: active
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
async attemptNew(event, session, now) {
|
|
121
|
+
const { deliveries } = this.store;
|
|
122
|
+
const adapter = await this.selectAdapter(this.adapters, session);
|
|
123
|
+
if (adapter === null) {
|
|
124
|
+
deliveries.enqueue({
|
|
125
|
+
eventSeq: event.seq,
|
|
126
|
+
sessionId: session.id,
|
|
127
|
+
adapter: NO_ADAPTER,
|
|
128
|
+
dueAt: null
|
|
129
|
+
});
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (PULL_ADAPTERS.has(adapter.name)) {
|
|
133
|
+
const dueAt = new Date(now.getTime() + hooksEscalateMs()).toISOString();
|
|
134
|
+
const row = deliveries.enqueue({
|
|
135
|
+
eventSeq: event.seq,
|
|
136
|
+
sessionId: session.id,
|
|
137
|
+
adapter: adapter.name,
|
|
138
|
+
dueAt
|
|
139
|
+
});
|
|
140
|
+
try {
|
|
141
|
+
await adapter.deliver(event, session);
|
|
142
|
+
} catch (cause) {
|
|
143
|
+
this.recordFailure(row, cause, now);
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const row = deliveries.enqueue({
|
|
148
|
+
eventSeq: event.seq,
|
|
149
|
+
sessionId: session.id,
|
|
150
|
+
adapter: adapter.name,
|
|
151
|
+
dueAt: null
|
|
152
|
+
});
|
|
153
|
+
try {
|
|
154
|
+
await adapter.deliver(event, session);
|
|
155
|
+
deliveries.markDelivered(row.id);
|
|
156
|
+
} catch (cause) {
|
|
157
|
+
this.recordFailure(row, cause, now);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async drainNow(now) {
|
|
161
|
+
const { store } = this;
|
|
162
|
+
for (const event of store.eventsAfter(store.deliveries.lastEventSeq())) await this.dispatchOne(event);
|
|
163
|
+
for (const row of store.deliveries.due(now)) {
|
|
164
|
+
if (row.sessionId === null) continue;
|
|
165
|
+
try {
|
|
166
|
+
await this.retryRow(row, now);
|
|
167
|
+
} catch {}
|
|
168
|
+
}
|
|
169
|
+
const active = store.sessions.active();
|
|
170
|
+
if (active !== null) for (const row of store.deliveries.unassigned()) try {
|
|
171
|
+
await this.claimRow(row, active, now);
|
|
172
|
+
} catch {}
|
|
173
|
+
}
|
|
174
|
+
async retryRow(row, now) {
|
|
175
|
+
const { deliveries, sessions } = this.store;
|
|
176
|
+
const event = this.eventOf(row);
|
|
177
|
+
if (event === null) {
|
|
178
|
+
deliveries.markFailed(row.id, `E_DELIVERY: event ${row.eventSeq} missing from log`, null);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const session = row.sessionId === null ? null : sessions.get(row.sessionId);
|
|
182
|
+
if (session === null) {
|
|
183
|
+
deliveries.markFailed(row.id, `E_SESSION_GONE: session ${row.sessionId} unknown`, null);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const adapter = await this.selectAdapter(this.rotatedAfter(row.adapter), session);
|
|
187
|
+
if (adapter === null) return;
|
|
188
|
+
if (PULL_ADAPTERS.has(adapter.name)) return;
|
|
189
|
+
try {
|
|
190
|
+
await adapter.deliver(event, session);
|
|
191
|
+
deliveries.markDelivered(row.id);
|
|
192
|
+
} catch (cause) {
|
|
193
|
+
this.recordFailure(row, cause, new Date(Date.parse(now)));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async claimRow(row, active, now) {
|
|
197
|
+
const event = this.eventOf(row);
|
|
198
|
+
if (event === null) {
|
|
199
|
+
this.store.deliveries.markFailed(row.id, `E_DELIVERY: event ${row.eventSeq} missing from log`, null);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
this.store.deliveries.assign(row.id, active.id);
|
|
203
|
+
const pinId = pinIdOfEvent(event);
|
|
204
|
+
if (pinId !== null) try {
|
|
205
|
+
this.store.bindSession(pinId, sessionRefOf(active));
|
|
206
|
+
} catch {}
|
|
207
|
+
await this.retryRow({
|
|
208
|
+
...row,
|
|
209
|
+
sessionId: active.id
|
|
210
|
+
}, now);
|
|
211
|
+
}
|
|
212
|
+
async selectAdapter(candidates, session) {
|
|
213
|
+
for (const adapter of candidates) try {
|
|
214
|
+
if (await adapter.matches(session)) return adapter;
|
|
215
|
+
} catch {}
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
rotatedAfter(name) {
|
|
219
|
+
const index = this.adapters.findIndex((adapter) => adapter.name === name);
|
|
220
|
+
if (index < 0) return [...this.adapters];
|
|
221
|
+
return [...this.adapters.slice(index + 1), ...this.adapters.slice(0, index + 1)];
|
|
222
|
+
}
|
|
223
|
+
recordFailure(row, cause, now) {
|
|
224
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
225
|
+
const sessionGone = message.startsWith("E_SESSION_GONE");
|
|
226
|
+
const attempts = row.attempts;
|
|
227
|
+
if (sessionGone || attempts >= 4) {
|
|
228
|
+
const lastError = sessionGone ? message : `E_DELIVERY: ${message}`;
|
|
229
|
+
this.store.deliveries.markFailed(row.id, lastError, null);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const backoffMs = Math.min(BACKOFF_BASE_MS * BACKOFF_FACTOR ** attempts, BACKOFF_CAP_MS);
|
|
233
|
+
const retryAt = new Date(now.getTime() + backoffMs).toISOString();
|
|
234
|
+
this.store.deliveries.markFailed(row.id, message, retryAt);
|
|
235
|
+
}
|
|
236
|
+
eventOf(row) {
|
|
237
|
+
const event = this.store.eventsAfter(row.eventSeq - 1).at(0);
|
|
238
|
+
return event !== void 0 && event.seq === row.eventSeq ? event : null;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
function sessionRefOf(session) {
|
|
242
|
+
return {
|
|
243
|
+
agent: session.agent,
|
|
244
|
+
key: session.key,
|
|
245
|
+
...session.cwd !== void 0 ? { cwd: session.cwd } : {}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/** pin.created payloads are the Pin (`id`); thread.message payloads carry `pinId`. */
|
|
249
|
+
function pinIdOfEvent(event) {
|
|
250
|
+
if (event.type === "pin.created") {
|
|
251
|
+
const parsed = PinSchema.safeParse(event.payload);
|
|
252
|
+
return parsed.success ? parsed.data.id : null;
|
|
253
|
+
}
|
|
254
|
+
if (event.type === "thread.message") {
|
|
255
|
+
const parsed = ThreadMessageSchema.safeParse(event.payload);
|
|
256
|
+
return parsed.success ? parsed.data.pinId : null;
|
|
257
|
+
}
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
//#endregion
|
|
261
|
+
export { createHooksAdapter as a, HOOK_CAPABLE_AGENTS as i, DeliveryRouter as n, hooksEscalateMs as r, DEFAULT_HOOKS_ESCALATE_MS as t };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as Session } from "./sessions-
|
|
3
|
-
import { c as PinStore, l as StoredEvent } from "./store-
|
|
1
|
+
import { g as ThreadMessage, s as Pin } from "./schema-HSZFobyd.js";
|
|
2
|
+
import { t as Session } from "./sessions-CHtzoBF6.js";
|
|
3
|
+
import { c as PinStore, l as StoredEvent } from "./store-DPp8xMdp.js";
|
|
4
4
|
//#region src/delivery/context.d.ts
|
|
5
5
|
/**
|
|
6
6
|
* The per-turn injection context: every open pin at the `compact` dial,
|
|
@@ -24,6 +24,21 @@ declare const AttachmentSchema: z.ZodObject<{
|
|
|
24
24
|
width: z.ZodOptional<z.ZodNumber>;
|
|
25
25
|
height: z.ZodOptional<z.ZodNumber>;
|
|
26
26
|
}, z.core.$strip>;
|
|
27
|
+
/**
|
|
28
|
+
* A change an agent made directly to the live page, rather than to a file.
|
|
29
|
+
*
|
|
30
|
+
* On a project with a repo the agent edits source and the dev server reloads, so the change needs
|
|
31
|
+
* no wire representation. A hosted page has no repo to edit, so the only way an agent can actually
|
|
32
|
+
* change what you are looking at is to say which element and what it should now read.
|
|
33
|
+
*
|
|
34
|
+
* `selector` is the pin's own captured selector, never one the agent chose. `texts` lines up with
|
|
35
|
+
* that element's captured `textRuns`, so pinning a nav bar and asking for all four labels to
|
|
36
|
+
* change is a single edit. Text only, never markup, so this can carry no script.
|
|
37
|
+
*/
|
|
38
|
+
declare const AppliedEditSchema: z.ZodObject<{
|
|
39
|
+
selector: z.ZodString;
|
|
40
|
+
texts: z.ZodArray<z.ZodString>;
|
|
41
|
+
}, z.core.$strip>;
|
|
27
42
|
declare const ThreadMessageSchema: z.ZodObject<{
|
|
28
43
|
id: z.ZodString;
|
|
29
44
|
pinId: z.ZodString;
|
|
@@ -34,6 +49,10 @@ declare const ThreadMessageSchema: z.ZodObject<{
|
|
|
34
49
|
}>;
|
|
35
50
|
origin: z.ZodOptional<z.ZodString>;
|
|
36
51
|
text: z.ZodString;
|
|
52
|
+
edit: z.ZodOptional<z.ZodObject<{
|
|
53
|
+
selector: z.ZodString;
|
|
54
|
+
texts: z.ZodArray<z.ZodString>;
|
|
55
|
+
}, z.core.$strip>>;
|
|
37
56
|
attachments: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
38
57
|
id: z.ZodString;
|
|
39
58
|
kind: z.ZodEnum<{
|
|
@@ -74,6 +93,10 @@ declare const PinInputSchema: z.ZodObject<{
|
|
|
74
93
|
width: number;
|
|
75
94
|
height: number;
|
|
76
95
|
}, unknown>>>;
|
|
96
|
+
spot: z.ZodOptional<z.ZodObject<{
|
|
97
|
+
x: z.ZodNumber;
|
|
98
|
+
y: z.ZodNumber;
|
|
99
|
+
}, z.core.$strip>>;
|
|
77
100
|
fixed: z.ZodOptional<z.ZodBoolean>;
|
|
78
101
|
anchor: z.ZodOptional<z.ZodString>;
|
|
79
102
|
source: z.ZodOptional<z.ZodObject<{
|
|
@@ -91,6 +114,7 @@ declare const PinInputSchema: z.ZodObject<{
|
|
|
91
114
|
aria: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
92
115
|
nearbyText: z.ZodOptional<z.ZodString>;
|
|
93
116
|
selectedText: z.ZodOptional<z.ZodString>;
|
|
117
|
+
textRuns: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
94
118
|
}, z.core.$strip>>;
|
|
95
119
|
}, z.core.$strip>>;
|
|
96
120
|
move: z.ZodOptional<z.ZodObject<{
|
|
@@ -176,6 +200,10 @@ declare const PinSchema: z.ZodObject<{
|
|
|
176
200
|
width: number;
|
|
177
201
|
height: number;
|
|
178
202
|
}, unknown>>>;
|
|
203
|
+
spot: z.ZodOptional<z.ZodObject<{
|
|
204
|
+
x: z.ZodNumber;
|
|
205
|
+
y: z.ZodNumber;
|
|
206
|
+
}, z.core.$strip>>;
|
|
179
207
|
fixed: z.ZodOptional<z.ZodBoolean>;
|
|
180
208
|
anchor: z.ZodOptional<z.ZodString>;
|
|
181
209
|
source: z.ZodOptional<z.ZodObject<{
|
|
@@ -193,6 +221,7 @@ declare const PinSchema: z.ZodObject<{
|
|
|
193
221
|
aria: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
194
222
|
nearbyText: z.ZodOptional<z.ZodString>;
|
|
195
223
|
selectedText: z.ZodOptional<z.ZodString>;
|
|
224
|
+
textRuns: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
196
225
|
}, z.core.$strip>>;
|
|
197
226
|
}, z.core.$strip>>;
|
|
198
227
|
move: z.ZodOptional<z.ZodObject<{
|
|
@@ -289,9 +318,10 @@ type Rect = z.infer<typeof RectSchema>;
|
|
|
289
318
|
type Pin = z.infer<typeof PinSchema>;
|
|
290
319
|
type PinInput = z.infer<typeof PinInputSchema>;
|
|
291
320
|
type ThreadMessage = z.infer<typeof ThreadMessageSchema>;
|
|
321
|
+
type AppliedEdit = z.infer<typeof AppliedEditSchema>;
|
|
292
322
|
type SessionRef = z.infer<typeof SessionRefSchema>;
|
|
293
323
|
type Attachment = z.infer<typeof AttachmentSchema>;
|
|
294
324
|
type Link = z.infer<typeof LinkSchema>;
|
|
295
325
|
declare function pinJsonSchema(): Record<string, unknown>;
|
|
296
326
|
//#endregion
|
|
297
|
-
export {
|
|
327
|
+
export { ThreadMessageSchema as _, Link as a, PinInput as c, Rect as d, RectSchema as f, ThreadMessage as g, SessionRefSchema as h, AttachmentSchema as i, PinInputSchema as l, SessionRef as m, AppliedEditSchema as n, LinkSchema as o, SCHEMA_VERSION as p, Attachment as r, Pin as s, AppliedEdit as t, PinSchema as u, pinJsonSchema as v };
|
package/dist/schema.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { Attachment, AttachmentSchema, Link, LinkSchema, Pin, PinInput, PinInputSchema, PinSchema, Rect, RectSchema, SCHEMA_VERSION, SessionRef, SessionRefSchema, ThreadMessage, ThreadMessageSchema, pinJsonSchema };
|
|
1
|
+
import { _ as ThreadMessageSchema, a as Link, c as PinInput, d as Rect, f as RectSchema, g as ThreadMessage, h as SessionRefSchema, i as AttachmentSchema, l as PinInputSchema, m as SessionRef, n as AppliedEditSchema, o as LinkSchema, p as SCHEMA_VERSION, r as Attachment, s as Pin, t as AppliedEdit, u as PinSchema, v as pinJsonSchema } from "./schema-HSZFobyd.js";
|
|
2
|
+
export { AppliedEdit, AppliedEditSchema, Attachment, AttachmentSchema, Link, LinkSchema, Pin, PinInput, PinInputSchema, PinSchema, Rect, RectSchema, SCHEMA_VERSION, SessionRef, SessionRefSchema, ThreadMessage, ThreadMessageSchema, pinJsonSchema };
|
package/dist/schema.js
CHANGED
|
@@ -21,6 +21,22 @@ const AttachmentSchema = z.object({
|
|
|
21
21
|
width: z.number().int().optional(),
|
|
22
22
|
height: z.number().int().optional()
|
|
23
23
|
});
|
|
24
|
+
/**
|
|
25
|
+
* A change an agent made directly to the live page, rather than to a file.
|
|
26
|
+
*
|
|
27
|
+
* On a project with a repo the agent edits source and the dev server reloads, so the change needs
|
|
28
|
+
* no wire representation. A hosted page has no repo to edit, so the only way an agent can actually
|
|
29
|
+
* change what you are looking at is to say which element and what it should now read.
|
|
30
|
+
*
|
|
31
|
+
* `selector` is the pin's own captured selector, never one the agent chose. `texts` lines up with
|
|
32
|
+
* that element's captured `textRuns`, so pinning a nav bar and asking for all four labels to
|
|
33
|
+
* change is a single edit. Text only, never markup, so this can carry no script.
|
|
34
|
+
*/
|
|
35
|
+
const AppliedEditSchema = z.object({
|
|
36
|
+
selector: z.string(),
|
|
37
|
+
/** New text for each of the element's text runs, in order. Length must match, or nothing is applied. */
|
|
38
|
+
texts: z.array(z.string())
|
|
39
|
+
});
|
|
24
40
|
const ThreadMessageSchema = z.object({
|
|
25
41
|
id: z.string(),
|
|
26
42
|
pinId: z.string(),
|
|
@@ -31,6 +47,7 @@ const ThreadMessageSchema = z.object({
|
|
|
31
47
|
]),
|
|
32
48
|
origin: z.string().optional(),
|
|
33
49
|
text: z.string(),
|
|
50
|
+
edit: AppliedEditSchema.optional(),
|
|
34
51
|
attachments: z.array(AttachmentSchema).optional(),
|
|
35
52
|
at: z.string()
|
|
36
53
|
});
|
|
@@ -48,13 +65,27 @@ const ContextSchema = z.object({
|
|
|
48
65
|
styles: z.record(z.string(), z.string()).optional(),
|
|
49
66
|
aria: z.record(z.string(), z.string()).optional(),
|
|
50
67
|
nearbyText: z.string().optional(),
|
|
51
|
-
selectedText: z.string().optional()
|
|
68
|
+
selectedText: z.string().optional(),
|
|
69
|
+
/**
|
|
70
|
+
* The pinned element's text, split the way the markup splits it — one entry per element that
|
|
71
|
+
* actually holds words. A heading is one entry; a nav bar is one per link.
|
|
72
|
+
*
|
|
73
|
+
* `nearbyText` runs them together, which is fine to read and useless to edit: it cannot tell an
|
|
74
|
+
* agent that "work approach people contact" is four separate elements. This can, and it is what
|
|
75
|
+
* lets feedback on a group ("capitalise these") reach every item in it.
|
|
76
|
+
*/
|
|
77
|
+
textRuns: z.array(z.string()).optional()
|
|
52
78
|
});
|
|
53
79
|
const TargetSchema = z.object({
|
|
54
80
|
url: z.string().optional(),
|
|
55
81
|
selector: z.string().optional(),
|
|
56
82
|
tag: z.string().optional(),
|
|
57
83
|
rect: RectSchema.optional(),
|
|
84
|
+
/** Where inside `rect` the click landed, 0–1 on each axis. Absent ⇒ anchor at the centre. */
|
|
85
|
+
spot: z.object({
|
|
86
|
+
x: z.number(),
|
|
87
|
+
y: z.number()
|
|
88
|
+
}).optional(),
|
|
58
89
|
fixed: z.boolean().optional(),
|
|
59
90
|
anchor: z.string().optional(),
|
|
60
91
|
source: SourceSchema.optional(),
|
|
@@ -118,4 +149,4 @@ function pinJsonSchema() {
|
|
|
118
149
|
return z.toJSONSchema(PinSchema);
|
|
119
150
|
}
|
|
120
151
|
//#endregion
|
|
121
|
-
export { AttachmentSchema, LinkSchema, PinInputSchema, PinSchema, RectSchema, SCHEMA_VERSION, SessionRefSchema, ThreadMessageSchema, pinJsonSchema };
|
|
152
|
+
export { AppliedEditSchema, AttachmentSchema, LinkSchema, PinInputSchema, PinSchema, RectSchema, SCHEMA_VERSION, SessionRefSchema, ThreadMessageSchema, pinJsonSchema };
|
package/dist/schema.json
CHANGED
|
@@ -50,6 +50,22 @@
|
|
|
50
50
|
],
|
|
51
51
|
"additionalProperties": false
|
|
52
52
|
},
|
|
53
|
+
"spot": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"properties": {
|
|
56
|
+
"x": {
|
|
57
|
+
"type": "number"
|
|
58
|
+
},
|
|
59
|
+
"y": {
|
|
60
|
+
"type": "number"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"required": [
|
|
64
|
+
"x",
|
|
65
|
+
"y"
|
|
66
|
+
],
|
|
67
|
+
"additionalProperties": false
|
|
68
|
+
},
|
|
53
69
|
"fixed": {
|
|
54
70
|
"type": "boolean"
|
|
55
71
|
},
|
|
@@ -112,6 +128,12 @@
|
|
|
112
128
|
},
|
|
113
129
|
"selectedText": {
|
|
114
130
|
"type": "string"
|
|
131
|
+
},
|
|
132
|
+
"textRuns": {
|
|
133
|
+
"type": "array",
|
|
134
|
+
"items": {
|
|
135
|
+
"type": "string"
|
|
136
|
+
}
|
|
115
137
|
}
|
|
116
138
|
},
|
|
117
139
|
"additionalProperties": false
|
package/dist/sessions.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as parseTrailers, i as SqliteSessionStore, n as SessionSchema, r as SessionStore, t as Session } from "./sessions-
|
|
1
|
+
import { a as parseTrailers, i as SqliteSessionStore, n as SessionSchema, r as SessionStore, t as Session } from "./sessions-CHtzoBF6.js";
|
|
2
2
|
export { Session, SessionSchema, SessionStore, SqliteSessionStore, parseTrailers };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { r as SessionStore } from "./sessions-
|
|
1
|
+
import { a as Link, c as PinInput, g as ThreadMessage, m as SessionRef, r as Attachment, s as Pin, t as AppliedEdit } from "./schema-HSZFobyd.js";
|
|
2
|
+
import { r as SessionStore } from "./sessions-CHtzoBF6.js";
|
|
3
3
|
//#region src/store-errors.d.ts
|
|
4
4
|
/** The addressed row does not exist. Mapped to 404 / `E_NOT_FOUND` by the hub. */
|
|
5
5
|
declare class NotFoundError extends Error {}
|
|
@@ -28,6 +28,7 @@ interface PinStore {
|
|
|
28
28
|
addThreadMessage(pinId: string, role: "human" | "agent" | "mirror", text: string, opts?: {
|
|
29
29
|
origin?: string;
|
|
30
30
|
attachments?: Attachment[];
|
|
31
|
+
edit?: AppliedEdit;
|
|
31
32
|
}): ThreadMessage;
|
|
32
33
|
getThread(pinId: string): ThreadMessage[];
|
|
33
34
|
/** @throws NotFoundError @throws ConflictError — the trailing commit param is additive */
|
|
@@ -327,6 +327,7 @@ var SqlitePinStore = class {
|
|
|
327
327
|
...opts?.origin !== void 0 ? { origin: opts.origin } : {},
|
|
328
328
|
text,
|
|
329
329
|
...opts?.attachments !== void 0 ? { attachments: opts.attachments } : {},
|
|
330
|
+
...opts?.edit !== void 0 ? { edit: opts.edit } : {},
|
|
330
331
|
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
331
332
|
});
|
|
332
333
|
this.mutate(() => {
|
package/dist/store.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as LinkStore, c as PinStore, d as ConflictError, f as NotFoundError, i as DeliveryStore, l as StoredEvent, n as DeliveryRow, o as MIGRATIONS, r as DeliveryStatus, s as Migration, t as CursorStore, u as openStore } from "./store-
|
|
1
|
+
import { a as LinkStore, c as PinStore, d as ConflictError, f as NotFoundError, i as DeliveryStore, l as StoredEvent, n as DeliveryRow, o as MIGRATIONS, r as DeliveryStatus, s as Migration, t as CursorStore, u as openStore } from "./store-DPp8xMdp.js";
|
|
2
2
|
export { ConflictError, CursorStore, DeliveryRow, DeliveryStatus, DeliveryStore, LinkStore, MIGRATIONS, Migration, NotFoundError, PinStore, StoredEvent, openStore };
|
package/dist/store.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { a as NotFoundError, i as ConflictError } from "./sessions-DrCVTMfI.js";
|
|
2
|
-
import { n as openStore, t as MIGRATIONS } from "./store-
|
|
2
|
+
import { n as openStore, t as MIGRATIONS } from "./store-qtk4G3q4.js";
|
|
3
3
|
export { ConflictError, MIGRATIONS, NotFoundError, openStore };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as Link, g as ThreadMessage, s as Pin } from "./schema-HSZFobyd.js";
|
|
2
2
|
//#region src/connectors/types.d.ts
|
|
3
3
|
/** Host-injected transport: local = `gh` CLI shell-out (impl in packages/cli, Bun.$); Worker = App token fetch. */
|
|
4
4
|
interface ConnectorTransport {
|
package/dist/ws-protocol.d.ts
CHANGED
package/package.json
CHANGED