@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.
- package/dist/src/adapters.js +12 -6
- package/dist/src/cli.js +18 -5
- package/dist/src/daemon.js +36 -2
- package/dist/src/http.js +1 -0
- package/dist/src/logging.js +112 -0
- package/dist/src/paths.js +1 -0
- package/dist/src/runtime_gate.js +585 -35
- package/dist/src/service.js +75 -6
- package/dist/src/source_resolution.js +20 -20
- package/dist/src/source_schema.js +2 -2
- package/dist/src/sources/remote.js +30 -20
- package/dist/src/sources/remote_modules.js +418 -0
- package/dist/src/sources/remote_profiles.js +9 -383
- package/dist/src/store.js +13 -7
- package/dist/src/terminal.js +5 -1
- package/drizzle/migrations/0008_inbox_item_sequence.sql +21 -0
- package/drizzle/schema.ts +3 -0
- package/package.json +2 -1
- package/scripts/iterm2_cursor_probe.py +91 -0
package/dist/src/service.js
CHANGED
|
@@ -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
|
|
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.
|
|
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
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
31
|
-
const capabilityDescription = typeof
|
|
32
|
-
?
|
|
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:${
|
|
37
|
-
implementationId:
|
|
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
|
|
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,
|
|
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
|
|
47
|
+
let module = null;
|
|
48
48
|
if (identity.hostType === "remote_source") {
|
|
49
|
-
|
|
50
|
-
capabilityDescription = typeof
|
|
51
|
-
?
|
|
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
|
|
64
|
-
supportsLifecycleSignals: typeof
|
|
65
|
-
shortcuts: typeof
|
|
66
|
-
?
|
|
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
|
|
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,
|
|
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
|
|
25
|
-
{ name: "profileConfig", type: "object", required: false, description: "
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
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
|
|
175
|
-
if (typeof
|
|
174
|
+
const module = await this.moduleRegistry.resolve(source, this.homeDir);
|
|
175
|
+
if (typeof module.projectLifecycleSignal !== "function") {
|
|
176
176
|
return null;
|
|
177
177
|
}
|
|
178
|
-
return
|
|
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
|
|
185
|
-
if (typeof
|
|
184
|
+
const module = await this.moduleRegistry.resolve(source, this.homeDir);
|
|
185
|
+
if (typeof module.expandSubscriptionShortcut !== "function") {
|
|
186
186
|
return null;
|
|
187
187
|
}
|
|
188
|
-
return
|
|
188
|
+
return module.expandSubscriptionShortcut({
|
|
189
189
|
name: input.name,
|
|
190
190
|
args: input.args,
|
|
191
|
-
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
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
const spec =
|
|
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 =
|
|
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
|
|
351
|
+
function moduleInputSource(source) {
|
|
342
352
|
if (source.sourceType !== "remote_source") {
|
|
343
353
|
return source;
|
|
344
354
|
}
|