@holon-run/agentinbox 1.0.3 → 1.0.5

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 CHANGED
@@ -13,8 +13,9 @@ In practice, that means `AgentInbox` can:
13
13
 
14
14
  - share one GitHub or Feishu source across multiple local agents
15
15
  - materialize those events into durable inboxes
16
- - wake or drive agent sessions running in `tmux` or `iTerm2`, even when the
17
- agent runtime does not expose a notification API
16
+ - activate terminal-backed sessions in `tmux` or `iTerm2`, or Holon agents
17
+ through runtime-provided webhook triggers, without embedding connector logic
18
+ in the agent runtime
18
19
 
19
20
  ## Event Flow
20
21
 
@@ -111,7 +112,7 @@ node dist/src/cli.js --help
111
112
 
112
113
  ## Recommended Onboarding
113
114
 
114
- If you are using Codex or Claude Code, start with the bundled AgentInbox skill:
115
+ If you are using Codex, Claude Code, or Holon, start with the bundled AgentInbox skill:
115
116
 
116
117
  - repo copy: [`skills/agentinbox/SKILL.md`](./skills/agentinbox/SKILL.md)
117
118
  - docs site copy: `https://agentinbox.holon.run/skills/agentinbox/SKILL`
@@ -120,7 +121,7 @@ If you use the community `skills` installer, you can install the bundled skill
120
121
  directly:
121
122
 
122
123
  ```bash
123
- npx skills add holon-run/agentinbox --skill agentinbox -a codex -a claude-code
124
+ npx skills add holon-run/agentinbox --skill agentinbox -a codex -a claude-code -a holon
124
125
  ```
125
126
 
126
127
  That skill is the recommended onboarding path. It can guide the agent through:
@@ -128,7 +129,8 @@ That skill is the recommended onboarding path. It can guide the agent through:
128
129
  - checking or installing `agentinbox`
129
130
  - checking or installing `uxc`
130
131
  - importing GitHub auth from the local `gh` CLI via `uxc auth credential import github --from gh`
131
- - registering the current terminal session as an agent
132
+ - registering the current runtime/session as an agent, including Holon webhook
133
+ activation when running in Holon
132
134
  - adding GitHub sources and standing subscriptions using the docs-site examples
133
135
 
134
136
  ## Quick Start
@@ -139,15 +141,20 @@ Start the local daemon:
139
141
  agentinbox daemon start
140
142
  ```
141
143
 
142
- Register the current terminal session:
144
+ Register the current runtime/session:
143
145
 
144
146
  ```bash
145
147
  agentinbox agent register
146
148
  agentinbox agent register --agent-id agent-alpha
149
+ agentinbox agent register --agent-id agent-alpha --webhook-url <external-trigger-url>
147
150
  agentinbox agent register --notify-lease-ms 600000 --min-unacked-items 5
148
151
  agentinbox agent current
