@holon-run/agentinbox 0.7.0 → 1.0.0-beta.1
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/README.md +36 -7
- package/dist/src/adapters.js +55 -0
- package/dist/src/backend.js +4 -16
- package/dist/src/cli.js +329 -52
- package/dist/src/client.js +2 -2
- package/dist/src/http.js +479 -55
- package/dist/src/runtime_gate.js +49 -41
- package/dist/src/service.js +922 -200
- package/dist/src/source_hosts.js +75 -0
- package/dist/src/source_resolution.js +2 -2
- package/dist/src/source_schema.js +2 -2
- package/dist/src/sources/feishu.js +66 -28
- package/dist/src/sources/github.js +222 -36
- package/dist/src/sources/remote.js +50 -3
- package/dist/src/sources/remote_modules.js +100 -15
- package/dist/src/store.js +875 -110
- package/dist/src/terminal.js +33 -7
- package/dist/src/util.js +46 -1
- package/drizzle/migrations/{0000_initial.sql → 0000_v1_initial.sql} +182 -6
- package/drizzle/migrations/0002_host_scoped_lifecycle_retirements.sql +42 -0
- package/drizzle/migrations/0003_subscription_tracked_resource_indexes.sql +5 -0
- package/drizzle/schema.ts +98 -3
- package/package.json +2 -1
- package/scripts/release_smoke.sh +123 -0
- package/dist/src/sources/remote_profiles.js +0 -13
- package/drizzle/migrations/0001_inbox_items_source_occurred_at_idx.sql +0 -3
- package/drizzle/migrations/0002_subscription_cleanup_policy.sql +0 -45
- package/drizzle/migrations/0003_subscription_lifecycle_retirements.sql +0 -14
- package/drizzle/migrations/0004_source_idle_states.sql +0 -10
- package/drizzle/migrations/0005_timers.sql +0 -22
- package/drizzle/migrations/0006_terminal_runtime_pid.sql +0 -1
- package/drizzle/migrations/0007_activation_dispatch_last_notified_fingerprint.sql +0 -2
- package/drizzle/migrations/0008_inbox_item_sequence.sql +0 -21
package/dist/src/terminal.js
CHANGED
|
@@ -16,6 +16,28 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
16
16
|
const node_os_1 = __importDefault(require("node:os"));
|
|
17
17
|
const node_path_1 = __importDefault(require("node:path"));
|
|
18
18
|
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
19
|
+
const AGENT_ADJECTIVES = [
|
|
20
|
+
"amber", "ancient", "autumn", "brisk", "bright", "bronze", "calm", "cedar",
|
|
21
|
+
"cinder", "clear", "cobalt", "cool", "copper", "crisp", "dawn", "deep",
|
|
22
|
+
"drift", "ember", "falling", "fern", "frost", "gentle", "glint", "golden",
|
|
23
|
+
"granite", "harbor", "hazel", "hidden", "hollow", "ice", "indigo", "iron",
|
|
24
|
+
"ivory", "jade", "juniper", "kind", "lake", "lively", "lunar", "maple",
|
|
25
|
+
"meadow", "misty", "moss", "night", "north", "oak", "olive", "opal",
|
|
26
|
+
"orchid", "pale", "pine", "plum", "quiet", "rapid", "river", "rose",
|
|
27
|
+
"royal", "rust", "sage", "silver", "soft", "solar", "spring", "stone",
|
|
28
|
+
"summer", "swift", "timber", "velvet", "warm", "west", "whisper", "winter",
|
|
29
|
+
];
|
|
30
|
+
const AGENT_ANIMALS = [
|
|
31
|
+
"antelope", "badger", "beaver", "bison", "bittern", "boar", "bobcat", "buffalo",
|
|
32
|
+
"canary", "cougar", "crane", "crow", "deer", "dingo", "dolphin", "dragonfly",
|
|
33
|
+
"eagle", "falcon", "finch", "firefly", "fox", "frog", "gazelle", "gecko",
|
|
34
|
+
"gull", "hare", "hawk", "heron", "ibis", "jaguar", "jay", "kingfisher",
|
|
35
|
+
"koala", "koi", "lemur", "leopard", "lizard", "lynx", "magpie", "martin",
|
|
36
|
+
"mink", "mole", "narwhal", "newt", "ocelot", "orca", "otter", "owl",
|
|
37
|
+
"panther", "parrot", "pebble", "pelican", "pika", "puma", "quail", "raven",
|
|
38
|
+
"robin", "seal", "sparrow", "stoat", "stork", "swift", "tern", "tiger",
|
|
39
|
+
"toad", "toucan", "trout", "viper", "weasel", "wren", "yak", "zebra",
|
|
40
|
+
];
|
|
19
41
|
function detectTerminalContext(env = process.env) {
|
|
20
42
|
const tmuxPaneId = normalizeOptionalString(env.TMUX_PANE);
|
|
21
43
|
const termProgram = normalizeOptionalString(env.TERM_PROGRAM);
|
|
@@ -48,7 +70,7 @@ function detectTerminalContext(env = process.env) {
|
|
|
48
70
|
throw new Error("unable to detect a supported terminal context");
|
|
49
71
|
}
|
|
50
72
|
function renderAgentPrompt(input) {
|
|
51
|
-
const totalUnackedCount = input.totalUnackedCount
|
|
73
|
+
const totalUnackedCount = input.totalUnackedCount;
|
|
52
74
|
const itemWord = totalUnackedCount === 1 ? "item" : "items";
|
|
53
75
|
const base = `AgentInbox: ${totalUnackedCount} unacked ${itemWord} in inbox ${input.inboxId}.`;
|
|
54
76
|
if (totalUnackedCount === 1 && input.preview) {
|
|
@@ -89,7 +111,7 @@ function deriveInlineItemPreview(item, summary) {
|
|
|
89
111
|
function normalizeInlinePreviewText(candidate) {
|
|
90
112
|
return normalizeInlinePreview(candidate);
|
|
91
113
|
}
|
|
92
|
-
function assignedAgentIdFromContext(input) {
|
|
114
|
+
function assignedAgentIdFromContext(input, attempt = 0) {
|
|
93
115
|
const primary = normalizeOptionalString(input.runtimeSessionId)
|
|
94
116
|
?? normalizeOptionalString(input.tmuxPaneId)
|
|
95
117
|
?? normalizeOptionalString(input.itermSessionId)
|
|
@@ -97,12 +119,16 @@ function assignedAgentIdFromContext(input) {
|
|
|
97
119
|
if (!primary) {
|
|
98
120
|
throw new Error("unable to derive agentId from terminal context");
|
|
99
121
|
}
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
122
|
+
const digest = node_crypto_1.default.createHash("sha256")
|
|
123
|
+
.update(`${input.runtimeKind ?? "unknown"}:${input.backend}:${primary}:${attempt}`)
|
|
124
|
+
.digest();
|
|
125
|
+
const adjective = AGENT_ADJECTIVES[digest[0] % AGENT_ADJECTIVES.length];
|
|
126
|
+
const animal = AGENT_ANIMALS[digest[1] % AGENT_ANIMALS.length];
|
|
127
|
+
if (attempt < 16) {
|
|
128
|
+
return `agt_${adjective}-${animal}`;
|
|
103
129
|
}
|
|
104
|
-
const
|
|
105
|
-
return `
|
|
130
|
+
const suffix = digest.toString("hex").slice(0, 3);
|
|
131
|
+
return `agt_${adjective}-${animal}-${suffix}`;
|
|
106
132
|
}
|
|
107
133
|
class TerminalDispatcher {
|
|
108
134
|
execAsync;
|
package/dist/src/util.js
CHANGED
|
@@ -4,16 +4,41 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.nowIso = nowIso;
|
|
7
|
+
exports.generateCanonicalId = generateCanonicalId;
|
|
7
8
|
exports.generateId = generateId;
|
|
9
|
+
exports.generateShortToken = generateShortToken;
|
|
8
10
|
exports.parseJsonArg = parseJsonArg;
|
|
9
11
|
exports.asObject = asObject;
|
|
10
12
|
exports.jsonResponse = jsonResponse;
|
|
13
|
+
exports.formatEntryRef = formatEntryRef;
|
|
14
|
+
exports.parseEntryRef = parseEntryRef;
|
|
15
|
+
exports.formatThreadRef = formatThreadRef;
|
|
16
|
+
exports.parseThreadRef = parseThreadRef;
|
|
11
17
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
18
|
+
const CANONICAL_ID_ALPHABET = "23456789abcdefghjkmnpqrstvwxyz";
|
|
19
|
+
const CANONICAL_ID_TOKEN_LENGTH = 12;
|
|
20
|
+
const ENTRY_THREAD_ID_PATTERN = new RegExp(`^(ent|thr)_[${CANONICAL_ID_ALPHABET}]{${CANONICAL_ID_TOKEN_LENGTH}}$`);
|
|
12
21
|
function nowIso() {
|
|
13
22
|
return new Date().toISOString();
|
|
14
23
|
}
|
|
24
|
+
function generateCanonicalId(prefix, length = CANONICAL_ID_TOKEN_LENGTH) {
|
|
25
|
+
return `${prefix}_${generateShortToken(length)}`;
|
|
26
|
+
}
|
|
15
27
|
function generateId(prefix) {
|
|
16
|
-
return
|
|
28
|
+
return generateCanonicalId(prefix);
|
|
29
|
+
}
|
|
30
|
+
function generateShortToken(length = CANONICAL_ID_TOKEN_LENGTH) {
|
|
31
|
+
let token = "";
|
|
32
|
+
while (token.length < length) {
|
|
33
|
+
const bytes = node_crypto_1.default.randomBytes(length - token.length);
|
|
34
|
+
for (const byte of bytes) {
|
|
35
|
+
token += CANONICAL_ID_ALPHABET[byte % CANONICAL_ID_ALPHABET.length];
|
|
36
|
+
if (token.length >= length) {
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return token;
|
|
17
42
|
}
|
|
18
43
|
function parseJsonArg(raw, source = "JSON argument", options) {
|
|
19
44
|
if (!raw) {
|
|
@@ -50,3 +75,23 @@ function asObject(value) {
|
|
|
50
75
|
function jsonResponse(data) {
|
|
51
76
|
return JSON.stringify(data, null, 2);
|
|
52
77
|
}
|
|
78
|
+
function formatEntryRef(entryId) {
|
|
79
|
+
return parseEntryRef(entryId);
|
|
80
|
+
}
|
|
81
|
+
function parseEntryRef(ref) {
|
|
82
|
+
const value = ref.trim();
|
|
83
|
+
if (!ENTRY_THREAD_ID_PATTERN.test(value) || !value.startsWith("ent_")) {
|
|
84
|
+
throw new Error(`invalid inbox entry id: ${ref}`);
|
|
85
|
+
}
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
function formatThreadRef(threadId) {
|
|
89
|
+
return parseThreadRef(threadId);
|
|
90
|
+
}
|
|
91
|
+
function parseThreadRef(ref) {
|
|
92
|
+
const value = ref.trim();
|
|
93
|
+
if (!ENTRY_THREAD_ID_PATTERN.test(value) || !value.startsWith("thr_")) {
|
|
94
|
+
throw new Error(`invalid digest thread id: ${ref}`);
|
|
95
|
+
}
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
create table if not exists sources (
|
|
2
2
|
source_id text primary key,
|
|
3
|
+
host_id text not null,
|
|
4
|
+
stream_kind text not null,
|
|
5
|
+
stream_key text not null,
|
|
3
6
|
source_type text not null,
|
|
4
7
|
source_key text not null,
|
|
5
8
|
config_ref text,
|
|
@@ -7,8 +10,7 @@ create table if not exists sources (
|
|
|
7
10
|
status text not null,
|
|
8
11
|
checkpoint text,
|
|
9
12
|
created_at text not null,
|
|
10
|
-
updated_at text not null
|
|
11
|
-
unique(source_type, source_key)
|
|
13
|
+
updated_at text not null
|
|
12
14
|
);
|
|
13
15
|
|
|
14
16
|
create table if not exists agents (
|
|
@@ -25,7 +27,11 @@ create table if not exists agents (
|
|
|
25
27
|
create table if not exists inboxes (
|
|
26
28
|
inbox_id text primary key,
|
|
27
29
|
owner_agent_id text not null unique,
|
|
28
|
-
created_at text not null
|
|
30
|
+
created_at text not null,
|
|
31
|
+
aggregation_enabled integer not null default 0,
|
|
32
|
+
aggregation_window_ms integer,
|
|
33
|
+
aggregation_max_items integer,
|
|
34
|
+
aggregation_max_thread_age_ms integer
|
|
29
35
|
);
|
|
30
36
|
|
|
31
37
|
create table if not exists subscriptions (
|
|
@@ -33,8 +39,8 @@ create table if not exists subscriptions (
|
|
|
33
39
|
agent_id text not null,
|
|
34
40
|
source_id text not null,
|
|
35
41
|
filter_json text not null,
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
tracked_resource_ref text,
|
|
43
|
+
cleanup_policy_json text not null,
|
|
38
44
|
start_policy text not null,
|
|
39
45
|
start_offset integer,
|
|
40
46
|
start_time text,
|
|
@@ -62,7 +68,9 @@ create table if not exists activation_targets (
|
|
|
62
68
|
iterm_session_id text,
|
|
63
69
|
created_at text not null,
|
|
64
70
|
updated_at text not null,
|
|
65
|
-
last_seen_at text not null
|
|
71
|
+
last_seen_at text not null,
|
|
72
|
+
runtime_pid integer,
|
|
73
|
+
min_unacked_items integer
|
|
66
74
|
);
|
|
67
75
|
|
|
68
76
|
create table if not exists activation_dispatch_states (
|
|
@@ -75,6 +83,12 @@ create table if not exists activation_dispatch_states (
|
|
|
75
83
|
pending_subscription_ids_json text not null,
|
|
76
84
|
pending_source_ids_json text not null,
|
|
77
85
|
updated_at text not null,
|
|
86
|
+
last_notified_fingerprint text,
|
|
87
|
+
defer_reason text,
|
|
88
|
+
defer_attempts integer not null default 0,
|
|
89
|
+
first_deferred_at text,
|
|
90
|
+
last_deferred_at text,
|
|
91
|
+
pending_fingerprint text,
|
|
78
92
|
primary key (agent_id, target_id)
|
|
79
93
|
);
|
|
80
94
|
|
|
@@ -89,6 +103,7 @@ create table if not exists inbox_items (
|
|
|
89
103
|
raw_payload_json text not null,
|
|
90
104
|
delivery_handle_json text,
|
|
91
105
|
acked_at text,
|
|
106
|
+
inbox_sequence integer,
|
|
92
107
|
unique(source_id, source_native_id, event_variant, inbox_id)
|
|
93
108
|
);
|
|
94
109
|
|
|
@@ -165,6 +180,107 @@ create table if not exists consumer_commits (
|
|
|
165
180
|
committed_at text not null
|
|
166
181
|
);
|
|
167
182
|
|
|
183
|
+
create table if not exists subscription_lifecycle_retirements (
|
|
184
|
+
subscription_id text primary key,
|
|
185
|
+
source_id text not null,
|
|
186
|
+
tracked_resource_ref text not null,
|
|
187
|
+
retire_at text not null,
|
|
188
|
+
terminal_state text,
|
|
189
|
+
terminal_result text,
|
|
190
|
+
terminal_occurred_at text,
|
|
191
|
+
created_at text not null,
|
|
192
|
+
updated_at text not null
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
create table if not exists source_idle_states (
|
|
196
|
+
source_id text primary key not null,
|
|
197
|
+
idle_since text not null,
|
|
198
|
+
auto_pause_at text not null,
|
|
199
|
+
auto_paused_at text,
|
|
200
|
+
updated_at text not null
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
create table if not exists timers (
|
|
204
|
+
schedule_id text primary key not null,
|
|
205
|
+
agent_id text not null,
|
|
206
|
+
status text not null,
|
|
207
|
+
mode text not null,
|
|
208
|
+
at text,
|
|
209
|
+
interval_ms integer,
|
|
210
|
+
cron_expr text,
|
|
211
|
+
timezone text not null,
|
|
212
|
+
message text not null,
|
|
213
|
+
sender text,
|
|
214
|
+
next_fire_at text,
|
|
215
|
+
last_fired_at text,
|
|
216
|
+
created_at text not null,
|
|
217
|
+
updated_at text not null
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
create table if not exists source_hosts (
|
|
221
|
+
host_id text primary key,
|
|
222
|
+
host_type text not null,
|
|
223
|
+
host_key text not null,
|
|
224
|
+
config_ref text,
|
|
225
|
+
config_json text not null,
|
|
226
|
+
status text not null,
|
|
227
|
+
created_at text not null,
|
|
228
|
+
updated_at text not null
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
create table if not exists digest_threads (
|
|
232
|
+
thread_id text primary key,
|
|
233
|
+
inbox_id text not null,
|
|
234
|
+
source_id text not null,
|
|
235
|
+
group_key text not null,
|
|
236
|
+
resource_ref text,
|
|
237
|
+
event_family text,
|
|
238
|
+
latest_revision integer not null,
|
|
239
|
+
latest_entry_id text,
|
|
240
|
+
status text not null,
|
|
241
|
+
summary text not null,
|
|
242
|
+
first_item_at text not null,
|
|
243
|
+
last_item_at text not null,
|
|
244
|
+
flush_after_at text,
|
|
245
|
+
created_at text not null,
|
|
246
|
+
updated_at text not null
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
create table if not exists inbox_entries (
|
|
250
|
+
entry_id text primary key,
|
|
251
|
+
inbox_id text not null,
|
|
252
|
+
kind text not null,
|
|
253
|
+
sequence integer not null unique,
|
|
254
|
+
thread_id text,
|
|
255
|
+
revision integer,
|
|
256
|
+
group_key text,
|
|
257
|
+
resource_ref text,
|
|
258
|
+
event_family text,
|
|
259
|
+
item_json text,
|
|
260
|
+
count integer not null,
|
|
261
|
+
summary text not null,
|
|
262
|
+
first_item_at text not null,
|
|
263
|
+
last_item_at text not null,
|
|
264
|
+
source_ids_json text not null,
|
|
265
|
+
subscription_ids_json text not null,
|
|
266
|
+
delivery_handle_json text,
|
|
267
|
+
acked_at text,
|
|
268
|
+
superseded_at text,
|
|
269
|
+
created_at text not null
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
create table if not exists inbox_entry_items (
|
|
273
|
+
entry_id text not null,
|
|
274
|
+
item_id text not null,
|
|
275
|
+
primary key (entry_id, item_id)
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
create table if not exists digest_thread_items (
|
|
279
|
+
thread_id text not null,
|
|
280
|
+
item_id text not null,
|
|
281
|
+
primary key (thread_id, item_id)
|
|
282
|
+
);
|
|
283
|
+
|
|
168
284
|
create unique index if not exists idx_activation_targets_terminal_tmux
|
|
169
285
|
on activation_targets(tmux_pane_id)
|
|
170
286
|
where kind = 'terminal' and tmux_pane_id is not null;
|
|
@@ -204,3 +320,63 @@ create index if not exists idx_consumers_stream
|
|
|
204
320
|
create index if not exists idx_consumer_commits_consumer
|
|
205
321
|
on consumer_commits(consumer_id, committed_offset);
|
|
206
322
|
|
|
323
|
+
create index if not exists idx_subscription_lifecycle_retirements_retire_at
|
|
324
|
+
on subscription_lifecycle_retirements(retire_at);
|
|
325
|
+
|
|
326
|
+
create index if not exists idx_source_idle_states_auto_pause_at
|
|
327
|
+
on source_idle_states(auto_pause_at);
|
|
328
|
+
|
|
329
|
+
create index if not exists idx_timers_agent_next_fire_at
|
|
330
|
+
on timers(agent_id, next_fire_at);
|
|
331
|
+
|
|
332
|
+
create index if not exists idx_timers_next_fire_at
|
|
333
|
+
on timers(next_fire_at);
|
|
334
|
+
|
|
335
|
+
create unique index if not exists idx_source_hosts_type_key
|
|
336
|
+
on source_hosts(host_type, host_key);
|
|
337
|
+
|
|
338
|
+
create unique index if not exists idx_sources_host_stream_key
|
|
339
|
+
on sources(host_id, stream_kind, stream_key);
|
|
340
|
+
|
|
341
|
+
create index if not exists idx_sources_type_key
|
|
342
|
+
on sources(source_type, source_key);
|
|
343
|
+
|
|
344
|
+
create unique index if not exists idx_inbox_items_inbox_sequence
|
|
345
|
+
on inbox_items(inbox_sequence);
|
|
346
|
+
|
|
347
|
+
create index if not exists idx_inbox_items_inbox_sequence_order
|
|
348
|
+
on inbox_items(inbox_id, inbox_sequence);
|
|
349
|
+
|
|
350
|
+
create index if not exists idx_inbox_items_source_occurred_at
|
|
351
|
+
on inbox_items(source_id, occurred_at);
|
|
352
|
+
|
|
353
|
+
create index if not exists idx_digest_threads_inbox_status
|
|
354
|
+
on digest_threads(inbox_id, status);
|
|
355
|
+
|
|
356
|
+
create index if not exists idx_digest_threads_group_status
|
|
357
|
+
on digest_threads(inbox_id, group_key, status);
|
|
358
|
+
|
|
359
|
+
create index if not exists idx_digest_threads_flush_after
|
|
360
|
+
on digest_threads(status, flush_after_at);
|
|
361
|
+
|
|
362
|
+
create index if not exists idx_inbox_entries_inbox_visible
|
|
363
|
+
on inbox_entries(inbox_id, acked_at, superseded_at, sequence);
|
|
364
|
+
|
|
365
|
+
create index if not exists idx_inbox_entries_thread_revision
|
|
366
|
+
on inbox_entries(thread_id, revision);
|
|
367
|
+
|
|
368
|
+
create index if not exists idx_inbox_entry_items_item
|
|
369
|
+
on inbox_entry_items(item_id);
|
|
370
|
+
|
|
371
|
+
create index if not exists idx_digest_thread_items_item
|
|
372
|
+
on digest_thread_items(item_id);
|
|
373
|
+
|
|
374
|
+
create trigger if not exists trg_inbox_items_set_sequence_after_insert
|
|
375
|
+
after insert on inbox_items
|
|
376
|
+
for each row
|
|
377
|
+
when new.inbox_sequence is null
|
|
378
|
+
begin
|
|
379
|
+
update inbox_items
|
|
380
|
+
set inbox_sequence = new.rowid
|
|
381
|
+
where rowid = new.rowid;
|
|
382
|
+
end;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
pragma foreign_keys=off;
|
|
2
|
+
|
|
3
|
+
create table if not exists subscription_lifecycle_retirements__new (
|
|
4
|
+
subscription_id text primary key,
|
|
5
|
+
host_id text not null,
|
|
6
|
+
tracked_resource_ref text not null,
|
|
7
|
+
retire_at text not null,
|
|
8
|
+
terminal_state text,
|
|
9
|
+
terminal_result text,
|
|
10
|
+
terminal_occurred_at text not null,
|
|
11
|
+
created_at text not null,
|
|
12
|
+
updated_at text not null
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
insert into subscription_lifecycle_retirements__new (
|
|
16
|
+
subscription_id,
|
|
17
|
+
host_id,
|
|
18
|
+
tracked_resource_ref,
|
|
19
|
+
retire_at,
|
|
20
|
+
terminal_state,
|
|
21
|
+
terminal_result,
|
|
22
|
+
terminal_occurred_at,
|
|
23
|
+
created_at,
|
|
24
|
+
updated_at
|
|
25
|
+
)
|
|
26
|
+
select
|
|
27
|
+
retirement.subscription_id,
|
|
28
|
+
coalesce(source.host_id, retirement.source_id),
|
|
29
|
+
retirement.tracked_resource_ref,
|
|
30
|
+
retirement.retire_at,
|
|
31
|
+
retirement.terminal_state,
|
|
32
|
+
retirement.terminal_result,
|
|
33
|
+
retirement.terminal_occurred_at,
|
|
34
|
+
retirement.created_at,
|
|
35
|
+
retirement.updated_at
|
|
36
|
+
from subscription_lifecycle_retirements as retirement
|
|
37
|
+
left join sources as source on source.source_id = retirement.source_id;
|
|
38
|
+
|
|
39
|
+
drop table subscription_lifecycle_retirements;
|
|
40
|
+
alter table subscription_lifecycle_retirements__new rename to subscription_lifecycle_retirements;
|
|
41
|
+
|
|
42
|
+
pragma foreign_keys=on;
|
package/drizzle/schema.ts
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
import { index, integer, primaryKey, sqliteTable, text, uniqueIndex } from "drizzle-orm/sqlite-core";
|
|
2
2
|
|
|
3
|
+
export const sourceHosts = sqliteTable("source_hosts", {
|
|
4
|
+
hostId: text("host_id").primaryKey(),
|
|
5
|
+
hostType: text("host_type").notNull(),
|
|
6
|
+
hostKey: text("host_key").notNull(),
|
|
7
|
+
configRef: text("config_ref"),
|
|
8
|
+
configJson: text("config_json").notNull(),
|
|
9
|
+
status: text("status").notNull(),
|
|
10
|
+
createdAt: text("created_at").notNull(),
|
|
11
|
+
updatedAt: text("updated_at").notNull(),
|
|
12
|
+
}, (table) => ({
|
|
13
|
+
hostTypeKey: uniqueIndex("idx_source_hosts_type_key").on(table.hostType, table.hostKey),
|
|
14
|
+
}));
|
|
15
|
+
|
|
3
16
|
export const sources = sqliteTable("sources", {
|
|
4
17
|
sourceId: text("source_id").primaryKey(),
|
|
18
|
+
hostId: text("host_id").notNull(),
|
|
19
|
+
streamKind: text("stream_kind").notNull(),
|
|
20
|
+
streamKey: text("stream_key").notNull(),
|
|
5
21
|
sourceType: text("source_type").notNull(),
|
|
6
22
|
sourceKey: text("source_key").notNull(),
|
|
7
23
|
configRef: text("config_ref"),
|
|
@@ -11,7 +27,8 @@ export const sources = sqliteTable("sources", {
|
|
|
11
27
|
createdAt: text("created_at").notNull(),
|
|
12
28
|
updatedAt: text("updated_at").notNull(),
|
|
13
29
|
}, (table) => ({
|
|
14
|
-
sourceTypeKey:
|
|
30
|
+
sourceTypeKey: index("idx_sources_type_key").on(table.sourceType, table.sourceKey),
|
|
31
|
+
hostStreamKey: uniqueIndex("idx_sources_host_stream_key").on(table.hostId, table.streamKind, table.streamKey),
|
|
15
32
|
}));
|
|
16
33
|
|
|
17
34
|
export const sourceIdleStates = sqliteTable("source_idle_states", {
|
|
@@ -60,6 +77,10 @@ export const agents = sqliteTable("agents", {
|
|
|
60
77
|
export const inboxes = sqliteTable("inboxes", {
|
|
61
78
|
inboxId: text("inbox_id").primaryKey(),
|
|
62
79
|
ownerAgentId: text("owner_agent_id").notNull(),
|
|
80
|
+
aggregationEnabled: integer("aggregation_enabled").notNull().default(0),
|
|
81
|
+
aggregationWindowMs: integer("aggregation_window_ms"),
|
|
82
|
+
aggregationMaxItems: integer("aggregation_max_items"),
|
|
83
|
+
aggregationMaxThreadAgeMs: integer("aggregation_max_thread_age_ms"),
|
|
63
84
|
createdAt: text("created_at").notNull(),
|
|
64
85
|
}, (table) => ({
|
|
65
86
|
ownerAgentIdUnique: uniqueIndex("idx_inboxes_owner_agent_id").on(table.ownerAgentId),
|
|
@@ -76,11 +97,14 @@ export const subscriptions = sqliteTable("subscriptions", {
|
|
|
76
97
|
startOffset: integer("start_offset"),
|
|
77
98
|
startTime: text("start_time"),
|
|
78
99
|
createdAt: text("created_at").notNull(),
|
|
79
|
-
})
|
|
100
|
+
}, (table) => ({
|
|
101
|
+
trackedResourceRefIdx: index("idx_subscriptions_tracked_resource_ref").on(table.trackedResourceRef),
|
|
102
|
+
trackedResourceRefSourceIdx: index("idx_subscriptions_tracked_resource_source").on(table.trackedResourceRef, table.sourceId),
|
|
103
|
+
}));
|
|
80
104
|
|
|
81
105
|
export const subscriptionLifecycleRetirements = sqliteTable("subscription_lifecycle_retirements", {
|
|
82
106
|
subscriptionId: text("subscription_id").primaryKey(),
|
|
83
|
-
|
|
107
|
+
hostId: text("host_id").notNull(),
|
|
84
108
|
trackedResourceRef: text("tracked_resource_ref").notNull(),
|
|
85
109
|
retireAt: text("retire_at").notNull(),
|
|
86
110
|
terminalState: text("terminal_state"),
|
|
@@ -101,6 +125,7 @@ export const activationTargets = sqliteTable("activation_targets", {
|
|
|
101
125
|
lastError: text("last_error"),
|
|
102
126
|
mode: text("mode").notNull(),
|
|
103
127
|
notifyLeaseMs: integer("notify_lease_ms").notNull(),
|
|
128
|
+
minUnackedItems: integer("min_unacked_items"),
|
|
104
129
|
url: text("url"),
|
|
105
130
|
runtimeKind: text("runtime_kind"),
|
|
106
131
|
runtimeSessionId: text("runtime_session_id"),
|
|
@@ -123,6 +148,11 @@ export const activationDispatchStates = sqliteTable("activation_dispatch_states"
|
|
|
123
148
|
status: text("status").notNull(),
|
|
124
149
|
leaseExpiresAt: text("lease_expires_at"),
|
|
125
150
|
lastNotifiedFingerprint: text("last_notified_fingerprint"),
|
|
151
|
+
deferReason: text("defer_reason"),
|
|
152
|
+
deferAttempts: integer("defer_attempts").notNull().default(0),
|
|
153
|
+
firstDeferredAt: text("first_deferred_at"),
|
|
154
|
+
lastDeferredAt: text("last_deferred_at"),
|
|
155
|
+
pendingFingerprint: text("pending_fingerprint"),
|
|
126
156
|
pendingNewItemCount: integer("pending_new_item_count").notNull(),
|
|
127
157
|
pendingSummary: text("pending_summary"),
|
|
128
158
|
pendingSubscriptionIdsJson: text("pending_subscription_ids_json").notNull(),
|
|
@@ -155,6 +185,71 @@ export const inboxItems = sqliteTable("inbox_items", {
|
|
|
155
185
|
sourceOccurredAtIdx: index("idx_inbox_items_source_occurred_at").on(table.sourceId, table.occurredAt),
|
|
156
186
|
}));
|
|
157
187
|
|
|
188
|
+
export const digestThreads = sqliteTable("digest_threads", {
|
|
189
|
+
threadId: text("thread_id").primaryKey(),
|
|
190
|
+
inboxId: text("inbox_id").notNull(),
|
|
191
|
+
sourceId: text("source_id").notNull(),
|
|
192
|
+
groupKey: text("group_key").notNull(),
|
|
193
|
+
resourceRef: text("resource_ref"),
|
|
194
|
+
eventFamily: text("event_family"),
|
|
195
|
+
latestRevision: integer("latest_revision").notNull(),
|
|
196
|
+
latestEntryId: text("latest_entry_id"),
|
|
197
|
+
status: text("status").notNull(),
|
|
198
|
+
summary: text("summary").notNull(),
|
|
199
|
+
firstItemAt: text("first_item_at").notNull(),
|
|
200
|
+
lastItemAt: text("last_item_at").notNull(),
|
|
201
|
+
flushAfterAt: text("flush_after_at"),
|
|
202
|
+
createdAt: text("created_at").notNull(),
|
|
203
|
+
updatedAt: text("updated_at").notNull(),
|
|
204
|
+
}, (table) => ({
|
|
205
|
+
inboxStatusIdx: index("idx_digest_threads_inbox_status").on(table.inboxId, table.status),
|
|
206
|
+
groupStatusIdx: index("idx_digest_threads_group_status").on(table.inboxId, table.groupKey, table.status),
|
|
207
|
+
flushIdx: index("idx_digest_threads_flush_after").on(table.status, table.flushAfterAt),
|
|
208
|
+
}));
|
|
209
|
+
|
|
210
|
+
export const inboxEntries = sqliteTable("inbox_entries", {
|
|
211
|
+
entryId: text("entry_id").primaryKey(),
|
|
212
|
+
inboxId: text("inbox_id").notNull(),
|
|
213
|
+
kind: text("kind").notNull(),
|
|
214
|
+
sequence: integer("sequence").notNull(),
|
|
215
|
+
threadId: text("thread_id"),
|
|
216
|
+
revision: integer("revision"),
|
|
217
|
+
groupKey: text("group_key"),
|
|
218
|
+
resourceRef: text("resource_ref"),
|
|
219
|
+
eventFamily: text("event_family"),
|
|
220
|
+
itemJson: text("item_json"),
|
|
221
|
+
count: integer("count").notNull(),
|
|
222
|
+
summary: text("summary").notNull(),
|
|
223
|
+
firstItemAt: text("first_item_at").notNull(),
|
|
224
|
+
lastItemAt: text("last_item_at").notNull(),
|
|
225
|
+
sourceIdsJson: text("source_ids_json").notNull(),
|
|
226
|
+
subscriptionIdsJson: text("subscription_ids_json").notNull(),
|
|
227
|
+
deliveryHandleJson: text("delivery_handle_json"),
|
|
228
|
+
ackedAt: text("acked_at"),
|
|
229
|
+
supersededAt: text("superseded_at"),
|
|
230
|
+
createdAt: text("created_at").notNull(),
|
|
231
|
+
}, (table) => ({
|
|
232
|
+
sequenceUnique: uniqueIndex("idx_inbox_entries_sequence").on(table.sequence),
|
|
233
|
+
inboxVisibleIdx: index("idx_inbox_entries_inbox_visible").on(table.inboxId, table.ackedAt, table.supersededAt, table.sequence),
|
|
234
|
+
threadRevisionIdx: index("idx_inbox_entries_thread_revision").on(table.threadId, table.revision),
|
|
235
|
+
}));
|
|
236
|
+
|
|
237
|
+
export const inboxEntryItems = sqliteTable("inbox_entry_items", {
|
|
238
|
+
entryId: text("entry_id").notNull(),
|
|
239
|
+
itemId: text("item_id").notNull(),
|
|
240
|
+
}, (table) => ({
|
|
241
|
+
pk: primaryKey({ columns: [table.entryId, table.itemId] }),
|
|
242
|
+
itemIdx: index("idx_inbox_entry_items_item").on(table.itemId),
|
|
243
|
+
}));
|
|
244
|
+
|
|
245
|
+
export const digestThreadItems = sqliteTable("digest_thread_items", {
|
|
246
|
+
threadId: text("thread_id").notNull(),
|
|
247
|
+
itemId: text("item_id").notNull(),
|
|
248
|
+
}, (table) => ({
|
|
249
|
+
pk: primaryKey({ columns: [table.threadId, table.itemId] }),
|
|
250
|
+
itemIdx: index("idx_digest_thread_items_item").on(table.itemId),
|
|
251
|
+
}));
|
|
252
|
+
|
|
158
253
|
export const activations = sqliteTable("activations", {
|
|
159
254
|
activationId: text("activation_id").primaryKey(),
|
|
160
255
|
kind: text("kind").notNull(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holon-run/agentinbox",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
4
|
"description": "Local event subscription and delivery service for agents.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"docs:deploy": "npm run docs:index && npm run docs:build && npx wrangler@4 deploy --config wrangler.jsonc",
|
|
39
39
|
"db:migrations:generate": "drizzle-kit generate --config drizzle.config.ts",
|
|
40
40
|
"prepack": "npm run build",
|
|
41
|
+
"smoke:release": "bash scripts/release_smoke.sh",
|
|
41
42
|
"test": "node --require ts-node/register --test --test-force-exit test/*.test.ts"
|
|
42
43
|
},
|
|
43
44
|
"dependencies": {
|