pi-harness-runtime 1.1.392 → 1.1.394
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/console-guard.ts +73 -0
- package/index.ts +103 -1
- 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/connection-runtime/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/contracts/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/durable-review/package.json +1 -1
- package/packages/e2e-tester/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-sink/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/permission-mode/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/relay/dist/client.d.ts +25 -1
- package/packages/relay/dist/client.js +71 -23
- package/packages/relay/dist/index.d.ts +1 -1
- package/packages/relay/package.json +1 -1
- package/packages/relay/src/client.ts +87 -23
- package/packages/relay/src/index.ts +1 -1
- package/packages/relay/test/client.test.ts +134 -0
- 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/telegram/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/voice-api/package.json +1 -1
- package/packages/webmcp-tester/package.json +1 -1
- package/packages/wiki/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
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Console guard - keep a TUI's terminal clean.
|
|
3
|
+
*
|
|
4
|
+
* A TUI owns the terminal: a stray `console.log` from a library lands in the
|
|
5
|
+
* prompt input field ("[RelayClient] Connected ..."). While the TUI runs, the
|
|
6
|
+
* harness redirects the console into its log file.
|
|
7
|
+
*
|
|
8
|
+
* The guard is deliberately dumb: it never writes to the terminal and a
|
|
9
|
+
* failing sink can never break the caller.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export type ConsoleLevel = "log" | "info" | "warn" | "error" | "debug";
|
|
13
|
+
|
|
14
|
+
export interface ConsoleGuardOptions {
|
|
15
|
+
/** Receives every console call. Exceptions are swallowed. */
|
|
16
|
+
sink: (level: ConsoleLevel, message: string) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const LEVELS: ConsoleLevel[] = ["log", "info", "warn", "error", "debug"];
|
|
20
|
+
|
|
21
|
+
/** Flatten the console arguments into a single line. */
|
|
22
|
+
export function formatConsoleArgs(args: unknown[]): string {
|
|
23
|
+
return args
|
|
24
|
+
.map((arg) => {
|
|
25
|
+
if (typeof arg === "string") return arg;
|
|
26
|
+
// The stack matters: pi's fatal path prints the error object.
|
|
27
|
+
if (arg instanceof Error) return arg.stack ?? arg.message;
|
|
28
|
+
try {
|
|
29
|
+
const json = JSON.stringify(arg);
|
|
30
|
+
return json === undefined ? String(arg) : json;
|
|
31
|
+
} catch {
|
|
32
|
+
return String(arg);
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
.join(" ");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Replace the console methods with a sink.
|
|
40
|
+
*
|
|
41
|
+
* @returns a restore function that puts the original console back.
|
|
42
|
+
*/
|
|
43
|
+
export function installConsoleGuard(options: ConsoleGuardOptions): () => void {
|
|
44
|
+
// SAFETY: the console methods are own, writable properties of the global
|
|
45
|
+
// console object; TypeScript cannot express that index signature.
|
|
46
|
+
const target = globalThis.console as unknown as Record<string, unknown>;
|
|
47
|
+
const originals = new Map<string, unknown>();
|
|
48
|
+
|
|
49
|
+
for (const level of LEVELS) {
|
|
50
|
+
originals.set(level, target[level]);
|
|
51
|
+
target[level] = (...args: unknown[]): void => {
|
|
52
|
+
try {
|
|
53
|
+
options.sink(level, formatConsoleArgs(args));
|
|
54
|
+
} catch {
|
|
55
|
+
// A failing sink must never break the caller.
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return () => {
|
|
61
|
+
for (const level of LEVELS) {
|
|
62
|
+
// The presence test must not depend on the value: a level that was
|
|
63
|
+
// absent before must be removed again.
|
|
64
|
+
if (!originals.has(level)) continue;
|
|
65
|
+
const original = originals.get(level);
|
|
66
|
+
if (original === undefined) {
|
|
67
|
+
delete target[level];
|
|
68
|
+
} else {
|
|
69
|
+
target[level] = original;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
package/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
import { renderDetailLines } from "./harness/startup-lines.ts";
|
|
31
31
|
import { describeInboundAccess, getAllowedUserIds } from "./harness/telegram-auth.ts";
|
|
32
32
|
import { buildTelegramCallbackUpdate, buildTelegramMessageUpdate } from "./harness/telegram-update.ts";
|
|
33
|
+
import { installConsoleGuard } from "./harness/console-guard.ts";
|
|
33
34
|
import {
|
|
34
35
|
jevMissingKeyReport,
|
|
35
36
|
lineConfigurationReport,
|
|
@@ -345,6 +346,7 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
|
|
|
345
346
|
if (ctx.hasUI && ctx.ui) {
|
|
346
347
|
_setAutoContinueStatus = ctx.ui.setStatus.bind(ctx.ui, "harness-auto-continue");
|
|
347
348
|
}
|
|
349
|
+
|
|
348
350
|
});
|
|
349
351
|
|
|
350
352
|
// Clear status and cancel background work on session shutdown
|
|
@@ -2044,6 +2046,14 @@ async function initRelayStream(): Promise<void> {
|
|
|
2044
2046
|
]);
|
|
2045
2047
|
}
|
|
2046
2048
|
},
|
|
2049
|
+
},
|
|
2050
|
+
{
|
|
2051
|
+
// A TUI owns the terminal: the relay diagnostics belong in the log
|
|
2052
|
+
// file. A console write would land in the prompt input field
|
|
2053
|
+
// ("[RelayClient] Connected ...").
|
|
2054
|
+
log: (message) => logToFile("[RelayStream]", `log: ${message}`),
|
|
2055
|
+
warn: (message) => logToFile("[RelayStream]", `warn: ${message}`),
|
|
2056
|
+
error: (message) => logToFile("[RelayStream]", `error: ${message}`),
|
|
2047
2057
|
}
|
|
2048
2058
|
);
|
|
2049
2059
|
|
|
@@ -2055,6 +2065,50 @@ async function initRelayStream(): Promise<void> {
|
|
|
2055
2065
|
}
|
|
2056
2066
|
}
|
|
2057
2067
|
|
|
2068
|
+
/**
|
|
2069
|
+
* Console guard - the TUI owns the terminal.
|
|
2070
|
+
*
|
|
2071
|
+
* A library that writes to the console puts its text into the prompt input
|
|
2072
|
+
* field ("[RelayClient] Connected ..."). In TUI mode every console call goes
|
|
2073
|
+
* to the harness log instead. An error is also surfaced as a harness message,
|
|
2074
|
+
* so a crash is never invisible.
|
|
2075
|
+
*/
|
|
2076
|
+
function initConsoleGuard(pi: ExtensionAPI): void {
|
|
2077
|
+
let restore: (() => void) | null = null;
|
|
2078
|
+
// A reload emits session_start BEFORE the previous session_shutdown:
|
|
2079
|
+
// the guard must stay installed for the new session. The console is only
|
|
2080
|
+
// given back when every started session has been shut down.
|
|
2081
|
+
let starts = 0;
|
|
2082
|
+
let shutdowns = 0;
|
|
2083
|
+
const recentErrors = new Map<string, number>();
|
|
2084
|
+
|
|
2085
|
+
pi.on("session_start", (_event, ctx) => {
|
|
2086
|
+
starts++;
|
|
2087
|
+
if (ctx.mode !== "tui" || restore) return;
|
|
2088
|
+
restore = installConsoleGuard({
|
|
2089
|
+
sink: (level, message) => {
|
|
2090
|
+
logToFile("[Console]", `${level}: ${message}`);
|
|
2091
|
+
if (level !== "error") return;
|
|
2092
|
+
// One message per 30s: a crash must be visible, a loop must not flood.
|
|
2093
|
+
const now = Date.now();
|
|
2094
|
+
if (now - (recentErrors.get(message) ?? 0) < 30_000) return;
|
|
2095
|
+
recentErrors.set(message, now);
|
|
2096
|
+
const text = message.length > 200 ? `${message.slice(0, 200)}...` : message;
|
|
2097
|
+
addHarnessMessage("Console", text, "err");
|
|
2098
|
+
},
|
|
2099
|
+
});
|
|
2100
|
+
logToFile("[Console]", "Console redirected to the log file (TUI mode)");
|
|
2101
|
+
});
|
|
2102
|
+
|
|
2103
|
+
pi.on("session_shutdown", () => {
|
|
2104
|
+
shutdowns++;
|
|
2105
|
+
// Only the last session may take the console back.
|
|
2106
|
+
if (!restore || shutdowns < starts) return;
|
|
2107
|
+
restore();
|
|
2108
|
+
restore = null;
|
|
2109
|
+
});
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2058
2112
|
// --- honcho-memory: Honcho MCP for peer/user memory via https://mcp.honcho.dev ------
|
|
2059
2113
|
// Registers Honcho MCP server and implements automatic memory lifecycle:
|
|
2060
2114
|
// - Ingest sanitized messages on completed turns
|
|
@@ -2765,7 +2819,7 @@ function processCompletionFile(filePath: string, pi: ExtensionAPI): void {
|
|
|
2765
2819
|
// --- Debug logging (file only, no console override) ---------------
|
|
2766
2820
|
// Logs written to file only. Real console output preserved for pi's TUI.
|
|
2767
2821
|
import { homedir } from "node:os";
|
|
2768
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, symlinkSync, appendFileSync, realpathSync, statSync } from "node:fs";
|
|
2822
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, symlinkSync, appendFileSync, realpathSync, statSync, chmodSync, openSync, closeSync, renameSync } from "node:fs";
|
|
2769
2823
|
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
2770
2824
|
import { join, dirname } from "node:path";
|
|
2771
2825
|
|
|
@@ -2775,6 +2829,11 @@ const HARNESS_ROOT_DIR = join(homedir(), ".pi-harness-runtime");
|
|
|
2775
2829
|
/** Startup messages collected during init — shown on session_start via ctx.ui.notify */
|
|
2776
2830
|
const startupMessages: string[] = [];
|
|
2777
2831
|
const HARNESS_LOG_FILE = "/tmp/pi-harness.log";
|
|
2832
|
+
// The log can contain urls and session ids: owner only, no rotation while it
|
|
2833
|
+
// is small, and a bounded size so a chatty dependency cannot fill the disk.
|
|
2834
|
+
const HARNESS_LOG_MAX_BYTES = 5 * 1024 * 1024;
|
|
2835
|
+
let harnessLogFileSecured = false;
|
|
2836
|
+
let harnessLogWrites = 0;
|
|
2778
2837
|
|
|
2779
2838
|
/** Log a harness message to file (not stderr - bypasses Pi's cursor tracking) */
|
|
2780
2839
|
function logToFile(...parts: string[]): void {
|
|
@@ -2782,6 +2841,44 @@ function logToFile(...parts: string[]): void {
|
|
|
2782
2841
|
// A timestamp makes the log usable for latency measurements.
|
|
2783
2842
|
const stamp = new Date().toISOString();
|
|
2784
2843
|
try {
|
|
2844
|
+
if (!harnessLogFileSecured) {
|
|
2845
|
+
try {
|
|
2846
|
+
// Create the file with the owner-only mode BEFORE the first
|
|
2847
|
+
// write: there must be no world-readable window.
|
|
2848
|
+
const fd = openSync(HARNESS_LOG_FILE, "a", 0o600);
|
|
2849
|
+
closeSync(fd);
|
|
2850
|
+
chmodSync(HARNESS_LOG_FILE, 0o600);
|
|
2851
|
+
// Only a successful attempt stops the retries.
|
|
2852
|
+
harnessLogFileSecured = true;
|
|
2853
|
+
} catch {
|
|
2854
|
+
// Ignore permission errors (e.g. a mounted /tmp) and retry
|
|
2855
|
+
// on the next write.
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
|
|
2859
|
+
// Keep the log bounded: rotate it when it grew too large.
|
|
2860
|
+
harnessLogWrites++;
|
|
2861
|
+
if (harnessLogWrites % 200 === 0) {
|
|
2862
|
+
try {
|
|
2863
|
+
if (statSync(HARNESS_LOG_FILE).size > HARNESS_LOG_MAX_BYTES) {
|
|
2864
|
+
renameSync(HARNESS_LOG_FILE, `${HARNESS_LOG_FILE}.1`);
|
|
2865
|
+
chmodSync(`${HARNESS_LOG_FILE}.1`, 0o600);
|
|
2866
|
+
// Secure the fresh file right away: the write that
|
|
2867
|
+
// follows must not land in a 0644 file.
|
|
2868
|
+
try {
|
|
2869
|
+
const fd = openSync(HARNESS_LOG_FILE, "a", 0o600);
|
|
2870
|
+
closeSync(fd);
|
|
2871
|
+
chmodSync(HARNESS_LOG_FILE, 0o600);
|
|
2872
|
+
harnessLogFileSecured = true;
|
|
2873
|
+
} catch {
|
|
2874
|
+
harnessLogFileSecured = false;
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
} catch {
|
|
2878
|
+
// Ignore rotation errors
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2785
2882
|
appendFileSync(HARNESS_LOG_FILE, `[${stamp}] ${msg}\n`);
|
|
2786
2883
|
} catch {
|
|
2787
2884
|
// Ignore file write errors
|
|
@@ -3294,6 +3391,11 @@ export default async function (pi: ExtensionAPI) {
|
|
|
3294
3391
|
// Telegram is not configured at all - that is the fresh-install case.
|
|
3295
3392
|
reportRelayConfiguration();
|
|
3296
3393
|
|
|
3394
|
+
// --- console-guard: keep the TUI prompt input field clean ----
|
|
3395
|
+
// A library that writes to the console would put its text into the
|
|
3396
|
+
// prompt input field. The guard sends it to the harness log instead.
|
|
3397
|
+
initConsoleGuard(pi);
|
|
3398
|
+
|
|
3297
3399
|
// --- line-voice-queue: Watch for LINE voice commands ----
|
|
3298
3400
|
void initLineVoiceQueue(pi);
|
|
3299
3401
|
|
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.394",
|
|
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",
|