@holon-run/agentinbox 0.6.0 → 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +32 -7
  2. package/dist/src/adapters.js +61 -6
  3. package/dist/src/backend.js +4 -16
  4. package/dist/src/cli.js +307 -53
  5. package/dist/src/client.js +2 -2
  6. package/dist/src/daemon.js +36 -2
  7. package/dist/src/http.js +467 -45
  8. package/dist/src/logging.js +112 -0
  9. package/dist/src/paths.js +1 -0
  10. package/dist/src/runtime_gate.js +394 -63
  11. package/dist/src/service.js +856 -154
  12. package/dist/src/source_hosts.js +75 -0
  13. package/dist/src/source_resolution.js +20 -20
  14. package/dist/src/source_schema.js +2 -2
  15. package/dist/src/sources/feishu.js +66 -28
  16. package/dist/src/sources/github.js +188 -24
  17. package/dist/src/sources/remote.js +44 -24
  18. package/dist/src/sources/{remote_profiles.js → remote_modules.js} +143 -58
  19. package/dist/src/store.js +861 -106
  20. package/dist/src/terminal.js +33 -7
  21. package/dist/src/util.js +46 -1
  22. package/drizzle/migrations/{0000_initial.sql → 0000_v1_initial.sql} +182 -6
  23. package/drizzle/schema.ts +93 -1
  24. package/package.json +2 -1
  25. package/scripts/iterm2_cursor_probe.py +8 -3
  26. package/scripts/release_smoke.sh +123 -0
  27. package/drizzle/migrations/0001_inbox_items_source_occurred_at_idx.sql +0 -3
  28. package/drizzle/migrations/0002_subscription_cleanup_policy.sql +0 -45
  29. package/drizzle/migrations/0003_subscription_lifecycle_retirements.sql +0 -14
  30. package/drizzle/migrations/0004_source_idle_states.sql +0 -10
  31. package/drizzle/migrations/0005_timers.sql +0 -22
  32. package/drizzle/migrations/0006_terminal_runtime_pid.sql +0 -1
  33. package/drizzle/migrations/0007_activation_dispatch_last_notified_fingerprint.sql +0 -2
  34. package/drizzle/migrations/0008_inbox_item_sequence.sql +0 -21
@@ -3,66 +3,66 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.RemoteSourceProfileRegistry = void 0;
7
- exports.builtInProfileIdForSourceType = builtInProfileIdForSourceType;
8
- exports.profileConfigForSource = profileConfigForSource;
6
+ exports.RemoteSourceModuleRegistry = void 0;
7
+ exports.builtInModuleIdForSourceType = builtInModuleIdForSourceType;
8
+ exports.moduleConfigForSource = moduleConfigForSource;
9
9
  const node_fs_1 = __importDefault(require("node:fs"));
10
10
  const node_path_1 = __importDefault(require("node:path"));
11
11
  const node_url_1 = require("node:url");
12
12
  const github_1 = require("./github");
13
13
  const github_ci_1 = require("./github_ci");
14
14
  const feishu_1 = require("./feishu");
