pi-harness-runtime 1.1.47 → 1.1.49
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 +108 -90
- package/harness/langchain/status-line.test.ts +20 -6
- package/harness/langchain/status-line.ts +7 -7
- 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
|
@@ -539,95 +539,105 @@ class BdTasksWatcher {
|
|
|
539
539
|
* Set "enabled": false to pause without deleting.
|
|
540
540
|
*/
|
|
541
541
|
class CronWatcher {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
542
|
+
private readonly workspace: string;
|
|
543
|
+
private readonly scheduleMs: Record<string, number>;
|
|
544
|
+
private readonly seen: Map<string, Set<string>> = new Map();
|
|
545
|
+
private readonly timers: Map<string, ReturnType<typeof setTimeout>> =
|
|
546
|
+
new Map();
|
|
547
|
+
private running = false;
|
|
548
|
+
|
|
549
|
+
constructor(workspace: string) {
|
|
550
|
+
this.workspace = workspace;
|
|
551
|
+
this.scheduleMs = {
|
|
552
|
+
"every-5m": 5 * 60_000,
|
|
553
|
+
"every-10m": 10 * 60_000,
|
|
554
|
+
"every-30m": 30 * 60_000,
|
|
555
|
+
hourly: 3_600_000,
|
|
556
|
+
daily: 86_400_000,
|
|
557
|
+
weekly: 7 * 86_400_000,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
start(onTask: (task: TriggeredTask) => void): void {
|
|
562
|
+
this.running = true;
|
|
563
|
+
const cronDir = `${this.workspace}/.cron-tasks`;
|
|
564
|
+
import("node:fs").then(({ mkdirSync, existsSync }) => {
|
|
565
|
+
if (!existsSync(cronDir)) mkdirSync(cronDir, { recursive: true });
|
|
566
|
+
});
|
|
567
|
+
// Fire once at start
|
|
568
|
+
this._tickAll(onTask);
|
|
569
|
+
// Schedule each bucket
|
|
570
|
+
for (const [schedule, ms] of Object.entries(this.scheduleMs)) {
|
|
571
|
+
this._scheduleNext(schedule, ms, onTask);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
stop(): void {
|
|
576
|
+
this.running = false;
|
|
577
|
+
for (const t of this.timers.values()) clearTimeout(t);
|
|
578
|
+
this.timers.clear();
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
private _scheduleNext(
|
|
582
|
+
schedule: string,
|
|
583
|
+
ms: number,
|
|
584
|
+
onTask: (task: TriggeredTask) => void,
|
|
585
|
+
): void {
|
|
586
|
+
const timer = setTimeout(async () => {
|
|
587
|
+
if (!this.running) return;
|
|
588
|
+
await this._tickSchedule(schedule, onTask);
|
|
589
|
+
this._scheduleNext(schedule, ms, onTask);
|
|
590
|
+
}, ms);
|
|
591
|
+
this.timers.set(schedule, timer);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
private async _tickAll(onTask: (task: TriggeredTask) => void): Promise<void> {
|
|
595
|
+
for (const schedule of Object.keys(this.scheduleMs)) {
|
|
596
|
+
await this._tickSchedule(schedule, onTask);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
private async _tickSchedule(
|
|
601
|
+
schedule: string,
|
|
602
|
+
onTask: (task: TriggeredTask) => void,
|
|
603
|
+
): Promise<void> {
|
|
604
|
+
if (!this.running) return;
|
|
605
|
+
const { readdirSync, readFileSync, existsSync } = await import("node:fs");
|
|
606
|
+
const cronDir = `${this.workspace}/.cron-tasks/${schedule}`;
|
|
607
|
+
if (!existsSync(cronDir)) return;
|
|
608
|
+
|
|
609
|
+
let seen = this.seen.get(schedule);
|
|
610
|
+
if (!seen) {
|
|
611
|
+
seen = new Set();
|
|
612
|
+
this.seen.set(schedule, seen);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
let files: string[];
|
|
616
|
+
try {
|
|
617
|
+
files = readdirSync(cronDir).filter((f) => f.endsWith(".json"));
|
|
618
|
+
} catch {
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
for (const file of files) {
|
|
623
|
+
if (seen.has(file)) continue;
|
|
624
|
+
seen.add(file);
|
|
625
|
+
let taskData: { request?: string; enabled?: boolean };
|
|
626
|
+
try {
|
|
627
|
+
taskData = JSON.parse(readFileSync(`${cronDir}/${file}`, "utf8"));
|
|
628
|
+
} catch {
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
if (taskData.enabled === false) continue;
|
|
632
|
+
if (!taskData.request) continue;
|
|
633
|
+
onTask({
|
|
634
|
+
taskId: `cron:${schedule}:${file.replace(".json", "")}`,
|
|
635
|
+
request: taskData.request,
|
|
636
|
+
source: "cron",
|
|
637
|
+
triggeredAt: new Date().toISOString(),
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
}
|
|
631
641
|
}
|
|
632
642
|
|
|
633
643
|
// ─── Tunnel Health Monitor ───────────────────────────────────────────────────
|
|
@@ -1100,7 +1110,12 @@ export class LoopDaemon {
|
|
|
1100
1110
|
: "max_iterations",
|
|
1101
1111
|
);
|
|
1102
1112
|
}
|
|
1103
|
-
const loopDone =
|
|
1113
|
+
const loopDone =
|
|
1114
|
+
verdict === "approved"
|
|
1115
|
+
? "[F] finished"
|
|
1116
|
+
: verdict === "blocked"
|
|
1117
|
+
? "[!] blocked"
|
|
1118
|
+
: "[~] max_iter";
|
|
1104
1119
|
this.statusLine?.updateLoopStatus(loopDone);
|
|
1105
1120
|
|
|
1106
1121
|
// ── Gate check after blocked verdict ────────────────────────────────
|
|
@@ -1196,16 +1211,19 @@ export class LoopDaemon {
|
|
|
1196
1211
|
switch (step) {
|
|
1197
1212
|
case "plan": {
|
|
1198
1213
|
w.setPhase("planning", "GPT planning");
|
|
1214
|
+
this.statusLine?.updateLoopStatus("[P] planning");
|
|
1199
1215
|
break;
|
|
1200
1216
|
}
|
|
1201
1217
|
case "write": {
|
|
1202
1218
|
w.setPhase("writing", "MiniMax coding");
|
|
1219
|
+
this.statusLine?.updateLoopStatus("[W] coding");
|
|
1203
1220
|
// After a write step, bump the iteration counter
|
|
1204
1221
|
w.startIteration(state.iteration, this.config.maxIterations);
|
|
1205
1222
|
break;
|
|
1206
1223
|
}
|
|
1207
1224
|
case "review": {
|
|
1208
1225
|
w.setPhase("reviewing", "GPT reviewing");
|
|
1226
|
+
this.statusLine?.updateLoopStatus("[R] reviewing");
|
|
1209
1227
|
// Process review comments — one record per file
|
|
1210
1228
|
const comments = state.review?.comments ?? [];
|
|
1211
1229
|
if (comments.length === 0) break;
|
|
@@ -9,7 +9,11 @@ import { describe, it, beforeEach, afterEach } from "node:test";
|
|
|
9
9
|
import assert from "node:assert";
|
|
10
10
|
import { existsSync, readFileSync, rmSync, writeFileSync, mkdirSync } from "fs";
|
|
11
11
|
import { join } from "path";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
StatusLineManager,
|
|
14
|
+
isPiLensAvailable,
|
|
15
|
+
formatQuotaLine,
|
|
16
|
+
} from "./status-line.js";
|
|
13
17
|
|
|
14
18
|
describe("isPiLensAvailable", () => {
|
|
15
19
|
it("returns false when no PI_LENS env vars are set", () => {
|
|
@@ -47,13 +51,19 @@ describe("formatQuotaLine", () => {
|
|
|
47
51
|
|
|
48
52
|
it("shows reset countdown when resetAt is provided", () => {
|
|
49
53
|
const future = new Date(Date.now() + 2 * 60 * 60 * 1000).toISOString(); // 2h from now
|
|
50
|
-
const line = formatQuotaLine({
|
|
54
|
+
const line = formatQuotaLine({
|
|
55
|
+
fiveHourPercent: 89,
|
|
56
|
+
fiveHourResetAt: future,
|
|
57
|
+
});
|
|
51
58
|
assert.ok(line.includes("resets in"), line);
|
|
52
59
|
});
|
|
53
60
|
});
|
|
54
61
|
|
|
55
62
|
describe("StatusLineManager", () => {
|
|
56
|
-
const tmpDir = join(
|
|
63
|
+
const tmpDir = join(
|
|
64
|
+
process.env.TMPDIR ?? "/tmp",
|
|
65
|
+
`harness-status-test-${Date.now()}`,
|
|
66
|
+
);
|
|
57
67
|
let manager: StatusLineManager;
|
|
58
68
|
|
|
59
69
|
beforeEach(() => {
|
|
@@ -63,11 +73,15 @@ describe("StatusLineManager", () => {
|
|
|
63
73
|
|
|
64
74
|
afterEach(() => {
|
|
65
75
|
manager.stop();
|
|
66
|
-
try {
|
|
76
|
+
try {
|
|
77
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
78
|
+
} catch {
|
|
79
|
+
/* ignore */
|
|
80
|
+
}
|
|
67
81
|
});
|
|
68
82
|
|
|
69
83
|
it("getLine returns combined loop + quota status", () => {
|
|
70
|
-
manager.updateLoopStatus("
|
|
84
|
+
manager.updateLoopStatus("[P] planning");
|
|
71
85
|
const line = manager.getLine();
|
|
72
86
|
assert.ok(line.includes("planning"), line);
|
|
73
87
|
});
|
|
@@ -78,7 +92,7 @@ describe("StatusLineManager", () => {
|
|
|
78
92
|
});
|
|
79
93
|
|
|
80
94
|
it("writes .harness-status file when pi-lens is not available", () => {
|
|
81
|
-
manager.updateLoopStatus("
|
|
95
|
+
manager.updateLoopStatus("[W] coding");
|
|
82
96
|
const statusFile = join(tmpDir, ".harness-status");
|
|
83
97
|
assert.ok(existsSync(statusFile), ".harness-status should exist");
|
|
84
98
|
const content = readFileSync(statusFile, "utf8");
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* The status line shows two tiers of information:
|
|
11
11
|
* [loop status] [API quota]
|
|
12
|
-
*
|
|
12
|
+
* [P] planning | MiniMax: 5h: 89% left · week: 90% left
|
|
13
13
|
*
|
|
14
14
|
* API quota is read from ~/.pi-harness/quota.json (written by the pi host
|
|
15
15
|
* process that has access to the TUI quota signals). The daemon polls this
|
|
@@ -95,12 +95,12 @@ function fmtResetAt(iso?: string): string {
|
|
|
95
95
|
* Format: "MiniMax: 5h: 89% left (resets in 2h) · week: 90% left"
|
|
96
96
|
*/
|
|
97
97
|
export function formatQuotaLine(q: QuotaData): string {
|
|
98
|
-
const fiveHour =
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
: `week: ${fmtPercent(q.weeklyPercent)}`;
|
|
98
|
+
const fiveHour =
|
|
99
|
+
q.fiveHourPercent === undefined
|
|
100
|
+
? null
|
|
101
|
+
: `5h: ${fmtPercent(q.fiveHourPercent)}${fmtResetAt(q.fiveHourResetAt)}`;
|
|
102
|
+
const weekly =
|
|
103
|
+
q.weeklyPercent === undefined ? null : `week: ${fmtPercent(q.weeklyPercent)}`;
|
|
104
104
|
|
|
105
105
|
const parts = [fiveHour, weekly].filter(Boolean);
|
|
106
106
|
if (parts.length === 0) return "MiniMax: quota unknown";
|
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.49",
|
|
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",
|