@holon-run/agentinbox 0.5.0 → 0.7.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.
@@ -9,6 +9,7 @@ const source_resolution_1 = require("./source_resolution");
9
9
  const filter_1 = require("./filter");
10
10
  const terminal_1 = require("./terminal");
11
11
  const runtime_gate_1 = require("./runtime_gate");
12
+ const logging_1 = require("./logging");
12
13
  const DEFAULT_SUBSCRIPTION_POLL_LIMIT = 100;
13
14
  const DEFAULT_ACTIVATION_WINDOW_MS = 3_000;
14
15
  const DEFAULT_ACTIVATION_MAX_ITEMS = 20;
@@ -43,7 +44,7 @@ class AgentInboxService {
43
44
  lastAckedInboxGcAt = 0;
44
45
  lastLifecycleCleanupAt = 0;
45
46
  lastOfflineAgentGcAt = 0;
46
- constructor(store, adapters, activationDispatcher = new ActivationDispatcher(), backend, activationPolicy, terminalDispatcher = new terminal_1.TerminalDispatcher(), activationGate = new runtime_gate_1.DefaultActivationGate()) {
47
+ constructor(store, adapters, activationDispatcher = new ActivationDispatcher(), backend, activationPolicy, terminalDispatcher = new terminal_1.TerminalDispatcher(), activationGate, logger = new logging_1.NoopLogger()) {
47
48
  this.store = store;
48
49
  this.adapters = adapters;
49
50
  this.activationDispatcher = activationDispatcher;
@@ -52,11 +53,13 @@ class AgentInboxService {
52
53
  this.activationMaxItems = activationPolicy?.maxItems ?? DEFAULT_ACTIVATION_MAX_ITEMS;
53
54
  this.ackedRetentionMs = DEFAULT_ACKED_RETENTION_MS;
54
55
  this.terminalDispatcher = terminalDispatcher;
55
- this.activationGate = activationGate;
56
+ this.logger = logger;
57
+ this.activationGate = activationGate ?? new runtime_gate_1.DefaultActivationGate(undefined, undefined, this.logger.child("gate"));
56
58
  }
57
59
  activationDispatcher;
58
60
  terminalDispatcher;
59
61
  activationGate;
62
+ logger;
60
63
  async start() {
61
64
  if (this.syncInterval) {
62
65
  return;
@@ -1550,12 +1553,32 @@ class AgentInboxService {
1550
1553
  try {
1551
1554
  if (target.kind === "terminal") {
1552
1555
  const gate = await this.activationGate.evaluate(target);
1556
+ this.logger.debug("activation.gate_decision", {
1557
+ agentId: input.agentId,
1558
+ targetId: target.targetId,
1559
+ runtimeKind: target.runtimeKind,
1560
+ backend: target.backend,
1561
+ outcome: gate.outcome,
1562
+ reason: gate.reason,
1563
+ });
1553
1564
  if (gate.outcome === "offline") {
1565
+ this.logger.info("activation.target_offline", {
1566
+ agentId: input.agentId,
1567
+ targetId: target.targetId,
1568
+ reason: gate.reason,
1569
+ transition: "runtime_gate",
1570
+ });
1554
1571
  this.markActivationTargetOffline(target.targetId, `runtime gate: ${gate.reason}`);
1555
1572
  this.reconcileAgentStatus(target.agentId);
1556
1573
  return "offline";
1557
1574
  }
1558
1575
  if (gate.outcome === "defer") {
1576
+ this.logger.debug("activation.dispatch_deferred", {
1577
+ agentId: input.agentId,
1578
+ targetId: target.targetId,
1579
+ reason: gate.reason,
1580
+ retryMs: DEFAULT_NOTIFY_RETRY_MS,
1581
+ });
1559
1582
  this.store.upsertActivationDispatchState({
1560
1583
  agentId: input.agentId,
1561
1584
  targetId: input.targetId,
@@ -1570,9 +1593,21 @@ class AgentInboxService {
1570
1593
  });
1571
1594
  return "deferred";
1572
1595
  }
1573
- const preview = totalUnackedCount === 1 && input.items.length === 1
1574
- ? (0, terminal_1.deriveInlineItemPreview)(input.items[0], input.summary)
1575
- : null;
1596
+ let preview = null;
1597
+ if (totalUnackedCount === 1 && input.items.length === 1) {
1598
+ const singleItem = input.items[0];
1599
+ const source = this.store.getSource(singleItem.sourceId);
1600
+ if (source) {
1601
+ try {
1602
+ const sourcePreview = await this.adapters.deriveInlinePreview(source, singleItem);
1603
+ preview = typeof sourcePreview === "string" ? (0, terminal_1.normalizeInlinePreviewText)(sourcePreview) : null;
1604
+ }
1605
+ catch {
1606
+ preview = null;
1607
+ }
1608
+ }
1609
+ preview ??= (0, terminal_1.deriveInlineItemPreview)(singleItem, input.summary);
1610
+ }
1576
1611
  const prompt = (0, terminal_1.renderAgentPrompt)({
1577
1612
  inboxId: inbox.inboxId,
1578
1613
  totalUnackedCount,
@@ -1582,6 +1617,11 @@ class AgentInboxService {
1582
1617
  terminalPrompt = prompt;
1583
1618
  const promptFingerprint = terminalReminderFingerprint(prompt, input.items);
1584
1619
  if (state?.status === "dirty" && state.lastNotifiedFingerprint === promptFingerprint) {
1620
+ this.logger.debug("activation.dispatch_suppressed", {
1621
+ agentId: input.agentId,
1622
+ targetId: target.targetId,
1623
+ reason: "unchanged_terminal_prompt",
1624
+ });
1585
1625
  this.store.upsertActivationDispatchState({
1586
1626
  agentId: input.agentId,
1587
1627
  targetId: input.targetId,
@@ -1596,7 +1636,19 @@ class AgentInboxService {
1596
1636
  });
1597
1637
  return "suppressed";
1598
1638
  }
1639
+ this.logger.debug("activation.terminal_dispatch_start", {
1640
+ agentId: input.agentId,
1641
+ targetId: target.targetId,
1642
+ totalUnackedCount,
1643
+ newItemCount: input.newItemCount,
1644
+ sourceIds: input.sourceIds,
1645
+ subscriptionIds: input.subscriptionIds,
1646
+ });
1599
1647
  await this.terminalDispatcher.dispatch(target, prompt);
1648
+ this.logger.debug("activation.terminal_dispatch_succeeded", {
1649
+ agentId: input.agentId,
1650
+ targetId: target.targetId,
1651
+ });
1600
1652
  }
1601
1653
  else {
1602
1654
  const activation = {
@@ -1636,11 +1688,28 @@ class AgentInboxService {
1636
1688
  return "dispatched";
1637
1689
  }
1638
1690
  catch (error) {
1639
- console.warn(`activation target dispatch failed for ${target.targetId}:`, error);
1640
1691
  const message = error instanceof Error ? error.message : String(error);
1692
+ this.logger.warn("activation.dispatch_failed", {
1693
+ agentId: input.agentId,
1694
+ targetId: target.targetId,
1695
+ targetKind: target.kind,
1696
+ error,
1697
+ message,
1698
+ });
1641
1699
  if (target.kind === "terminal") {
1642
1700
  const exists = await this.terminalDispatcher.probe(target);
1701
+ this.logger.debug("activation.terminal_probe_after_failure", {
1702
+ agentId: input.agentId,
1703
+ targetId: target.targetId,
1704
+ exists,
1705
+ });
1643
1706
  if (!exists) {
1707
+ this.logger.info("activation.target_offline", {
1708
+ agentId: input.agentId,
1709
+ targetId: target.targetId,
1710
+ reason: message,
1711
+ transition: "dispatch_probe",
1712
+ });
1644
1713
  this.markActivationTargetOffline(target.targetId, message);
1645
1714
  this.reconcileAgentStatus(target.agentId);
1646
1715
  return "offline";
@@ -4,7 +4,7 @@ exports.resolveSourceIdentity = resolveSourceIdentity;
4
4
  exports.resolveSourceSchema = resolveSourceSchema;
5
5
  exports.withResolvedIdentity = withResolvedIdentity;
6
6
  const paths_1 = require("./paths");
7
- const remote_profiles_1 = require("./sources/remote_profiles");
7
+ const remote_modules_1 = require("./sources/remote_modules");
8
8
  const source_schema_1 = require("./source_schema");
9
9
  async function resolveSourceIdentity(source, context = {}) {
10
10
  if (source.sourceType === "local_event") {
@@ -14,7 +14,7 @@ async function resolveSourceIdentity(source, context = {}) {
14
14
  implementationId: "builtin.local_event",
15
15
  };
16
16
  }
17
- const builtinImplementationId = (0, remote_profiles_1.builtInProfileIdForSourceType)(source.sourceType);
17
+ const builtinImplementationId = (0, remote_modules_1.builtInModuleIdForSourceType)(source.sourceType);
18
18
  if (builtinImplementationId) {
19
19
  return {
20
20
  hostType: "remote_source",
@@ -25,30 +25,30 @@ async function resolveSourceIdentity(source, context = {}) {
25
25
  if (source.sourceType !== "remote_source") {
26
26
  throw new Error(`unsupported source type for source resolution: ${source.sourceType}`);
27
27
  }
28
- const profileRegistry = context.profileRegistry ?? new remote_profiles_1.RemoteSourceProfileRegistry();
28
+ const moduleRegistry = context.moduleRegistry ?? context.profileRegistry ?? new remote_modules_1.RemoteSourceModuleRegistry();
29
29
  const homeDir = context.homeDir ?? (0, paths_1.resolveAgentInboxHome)(process.env);
30
- const profile = await profileRegistry.resolve(source, homeDir);
31
- const capabilityDescription = typeof profile.describeCapabilities === "function"
32
- ? profile.describeCapabilities(profileInputSource(source))
30
+ const module = await moduleRegistry.resolve(source, homeDir);
31
+ const capabilityDescription = typeof module.describeCapabilities === "function"
32
+ ? module.describeCapabilities(moduleInputSource(source))
33
33
  : undefined;
34
34
  return {
35
35
  hostType: "remote_source",
36
- sourceKind: capabilityDescription?.sourceKind?.trim() || `remote:${profile.id}`,
37
- implementationId: profile.id,
36
+ sourceKind: capabilityDescription?.sourceKind?.trim() || `remote:${module.id}`,
37
+ implementationId: module.id,
38
38
  };
39
39
  }
40
40
  async function resolveSourceSchema(source, context = {}) {
41
- const profileRegistry = context.profileRegistry ?? new remote_profiles_1.RemoteSourceProfileRegistry();
41
+ const moduleRegistry = context.moduleRegistry ?? context.profileRegistry ?? new remote_modules_1.RemoteSourceModuleRegistry();
42
42
  const homeDir = context.homeDir ?? (0, paths_1.resolveAgentInboxHome)(process.env);
43
- const resolvedContext = { ...context, profileRegistry, homeDir };
43
+ const resolvedContext = { ...context, moduleRegistry, homeDir };
44
44
  const identity = await resolveSourceIdentity(source, resolvedContext);
45
45
  const staticSchema = (0, source_schema_1.getSourceSchema)(source.sourceType);
46
46
  let capabilityDescription;
47
- let profile = null;
47
+ let module = null;
48
48
  if (identity.hostType === "remote_source") {
49
- profile = await profileRegistry.resolve(source, homeDir);
50
- capabilityDescription = typeof profile.describeCapabilities === "function"
51
- ? profile.describeCapabilities(profileInputSource(source))
49
+ module = await moduleRegistry.resolve(source, homeDir);
50
+ capabilityDescription = typeof module.describeCapabilities === "function"
51
+ ? module.describeCapabilities(moduleInputSource(source))
52
52
  : undefined;
53
53
  }
54
54
  return withResolvedIdentity(source.sourceId, {
@@ -60,10 +60,10 @@ async function resolveSourceSchema(source, context = {}) {
60
60
  }, identity, identity.hostType === "remote_source"
61
61
  ? {
62
62
  aliases: capabilityDescription?.aliases,
63
- supportsTrackedResourceRef: typeof profile?.deriveTrackedResource === "function",
64
- supportsLifecycleSignals: typeof profile?.projectLifecycleSignal === "function",
65
- shortcuts: typeof profile?.listSubscriptionShortcuts === "function"
66
- ? profile.listSubscriptionShortcuts(profileInputSource(source))
63
+ supportsTrackedResourceRef: typeof module?.deriveTrackedResource === "function",
64
+ supportsLifecycleSignals: typeof module?.projectLifecycleSignal === "function",
65
+ shortcuts: typeof module?.listSubscriptionShortcuts === "function"
66
+ ? module.listSubscriptionShortcuts(moduleInputSource(source))
67
67
  : [],
68
68
  }
69
69
  : undefined);
@@ -89,12 +89,12 @@ function withResolvedIdentity(sourceId, schema, identity, capabilityMetadata) {
89
89
  } : {}),
90
90
  };
91
91
  }
92
- function profileInputSource(source) {
92
+ function moduleInputSource(source) {
93
93
  if (source.sourceType !== "remote_source") {
94
94
  return source;
95
95
  }
96
96
  return {
97
97
  ...source,
98
- config: (0, remote_profiles_1.profileConfigForSource)(source),
98
+ config: (0, remote_modules_1.moduleConfigForSource)(source),
99
99
  };
100
100
  }
@@ -21,8 +21,8 @@ const SOURCE_SCHEMAS = {
21
21
  payloadExamples: [],
22
22
  eventVariantExamples: [],
23
23
  configFields: [
24
- { name: "profilePath", type: "string", required: true, description: "Local profile module path under $AGENTINBOX_HOME/source-profiles." },
25
- { name: "profileConfig", type: "object", required: false, description: "Profile-specific configuration passed to validate/spec/map hooks." },
24
+ { name: "profilePath", type: "string", required: true, description: "Local module path under $AGENTINBOX_HOME/source-profiles." },
25
+ { name: "profileConfig", type: "object", required: false, description: "Module-specific configuration passed to validate/spec/map hooks." },
26
26
  ],
27
27
  },
28
28
  github_repo: {
@@ -4,7 +4,7 @@ exports.RemoteSourceRuntime = exports.RpcUxcRemoteSourceClient = void 0;
4
4
  const uxc_daemon_client_1 = require("@holon-run/uxc-daemon-client");
5
5
  const paths_1 = require("../paths");
6
6
  const util_1 = require("../util");
7
- const remote_profiles_1 = require("./remote_profiles");
7
+ const remote_modules_1 = require("./remote_modules");
8
8
  const REMOTE_SOURCE_TYPES = new Set([
9
9
  "remote_source",
10
10
  "github_repo",
@@ -55,7 +55,7 @@ exports.RpcUxcRemoteSourceClient = RpcUxcRemoteSourceClient;
55
55
  class RemoteSourceRuntime {
56
56
  store;
57
57
  appendSourceEvent;
58
- profileRegistry;
58
+ moduleRegistry;
59
59
  client;
60
60
  interval = null;
61
61
  inFlight = new Set();
@@ -65,7 +65,7 @@ class RemoteSourceRuntime {
65
65
  constructor(store, appendSourceEvent, options) {
66
66
  this.store = store;
67
67
  this.appendSourceEvent = appendSourceEvent;
68
- this.profileRegistry = options?.profileRegistry ?? new remote_profiles_1.RemoteSourceProfileRegistry();
68
+ this.moduleRegistry = options?.moduleRegistry ?? options?.profileRegistry ?? new remote_modules_1.RemoteSourceModuleRegistry();
69
69
  this.client = options?.client ?? new RpcUxcRemoteSourceClient();
70
70
  this.homeDir = options?.homeDir ?? (0, paths_1.resolveAgentInboxHome)(process.env);
71
71
  }
@@ -79,10 +79,10 @@ class RemoteSourceRuntime {
79
79
  if (!REMOTE_SOURCE_TYPES.has(source.sourceType)) {
80
80
  return;
81
81
  }
82
- const profile = await this.profileRegistry.resolve(source, this.homeDir);
83
- const profileSource = profileInputSource(source);
84
- profile.validateConfig(profileSource);
85
- profile.buildManagedSourceSpec(profileSource);
82
+ const module = await this.moduleRegistry.resolve(source, this.homeDir);
83
+ const moduleSource = moduleInputSource(source);
84
+ module.validateConfig(moduleSource);
85
+ module.buildManagedSourceSpec(moduleSource);
86
86
  }
87
87
  async start() {
88
88
  if (this.interval) {
@@ -171,26 +171,36 @@ class RemoteSourceRuntime {
171
171
  if (!REMOTE_SOURCE_TYPES.has(source.sourceType)) {
172
172
  return null;
173
173
  }
174
- const profile = await this.profileRegistry.resolve(source, this.homeDir);
175
- if (typeof profile.projectLifecycleSignal !== "function") {
174
+ const module = await this.moduleRegistry.resolve(source, this.homeDir);
175
+ if (typeof module.projectLifecycleSignal !== "function") {
176
176
  return null;
177
177
  }
178
- return profile.projectLifecycleSignal(rawPayload, profileInputSource(source));
178
+ return module.projectLifecycleSignal(rawPayload, moduleInputSource(source));
179
179
  }
180
180
  async expandSubscriptionShortcut(source, input) {
181
181
  if (!REMOTE_SOURCE_TYPES.has(source.sourceType)) {
182
182
  return null;
183
183
  }
184
- const profile = await this.profileRegistry.resolve(source, this.homeDir);
185
- if (typeof profile.expandSubscriptionShortcut !== "function") {
184
+ const module = await this.moduleRegistry.resolve(source, this.homeDir);
185
+ if (typeof module.expandSubscriptionShortcut !== "function") {
186
186
  return null;
187
187
  }
188
- return profile.expandSubscriptionShortcut({
188
+ return module.expandSubscriptionShortcut({
189
189
  name: input.name,
190
190
  args: input.args,
191
- source: profileInputSource(source),
191
+ source: moduleInputSource(source),
192
192
  });
193
193
  }
194
+ async deriveInlinePreview(source, item) {
195
+ if (!REMOTE_SOURCE_TYPES.has(source.sourceType)) {
196
+ return null;
197
+ }
198
+ const module = await this.moduleRegistry.resolve(source, this.homeDir);
199
+ if (typeof module.deriveInlinePreview !== "function") {
200
+ return null;
201
+ }
202
+ return module.deriveInlinePreview(item, moduleInputSource(source));
203
+ }
194
204
  async syncAll() {
195
205
  const sources = this.store
196
206
  .listSources()
@@ -245,10 +255,10 @@ class RemoteSourceRuntime {
245
255
  };
246
256
  }
247
257
  }
248
- const profile = await this.profileRegistry.resolve(source, this.homeDir);
249
- const profileSource = profileInputSource(source);
250
- profile.validateConfig(profileSource);
251
- const spec = profile.buildManagedSourceSpec(profileSource);
258
+ const module = await this.moduleRegistry.resolve(source, this.homeDir);
259
+ const moduleSource = moduleInputSource(source);
260
+ module.validateConfig(moduleSource);
261
+ const spec = module.buildManagedSourceSpec(moduleSource);
252
262
  const binding = managedBindingForSource(source);
253
263
  const ensured = await this.client.sourceEnsure({
254
264
  namespace: binding.namespace,
@@ -269,7 +279,7 @@ class RemoteSourceRuntime {
269
279
  if (Object.keys(rawPayload).length === 0) {
270
280
  continue;
271
281
  }
272
- const mapped = profile.mapRawEvent(rawPayload, profileSource);
282
+ const mapped = module.mapRawEvent(rawPayload, moduleSource);
273
283
  if (!mapped) {
274
284
  continue;
275
285
  }
@@ -338,7 +348,7 @@ class RemoteSourceRuntime {
338
348
  }
339
349
  }
340
350
  exports.RemoteSourceRuntime = RemoteSourceRuntime;
341
- function profileInputSource(source) {
351
+ function moduleInputSource(source) {
342
352
  if (source.sourceType !== "remote_source") {
343
353
  return source;
344
354
  }