pi-harness-runtime 1.1.152 → 1.1.153
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/graph.ts +9 -9
- package/harness/langchain/{jev-logger.ts → planner-logger.ts} +25 -24
- package/harness/service-diagnostics.ts +217 -0
- package/index.ts +30 -6
- package/package.json +1 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/agent-policy/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/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/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/paperclip-integration/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/dist/client.d.ts.map +1 -1
- package/packages/qdrant-skills/dist/client.js +4 -3
- package/packages/qdrant-skills/dist/client.js.map +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/qdrant-skills/src/client.ts +14 -17
- 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/dist/loader.d.ts +9 -2
- package/packages/skills/dist/loader.d.ts.map +1 -1
- package/packages/skills/dist/loader.js +24 -7
- package/packages/skills/dist/loader.js.map +1 -1
- package/packages/skills/package.json +1 -1
- package/packages/skills/src/loader.ts +27 -7
- 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/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
|
@@ -457,22 +457,22 @@ export async function buildRealLoopDeps(
|
|
|
457
457
|
});
|
|
458
458
|
const planOutput = lastMessage(plannerResponse) ?? "";
|
|
459
459
|
|
|
460
|
-
// Import and use
|
|
460
|
+
// Import and use planner logger (async - logs to file + Telegram)
|
|
461
461
|
try {
|
|
462
|
-
const {
|
|
463
|
-
const
|
|
464
|
-
setJevProvider(
|
|
465
|
-
await
|
|
462
|
+
const { logPlannerDecision, parsePlannerDecision } = await import("./planner-logger.js");
|
|
463
|
+
const plannerDecision = parsePlannerDecision(planOutput);
|
|
464
|
+
setJevProvider(plannerDecision.provider);
|
|
465
|
+
await logPlannerDecision({
|
|
466
466
|
timestamp: new Date().toISOString(),
|
|
467
467
|
request,
|
|
468
|
-
provider:
|
|
469
|
-
reason:
|
|
470
|
-
userInput:
|
|
468
|
+
provider: plannerDecision.provider,
|
|
469
|
+
reason: plannerDecision.reason,
|
|
470
|
+
userInput: plannerDecision.userInput,
|
|
471
471
|
escalate: false,
|
|
472
472
|
raw: planOutput,
|
|
473
473
|
});
|
|
474
474
|
} catch (logErr) {
|
|
475
|
-
console.error("[
|
|
475
|
+
console.error("[PLANNER] Logger error:", (logErr as Error).message);
|
|
476
476
|
}
|
|
477
477
|
|
|
478
478
|
return planOutput;
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Planner Logger - Logs LangChain planner decisions
|
|
3
3
|
*
|
|
4
|
-
* Logs
|
|
4
|
+
* Logs planner output:
|
|
5
5
|
* - Provider selection (minimax vs gpt)
|
|
6
6
|
* - User input decisions
|
|
7
7
|
* - Escalation triggers
|
|
8
8
|
* - Reasoning
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* NOTE: This logs the LangChain planner's output, not TypeSafe Jev service calls.
|
|
11
|
+
* The TypeSafe Jev service is tracked separately in the auto-continue system.
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
14
|
import { mkdirSync, appendFileSync, existsSync } from "node:fs";
|
|
14
15
|
import { join } from "node:path";
|
|
15
16
|
import { homedir } from "node:os";
|
|
16
17
|
|
|
17
|
-
export interface
|
|
18
|
+
export interface PlannerDecision {
|
|
18
19
|
timestamp: string;
|
|
19
20
|
request: string;
|
|
20
21
|
provider: "minimax" | "gpt";
|
|
@@ -27,12 +28,12 @@ export interface JevDecision {
|
|
|
27
28
|
escalateReason?: string;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
export interface
|
|
31
|
+
export interface PlannerLogEntry extends PlannerDecision {
|
|
31
32
|
raw?: string; // Raw planner output for debugging
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
const LOG_DIR = join(homedir(), ".pi-harness-runtime", "logs");
|
|
35
|
-
const LOG_FILE = join(LOG_DIR, "
|
|
36
|
+
const LOG_FILE = join(LOG_DIR, "planner.log");
|
|
36
37
|
|
|
37
38
|
// Telegram config (loaded from environment)
|
|
38
39
|
let TELEGRAM_BOT_TOKEN: string | undefined;
|
|
@@ -54,20 +55,20 @@ function ensureLogDir(): void {
|
|
|
54
55
|
/**
|
|
55
56
|
* Format decision for file log (detailed, multi-line)
|
|
56
57
|
*/
|
|
57
|
-
function formatFileLog(entry:
|
|
58
|
+
function formatFileLog(entry: PlannerLogEntry): string {
|
|
58
59
|
const lines = [
|
|
59
60
|
`═══════════════════════════════════════════════════════════════`,
|
|
60
|
-
`[
|
|
61
|
+
`[PLANNER] ${entry.timestamp}`,
|
|
61
62
|
`═══════════════════════════════════════════════════════════════`,
|
|
62
63
|
``,
|
|
63
64
|
`## Request`,
|
|
64
65
|
`${entry.request}`,
|
|
65
66
|
``,
|
|
66
|
-
`##
|
|
67
|
+
`## Provider Decision`,
|
|
67
68
|
`Provider: ${entry.provider.toUpperCase()}`,
|
|
68
69
|
`Reason: ${entry.reason}`,
|
|
69
70
|
``,
|
|
70
|
-
`##
|
|
71
|
+
`## User Input Decision`,
|
|
71
72
|
`Type: ${entry.userInput?.type ?? "none (proceed automatically)"}`,
|
|
72
73
|
entry.userInput?.question ? `Question: ${entry.userInput.question}` : ``,
|
|
73
74
|
``,
|
|
@@ -92,12 +93,12 @@ function formatFileLog(entry: JevLogEntry): string {
|
|
|
92
93
|
/**
|
|
93
94
|
* Format decision for Telegram (compact, single message)
|
|
94
95
|
*/
|
|
95
|
-
function formatTelegramMessage(entry:
|
|
96
|
+
function formatTelegramMessage(entry: PlannerLogEntry): string {
|
|
96
97
|
const providerIcon = entry.provider === "gpt" ? "🤖" : "⚡";
|
|
97
98
|
const escalateIcon = entry.escalate ? " ↑ ESCALATE" : "";
|
|
98
99
|
|
|
99
100
|
const parts = [
|
|
100
|
-
`${providerIcon} *
|
|
101
|
+
`${providerIcon} *PLANNER DECISION*`,
|
|
101
102
|
`\n*Provider:* ${entry.provider.toUpperCase()}${escalateIcon}`,
|
|
102
103
|
`\n*Reason:* ${entry.reason}`,
|
|
103
104
|
];
|
|
@@ -125,16 +126,16 @@ async function sendToTelegram(message: string): Promise<void> {
|
|
|
125
126
|
TELEGRAM_CHAT_ID = process.env.TELEGRAM_CHAT_ID ?? TELEGRAM_CHAT_ID;
|
|
126
127
|
|
|
127
128
|
if (!TELEGRAM_BOT_TOKEN || !TELEGRAM_CHAT_ID) {
|
|
128
|
-
console.log("[
|
|
129
|
+
console.log("[PLANNER] Telegram not configured - skipping");
|
|
129
130
|
return;
|
|
130
131
|
}
|
|
131
132
|
|
|
132
|
-
console.log(`[
|
|
133
|
+
console.log(`[PLANNER] Telegram sending: ${TELEGRAM_BOT_TOKEN?.slice(0, 10)}... to ${TELEGRAM_CHAT_ID}`);
|
|
133
134
|
telegramQueue.push(message);
|
|
134
135
|
|
|
135
136
|
// Process queue sequentially
|
|
136
137
|
if (telegramSending) {
|
|
137
|
-
console.log("[
|
|
138
|
+
console.log("[PLANNER] Telegram already sending, queued");
|
|
138
139
|
return;
|
|
139
140
|
}
|
|
140
141
|
telegramSending = true;
|
|
@@ -156,12 +157,12 @@ async function sendToTelegram(message: string): Promise<void> {
|
|
|
156
157
|
);
|
|
157
158
|
|
|
158
159
|
if (!response.ok) {
|
|
159
|
-
console.error(`[
|
|
160
|
+
console.error(`[PLANNER] Telegram send failed: ${response.status}`);
|
|
160
161
|
} else {
|
|
161
|
-
console.log("[
|
|
162
|
+
console.log("[PLANNER] Telegram sent successfully");
|
|
162
163
|
}
|
|
163
164
|
} catch (err) {
|
|
164
|
-
console.error(`[
|
|
165
|
+
console.error(`[PLANNER] Telegram error:`, err);
|
|
165
166
|
}
|
|
166
167
|
// Rate limit: 1 message per second
|
|
167
168
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -170,7 +171,7 @@ async function sendToTelegram(message: string): Promise<void> {
|
|
|
170
171
|
telegramSending = false;
|
|
171
172
|
}
|
|
172
173
|
|
|
173
|
-
export async function
|
|
174
|
+
export async function logPlannerDecision(decision: PlannerLogEntry): Promise<void> {
|
|
174
175
|
ensureLogDir();
|
|
175
176
|
|
|
176
177
|
// Always log to file (sync)
|
|
@@ -182,25 +183,25 @@ export async function logJevDecision(decision: JevLogEntry): Promise<void> {
|
|
|
182
183
|
const telegramMsg = formatTelegramMessage(decision);
|
|
183
184
|
// Fire-and-forget: don't await, let it run in background
|
|
184
185
|
sendToTelegram(telegramMsg).catch((err) => {
|
|
185
|
-
console.error("[
|
|
186
|
+
console.error("[PLANNER] Failed to send Telegram notification:", err);
|
|
186
187
|
});
|
|
187
188
|
// Also await briefly for testing, then let process exit handle it
|
|
188
189
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
189
190
|
}
|
|
190
191
|
|
|
191
192
|
/**
|
|
192
|
-
* Parse
|
|
193
|
+
* Parse planner's decision from output
|
|
193
194
|
* Looks for [JEVO] and [USER_INPUT] markers
|
|
194
195
|
*/
|
|
195
|
-
export function
|
|
196
|
+
export function parsePlannerDecision(planOutput: string): {
|
|
196
197
|
provider: "minimax" | "gpt";
|
|
197
198
|
reason: string;
|
|
198
|
-
userInput:
|
|
199
|
+
userInput: PlannerDecision["userInput"];
|
|
199
200
|
} {
|
|
200
201
|
const result: {
|
|
201
202
|
provider: "minimax" | "gpt";
|
|
202
203
|
reason: string;
|
|
203
|
-
userInput:
|
|
204
|
+
userInput: PlannerDecision["userInput"];
|
|
204
205
|
} = {
|
|
205
206
|
provider: "minimax",
|
|
206
207
|
reason: "default (no explicit decision)",
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service Diagnostics - Structured logging for harness services
|
|
3
|
+
*
|
|
4
|
+
* Logs service events to ~/.pi-harness-runtime/logs/services.jsonl
|
|
5
|
+
* with structured data for observability.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { appendFileSync, existsSync, mkdirSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
|
|
12
|
+
export type ServiceLevel = "info" | "warn" | "error" | "debug";
|
|
13
|
+
export type ServiceName = "jev" | "honcho" | "qdrant";
|
|
14
|
+
|
|
15
|
+
export interface ServiceEvent {
|
|
16
|
+
ts: string;
|
|
17
|
+
level: ServiceLevel;
|
|
18
|
+
service: ServiceName;
|
|
19
|
+
operation: string;
|
|
20
|
+
phase: "start" | "completed" | "failed";
|
|
21
|
+
sessionId?: string;
|
|
22
|
+
requestId?: string;
|
|
23
|
+
durationMs?: number;
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const LOG_DIR = join(homedir(), ".pi-harness-runtime", "logs");
|
|
28
|
+
const LOG_FILE = join(LOG_DIR, "services.jsonl");
|
|
29
|
+
let _initialized = false;
|
|
30
|
+
|
|
31
|
+
function ensureLogDir(): void {
|
|
32
|
+
if (_initialized) return;
|
|
33
|
+
if (!existsSync(LOG_DIR)) {
|
|
34
|
+
mkdirSync(LOG_DIR, { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
_initialized = true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Log a structured service event.
|
|
41
|
+
* Sensitive fields (keys, tokens, full content) are never logged.
|
|
42
|
+
*/
|
|
43
|
+
export function logServiceEvent(event: ServiceEvent): void {
|
|
44
|
+
ensureLogDir();
|
|
45
|
+
|
|
46
|
+
// Redact sensitive fields
|
|
47
|
+
const safeEvent = redactSensitive(event);
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
appendFileSync(LOG_FILE, JSON.stringify(safeEvent) + "\n");
|
|
51
|
+
} catch {
|
|
52
|
+
// Silently fail if logging fails - don't crash the service
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Redact sensitive fields from event data.
|
|
58
|
+
* Never log: apiKey, token, secret, chatId, content, vectors, embeddings
|
|
59
|
+
*/
|
|
60
|
+
function redactSensitive(event: ServiceEvent): Record<string, unknown> {
|
|
61
|
+
const sensitiveKeys = [
|
|
62
|
+
"apiKey",
|
|
63
|
+
"token",
|
|
64
|
+
"secret",
|
|
65
|
+
"bearerToken",
|
|
66
|
+
"chatId",
|
|
67
|
+
"content",
|
|
68
|
+
"vectors",
|
|
69
|
+
"embeddings",
|
|
70
|
+
"prompt",
|
|
71
|
+
"messages",
|
|
72
|
+
"raw",
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
const result: Record<string, unknown> = { ...event };
|
|
76
|
+
|
|
77
|
+
for (const key of Object.keys(result)) {
|
|
78
|
+
if (sensitiveKeys.some((sk) => key.toLowerCase().includes(sk.toLowerCase()))) {
|
|
79
|
+
result[key] = "[REDACTED]";
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Create a service status tracker.
|
|
88
|
+
*/
|
|
89
|
+
export class ServiceStatus {
|
|
90
|
+
private _configured = false;
|
|
91
|
+
private _initialized = false;
|
|
92
|
+
private _connected = false;
|
|
93
|
+
private _lastSuccessAt: string | null = null;
|
|
94
|
+
private _lastFailureAt: string | null = null;
|
|
95
|
+
private _lastErrorCode: string | null = null;
|
|
96
|
+
private _requests = 0;
|
|
97
|
+
private _successes = 0;
|
|
98
|
+
private _failures = 0;
|
|
99
|
+
private _featureEnabled = false;
|
|
100
|
+
private _service: ServiceName;
|
|
101
|
+
|
|
102
|
+
constructor(service: ServiceName) {
|
|
103
|
+
this._service = service;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
get configured() { return this._configured; }
|
|
107
|
+
get initialized() { return this._initialized; }
|
|
108
|
+
get connected() { return this._connected; }
|
|
109
|
+
get lastSuccessAt() { return this._lastSuccessAt; }
|
|
110
|
+
get lastFailureAt() { return this._lastFailureAt; }
|
|
111
|
+
get lastErrorCode() { return this._lastErrorCode; }
|
|
112
|
+
get requests() { return this._requests; }
|
|
113
|
+
get successes() { return this._successes; }
|
|
114
|
+
get failures() { return this._failures; }
|
|
115
|
+
get featureEnabled() { return this._featureEnabled; }
|
|
116
|
+
|
|
117
|
+
setConfigured(v: boolean): this {
|
|
118
|
+
this._configured = v;
|
|
119
|
+
logServiceEvent({
|
|
120
|
+
ts: new Date().toISOString(),
|
|
121
|
+
level: "info",
|
|
122
|
+
service: this._service,
|
|
123
|
+
operation: "config_checked",
|
|
124
|
+
phase: "completed",
|
|
125
|
+
configured: v,
|
|
126
|
+
});
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
setInitialized(v: boolean): this {
|
|
131
|
+
this._initialized = v;
|
|
132
|
+
if (v) {
|
|
133
|
+
logServiceEvent({
|
|
134
|
+
ts: new Date().toISOString(),
|
|
135
|
+
level: "info",
|
|
136
|
+
service: this._service,
|
|
137
|
+
operation: "initialized",
|
|
138
|
+
phase: "completed",
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return this;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
setConnected(v: boolean): this {
|
|
145
|
+
this._connected = v;
|
|
146
|
+
logServiceEvent({
|
|
147
|
+
ts: new Date().toISOString(),
|
|
148
|
+
level: "info",
|
|
149
|
+
service: this._service,
|
|
150
|
+
operation: "connected",
|
|
151
|
+
phase: v ? "completed" : "failed",
|
|
152
|
+
connected: v,
|
|
153
|
+
});
|
|
154
|
+
return this;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
setFeatureEnabled(v: boolean): this {
|
|
158
|
+
this._featureEnabled = v;
|
|
159
|
+
return this;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
recordSuccess(operation: string, durationMs?: number, extra?: Record<string, unknown>): void {
|
|
163
|
+
this._requests++;
|
|
164
|
+
this._successes++;
|
|
165
|
+
this._lastSuccessAt = new Date().toISOString();
|
|
166
|
+
logServiceEvent({
|
|
167
|
+
ts: new Date().toISOString(),
|
|
168
|
+
level: "info",
|
|
169
|
+
service: this._service,
|
|
170
|
+
operation,
|
|
171
|
+
phase: "completed",
|
|
172
|
+
durationMs,
|
|
173
|
+
requests: this._requests,
|
|
174
|
+
successes: this._successes,
|
|
175
|
+
...extra,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
recordFailure(operation: string, errorCode: string, extra?: Record<string, unknown>): void {
|
|
180
|
+
this._requests++;
|
|
181
|
+
this._failures++;
|
|
182
|
+
this._lastFailureAt = new Date().toISOString();
|
|
183
|
+
this._lastErrorCode = errorCode;
|
|
184
|
+
logServiceEvent({
|
|
185
|
+
ts: new Date().toISOString(),
|
|
186
|
+
level: "error",
|
|
187
|
+
service: this._service,
|
|
188
|
+
operation,
|
|
189
|
+
phase: "failed",
|
|
190
|
+
errorCode,
|
|
191
|
+
requests: this._requests,
|
|
192
|
+
failures: this._failures,
|
|
193
|
+
...extra,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
toJSON(): Record<string, unknown> {
|
|
198
|
+
return {
|
|
199
|
+
service: this._service,
|
|
200
|
+
configured: this._configured,
|
|
201
|
+
initialized: this._initialized,
|
|
202
|
+
connected: this._connected,
|
|
203
|
+
lastSuccessAt: this._lastSuccessAt,
|
|
204
|
+
lastFailureAt: this._lastFailureAt,
|
|
205
|
+
lastErrorCode: this._lastErrorCode,
|
|
206
|
+
requests: this._requests,
|
|
207
|
+
successes: this._successes,
|
|
208
|
+
failures: this._failures,
|
|
209
|
+
featureEnabled: this._featureEnabled,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Export service status instances
|
|
215
|
+
export const jevStatus = new ServiceStatus("jev");
|
|
216
|
+
export const honchoStatus = new ServiceStatus("honcho");
|
|
217
|
+
export const qdrantStatus = new ServiceStatus("qdrant");
|
package/index.ts
CHANGED
|
@@ -103,6 +103,11 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
|
|
|
103
103
|
return;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
// Resolve the actual API key: env takes priority, file is fallback
|
|
107
|
+
const resolvedApiKey = process.env.TYPESAFE_API_KEY ||
|
|
108
|
+
process.env.OPENROUTER_API_KEY ||
|
|
109
|
+
readFileSync(`${keysDir}/jev-api-key.txt`, "utf8").trim();
|
|
110
|
+
|
|
106
111
|
const mod = await import("./packages/jev-judge/src/auto-continue.js");
|
|
107
112
|
|
|
108
113
|
// Track waiting state
|
|
@@ -111,10 +116,22 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
|
|
|
111
116
|
|
|
112
117
|
// Detect when agent asks for continuation
|
|
113
118
|
pi.on("message_end", async (event) => {
|
|
114
|
-
const msg = event.message as { role?: string; content?: string } | undefined;
|
|
119
|
+
const msg = event.message as { role?: string; content?: string | Array<{ type?: string; text?: string }> } | undefined;
|
|
115
120
|
if (!msg || msg.role !== "assistant") return;
|
|
116
121
|
|
|
117
|
-
|
|
122
|
+
// Extract text from array content (TextContent blocks) or legacy string
|
|
123
|
+
let content = "";
|
|
124
|
+
if (typeof msg.content === "string") {
|
|
125
|
+
content = msg.content;
|
|
126
|
+
} else if (Array.isArray(msg.content)) {
|
|
127
|
+
// Extract text from TextContent blocks only; ignore thinking/tool calls
|
|
128
|
+
content = msg.content
|
|
129
|
+
.filter((block): block is { type: "text"; text: string } =>
|
|
130
|
+
block.type === "text" && typeof block.text === "string"
|
|
131
|
+
)
|
|
132
|
+
.map(block => block.text)
|
|
133
|
+
.join("\n");
|
|
134
|
+
}
|
|
118
135
|
const hasContinuation = /continue|proceed|next phase|next step/i.test(content);
|
|
119
136
|
|
|
120
137
|
if (hasContinuation && !waitingForUserSince) {
|
|
@@ -154,7 +171,7 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
|
|
|
154
171
|
|
|
155
172
|
try {
|
|
156
173
|
const judge = new mod.AutoContinueJudge({
|
|
157
|
-
apiKey:
|
|
174
|
+
apiKey: resolvedApiKey,
|
|
158
175
|
proceedThreshold: 0.7,
|
|
159
176
|
maxWaitMinutes: 30,
|
|
160
177
|
});
|
|
@@ -336,7 +353,7 @@ async function initHoncho(pi: { events: { emit(name: string, data: unknown): voi
|
|
|
336
353
|
const keyPath = `${home}/.pi-harness-runtime/keys/honcho-api-key.txt`;
|
|
337
354
|
|
|
338
355
|
if (!existsSync(keyPath)) {
|
|
339
|
-
logStartup("[pi-harness] Honcho
|
|
356
|
+
logStartup("[pi-harness] Honcho not configured:");
|
|
340
357
|
logStartup("[pi-harness] Get key: https://app.honcho.dev/api-keys");
|
|
341
358
|
logStartup(`[pi-harness] Then: echo "{api-key}" > ~/.pi-harness-runtime/keys/honcho-api-key.txt`);
|
|
342
359
|
return;
|
|
@@ -356,12 +373,19 @@ async function initHoncho(pi: { events: { emit(name: string, data: unknown): voi
|
|
|
356
373
|
name: "honcho",
|
|
357
374
|
definition: {
|
|
358
375
|
url: "https://mcp.honcho.dev",
|
|
359
|
-
auth:
|
|
376
|
+
auth: "bearer" as const,
|
|
377
|
+
bearerToken: apiKey,
|
|
360
378
|
},
|
|
361
379
|
};
|
|
380
|
+
|
|
381
|
+
// Emit registration request — pi-mcp-adapter will respond with
|
|
382
|
+
// pi-mcp-adapter:runtime-register-result event on completion
|
|
362
383
|
pi.events.emit("pi-mcp-adapter:runtime-register:v1", request);
|
|
384
|
+
console.log("[honcho] MCP registration emitted (awaiting adapter ack)");
|
|
363
385
|
});
|
|
364
|
-
|
|
386
|
+
|
|
387
|
+
// Report pending state — actual memory active requires explicit lifecycle
|
|
388
|
+
logStartup("[pi-harness] Honcho registered (awaiting adapter connection)");
|
|
365
389
|
} catch (err) {
|
|
366
390
|
logStartup("[pi-harness] Honcho: init failed:", err instanceof Error ? err.message : String(err));
|
|
367
391
|
}
|
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.153",
|
|
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",
|