@holon-run/agentinbox 1.0.2 → 1.0.4

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/src/store.js CHANGED
@@ -288,8 +288,10 @@ class AgentInboxStore {
288
288
  ]);
289
289
  this.persist();
290
290
  }
291
- listSourceHosts() {
292
- const rows = this.getAll("select * from source_hosts order by created_at asc");
291
+ listSourceHosts(limit) {
292
+ const rows = typeof limit === "number"
293
+ ? this.getAll("select * from source_hosts order by created_at asc, host_id asc limit ?", [limit])
294
+ : this.getAll("select * from source_hosts order by created_at asc, host_id asc");
293
295
  return rows.map((row) => this.mapSourceHost(row));
294
296
  }
295
297
  updateSourceHostDefinition(hostId, input) {
@@ -362,8 +364,10 @@ class AgentInboxStore {
362
364
  });
363
365
  this.persist();
364
366
  }
365
- listSources() {
366
- const rows = this.getAll("select * from sources order by created_at asc");
367
+ listSources(limit) {
368
+ const rows = typeof limit === "number"
369
+ ? this.getAll("select * from sources order by created_at asc, source_id asc limit ?", [limit])
370
+ : this.getAll("select * from sources order by created_at asc, source_id asc");
367
371
  return rows.map((row) => this.mapSource(row));
368
372
  }
369
373
  getSourceIdleState(sourceId) {
@@ -505,8 +509,10 @@ class AgentInboxStore {
505
509
  this.persist();
506
510
  return this.getAgent(agentId);
507
511
  }
508
- listAgents() {
509
- const rows = this.getAll("select * from agents order by created_at asc");
512
+ listAgents(limit) {
513
+ const rows = typeof limit === "number"
514
+ ? this.getAll("select * from agents order by created_at asc, agent_id asc limit ?", [limit])
515
+ : this.getAll("select * from agents order by created_at asc, agent_id asc");
510
516
  return rows.map((row) => this.mapAgent(row));
511
517
  }
512
518
  getInbox(inboxId) {
@@ -556,8 +562,10 @@ class AgentInboxStore {
556
562
  this.persist();
557
563
  return this.getInbox(inboxId);
558
564
  }
559
- listInboxes() {
560
- const rows = this.getAll("select * from inboxes order by created_at asc");
565
+ listInboxes(limit) {
566
+ const rows = typeof limit === "number"
567
+ ? this.getAll("select * from inboxes order by created_at asc, inbox_id asc limit ?", [limit])
568
+ : this.getAll("select * from inboxes order by created_at asc, inbox_id asc");
561
569
  return rows.map((row) => this.mapInbox(row));
562
570
  }
563
571
  getSubscription(subscriptionId) {
@@ -583,16 +591,41 @@ class AgentInboxStore {
583
591
  ]);
584
592
  this.persist();
585
593
  }
586
- listSubscriptions() {
587
- const rows = this.getAll("select * from subscriptions order by created_at asc");
594
+ listSubscriptions(limit) {
595
+ const rows = typeof limit === "number"
596
+ ? this.getAll("select * from subscriptions order by created_at asc, subscription_id asc limit ?", [limit])
597
+ : this.getAll("select * from subscriptions order by created_at asc, subscription_id asc");
588
598
  return rows.map((row) => this.mapSubscription(row));
589
599
  }
590
- listSubscriptionsForSource(sourceId) {
591
- const rows = this.getAll("select * from subscriptions where source_id = ? order by created_at asc", [sourceId]);
600
+ listSubscriptionsFiltered(filters) {
601
+ const clauses = [];
602
+ const params = [];
603
+ if (filters?.sourceId) {
604
+ clauses.push("source_id = ?");
605
+ params.push(filters.sourceId);
606
+ }
607
+ if (filters?.agentId) {
608
+ clauses.push("agent_id = ?");
609
+ params.push(filters.agentId);
610
+ }
611
+ const where = clauses.length > 0 ? ` where ${clauses.join(" and ")}` : "";
612
+ const limitClause = typeof filters?.limit === "number" ? " limit ?" : "";
613
+ if (typeof filters?.limit === "number") {
614
+ params.push(filters.limit);
615
+ }
616
+ const rows = this.getAll(`select * from subscriptions${where} order by created_at asc, subscription_id asc${limitClause}`, params);
617
+ return rows.map((row) => this.mapSubscription(row));
618
+ }
619
+ listSubscriptionsForSource(sourceId, limit) {
620
+ const rows = typeof limit === "number"
621
+ ? this.getAll("select * from subscriptions where source_id = ? order by created_at asc, subscription_id asc limit ?", [sourceId, limit])
622
+ : this.getAll("select * from subscriptions where source_id = ? order by created_at asc, subscription_id asc", [sourceId]);
592
623
  return rows.map((row) => this.mapSubscription(row));
593
624
  }
594
- listSubscriptionsForAgent(agentId) {
595
- const rows = this.getAll("select * from subscriptions where agent_id = ? order by created_at asc", [agentId]);
625
+ listSubscriptionsForAgent(agentId, limit) {
626
+ const rows = typeof limit === "number"
627
+ ? this.getAll("select * from subscriptions where agent_id = ? order by created_at asc, subscription_id asc limit ?", [agentId, limit])
628
+ : this.getAll("select * from subscriptions where agent_id = ? order by created_at asc, subscription_id asc", [agentId]);
596
629
  return rows.map((row) => this.mapSubscription(row));
597
630
  }
598
631
  listSubscriptionsForHostTrackedResourceRef(hostId, trackedResourceRef) {
@@ -821,12 +854,16 @@ class AgentInboxStore {
821
854
  this.persist();
822
855
  return this.getActivationTarget(targetId);
823
856
  }
824
- listActivationTargets() {
825
- const rows = this.getAll("select * from activation_targets order by created_at asc");
857
+ listActivationTargets(limit) {
858
+ const rows = typeof limit === "number"
859
+ ? this.getAll("select * from activation_targets order by created_at asc, target_id asc limit ?", [limit])
860
+ : this.getAll("select * from activation_targets order by created_at asc, target_id asc");
826
861
  return rows.map((row) => this.mapActivationTarget(row));
827
862
  }
828
- listActivationTargetsForAgent(agentId) {
829
- const rows = this.getAll("select * from activation_targets where agent_id = ? order by created_at asc", [agentId]);
863
+ listActivationTargetsForAgent(agentId, limit) {
864
+ const rows = typeof limit === "number"
865
+ ? this.getAll("select * from activation_targets where agent_id = ? order by created_at asc, target_id asc limit ?", [agentId, limit])
866
+ : this.getAll("select * from activation_targets where agent_id = ? order by created_at asc, target_id asc", [agentId]);
830
867
  return rows.map((row) => this.mapActivationTarget(row));
831
868
  }
832
869
  deleteActivationTarget(agentId, targetId) {
@@ -902,12 +939,16 @@ class AgentInboxStore {
902
939
  const row = this.getOne("select * from timers where schedule_id = ?", [scheduleId]);
903
940
  return row ? this.mapTimer(row) : null;
904
941
  }
905
- listTimers() {
906
- const rows = this.getAll("select * from timers order by created_at asc");
942
+ listTimers(limit) {
943
+ const rows = typeof limit === "number"
944
+ ? this.getAll("select * from timers order by created_at asc, schedule_id asc limit ?", [limit])
945
+ : this.getAll("select * from timers order by created_at asc, schedule_id asc");
907
946
  return rows.map((row) => this.mapTimer(row));
908
947
  }
909
- listTimersForAgent(agentId) {
910
- const rows = this.getAll("select * from timers where agent_id = ? order by created_at asc", [agentId]);
948
+ listTimersForAgent(agentId, limit) {
949
+ const rows = typeof limit === "number"
950
+ ? this.getAll("select * from timers where agent_id = ? order by created_at asc, schedule_id asc limit ?", [agentId, limit])
951
+ : this.getAll("select * from timers where agent_id = ? order by created_at asc, schedule_id asc", [agentId]);
911
952
  return rows.map((row) => this.mapTimer(row));
912
953
  }
913
954
  listDueTimers(cutoffIso) {
@@ -1015,8 +1056,8 @@ class AgentInboxStore {
1015
1056
  this.db.run(`
1016
1057
  insert or ignore into inbox_items (
1017
1058
  item_id, source_id, source_native_id, event_variant, inbox_id, occurred_at,
1018
- metadata_json, raw_payload_json, delivery_handle_json, acked_at
1019
- ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1059
+ metadata_json, raw_payload_json, provider_raw_payload_json, delivery_handle_json, acked_at
1060
+ ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1020
1061
  `, [
1021
1062
  item.itemId,
1022
1063
  item.sourceId,
@@ -1026,6 +1067,7 @@ class AgentInboxStore {
1026
1067
  item.occurredAt,
1027
1068
  JSON.stringify(item.metadata),
1028
1069
  JSON.stringify(item.rawPayload),
1070
+ item.providerRawPayload ? JSON.stringify(item.providerRawPayload) : null,
1029
1071
  item.deliveryHandle ? JSON.stringify(item.deliveryHandle) : null,
1030
1072
  item.ackedAt ?? null,
1031
1073
  ]);
@@ -1059,6 +1101,7 @@ class AgentInboxStore {
1059
1101
  occurredAt: item.occurredAt,
1060
1102
  metadata: item.metadata,
1061
1103
  rawPayload: item.rawPayload,
1104
+ providerRawPayload: item.providerRawPayload,
1062
1105
  deliveryHandle: item.deliveryHandle ?? null,
1063
1106
  }),
1064
1107
  count: 1,
@@ -1092,7 +1135,11 @@ class AgentInboxStore {
1092
1135
  filters.push("sequence > ?");
1093
1136
  params.push(Number(anchor.sequence));
1094
1137
  }
1095
- const rows = this.getAll(`select * from inbox_entries where ${filters.join(" and ")} order by sequence asc`, params);
1138
+ const sql = `select * from inbox_entries where ${filters.join(" and ")} order by sequence asc${typeof options?.limit === "number" ? " limit ?" : ""}`;
1139
+ if (typeof options?.limit === "number") {
1140
+ params.push(options.limit);
1141
+ }
1142
+ const rows = this.getAll(sql, params);
1096
1143
  return this.mapInboxEntries(rows);
1097
1144
  }
1098
1145
  getInboxEntry(entryId) {
@@ -1273,7 +1320,11 @@ class AgentInboxStore {
1273
1320
  if (!includeAcked) {
1274
1321
  filters.push("acked_at is null");
1275
1322
  }
1276
- const rows = this.getAll(`select * from inbox_items where ${filters.join(" and ")} order by coalesce(inbox_sequence, rowid) asc`, params);
1323
+ const sql = `select * from inbox_items where ${filters.join(" and ")} order by coalesce(inbox_sequence, rowid) asc${typeof options?.limit === "number" ? " limit ?" : ""}`;
1324
+ if (typeof options?.limit === "number") {
1325
+ params.push(options.limit);
1326
+ }
1327
+ const rows = this.getAll(sql, params);
1277
1328
  return rows.map((row) => this.mapInboxItem(row));
1278
1329
  }
1279
1330
  ackItems(inboxId, itemIds, ackedAt) {
@@ -1406,8 +1457,10 @@ class AgentInboxStore {
1406
1457
  const row = this.getOne("select * from streams where stream_id = ?", [streamId]);
1407
1458
  return row ? this.mapStream(row) : null;
1408
1459
  }
1409
- listStreams() {
1410
- const rows = this.getAll("select * from streams order by created_at asc");
1460
+ listStreams(limit) {
1461
+ const rows = typeof limit === "number"
1462
+ ? this.getAll("select * from streams order by created_at asc, stream_id asc limit ?", [limit])
1463
+ : this.getAll("select * from streams order by created_at asc, stream_id asc");
1411
1464
  return rows.map((row) => this.mapStream(row));
1412
1465
  }
1413
1466
  getStreamBySourceId(sourceId) {
@@ -1418,14 +1471,15 @@ class AgentInboxStore {
1418
1471
  const occurredAt = event.occurredAt ?? (0, util_1.nowIso)();
1419
1472
  const metadataJson = JSON.stringify(event.metadata ?? {});
1420
1473
  const rawPayloadJson = JSON.stringify(event.rawPayload ?? {});
1474
+ const providerRawPayloadJson = event.providerRawPayload ? JSON.stringify(event.providerRawPayload) : null;
1421
1475
  const deliveryHandleJson = event.deliveryHandle ? JSON.stringify(event.deliveryHandle) : null;
1422
1476
  for (let attempt = 0; attempt < 5; attempt += 1) {
1423
1477
  const before = this.changes();
1424
1478
  this.db.run(`
1425
1479
  insert or ignore into stream_events (
1426
1480
  stream_event_id, stream_id, source_id, source_native_id, event_variant,
1427
- occurred_at, metadata_json, raw_payload_json, delivery_handle_json, created_at
1428
- ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1481
+ occurred_at, metadata_json, raw_payload_json, provider_raw_payload_json, delivery_handle_json, created_at
1482
+ ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1429
1483
  `, [
1430
1484
  (0, util_1.generateCanonicalId)("evt"),
1431
1485
  streamId,
@@ -1435,6 +1489,7 @@ class AgentInboxStore {
1435
1489
  occurredAt,
1436
1490
  metadataJson,
1437
1491
  rawPayloadJson,
1492
+ providerRawPayloadJson,
1438
1493
  deliveryHandleJson,
1439
1494
  (0, util_1.nowIso)(),
1440
1495
  ]);
@@ -1821,6 +1876,9 @@ class AgentInboxStore {
1821
1876
  occurredAt: String(row.occurred_at),
1822
1877
  metadata: parseJson(row.metadata_json),
1823
1878
  rawPayload: parseJson(row.raw_payload_json),
1879
+ providerRawPayload: row.provider_raw_payload_json
1880
+ ? parseJson(row.provider_raw_payload_json)
1881
+ : undefined,
1824
1882
  deliveryHandle: row.delivery_handle_json
1825
1883
  ? parseJson(row.delivery_handle_json)
1826
1884
  : null,
@@ -1858,6 +1916,7 @@ class AgentInboxStore {
1858
1916
  occurredAt: item.occurredAt,
1859
1917
  metadata: item.metadata,
1860
1918
  rawPayload: item.rawPayload,
1919
+ providerRawPayload: item.providerRawPayload,
1861
1920
  item,
1862
1921
  };
1863
1922
  }
@@ -2130,6 +2189,9 @@ class AgentInboxStore {
2130
2189
  occurredAt: String(row.occurred_at),
2131
2190
  metadata: parseJson(row.metadata_json),
2132
2191
  rawPayload: parseJson(row.raw_payload_json),
2192
+ providerRawPayload: row.provider_raw_payload_json
2193
+ ? parseJson(row.provider_raw_payload_json)
2194
+ : undefined,
2133
2195
  deliveryHandle: row.delivery_handle_json
2134
2196
  ? parseJson(row.delivery_handle_json)
2135
2197
  : null,
@@ -0,0 +1,3 @@
1
+ alter table inbox_items add column provider_raw_payload_json text;
2
+
3
+ alter table stream_events add column provider_raw_payload_json text;
package/drizzle/schema.ts CHANGED
@@ -173,6 +173,7 @@ export const inboxItems = sqliteTable("inbox_items", {
173
173
  occurredAt: text("occurred_at").notNull(),
174
174
  metadataJson: text("metadata_json").notNull(),
175
175
  rawPayloadJson: text("raw_payload_json").notNull(),
176
+ providerRawPayloadJson: text("provider_raw_payload_json"),
176
177
  deliveryHandleJson: text("delivery_handle_json"),
177
178
  ackedAt: text("acked_at"),
178
179
  }, (table) => ({
@@ -302,6 +303,7 @@ export const streamEvents = sqliteTable("stream_events", {
302
303
  occurredAt: text("occurred_at").notNull(),
303
304
  metadataJson: text("metadata_json").notNull(),
304
305
  rawPayloadJson: text("raw_payload_json").notNull(),
306
+ providerRawPayloadJson: text("provider_raw_payload_json"),
305
307
  deliveryHandleJson: text("delivery_handle_json"),
306
308
  createdAt: text("created_at").notNull(),
307
309
  }, (table) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holon-run/agentinbox",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Local event subscription and delivery service for agents.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -20,6 +20,7 @@
20
20
  "files": [
21
21
  "dist/src/",
22
22
  "scripts/",
23
+ "skills/",
23
24
  "drizzle/",
24
25
  "README.md",
25
26
  "LICENSE"
@@ -43,7 +44,7 @@
43
44
  },
44
45
  "dependencies": {
45
46
  "@fastify/swagger": "^9.5.2",
46
- "@holon-run/uxc-daemon-client": "^0.15.3",
47
+ "@holon-run/uxc-daemon-client": "^0.15.4",
47
48
  "fastify": "^5.6.1",
48
49
  "jexl": "^2.3.0",
49
50
  "sql.js": "^1.13.0"
@@ -0,0 +1,18 @@
1
+ # Skills
2
+
3
+ The docs site exposes selected repository skills so they can be handed directly
4
+ to another agent.
5
+
6
+ For `AgentInbox`, the preferred first-run path is skill-first onboarding:
7
+
8
+ - hand the bundled `AgentInbox` skill to the agent
9
+ - let the agent verify `agentinbox`, `uxc`, and GitHub auth
10
+ - let the agent register the current session and set up standing subscriptions
11
+
12
+ <!-- INDEX:START -->
13
+
14
+ - [agentinbox](./agentinbox/)
15
+ Use the local AgentInbox service to onboard the current session, connect GitHub through UXC, and operate sources, subscriptions, and the agent inbox.
16
+ <!-- mdorigin:index kind=article -->
17
+
18
+ <!-- INDEX:END -->
@@ -0,0 +1,239 @@
1
+ ---
2
+ name: agentinbox
3
+ description: Use the local AgentInbox service to onboard the current session, manage shared sources and subscriptions, connect external providers such as GitHub through UXC, and operate the agent inbox.
4
+ metadata:
5
+ short-description: Operate AgentInbox sources and subscriptions
6
+ ---
7
+
8
+ # AgentInbox Skill
9
+
10
+ Use this skill when the current agent should set up or use the local
11
+ `agentinbox` daemon.
12
+
13
+ Primary docs:
14
+
15
+ - `https://agentinbox.holon.run/guides/onboarding-with-agent-skill`
16
+ - `https://agentinbox.holon.run/guides/getting-started`
17
+ - `https://agentinbox.holon.run/guides/review-workflows`
18
+ - `https://agentinbox.holon.run/reference/cli`
19
+
20
+ ## Install
21
+
22
+ Install `agentinbox` if it is not already available:
23
+
24
+ ```bash
25
+ npm install -g @holon-run/agentinbox
26
+ ```
27
+
28
+ Install `uxc` if GitHub or Feishu adapters are needed:
29
+
30
+ ```bash
31
+ brew tap holon-run/homebrew-tap
32
+ brew install uxc
33
+ ```
34
+
35
+ UXC repository:
36
+
37
+ - `https://github.com/holon-run/uxc`
38
+
39
+ ## First-Run Onboarding
40
+
41
+ Recommended first-run sequence:
42
+
43
+ 1. if GitHub access is needed and `gh` is already authenticated, import that auth into `uxc`
44
+ 2. register the current terminal session
45
+ 3. follow the required sources or resources using the docs examples
46
+
47
+ If GitHub-backed adapters are needed:
48
+
49
+ ```bash
50
+ gh auth status
51
+ uxc auth credential import github --from gh
52
+ ```
53
+
54
+ This `gh` import path requires `uxc` 0.15.3 or newer.
55
+
56
+ Register the current terminal/runtime session:
57
+
58
+ ```bash
59
+ agentinbox agent register
60
+ ```
61
+
62
+ Treat the returned `agentId` as the stable identity for later commands.
63
+
64
+ ## Usage Discipline
65
+
66
+ Defaults:
67
+
68
+ - do not create one GitHub source per PR unless the source itself must be PR-
69
+ scoped
70
+ - prefer shared repo/repo-CI sources plus narrow follows or subscriptions
71
+ - prefer `agentinbox follow` over manual `subscription add` when a follow
72
+ template exists
73
+ - if a source exposes only subscription shortcuts, prefer the shortcut over
74
+ manually reconstructing the same filter and lifecycle fields
75
+ - treat unused task-specific subscriptions as cleanup debt
76
+ - if GitHub polling volume looks high, inspect old subscriptions and duplicate
77
+ temporary sources before adding more
78
+
79
+ This matches the AgentInbox model: sources are shared hosting units, while
80
+ subscriptions carry agent-specific filtering and delivery intent.
81
+
82
+ ## Default Async Lifecycle
83
+
84
+ Default to AgentInbox when the work is not purely synchronous in the current
85
+ session.
86
+
87
+ Typical cases:
88
+
89
+ - delayed follow-up that should happen after you stop actively looking at the
90
+ terminal
91
+ - waiting for PR review, CI completion, issue replies, or other external
92
+ events
93
+ - anything that must survive session boundaries instead of living only in chat
94
+ memory
95
+ - reminders that should fire at a specific time or on a recurring schedule
96
+
97
+ Important boundary:
98
+
99
+ - inbox items, subscriptions, and timers can outlive the current terminal
100
+ session
101
+ - terminal delivery does not automatically survive session boundaries just
102
+ because the inbox state does
103
+ - if the original runtime/terminal disappears, items may keep accumulating in
104
+ the inbox while notifications stop reaching you
105
+ - if a later session should resume the same logical agent, re-register or
106
+ explicitly rebind that agent to the current terminal before assuming prompt
107
+ delivery is live again
108
+
109
+ Lifecycle:
110
+
111
+ 1. register once per live terminal/runtime session
112
+ 2. reuse broad shared sources
113
+ 3. add narrow task-scoped follows, subscriptions, or timers
114
+ 4. read inbox items in bounded batches and ack only the reviewed batch
115
+ 5. clean up task-scoped subscriptions/timers after merge, closure, or abandonment
116
+
117
+ For PR and review workflows, prefer `follow` templates. They reuse shared
118
+ sources, expand the source-specific filters, and attach cleanup behavior:
119
+
120
+ ```bash
121
+ agentinbox agent register
122
+ agentinbox follow github pr --agent-id <agentId> --arg owner=holon-run --arg repo=agentinbox --arg number=87 --arg withCi=true
123
+ agentinbox inbox read --agent-id <agentId>
124
+ agentinbox inbox ack --agent-id <agentId> --through <lastEntryId>
125
+ ```
126
+
127
+ Ack discipline:
128
+
129
+ - prefer `agentinbox inbox ack --agent-id <agentId> --through <lastEntryId>`
130
+ after reading a reviewed batch
131
+ - avoid `ack --all` unless you explicitly verified that every current item
132
+ should be cleared
133
+ - do not ack speculative future work just because the current terminal message
134
+ was seen
135
+
136
+ Timer intent:
137
+
138
+ - use `agentinbox timer add` when the trigger is time-based rather than source-based
139
+ - prefer timers over inventing fake local events for reminders like "check CI
140
+ in 30 minutes" or "revisit this PR tomorrow morning"
141
+
142
+ ## Core Commands
143
+
144
+ Follow high-level templates:
145
+
146
+ ```bash
147
+ agentinbox follow github repo --agent-id <agentId> --arg owner=holon-run --arg repo=agentinbox
148
+ agentinbox follow github pr --agent-id <agentId> --arg owner=holon-run --arg repo=agentinbox --arg number=87 --arg withCi=true
149
+ agentinbox follow github issue --agent-id <agentId> --arg owner=holon-run --arg repo=agentinbox --arg number=180
150
+ agentinbox follow github pr --agent-id <agentId> --args-json '{"owner":"holon-run","repo":"agentinbox","number":87,"withCi":true}'
151
+ ```
152
+
153
+ Use `follow` as the default path for GitHub repo, PR, and issue tracking. Drop
154
+ to `source schema` plus `subscription add` only when you need a custom filter or
155
+ the source has no follow template.
156
+
157
+ Advanced source/subscription commands:
158
+
159
+ ```bash
160
+ agentinbox source list
161
+ agentinbox source show <sourceId>
162
+ agentinbox source schema <sourceId>
163
+ agentinbox source add <hostId> repo_events <owner>/<repo> --config-json '{"owner":"holon-run","repo":"agentinbox"}'
164
+ agentinbox source add <hostId> ci_runs <owner>/<repo> --config-json '{"owner":"holon-run","repo":"agentinbox","pollIntervalSecs":30}'
165
+ agentinbox subscription add <sourceId> --shortcut pr --shortcut-args-json '{"number":87}'
166
+ agentinbox subscription add <sourceId> --filter-json '{"metadata":{"headBranch":"main","conclusion":"failure"}}'
167
+ agentinbox subscription list --agent-id <agentId>
168
+ agentinbox subscription remove <subscriptionId>
169
+ ```
170
+
171
+ Use manual subscriptions for advanced filters, custom source kinds, or when
172
+ debugging template expansion. If `subscriptionSchema.shortcuts` is non-empty
173
+ but no follow template is available, prefer those shortcut entries first.
174
+
175
+ Read and ack the inbox:
176
+
177
+ ```bash
178
+ agentinbox inbox read --agent-id <agentId>
179
+ agentinbox inbox ack --agent-id <agentId> --through <lastEntryId>
180
+ agentinbox inbox send --agent-id <agentId> --message "Please review PR #87"
181
+ agentinbox inbox watch --agent-id <agentId>
182
+ agentinbox inbox ack --agent-id <agentId> --all
183
+ ```
184
+
185
+ Default to a batch-bounded ack flow:
186
+
187
+ 1. read the inbox items you intend to process
188
+ 2. identify the last item you actually reviewed in that batch
189
+ 3. ack with `--through <lastEntryId>`
190
+
191
+ Use `ack --all` only after explicitly verifying every current item should be
192
+ cleared. Use `inbox send` for local operator/direct text ingress.
193
+
194
+ Manage timers:
195
+
196
+ ```bash
197
+ agentinbox timer add --agent-id <agentId> --at <RFC3339_TIMESTAMP> --message "Check the morning build"
198
+ agentinbox timer add --agent-id <agentId> --every 24h --message "Review today's open PRs"
199
+ agentinbox timer add --agent-id <agentId> --cron "0 8 * * *" --timezone Asia/Shanghai --message "Daily triage"
200
+ agentinbox timer list
201
+ agentinbox timer list --agent-id <agentId>
202
+ agentinbox timer pause <scheduleId>
203
+ agentinbox timer resume <scheduleId>
204
+ agentinbox timer remove <scheduleId>
205
+ ```
206
+
207
+ Activation targets:
208
+
209
+ ```bash
210
+ agentinbox agent target list <agentId>
211
+ agentinbox agent target add webhook <agentId> --url http://127.0.0.1:8787/webhook
212
+ agentinbox agent target remove <agentId> <targetId>
213
+ ```
214
+
215
+ For filtering strategy and review workflow setup, follow the docs links above
216
+ instead of re-explaining the full architecture here.
217
+
218
+ ## Troubleshooting
219
+
220
+ Do not start every task by checking daemon status; normal CLI commands should
221
+ auto-connect or surface actionable errors. Use these checks after the first
222
+ AgentInbox command fails, notifications stop arriving, or you suspect a stale
223
+ terminal binding:
224
+
225
+ ```bash
226
+ agentinbox --version
227
+ agentinbox daemon status
228
+ agentinbox daemon start
229
+ agentinbox agent register --agent-id <agentId> --force-rebind
230
+ ```
231
+
232
+ If GitHub-backed sources fail, verify UXC and imported GitHub auth only after
233
+ the failure points there:
234
+
235
+ ```bash
236
+ uxc --version
237
+ gh auth status
238
+ uxc auth credential import github --from gh
239
+ ```
@@ -0,0 +1,3 @@
1
+ display_name: AgentInbox
2
+ short_description: Operate the local AgentInbox service for shared sources, subscriptions, inbox workflows, activation targets, and external providers such as GitHub.
3
+ default_prompt: Use AgentInbox to register the current session, inspect sources and subscriptions, prefer broad reusable sources with subscription-side filtering, remove stale subscriptions when they are no longer needed, and read or watch the agent inbox.