pi-harness-runtime 1.1.136 → 1.1.138
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 +128 -25
- package/harness/langchain/run.ts +1 -1
- package/index.ts +41 -2
- 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/codex-watcher/dist/src/index.d.ts +42 -0
- package/packages/codex-watcher/dist/src/index.d.ts.map +1 -0
- package/packages/codex-watcher/dist/src/index.js +198 -0
- package/packages/codex-watcher/dist/src/index.js.map +1 -0
- package/packages/codex-watcher/dist/src/parser.d.ts +36 -0
- package/packages/codex-watcher/dist/src/parser.d.ts.map +1 -0
- package/packages/codex-watcher/dist/src/parser.js +151 -0
- package/packages/codex-watcher/dist/src/parser.js.map +1 -0
- package/packages/codex-watcher/dist/src/session.d.ts +9 -0
- package/packages/codex-watcher/dist/src/session.d.ts.map +1 -0
- package/packages/codex-watcher/dist/src/session.js +131 -0
- package/packages/codex-watcher/dist/src/session.js.map +1 -0
- package/packages/codex-watcher/dist/src/types.d.ts +63 -0
- package/packages/codex-watcher/dist/src/types.d.ts.map +1 -0
- package/packages/codex-watcher/dist/src/types.js +17 -0
- package/packages/codex-watcher/dist/src/types.js.map +1 -0
- package/packages/codex-watcher/package.json +26 -0
- package/packages/codex-watcher/src/index.ts +244 -0
- package/packages/codex-watcher/src/parser.ts +210 -0
- package/packages/codex-watcher/src/session.ts +151 -0
- package/packages/codex-watcher/src/types.ts +67 -0
- package/packages/codex-watcher/test/watch.test.ts +150 -0
- package/packages/config-capture/package.json +1 -1
- package/packages/config-capture/src/index.ts +5 -16
- 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/jev-judge/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/qdrant-skills/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/skills/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/trajectory/package.json +1 -1
- package/packages/tui/dist/harness-header.d.ts +18 -14
- package/packages/tui/dist/harness-header.d.ts.map +1 -1
- package/packages/tui/dist/harness-header.js +110 -64
- package/packages/tui/dist/harness-header.js.map +1 -1
- package/packages/tui/dist/index.d.ts +1 -2
- package/packages/tui/dist/index.d.ts.map +1 -1
- package/packages/tui/dist/index.js +1 -2
- package/packages/tui/dist/index.js.map +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/tui/src/harness-header.ts +156 -77
- package/packages/tui/src/index.ts +6 -2
- package/packages/tui/test/harness-header.test.ts +380 -100
- 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
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
* Wiki: wiki/auto-trigger-multi-agent.md
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
+
import { execSync } from "node:child_process";
|
|
29
30
|
import { randomUUID } from "node:crypto";
|
|
30
31
|
import {
|
|
31
32
|
existsSync,
|
|
@@ -34,42 +35,34 @@ import {
|
|
|
34
35
|
readFileSync,
|
|
35
36
|
writeFileSync,
|
|
36
37
|
} from "node:fs";
|
|
37
|
-
import { execSync } from "node:child_process";
|
|
38
|
-
import { join } from "node:path";
|
|
39
38
|
import { homedir } from "node:os";
|
|
39
|
+
import { join } from "node:path";
|
|
40
40
|
|
|
41
41
|
import { LeaseManager } from "../../packages/autonomous-runtime/src/lease.js";
|
|
42
42
|
import type { ApprovalClass } from "../../packages/autonomous-runtime/src/types.js";
|
|
43
43
|
import {
|
|
44
|
+
type CodexSession,
|
|
45
|
+
startWatcher,
|
|
46
|
+
type WatcherHandle,
|
|
47
|
+
} from "../../packages/codex-watcher/dist/src/index.js";
|
|
48
|
+
import { classifyMessage } from "../../packages/codex-watcher/dist/src/parser.js";
|
|
49
|
+
import {
|
|
50
|
+
createHerdrBus,
|
|
44
51
|
ensureHerdrWorkspace,
|
|
45
52
|
getHerdrWorkspace,
|
|
46
53
|
type HerdrEventBus,
|
|
47
54
|
type LoopVerdict,
|
|
48
|
-
} from "../../packages/event-bus/src/herdr-bus.js";
|
|
49
|
-
import {
|
|
50
|
-
createHerdrBus,
|
|
51
55
|
publishCodeWritten,
|
|
52
56
|
publishLoopFinished,
|
|
53
57
|
publishLoopStarted,
|
|
54
58
|
publishReviewCompleted,
|
|
55
59
|
} from "../../packages/event-bus/src/herdr-bus.js";
|
|
56
60
|
import { NotificationCenter } from "../../packages/notification/dist/notification-center.js";
|
|
57
|
-
|
|
58
|
-
import {
|
|
59
|
-
invokeWithSurgeRetry,
|
|
60
|
-
invokeWithGLMRetry,
|
|
61
|
-
SurgeScheduler,
|
|
62
|
-
type SurgePolicy,
|
|
63
|
-
} from "./surge.js";
|
|
64
|
-
import type { LoopWidget } from "./widget.js";
|
|
65
|
-
import { StatusLineManager, isPiLensAvailable } from "./status-line.js";
|
|
66
|
-
import { createLoopCheckpointer } from "./checkpointer.js";
|
|
67
|
-
|
|
68
61
|
import {
|
|
69
|
-
getTrajectoryStore,
|
|
70
62
|
getApprovedPatternStore,
|
|
63
|
+
getTrajectoryStore,
|
|
71
64
|
} from "../../packages/trajectory/src/index.js";
|
|
72
|
-
|
|
65
|
+
import { createLoopCheckpointer } from "./checkpointer.js";
|
|
73
66
|
import {
|
|
74
67
|
buildDryRunDeps,
|
|
75
68
|
buildRealLoopDeps,
|
|
@@ -78,10 +71,19 @@ import {
|
|
|
78
71
|
type LoopState,
|
|
79
72
|
type WriteReviewLoop,
|
|
80
73
|
} from "./graph.js";
|
|
74
|
+
import { submitPingPongIntent } from "./ping-pong-middleware.js";
|
|
75
|
+
import { isPiLensAvailable, StatusLineManager } from "./status-line.js";
|
|
76
|
+
import {
|
|
77
|
+
invokeWithGLMRetry,
|
|
78
|
+
invokeWithSurgeRetry,
|
|
79
|
+
type SurgePolicy,
|
|
80
|
+
SurgeScheduler,
|
|
81
|
+
} from "./surge.js";
|
|
82
|
+
import type { LoopWidget } from "./widget.js";
|
|
81
83
|
|
|
82
84
|
// ─── Config ─────────────────────────────────────────────────────────────────
|
|
83
85
|
|
|
84
|
-
export type TriggerSource = "bd-tasks" | "inbox" | "bus" | "cron";
|
|
86
|
+
export type TriggerSource = "bd-tasks" | "inbox" | "bus" | "cron" | "codex";
|
|
85
87
|
|
|
86
88
|
export type ApprovalPolicy =
|
|
87
89
|
| "never" // fully autonomous; notify only on finish/blocked (default)
|
|
@@ -556,6 +558,73 @@ class BdTasksWatcher {
|
|
|
556
558
|
* Drop a JSON file into the right bucket to schedule a task.
|
|
557
559
|
* Set "enabled": false to pause without deleting.
|
|
558
560
|
*/
|
|
561
|
+
|
|
562
|
+
// ─── Codex watcher ──────────────────────────────────────────────────────────
|
|
563
|
+
|
|
564
|
+
/** Polls ~/.codex/sessions/ for active Codex CLI sessions.
|
|
565
|
+
* When Codex emits a plan-type message, emits a TriggeredTask so the daemon
|
|
566
|
+
* can qualify it via submitPingPongIntent and run the ping-pong loop.
|
|
567
|
+
*/
|
|
568
|
+
class CodexWatcher {
|
|
569
|
+
private readonly pollMs: number;
|
|
570
|
+
private handle: WatcherHandle | null = null;
|
|
571
|
+
private running = false;
|
|
572
|
+
private seenMsgs = new Set<string>();
|
|
573
|
+
private seenSessions = new Set<string>();
|
|
574
|
+
|
|
575
|
+
constructor(pollMs: number) {
|
|
576
|
+
this.pollMs = pollMs;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
start(onTask: (task: TriggeredTask) => void): void {
|
|
580
|
+
if (this.running) return;
|
|
581
|
+
this.running = true;
|
|
582
|
+
|
|
583
|
+
this.handle = startWatcher({
|
|
584
|
+
pollIntervalMs: this.pollMs,
|
|
585
|
+
onMessages: (msgs) => {
|
|
586
|
+
// Get current session from handle for deduplication key
|
|
587
|
+
const session = this.handle?.latestSession();
|
|
588
|
+
const sessionId = session?.id ?? "unknown";
|
|
589
|
+
|
|
590
|
+
for (const msg of msgs) {
|
|
591
|
+
// Deduplicate by ordinal — Codex rewrites the last line on each turn
|
|
592
|
+
const msgKey = `${sessionId}:${msg.ordinal}`;
|
|
593
|
+
if (this.seenMsgs.has(msgKey)) continue;
|
|
594
|
+
this.seenMsgs.add(msgKey);
|
|
595
|
+
|
|
596
|
+
// Only emit plan-type messages
|
|
597
|
+
const classification = classifyMessage(msg.text);
|
|
598
|
+
if (classification !== "plan") continue;
|
|
599
|
+
if (!msg.text.trim()) continue;
|
|
600
|
+
|
|
601
|
+
const taskId = `codex-${sessionId}-${msg.ordinal}`;
|
|
602
|
+
onTask({
|
|
603
|
+
taskId,
|
|
604
|
+
request: msg.text.trim(),
|
|
605
|
+
source: "codex",
|
|
606
|
+
triggeredAt: msg.timestamp,
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
},
|
|
610
|
+
onSessionChange: (session: CodexSession | null) => {
|
|
611
|
+
if (!session) return;
|
|
612
|
+
if (this.seenSessions.has(session.id)) return;
|
|
613
|
+
this.seenSessions.add(session.id);
|
|
614
|
+
console.log(
|
|
615
|
+
`[codex-watcher] Session started: ${session.id} — "${session.threadName}"`,
|
|
616
|
+
);
|
|
617
|
+
},
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
stop(): void {
|
|
622
|
+
this.running = false;
|
|
623
|
+
this.handle?.stop();
|
|
624
|
+
this.handle = null;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
559
628
|
class CronWatcher {
|
|
560
629
|
private readonly workspace: string;
|
|
561
630
|
private readonly scheduleMs: Record<string, number>;
|
|
@@ -814,6 +883,7 @@ export class LoopDaemon {
|
|
|
814
883
|
private readonly inboxWatcher: InboxWatcher;
|
|
815
884
|
private readonly busWatcher: BusWatcher;
|
|
816
885
|
private readonly bdWatcher: BdTasksWatcher;
|
|
886
|
+
private readonly codexWatcher: CodexWatcher | null;
|
|
817
887
|
private readonly cronWatcher: CronWatcher | null;
|
|
818
888
|
private readonly tunnelMonitor: TunnelMonitor | null;
|
|
819
889
|
private readonly statusLine: StatusLineManager | null;
|
|
@@ -858,9 +928,11 @@ export class LoopDaemon {
|
|
|
858
928
|
|
|
859
929
|
if (this.config.notificationConfig) {
|
|
860
930
|
this.notificationCenter = new NotificationCenter({
|
|
861
|
-
channels: this._buildChannels(
|
|
862
|
-
|
|
863
|
-
>[0] extends {
|
|
931
|
+
channels: this._buildChannels(
|
|
932
|
+
this.config.notificationConfig,
|
|
933
|
+
) as Parameters<typeof NotificationCenter.prototype.notify>[0] extends {
|
|
934
|
+
channels: infer C;
|
|
935
|
+
}
|
|
864
936
|
? C
|
|
865
937
|
: never,
|
|
866
938
|
enabled: true,
|
|
@@ -873,6 +945,9 @@ export class LoopDaemon {
|
|
|
873
945
|
this.widget = this.config.widget ?? null;
|
|
874
946
|
this.busWatcher = new BusWatcher(this.bus, this.config.pollMs);
|
|
875
947
|
this.bdWatcher = new BdTasksWatcher(this.config.pollMs);
|
|
948
|
+
this.codexWatcher = this.config.sources.includes("codex")
|
|
949
|
+
? new CodexWatcher(this.config.pollMs)
|
|
950
|
+
: null;
|
|
876
951
|
this.cronWatcher = this.config.sources.includes("cron")
|
|
877
952
|
? new CronWatcher(this.bus.getWorkspace())
|
|
878
953
|
: null;
|
|
@@ -920,6 +995,11 @@ export class LoopDaemon {
|
|
|
920
995
|
this.bdWatcher.start((t) => this._onTriggered(t));
|
|
921
996
|
}
|
|
922
997
|
|
|
998
|
+
if (this.config.sources.includes("codex")) {
|
|
999
|
+
console.log(`[daemon] Starting Codex CLI session watcher`);
|
|
1000
|
+
this.codexWatcher?.start((t) => this._onTriggered(t));
|
|
1001
|
+
}
|
|
1002
|
+
|
|
923
1003
|
this.tunnelMonitor?.start();
|
|
924
1004
|
this.statusLine?.start();
|
|
925
1005
|
|
|
@@ -934,6 +1014,7 @@ export class LoopDaemon {
|
|
|
934
1014
|
this.inboxWatcher.stop();
|
|
935
1015
|
this.busWatcher.stop();
|
|
936
1016
|
this.bdWatcher.stop();
|
|
1017
|
+
this.codexWatcher?.stop();
|
|
937
1018
|
this.cronWatcher?.stop();
|
|
938
1019
|
this.statusLine?.stop();
|
|
939
1020
|
this.tunnelMonitor?.stop();
|
|
@@ -961,6 +1042,23 @@ export class LoopDaemon {
|
|
|
961
1042
|
private _onTriggered(task: TriggeredTask): void {
|
|
962
1043
|
if (this.stopped) return;
|
|
963
1044
|
|
|
1045
|
+
// ── Codex source: qualify via ping-pong middleware ──────────────────
|
|
1046
|
+
if (task.source === "codex") {
|
|
1047
|
+
const result = submitPingPongIntent(task.request, {
|
|
1048
|
+
actorId: this.agentId,
|
|
1049
|
+
});
|
|
1050
|
+
console.log(
|
|
1051
|
+
`[daemon] Ping-pong decision for [codex] ${task.taskId}: ${result.decision.decision} ` +
|
|
1052
|
+
`(score=${result.decision.score.toFixed(1)}, ` +
|
|
1053
|
+
`reasons=${result.decision.reasons.slice(0, 2).join("; ")}...)`,
|
|
1054
|
+
);
|
|
1055
|
+
console.log(`[daemon] blackboard: ${result.blackboardFile}`);
|
|
1056
|
+
console.log(`[daemon] audit: ${result.auditFile}`);
|
|
1057
|
+
if (result.enqueued) {
|
|
1058
|
+
console.log(`[daemon] enqueued: ${result.taskFile}`);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
|
|
964
1062
|
// Skip if already running this task
|
|
965
1063
|
if (this.running.has(task.taskId)) {
|
|
966
1064
|
console.log(
|
|
@@ -1014,7 +1112,9 @@ export class LoopDaemon {
|
|
|
1014
1112
|
// ── Gate check (I7) ──────────────────────────────────────────────
|
|
1015
1113
|
const policy = effectivePolicy(task, this.config);
|
|
1016
1114
|
if (policy === "always" || policy === "on_blocked") {
|
|
1017
|
-
log(
|
|
1115
|
+
log(
|
|
1116
|
+
`Gate policy=${policy} — waiting for human approval before starting`,
|
|
1117
|
+
);
|
|
1018
1118
|
const approved = await waitForAck(
|
|
1019
1119
|
task,
|
|
1020
1120
|
this.config,
|
|
@@ -1079,7 +1179,9 @@ export class LoopDaemon {
|
|
|
1079
1179
|
}
|
|
1080
1180
|
return undefined; // default MemorySaver
|
|
1081
1181
|
})();
|
|
1082
|
-
const loop: WriteReviewLoop = buildWriteReviewLoop(deps, {
|
|
1182
|
+
const loop: WriteReviewLoop = buildWriteReviewLoop(deps, {
|
|
1183
|
+
checkpointer,
|
|
1184
|
+
});
|
|
1083
1185
|
|
|
1084
1186
|
// Persistent scheduler: attempt counter survives across multiple loop.invoke()
|
|
1085
1187
|
// calls within the same task. This enables true exponential escalation
|
|
@@ -1211,7 +1313,8 @@ export class LoopDaemon {
|
|
|
1211
1313
|
? "reviewer blocked the task"
|
|
1212
1314
|
: iterations >= this.config.maxIterations
|
|
1213
1315
|
? `max iterations (${iterations}) reached with changes still requested`
|
|
1214
|
-
: comments.length > 0 &&
|
|
1316
|
+
: comments.length > 0 &&
|
|
1317
|
+
comments.every((c) => c.severity === "minor")
|
|
1215
1318
|
? `converged: only minor comments (${comments.length})`
|
|
1216
1319
|
: `stuck: changes still requested after ${iterations} iteration(s)`;
|
|
1217
1320
|
|
package/harness/langchain/run.ts
CHANGED
|
@@ -180,7 +180,7 @@ async function runDaemon(args: CliArgs): Promise<void> {
|
|
|
180
180
|
const daemon = new LoopDaemon({
|
|
181
181
|
maxIterations: args.maxIterations,
|
|
182
182
|
dryRun: args.dryRun,
|
|
183
|
-
sources: ["inbox", "bus"],
|
|
183
|
+
sources: ["inbox", "bus", "codex"],
|
|
184
184
|
});
|
|
185
185
|
|
|
186
186
|
// Graceful shutdown on SIGTERM / SIGINT
|
package/index.ts
CHANGED
|
@@ -244,7 +244,6 @@ async function initQdrant(): Promise<void> {
|
|
|
244
244
|
|
|
245
245
|
// --- telegram-notifications: Telegram bot for harness event notifications ------------
|
|
246
246
|
// Sends Telegram messages when jobs complete, tasks finish, or human input is needed.
|
|
247
|
-
let _notificationCenter: unknown = null;
|
|
248
247
|
async function initTelegram(): Promise<void> {
|
|
249
248
|
try {
|
|
250
249
|
const { NotificationCenter } = await import("./packages/notification/dist/index.js");
|
|
@@ -290,7 +289,7 @@ async function initTelegram(): Promise<void> {
|
|
|
290
289
|
return;
|
|
291
290
|
}
|
|
292
291
|
|
|
293
|
-
|
|
292
|
+
// NotificationCenter is ready — Telegram messages will be sent via NotificationCenter API
|
|
294
293
|
logStartup(`[pi-harness] Telegram notifications ready`);
|
|
295
294
|
} catch (err) {
|
|
296
295
|
logStartup("[pi-harness] Telegram: init failed:", err instanceof Error ? err.message : String(err));
|
|
@@ -2354,6 +2353,46 @@ Run \`bd ready\` to see current bd issues.
|
|
|
2354
2353
|
|
|
2355
2354
|
// --- Footer status (persistent badge) --------------------------------
|
|
2356
2355
|
pi.on("session_start", async (_event, ctx) => {
|
|
2356
|
+
// ── Auto-start LangChain loop daemon ───────────────────────────────────
|
|
2357
|
+
// Spawns `bun harness/langchain/run.ts --daemon` as a detached background
|
|
2358
|
+
// process if no daemon is currently running. The daemon watches:
|
|
2359
|
+
// inbox/ → manual task files dropped by the user
|
|
2360
|
+
// bus/ → task.proposed events (herdr event bus)
|
|
2361
|
+
// codex/ → Codex CLI session plans (PING-PONG Planner)
|
|
2362
|
+
// Detached pattern: spawn with detached:true + stdio:ignore + unref() so
|
|
2363
|
+
// the extension process can exit without killing the daemon.
|
|
2364
|
+
// PID file at ~/.pi-harness-runtime/daemon.pid prevents double-start.
|
|
2365
|
+
const { readFileSync: dfs, existsSync: efs, writeFileSync: wfs, unlinkSync: ufs } = await import("node:fs");
|
|
2366
|
+
const { spawn: spwn } = await import("node:child_process");
|
|
2367
|
+
const { dirname } = await import("node:path");
|
|
2368
|
+
const { fileURLToPath } = await import("node:url");
|
|
2369
|
+
const runtimeRoot = dirname(fileURLToPath(import.meta.url));
|
|
2370
|
+
const DAEMON_PID_FILE = `${process.env.HOME}/.pi-harness-runtime/daemon.pid`;
|
|
2371
|
+
const DAEMON_SCRIPT = `${runtimeRoot}/harness/langchain/run.ts`;
|
|
2372
|
+
const daemonPid = (() => {
|
|
2373
|
+
try { return parseInt(dfs(DAEMON_PID_FILE, "utf8").trim(), 10); } catch { return 0; }
|
|
2374
|
+
})();
|
|
2375
|
+
if (daemonPid > 0) {
|
|
2376
|
+
try { process.kill(daemonPid, 0); /* alive? */ } catch {
|
|
2377
|
+
// Stale PID — daemon is gone, clear and fall through to start
|
|
2378
|
+
try { ufs(DAEMON_PID_FILE); } catch { /* ignore */ }
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
// Only start if not already alive
|
|
2382
|
+
if (daemonPid <= 0 || !efs(DAEMON_PID_FILE)) {
|
|
2383
|
+
// Use detached: true + stdio: ignore so the daemon outlives this process
|
|
2384
|
+
const daemonProc = spwn(
|
|
2385
|
+
"bun",
|
|
2386
|
+
["run", DAEMON_SCRIPT, "--daemon"],
|
|
2387
|
+
{ detached: true, stdio: "ignore" },
|
|
2388
|
+
);
|
|
2389
|
+
daemonProc.unref(); // Don't wait for daemon to exit
|
|
2390
|
+
try { wfs(DAEMON_PID_FILE, String(daemonProc.pid), "utf8"); } catch { /* ignore */ }
|
|
2391
|
+
console.error("[pi-harness] LangChain daemon started (pid=%d)", daemonProc.pid);
|
|
2392
|
+
} else {
|
|
2393
|
+
console.error("[pi-harness] LangChain daemon already running (pid=%d)", daemonPid);
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2357
2396
|
// Capture only the setStatus function, not the ctx
|
|
2358
2397
|
footerSetStatus = ctx.ui.setStatus.bind(ctx.ui);
|
|
2359
2398
|
refreshFooterStatus(
|
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.138",
|
|
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",
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex Session Watcher — Main API
|
|
3
|
+
*
|
|
4
|
+
* Watches the active Codex CLI session and emits visible messages (user + assistant)
|
|
5
|
+
* so the harness PING-PONG middleware can feed them to pi.dev.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { startWatcher, getLatestSession, getVisibleMessages } from "@pi-harness/codex-watcher";
|
|
9
|
+
*
|
|
10
|
+
* // One-shot: get latest messages from the current session
|
|
11
|
+
* const session = getLatestSession();
|
|
12
|
+
* const messages = await getVisibleMessages(session.rolloutPath, { fromOrdinal: 0 });
|
|
13
|
+
*
|
|
14
|
+
* // Continuous: poll the active session and emit messages
|
|
15
|
+
* const handle = startWatcher({
|
|
16
|
+
* onMessages: (msgs) => console.log(msgs),
|
|
17
|
+
* });
|
|
18
|
+
* // later: handle.stop();
|
|
19
|
+
*/
|
|
20
|
+
import { classifyMessage } from "./parser.js";
|
|
21
|
+
import { getAllSessions, getLatestSession } from "./session.js";
|
|
22
|
+
import type { CodexMessage, CodexSession, WatcherHandle, WatcherOptions } from "./types.js";
|
|
23
|
+
export type { CodexMessage, CodexSession, WatcherHandle, WatcherOptions };
|
|
24
|
+
export { classifyMessage, getAllSessions, getLatestSession };
|
|
25
|
+
/** Read all visible messages from a rollout file, starting from a given ordinal. */
|
|
26
|
+
export declare function getVisibleMessages(rolloutPath: string, options?: {
|
|
27
|
+
fromOrdinal?: number;
|
|
28
|
+
limit?: number;
|
|
29
|
+
}): Promise<CodexMessage[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Start watching the active Codex session and call `onMessages` whenever
|
|
32
|
+
* new visible messages appear in the rollout file.
|
|
33
|
+
*
|
|
34
|
+
* @returns WatcherHandle — call handle.stop() to stop watching
|
|
35
|
+
*
|
|
36
|
+
* The watcher:
|
|
37
|
+
* 1. Polls session_index.jsonl to detect active session changes
|
|
38
|
+
* 2. Tracks file position so it only reads new lines (no re-read of full file)
|
|
39
|
+
* 3. Calls onMessages with incremental messages each poll cycle
|
|
40
|
+
*/
|
|
41
|
+
export declare function startWatcher(options?: WatcherOptions): WatcherHandle;
|
|
42
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,OAAO,EAAE,eAAe,EAAoB,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,KAAK,EACX,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,cAAc,EACd,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAC;AAM7D,oFAAoF;AACpF,wBAAsB,kBAAkB,CACvC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GACpD,OAAO,CAAC,YAAY,EAAE,CAAC,CAkBzB;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,cAAmB,GAAG,aAAa,CA6JxE"}
|