15
- const REMOTE_USER_PROFILE_ROOT_DIR = "source-profiles";
16
- const BUILTIN_PROFILE_IDS = new Set(["builtin.github_repo", "builtin.github_repo_ci", "builtin.feishu_bot"]);
17
- class RemoteSourceProfileRegistry {
18
- profileCache = new Map();
15
+ const REMOTE_USER_MODULE_ROOT_DIR = "source-modules";
16
+ const BUILTIN_MODULE_IDS = new Set(["builtin.github_repo", "builtin.github_repo_ci", "builtin.feishu_bot"]);
17
+ class RemoteSourceModuleRegistry {
18
+ moduleCache = new Map();
19
19
  resolve(source, homeDir) {
20
20
  if (source.sourceType === "github_repo") {
21
- return Promise.resolve(GITHUB_REPO_PROFILE);
21
+ return Promise.resolve(GITHUB_REPO_MODULE);
22
22
  }
23
23
  if (source.sourceType === "github_repo_ci") {
24
- return Promise.resolve(GITHUB_REPO_CI_PROFILE);
24
+ return Promise.resolve(GITHUB_REPO_CI_MODULE);
25
25
  }
26
26
  if (source.sourceType === "feishu_bot") {
27
- return Promise.resolve(FEISHU_BOT_PROFILE);
27
+ return Promise.resolve(FEISHU_BOT_MODULE);
28
28
  }
29
29
  if (source.sourceType !== "remote_source") {
30
- throw new Error(`unsupported source type for remote profile: ${source.sourceType}`);
30
+ throw new Error(`unsupported source type for remote module: ${source.sourceType}`);
31
31
  }
32
- return this.loadUserProfile(source, homeDir);
32
+ return this.loadUserModule(source, homeDir);
33
33
  }
34
- async loadUserProfile(source, homeDir) {
34
+ async loadUserModule(source, homeDir) {
35
35
  const config = source.config ?? {};
36
- const profilePath = asNonEmptyString(config.profilePath);
37
- if (!profilePath) {
38
- throw new Error("remote_source requires config.profilePath");
36
+ const modulePath = asNonEmptyString(config.modulePath);
37
+ if (!modulePath) {
38
+ throw new Error("remote_source requires config.modulePath (module path)");
39
39
  }
40
- const profileRoot = node_path_1.default.resolve(homeDir, REMOTE_USER_PROFILE_ROOT_DIR);
41
- const resolvedPath = resolveAndValidateProfilePath(profileRoot, profilePath);
40
+ const moduleRoot = node_path_1.default.resolve(homeDir, REMOTE_USER_MODULE_ROOT_DIR);
41
+ const resolvedPath = resolveAndValidateModulePath(moduleRoot, modulePath);
42
42
  const cacheKey = `${resolvedPath}:${source.sourceKey}`;
43
- const cached = this.profileCache.get(cacheKey);
43
+ const cached = this.moduleCache.get(cacheKey);
44
44
  if (cached) {
45
45
  return cached;
46
46
  }
47
47
  if (!node_fs_1.default.existsSync(resolvedPath)) {
48
- throw new Error(`remote_source profile not found: ${resolvedPath}`);
48
+ throw new Error(`remote_source module not found: ${resolvedPath}`);
49
49
  }
50
50
  const imported = await dynamicImportModule((0, node_url_1.pathToFileURL)(resolvedPath).href);
51
- const profile = (imported.default ?? imported);
52
- if (!profile || typeof profile !== "object") {
53
- throw new Error(`remote_source profile must export default object: ${resolvedPath}`);
51
+ const module = (imported.default ?? imported);
52
+ if (!module || typeof module !== "object") {
53
+ throw new Error(`remote_source module must export default object: ${resolvedPath}`);
54
54
  }
55
- validateProfileContract(profile, resolvedPath);
56
- this.profileCache.set(cacheKey, profile);
57
- return profile;
55
+ validateModuleContract(module, resolvedPath);
56
+ this.moduleCache.set(cacheKey, module);
57
+ return module;
58
58
  }
59
59
  }
60
- exports.RemoteSourceProfileRegistry = RemoteSourceProfileRegistry;
60
+ exports.RemoteSourceModuleRegistry = RemoteSourceModuleRegistry;
61
61
  async function dynamicImportModule(moduleUrl) {
62
62
  const importer = new Function("url", "return import(url);");
63
63
  return importer(moduleUrl);
64
64
  }
65
- function builtInProfileIdForSourceType(sourceType) {
65
+ function builtInModuleIdForSourceType(sourceType) {
66
66
  if (sourceType === "github_repo") {
67
67
  return "builtin.github_repo";
68
68
  }
@@ -74,42 +74,46 @@ function builtInProfileIdForSourceType(sourceType) {
74
74
  }
75
75
  return null;
76
76
  }
77
- function validateProfileContract(profile, sourcePath) {
78
- if (!profile.id || typeof profile.id !== "string") {
79
- throw new Error(`remote_source profile id must be a non-empty string: ${sourcePath}`);
77
+ function validateModuleContract(module, sourcePath) {
78
+ if (!module.id || typeof module.id !== "string") {
79
+ throw new Error(`remote_source module id must be a non-empty string: ${sourcePath}`);
80
80
  }
81
- if (BUILTIN_PROFILE_IDS.has(profile.id)) {
82
- throw new Error(`remote_source profile id is reserved: ${profile.id}`);
81
+ if (BUILTIN_MODULE_IDS.has(module.id)) {
82
+ throw new Error(`remote_source module id is reserved: ${module.id}`);
83
83
  }
84
- if (typeof profile.validateConfig !== "function") {
85
- throw new Error(`remote_source profile missing validateConfig(): ${sourcePath}`);
84
+ if (typeof module.validateConfig !== "function") {
85
+ throw new Error(`remote_source module missing validateConfig(): ${sourcePath}`);
86
86
  }
87
- if (typeof profile.buildManagedSourceSpec !== "function") {
88
- throw new Error(`remote_source profile missing buildManagedSourceSpec(): ${sourcePath}`);
87
+ if (typeof module.buildManagedSourceSpec !== "function") {
88
+ throw new Error(`remote_source module missing buildManagedSourceSpec(): ${sourcePath}`);
89
89
  }
90
- if (typeof profile.mapRawEvent !== "function") {
91
- throw new Error(`remote_source profile missing mapRawEvent(): ${sourcePath}`);
90
+ if (typeof module.mapRawEvent !== "function") {
91
+ throw new Error(`remote_source module missing mapRawEvent(): ${sourcePath}`);
92
92
  }
93
- validateOptionalHook(profile.describeCapabilities, "describeCapabilities", sourcePath);
94
- validateOptionalHook(profile.listSubscriptionShortcuts, "listSubscriptionShortcuts", sourcePath);
95
- validateOptionalHook(profile.expandSubscriptionShortcut, "expandSubscriptionShortcut", sourcePath);
96
- validateOptionalHook(profile.deriveTrackedResource, "deriveTrackedResource", sourcePath);
97
- validateOptionalHook(profile.projectLifecycleSignal, "projectLifecycleSignal", sourcePath);
98
- validateOptionalHook(profile.deriveInlinePreview, "deriveInlinePreview", sourcePath);
93
+ validateOptionalHook(module.describeCapabilities, "describeCapabilities", sourcePath);
94
+ validateOptionalHook(module.listSubscriptionShortcuts, "listSubscriptionShortcuts", sourcePath);
95
+ validateOptionalHook(module.expandSubscriptionShortcut, "expandSubscriptionShortcut", sourcePath);
96
+ validateOptionalHook(module.deriveTrackedResource, "deriveTrackedResource", sourcePath);
97
+ validateOptionalHook(module.projectLifecycleSignal, "projectLifecycleSignal", sourcePath);
98
+ validateOptionalHook(module.deriveInlinePreview, "deriveInlinePreview", sourcePath);
99
+ validateOptionalHook(module.deriveNotificationGrouping, "deriveNotificationGrouping", sourcePath);
100
+ validateOptionalHook(module.listDeliveryOperations, "listDeliveryOperations", sourcePath);
101
+ validateOptionalHook(module.invokeDeliveryOperation, "invokeDeliveryOperation", sourcePath);
102
+ validateOptionalHook(module.summarizeDigestThread, "summarizeDigestThread", sourcePath);
99
103
  }
100
104
  function validateOptionalHook(value, name, sourcePath) {
101
105
  if (value !== undefined && typeof value !== "function") {
102
- throw new Error(`remote_source profile ${name} must be a function when provided: ${sourcePath}`);
106
+ throw new Error(`remote_source module ${name} must be a function when provided: ${sourcePath}`);
103
107
  }
104
108
  }
105
- function resolveAndValidateProfilePath(profileRoot, profilePath) {
106
- const resolved = node_path_1.default.isAbsolute(profilePath)
107
- ? node_path_1.default.resolve(profilePath)
108
- : node_path_1.default.resolve(profileRoot, profilePath);
109
- const normalizedRoot = ensureTrailingSep(node_path_1.default.resolve(profileRoot));
109
+ function resolveAndValidateModulePath(moduleRoot, modulePath) {
110
+ const resolved = node_path_1.default.isAbsolute(modulePath)
111
+ ? node_path_1.default.resolve(modulePath)
112
+ : node_path_1.default.resolve(moduleRoot, modulePath);
113
+ const normalizedRoot = ensureTrailingSep(node_path_1.default.resolve(moduleRoot));
110
114
  const normalizedResolved = node_path_1.default.resolve(resolved);
111
- if (!normalizedResolved.startsWith(normalizedRoot) && normalizedResolved !== node_path_1.default.resolve(profileRoot)) {
112
- throw new Error(`remote_source profilePath must stay under ${profileRoot}`);
115
+ if (!normalizedResolved.startsWith(normalizedRoot) && normalizedResolved !== node_path_1.default.resolve(moduleRoot)) {
116
+ throw new Error(`remote_source modulePath must stay under ${moduleRoot}`);
113
117
  }
114
118
  return normalizedResolved;
115
119
  }
@@ -129,8 +133,14 @@ function asNonEmptyString(value) {
129
133
  const trimmed = value.trim();
130
134
  return trimmed.length > 0 ? trimmed : null;
131
135
  }
132
- const GITHUB_REPO_PROFILE = {
136
+ const GITHUB_REPO_MODULE = {
133
137
  id: "builtin.github_repo",
138
+ listDeliveryOperations(input) {
139
+ return (0, github_1.githubDeliveryOperationsForHandle)(input.handle);
140
+ },
141
+ async invokeDeliveryOperation(input) {
142
+ return (0, github_1.invokeGithubDeliveryOperation)(input.handle, input.operation, input.input);
143
+ },
134
144
  describeCapabilities(source) {
135
145
  const config = (0, github_1.parseGithubSourceConfig)(source);
136
146
  return {
@@ -193,6 +203,54 @@ const GITHUB_REPO_PROFILE = {
193
203
  deriveTrackedResource(filter) {
194
204
  return (0, github_1.deriveGithubTrackedResource)(filter);
195
205
  },
206
+ deriveNotificationGrouping(item) {
207
+ const number = typeof item.metadata.number === "number" ? item.metadata.number : null;
208
+ if (!number) {
209
+ return null;
210
+ }
211
+ const variant = item.eventVariant;
212
+ if (variant.startsWith("PullRequestReviewCommentEvent.")) {
213
+ return {
214
+ groupable: true,
215
+ resourceRef: `pr:${number}`,
216
+ eventFamily: "review_comments",
217
+ summaryHint: `review comments on PR #${number}`,
218
+ };
219
+ }
220
+ if (variant.startsWith("PullRequestReviewEvent.")) {
221
+ return {
222
+ groupable: true,
223
+ resourceRef: `pr:${number}`,
224
+ eventFamily: "reviews",
225
+ summaryHint: `reviews on PR #${number}`,
226
+ };
227
+ }
228
+ if (variant.startsWith("IssueCommentEvent.")) {
229
+ return {
230
+ groupable: true,
231
+ resourceRef: item.metadata.isPullRequest === true ? `pr:${number}` : `issue:${number}`,
232
+ eventFamily: "comments",
233
+ summaryHint: item.metadata.isPullRequest === true ? `comments on PR #${number}` : `comments on issue #${number}`,
234
+ };
235
+ }
236
+ if (variant.startsWith("PullRequestEvent.closed")) {
237
+ return {
238
+ groupable: true,
239
+ resourceRef: `pr:${number}`,
240
+ eventFamily: "lifecycle",
241
+ summaryHint: `PR #${number} lifecycle updates`,
242
+ flushClass: "immediate",
243
+ };
244
+ }
245
+ return null;
246
+ },
247
+ summarizeDigestThread(items, _source, grouping) {
248
+ if (!grouping.summaryHint || items.length === 0) {
249
+ return null;
250
+ }
251
+ const count = items.length;
252
+ return `${count} ${grouping.summaryHint}`;
253
+ },
196
254
  projectLifecycleSignal(rawPayload) {
197
255
  return (0, github_1.projectGithubLifecycleSignal)(rawPayload);
198
256
  },
@@ -238,7 +296,7 @@ const GITHUB_REPO_PROFILE = {
238
296
  };
239
297
  },
240
298
  };
241
- const GITHUB_REPO_CI_PROFILE = {
299
+ const GITHUB_REPO_CI_MODULE = {
242
300
  id: "builtin.github_repo_ci",
243
301
  describeCapabilities() {
244
302
  return {
@@ -298,6 +356,27 @@ const GITHUB_REPO_CI_PROFILE = {
298
356
  }
299
357
  return parts.join(" ");
300
358
  },
359
+ deriveNotificationGrouping(item) {
360
+ const workflowRunId = typeof item.metadata.workflowRunId === "number" ? item.metadata.workflowRunId : null;
361
+ if (!workflowRunId) {
362
+ return null;
363
+ }
364
+ const conclusion = asNonEmptyString(item.metadata.conclusion);
365
+ const status = asNonEmptyString(item.metadata.status);
366
+ return {
367
+ groupable: true,
368
+ resourceRef: `workflow_run:${workflowRunId}`,
369
+ eventFamily: "ci_updates",
370
+ summaryHint: `CI updates for run #${workflowRunId}`,
371
+ flushClass: conclusion || status === "completed" ? "immediate" : "normal",
372
+ };
373
+ },
374
+ summarizeDigestThread(items, _source, grouping) {
375
+ if (!grouping.summaryHint || items.length === 0) {
376
+ return null;
377
+ }
378
+ return `${items.length} ${grouping.summaryHint}`;
379
+ },
301
380
  validateConfig(source) {
302
381
  (0, github_ci_1.parseGithubCiSourceConfig)(source);
303
382
  },
@@ -349,8 +428,14 @@ const GITHUB_REPO_CI_PROFILE = {
349
428
  };
350
429
  },
351
430
  };
352
- const FEISHU_BOT_PROFILE = {
431
+ const FEISHU_BOT_MODULE = {
353
432
  id: "builtin.feishu_bot",
433
+ listDeliveryOperations(input) {
434
+ return (0, feishu_1.feishuDeliveryOperationsForHandle)(input.handle);
435
+ },
436
+ async invokeDeliveryOperation(input) {
437
+ return (0, feishu_1.invokeFeishuDeliveryOperation)(input.handle, input.operation, input.input);
438
+ },
354
439
  describeCapabilities() {
355
440
  return {
356
441
  sourceKind: "feishu_bot",
@@ -409,10 +494,10 @@ const FEISHU_BOT_PROFILE = {
409
494
  };
410
495
  },
411
496
  };
412
- function profileConfigForSource(source) {
497
+ function moduleConfigForSource(source) {
413
498
  const config = asRecord(source.config);
414
499
  if (source.sourceType === "remote_source") {
415
- return asRecord(config.profileConfig);
500
+ return asRecord(config.moduleConfig);
416
501
  }
417
502
  return config;
418
503
  }