@holon-run/agentinbox 0.7.0 → 1.0.0-beta.1
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 +36 -7
- package/dist/src/adapters.js +55 -0
- package/dist/src/backend.js +4 -16
- package/dist/src/cli.js +329 -52
- package/dist/src/client.js +2 -2
- package/dist/src/http.js +479 -55
- package/dist/src/runtime_gate.js +49 -41
- package/dist/src/service.js +922 -200
- package/dist/src/source_hosts.js +75 -0
- package/dist/src/source_resolution.js +2 -2
- package/dist/src/source_schema.js +2 -2
- package/dist/src/sources/feishu.js +66 -28
- package/dist/src/sources/github.js +222 -36
- package/dist/src/sources/remote.js +50 -3
- package/dist/src/sources/remote_modules.js +100 -15
- package/dist/src/store.js +875 -110
- package/dist/src/terminal.js +33 -7
- package/dist/src/util.js +46 -1
- package/drizzle/migrations/{0000_initial.sql → 0000_v1_initial.sql} +182 -6
- package/drizzle/migrations/0002_host_scoped_lifecycle_retirements.sql +42 -0
- package/drizzle/migrations/0003_subscription_tracked_resource_indexes.sql +5 -0
- package/drizzle/schema.ts +98 -3
- package/package.json +2 -1
- package/scripts/release_smoke.sh +123 -0
- package/dist/src/sources/remote_profiles.js +0 -13
- package/drizzle/migrations/0001_inbox_items_source_occurred_at_idx.sql +0 -3
- package/drizzle/migrations/0002_subscription_cleanup_policy.sql +0 -45
- package/drizzle/migrations/0003_subscription_lifecycle_retirements.sql +0 -14
- package/drizzle/migrations/0004_source_idle_states.sql +0 -10
- package/drizzle/migrations/0005_timers.sql +0 -22
- package/drizzle/migrations/0006_terminal_runtime_pid.sql +0 -1
- package/drizzle/migrations/0007_activation_dispatch_last_notified_fingerprint.sql +0 -2
- package/drizzle/migrations/0008_inbox_item_sequence.sql +0 -21
package/dist/src/runtime_gate.js
CHANGED
|
@@ -12,33 +12,35 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
12
12
|
const node_os_1 = __importDefault(require("node:os"));
|
|
13
13
|
const logging_1 = require("./logging");
|
|
14
14
|
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
15
|
+
const DEFAULT_GATING_POLICY = {
|
|
16
|
+
iTerm2SampleDelayMs: 900,
|
|
17
|
+
tmuxSampleDelayMs: 900,
|
|
18
|
+
normalizedBufferTailLines: 20,
|
|
19
|
+
activeBufferMarkers: [
|
|
20
|
+
"esc to interrupt",
|
|
21
|
+
"Working (",
|
|
22
|
+
"⠼ ",
|
|
23
|
+
],
|
|
24
|
+
typingPromptPrefixes: [
|
|
25
|
+
"› ",
|
|
26
|
+
"> ",
|
|
27
|
+
],
|
|
28
|
+
};
|
|
27
29
|
class DefaultActivationGate {
|
|
28
30
|
runtimeProbes;
|
|
29
31
|
terminalProbes;
|
|
30
32
|
logger;
|
|
31
|
-
constructor(runtimeProbes, terminalProbes, logger = new logging_1.NoopLogger()) {
|
|
33
|
+
constructor(runtimeProbes, terminalProbes, logger = new logging_1.NoopLogger(), gatingPolicy = DEFAULT_GATING_POLICY) {
|
|
32
34
|
this.logger = logger.child("activation_gate");
|
|
33
35
|
this.runtimeProbes = runtimeProbes ?? [
|
|
34
36
|
new CodexRuntimePresenceProbe(undefined, undefined, this.logger.child("codex_runtime")),
|
|
35
37
|
new ClaudeCodeRuntimePresenceProbe(undefined, undefined, this.logger.child("claude_runtime")),
|
|
36
38
|
];
|
|
37
39
|
this.terminalProbes = terminalProbes ?? [
|
|
38
|
-
new CodexTerminalStateProbe(undefined, undefined, undefined, undefined, undefined, this.logger.child("codex_terminal")),
|
|
39
|
-
new Iterm2TerminalStateProbe(undefined, {}, this.logger.child("iterm2_terminal")),
|
|
40
|
+
new CodexTerminalStateProbe(undefined, undefined, undefined, undefined, undefined, this.logger.child("codex_terminal"), gatingPolicy),
|
|
41
|
+
new Iterm2TerminalStateProbe(undefined, {}, this.logger.child("iterm2_terminal"), gatingPolicy),
|
|
40
42
|
new ClaudeCodeTerminalStateProbe(undefined, undefined, undefined, this.logger.child("claude_terminal")),
|
|
41
|
-
new TmuxTerminalStateProbe(undefined, {}, this.logger.child("tmux_terminal")),
|
|
43
|
+
new TmuxTerminalStateProbe(undefined, {}, this.logger.child("tmux_terminal"), gatingPolicy),
|
|
42
44
|
];
|
|
43
45
|
}
|
|
44
46
|
async evaluate(target) {
|
|
@@ -178,13 +180,15 @@ class CodexTerminalStateProbe {
|
|
|
178
180
|
statReader;
|
|
179
181
|
nowFn;
|
|
180
182
|
logger;
|
|
181
|
-
|
|
183
|
+
gatingPolicy;
|
|
184
|
+
constructor(iTermProbe = new Iterm2TerminalStateProbe(undefined, {}, new logging_1.NoopLogger(), DEFAULT_GATING_POLICY), tmuxProbe = new TmuxTerminalStateProbe(undefined, {}, new logging_1.NoopLogger(), DEFAULT_GATING_POLICY), sessionFileReader, statReader, nowFn = () => Date.now(), logger = new logging_1.NoopLogger(), gatingPolicy = DEFAULT_GATING_POLICY) {
|
|
182
185
|
this.iTermProbe = iTermProbe;
|
|
183
186
|
this.tmuxProbe = tmuxProbe;
|
|
184
187
|
this.sessionFileReader = sessionFileReader;
|
|
185
188
|
this.statReader = statReader;
|
|
186
189
|
this.nowFn = nowFn;
|
|
187
190
|
this.logger = logger;
|
|
191
|
+
this.gatingPolicy = gatingPolicy;
|
|
188
192
|
}
|
|
189
193
|
supports(target) {
|
|
190
194
|
return target.runtimeKind === "codex";
|
|
@@ -269,10 +273,12 @@ class Iterm2TerminalStateProbe {
|
|
|
269
273
|
execAsync;
|
|
270
274
|
options;
|
|
271
275
|
logger;
|
|
272
|
-
|
|
276
|
+
gatingPolicy;
|
|
277
|
+
constructor(execAsync = execFileAsync, options = {}, logger = new logging_1.NoopLogger(), gatingPolicy = DEFAULT_GATING_POLICY) {
|
|
273
278
|
this.execAsync = execAsync;
|
|
274
279
|
this.options = options;
|
|
275
280
|
this.logger = logger;
|
|
281
|
+
this.gatingPolicy = gatingPolicy;
|
|
276
282
|
}
|
|
277
283
|
supports(target) {
|
|
278
284
|
// Only support iTerm2 targets that are NOT Claude Code
|
|
@@ -337,7 +343,7 @@ class Iterm2TerminalStateProbe {
|
|
|
337
343
|
if (data.status === "available") {
|
|
338
344
|
// If we have cursor and lines data, use cursor-aware detection
|
|
339
345
|
if (data.cursor && Array.isArray(data.lines)) {
|
|
340
|
-
const bufferTail = normalizeProbeTailFromLines(data.lines);
|
|
346
|
+
const bufferTail = normalizeProbeTailFromLines(data.lines, this.gatingPolicy.normalizedBufferTailLines);
|
|
341
347
|
if (!bufferTail) {
|
|
342
348
|
this.logger.trace("probe_python_result", {
|
|
343
349
|
sessionId,
|
|
@@ -363,13 +369,13 @@ class Iterm2TerminalStateProbe {
|
|
|
363
369
|
// If cursor-aware detection is certain it's not busy, don't apply prompt heuristics
|
|
364
370
|
if (busyStatus === "not_busy") {
|
|
365
371
|
// Still check for buffer changes and active markers
|
|
366
|
-
await (this.options.sleep ?? sleep)(this.options.sampleDelayMs ??
|
|
372
|
+
await (this.options.sleep ?? sleep)(this.options.sampleDelayMs ?? this.gatingPolicy.iTerm2SampleDelayMs);
|
|
367
373
|
const secondResult = await this.execAsync("python3", [scriptPath, sessionId], {
|
|
368
374
|
env: { ...process.env, PYTHONIOENCODING: "utf-8" }
|
|
369
375
|
});
|
|
370
376
|
const secondData = JSON.parse(secondResult.stdout);
|
|
371
377
|
if (secondData.status === "available" && Array.isArray(secondData.lines)) {
|
|
372
|
-
const secondBufferTail = normalizeProbeTailFromLines(secondData.lines);
|
|
378
|
+
const secondBufferTail = normalizeProbeTailFromLines(secondData.lines, this.gatingPolicy.normalizedBufferTailLines);
|
|
373
379
|
if (secondBufferTail && bufferTail !== secondBufferTail) {
|
|
374
380
|
this.logger.trace("probe_python_result", {
|
|
375
381
|
sessionId,
|
|
@@ -380,7 +386,7 @@ class Iterm2TerminalStateProbe {
|
|
|
380
386
|
}
|
|
381
387
|
}
|
|
382
388
|
// Check for active buffer markers
|
|
383
|
-
if (containsActiveBufferMarker(bufferTail)) {
|
|
389
|
+
if (containsActiveBufferMarker(bufferTail, this.gatingPolicy.activeBufferMarkers)) {
|
|
384
390
|
this.logger.trace("probe_python_result", {
|
|
385
391
|
sessionId,
|
|
386
392
|
result: { presence: "available", busy: "busy" },
|
|
@@ -398,13 +404,13 @@ class Iterm2TerminalStateProbe {
|
|
|
398
404
|
// If cursor-aware detection is uncertain, fall back to buffer change detection only
|
|
399
405
|
// Do NOT use generic typing prompts to avoid false positives (see #116)
|
|
400
406
|
if (busyStatus === "unknown") {
|
|
401
|
-
await (this.options.sleep ?? sleep)(this.options.sampleDelayMs ??
|
|
407
|
+
await (this.options.sleep ?? sleep)(this.options.sampleDelayMs ?? this.gatingPolicy.iTerm2SampleDelayMs);
|
|
402
408
|
const secondResult = await this.execAsync("python3", [scriptPath, sessionId], {
|
|
403
409
|
env: { ...process.env, PYTHONIOENCODING: "utf-8" }
|
|
404
410
|
});
|
|
405
411
|
const secondData = JSON.parse(secondResult.stdout);
|
|
406
412
|
if (secondData.status === "available" && Array.isArray(secondData.lines)) {
|
|
407
|
-
const secondBufferTail = normalizeProbeTailFromLines(secondData.lines);
|
|
413
|
+
const secondBufferTail = normalizeProbeTailFromLines(secondData.lines, this.gatingPolicy.normalizedBufferTailLines);
|
|
408
414
|
if (secondBufferTail && bufferTail !== secondBufferTail) {
|
|
409
415
|
this.logger.trace("probe_python_result", {
|
|
410
416
|
sessionId,
|
|
@@ -415,7 +421,7 @@ class Iterm2TerminalStateProbe {
|
|
|
415
421
|
}
|
|
416
422
|
}
|
|
417
423
|
// Check for active buffer markers
|
|
418
|
-
if (containsActiveBufferMarker(bufferTail)) {
|
|
424
|
+
if (containsActiveBufferMarker(bufferTail, this.gatingPolicy.activeBufferMarkers)) {
|
|
419
425
|
this.logger.trace("probe_python_result", {
|
|
420
426
|
sessionId,
|
|
421
427
|
result: { presence: "available", busy: "busy" },
|
|
@@ -493,7 +499,7 @@ class Iterm2TerminalStateProbe {
|
|
|
493
499
|
if (!first) {
|
|
494
500
|
return { presence: "available", busy: "unknown" };
|
|
495
501
|
}
|
|
496
|
-
await (this.options.sleep ?? sleep)(this.options.sampleDelayMs ??
|
|
502
|
+
await (this.options.sleep ?? sleep)(this.options.sampleDelayMs ?? this.gatingPolicy.iTerm2SampleDelayMs);
|
|
497
503
|
const second = await this.readBufferTail(it2api, sessionId);
|
|
498
504
|
if (!second) {
|
|
499
505
|
return { presence: "available", busy: "unknown" };
|
|
@@ -506,7 +512,7 @@ class Iterm2TerminalStateProbe {
|
|
|
506
512
|
});
|
|
507
513
|
return { presence: "available", busy: "busy" };
|
|
508
514
|
}
|
|
509
|
-
if (containsActiveBufferMarker(second)) {
|
|
515
|
+
if (containsActiveBufferMarker(second, this.gatingPolicy.activeBufferMarkers)) {
|
|
510
516
|
this.logger.trace("probe_cli_result", {
|
|
511
517
|
sessionId,
|
|
512
518
|
result: { presence: "available", busy: "busy" },
|
|
@@ -549,7 +555,7 @@ class Iterm2TerminalStateProbe {
|
|
|
549
555
|
async readBufferTail(it2api, sessionId) {
|
|
550
556
|
try {
|
|
551
557
|
const result = await this.execAsync(it2api, ["get-buffer", sessionId]);
|
|
552
|
-
return normalizeBufferTail(result.stdout);
|
|
558
|
+
return normalizeBufferTail(result.stdout, this.gatingPolicy.normalizedBufferTailLines);
|
|
553
559
|
}
|
|
554
560
|
catch {
|
|
555
561
|
return null;
|
|
@@ -561,10 +567,12 @@ class TmuxTerminalStateProbe {
|
|
|
561
567
|
execAsync;
|
|
562
568
|
options;
|
|
563
569
|
logger;
|
|
564
|
-
|
|
570
|
+
gatingPolicy;
|
|
571
|
+
constructor(execAsync = execFileAsync, options = {}, logger = new logging_1.NoopLogger(), gatingPolicy = DEFAULT_GATING_POLICY) {
|
|
565
572
|
this.execAsync = execAsync;
|
|
566
573
|
this.options = options;
|
|
567
574
|
this.logger = logger;
|
|
575
|
+
this.gatingPolicy = gatingPolicy;
|
|
568
576
|
}
|
|
569
577
|
supports(target) {
|
|
570
578
|
return target.backend === "tmux" && typeof target.tmuxPaneId === "string" && target.tmuxPaneId.trim().length > 0;
|
|
@@ -611,7 +619,7 @@ class TmuxTerminalStateProbe {
|
|
|
611
619
|
if (!first) {
|
|
612
620
|
return { presence: "available", busy: "unknown" };
|
|
613
621
|
}
|
|
614
|
-
await (this.options.sleep ?? sleep)(this.options.sampleDelayMs ??
|
|
622
|
+
await (this.options.sleep ?? sleep)(this.options.sampleDelayMs ?? this.gatingPolicy.tmuxSampleDelayMs);
|
|
615
623
|
const second = await this.readBufferTail(paneId);
|
|
616
624
|
if (!second) {
|
|
617
625
|
return { presence: "available", busy: "unknown" };
|
|
@@ -625,7 +633,7 @@ class TmuxTerminalStateProbe {
|
|
|
625
633
|
});
|
|
626
634
|
return { presence: "available", busy: "busy" };
|
|
627
635
|
}
|
|
628
|
-
if (containsActiveBufferMarker(second)) {
|
|
636
|
+
if (containsActiveBufferMarker(second, this.gatingPolicy.activeBufferMarkers)) {
|
|
629
637
|
this.logger.trace("probe_result", {
|
|
630
638
|
targetId: target.targetId,
|
|
631
639
|
paneId,
|
|
@@ -684,8 +692,8 @@ class TmuxTerminalStateProbe {
|
|
|
684
692
|
}
|
|
685
693
|
async readBufferTail(paneId) {
|
|
686
694
|
try {
|
|
687
|
-
const result = await this.execAsync("tmux", ["capture-pane", "-p", "-t", paneId, "-S", `-${
|
|
688
|
-
return normalizeBufferTail(result.stdout);
|
|
695
|
+
const result = await this.execAsync("tmux", ["capture-pane", "-p", "-t", paneId, "-S", `-${this.gatingPolicy.normalizedBufferTailLines}`]);
|
|
696
|
+
return normalizeBufferTail(result.stdout, this.gatingPolicy.normalizedBufferTailLines);
|
|
689
697
|
}
|
|
690
698
|
catch {
|
|
691
699
|
return null;
|
|
@@ -693,10 +701,10 @@ class TmuxTerminalStateProbe {
|
|
|
693
701
|
}
|
|
694
702
|
}
|
|
695
703
|
exports.TmuxTerminalStateProbe = TmuxTerminalStateProbe;
|
|
696
|
-
function containsActiveBufferMarker(bufferTail) {
|
|
697
|
-
return
|
|
704
|
+
function containsActiveBufferMarker(bufferTail, markers = DEFAULT_GATING_POLICY.activeBufferMarkers) {
|
|
705
|
+
return markers.some((marker) => bufferTail.includes(marker));
|
|
698
706
|
}
|
|
699
|
-
function hasVisibleTypingPrompt(bufferTail, prefixes =
|
|
707
|
+
function hasVisibleTypingPrompt(bufferTail, prefixes = DEFAULT_GATING_POLICY.typingPromptPrefixes) {
|
|
700
708
|
const lines = bufferTail
|
|
701
709
|
.split("\n")
|
|
702
710
|
.map((line) => line.trimEnd())
|
|
@@ -754,7 +762,7 @@ function typingPromptPrefixesForRuntime(runtimeKind) {
|
|
|
754
762
|
if (runtimeKind === "claude_code") {
|
|
755
763
|
return ["❯ ", "› "];
|
|
756
764
|
}
|
|
757
|
-
return
|
|
765
|
+
return DEFAULT_GATING_POLICY.typingPromptPrefixes;
|
|
758
766
|
}
|
|
759
767
|
function parseOptionalInt(value) {
|
|
760
768
|
if (!value) {
|
|
@@ -763,18 +771,18 @@ function parseOptionalInt(value) {
|
|
|
763
771
|
const parsed = Number.parseInt(value, 10);
|
|
764
772
|
return Number.isFinite(parsed) ? parsed : null;
|
|
765
773
|
}
|
|
766
|
-
function normalizeBufferTail(buffer) {
|
|
774
|
+
function normalizeBufferTail(buffer, tailLines = DEFAULT_GATING_POLICY.normalizedBufferTailLines) {
|
|
767
775
|
const normalized = buffer
|
|
768
776
|
.replace(/\r/g, "")
|
|
769
777
|
.split("\n")
|
|
770
778
|
.map((line) => line.trimEnd())
|
|
771
|
-
.slice(-
|
|
779
|
+
.slice(-tailLines)
|
|
772
780
|
.join("\n")
|
|
773
781
|
.trim();
|
|
774
782
|
return normalized.length > 0 ? normalized : null;
|
|
775
783
|
}
|
|
776
|
-
function normalizeProbeTailFromLines(lines) {
|
|
777
|
-
return normalizeBufferTail(lines.join("\n"));
|
|
784
|
+
function normalizeProbeTailFromLines(lines, tailLines = DEFAULT_GATING_POLICY.normalizedBufferTailLines) {
|
|
785
|
+
return normalizeBufferTail(lines.join("\n"), tailLines);
|
|
778
786
|
}
|
|
779
787
|
function resolveIterm2ApiPath(override) {
|
|
780
788
|
if (override) {
|