149
152
  ```
150
153
 
154
+ Terminal-backed runtimes attach a terminal activation target. In Holon, the
155
+ no-arg form uses `HOLON_AGENT_ID` and `HOLON_EXTERNAL_TRIGGER_URL` when present;
156
+ otherwise pass `--webhook-url` explicitly.
157
+
151
158
  `notifyLeaseMs` and `minUnackedItems` are target-facing notification policy:
152
159
  they control how often a target may be reminded and how many unacked items must
153
160
  accumulate before AgentInbox notifies it. Service-wide activation batching
@@ -208,6 +208,18 @@ class AdapterRegistry {
208
208
  }
209
209
  return module.invokeDeliveryOperation({ handle, operation, input, attempt, source });
210
210
  }
211
+ async listSourceOperations(source) {
212
+ if (source.sourceType === "local_event") {
213
+ return [];
214
+ }
215
+ return this.remoteSource.listSourceOperations(source);
216
+ }
217
+ async invokeSourceOperation(source, operation, input) {
218
+ if (source.sourceType === "local_event") {
219
+ throw new Error(`source operations are not supported for source type ${source.sourceType}`);
220
+ }
221
+ return this.remoteSource.invokeSourceOperation(source, operation, input);
222
+ }
211
223
  status() {
212
224
  return {
213
225
  remote: this.remoteSource.status?.() ?? {},
package/dist/src/cli.js CHANGED
@@ -6,6 +6,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const node_fs_1 = __importDefault(require("node:fs"));
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
+ const commander_1 = require("commander");
9
10
  const adapters_1 = require("./adapters");
10
11
  const client_1 = require("./client");
11
12
  const control_server_1 = require("./control_server");
@@ -24,11 +25,16 @@ async function main() {
24
25
  const normalized = shouldTreatJsonFlagAsNoop(detectedArgs)
25
26
  ? stripNoopJsonFlag(detectedArgs)
26
27
  : detectedArgs;
28
+ const shell = parseCommanderShell(normalized);
27
29
  const command = normalized[0];
28
30
  if (!command || command === "help" || command === "--help" || command === "-h") {
29
31
  printHelp(normalized.slice(1));
30
32
  return;
31
33
  }
34
+ if (shell.isBareGroup) {
35
+ printHelp([command]);
36
+ return;
37
+ }
32
38
  if (command === "version" || command === "--version" || command === "-v") {
33
39
  if (hasHelpFlag(normalized.slice(1))) {
34
40
  printHelp(["version"]);
@@ -57,7 +63,7 @@ async function main() {
57
63
  printHelp([command]);
58
64
  return;
59
65
  }
60
- const client = await createClient(normalized);
66
+ const client = await createClient(clientArgsForCommand(normalized));
61
67
  if (command === "host" && normalized[1] === "add") {
62
68
  const [hostType, hostKey] = normalized.slice(2, 4);
63
69
  if (!hostType || !hostKey) {
@@ -72,7 +78,10 @@ async function main() {
72
78
  return;
73
79
  }
74
80
  if (command === "host" && normalized[1] === "list") {
75
- await printRemote(client, "/hosts", undefined, "GET");
81
+ const query = buildQuery({
82
+ limit: parseOptionalLimitArg(normalized),
83
+ });
84
+ await printRemote(client, `/hosts${query}`, undefined, "GET");
76
85
  return;
77
86
  }
78
87
  if (command === "host" && normalized[1] === "show") {
@@ -106,7 +115,10 @@ async function main() {
106
115
  return;
107
116
  }
108
117
  if (command === "stream" && normalized[1] === "list") {
109
- await printRemote(client, "/streams", undefined, "GET");
118
+ const query = buildQuery({
119
+ limit: parseOptionalLimitArg(normalized),
120
+ });
121
+ await printRemote(client, `/streams${query}`, undefined, "GET");
110
122
  return;
111
123
  }
112
124
  if (command === "stream" && normalized[1] === "show") {
@@ -266,7 +278,10 @@ async function main() {
266
278
  return;
267
279
  }
268
280
  if (command === "source" && normalized[1] === "list") {
269
- await printRemote(client, "/sources", undefined, "GET");
281
+ const query = buildQuery({
282
+ limit: parseOptionalLimitArg(normalized),
283
+ });
284
+ await printRemote(client, `/sources${query}`, undefined, "GET");
270
285
  return;
271
286
  }
272
287
  if (command === "source" && normalized[1] === "show") {
@@ -334,6 +349,27 @@ async function main() {
334
349
  await printRemote(client, `/sources/${encodeURIComponent(sourceRef)}/schema`, undefined, "GET");
335
350
  return;
336
351
  }
352
+ if (command === "source" && normalized[1] === "actions") {
353
+ const sourceId = normalized[2];
354
+ if (!sourceId) {
355
+ throw new Error("usage: agentinbox source actions <sourceId>");
356
+ }
357
+ await printRemote(client, `/sources/${encodeURIComponent(sourceId)}/actions`, undefined, "GET");
358
+ return;
359
+ }
360
+ if (command === "source" && normalized[1] === "invoke") {
361
+ const sourceId = normalized[2];
362
+ const operation = takeFlagValue(normalized, "--operation");
363
+ const inputJson = takeFlagValue(normalized, "--input-json");
364
+ if (!sourceId || !operation || !inputJson) {
365
+ throw new Error("usage: agentinbox source invoke <sourceId> --operation NAME --input-json JSON");
366
+ }
367
+ await printRemote(client, `/sources/${encodeURIComponent(sourceId)}/invoke`, {
368
+ operation,
369
+ input: (0, util_1.parseJsonArg)(inputJson, "--input-json"),
370
+ });
371
+ return;
372
+ }
337
373
  if (command === "source" && normalized[1] === "poll") {
338
374
  const sourceId = normalized[2];
339
375
  if (!sourceId) {
@@ -363,15 +399,18 @@ async function main() {
363
399
  const notifyLeaseMs = parseOptionalNumber(takeFlagValue(normalized, "--notify-lease-ms")) ?? undefined;
364
400
  const minUnackedItems = parseOptionalNumber(takeFlagValue(normalized, "--min-unacked-items")) ?? undefined;
365
401
  const webhookUrl = takeFlagValue(normalized, "--webhook-url");
402
+ const holonContext = (0, current_agent_1.detectHolonRuntimeContext)(process.env);
366
403
  if (webhookUrl) {
367
- if (!agentId) {
368
- throw new Error("usage: agentinbox agent register --agent-id ID --webhook-url URL [--webhook-activation-mode MODE] [--webhook-notify-lease-ms N] [--webhook-min-unacked-items N]");
404
+ const resolvedAgentId = agentId ?? holonContext?.agentId;
405
+ if (!resolvedAgentId) {
406
+ throw new Error("usage: agentinbox agent register --agent-id ID --webhook-url URL [--webhook-activation-mode MODE] [--webhook-notify-lease-ms N] [--webhook-min-unacked-items N] (or set HOLON_AGENT_ID in Holon)");
369
407
  }
370
408
  const webhookNotifyLeaseMs = parseOptionalNumber(takeFlagValue(normalized, "--webhook-notify-lease-ms")) ?? notifyLeaseMs;
371
409
  const webhookMinUnackedItems = parseOptionalNumber(takeFlagValue(normalized, "--webhook-min-unacked-items")) ?? minUnackedItems;
372
410
  await printRemote(client, "/agents", {
373
- agentId,
411
+ agentId: resolvedAgentId,
374
412
  forceRebind: normalized.includes("--force-rebind"),
413
+ ...(holonContext ? { runtimeKind: holonContext.runtimeKind } : {}),
375
414
  webhook: {
376
415
  url: webhookUrl,
377
416
  activationMode: takeFlagValue(normalized, "--webhook-activation-mode") ?? undefined,
@@ -381,6 +420,28 @@ async function main() {
381
420
  });
382
421
  return;
383
422
  }
423
+ if ((0, current_agent_1.isHolonRuntimeEnv)(process.env)) {
424
+ if (!holonContext) {
425
+ throw new Error("agent register in Holon requires HOLON_AGENT_ID");
426
+ }
427
+ if (!holonContext.externalTriggerUrl) {
428
+ throw new Error("agent register in Holon requires --webhook-url or HOLON_EXTERNAL_TRIGGER_URL");
429
+ }
430
+ const webhookNotifyLeaseMs = parseOptionalNumber(takeFlagValue(normalized, "--webhook-notify-lease-ms")) ?? notifyLeaseMs;
431
+ const webhookMinUnackedItems = parseOptionalNumber(takeFlagValue(normalized, "--webhook-min-unacked-items")) ?? minUnackedItems;
432
+ await printRemote(client, "/agents", {
433
+ agentId: agentId ?? holonContext.agentId,
434
+ forceRebind: normalized.includes("--force-rebind"),
435
+ runtimeKind: holonContext.runtimeKind,
436
+ webhook: {
437
+ url: holonContext.externalTriggerUrl,
438
+ activationMode: takeFlagValue(normalized, "--webhook-activation-mode") ?? undefined,
439
+ notifyLeaseMs: webhookNotifyLeaseMs,
440
+ minUnackedItems: webhookMinUnackedItems,
441
+ },
442
+ });
443
+ return;
444
+ }
384
445
  const detected = (0, terminal_1.detectTerminalContext)(process.env);
385
446
  await printRemote(client, "/agents", {
386
447
  agentId,
@@ -399,7 +460,7 @@ async function main() {
399
460
  return;
400
461
  }
401
462
  if (command === "agent" && normalized[1] === "list") {
402
- await printAgentList(client);
463
+ await printAgentList(client, parseOptionalLimitArg(normalized));
403
464
  return;
404
465
  }
405
466
  if (command === "agent" && normalized[1] === "current") {
@@ -448,9 +509,12 @@ async function main() {
448
509
  if (command === "agent" && normalized[1] === "target" && normalized[2] === "list") {
449
510
  const agentId = normalized[3];
450
511
  if (!agentId) {
451
- throw new Error("usage: agentinbox agent target list <agentId>");
512
+ throw new Error("usage: agentinbox agent target list <agentId> [--limit N]");
452
513
  }
453
- await printRemote(client, `/agents/${encodeURIComponent(agentId)}/targets`, undefined, "GET");
514
+ const query = buildQuery({
515
+ limit: parseOptionalLimitArg(normalized),
516
+ });
517
+ await printRemote(client, `/agents/${encodeURIComponent(agentId)}/targets${query}`, undefined, "GET");
454
518
  return;
455
519
  }
456
520
  if (command === "agent" && normalized[1] === "target" && normalized[2] === "remove") {
@@ -522,6 +586,7 @@ async function main() {
522
586
  const query = buildQuery({
523
587
  source_id: takeFlagValue(normalized, "--source-id"),
524
588
  agent_id: takeFlagValue(normalized, "--agent-id"),
589
+ limit: parseOptionalLimitArg(normalized),
525
590
  });
526
591
  await printRemote(client, `/subscriptions${query}`, undefined, "GET");
527
592
  return;
@@ -599,6 +664,7 @@ async function main() {
599
664
  if (command === "timer" && normalized[1] === "list") {
600
665
  const query = buildQuery({
601
666
  agent_id: takeFlagValue(normalized, "--agent-id"),
667
+ limit: parseOptionalLimitArg(normalized),
602
668
  });
603
669
  await printRemote(client, `/timers${query}`, undefined, "GET");
604
670
  return;
@@ -628,7 +694,10 @@ async function main() {
628
694
  return;
629
695
  }
630
696
  if (command === "inbox" && normalized[1] === "list") {
631
- await printRemote(client, "/agents", undefined, "GET");
697
+ const query = buildQuery({
698
+ limit: parseOptionalLimitArg(normalized),
699
+ });
700
+ await printRemote(client, `/agents${query}`, undefined, "GET");
632
701
  return;
633
702
  }
634
703
  if (command === "inbox" && normalized[1] === "show") {
@@ -641,9 +710,9 @@ async function main() {
641
710
  }
642
711
  if (command === "inbox" && normalized[1] === "read") {
643
712
  const args = normalized.slice(2);
644
- const allowedFlags = ["--agent-id", "--after-entry", "--include-acked"];
645
- if (positionalArgs(args, ["--agent-id", "--after-entry"]).length > 0 || unexpectedFlags(args, allowedFlags).length > 0) {
646
- throw new Error("usage: agentinbox inbox read [--agent-id ID] [--after-entry ID] [--include-acked]");
713
+ const allowedFlags = ["--agent-id", "--after-entry", "--include-acked", "--limit"];
714
+ if (positionalArgs(args, ["--agent-id", "--after-entry", "--limit"]).length > 0 || unexpectedFlags(args, allowedFlags).length > 0) {
715
+ throw new Error("usage: agentinbox inbox read [--agent-id ID] [--after-entry ID] [--include-acked] [--limit N]");
647
716
  }
648
717
  const selection = await selectAgentForCommand(client, {
649
718
  explicitAgentId: takeFlagValue(normalized, "--agent-id"),
@@ -652,6 +721,7 @@ async function main() {
652
721
  const query = buildQuery({
653
722
  after_entry_id: takeFlagValue(normalized, "--after-entry"),
654
723
  include_acked: hasFlag(normalized, "--include-acked") ? "true" : undefined,
724
+ limit: parseOptionalLimitArg(normalized),
655
725
  });
656
726
  const response = await requestRemote(client, `/agents/${encodeURIComponent(selection.agentId)}/inbox/entries${query}`, undefined, "GET");
657
727
  console.log((0, util_1.jsonResponse)(withCommandMetadata(response.data, selection)));
@@ -835,6 +905,53 @@ async function main() {
835
905
  }
836
906
  throw new Error(`unknown command: ${normalized.join(" ")}`);
837
907
  }
908
+ const COMMAND_SHELL_SPECS = [
909
+ { name: "serve", group: false, summary: "Run the AgentInbox control server." },
910
+ { name: "daemon", group: true, summary: "Manage the local AgentInbox daemon." },
911
+ { name: "host", group: true, summary: "Manage source hosts." },
912
+ { name: "stream", group: true, summary: "Manage source streams." },
913
+ { name: "follow", group: true, summary: "Create high-level source subscriptions." },
914
+ { name: "source", group: true, summary: "Manage source streams through the source alias." },
915
+ { name: "agent", group: true, summary: "Manage local agent registrations and activation targets." },
916
+ { name: "timer", group: true, summary: "Manage agent reminder timers." },
917
+ { name: "subscription", group: true, summary: "Manage source subscriptions." },
918
+ { name: "inbox", group: true, summary: "Read and manage agent inboxes." },
919
+ { name: "gc", group: false, summary: "Run garbage collection." },
920
+ { name: "deliver", group: true, summary: "Send outbound delivery actions." },
921
+ { name: "status", group: false, summary: "Show daemon status." },
922
+ { name: "version", group: false, summary: "Show CLI version." },
923
+ ];
924
+ const COMMANDER_SHELL = createCommanderShell();
925
+ function createCommanderShell() {
926
+ const program = new commander_1.Command();
927
+ program
928
+ .name("agentinbox")
929
+ .allowUnknownOption(true)
930
+ .allowExcessArguments(true)
931
+ .exitOverride()
932
+ .configureOutput({
933
+ writeOut: () => { },
934
+ writeErr: () => { },
935
+ });
936
+ for (const spec of COMMAND_SHELL_SPECS) {
937
+ program
938
+ .command(`${spec.name} [args...]`)
939
+ .description(spec.summary)
940
+ .allowUnknownOption(true)
941
+ .allowExcessArguments(true);
942
+ }
943
+ return program;
944
+ }
945
+ function parseCommanderShell(args) {
946
+ const command = args[0] ?? null;
947
+ if (!command || command === "help" || command === "--help" || command === "-h") {
948
+ return { isBareGroup: false };
949
+ }
950
+ const spec = COMMANDER_SHELL.commands.find((item) => item.name() === command);
951
+ return {
952
+ isBareGroup: Boolean(COMMAND_SHELL_SPECS.find((item) => item.name === spec?.name())?.group && args.length === 1),
953
+ };
954
+ }
838
955
  async function runServe(args) {
839
956
  const port = parseOptionalNumber(takeFlagValue(args, "--port"));
840
957
  const homeOverride = takeFlagValue(args, "--home");
@@ -925,12 +1042,12 @@ async function createClient(args) {
925
1042
  });
926
1043
  return new client_1.AgentInboxClient(transport);
927
1044
  }
928
- async function printAgentList(client) {
929
- const records = await listAgentsWithTargets(client);
930
- console.log((0, util_1.jsonResponse)((0, current_agent_1.annotateAgents)(records, tryDetectTerminalContext())));
1045
+ async function printAgentList(client, limit) {
1046
+ const records = await listAgentsWithTargets(client, limit);
1047
+ console.log((0, util_1.jsonResponse)((0, current_agent_1.annotateAgents)(records, tryDetectCurrentAgentContext())));
931
1048
  }
932
1049
  async function printCurrentAgent(client) {
933
- const context = getRequiredTerminalContext();
1050
+ const context = getRequiredCurrentAgentContext();
934
1051
  const records = await listAgentsWithTargets(client);
935
1052
  const current = (0, current_agent_1.resolveCurrentAgent)(records, context);
936
1053
  if (!current) {
@@ -940,7 +1057,7 @@ async function printCurrentAgent(client) {
940
1057
  }
941
1058
  async function selectAgentForCommand(client, options) {
942
1059
  const records = await listAgentsWithTargets(client);
943
- const context = tryDetectTerminalContext();
1060
+ const context = tryDetectCurrentAgentContext();
944
1061
  if (options.explicitAgentId) {
945
1062
  const current = context ? (0, current_agent_1.resolveCurrentAgent)(records, context) : null;
946
1063
  const requested = records.find((entry) => entry.agent.agentId === options.explicitAgentId);
@@ -960,7 +1077,7 @@ async function selectAgentForCommand(client, options) {
960
1077
  warnings,
961
1078
  };
962
1079
  }
963
- const contextForCurrent = getRequiredTerminalContext();
1080
+ const contextForCurrent = getRequiredCurrentAgentContext();
964
1081
  const current = (0, current_agent_1.resolveCurrentAgent)(records, contextForCurrent);
965
1082
  if (current) {
966
1083
  return {
@@ -972,17 +1089,7 @@ async function selectAgentForCommand(client, options) {
972
1089
  if (!options.autoRegister) {
973
1090
  throw new Error(noCurrentAgentMessage());
974
1091
  }
975
- await requestRemote(client, "/agents", {
976
- backend: contextForCurrent.backend,
977
- runtimeKind: contextForCurrent.runtimeKind,
978
- runtimeSessionId: contextForCurrent.runtimeSessionId ?? undefined,
979
- runtimePid: contextForCurrent.runtimePid ?? undefined,
980
- tmuxPaneId: contextForCurrent.tmuxPaneId ?? undefined,
981
- tty: contextForCurrent.tty ?? undefined,
982
- termProgram: contextForCurrent.termProgram ?? undefined,
983
- itermSessionId: contextForCurrent.itermSessionId ?? undefined,
984
- notifyLeaseMs: undefined,
985
- });
1092
+ await requestRemote(client, "/agents", registrationInputForCurrentContext(contextForCurrent));
986
1093
  const refreshed = await listAgentsWithTargets(client);
987
1094
  const registered = (0, current_agent_1.resolveCurrentAgent)(refreshed, contextForCurrent);
988
1095
  if (!registered) {
@@ -994,8 +1101,38 @@ async function selectAgentForCommand(client, options) {
994
1101
  warnings: [],
995
1102
  };
996
1103
  }
997
- async function listAgentsWithTargets(client) {
998
- const response = await requestRemote(client, "/agents?include_targets=true", undefined, "GET");
1104
+ function registrationInputForCurrentContext(context) {
1105
+ if ((0, current_agent_1.isHolonRuntimeContext)(context)) {
1106
+ if (!context.externalTriggerUrl) {
1107
+ throw new Error("unable to auto-register current Holon agent: HOLON_EXTERNAL_TRIGGER_URL is not set");
1108
+ }
1109
+ return {
1110
+ agentId: context.agentId,
1111
+ runtimeKind: context.runtimeKind,
1112
+ webhook: {
1113
+ url: context.externalTriggerUrl,
1114
+ notifyLeaseMs: undefined,
1115
+ },
1116
+ };
1117
+ }
1118
+ return {
1119
+ backend: context.backend,
1120
+ runtimeKind: context.runtimeKind,
1121
+ runtimeSessionId: context.runtimeSessionId ?? undefined,
1122
+ runtimePid: context.runtimePid ?? undefined,
1123
+ tmuxPaneId: context.tmuxPaneId ?? undefined,
1124
+ tty: context.tty ?? undefined,
1125
+ termProgram: context.termProgram ?? undefined,
1126
+ itermSessionId: context.itermSessionId ?? undefined,
1127
+ notifyLeaseMs: undefined,
1128
+ };
1129
+ }
1130
+ async function listAgentsWithTargets(client, limit) {
1131
+ const query = buildQuery({
1132
+ include_targets: "true",
1133
+ limit,
1134
+ });
1135
+ const response = await requestRemote(client, `/agents${query}`, undefined, "GET");
999
1136
  return response.data.agents;
1000
1137
  }
1001
1138
  async function printRemote(client, endpoint, body, method = "POST") {
@@ -1005,13 +1142,38 @@ async function printRemote(client, endpoint, body, method = "POST") {
1005
1142
  async function requestRemote(client, endpoint, body, method = "POST") {
1006
1143
  const response = await client.request(endpoint, body, method);
1007
1144
  if (response.statusCode < 200 || response.statusCode >= 300) {
1008
- throw new Error((0, util_1.jsonResponse)(response.data));
1145
+ const bodyText = (0, util_1.jsonResponse)(response.data);
1146
+ throw new Error(`request ${method} ${endpoint} failed with HTTP ${response.statusCode}: ${bodyText}`);
1009
1147
  }
1010
1148
  return { data: response.data };
1011
1149
  }
1012
1150
  function noCurrentAgentMessage() {
1013
1151
  return "no current agent is registered for this terminal/runtime context; run `agentinbox agent list` to inspect offline agents, then `agentinbox agent register` or `agentinbox agent register --force-rebind --agent-id <agentId>` to rebind one";
1014
1152
  }
1153
+ function clientArgsForCommand(args) {
1154
+ if (isAgentTargetAddWebhookCommand(args)) {
1155
+ return withoutFlagValues(args, "--url");
1156
+ }
1157
+ return args;
1158
+ }
1159
+ function isAgentTargetAddWebhookCommand(args) {
1160
+ return args[0] === "agent" && args[1] === "target" && args[2] === "add" && args[3] === "webhook";
1161
+ }
1162
+ function withoutFlagValues(args, flag) {
1163
+ const filtered = [];
1164
+ for (let index = 0; index < args.length; index += 1) {
1165
+ const value = args[index];
1166
+ if (value !== flag) {
1167
+ filtered.push(value);
1168
+ continue;
1169
+ }
1170
+ const nextIndex = index + 1;
1171
+ if (nextIndex < args.length && !args[nextIndex].startsWith("--")) {
1172
+ index = nextIndex;
1173
+ }
1174
+ }
1175
+ return filtered;
1176
+ }
1015
1177
  function takeFlagValue(args, flag) {
1016
1178
  const index = args.indexOf(flag);
1017
1179
  if (index === -1) {
@@ -1102,6 +1264,17 @@ function buildQuery(params) {
1102
1264
  const query = search.toString();
1103
1265
  return query ? `?${query}` : "";
1104
1266
  }
1267
+ function parseOptionalLimitArg(args) {
1268
+ const raw = takeFlagValue(args, "--limit");
1269
+ if (raw === undefined) {
1270
+ return undefined;
1271
+ }
1272
+ const parsed = Number.parseInt(raw, 10);
1273
+ if (!Number.isFinite(parsed) || parsed < 1 || String(parsed) !== raw) {
1274
+ throw new Error("`--limit` must be a positive integer");
1275
+ }
1276
+ return String(parsed);
1277
+ }
1105
1278
  function readSubscriptionFilter(args) {
1106
1279
  const filterJson = takeFlagValue(args, "--filter-json");
1107
1280
  const filterFile = takeFlagValue(args, "--filter-file");
@@ -1193,7 +1366,14 @@ function normalizeSubscriptionAddOutput(data, shortcutUsed) {
1193
1366
  }
1194
1367
  return data;
1195
1368
  }
1196
- function getRequiredTerminalContext() {
1369
+ function getRequiredCurrentAgentContext() {
1370
+ const holonContext = (0, current_agent_1.detectHolonRuntimeContext)(process.env);
1371
+ if (holonContext) {
1372
+ return holonContext;
1373
+ }
1374
+ if ((0, current_agent_1.isHolonRuntimeEnv)(process.env)) {
1375
+ throw new Error("unable to detect current Holon agent: HOLON_AGENT_ID is not set");
1376
+ }
1197
1377
  try {
1198
1378
  return (0, terminal_1.detectTerminalContext)(process.env);
1199
1379
  }
@@ -1202,7 +1382,11 @@ function getRequiredTerminalContext() {
1202
1382
  throw new Error(`unable to resolve current agent: ${message}`);
1203
1383
  }
1204
1384
  }
1205
- function tryDetectTerminalContext() {
1385
+ function tryDetectCurrentAgentContext() {
1386
+ const holonContext = (0, current_agent_1.detectHolonRuntimeContext)(process.env);
1387
+ if (holonContext || (0, current_agent_1.isHolonRuntimeEnv)(process.env)) {
1388
+ return holonContext;
1389
+ }
1206
1390
  try {
1207
1391
  return (0, terminal_1.detectTerminalContext)(process.env);
1208
1392
  }
@@ -1235,10 +1419,9 @@ function unexpectedFlags(args, allowedFlags) {
1235
1419
  const allowed = new Set(allowedFlags);
1236
1420
  return args.filter((token) => token.startsWith("--") && !allowed.has(token));
1237
1421
  }
1238
- function printHelp(path = []) {
1239
- const key = path[0] ?? "root";
1240
- const helpByKey = {
1241
- root: `agentinbox
1422
+ function renderRootHelp() {
1423
+ const commands = COMMAND_SHELL_SPECS.map((spec) => ` ${spec.name}`).join("\n");
1424
+ return `agentinbox
1242
1425
 
1243
1426
  Usage:
1244
1427
  agentinbox <command> [options]
@@ -1247,19 +1430,13 @@ Usage:
1247
1430
  agentinbox --version, -v
1248
1431
 
1249
1432
  Commands:
1250
- serve
1251
- daemon
1252
- follow
1253
- source
1254
- agent
1255
- timer
1256
- subscription
1257
- inbox
1258
- gc
1259
- deliver
1260
- status
1261
- version
1262
- `,
1433
+ ${commands}
1434
+ `;
1435
+ }
1436
+ function printHelp(path = []) {
1437
+ const key = path[0] ?? "root";
1438
+ const helpByKey = {
1439
+ root: renderRootHelp(),
1263
1440
  serve: `agentinbox serve
1264
1441
 
1265
1442
  Usage:
@@ -1277,7 +1454,7 @@ Usage:
1277
1454
 
1278
1455
  Usage:
1279
1456
  agentinbox host add <hostType> <hostKey> [--config-json JSON] [--config-ref REF]
1280
- agentinbox host list
1457
+ agentinbox host list [--limit N]
1281
1458
  agentinbox host show <hostId>
1282
1459
  agentinbox host schema <hostId>
1283
1460
  `,
@@ -1285,7 +1462,7 @@ Usage:
1285
1462
 
1286
1463
  Usage:
1287
1464
  agentinbox stream add <hostId> <streamKind> <streamKey> [--config-json JSON] [--config-ref REF]
1288
- agentinbox stream list
1465
+ agentinbox stream list [--limit N]
1289
1466
  agentinbox stream show <streamId>
1290
1467
  agentinbox stream update <streamId> [--config-json JSON] [--config-ref REF | --clear-config-ref]
1291
1468
  agentinbox stream remove <streamId> [--with-subscriptions]
@@ -1305,18 +1482,21 @@ Examples:
1305
1482
  agentinbox follow github repo --agent-id <agentId> --arg owner=holon-run --arg repo=agentinbox
1306
1483
  agentinbox follow github pr --agent-id <agentId> --arg owner=holon-run --arg repo=agentinbox --arg number=87 --arg withCi=true
1307
1484
  agentinbox follow github issue --agent-id <agentId> --arg owner=holon-run --arg repo=agentinbox --arg number=180
1485
+ agentinbox follow feishu mentions --agent-id <agentId> --config-json '{"uxcAuth":"feishu-default"}'
1308
1486
  `,
1309
1487
  source: `agentinbox source
1310
1488
 
1311
1489
  Usage:
1312
1490
  agentinbox source add <hostId> <streamKind> <streamKey> [--config-json JSON] [--config-ref REF]
1313
- agentinbox source list
1491
+ agentinbox source list [--limit N]
1314
1492
  agentinbox source show <sourceId>
1315
1493
  agentinbox source update <sourceId> [--config-json JSON] [--config-ref REF | --clear-config-ref]
1316
1494
  agentinbox source remove <sourceId> [--with-subscriptions]
1317
1495
  agentinbox source pause <remoteSourceId>
1318
1496
  agentinbox source resume <remoteSourceId>
1319
1497
  agentinbox source schema <sourceId>
1498
+ agentinbox source actions <sourceId>
1499
+ agentinbox source invoke <sourceId> --operation NAME --input-json JSON
1320
1500
  agentinbox source poll <sourceId>
1321
1501
  agentinbox source event <sourceId> --native-id ID --event EVENT [--occurred-at ISO8601] [--metadata-json JSON] [--payload-json JSON]
1322
1502
  `,
@@ -1325,13 +1505,13 @@ Usage:
1325
1505
  Usage:
1326
1506
  agentinbox agent register [--agent-id ID] [--force-rebind] [--notify-lease-ms N] [--min-unacked-items N]
1327
1507
  agentinbox agent register --agent-id ID --webhook-url URL [--webhook-activation-mode MODE] [--webhook-notify-lease-ms N] [--webhook-min-unacked-items N]
1328
- agentinbox agent list
1508
+ agentinbox agent list [--limit N]
1329
1509
  agentinbox agent current
1330
1510
  agentinbox agent show <agentId>
1331
1511
  agentinbox agent remove <agentId>
1332
1512
  agentinbox agent resume <agentId>
1333
1513
  agentinbox agent target add webhook <agentId> --url URL [--activation-mode MODE] [--notify-lease-ms N] [--min-unacked-items N]
1334
- agentinbox agent target list <agentId>
1514
+ agentinbox agent target list <agentId> [--limit N]
1335
1515
  agentinbox agent target remove <agentId> <targetId>
1336
1516
  agentinbox agent target resume <agentId> <targetId>
1337
1517
  `,
@@ -1339,7 +1519,7 @@ Usage:
1339
1519
 
1340
1520
  Usage:
1341
1521
  agentinbox timer add --agent-id ID (--at ISO8601 | --every DURATION | --cron EXPR) --message TEXT [--timezone TZ] [--sender SENDER]
1342
- agentinbox timer list [--agent-id ID]
1522
+ agentinbox timer list [--agent-id ID] [--limit N]
1343
1523
  agentinbox timer pause <scheduleId>
1344
1524
  agentinbox timer resume <scheduleId>
1345
1525
  agentinbox timer remove <scheduleId>
@@ -1348,7 +1528,7 @@ Usage:
1348
1528
 
1349
1529
  Usage:
1350
1530
  agentinbox subscription add <sourceId> [--agent-id ID] [--shortcut NAME --shortcut-args-json JSON] [--filter-json JSON | --filter-file PATH | --filter-stdin] [--tracked-resource-ref REF] [--cleanup-policy-json JSON] [--start-policy POLICY] [--start-offset N] [--start-time ISO8601]
1351
- agentinbox subscription list [--source-id ID] [--agent-id ID]
1531
+ agentinbox subscription list [--source-id ID] [--agent-id ID] [--limit N]
1352
1532
  agentinbox subscription show <subscriptionId>
1353
1533
  agentinbox subscription remove <subscriptionId>
1354
1534
  agentinbox subscription poll <subscriptionId>
@@ -1358,9 +1538,9 @@ Usage:
1358
1538
  inbox: `agentinbox inbox
1359
1539
 
1360
1540
  Usage:
1361
- agentinbox inbox list
1541
+ agentinbox inbox list [--limit N]
1362
1542
  agentinbox inbox show <agentId>
1363
- agentinbox inbox read [--agent-id ID] [--after-entry ID] [--include-acked]
1543
+ agentinbox inbox read [--agent-id ID] [--after-entry ID] [--include-acked] [--limit N]
1364
1544
  agentinbox inbox send --agent-id ID --message TEXT [--sender SENDER]
1365
1545
  agentinbox inbox watch [--agent-id ID] [--after-entry ID] [--include-acked] [--heartbeat-ms N]
1366
1546
  agentinbox inbox ack [--agent-id ID] (--through <entryId> | --entry <entryId> | --all)