@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
|
@@ -1,387 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.RemoteSourceProfileRegistry = void 0;
|
|
7
|
-
|
|
8
|
-
exports.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const github_ci_1 = require("./github_ci");
|
|
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();
|
|
19
|
-
resolve(source, homeDir) {
|
|
20
|
-
if (source.sourceType === "github_repo") {
|
|
21
|
-
return Promise.resolve(GITHUB_REPO_PROFILE);
|
|
22
|
-
}
|
|
23
|
-
if (source.sourceType === "github_repo_ci") {
|
|
24
|
-
return Promise.resolve(GITHUB_REPO_CI_PROFILE);
|
|
25
|
-
}
|
|
26
|
-
if (source.sourceType === "feishu_bot") {
|
|
27
|
-
return Promise.resolve(FEISHU_BOT_PROFILE);
|
|
28
|
-
}
|
|
29
|
-
if (source.sourceType !== "remote_source") {
|
|
30
|
-
throw new Error(`unsupported source type for remote profile: ${source.sourceType}`);
|
|
31
|
-
}
|
|
32
|
-
return this.loadUserProfile(source, homeDir);
|
|
33
|
-
}
|
|
34
|
-
async loadUserProfile(source, homeDir) {
|
|
35
|
-
const config = source.config ?? {};
|
|
36
|
-
const profilePath = asNonEmptyString(config.profilePath);
|
|
37
|
-
if (!profilePath) {
|
|
38
|
-
throw new Error("remote_source requires config.profilePath");
|
|
39
|
-
}
|
|
40
|
-
const profileRoot = node_path_1.default.resolve(homeDir, REMOTE_USER_PROFILE_ROOT_DIR);
|
|
41
|
-
const resolvedPath = resolveAndValidateProfilePath(profileRoot, profilePath);
|
|
42
|
-
const cacheKey = `${resolvedPath}:${source.sourceKey}`;
|
|
43
|
-
const cached = this.profileCache.get(cacheKey);
|
|
44
|
-
if (cached) {
|
|
45
|
-
return cached;
|
|
46
|
-
}
|
|
47
|
-
if (!node_fs_1.default.existsSync(resolvedPath)) {
|
|
48
|
-
throw new Error(`remote_source profile not found: ${resolvedPath}`);
|
|
49
|
-
}
|
|
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}`);
|
|
54
|
-
}
|
|
55
|
-
validateProfileContract(profile, resolvedPath);
|
|
56
|
-
this.profileCache.set(cacheKey, profile);
|
|
57
|
-
return profile;
|
|
58
|
-
}
|
|
3
|
+
exports.profileConfigForSource = exports.builtInProfileIdForSourceType = exports.RemoteSourceProfileRegistry = exports.moduleConfigForSource = exports.builtInModuleIdForSourceType = exports.RemoteSourceModuleRegistry = void 0;
|
|
4
|
+
var remote_modules_1 = require("./remote_modules");
|
|
5
|
+
Object.defineProperty(exports, "RemoteSourceModuleRegistry", { enumerable: true, get: function () { return remote_modules_1.RemoteSourceModuleRegistry; } });
|
|
6
|
+
Object.defineProperty(exports, "builtInModuleIdForSourceType", { enumerable: true, get: function () { return remote_modules_1.builtInModuleIdForSourceType; } });
|
|
7
|
+
Object.defineProperty(exports, "moduleConfigForSource", { enumerable: true, get: function () { return remote_modules_1.moduleConfigForSource; } });
|
|
8
|
+
const remote_modules_2 = require("./remote_modules");
|
|
9
|
+
class RemoteSourceProfileRegistry extends remote_modules_2.RemoteSourceModuleRegistry {
|
|
59
10
|
}
|
|
60
11
|
exports.RemoteSourceProfileRegistry = RemoteSourceProfileRegistry;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return importer(moduleUrl);
|
|
64
|
-
}
|
|
65
|
-
function builtInProfileIdForSourceType(sourceType) {
|
|
66
|
-
if (sourceType === "github_repo") {
|
|
67
|
-
return "builtin.github_repo";
|
|
68
|
-
}
|
|
69
|
-
if (sourceType === "github_repo_ci") {
|
|
70
|
-
return "builtin.github_repo_ci";
|
|
71
|
-
}
|
|
72
|
-
if (sourceType === "feishu_bot") {
|
|
73
|
-
return "builtin.feishu_bot";
|
|
74
|
-
}
|
|
75
|
-
return null;
|
|
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}`);
|
|
80
|
-
}
|
|
81
|
-
if (BUILTIN_PROFILE_IDS.has(profile.id)) {
|
|
82
|
-
throw new Error(`remote_source profile id is reserved: ${profile.id}`);
|
|
83
|
-
}
|
|
84
|
-
if (typeof profile.validateConfig !== "function") {
|
|
85
|
-
throw new Error(`remote_source profile missing validateConfig(): ${sourcePath}`);
|
|
86
|
-
}
|
|
87
|
-
if (typeof profile.buildManagedSourceSpec !== "function") {
|
|
88
|
-
throw new Error(`remote_source profile missing buildManagedSourceSpec(): ${sourcePath}`);
|
|
89
|
-
}
|
|
90
|
-
if (typeof profile.mapRawEvent !== "function") {
|
|
91
|
-
throw new Error(`remote_source profile missing mapRawEvent(): ${sourcePath}`);
|
|
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
|
-
}
|
|
99
|
-
function validateOptionalHook(value, name, sourcePath) {
|
|
100
|
-
if (value !== undefined && typeof value !== "function") {
|
|
101
|
-
throw new Error(`remote_source profile ${name} must be a function when provided: ${sourcePath}`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
function resolveAndValidateProfilePath(profileRoot, profilePath) {
|
|
105
|
-
const resolved = node_path_1.default.isAbsolute(profilePath)
|
|
106
|
-
? node_path_1.default.resolve(profilePath)
|
|
107
|
-
: node_path_1.default.resolve(profileRoot, profilePath);
|
|
108
|
-
const normalizedRoot = ensureTrailingSep(node_path_1.default.resolve(profileRoot));
|
|
109
|
-
const normalizedResolved = node_path_1.default.resolve(resolved);
|
|
110
|
-
if (!normalizedResolved.startsWith(normalizedRoot) && normalizedResolved !== node_path_1.default.resolve(profileRoot)) {
|
|
111
|
-
throw new Error(`remote_source profilePath must stay under ${profileRoot}`);
|
|
112
|
-
}
|
|
113
|
-
return normalizedResolved;
|
|
114
|
-
}
|
|
115
|
-
function ensureTrailingSep(input) {
|
|
116
|
-
return input.endsWith(node_path_1.default.sep) ? input : `${input}${node_path_1.default.sep}`;
|
|
117
|
-
}
|
|
118
|
-
function asRecord(value) {
|
|
119
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
120
|
-
return {};
|
|
121
|
-
}
|
|
122
|
-
return value;
|
|
123
|
-
}
|
|
124
|
-
function asNonEmptyString(value) {
|
|
125
|
-
if (typeof value !== "string") {
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
const trimmed = value.trim();
|
|
129
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
130
|
-
}
|
|
131
|
-
const GITHUB_REPO_PROFILE = {
|
|
132
|
-
id: "builtin.github_repo",
|
|
133
|
-
describeCapabilities(source) {
|
|
134
|
-
const config = (0, github_1.parseGithubSourceConfig)(source);
|
|
135
|
-
return {
|
|
136
|
-
sourceKind: "github_repo",
|
|
137
|
-
aliases: ["github_repo"],
|
|
138
|
-
configSchema: [
|
|
139
|
-
{ name: "owner", type: "string", required: true, description: "GitHub repository owner." },
|
|
140
|
-
{ name: "repo", type: "string", required: true, description: "GitHub repository name." },
|
|
141
|
-
{ name: "uxcAuth", type: "string", required: false, description: "Optional uxc auth profile." },
|
|
142
|
-
{ name: "eventTypes", type: "string[]", required: false, description: "Optional GitHub event type allowlist." },
|
|
143
|
-
{ name: "perPage", type: "number", required: false, description: `Repository events requested per poll. Default ${config.perPage ?? 10}.` },
|
|
144
|
-
{ name: "pollIntervalSecs", type: "number", required: false, description: `Polling interval in seconds. Default ${config.pollIntervalSecs ?? 30}.` },
|
|
145
|
-
],
|
|
146
|
-
metadataFields: [
|
|
147
|
-
{ name: "eventType", type: "string", description: "GitHub event type such as IssueCommentEvent." },
|
|
148
|
-
{ name: "action", type: "string", description: "GitHub event action suffix such as created." },
|
|
149
|
-
{ name: "author", type: "string|null", description: "Actor login for the event." },
|
|
150
|
-
{ name: "isPullRequest", type: "boolean", description: "Whether the event targets a pull request surface." },
|
|
151
|
-
{ name: "reviewState", type: "string|null", description: "Review decision state for PullRequestReviewEvent such as approved or changes_requested." },
|
|
152
|
-
{ name: "labels", type: "string[]", description: "Labels extracted from the issue or pull request." },
|
|
153
|
-
{ name: "mentions", type: "string[]", description: "Mention handles extracted from title/body/comment text." },
|
|
154
|
-
{ name: "number", type: "number|null", description: "Issue or pull request number when present." },
|
|
155
|
-
{ name: "repoFullName", type: "string", description: "Repository full name in owner/repo form." },
|
|
156
|
-
{ name: "title", type: "string|null", description: "Issue, pull request, or comment title." },
|
|
157
|
-
{ name: "body", type: "string|null", description: "Issue, pull request, or comment body text." },
|
|
158
|
-
{ name: "url", type: "string|null", description: "Primary GitHub HTML URL for the event target." },
|
|
159
|
-
],
|
|
160
|
-
payloadExamples: [
|
|
161
|
-
{
|
|
162
|
-
id: "1234567891",
|
|
163
|
-
type: "PullRequestReviewEvent",
|
|
164
|
-
action: "created",
|
|
165
|
-
actor: "Copilot",
|
|
166
|
-
pull_request: { number: 67, title: "feat: add remote module capability hooks" },
|
|
167
|
-
review: { state: "commented", body: "review summary" },
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
id: "1234567892",
|
|
171
|
-
type: "PullRequestEvent",
|
|
172
|
-
action: "closed",
|
|
173
|
-
actor: "jolestar",
|
|
174
|
-
pull_request: { number: 72, title: "feat: add cleanup policy lifecycle engine", merged: true },
|
|
175
|
-
},
|
|
176
|
-
],
|
|
177
|
-
eventVariantExamples: [
|
|
178
|
-
"IssueCommentEvent.created",
|
|
179
|
-
"PullRequestEvent.opened",
|
|
180
|
-
"PullRequestEvent.closed",
|
|
181
|
-
"PullRequestReviewEvent.created",
|
|
182
|
-
"PullRequestReviewCommentEvent.created",
|
|
183
|
-
],
|
|
184
|
-
};
|
|
185
|
-
},
|
|
186
|
-
listSubscriptionShortcuts() {
|
|
187
|
-
return (0, github_1.githubSubscriptionShortcutSpec)();
|
|
188
|
-
},
|
|
189
|
-
expandSubscriptionShortcut(input) {
|
|
190
|
-
return (0, github_1.expandGithubSubscriptionShortcut)(input);
|
|
191
|
-
},
|
|
192
|
-
deriveTrackedResource(filter) {
|
|
193
|
-
return (0, github_1.deriveGithubTrackedResource)(filter);
|
|
194
|
-
},
|
|
195
|
-
projectLifecycleSignal(rawPayload) {
|
|
196
|
-
return (0, github_1.projectGithubLifecycleSignal)(rawPayload);
|
|
197
|
-
},
|
|
198
|
-
validateConfig(source) {
|
|
199
|
-
(0, github_1.parseGithubSourceConfig)(source);
|
|
200
|
-
},
|
|
201
|
-
buildManagedSourceSpec(source) {
|
|
202
|
-
const config = (0, github_1.parseGithubSourceConfig)(source);
|
|
203
|
-
return {
|
|
204
|
-
endpoint: github_1.GITHUB_ENDPOINT,
|
|
205
|
-
operation_id: "get:/repos/{owner}/{repo}/events",
|
|
206
|
-
args: {
|
|
207
|
-
owner: config.owner,
|
|
208
|
-
repo: config.repo,
|
|
209
|
-
per_page: config.perPage ?? 10,
|
|
210
|
-
},
|
|
211
|
-
mode: "poll",
|
|
212
|
-
poll_config: {
|
|
213
|
-
interval_secs: config.pollIntervalSecs ?? 30,
|
|
214
|
-
extract_items_pointer: "",
|
|
215
|
-
checkpoint_strategy: {
|
|
216
|
-
type: "item_key",
|
|
217
|
-
item_key_pointer: "/id",
|
|
218
|
-
seen_window: 1024,
|
|
219
|
-
},
|
|
220
|
-
},
|
|
221
|
-
options: { auth: config.uxcAuth },
|
|
222
|
-
};
|
|
223
|
-
},
|
|
224
|
-
mapRawEvent(rawPayload, source) {
|
|
225
|
-
const config = (0, github_1.parseGithubSourceConfig)(source);
|
|
226
|
-
const normalized = (0, github_1.normalizeGithubRepoEvent)(source, config, rawPayload);
|
|
227
|
-
if (!normalized) {
|
|
228
|
-
return null;
|
|
229
|
-
}
|
|
230
|
-
return {
|
|
231
|
-
sourceNativeId: normalized.sourceNativeId,
|
|
232
|
-
eventVariant: normalized.eventVariant,
|
|
233
|
-
metadata: normalized.metadata ?? {},
|
|
234
|
-
rawPayload: normalized.rawPayload ?? rawPayload,
|
|
235
|
-
occurredAt: normalized.occurredAt,
|
|
236
|
-
deliveryHandle: normalized.deliveryHandle,
|
|
237
|
-
};
|
|
238
|
-
},
|
|
239
|
-
};
|
|
240
|
-
const GITHUB_REPO_CI_PROFILE = {
|
|
241
|
-
id: "builtin.github_repo_ci",
|
|
242
|
-
describeCapabilities() {
|
|
243
|
-
return {
|
|
244
|
-
sourceKind: "github_repo_ci",
|
|
245
|
-
aliases: ["github_repo_ci"],
|
|
246
|
-
configSchema: [
|
|
247
|
-
{ name: "owner", type: "string", required: true, description: "GitHub repository owner." },
|
|
248
|
-
{ name: "repo", type: "string", required: true, description: "GitHub repository name." },
|
|
249
|
-
{ name: "uxcAuth", type: "string", required: false, description: "Optional uxc auth profile." },
|
|
250
|
-
{ name: "pollIntervalSecs", type: "number", required: false, description: "Polling interval in seconds." },
|
|
251
|
-
{ name: "perPage", type: "number", required: false, description: "Workflow runs requested per poll." },
|
|
252
|
-
{ name: "eventFilter", type: "string", required: false, description: "Optional GitHub workflow event filter." },
|
|
253
|
-
{ name: "branch", type: "string", required: false, description: "Optional branch filter for workflow runs." },
|
|
254
|
-
{ name: "statusFilter", type: "string", required: false, description: "Optional workflow status filter." },
|
|
255
|
-
],
|
|
256
|
-
metadataFields: [
|
|
257
|
-
{ name: "name", type: "string|null", description: "Workflow run name." },
|
|
258
|
-
{ name: "status", type: "string", description: "Normalized workflow run status." },
|
|
259
|
-
{ name: "conclusion", type: "string|null", description: "Workflow run conclusion when completed." },
|
|
260
|
-
{ name: "event", type: "string|null", description: "GitHub trigger event for the workflow run." },
|
|
261
|
-
{ name: "headBranch", type: "string|null", description: "Head branch for the workflow run." },
|
|
262
|
-
{ name: "headSha", type: "string|null", description: "Head commit SHA for the workflow run." },
|
|
263
|
-
{ name: "actor", type: "string|null", description: "Actor login for the workflow run." },
|
|
264
|
-
{ name: "commitMessage", type: "string|null", description: "Head commit message when present." },
|
|
265
|
-
{ name: "htmlUrl", type: "string|null", description: "GitHub Actions run URL." },
|
|
266
|
-
],
|
|
267
|
-
eventVariantExamples: ["workflow_run.ci.completed.failure", "workflow_run.nightly_checks.observed"],
|
|
268
|
-
};
|
|
269
|
-
},
|
|
270
|
-
validateConfig(source) {
|
|
271
|
-
(0, github_ci_1.parseGithubCiSourceConfig)(source);
|
|
272
|
-
},
|
|
273
|
-
buildManagedSourceSpec(source) {
|
|
274
|
-
const config = (0, github_ci_1.parseGithubCiSourceConfig)(source);
|
|
275
|
-
const args = {
|
|
276
|
-
owner: config.owner,
|
|
277
|
-
repo: config.repo,
|
|
278
|
-
per_page: config.perPage ?? github_ci_1.DEFAULT_GITHUB_CI_PER_PAGE,
|
|
279
|
-
};
|
|
280
|
-
if (config.eventFilter) {
|
|
281
|
-
args.event = config.eventFilter;
|
|
282
|
-
}
|
|
283
|
-
if (config.branch) {
|
|
284
|
-
args.branch = config.branch;
|
|
285
|
-
}
|
|
286
|
-
if (config.statusFilter) {
|
|
287
|
-
args.status = config.statusFilter;
|
|
288
|
-
}
|
|
289
|
-
return {
|
|
290
|
-
endpoint: github_ci_1.GITHUB_CI_ENDPOINT,
|
|
291
|
-
operation_id: "get:/repos/{owner}/{repo}/actions/runs",
|
|
292
|
-
args,
|
|
293
|
-
mode: "poll",
|
|
294
|
-
poll_config: {
|
|
295
|
-
interval_secs: config.pollIntervalSecs ?? github_ci_1.DEFAULT_GITHUB_CI_POLL_INTERVAL_SECS,
|
|
296
|
-
extract_items_pointer: "/workflow_runs",
|
|
297
|
-
checkpoint_strategy: {
|
|
298
|
-
type: "content_hash",
|
|
299
|
-
seen_window: 1024,
|
|
300
|
-
},
|
|
301
|
-
},
|
|
302
|
-
options: { auth: config.uxcAuth },
|
|
303
|
-
};
|
|
304
|
-
},
|
|
305
|
-
mapRawEvent(rawPayload, source) {
|
|
306
|
-
const config = (0, github_ci_1.parseGithubCiSourceConfig)(source);
|
|
307
|
-
const normalized = (0, github_ci_1.normalizeGithubWorkflowRunEvent)(source, config, rawPayload);
|
|
308
|
-
if (!normalized) {
|
|
309
|
-
return null;
|
|
310
|
-
}
|
|
311
|
-
return {
|
|
312
|
-
sourceNativeId: normalized.sourceNativeId,
|
|
313
|
-
eventVariant: normalized.eventVariant,
|
|
314
|
-
metadata: normalized.metadata ?? {},
|
|
315
|
-
rawPayload: normalized.rawPayload ?? rawPayload,
|
|
316
|
-
occurredAt: normalized.occurredAt,
|
|
317
|
-
deliveryHandle: normalized.deliveryHandle,
|
|
318
|
-
};
|
|
319
|
-
},
|
|
320
|
-
};
|
|
321
|
-
const FEISHU_BOT_PROFILE = {
|
|
322
|
-
id: "builtin.feishu_bot",
|
|
323
|
-
describeCapabilities() {
|
|
324
|
-
return {
|
|
325
|
-
sourceKind: "feishu_bot",
|
|
326
|
-
aliases: ["feishu_bot"],
|
|
327
|
-
configSchema: [
|
|
328
|
-
{ name: "appId", type: "string", required: true, description: "Feishu app ID." },
|
|
329
|
-
{ name: "appSecret", type: "string", required: true, description: "Feishu app secret." },
|
|
330
|
-
{ name: "eventTypes", type: "string[]", required: false, description: "Optional Feishu event type allowlist." },
|
|
331
|
-
{ name: "chatIds", type: "string[]", required: false, description: "Optional Feishu chat allowlist." },
|
|
332
|
-
{ name: "schemaUrl", type: "string", required: false, description: "Optional Feishu OpenAPI schema URL." },
|
|
333
|
-
{ name: "replyInThread", type: "boolean", required: false, description: "Reply in thread when sending outbound messages." },
|
|
334
|
-
{ name: "uxcAuth", type: "string", required: false, description: "Optional uxc auth profile." },
|
|
335
|
-
],
|
|
336
|
-
metadataFields: [
|
|
337
|
-
{ name: "eventType", type: "string", description: "Feishu event type." },
|
|
338
|
-
{ name: "chatId", type: "string", description: "Target chat ID." },
|
|
339
|
-
{ name: "chatType", type: "string|null", description: "Feishu chat type." },
|
|
340
|
-
{ name: "messageId", type: "string", description: "Feishu message ID." },
|
|
341
|
-
{ name: "messageType", type: "string", description: "Feishu message type such as text." },
|
|
342
|
-
{ name: "senderOpenId", type: "string|null", description: "Sender open_id when present." },
|
|
343
|
-
{ name: "senderType", type: "string|null", description: "Sender type." },
|
|
344
|
-
{ name: "mentions", type: "string[]", description: "Mention names extracted from the message." },
|
|
345
|
-
{ name: "mentionOpenIds", type: "string[]", description: "Mention open_ids extracted from the message." },
|
|
346
|
-
{ name: "content", type: "string|null", description: "Normalized message content string." },
|
|
347
|
-
{ name: "threadId", type: "string|null", description: "Thread or root message ID when present." },
|
|
348
|
-
{ name: "parentId", type: "string|null", description: "Parent message ID when present." },
|
|
349
|
-
],
|
|
350
|
-
eventVariantExamples: ["im.message.receive_v1.text"],
|
|
351
|
-
};
|
|
352
|
-
},
|
|
353
|
-
validateConfig(source) {
|
|
354
|
-
(0, feishu_1.parseFeishuSourceConfig)(source);
|
|
355
|
-
},
|
|
356
|
-
buildManagedSourceSpec(source) {
|
|
357
|
-
const config = (0, feishu_1.parseFeishuSourceConfig)(source);
|
|
358
|
-
return {
|
|
359
|
-
endpoint: config.endpoint ?? feishu_1.FEISHU_OPENAPI_ENDPOINT,
|
|
360
|
-
mode: "stream",
|
|
361
|
-
transport_hint: "feishu_long_connection",
|
|
362
|
-
options: { auth: config.uxcAuth },
|
|
363
|
-
};
|
|
364
|
-
},
|
|
365
|
-
mapRawEvent(rawPayload, source) {
|
|
366
|
-
const config = (0, feishu_1.parseFeishuSourceConfig)(source);
|
|
367
|
-
const normalized = (0, feishu_1.normalizeFeishuBotEvent)(source, config, rawPayload);
|
|
368
|
-
if (!normalized) {
|
|
369
|
-
return null;
|
|
370
|
-
}
|
|
371
|
-
return {
|
|
372
|
-
sourceNativeId: normalized.sourceNativeId,
|
|
373
|
-
eventVariant: normalized.eventVariant,
|
|
374
|
-
metadata: normalized.metadata ?? {},
|
|
375
|
-
rawPayload: normalized.rawPayload ?? rawPayload,
|
|
376
|
-
occurredAt: normalized.occurredAt,
|
|
377
|
-
deliveryHandle: normalized.deliveryHandle,
|
|
378
|
-
};
|
|
379
|
-
},
|
|
380
|
-
};
|
|
381
|
-
function profileConfigForSource(source) {
|
|
382
|
-
const config = asRecord(source.config);
|
|
383
|
-
if (source.sourceType === "remote_source") {
|
|
384
|
-
return asRecord(config.profileConfig);
|
|
385
|
-
}
|
|
386
|
-
return config;
|
|
387
|
-
}
|
|
12
|
+
exports.builtInProfileIdForSourceType = remote_modules_2.builtInModuleIdForSourceType;
|
|
13
|
+
exports.profileConfigForSource = remote_modules_2.moduleConfigForSource;
|
package/dist/src/store.js
CHANGED
|
@@ -780,6 +780,12 @@ class AgentInboxStore {
|
|
|
780
780
|
]);
|
|
781
781
|
const inserted = this.changes() > before;
|
|
782
782
|
if (inserted) {
|
|
783
|
+
const rowId = this.lastInsertRowId();
|
|
784
|
+
this.db.run(`
|
|
785
|
+
update inbox_items
|
|
786
|
+
set inbox_sequence = ?
|
|
787
|
+
where rowid = ? and inbox_sequence is null
|
|
788
|
+
`, [rowId, rowId]);
|
|
783
789
|
this.persist();
|
|
784
790
|
}
|
|
785
791
|
return inserted;
|
|
@@ -792,14 +798,14 @@ class AgentInboxStore {
|
|
|
792
798
|
filters.push("acked_at is null");
|
|
793
799
|
}
|
|
794
800
|
if (options?.afterItemId) {
|
|
795
|
-
const anchor = this.getOne("select
|
|
801
|
+
const anchor = this.getOne("select coalesce(inbox_sequence, rowid) as inbox_sequence from inbox_items where inbox_id = ? and item_id = ?", [inboxId, options.afterItemId]);
|
|
796
802
|
if (!anchor) {
|
|
797
803
|
throw new Error(`unknown inbox item: ${options.afterItemId}`);
|
|
798
804
|
}
|
|
799
|
-
filters.push("(
|
|
800
|
-
params.push(
|
|
805
|
+
filters.push("coalesce(inbox_sequence, rowid) > ?");
|
|
806
|
+
params.push(Number(anchor.inbox_sequence));
|
|
801
807
|
}
|
|
802
|
-
const rows = this.getAll(`select * from inbox_items where ${filters.join(" and ")} order by
|
|
808
|
+
const rows = this.getAll(`select * from inbox_items where ${filters.join(" and ")} order by coalesce(inbox_sequence, rowid) asc`, params);
|
|
803
809
|
return rows.map((row) => this.mapInboxItem(row));
|
|
804
810
|
}
|
|
805
811
|
ackItems(inboxId, itemIds, ackedAt) {
|
|
@@ -818,7 +824,7 @@ class AgentInboxStore {
|
|
|
818
824
|
return changes;
|
|
819
825
|
}
|
|
820
826
|
ackItemsThrough(inboxId, itemId, ackedAt) {
|
|
821
|
-
const anchor = this.getOne("select
|
|
827
|
+
const anchor = this.getOne("select coalesce(inbox_sequence, rowid) as inbox_sequence from inbox_items where inbox_id = ? and item_id = ?", [inboxId, itemId]);
|
|
822
828
|
if (!anchor) {
|
|
823
829
|
throw new Error(`unknown inbox item: ${itemId}`);
|
|
824
830
|
}
|
|
@@ -827,8 +833,8 @@ class AgentInboxStore {
|
|
|
827
833
|
set acked_at = ?
|
|
828
834
|
where inbox_id = ?
|
|
829
835
|
and acked_at is null
|
|
830
|
-
and (
|
|
831
|
-
`, [ackedAt, inboxId,
|
|
836
|
+
and coalesce(inbox_sequence, rowid) <= ?
|
|
837
|
+
`, [ackedAt, inboxId, Number(anchor.inbox_sequence)]);
|
|
832
838
|
const changes = this.changes();
|
|
833
839
|
if (changes > 0) {
|
|
834
840
|
this.persist();
|
package/dist/src/terminal.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.TerminalDispatcher = void 0;
|
|
|
7
7
|
exports.detectTerminalContext = detectTerminalContext;
|
|
8
8
|
exports.renderAgentPrompt = renderAgentPrompt;
|
|
9
9
|
exports.deriveInlineItemPreview = deriveInlineItemPreview;
|
|
10
|
+
exports.normalizeInlinePreviewText = normalizeInlinePreviewText;
|
|
10
11
|
exports.assignedAgentIdFromContext = assignedAgentIdFromContext;
|
|
11
12
|
const node_child_process_1 = require("node:child_process");
|
|
12
13
|
const node_util_1 = require("node:util");
|
|
@@ -78,13 +79,16 @@ function deriveInlineItemPreview(item, summary) {
|
|
|
78
79
|
candidates.push(summary);
|
|
79
80
|
}
|
|
80
81
|
for (const candidate of candidates) {
|
|
81
|
-
const preview =
|
|
82
|
+
const preview = normalizeInlinePreviewText(candidate);
|
|
82
83
|
if (preview) {
|
|
83
84
|
return preview;
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
87
|
return null;
|
|
87
88
|
}
|
|
89
|
+
function normalizeInlinePreviewText(candidate) {
|
|
90
|
+
return normalizeInlinePreview(candidate);
|
|
91
|
+
}
|
|
88
92
|
function assignedAgentIdFromContext(input) {
|
|
89
93
|
const primary = normalizeOptionalString(input.runtimeSessionId)
|
|
90
94
|
?? normalizeOptionalString(input.tmuxPaneId)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
alter table inbox_items add column inbox_sequence integer;
|
|
2
|
+
|
|
3
|
+
update inbox_items
|
|
4
|
+
set inbox_sequence = rowid
|
|
5
|
+
where inbox_sequence is null;
|
|
6
|
+
|
|
7
|
+
create unique index if not exists idx_inbox_items_inbox_sequence
|
|
8
|
+
on inbox_items(inbox_sequence);
|
|
9
|
+
|
|
10
|
+
create index if not exists idx_inbox_items_inbox_sequence_order
|
|
11
|
+
on inbox_items(inbox_id, inbox_sequence);
|
|
12
|
+
|
|
13
|
+
create trigger if not exists trg_inbox_items_set_sequence_after_insert
|
|
14
|
+
after insert on inbox_items
|
|
15
|
+
for each row
|
|
16
|
+
when new.inbox_sequence is null
|
|
17
|
+
begin
|
|
18
|
+
update inbox_items
|
|
19
|
+
set inbox_sequence = new.rowid
|
|
20
|
+
where rowid = new.rowid;
|
|
21
|
+
end;
|
package/drizzle/schema.ts
CHANGED
|
@@ -139,6 +139,7 @@ export const inboxItems = sqliteTable("inbox_items", {
|
|
|
139
139
|
sourceNativeId: text("source_native_id").notNull(),
|
|
140
140
|
eventVariant: text("event_variant").notNull(),
|
|
141
141
|
inboxId: text("inbox_id").notNull(),
|
|
142
|
+
inboxSequence: integer("inbox_sequence"),
|
|
142
143
|
occurredAt: text("occurred_at").notNull(),
|
|
143
144
|
metadataJson: text("metadata_json").notNull(),
|
|
144
145
|
rawPayloadJson: text("raw_payload_json").notNull(),
|
|
@@ -147,8 +148,10 @@ export const inboxItems = sqliteTable("inbox_items", {
|
|
|
147
148
|
}, (table) => ({
|
|
148
149
|
sourceNativeEventInboxUnique: uniqueIndex("idx_inbox_items_source_native_event_inbox")
|
|
149
150
|
.on(table.sourceId, table.sourceNativeId, table.eventVariant, table.inboxId),
|
|
151
|
+
inboxSequenceUnique: uniqueIndex("idx_inbox_items_inbox_sequence").on(table.inboxSequence),
|
|
150
152
|
ackedAtIdx: index("idx_inbox_items_acked_at").on(table.ackedAt),
|
|
151
153
|
inboxAckedAtIdx: index("idx_inbox_items_inbox_acked_at").on(table.inboxId, table.ackedAt),
|
|
154
|
+
inboxSequenceIdx: index("idx_inbox_items_inbox_sequence_order").on(table.inboxId, table.inboxSequence),
|
|
152
155
|
sourceOccurredAtIdx: index("idx_inbox_items_source_occurred_at").on(table.sourceId, table.occurredAt),
|
|
153
156
|
}));
|
|
154
157
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holon-run/agentinbox",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Local event subscription and delivery service for agents.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
21
|
"dist/src/",
|
|
22
|
+
"scripts/",
|
|
22
23
|
"drizzle/",
|
|
23
24
|
"README.md",
|
|
24
25
|
"LICENSE"
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
iTerm2 Python API cursor probe for AgentInbox.
|
|
4
|
+
Returns session state including cursor position and buffer contents.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import json
|
|
9
|
+
import asyncio
|
|
10
|
+
|
|
11
|
+
# Keep this value in sync with the TypeScript probe normalization constant
|
|
12
|
+
# `NORMALIZED_BUFFER_TAIL_LINES`, since both are intended to mirror the same
|
|
13
|
+
# CLI/probe tail window.
|
|
14
|
+
CAPTURE_TAIL_LINES = 20
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def probe_session(connection, session_id):
|
|
18
|
+
"""Probe iTerm2 session state and return cursor position with buffer contents."""
|
|
19
|
+
try:
|
|
20
|
+
import iterm2
|
|
21
|
+
|
|
22
|
+
async def get_session_state():
|
|
23
|
+
try:
|
|
24
|
+
app = await iterm2.async_get_app(connection)
|
|
25
|
+
session = app.get_session_by_id(session_id)
|
|
26
|
+
|
|
27
|
+
if not session:
|
|
28
|
+
return {"status": "gone"}
|
|
29
|
+
|
|
30
|
+
screen = await session.async_get_screen_contents()
|
|
31
|
+
cursor = screen.cursor_coord
|
|
32
|
+
|
|
33
|
+
# Capture a tail comparable to the CLI probe window.
|
|
34
|
+
lines = []
|
|
35
|
+
start_line = max(0, screen.number_of_lines - CAPTURE_TAIL_LINES)
|
|
36
|
+
for i in range(start_line, screen.number_of_lines):
|
|
37
|
+
try:
|
|
38
|
+
line = screen.line(i)
|
|
39
|
+
line_str = line.string if hasattr(line, 'string') else str(line)
|
|
40
|
+
lines.append(line_str)
|
|
41
|
+
except Exception:
|
|
42
|
+
break
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
"status": "available",
|
|
46
|
+
"cursor": {"x": cursor.x, "y": cursor.y},
|
|
47
|
+
"screen_height": screen.number_of_lines,
|
|
48
|
+
"start_line": start_line,
|
|
49
|
+
"lines": lines
|
|
50
|
+
}
|
|
51
|
+
except Exception as e:
|
|
52
|
+
return {"status": "error", "error": str(e)}
|
|
53
|
+
|
|
54
|
+
return get_session_state()
|
|
55
|
+
|
|
56
|
+
except ImportError:
|
|
57
|
+
return {"status": "error", "error": "iterm2 module not installed"}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def main():
|
|
61
|
+
if len(sys.argv) < 2:
|
|
62
|
+
result = {"status": "error", "error": "missing session_id argument"}
|
|
63
|
+
print(json.dumps(result))
|
|
64
|
+
sys.exit(1)
|
|
65
|
+
|
|
66
|
+
session_id = sys.argv[1]
|
|
67
|
+
|
|
68
|
+
# Try to import iterm2 and run the probe
|
|
69
|
+
try:
|
|
70
|
+
import iterm2
|
|
71
|
+
result = iterm2.run_until_complete(probe_session, session_id)
|
|
72
|
+
print(json.dumps(result))
|
|
73
|
+
|
|
74
|
+
# Exit with error code if status is not "available" or "gone"
|
|
75
|
+
if result.get("status") not in ["available", "gone"]:
|
|
76
|
+
sys.exit(1)
|
|
77
|
+
else:
|
|
78
|
+
sys.exit(0)
|
|
79
|
+
|
|
80
|
+
except ImportError:
|
|
81
|
+
result = {"status": "error", "error": "iterm2 module not installed"}
|
|
82
|
+
print(json.dumps(result))
|
|
83
|
+
sys.exit(1)
|
|
84
|
+
except Exception as e:
|
|
85
|
+
result = {"status": "error", "error": str(e)}
|
|
86
|
+
print(json.dumps(result))
|
|
87
|
+
sys.exit(1)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
main()
|