pi-harness-runtime 1.1.36 → 1.1.38
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/harness/langchain/daemon.ts +182 -20
- package/package.json +1 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/package.json +1 -1
|
@@ -27,13 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
import { randomUUID } from "node:crypto";
|
|
30
|
-
import {
|
|
31
|
-
existsSync,
|
|
32
|
-
mkdirSync,
|
|
33
|
-
readdirSync,
|
|
34
|
-
readFileSync,
|
|
35
|
-
writeFileSync,
|
|
36
|
-
} from "node:fs";
|
|
30
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
37
31
|
import { join } from "node:path";
|
|
38
32
|
|
|
39
33
|
import { LeaseManager } from "../../packages/autonomous-runtime/src/lease.js";
|
|
@@ -107,6 +101,16 @@ export interface DaemonConfig {
|
|
|
107
101
|
ntfyTopic?: string;
|
|
108
102
|
webhookUrl?: string;
|
|
109
103
|
};
|
|
104
|
+
/**
|
|
105
|
+
* Path to a tunnel-start script (e.g. tailscale-tunnel.sh).
|
|
106
|
+
* If set, the daemon health-checks the tunnel before each task and
|
|
107
|
+
* auto-restarts it if the target port is unreachable.
|
|
108
|
+
*
|
|
109
|
+
* Set to "auto" to search for scripts/tailscale-tunnel.sh in the
|
|
110
|
+
* project root, or give an absolute path.
|
|
111
|
+
* Default: none (no tunnel management)
|
|
112
|
+
*/
|
|
113
|
+
tunnelCommand?: string;
|
|
110
114
|
}
|
|
111
115
|
|
|
112
116
|
// ─── TriggeredTask ──────────────────────────────────────────────────────────
|
|
@@ -507,7 +511,140 @@ class BdTasksWatcher {
|
|
|
507
511
|
}
|
|
508
512
|
}
|
|
509
513
|
|
|
510
|
-
// ───
|
|
514
|
+
// ─── Tunnel Health Monitor ───────────────────────────────────────────────────
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Monitors the health of a SSH tunnel (e.g. tailscale-tunnel.sh) and
|
|
518
|
+
* auto-restarts it if the target port becomes unreachable.
|
|
519
|
+
*
|
|
520
|
+
* This prevents the daemon from freezing when the tunnel drops silently —
|
|
521
|
+
* a common cause of tasks that appear to hang forever.
|
|
522
|
+
*/
|
|
523
|
+
class TunnelMonitor {
|
|
524
|
+
private readonly scriptPath: string;
|
|
525
|
+
private readonly checkIntervalMs: number;
|
|
526
|
+
private readonly targetHost: string;
|
|
527
|
+
private readonly targetPort: number;
|
|
528
|
+
private running = false;
|
|
529
|
+
private timer?: ReturnType<typeof setTimeout>;
|
|
530
|
+
private consecutiveFailures = 0;
|
|
531
|
+
private readonly maxConsecutiveFailures = 2;
|
|
532
|
+
|
|
533
|
+
constructor(
|
|
534
|
+
scriptPath: string,
|
|
535
|
+
opts: {
|
|
536
|
+
checkIntervalMs?: number;
|
|
537
|
+
targetHost?: string;
|
|
538
|
+
targetPort?: number;
|
|
539
|
+
} = {},
|
|
540
|
+
) {
|
|
541
|
+
this.scriptPath = scriptPath;
|
|
542
|
+
this.checkIntervalMs = opts.checkIntervalMs ?? 30_000; // 30s default
|
|
543
|
+
this.targetHost = opts.targetHost ?? "127.0.0.1";
|
|
544
|
+
this.targetPort = opts.targetPort ?? 5433;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
start(): void {
|
|
548
|
+
this.running = true;
|
|
549
|
+
console.log(
|
|
550
|
+
`[tunnel-monitor] Starting — script=${this.scriptPath}, check every ${this.checkIntervalMs / 1000}s, target=${this.targetHost}:${this.targetPort}`,
|
|
551
|
+
);
|
|
552
|
+
this._check();
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
stop(): void {
|
|
556
|
+
this.running = false;
|
|
557
|
+
if (this.timer) {
|
|
558
|
+
clearTimeout(this.timer);
|
|
559
|
+
this.timer = undefined;
|
|
560
|
+
}
|
|
561
|
+
console.log("[tunnel-monitor] Stopped.");
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Run a health check and restart if the port is down.
|
|
566
|
+
* Returns true if the tunnel is healthy.
|
|
567
|
+
*/
|
|
568
|
+
async checkAndFix(): Promise<boolean> {
|
|
569
|
+
const healthy = await this._isPortOpen();
|
|
570
|
+
if (healthy) {
|
|
571
|
+
this.consecutiveFailures = 0;
|
|
572
|
+
return true;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
this.consecutiveFailures++;
|
|
576
|
+
console.warn(
|
|
577
|
+
`[tunnel-monitor] Port ${this.targetHost}:${this.targetPort} unreachable (failure #${this.consecutiveFailures})`,
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
if (this.consecutiveFailures >= this.maxConsecutiveFailures) {
|
|
581
|
+
console.warn(`[tunnel-monitor] Tunnel appears down — restarting...`);
|
|
582
|
+
await this._restartTunnel();
|
|
583
|
+
this.consecutiveFailures = 0;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
private async _check(): Promise<void> {
|
|
590
|
+
if (!this.running) return;
|
|
591
|
+
await this.checkAndFix();
|
|
592
|
+
this.timer = setTimeout(() => this._check(), this.checkIntervalMs);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
private async _isPortOpen(): Promise<boolean> {
|
|
596
|
+
const { createConnection } = await import("node:net");
|
|
597
|
+
return new Promise((resolve) => {
|
|
598
|
+
const socket = createConnection({
|
|
599
|
+
host: this.targetHost,
|
|
600
|
+
port: this.targetPort,
|
|
601
|
+
timeout: 2_000, // 2s timeout — just checking reachability
|
|
602
|
+
});
|
|
603
|
+
socket.on("connect", () => {
|
|
604
|
+
socket.destroy();
|
|
605
|
+
resolve(true);
|
|
606
|
+
});
|
|
607
|
+
socket.on("timeout", () => {
|
|
608
|
+
socket.destroy();
|
|
609
|
+
resolve(false);
|
|
610
|
+
});
|
|
611
|
+
socket.on("error", () => {
|
|
612
|
+
resolve(false);
|
|
613
|
+
});
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
private async _restartTunnel(): Promise<void> {
|
|
618
|
+
const { exec } = await import("node:child_process");
|
|
619
|
+
const { promisify } = await import("node:util");
|
|
620
|
+
const execAsync = promisify(exec);
|
|
621
|
+
|
|
622
|
+
try {
|
|
623
|
+
// Stop first
|
|
624
|
+
await execAsync(`bash "${this.scriptPath}" stop 2>/dev/null`, {
|
|
625
|
+
timeout: 10_000,
|
|
626
|
+
}).catch(() => {}); // ignore errors on stop
|
|
627
|
+
|
|
628
|
+
// Small delay before restart
|
|
629
|
+
await new Promise((r) => setTimeout(r, 1_000));
|
|
630
|
+
|
|
631
|
+
// Start
|
|
632
|
+
const { stdout, stderr } = await execAsync(
|
|
633
|
+
`bash "${this.scriptPath}" start 2>&1`,
|
|
634
|
+
{ timeout: 30_000 },
|
|
635
|
+
);
|
|
636
|
+
console.log(
|
|
637
|
+
`[tunnel-monitor] Restart output: ${stdout.trim() || stderr.trim() || "ok"}`,
|
|
638
|
+
);
|
|
639
|
+
} catch (err) {
|
|
640
|
+
console.error(
|
|
641
|
+
`[tunnel-monitor] Restart failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// ─── Loop Daemon ───────────────────────────────────────────────────────────
|
|
511
648
|
|
|
512
649
|
/**
|
|
513
650
|
* The main daemon class. Coordinates watchers, leasing, loop execution, and
|
|
@@ -530,6 +667,7 @@ export class LoopDaemon {
|
|
|
530
667
|
private readonly inboxWatcher: InboxWatcher;
|
|
531
668
|
private readonly busWatcher: BusWatcher;
|
|
532
669
|
private readonly bdWatcher: BdTasksWatcher;
|
|
670
|
+
private readonly tunnelMonitor: TunnelMonitor | null;
|
|
533
671
|
|
|
534
672
|
/** Currently running tasks (taskId → RunningTask) */
|
|
535
673
|
private readonly running = new Map<string, RunningTask>();
|
|
@@ -546,10 +684,13 @@ export class LoopDaemon {
|
|
|
546
684
|
approvalPolicy: config.approvalPolicy ?? "never",
|
|
547
685
|
dryRun: config.dryRun ?? false,
|
|
548
686
|
costCapUsd: config.costCapUsd ?? undefined,
|
|
687
|
+
surgePolicy: config.surgePolicy,
|
|
688
|
+
taskTimeoutMs: config.taskTimeoutMs,
|
|
689
|
+
checkpointer: config.checkpointer,
|
|
549
690
|
sources: config.sources ?? ["inbox", "bus"],
|
|
550
691
|
workspace: config.workspace ?? getHerdrWorkspace(),
|
|
551
692
|
notificationConfig: config.notificationConfig ?? undefined,
|
|
552
|
-
|
|
693
|
+
tunnelCommand: config.tunnelCommand,
|
|
553
694
|
} as Required<DaemonConfig>;
|
|
554
695
|
|
|
555
696
|
this.agentId = this.config.agentId;
|
|
@@ -580,6 +721,13 @@ export class LoopDaemon {
|
|
|
580
721
|
this.busWatcher = new BusWatcher(this.bus, this.config.pollMs);
|
|
581
722
|
this.bdWatcher = new BdTasksWatcher(this.config.pollMs);
|
|
582
723
|
|
|
724
|
+
// Tunnel monitor — only active when a tunnel script is configured
|
|
725
|
+
if (this.config.tunnelCommand) {
|
|
726
|
+
this.tunnelMonitor = new TunnelMonitor(this.config.tunnelCommand);
|
|
727
|
+
} else {
|
|
728
|
+
this.tunnelMonitor = null;
|
|
729
|
+
}
|
|
730
|
+
|
|
583
731
|
// Reap stale leases from previous runs
|
|
584
732
|
const reclaimed = this.leaseManager.recoverOnStartup();
|
|
585
733
|
if (reclaimed.length > 0) {
|
|
@@ -613,6 +761,8 @@ export class LoopDaemon {
|
|
|
613
761
|
this.bdWatcher.start((t) => this._onTriggered(t));
|
|
614
762
|
}
|
|
615
763
|
|
|
764
|
+
this.tunnelMonitor?.start();
|
|
765
|
+
|
|
616
766
|
console.log("[daemon] Started.");
|
|
617
767
|
}
|
|
618
768
|
|
|
@@ -624,6 +774,7 @@ export class LoopDaemon {
|
|
|
624
774
|
this.inboxWatcher.stop();
|
|
625
775
|
this.busWatcher.stop();
|
|
626
776
|
this.bdWatcher.stop();
|
|
777
|
+
this.tunnelMonitor?.stop();
|
|
627
778
|
|
|
628
779
|
// Release all held leases
|
|
629
780
|
for (const [taskId, _runningTask] of this.running) {
|
|
@@ -688,6 +839,14 @@ export class LoopDaemon {
|
|
|
688
839
|
const log = (msg: string) => console.log(`[${loopId}] ${msg}`);
|
|
689
840
|
|
|
690
841
|
try {
|
|
842
|
+
// ── Tunnel health check — self-heal before running a task ───────────────
|
|
843
|
+
if (this.tunnelMonitor) {
|
|
844
|
+
const tunnelOk = await this.tunnelMonitor.checkAndFix();
|
|
845
|
+
if (!tunnelOk) {
|
|
846
|
+
log("Tunnel health check failed — waiting for tunnel restart...");
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
691
850
|
// ── Gate check (I7) ──────────────────────────────────────────────
|
|
692
851
|
const policy = effectivePolicy(task, this.config);
|
|
693
852
|
if (policy === "always" || policy === "on_blocked") {
|
|
@@ -731,17 +890,20 @@ export class LoopDaemon {
|
|
|
731
890
|
log(
|
|
732
891
|
`Starting write-review loop (maxIterations=${this.config.maxIterations})`,
|
|
733
892
|
);
|
|
734
|
-
const checkpointer =
|
|
735
|
-
this.config.checkpointer === false
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
)
|
|
744
|
-
|
|
893
|
+
const checkpointer = (() => {
|
|
894
|
+
if (this.config.checkpointer === false) return false;
|
|
895
|
+
if (
|
|
896
|
+
this.config.checkpointer === true ||
|
|
897
|
+
typeof this.config.checkpointer === "string"
|
|
898
|
+
) {
|
|
899
|
+
return createLoopCheckpointer(
|
|
900
|
+
this.config.checkpointer === true
|
|
901
|
+
? this.bus.getWorkspace()
|
|
902
|
+
: (this.config.checkpointer as string),
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
return undefined; // default MemorySaver
|
|
906
|
+
})();
|
|
745
907
|
const loop: WriteReviewLoop = buildWriteReviewLoop(deps, { checkpointer });
|
|
746
908
|
|
|
747
909
|
const invokeTask = (signal?: AbortSignal) =>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-harness/capability-registry",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.38",
|
|
4
4
|
"description": "Capability Registry for pi-harness-runtime — tracks model capabilities and enables capability-based routing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-harness/provider-router",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.38",
|
|
4
4
|
"description": "Enhanced Provider Router for pi-harness-runtime — intelligent routing with capabilities, costs, and quotas",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/provider-router.js",
|