mirror of
https://github.com/earendil-works/pi.git
synced 2026-06-18 15:54:04 +08:00
Merge branch 'main' into async-file-tools
This commit is contained in:
@@ -2,19 +2,41 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### New Features
|
||||
|
||||
- **Hardened npm install and release path** - Pi now ships the CLI with a generated shrinkwrap for transitive dependencies, blocks accidental lockfile changes, verifies dependency pinning and lifecycle-script allowlists in checks, disables lifecycle scripts for self-update and local release installs where supported, and smoke-tests isolated npm and Bun installs before release. See [Supply-chain hardening](../../README.md#supply-chain-hardening).
|
||||
|
||||
### Added
|
||||
|
||||
- Added interactive update notes after `pi update` runs, so users can see the installed version's changelog before continuing ([#4724](https://github.com/earendil-works/pi-mono/pull/4724) by [@mitsuhiko](https://github.com/mitsuhiko)).
|
||||
- Exported image resize utilities from the package root for SDK consumers ([#4775](https://github.com/earendil-works/pi-mono/pull/4775) by [@xl0](https://github.com/xl0)).
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed source syntax to avoid TypeScript constructs that require JavaScript emit, keeping core sources compatible with Node.js strip-only TypeScript checks.
|
||||
- Removed web UI workspace references from the CLI package and dropped the package-level development watch script.
|
||||
- Published npm installs now include an `npm-shrinkwrap.json` to lock transitive dependencies for the CLI package.
|
||||
- Improved terminal theme detection for light/dark and truecolor handling.
|
||||
- Changed self-update package-manager commands to disable lifecycle scripts during reinstall.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed the system prompt to tell models to resolve pi docs and examples under the absolute package paths before reading topic-specific relative references ([#4752](https://github.com/earendil-works/pi/issues/4752)).
|
||||
- Fixed extension `ctx.abort()` during tool-call preflight to stop later confirmations and restore queued interactive input like Escape ([#4276](https://github.com/earendil-works/pi/issues/4276)).
|
||||
- Fixed AgentSession retry, compaction, and event settlement to use the awaited agent lifecycle instead of a separate event queue, and added `willRetry` to `agent_end` session events.
|
||||
- Fixed forked session runtime state to keep the active session id aligned with the fork target ([#4799](https://github.com/earendil-works/pi-mono/pull/4799) by [@Perlence](https://github.com/Perlence)).
|
||||
- Fixed the subagent extension's parallel mode to return useful per-task output and failed-task diagnostics to the parent model instead of 100-character previews ([#4710](https://github.com/earendil-works/pi/issues/4710)).
|
||||
- Fixed Windows local bash execution to hide helper console windows when launched from background SDK processes ([#4699](https://github.com/earendil-works/pi/issues/4699)).
|
||||
- Fixed managed npm extension folders to set cloud-sync ignore metadata where supported ([#4763](https://github.com/earendil-works/pi/issues/4763)).
|
||||
- Fixed HTTP idle timeout configuration so long-running provider streams can avoid premature idle disconnects ([#4759](https://github.com/earendil-works/pi-mono/pull/4759) by [@mitsuhiko](https://github.com/mitsuhiko)).
|
||||
- Fixed default system prompt boundaries to use explicit XML tags for clearer file separation ([#4709](https://github.com/earendil-works/pi-mono/pull/4709) by [@herrnel](https://github.com/herrnel)).
|
||||
- Fixed HTML share/export sidebar clicks for shared tool entries to scroll to the rendered tool call ([#4664](https://github.com/earendil-works/pi-mono/pull/4664) by [@yzhg1983](https://github.com/yzhg1983)).
|
||||
- Fixed theme palettes to set explicit text colors and avoid terminal-default color drift.
|
||||
- Fixed truecolor detection to align terminal image rendering and interactive theme decisions.
|
||||
- Fixed loader indicator startup inherited from `@earendil-works/pi-tui` so initialization cannot run before frames are available.
|
||||
- Fixed OpenAI-compatible default output token requests inherited from `@earendil-works/pi-ai` to avoid reserving impossible context windows on servers such as vLLM ([#4675](https://github.com/earendil-works/pi/issues/4675)).
|
||||
- Fixed OpenAI prompt cache keys inherited from `@earendil-works/pi-ai` to stay within the 64-character provider limit ([#4720](https://github.com/earendil-works/pi/issues/4720)).
|
||||
- Fixed Windows npm-family package commands for fnm-managed Node.js installs that expose both extensionless Unix scripts and `.cmd` shims ([#4793](https://github.com/earendil-works/pi/issues/4793)).
|
||||
|
||||
## [0.75.3] - 2026-05-18
|
||||
|
||||
|
||||
@@ -68,13 +68,15 @@ I regularly publish my own `pi-mono` work sessions here:
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
curl -fsSL https://pi.dev/install.sh | sh
|
||||
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
|
||||
```
|
||||
|
||||
Or with npm:
|
||||
`--ignore-scripts` disables dependency lifecycle scripts during install. Pi does not require install scripts for normal npm installs.
|
||||
|
||||
Installer alternative:
|
||||
|
||||
```bash
|
||||
npm install -g @earendil-works/pi-coding-agent
|
||||
curl -fsSL https://pi.dev/install.sh | sh
|
||||
```
|
||||
|
||||
Authenticate with an API key:
|
||||
|
||||
@@ -4,18 +4,20 @@ Pi is a minimal terminal coding harness. It is designed to stay small at the cor
|
||||
|
||||
## Quick start
|
||||
|
||||
On linux or mac you can install Pi with curl:
|
||||
Install Pi with npm:
|
||||
|
||||
```bash
|
||||
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
|
||||
```
|
||||
|
||||
`--ignore-scripts` disables dependency lifecycle scripts during install. Pi does not require install scripts for normal npm installs.
|
||||
|
||||
On Linux or macOS, you can also use the installer:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://pi.dev/install.sh | sh
|
||||
```
|
||||
|
||||
Or alternatively with npm:
|
||||
|
||||
```bash
|
||||
npm install -g @earendil-works/pi-coding-agent
|
||||
```
|
||||
|
||||
To uninstall pi itself, use npm for curl and npm installs:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -7,9 +7,11 @@ This page gets you from install to a useful first pi session.
|
||||
Pi is distributed as an npm package:
|
||||
|
||||
```bash
|
||||
npm install -g @earendil-works/pi-coding-agent
|
||||
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
|
||||
```
|
||||
|
||||
`--ignore-scripts` disables dependency lifecycle scripts during install. Pi does not require install scripts for normal npm installs.
|
||||
|
||||
### Uninstall
|
||||
|
||||
Use the package manager that installed pi. The curl installer uses npm globally, so curl and npm installs are removed with npm:
|
||||
|
||||
@@ -17,7 +17,7 @@ pkg update && pkg upgrade
|
||||
pkg install nodejs termux-api git
|
||||
|
||||
# Install pi
|
||||
npm install -g @earendil-works/pi-coding-agent
|
||||
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
|
||||
|
||||
# Create config directory
|
||||
mkdir -p ~/.pi/agent
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.52.0"
|
||||
"@anthropic-ai/sdk": "0.52.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { type Api, type Context, type Model, registerApiProvider, streamSimple }
|
||||
import { readFileSync } from "fs";
|
||||
import { getAgentDir } from "packages/coding-agent/src/config.js";
|
||||
import { join } from "path";
|
||||
import { MODELS, streamGitLabDuo } from "./index.js";
|
||||
import { MODELS, streamGitLabDuo } from "./index.ts";
|
||||
|
||||
const MODEL_MAP = new Map(MODELS.map((m) => [m.id, m]));
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
import type { Component } from "@earendil-works/pi-tui";
|
||||
import { isKeyRelease, type TUI } from "@earendil-works/pi-tui";
|
||||
import type { DoomEngine } from "./doom-engine.js";
|
||||
import { DoomKeys, mapKeyToDoom } from "./doom-keys.js";
|
||||
import type { DoomEngine } from "./doom-engine.ts";
|
||||
import { DoomKeys, mapKeyToDoom } from "./doom-keys.ts";
|
||||
|
||||
function renderHalfBlock(
|
||||
rgba: Uint8Array,
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
*/
|
||||
|
||||
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
||||
import { DoomOverlayComponent } from "./doom-component.js";
|
||||
import { DoomEngine } from "./doom-engine.js";
|
||||
import { ensureWadFile } from "./wad-finder.js";
|
||||
import { DoomOverlayComponent } from "./doom-component.ts";
|
||||
import { DoomEngine } from "./doom-engine.ts";
|
||||
import { ensureWadFile } from "./wad-finder.ts";
|
||||
|
||||
// Persistent engine instance - survives between invocations
|
||||
let activeEngine: DoomEngine | null = null;
|
||||
|
||||
@@ -16,7 +16,7 @@ import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
||||
import type { AssistantMessage, TextContent } from "@earendil-works/pi-ai";
|
||||
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
||||
import { Key } from "@earendil-works/pi-tui";
|
||||
import { extractTodoItems, isSafeCommand, markCompletedSteps, type TodoItem } from "./utils.js";
|
||||
import { extractTodoItems, isSafeCommand, markCompletedSteps, type TodoItem } from "./utils.ts";
|
||||
|
||||
// Tools
|
||||
const PLAN_MODE_TOOLS = ["read", "bash", "grep", "find", "ls", "questionnaire"];
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sandbox-runtime": "^0.0.26"
|
||||
"@anthropic-ai/sandbox-runtime": "0.0.26"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import { StringEnum } from "@earendil-works/pi-ai";
|
||||
import { type ExtensionAPI, getMarkdownTheme, withFileMutationQueue } from "@earendil-works/pi-coding-agent";
|
||||
import { Container, Markdown, Spacer, Text } from "@earendil-works/pi-tui";
|
||||
import { Type } from "typebox";
|
||||
import { type AgentConfig, type AgentScope, discoverAgents } from "./agents.js";
|
||||
import { type AgentConfig, type AgentScope, discoverAgents } from "./agents.ts";
|
||||
|
||||
const MAX_PARALLEL_TASKS = 8;
|
||||
const MAX_CONCURRENCY = 4;
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
"ms": "2.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ms": "^2.1.0"
|
||||
"@types/ms": "2.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
+1803
File diff suppressed because it is too large
Load Diff
@@ -25,7 +25,8 @@
|
||||
"dist",
|
||||
"docs",
|
||||
"examples",
|
||||
"CHANGELOG.md"
|
||||
"CHANGELOG.md",
|
||||
"npm-shrinkwrap.json"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "shx rm -rf dist",
|
||||
@@ -34,26 +35,27 @@
|
||||
"copy-assets": "shx mkdir -p dist/modes/interactive/theme && shx cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && shx mkdir -p dist/modes/interactive/assets && shx cp src/modes/interactive/assets/*.png dist/modes/interactive/assets/ && shx mkdir -p dist/core/export-html/vendor && shx cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && shx cp src/core/export-html/vendor/*.js dist/core/export-html/vendor/",
|
||||
"copy-binary-assets": "shx cp package.json dist/ && shx cp README.md dist/ && shx cp CHANGELOG.md dist/ && shx mkdir -p dist/theme && shx cp src/modes/interactive/theme/*.json dist/theme/ && shx mkdir -p dist/assets && shx cp src/modes/interactive/assets/*.png dist/assets/ && shx mkdir -p dist/export-html/vendor && shx cp src/core/export-html/template.html dist/export-html/ && shx cp src/core/export-html/vendor/*.js dist/export-html/vendor/ && shx cp -r docs dist/ && shx cp -r examples dist/ && shx cp ../../node_modules/@silvia-odwyer/photon-node/photon_rs_bg.wasm dist/",
|
||||
"test": "vitest --run",
|
||||
"prepublishOnly": "npm run clean && npm run build"
|
||||
"shrinkwrap": "node ../../scripts/generate-coding-agent-shrinkwrap.mjs",
|
||||
"prepublishOnly": "npm run clean && npm run build && npm run shrinkwrap"
|
||||
},
|
||||
"dependencies": {
|
||||
"@earendil-works/pi-agent-core": "^0.75.3",
|
||||
"@earendil-works/pi-ai": "^0.75.3",
|
||||
"@earendil-works/pi-tui": "^0.75.3",
|
||||
"@silvia-odwyer/photon-node": "^0.3.4",
|
||||
"chalk": "^5.5.0",
|
||||
"cross-spawn": "^7.0.6",
|
||||
"diff": "^8.0.2",
|
||||
"glob": "^13.0.1",
|
||||
"highlight.js": "^10.7.3",
|
||||
"hosted-git-info": "^9.0.2",
|
||||
"ignore": "^7.0.5",
|
||||
"jiti": "^2.7.0",
|
||||
"minimatch": "^10.2.3",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"typebox": "^1.1.24",
|
||||
"undici": "^8.3.0",
|
||||
"yaml": "^2.8.2"
|
||||
"@silvia-odwyer/photon-node": "0.3.4",
|
||||
"chalk": "5.6.2",
|
||||
"cross-spawn": "7.0.6",
|
||||
"diff": "8.0.4",
|
||||
"glob": "13.0.6",
|
||||
"highlight.js": "10.7.3",
|
||||
"hosted-git-info": "9.0.3",
|
||||
"ignore": "7.0.5",
|
||||
"jiti": "2.7.0",
|
||||
"minimatch": "10.2.5",
|
||||
"proper-lockfile": "4.1.2",
|
||||
"typebox": "1.1.38",
|
||||
"undici": "8.3.0",
|
||||
"yaml": "2.9.0"
|
||||
},
|
||||
"overrides": {
|
||||
"rimraf": "6.1.2",
|
||||
@@ -62,18 +64,18 @@
|
||||
}
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@mariozechner/clipboard": "^0.3.6"
|
||||
"@mariozechner/clipboard": "0.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cross-spawn": "^6.0.6",
|
||||
"@types/diff": "^7.0.2",
|
||||
"@types/hosted-git-info": "^3.0.5",
|
||||
"@types/ms": "^2.1.0",
|
||||
"@types/node": "^24.3.0",
|
||||
"@types/proper-lockfile": "^4.1.4",
|
||||
"shx": "^0.4.0",
|
||||
"typescript": "^5.7.3",
|
||||
"vitest": "^3.2.4"
|
||||
"@types/cross-spawn": "6.0.6",
|
||||
"@types/diff": "7.0.2",
|
||||
"@types/hosted-git-info": "3.0.5",
|
||||
"@types/ms": "2.1.0",
|
||||
"@types/node": "24.12.4",
|
||||
"@types/proper-lockfile": "4.1.4",
|
||||
"shx": "0.4.0",
|
||||
"typescript": "5.9.3",
|
||||
"vitest": "3.2.4"
|
||||
},
|
||||
"keywords": [
|
||||
"coding-agent",
|
||||
|
||||
@@ -5,23 +5,16 @@
|
||||
*
|
||||
* Test with: npx tsx src/cli-new.ts [args...]
|
||||
*/
|
||||
import * as undici from "undici";
|
||||
import { APP_NAME } from "./config.ts";
|
||||
import { configureHttpDispatcher } from "./core/http-dispatcher.ts";
|
||||
import { main } from "./main.ts";
|
||||
|
||||
process.title = APP_NAME;
|
||||
process.env.PI_CODING_AGENT = "true";
|
||||
process.emitWarning = (() => {}) as typeof process.emitWarning;
|
||||
|
||||
// bodyTimeout/headersTimeout default to 300s in undici; long local-LLM stalls
|
||||
// (e.g. vLLM buffering a large tool call) exceed that and abort the SSE stream
|
||||
// with UND_ERR_BODY_TIMEOUT. Disable both — provider SDKs enforce their own
|
||||
// AbortController-based deadlines via retry.provider.timeoutMs.
|
||||
undici.setGlobalDispatcher(new undici.EnvHttpProxyAgent({ allowH2: false, bodyTimeout: 0, headersTimeout: 0 }));
|
||||
|
||||
// Keep fetch and the dispatcher on the same undici implementation. Node 26.0's
|
||||
// bundled fetch can otherwise consume compressed responses through npm undici's
|
||||
// dispatcher without decompressing them, causing response.json() failures.
|
||||
undici.install?.();
|
||||
// Configure undici's global dispatcher before provider SDKs issue requests.
|
||||
// Runtime settings are applied once SettingsManager has loaded global/project settings.
|
||||
configureHttpDispatcher();
|
||||
|
||||
main(process.argv.slice(2));
|
||||
|
||||
@@ -110,21 +110,21 @@ function getSelfUpdateCommandForMethod(
|
||||
return undefined;
|
||||
case "pnpm":
|
||||
return makeSelfUpdateCommand(
|
||||
makeSelfUpdateCommandStep("pnpm", ["install", "-g", updatePackageName]),
|
||||
makeSelfUpdateCommandStep("pnpm", ["install", "-g", "--ignore-scripts", updatePackageName]),
|
||||
updatePackageName === installedPackageName
|
||||
? undefined
|
||||
: makeSelfUpdateCommandStep("pnpm", ["remove", "-g", installedPackageName]),
|
||||
);
|
||||
case "yarn":
|
||||
return makeSelfUpdateCommand(
|
||||
makeSelfUpdateCommandStep("yarn", ["global", "add", updatePackageName]),
|
||||
makeSelfUpdateCommandStep("yarn", ["global", "add", "--ignore-scripts", updatePackageName]),
|
||||
updatePackageName === installedPackageName
|
||||
? undefined
|
||||
: makeSelfUpdateCommandStep("yarn", ["global", "remove", installedPackageName]),
|
||||
);
|
||||
case "bun":
|
||||
return makeSelfUpdateCommand(
|
||||
makeSelfUpdateCommandStep("bun", ["install", "-g", updatePackageName]),
|
||||
makeSelfUpdateCommandStep("bun", ["install", "-g", "--ignore-scripts", updatePackageName]),
|
||||
updatePackageName === installedPackageName
|
||||
? undefined
|
||||
: makeSelfUpdateCommandStep("bun", ["uninstall", "-g", installedPackageName]),
|
||||
@@ -133,7 +133,13 @@ function getSelfUpdateCommandForMethod(
|
||||
const [command = "npm", ...npmArgs] = npmCommand ?? [];
|
||||
const inferred = npmCommand?.length ? undefined : getInferredNpmInstall();
|
||||
const prefixArgs = [...npmArgs, ...(inferred ? ["--prefix", inferred.prefix] : [])];
|
||||
const installStep = makeSelfUpdateCommandStep(command, [...prefixArgs, "install", "-g", updatePackageName]);
|
||||
const installStep = makeSelfUpdateCommandStep(command, [
|
||||
...prefixArgs,
|
||||
"install",
|
||||
"-g",
|
||||
"--ignore-scripts",
|
||||
updatePackageName,
|
||||
]);
|
||||
const uninstallStep =
|
||||
updatePackageName === installedPackageName
|
||||
? undefined
|
||||
|
||||
@@ -292,12 +292,11 @@ export class AgentSessionRuntime {
|
||||
return { cancelled: false, selectedText };
|
||||
}
|
||||
|
||||
const sourceManager = SessionManager.open(currentSessionFile, sessionDir);
|
||||
const forkedSessionPath = sourceManager.createBranchedSession(targetLeafId);
|
||||
const sessionManager = SessionManager.open(currentSessionFile, sessionDir);
|
||||
const forkedSessionPath = sessionManager.createBranchedSession(targetLeafId);
|
||||
if (!forkedSessionPath) {
|
||||
throw new Error("Failed to create forked session");
|
||||
}
|
||||
const sessionManager = SessionManager.open(forkedSessionPath, sessionDir);
|
||||
await this.teardownCurrent("fork", sessionManager.getSessionFile());
|
||||
this.apply(
|
||||
await this.createRuntime({
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import * as undici from "undici";
|
||||
|
||||
export const DEFAULT_HTTP_IDLE_TIMEOUT_MS = 300_000;
|
||||
|
||||
export const HTTP_IDLE_TIMEOUT_CHOICES = [
|
||||
{ label: "30 sec", timeoutMs: 30_000 },
|
||||
{ label: "1 min", timeoutMs: 60_000 },
|
||||
{ label: "2 min", timeoutMs: 120_000 },
|
||||
{ label: "5 min", timeoutMs: 300_000 },
|
||||
{ label: "disabled", timeoutMs: 0 },
|
||||
] as const;
|
||||
|
||||
export function parseHttpIdleTimeoutMs(value: unknown): number | undefined {
|
||||
if (typeof value === "string") {
|
||||
const trimmed = value.trim();
|
||||
if (trimmed.toLowerCase() === "disabled") {
|
||||
return 0;
|
||||
}
|
||||
if (trimmed.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return parseHttpIdleTimeoutMs(Number(trimmed));
|
||||
}
|
||||
|
||||
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
||||
return undefined;
|
||||
}
|
||||
return Math.floor(value);
|
||||
}
|
||||
|
||||
export function formatHttpIdleTimeoutMs(timeoutMs: number): string {
|
||||
const choice = HTTP_IDLE_TIMEOUT_CHOICES.find((item) => item.timeoutMs === timeoutMs);
|
||||
if (choice) {
|
||||
return choice.label;
|
||||
}
|
||||
return `${timeoutMs / 1000} sec`;
|
||||
}
|
||||
|
||||
export function configureHttpDispatcher(timeoutMs: number = DEFAULT_HTTP_IDLE_TIMEOUT_MS): void {
|
||||
const normalizedTimeoutMs = parseHttpIdleTimeoutMs(timeoutMs);
|
||||
if (normalizedTimeoutMs === undefined) {
|
||||
throw new Error(`Invalid HTTP idle timeout: ${String(timeoutMs)}`);
|
||||
}
|
||||
undici.setGlobalDispatcher(
|
||||
new undici.EnvHttpProxyAgent({
|
||||
allowH2: false,
|
||||
bodyTimeout: normalizedTimeoutMs,
|
||||
headersTimeout: normalizedTimeoutMs,
|
||||
}),
|
||||
);
|
||||
// Keep fetch and the dispatcher on the same undici implementation. Node 26.0's
|
||||
// bundled fetch can otherwise consume compressed responses through npm undici's
|
||||
// dispatcher without decompressing them, causing response.json() failures.
|
||||
undici.install?.();
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { homedir } from "os";
|
||||
import { dirname, join } from "path";
|
||||
import lockfile from "proper-lockfile";
|
||||
import { CONFIG_DIR_NAME, getAgentDir } from "../config.ts";
|
||||
import { DEFAULT_HTTP_IDLE_TIMEOUT_MS, parseHttpIdleTimeoutMs } from "./http-dispatcher.ts";
|
||||
|
||||
export interface CompactionSettings {
|
||||
enabled?: boolean; // default: true
|
||||
@@ -110,6 +111,7 @@ export interface Settings {
|
||||
markdown?: MarkdownSettings;
|
||||
warnings?: WarningSettings;
|
||||
sessionDir?: string; // Custom session storage directory (same format as --session-dir CLI flag)
|
||||
httpIdleTimeoutMs?: number; // HTTP header/body idle timeout in milliseconds; 0 disables it
|
||||
}
|
||||
|
||||
/** Deep merge settings: project/overrides take precedence, nested objects merge recursively */
|
||||
@@ -726,6 +728,27 @@ export class SettingsManager {
|
||||
};
|
||||
}
|
||||
|
||||
getHttpIdleTimeoutMs(): number {
|
||||
const value = this.settings.httpIdleTimeoutMs;
|
||||
const timeoutMs = parseHttpIdleTimeoutMs(value);
|
||||
if (timeoutMs !== undefined) {
|
||||
return timeoutMs;
|
||||
}
|
||||
if (value !== undefined) {
|
||||
throw new Error(`Invalid httpIdleTimeoutMs setting: ${String(value)}`);
|
||||
}
|
||||
return DEFAULT_HTTP_IDLE_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
setHttpIdleTimeoutMs(timeoutMs: number): void {
|
||||
if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
|
||||
throw new Error(`Invalid httpIdleTimeoutMs setting: ${String(timeoutMs)}`);
|
||||
}
|
||||
this.globalSettings.httpIdleTimeoutMs = Math.floor(timeoutMs);
|
||||
this.markModified("httpIdleTimeoutMs");
|
||||
this.save();
|
||||
}
|
||||
|
||||
getProviderRetrySettings(): { timeoutMs?: number; maxRetries?: number; maxRetryDelayMs: number } {
|
||||
return {
|
||||
timeoutMs: this.settings.retry?.provider?.timeoutMs,
|
||||
|
||||
@@ -26,6 +26,7 @@ import { formatNoModelsAvailableMessage } from "./core/auth-guidance.ts";
|
||||
import { AuthStorage } from "./core/auth-storage.ts";
|
||||
import { exportFromFile } from "./core/export-html/index.ts";
|
||||
import type { ExtensionFactory } from "./core/extensions/types.ts";
|
||||
import { configureHttpDispatcher } from "./core/http-dispatcher.ts";
|
||||
import { KeybindingsManager } from "./core/keybindings.ts";
|
||||
import type { ModelRegistry } from "./core/model-registry.ts";
|
||||
import { resolveCliModel, resolveModelScope, type ScopedModel } from "./core/model-resolver.ts";
|
||||
@@ -617,6 +618,7 @@ export async function main(args: string[], options?: MainOptions) {
|
||||
});
|
||||
const { services, session, modelFallbackMessage } = runtime;
|
||||
const { settingsManager, modelRegistry, resourceLoader } = services;
|
||||
configureHttpDispatcher(settingsManager.getHttpIdleTimeoutMs());
|
||||
|
||||
if (parsed.help) {
|
||||
const extensionFlags = resourceLoader
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Spacer,
|
||||
Text,
|
||||
} from "@earendil-works/pi-tui";
|
||||
import { formatHttpIdleTimeoutMs, HTTP_IDLE_TIMEOUT_CHOICES } from "../../../core/http-dispatcher.ts";
|
||||
import type { WarningSettings } from "../../../core/settings-manager.ts";
|
||||
import { getSelectListTheme, getSettingsListTheme, theme } from "../theme/theme.ts";
|
||||
import { DynamicBorder } from "./dynamic-border.ts";
|
||||
@@ -40,6 +41,7 @@ export interface SettingsConfig {
|
||||
steeringMode: "all" | "one-at-a-time";
|
||||
followUpMode: "all" | "one-at-a-time";
|
||||
transport: Transport;
|
||||
httpIdleTimeoutMs: number;
|
||||
thinkingLevel: ThinkingLevel;
|
||||
availableThinkingLevels: ThinkingLevel[];
|
||||
currentTheme: string;
|
||||
@@ -68,6 +70,7 @@ export interface SettingsCallbacks {
|
||||
onSteeringModeChange: (mode: "all" | "one-at-a-time") => void;
|
||||
onFollowUpModeChange: (mode: "all" | "one-at-a-time") => void;
|
||||
onTransportChange: (transport: Transport) => void;
|
||||
onHttpIdleTimeoutMsChange: (timeoutMs: number) => void;
|
||||
onThinkingLevelChange: (level: ThinkingLevel) => void;
|
||||
onThemeChange: (theme: string) => void;
|
||||
onThemePreview?: (theme: string) => void;
|
||||
@@ -238,6 +241,14 @@ export class SettingsSelectorComponent extends Container {
|
||||
currentValue: config.transport,
|
||||
values: ["sse", "websocket", "websocket-cached", "auto"],
|
||||
},
|
||||
{
|
||||
id: "http-idle-timeout",
|
||||
label: "HTTP idle timeout",
|
||||
description:
|
||||
"Maximum idle gap while waiting for HTTP headers or body chunks. Disable for local models that pause longer than five minutes.",
|
||||
currentValue: formatHttpIdleTimeoutMs(config.httpIdleTimeoutMs),
|
||||
values: HTTP_IDLE_TIMEOUT_CHOICES.map((choice) => choice.label),
|
||||
},
|
||||
{
|
||||
id: "hide-thinking",
|
||||
label: "Hide thinking",
|
||||
@@ -482,6 +493,13 @@ export class SettingsSelectorComponent extends Container {
|
||||
case "transport":
|
||||
callbacks.onTransportChange(newValue as Transport);
|
||||
break;
|
||||
case "http-idle-timeout": {
|
||||
const choice = HTTP_IDLE_TIMEOUT_CHOICES.find((item) => item.label === newValue);
|
||||
if (choice) {
|
||||
callbacks.onHttpIdleTimeoutMsChange(choice.timeoutMs);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "hide-thinking":
|
||||
callbacks.onHideThinkingBlockChange(newValue === "true");
|
||||
break;
|
||||
|
||||
@@ -71,6 +71,7 @@ import type {
|
||||
ExtensionWidgetOptions,
|
||||
} from "../../core/extensions/index.ts";
|
||||
import { FooterDataProvider, type ReadonlyFooterDataProvider } from "../../core/footer-data-provider.ts";
|
||||
import { configureHttpDispatcher, formatHttpIdleTimeoutMs } from "../../core/http-dispatcher.ts";
|
||||
import { type AppKeybinding, KeybindingsManager } from "../../core/keybindings.ts";
|
||||
import { createCompactionSummaryMessage } from "../../core/messages.ts";
|
||||
import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScope } from "../../core/model-resolver.ts";
|
||||
@@ -1566,6 +1567,7 @@ export class InteractiveMode {
|
||||
}
|
||||
|
||||
private applyRuntimeSettings(): void {
|
||||
configureHttpDispatcher(this.settingsManager.getHttpIdleTimeoutMs());
|
||||
this.footer.setSession(this.session);
|
||||
this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
|
||||
this.footerDataProvider.setCwd(this.sessionManager.getCwd());
|
||||
@@ -3846,6 +3848,7 @@ export class InteractiveMode {
|
||||
steeringMode: this.session.steeringMode,
|
||||
followUpMode: this.session.followUpMode,
|
||||
transport: this.settingsManager.getTransport(),
|
||||
httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
|
||||
thinkingLevel: this.session.thinkingLevel,
|
||||
availableThinkingLevels: this.session.getAvailableThinkingLevels(),
|
||||
currentTheme: this.settingsManager.getTheme() || "dark",
|
||||
@@ -3904,6 +3907,11 @@ export class InteractiveMode {
|
||||
this.settingsManager.setTransport(transport);
|
||||
this.session.agent.transport = transport;
|
||||
},
|
||||
onHttpIdleTimeoutMsChange: (timeoutMs) => {
|
||||
this.settingsManager.setHttpIdleTimeoutMs(timeoutMs);
|
||||
configureHttpDispatcher(timeoutMs);
|
||||
this.showStatus(`HTTP idle timeout: ${formatHttpIdleTimeoutMs(timeoutMs)}`);
|
||||
},
|
||||
onThinkingLevelChange: (level) => {
|
||||
this.session.setThinkingLevel(level);
|
||||
this.footer.invalidate();
|
||||
@@ -4891,6 +4899,7 @@ export class InteractiveMode {
|
||||
|
||||
try {
|
||||
await this.session.reload();
|
||||
configureHttpDispatcher(this.settingsManager.getHttpIdleTimeoutMs());
|
||||
this.keybindings.reload();
|
||||
const activeHeader = this.customHeader ?? this.builtInHeader;
|
||||
if (isExpandable(activeHeader)) {
|
||||
|
||||
@@ -4,12 +4,12 @@ import { join } from "node:path";
|
||||
import { Agent } from "@earendil-works/pi-agent-core";
|
||||
import { type AssistantMessage, getModel } from "@earendil-works/pi-ai";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { AgentSession } from "../src/core/agent-session.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { ModelRegistry } from "../src/core/model-registry.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
import { createTestResourceLoader } from "./utilities.js";
|
||||
import { AgentSession } from "../src/core/agent-session.ts";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { ModelRegistry } from "../src/core/model-registry.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
import { createTestResourceLoader } from "./utilities.ts";
|
||||
|
||||
vi.mock("../src/core/compaction/index.js", () => ({
|
||||
calculateContextTokens: (usage: {
|
||||
|
||||
@@ -12,17 +12,17 @@ import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { getModel } from "@earendil-works/pi-ai";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import type { AgentSession } from "../src/core/agent-session.js";
|
||||
import type { AgentSession } from "../src/core/agent-session.ts";
|
||||
import {
|
||||
type AgentSessionRuntime,
|
||||
type CreateAgentSessionRuntimeFactory,
|
||||
createAgentSessionFromServices,
|
||||
createAgentSessionRuntime,
|
||||
createAgentSessionServices,
|
||||
} from "../src/core/agent-session-runtime.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { API_KEY } from "./utilities.js";
|
||||
} from "../src/core/agent-session-runtime.ts";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import { API_KEY } from "./utilities.ts";
|
||||
|
||||
describe.skipIf(!API_KEY)("AgentSession forking", () => {
|
||||
let session: AgentSession;
|
||||
|
||||
@@ -13,13 +13,13 @@ import { join } from "node:path";
|
||||
import { Agent } from "@earendil-works/pi-agent-core";
|
||||
import { getModel } from "@earendil-works/pi-ai";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { AgentSession, type AgentSessionEvent } from "../src/core/agent-session.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { ModelRegistry } from "../src/core/model-registry.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
import { createCodingTools } from "../src/index.js";
|
||||
import { API_KEY, createTestResourceLoader } from "./utilities.js";
|
||||
import { AgentSession, type AgentSessionEvent } from "../src/core/agent-session.ts";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { ModelRegistry } from "../src/core/model-registry.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
import { createCodingTools } from "../src/index.ts";
|
||||
import { API_KEY, createTestResourceLoader } from "./utilities.ts";
|
||||
|
||||
describe.skipIf(!API_KEY)("AgentSession compaction e2e", () => {
|
||||
let session: AgentSession;
|
||||
|
||||
@@ -16,13 +16,13 @@ import {
|
||||
} from "@earendil-works/pi-ai";
|
||||
import { Type } from "typebox";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { AgentSession } from "../src/core/agent-session.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { ModelRegistry } from "../src/core/model-registry.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
import type { BuildSystemPromptOptions } from "../src/core/system-prompt.js";
|
||||
import { createTestExtensionsResult, createTestResourceLoader } from "./utilities.js";
|
||||
import { AgentSession } from "../src/core/agent-session.ts";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { ModelRegistry } from "../src/core/model-registry.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
import type { BuildSystemPromptOptions } from "../src/core/system-prompt.ts";
|
||||
import { createTestExtensionsResult, createTestResourceLoader } from "./utilities.ts";
|
||||
|
||||
// Mock stream that mimics AssistantMessageEventStream
|
||||
class MockAssistantStream extends EventStream<AssistantMessageEvent, AssistantMessage> {
|
||||
|
||||
@@ -3,12 +3,12 @@ import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { getModel } from "@earendil-works/pi-ai";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { DefaultResourceLoader } from "../src/core/resource-loader.js";
|
||||
import type { ExtensionFactory } from "../src/core/sdk.js";
|
||||
import { createAgentSession } from "../src/core/sdk.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { DefaultResourceLoader } from "../src/core/resource-loader.ts";
|
||||
import type { ExtensionFactory } from "../src/core/sdk.ts";
|
||||
import { createAgentSession } from "../src/core/sdk.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
|
||||
describe("AgentSession dynamic provider registration", () => {
|
||||
let tempDir: string;
|
||||
|
||||
@@ -4,10 +4,10 @@ import { join } from "node:path";
|
||||
import { getModel } from "@earendil-works/pi-ai";
|
||||
import { Type } from "typebox";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { DefaultResourceLoader } from "../src/core/resource-loader.js";
|
||||
import { createAgentSession } from "../src/core/sdk.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
import { DefaultResourceLoader } from "../src/core/resource-loader.ts";
|
||||
import { createAgentSession } from "../src/core/sdk.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
|
||||
describe("AgentSession dynamic tool registration", () => {
|
||||
let tempDir: string;
|
||||
|
||||
@@ -5,12 +5,12 @@ import { Agent, type AgentEvent, type AgentTool } from "@earendil-works/pi-agent
|
||||
import { type AssistantMessage, type AssistantMessageEvent, EventStream, getModel } from "@earendil-works/pi-ai";
|
||||
import { Type } from "typebox";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { AgentSession } from "../src/core/agent-session.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { ModelRegistry } from "../src/core/model-registry.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
import { createTestResourceLoader } from "./utilities.js";
|
||||
import { AgentSession } from "../src/core/agent-session.ts";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { ModelRegistry } from "../src/core/model-registry.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
import { createTestResourceLoader } from "./utilities.ts";
|
||||
|
||||
class MockAssistantStream extends EventStream<AssistantMessageEvent, AssistantMessage> {
|
||||
constructor() {
|
||||
|
||||
@@ -8,16 +8,16 @@ import {
|
||||
createAgentSessionFromServices,
|
||||
createAgentSessionRuntime,
|
||||
createAgentSessionServices,
|
||||
} from "../src/core/agent-session-runtime.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
} from "../src/core/agent-session-runtime.ts";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import type {
|
||||
ExtensionFactory,
|
||||
SessionBeforeForkEvent,
|
||||
SessionBeforeSwitchEvent,
|
||||
SessionShutdownEvent,
|
||||
SessionStartEvent,
|
||||
} from "../src/index.js";
|
||||
} from "../src/index.ts";
|
||||
|
||||
type RecordedSessionEvent =
|
||||
| SessionBeforeSwitchEvent
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Agent } from "@earendil-works/pi-agent-core";
|
||||
import { type AssistantMessage, getModel, type Usage } from "@earendil-works/pi-ai";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { AgentSession } from "../src/core/agent-session.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { ModelRegistry } from "../src/core/model-registry.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
import { createTestResourceLoader } from "./utilities.js";
|
||||
import { AgentSession } from "../src/core/agent-session.ts";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { ModelRegistry } from "../src/core/model-registry.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
import { createTestResourceLoader } from "./utilities.ts";
|
||||
|
||||
const model = getModel("anthropic", "claude-sonnet-4-5")!;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { API_KEY, createTestSession, type TestSessionContext } from "./utilities.js";
|
||||
import { API_KEY, createTestSession, type TestSessionContext } from "./utilities.ts";
|
||||
|
||||
describe.skipIf(!API_KEY)("AgentSession tree navigation e2e", () => {
|
||||
let ctx: TestSessionContext;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { stripAnsi } from "../src/utils/ansi.js";
|
||||
import { stripAnsi } from "../src/utils/ansi.ts";
|
||||
|
||||
function referenceAnsiRegex(): RegExp {
|
||||
const ST = "(?:\\u0007|\\u001B\\u005C|\\u009C)";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { parseArgs } from "../src/cli/args.js";
|
||||
import { parseArgs } from "../src/cli/args.ts";
|
||||
|
||||
describe("parseArgs", () => {
|
||||
describe("--version flag", () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { AssistantMessage } from "@earendil-works/pi-ai";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { AssistantMessageComponent } from "../src/modes/interactive/components/assistant-message.js";
|
||||
import { initTheme } from "../src/modes/interactive/theme/theme.js";
|
||||
import { AssistantMessageComponent } from "../src/modes/interactive/components/assistant-message.ts";
|
||||
import { initTheme } from "../src/modes/interactive/theme/theme.ts";
|
||||
|
||||
const OSC133_ZONE_START = "\x1b]133;A\x07";
|
||||
const OSC133_ZONE_END = "\x1b]133;B\x07";
|
||||
|
||||
@@ -4,8 +4,8 @@ import { join } from "node:path";
|
||||
import { registerOAuthProvider } from "@earendil-works/pi-ai/oauth";
|
||||
import lockfile from "proper-lockfile";
|
||||
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { clearConfigValueCache } from "../src/core/resolve-config-value.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { clearConfigValueCache } from "../src/core/resolve-config-value.ts";
|
||||
|
||||
describe("AuthStorage", () => {
|
||||
let tempDir: string;
|
||||
|
||||
@@ -3,8 +3,8 @@ import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { executeBashWithOperations } from "../src/core/bash-executor.js";
|
||||
import { createBashTool, createLocalBashOperations } from "../src/core/tools/bash.js";
|
||||
import { executeBashWithOperations } from "../src/core/bash-executor.ts";
|
||||
import { createBashTool, createLocalBashOperations } from "../src/core/tools/bash.ts";
|
||||
|
||||
function toBashSingleQuotedArg(value: string): string {
|
||||
return `'${value.replace(/\\/g, "/").replace(/'/g, `'"'"'`)}'`;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
*/
|
||||
import { visibleWidth } from "@earendil-works/pi-tui";
|
||||
import { beforeAll, describe, expect, it } from "vitest";
|
||||
import { BashExecutionComponent } from "../src/modes/interactive/components/bash-execution.js";
|
||||
import { initTheme } from "../src/modes/interactive/theme/theme.js";
|
||||
import { BashExecutionComponent } from "../src/modes/interactive/components/bash-execution.ts";
|
||||
import { initTheme } from "../src/modes/interactive/theme/theme.ts";
|
||||
|
||||
/** Minimal TUI stub that only exposes terminal.columns */
|
||||
function createTuiStub(columns: number): { columns: number; stub: any } {
|
||||
|
||||
@@ -2,9 +2,9 @@ import { mkdirSync, rmSync, writeFileSync } from "fs";
|
||||
import { tmpdir } from "os";
|
||||
import { join } from "path";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { processFileArguments } from "../src/cli/file-processor.js";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
import { createReadTool } from "../src/core/tools/read.js";
|
||||
import { processFileArguments } from "../src/cli/file-processor.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
import { createReadTool } from "../src/core/tools/read.ts";
|
||||
|
||||
// 1x1 red PNG image as base64 (smallest valid PNG)
|
||||
const TINY_PNG_BASE64 =
|
||||
|
||||
@@ -68,7 +68,7 @@ vi.mock("@mariozechner/clipboard", () => ({
|
||||
|
||||
describe("readClipboardImage BMP conversion", () => {
|
||||
test("converts BMP to PNG on Wayland/WSLg", async () => {
|
||||
const { readClipboardImage } = await import("../src/utils/clipboard-image.js");
|
||||
const { readClipboardImage } = await import("../src/utils/clipboard-image.ts");
|
||||
|
||||
// Simulate Wayland session (WSLg)
|
||||
const image = await readClipboardImage({
|
||||
|
||||
@@ -70,7 +70,7 @@ describe("readClipboardImage", () => {
|
||||
throw new Error(`Unexpected spawnSync call: ${command} ${args.join(" ")}`);
|
||||
});
|
||||
|
||||
const { readClipboardImage } = await import("../src/utils/clipboard-image.js");
|
||||
const { readClipboardImage } = await import("../src/utils/clipboard-image.ts");
|
||||
const result = await readClipboardImage({ platform: "linux", env: { WAYLAND_DISPLAY: "1" } });
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.mimeType).toBe("image/png");
|
||||
@@ -101,7 +101,7 @@ describe("readClipboardImage", () => {
|
||||
return spawnOk(Buffer.alloc(0));
|
||||
});
|
||||
|
||||
const { readClipboardImage } = await import("../src/utils/clipboard-image.js");
|
||||
const { readClipboardImage } = await import("../src/utils/clipboard-image.ts");
|
||||
const result = await readClipboardImage({ platform: "linux", env: { XDG_SESSION_TYPE: "wayland" } });
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.mimeType).toBe("image/png");
|
||||
@@ -138,7 +138,7 @@ describe("readClipboardImage", () => {
|
||||
throw new Error(`Unexpected spawnSync call: ${command} ${args.join(" ")}`);
|
||||
});
|
||||
|
||||
const { readClipboardImage } = await import("../src/utils/clipboard-image.js");
|
||||
const { readClipboardImage } = await import("../src/utils/clipboard-image.ts");
|
||||
const result = await readClipboardImage({ platform: "linux", env: { WSL_DISTRO_NAME: "Ubuntu" } });
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.mimeType).toBe("image/png");
|
||||
@@ -153,7 +153,7 @@ describe("readClipboardImage", () => {
|
||||
mocks.clipboard.hasImage.mockReturnValue(true);
|
||||
mocks.clipboard.getImageBinary.mockResolvedValue(new Uint8Array([7]));
|
||||
|
||||
const { readClipboardImage } = await import("../src/utils/clipboard-image.js");
|
||||
const { readClipboardImage } = await import("../src/utils/clipboard-image.ts");
|
||||
const result = await readClipboardImage({ platform: "linux", env: {} });
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.mimeType).toBe("image/png");
|
||||
@@ -167,7 +167,7 @@ describe("readClipboardImage", () => {
|
||||
|
||||
mocks.clipboard.hasImage.mockReturnValue(false);
|
||||
|
||||
const { readClipboardImage } = await import("../src/utils/clipboard-image.js");
|
||||
const { readClipboardImage } = await import("../src/utils/clipboard-image.ts");
|
||||
const result = await readClipboardImage({ platform: "linux", env: {} });
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { execSync, spawn } from "child_process";
|
||||
import { platform } from "os";
|
||||
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { copyToClipboard } from "../src/utils/clipboard.js";
|
||||
import { copyToClipboard } from "../src/utils/clipboard.ts";
|
||||
|
||||
const mocks = vi.hoisted(() => {
|
||||
return {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { ExtensionAPI, SessionBeforeCompactEvent, SessionCompactEvent } from "../src/core/extensions/index.js";
|
||||
import type { ExtensionAPI, SessionBeforeCompactEvent, SessionCompactEvent } from "../src/core/extensions/index.ts";
|
||||
|
||||
describe("Documentation example", () => {
|
||||
it("custom compaction example should type-check correctly", () => {
|
||||
|
||||
@@ -8,21 +8,21 @@ import { join } from "node:path";
|
||||
import { Agent } from "@earendil-works/pi-agent-core";
|
||||
import { getModel } from "@earendil-works/pi-ai";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { AgentSession } from "../src/core/agent-session.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { AgentSession } from "../src/core/agent-session.ts";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import {
|
||||
createExtensionRuntime,
|
||||
type Extension,
|
||||
type SessionBeforeCompactEvent,
|
||||
type SessionCompactEvent,
|
||||
type SessionEvent,
|
||||
} from "../src/core/extensions/index.js";
|
||||
import { ModelRegistry } from "../src/core/model-registry.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
import { createSyntheticSourceInfo } from "../src/core/source-info.js";
|
||||
import { createCodingTools } from "../src/index.js";
|
||||
import { createTestResourceLoader } from "./utilities.js";
|
||||
} from "../src/core/extensions/index.ts";
|
||||
import { ModelRegistry } from "../src/core/model-registry.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
import { createSyntheticSourceInfo } from "../src/core/source-info.ts";
|
||||
import { createCodingTools } from "../src/index.ts";
|
||||
import { createTestResourceLoader } from "./utilities.ts";
|
||||
|
||||
const API_KEY = process.env.ANTHROPIC_OAUTH_TOKEN || process.env.ANTHROPIC_API_KEY;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Message } from "@earendil-works/pi-ai";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { serializeConversation } from "../src/core/compaction/utils.js";
|
||||
import { serializeConversation } from "../src/core/compaction/utils.ts";
|
||||
|
||||
describe("serializeConversation", () => {
|
||||
it("should truncate long tool results", () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
||||
import type { AssistantMessage, Model } from "@earendil-works/pi-ai";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { type CompactionPreparation, compact, generateSummary } from "../src/core/compaction/index.js";
|
||||
import { type CompactionPreparation, compact, generateSummary } from "../src/core/compaction/index.ts";
|
||||
|
||||
const { completeSimpleMock } = vi.hoisted(() => ({
|
||||
completeSimpleMock: vi.fn(),
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
getLastAssistantUsage,
|
||||
prepareCompaction,
|
||||
shouldCompact,
|
||||
} from "../src/core/compaction/index.js";
|
||||
} from "../src/core/compaction/index.ts";
|
||||
import {
|
||||
buildSessionContext,
|
||||
type CompactionEntry,
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
type SessionEntry,
|
||||
type SessionMessageEntry,
|
||||
type ThinkingLevelChangeEntry,
|
||||
} from "../src/core/session-manager.js";
|
||||
} from "../src/core/session-manager.ts";
|
||||
|
||||
// ============================================================================
|
||||
// Test fixtures
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
getSelfUpdateCommand,
|
||||
getSelfUpdateUnavailableInstruction,
|
||||
getUpdateInstruction,
|
||||
} from "../src/config.js";
|
||||
} from "../src/config.ts";
|
||||
|
||||
const execPathDescriptor = Object.getOwnPropertyDescriptor(process, "execPath");
|
||||
const originalPath = process.env.PATH;
|
||||
@@ -153,7 +153,7 @@ describe("detectInstallMethod", () => {
|
||||
|
||||
expect(detectInstallMethod()).toBe("pnpm");
|
||||
expect(getUpdateInstruction("@earendil-works/pi-coding-agent")).toBe(
|
||||
"Run: pnpm install -g @earendil-works/pi-coding-agent",
|
||||
"Run: pnpm install -g --ignore-scripts @earendil-works/pi-coding-agent",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -175,8 +175,8 @@ describe("detectInstallMethod", () => {
|
||||
expect(detectInstallMethod()).toBe("npm");
|
||||
expect(command).toEqual({
|
||||
command: "npm",
|
||||
args: ["--prefix", prefix, "install", "-g", "@earendil-works/pi-coding-agent"],
|
||||
display: `npm --prefix ${prefix} install -g @earendil-works/pi-coding-agent`,
|
||||
args: ["--prefix", prefix, "install", "-g", "--ignore-scripts", "@earendil-works/pi-coding-agent"],
|
||||
display: `npm --prefix ${prefix} install -g --ignore-scripts @earendil-works/pi-coding-agent`,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -187,8 +187,8 @@ describe("detectInstallMethod", () => {
|
||||
|
||||
expect(command).toEqual({
|
||||
command: "npm",
|
||||
args: ["--prefix", prefix, "install", "-g", "@new-scope/pi"],
|
||||
display: `npm --prefix ${prefix} uninstall -g @mariozechner/pi-coding-agent && npm --prefix ${prefix} install -g @new-scope/pi`,
|
||||
args: ["--prefix", prefix, "install", "-g", "--ignore-scripts", "@new-scope/pi"],
|
||||
display: `npm --prefix ${prefix} uninstall -g @mariozechner/pi-coding-agent && npm --prefix ${prefix} install -g --ignore-scripts @new-scope/pi`,
|
||||
steps: [
|
||||
{
|
||||
command: "npm",
|
||||
@@ -197,8 +197,8 @@ describe("detectInstallMethod", () => {
|
||||
},
|
||||
{
|
||||
command: "npm",
|
||||
args: ["--prefix", prefix, "install", "-g", "@new-scope/pi"],
|
||||
display: `npm --prefix ${prefix} install -g @new-scope/pi`,
|
||||
args: ["--prefix", prefix, "install", "-g", "--ignore-scripts", "@new-scope/pi"],
|
||||
display: `npm --prefix ${prefix} install -g --ignore-scripts @new-scope/pi`,
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -211,8 +211,8 @@ describe("detectInstallMethod", () => {
|
||||
|
||||
expect(command).toEqual({
|
||||
command: "npm",
|
||||
args: ["--prefix", prefix, "install", "-g", "@earendil-works/pi-coding-agent"],
|
||||
display: `npm --prefix ${prefix} install -g @earendil-works/pi-coding-agent`,
|
||||
args: ["--prefix", prefix, "install", "-g", "--ignore-scripts", "@earendil-works/pi-coding-agent"],
|
||||
display: `npm --prefix ${prefix} install -g --ignore-scripts @earendil-works/pi-coding-agent`,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -221,7 +221,14 @@ describe("detectInstallMethod", () => {
|
||||
|
||||
const command = getSelfUpdateCommand("@earendil-works/pi-coding-agent", []);
|
||||
|
||||
expect(command?.args).toEqual(["--prefix", prefix, "install", "-g", "@earendil-works/pi-coding-agent"]);
|
||||
expect(command?.args).toEqual([
|
||||
"--prefix",
|
||||
prefix,
|
||||
"install",
|
||||
"-g",
|
||||
"--ignore-scripts",
|
||||
"@earendil-works/pi-coding-agent",
|
||||
]);
|
||||
});
|
||||
|
||||
test("quotes npm self-update display paths", () => {
|
||||
@@ -229,7 +236,9 @@ describe("detectInstallMethod", () => {
|
||||
|
||||
const command = getSelfUpdateCommand("@earendil-works/pi-coding-agent");
|
||||
|
||||
expect(command?.display).toBe(`npm --prefix "${prefix}" install -g @earendil-works/pi-coding-agent`);
|
||||
expect(command?.display).toBe(
|
||||
`npm --prefix "${prefix}" install -g --ignore-scripts @earendil-works/pi-coding-agent`,
|
||||
);
|
||||
});
|
||||
|
||||
test("does not infer Windows npm custom prefixes from package paths", () => {
|
||||
@@ -239,7 +248,7 @@ describe("detectInstallMethod", () => {
|
||||
|
||||
expect(detectInstallMethod()).toBe("npm");
|
||||
expect(getUpdateInstruction("@earendil-works/pi-coding-agent")).toBe(
|
||||
"Run: npm install -g @earendil-works/pi-coding-agent",
|
||||
"Run: npm install -g --ignore-scripts @earendil-works/pi-coding-agent",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -251,8 +260,8 @@ describe("detectInstallMethod", () => {
|
||||
expect(detectInstallMethod()).toBe("bun");
|
||||
expect(command).toEqual({
|
||||
command: "bun",
|
||||
args: ["install", "-g", "@earendil-works/pi-coding-agent"],
|
||||
display: "bun install -g @earendil-works/pi-coding-agent",
|
||||
args: ["install", "-g", "--ignore-scripts", "@earendil-works/pi-coding-agent"],
|
||||
display: "bun install -g --ignore-scripts @earendil-works/pi-coding-agent",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -264,8 +273,8 @@ describe("detectInstallMethod", () => {
|
||||
expect(detectInstallMethod()).toBe("pnpm");
|
||||
expect(command).toEqual({
|
||||
command: "pnpm",
|
||||
args: ["install", "-g", "@new-scope/pi"],
|
||||
display: "pnpm remove -g @mariozechner/pi-coding-agent && pnpm install -g @new-scope/pi",
|
||||
args: ["install", "-g", "--ignore-scripts", "@new-scope/pi"],
|
||||
display: "pnpm remove -g @mariozechner/pi-coding-agent && pnpm install -g --ignore-scripts @new-scope/pi",
|
||||
steps: [
|
||||
{
|
||||
command: "pnpm",
|
||||
@@ -274,8 +283,8 @@ describe("detectInstallMethod", () => {
|
||||
},
|
||||
{
|
||||
command: "pnpm",
|
||||
args: ["install", "-g", "@new-scope/pi"],
|
||||
display: "pnpm install -g @new-scope/pi",
|
||||
args: ["install", "-g", "--ignore-scripts", "@new-scope/pi"],
|
||||
display: "pnpm install -g --ignore-scripts @new-scope/pi",
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -319,8 +328,8 @@ describe("detectInstallMethod", () => {
|
||||
expect(detectInstallMethod()).toBe("pnpm");
|
||||
expect(command).toEqual({
|
||||
command: "pnpm",
|
||||
args: ["install", "-g", packageName],
|
||||
display: `pnpm install -g ${packageName}`,
|
||||
args: ["install", "-g", "--ignore-scripts", packageName],
|
||||
display: `pnpm install -g --ignore-scripts ${packageName}`,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -332,8 +341,8 @@ describe("detectInstallMethod", () => {
|
||||
expect(detectInstallMethod()).toBe("yarn");
|
||||
expect(command).toEqual({
|
||||
command: "yarn",
|
||||
args: ["global", "add", "@new-scope/pi"],
|
||||
display: "yarn global remove @mariozechner/pi-coding-agent && yarn global add @new-scope/pi",
|
||||
args: ["global", "add", "--ignore-scripts", "@new-scope/pi"],
|
||||
display: "yarn global remove @mariozechner/pi-coding-agent && yarn global add --ignore-scripts @new-scope/pi",
|
||||
steps: [
|
||||
{
|
||||
command: "yarn",
|
||||
@@ -342,8 +351,8 @@ describe("detectInstallMethod", () => {
|
||||
},
|
||||
{
|
||||
command: "yarn",
|
||||
args: ["global", "add", "@new-scope/pi"],
|
||||
display: "yarn global add @new-scope/pi",
|
||||
args: ["global", "add", "--ignore-scripts", "@new-scope/pi"],
|
||||
display: "yarn global add --ignore-scripts @new-scope/pi",
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -357,8 +366,8 @@ describe("detectInstallMethod", () => {
|
||||
expect(detectInstallMethod()).toBe("bun");
|
||||
expect(command).toEqual({
|
||||
command: "bun",
|
||||
args: ["install", "-g", "@new-scope/pi"],
|
||||
display: "bun uninstall -g @mariozechner/pi-coding-agent && bun install -g @new-scope/pi",
|
||||
args: ["install", "-g", "--ignore-scripts", "@new-scope/pi"],
|
||||
display: "bun uninstall -g @mariozechner/pi-coding-agent && bun install -g --ignore-scripts @new-scope/pi",
|
||||
steps: [
|
||||
{
|
||||
command: "bun",
|
||||
@@ -367,8 +376,8 @@ describe("detectInstallMethod", () => {
|
||||
},
|
||||
{
|
||||
command: "bun",
|
||||
args: ["install", "-g", "@new-scope/pi"],
|
||||
display: "bun install -g @new-scope/pi",
|
||||
args: ["install", "-g", "--ignore-scripts", "@new-scope/pi"],
|
||||
display: "bun install -g --ignore-scripts @new-scope/pi",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -2,8 +2,8 @@ import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import type { ExtensionContext } from "../src/core/extensions/types.js";
|
||||
import { createEditToolDefinition } from "../src/core/tools/edit.js";
|
||||
import type { ExtensionContext } from "../src/core/extensions/types.ts";
|
||||
import { createEditToolDefinition } from "../src/core/tools/edit.ts";
|
||||
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { Container, type Terminal, Text, TUI } from "@earendil-works/pi-tui";
|
||||
import { afterEach, beforeAll, describe, expect, it } from "vitest";
|
||||
import { createEditToolDefinition } from "../src/core/tools/edit.js";
|
||||
import { computeEditsDiff, type Edit } from "../src/core/tools/edit-diff.js";
|
||||
import { ToolExecutionComponent } from "../src/modes/interactive/components/tool-execution.js";
|
||||
import { initTheme } from "../src/modes/interactive/theme/theme.js";
|
||||
import { createEditToolDefinition } from "../src/core/tools/edit.ts";
|
||||
import { computeEditsDiff, type Edit } from "../src/core/tools/edit-diff.ts";
|
||||
import { ToolExecutionComponent } from "../src/modes/interactive/components/tool-execution.ts";
|
||||
import { initTheme } from "../src/modes/interactive/theme/theme.ts";
|
||||
|
||||
class FakeTerminal implements Terminal {
|
||||
columns = 80;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Component } from "@earendil-works/pi-tui";
|
||||
import { readFileSync } from "fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { ansiLinesToHtml } from "../src/core/export-html/ansi-to-html.js";
|
||||
import { createToolHtmlRenderer } from "../src/core/export-html/tool-renderer.js";
|
||||
import type { ToolDefinition } from "../src/core/extensions/types.js";
|
||||
import type { Theme } from "../src/modes/interactive/theme/theme.js";
|
||||
import { ansiLinesToHtml } from "../src/core/export-html/ansi-to-html.ts";
|
||||
import { createToolHtmlRenderer } from "../src/core/export-html/tool-renderer.ts";
|
||||
import type { ToolDefinition } from "../src/core/extensions/types.ts";
|
||||
import type { Theme } from "../src/modes/interactive/theme/theme.ts";
|
||||
|
||||
describe("export HTML tool output whitespace", () => {
|
||||
it("preserves whitespace for plain-text tool output lines without preserving template whitespace", () => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { discoverAndLoadExtensions } from "../src/core/extensions/loader.js";
|
||||
import { discoverAndLoadExtensions } from "../src/core/extensions/loader.ts";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
@@ -440,7 +440,7 @@ describe("extensions discovery", () => {
|
||||
fs.writeFileSync(explicitPath, extensionCodeWithTool("explicit"));
|
||||
|
||||
// Use loadExtensions directly to skip discovery
|
||||
const { loadExtensions } = await import("../src/core/extensions/loader.js");
|
||||
const { loadExtensions } = await import("../src/core/extensions/loader.ts");
|
||||
const result = await loadExtensions([explicitPath], tempDir);
|
||||
|
||||
expect(result.errors).toHaveLength(0);
|
||||
@@ -454,7 +454,7 @@ describe("extensions discovery", () => {
|
||||
fs.writeFileSync(path.join(extensionsDir, "discovered.ts"), extensionCode);
|
||||
|
||||
// Use loadExtensions directly with empty paths
|
||||
const { loadExtensions } = await import("../src/core/extensions/loader.js");
|
||||
const { loadExtensions } = await import("../src/core/extensions/loader.ts");
|
||||
const result = await loadExtensions([], tempDir);
|
||||
|
||||
expect(result.errors).toHaveLength(0);
|
||||
|
||||
@@ -2,11 +2,11 @@ import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { discoverAndLoadExtensions } from "../src/core/extensions/loader.js";
|
||||
import { ExtensionRunner } from "../src/core/extensions/runner.js";
|
||||
import { ModelRegistry } from "../src/core/model-registry.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { discoverAndLoadExtensions } from "../src/core/extensions/loader.ts";
|
||||
import { ExtensionRunner } from "../src/core/extensions/runner.ts";
|
||||
import { ModelRegistry } from "../src/core/model-registry.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
|
||||
describe("Input Event", () => {
|
||||
let tempDir: string;
|
||||
|
||||
@@ -6,13 +6,13 @@ import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { createExtensionRuntime, discoverAndLoadExtensions } from "../src/core/extensions/loader.js";
|
||||
import { ExtensionRunner } from "../src/core/extensions/runner.js";
|
||||
import type { ExtensionActions, ExtensionContextActions, ProviderConfig } from "../src/core/extensions/types.js";
|
||||
import { KeybindingsManager, type KeyId } from "../src/core/keybindings.js";
|
||||
import { ModelRegistry } from "../src/core/model-registry.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { createExtensionRuntime, discoverAndLoadExtensions } from "../src/core/extensions/loader.ts";
|
||||
import { ExtensionRunner } from "../src/core/extensions/runner.ts";
|
||||
import type { ExtensionActions, ExtensionContextActions, ProviderConfig } from "../src/core/extensions/types.ts";
|
||||
import { KeybindingsManager, type KeyId } from "../src/core/keybindings.ts";
|
||||
import { ModelRegistry } from "../src/core/model-registry.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
|
||||
describe("ExtensionRunner", () => {
|
||||
let tempDir: string;
|
||||
|
||||
@@ -2,9 +2,9 @@ import { access, mkdtemp, readFile, rm, symlink, writeFile } from "node:fs/promi
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { createEditTool } from "../src/core/tools/edit.js";
|
||||
import { withFileMutationQueue } from "../src/core/tools/file-mutation-queue.js";
|
||||
import { createWriteTool } from "../src/core/tools/write.js";
|
||||
import { createEditTool } from "../src/core/tools/edit.ts";
|
||||
import { withFileMutationQueue } from "../src/core/tools/file-mutation-queue.ts";
|
||||
import { createWriteTool } from "../src/core/tools/write.ts";
|
||||
|
||||
function delay(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
@@ -37,7 +37,7 @@ vi.mock("child_process", () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
import { FooterDataProvider } from "../src/core/footer-data-provider.js";
|
||||
import { FooterDataProvider } from "../src/core/footer-data-provider.ts";
|
||||
|
||||
type WorktreeFixture = {
|
||||
worktreeDir: string;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { visibleWidth } from "@earendil-works/pi-tui";
|
||||
import { beforeAll, describe, expect, it } from "vitest";
|
||||
import type { AgentSession } from "../src/core/agent-session.js";
|
||||
import type { ReadonlyFooterDataProvider } from "../src/core/footer-data-provider.js";
|
||||
import { FooterComponent } from "../src/modes/interactive/components/footer.js";
|
||||
import { initTheme } from "../src/modes/interactive/theme/theme.js";
|
||||
import type { AgentSession } from "../src/core/agent-session.ts";
|
||||
import type { ReadonlyFooterDataProvider } from "../src/core/footer-data-provider.ts";
|
||||
import { FooterComponent } from "../src/modes/interactive/components/footer.ts";
|
||||
import { initTheme } from "../src/modes/interactive/theme/theme.ts";
|
||||
|
||||
type AssistantUsage = {
|
||||
input: number;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parseFrontmatter, stripFrontmatter } from "../src/utils/frontmatter.js";
|
||||
import { parseFrontmatter, stripFrontmatter } from "../src/utils/frontmatter.ts";
|
||||
|
||||
describe("parseFrontmatter", () => {
|
||||
it("parses keys, strips quotes, and returns body", () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parseGitUrl } from "../src/utils/git.js";
|
||||
import { parseGitUrl } from "../src/utils/git.ts";
|
||||
|
||||
describe("Git URL Parsing", () => {
|
||||
describe("protocol URLs (accepted without git: prefix)", () => {
|
||||
|
||||
@@ -12,8 +12,8 @@ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { DefaultPackageManager } from "../src/core/package-manager.js";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
import { DefaultPackageManager } from "../src/core/package-manager.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
|
||||
// Helper to run git commands in a directory
|
||||
function git(args: string[], cwd: string): string {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { convertToPng } from "../src/utils/image-convert.js";
|
||||
import { formatDimensionNote, resizeImage } from "../src/utils/image-resize.js";
|
||||
import { convertToPng } from "../src/utils/image-convert.ts";
|
||||
import { formatDimensionNote, resizeImage } from "../src/utils/image-resize.ts";
|
||||
|
||||
// Small 2x2 red PNG image (base64) - generated with ImageMagick
|
||||
const TINY_PNG =
|
||||
|
||||
@@ -8,9 +8,9 @@ vi.mock("../src/utils/image-resize.js", () => ({
|
||||
formatDimensionNote: vi.fn(() => undefined),
|
||||
}));
|
||||
|
||||
import { processFileArguments } from "../src/cli/file-processor.js";
|
||||
import { createReadTool } from "../src/core/tools/read.js";
|
||||
import { resizeImage } from "../src/utils/image-resize.js";
|
||||
import { processFileArguments } from "../src/cli/file-processor.ts";
|
||||
import { createReadTool } from "../src/core/tools/read.ts";
|
||||
import { resizeImage } from "../src/utils/image-resize.ts";
|
||||
|
||||
const TINY_PNG_BASE64 =
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import type { Args } from "../src/cli/args.js";
|
||||
import { buildInitialMessage } from "../src/cli/initial-message.js";
|
||||
import type { Args } from "../src/cli/args.ts";
|
||||
import { buildInitialMessage } from "../src/cli/initial-message.ts";
|
||||
|
||||
function createArgs(messages: string[] = []): Args {
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.js";
|
||||
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.ts";
|
||||
|
||||
function createSettingsManager(warnings: { anthropicExtraUsage?: boolean } = {}) {
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.js";
|
||||
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.ts";
|
||||
|
||||
type CloneCommandContext = {
|
||||
sessionManager: { getLeafId: () => string | null };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.js";
|
||||
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.ts";
|
||||
|
||||
describe("InteractiveMode compaction events", () => {
|
||||
test("rebuilds chat and appends a synthetic compaction summary at the bottom", async () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { SessionImportFileNotFoundError } from "../src/core/agent-session-runtime.js";
|
||||
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.js";
|
||||
import { SessionImportFileNotFoundError } from "../src/core/agent-session-runtime.ts";
|
||||
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.ts";
|
||||
|
||||
type PathCommand = "/export" | "/import";
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@ import { homedir } from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { type AutocompleteProvider, CombinedAutocompleteProvider, Container } from "@earendil-works/pi-tui";
|
||||
import { beforeAll, describe, expect, test, vi } from "vitest";
|
||||
import type { AutocompleteProviderFactory } from "../src/core/extensions/types.js";
|
||||
import type { SourceInfo } from "../src/core/source-info.js";
|
||||
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.js";
|
||||
import { initTheme } from "../src/modes/interactive/theme/theme.js";
|
||||
import type { AutocompleteProviderFactory } from "../src/core/extensions/types.ts";
|
||||
import type { SourceInfo } from "../src/core/source-info.ts";
|
||||
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.ts";
|
||||
import { initTheme } from "../src/modes/interactive/theme/theme.ts";
|
||||
|
||||
function renderLastLine(container: Container, width = 120): string {
|
||||
const last = container.children[container.children.length - 1];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { afterEach, describe, expect, test, vi } from "vitest";
|
||||
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.js";
|
||||
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.ts";
|
||||
|
||||
type FakeUi = {
|
||||
start: () => void;
|
||||
|
||||
@@ -2,9 +2,9 @@ import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { ENV_AGENT_DIR } from "../src/config.js";
|
||||
import { KeybindingsManager } from "../src/core/keybindings.js";
|
||||
import { runMigrations } from "../src/migrations.js";
|
||||
import { ENV_AGENT_DIR } from "../src/config.ts";
|
||||
import { KeybindingsManager } from "../src/core/keybindings.ts";
|
||||
import { runMigrations } from "../src/migrations.ts";
|
||||
|
||||
describe("keybindings migration", () => {
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
@@ -5,8 +5,8 @@ import type { AnthropicMessagesCompat, Api, Context, Model, OpenAICompletionsCom
|
||||
import { getApiProvider } from "@earendil-works/pi-ai";
|
||||
import { getOAuthProvider } from "@earendil-works/pi-ai/oauth";
|
||||
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { clearApiKeyCache, ModelRegistry, type ProviderConfigInput } from "../src/core/model-registry.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { clearApiKeyCache, ModelRegistry, type ProviderConfigInput } from "../src/core/model-registry.ts";
|
||||
|
||||
describe("ModelRegistry", () => {
|
||||
let tempDir: string;
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
findInitialModel,
|
||||
parseModelPattern,
|
||||
resolveCliModel,
|
||||
} from "../src/core/model-resolver.js";
|
||||
} from "../src/core/model-resolver.ts";
|
||||
|
||||
// Mock models for testing
|
||||
const mockModels: Model<"anthropic-messages">[] = [
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { setKeybindings } from "@earendil-works/pi-tui";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { KeybindingsManager } from "../src/core/keybindings.js";
|
||||
import { BUILT_IN_PROVIDER_DISPLAY_NAMES } from "../src/core/provider-display-names.js";
|
||||
import { OAuthSelectorComponent } from "../src/modes/interactive/components/oauth-selector.js";
|
||||
import { isApiKeyLoginProvider } from "../src/modes/interactive/interactive-mode.js";
|
||||
import { initTheme } from "../src/modes/interactive/theme/theme.js";
|
||||
import { stripAnsi } from "../src/utils/ansi.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { KeybindingsManager } from "../src/core/keybindings.ts";
|
||||
import { BUILT_IN_PROVIDER_DISPLAY_NAMES } from "../src/core/provider-display-names.ts";
|
||||
import { OAuthSelectorComponent } from "../src/modes/interactive/components/oauth-selector.ts";
|
||||
import { isApiKeyLoginProvider } from "../src/modes/interactive/interactive-mode.ts";
|
||||
import { initTheme } from "../src/modes/interactive/theme/theme.ts";
|
||||
import { stripAnsi } from "../src/utils/ansi.ts";
|
||||
|
||||
const originalOpenAiApiKey = process.env.OPENAI_API_KEY;
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ import { mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "no
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { ENV_AGENT_DIR, PACKAGE_NAME, VERSION } from "../src/config.js";
|
||||
import { main } from "../src/main.js";
|
||||
import { ENV_AGENT_DIR, PACKAGE_NAME, VERSION } from "../src/config.ts";
|
||||
import { main } from "../src/main.ts";
|
||||
|
||||
describe("package commands", () => {
|
||||
let tempDir: string;
|
||||
|
||||
@@ -2,8 +2,8 @@ import { mkdirSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { DefaultPackageManager } from "../src/core/package-manager.js";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
import { DefaultPackageManager } from "../src/core/package-manager.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
|
||||
describe("Package Manager git source parsing", () => {
|
||||
let tempDir: string;
|
||||
|
||||
@@ -4,8 +4,8 @@ import { tmpdir } from "node:os";
|
||||
import { join, relative } from "node:path";
|
||||
import { PassThrough } from "node:stream";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { DefaultPackageManager, type ProgressEvent, type ResolvedResource } from "../src/core/package-manager.js";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
import { DefaultPackageManager, type ProgressEvent, type ResolvedResource } from "../src/core/package-manager.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
|
||||
function normalizeForMatch(value: string): string {
|
||||
return value.replace(/\\/g, "/");
|
||||
@@ -1747,7 +1747,7 @@ Content`,
|
||||
// Main entry point
|
||||
writeFileSync(
|
||||
join(pkgDir, "extensions", "subagent", "index.ts"),
|
||||
`import { helper } from "./agents.js";
|
||||
`import { helper } from "./agents.ts";
|
||||
export default function(api) { api.registerTool({ name: "test", description: "test", execute: async () => helper() }); }`,
|
||||
);
|
||||
// Helper module (should NOT be loaded as standalone extension)
|
||||
@@ -1803,7 +1803,7 @@ export default function(api) { api.registerTool({ name: "test", description: "te
|
||||
// Subdirectory with index.ts + helpers
|
||||
writeFileSync(
|
||||
join(pkgDir, "extensions", "complex", "index.ts"),
|
||||
"import { a } from './a.js'; export default function(api) {}",
|
||||
"import { a } from './a.ts'; export default function(api) {}",
|
||||
);
|
||||
writeFileSync(join(pkgDir, "extensions", "complex", "a.ts"), "export const a = 1;");
|
||||
writeFileSync(join(pkgDir, "extensions", "complex", "b.ts"), "export const b = 2;");
|
||||
|
||||
@@ -2,7 +2,7 @@ import { mkdtempSync, readdirSync, rmdirSync, unlinkSync, writeFileSync } from "
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { expandPath, resolveReadPath, resolveToCwd } from "../src/core/tools/path-utils.js";
|
||||
import { expandPath, resolveReadPath, resolveToCwd } from "../src/core/tools/path-utils.ts";
|
||||
|
||||
describe("path-utils", () => {
|
||||
describe("expandPath", () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { mkdirSync, mkdtempSync, realpathSync, rmSync, symlinkSync, writeFileSyn
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { canonicalizePath, getCwdRelativePath, isLocalPath } from "../src/utils/paths.js";
|
||||
import { canonicalizePath, getCwdRelativePath, isLocalPath } from "../src/utils/paths.ts";
|
||||
|
||||
let tempDir: string;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { getPiUserAgent } from "../src/utils/pi-user-agent.js";
|
||||
import { getPiUserAgent } from "../src/utils/pi-user-agent.ts";
|
||||
|
||||
describe("getPiUserAgent", () => {
|
||||
it("formats the user agent expected by pi.dev", () => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
isSafeCommand,
|
||||
markCompletedSteps,
|
||||
type TodoItem,
|
||||
} from "../examples/extensions/plan-mode/utils.js";
|
||||
} from "../examples/extensions/plan-mode/utils.ts";
|
||||
|
||||
describe("isSafeCommand", () => {
|
||||
describe("safe commands", () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { AssistantMessage, ImageContent } from "@earendil-works/pi-ai";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import type { SessionShutdownEvent } from "../src/index.js";
|
||||
import { runPrintMode } from "../src/modes/print-mode.js";
|
||||
import type { SessionShutdownEvent } from "../src/index.ts";
|
||||
import { runPrintMode } from "../src/modes/print-mode.ts";
|
||||
|
||||
type EmitEvent = SessionShutdownEvent;
|
||||
|
||||
|
||||
@@ -12,13 +12,13 @@ import { mkdirSync, rmSync, writeFileSync } from "fs";
|
||||
import { tmpdir } from "os";
|
||||
import { join } from "path";
|
||||
import { afterAll, describe, expect, test } from "vitest";
|
||||
import { getAgentDir } from "../src/config.js";
|
||||
import { getAgentDir } from "../src/config.ts";
|
||||
import {
|
||||
expandPromptTemplate,
|
||||
loadPromptTemplates,
|
||||
parseCommandArgs,
|
||||
substituteArgs,
|
||||
} from "../src/core/prompt-templates.js";
|
||||
} from "../src/core/prompt-templates.ts";
|
||||
|
||||
// ============================================================================
|
||||
// substituteArgs
|
||||
|
||||
@@ -2,14 +2,14 @@ import { mkdirSync, readFileSync, rmSync, symlinkSync, writeFileSync } from "nod
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { ExtensionRunner } from "../src/core/extensions/runner.js";
|
||||
import { ModelRegistry } from "../src/core/model-registry.js";
|
||||
import { DefaultResourceLoader } from "../src/core/resource-loader.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
import type { Skill } from "../src/core/skills.js";
|
||||
import { createSyntheticSourceInfo } from "../src/core/source-info.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { ExtensionRunner } from "../src/core/extensions/runner.ts";
|
||||
import { ModelRegistry } from "../src/core/model-registry.ts";
|
||||
import { DefaultResourceLoader } from "../src/core/resource-loader.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
import type { Skill } from "../src/core/skills.ts";
|
||||
import { createSyntheticSourceInfo } from "../src/core/source-info.ts";
|
||||
|
||||
describe("DefaultResourceLoader", () => {
|
||||
let tempDir: string;
|
||||
|
||||
@@ -6,7 +6,7 @@ vi.mock("node:fs", () => ({
|
||||
readFileSync,
|
||||
}));
|
||||
|
||||
const { restoreSandboxEnv } = await import("../src/bun/restore-sandbox-env.js");
|
||||
const { restoreSandboxEnv } = await import("../src/bun/restore-sandbox-env.ts");
|
||||
|
||||
describe("restoreSandboxEnv", () => {
|
||||
it("does nothing when not running under bun", () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { RpcClient } from "../src/modes/rpc/rpc-client.js";
|
||||
import { RpcClient } from "../src/modes/rpc/rpc-client.ts";
|
||||
|
||||
type RpcClientPrivate = {
|
||||
send: (command: { type: string }) => Promise<unknown>;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { dirname, join } from "node:path";
|
||||
import * as readline from "node:readline";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { RpcClient } from "../src/modes/rpc/rpc-client.js";
|
||||
import { RpcClient } from "../src/modes/rpc/rpc-client.ts";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Readable } from "node:stream";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { attachJsonlLineReader, serializeJsonLine } from "../src/modes/rpc/jsonl.js";
|
||||
import { attachJsonlLineReader, serializeJsonLine } from "../src/modes/rpc/jsonl.ts";
|
||||
|
||||
describe("RPC JSONL framing", () => {
|
||||
test("serializes strict JSONL records without escaping Unicode separators", () => {
|
||||
|
||||
@@ -10,14 +10,14 @@ import {
|
||||
type Model,
|
||||
} from "@earendil-works/pi-ai";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { AgentSession } from "../src/core/agent-session.js";
|
||||
import type { AgentSessionRuntime } from "../src/core/agent-session-runtime.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { ModelRegistry } from "../src/core/model-registry.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
import { runRpcMode } from "../src/modes/rpc/rpc-mode.js";
|
||||
import { createTestResourceLoader } from "./utilities.js";
|
||||
import { AgentSession } from "../src/core/agent-session.ts";
|
||||
import type { AgentSessionRuntime } from "../src/core/agent-session-runtime.ts";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { ModelRegistry } from "../src/core/model-registry.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
import { runRpcMode } from "../src/modes/rpc/rpc-mode.ts";
|
||||
import { createTestResourceLoader } from "./utilities.ts";
|
||||
|
||||
const rpcIo = vi.hoisted(() => ({
|
||||
outputLines: [] as string[],
|
||||
|
||||
@@ -4,7 +4,7 @@ import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import type { AgentEvent } from "@earendil-works/pi-agent-core";
|
||||
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
||||
import { RpcClient } from "../src/modes/rpc/rpc-client.js";
|
||||
import { RpcClient } from "../src/modes/rpc/rpc-client.ts";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env tsx
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Manual SDK probe for OpenAI Codex prompt caching through the tool loop.
|
||||
*
|
||||
@@ -24,15 +24,15 @@ import {
|
||||
import {
|
||||
getOpenAICodexWebSocketDebugStats,
|
||||
streamSimpleOpenAICodexResponses,
|
||||
} from "../../ai/src/providers/openai-codex-responses.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { createExtensionRuntime } from "../src/core/extensions/loader.js";
|
||||
import type { ToolDefinition } from "../src/core/extensions/types.js";
|
||||
import { ModelRegistry } from "../src/core/model-registry.js";
|
||||
import type { ResourceLoader } from "../src/core/resource-loader.js";
|
||||
import { createAgentSession } from "../src/core/sdk.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
} from "../../ai/src/providers/openai-codex-responses.ts";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { createExtensionRuntime } from "../src/core/extensions/loader.ts";
|
||||
import type { ToolDefinition } from "../src/core/extensions/types.ts";
|
||||
import { ModelRegistry } from "../src/core/model-registry.ts";
|
||||
import type { ResourceLoader } from "../src/core/resource-loader.ts";
|
||||
import { createAgentSession } from "../src/core/sdk.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
|
||||
type Transport = "sse" | "websocket" | "websocket-cached" | "auto";
|
||||
|
||||
@@ -123,7 +123,7 @@ function parseArgs(argv: string[]): Args {
|
||||
}
|
||||
|
||||
function printHelp(): void {
|
||||
console.log(`Usage: npx tsx test/sdk-codex-cache-probe-tool-loop.ts [options]
|
||||
console.log(`Usage: node test/sdk-codex-cache-probe-tool-loop.ts [options]
|
||||
|
||||
Options:
|
||||
--turns <n> Number of turns to run. Must be between ${MIN_TURNS} and ${MAX_TURNS}. Default: ${DEFAULT_TURNS}
|
||||
|
||||
@@ -9,11 +9,11 @@ import {
|
||||
type SimpleStreamOptions,
|
||||
} from "@earendil-works/pi-ai";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||
import { ModelRegistry } from "../src/core/model-registry.js";
|
||||
import { createAgentSession } from "../src/core/sdk.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { ModelRegistry } from "../src/core/model-registry.ts";
|
||||
import { createAgentSession } from "../src/core/sdk.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
|
||||
describe("createAgentSession OpenRouter attribution headers", () => {
|
||||
let tempDir: string;
|
||||
|
||||
@@ -3,8 +3,8 @@ import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { getModel } from "@earendil-works/pi-ai";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { createAgentSession } from "../src/core/sdk.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { createAgentSession } from "../src/core/sdk.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
|
||||
describe("createAgentSession session manager defaults", () => {
|
||||
let tempDir: string;
|
||||
|
||||
@@ -2,11 +2,11 @@ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { createExtensionRuntime } from "../src/core/extensions/loader.js";
|
||||
import type { ResourceLoader } from "../src/core/resource-loader.js";
|
||||
import { createAgentSession } from "../src/core/sdk.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { createSyntheticSourceInfo } from "../src/core/source-info.js";
|
||||
import { createExtensionRuntime } from "../src/core/extensions/loader.ts";
|
||||
import type { ResourceLoader } from "../src/core/resource-loader.ts";
|
||||
import { createAgentSession } from "../src/core/sdk.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import { createSyntheticSourceInfo } from "../src/core/source-info.ts";
|
||||
|
||||
describe("createAgentSession skills option", () => {
|
||||
let tempDir: string;
|
||||
|
||||
@@ -2,9 +2,9 @@ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { type CreateAgentSessionRuntimeFactory, createAgentSessionRuntime } from "../src/core/agent-session-runtime.js";
|
||||
import { getMissingSessionCwdIssue, MissingSessionCwdError } from "../src/core/session-cwd.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { type CreateAgentSessionRuntimeFactory, createAgentSessionRuntime } from "../src/core/agent-session-runtime.ts";
|
||||
import { getMissingSessionCwdIssue, MissingSessionCwdError } from "../src/core/session-cwd.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
|
||||
function createTempDir(name: string): string {
|
||||
const dir = join(tmpdir(), `${name}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
||||
|
||||
@@ -3,9 +3,9 @@ import { stat } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import type { SessionHeader } from "../src/core/session-manager.js";
|
||||
import { SessionManager } from "../src/core/session-manager.js";
|
||||
import { initTheme } from "../src/modes/interactive/theme/theme.js";
|
||||
import type { SessionHeader } from "../src/core/session-manager.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import { initTheme } from "../src/modes/interactive/theme/theme.ts";
|
||||
|
||||
function createSessionFile(path: string): void {
|
||||
const header: SessionHeader = {
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
type SessionEntry,
|
||||
type SessionMessageEntry,
|
||||
type ThinkingLevelChangeEntry,
|
||||
} from "../../src/core/session-manager.js";
|
||||
} from "../../src/core/session-manager.ts";
|
||||
|
||||
function msg(id: string, parentId: string | null, role: "user" | "assistant", text: string): SessionMessageEntry {
|
||||
const base = { type: "message" as const, id, parentId, timestamp: "2025-01-01T00:00:00Z" };
|
||||
|
||||
@@ -2,7 +2,7 @@ import { mkdtempSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { SessionManager } from "../../src/core/session-manager.js";
|
||||
import { SessionManager } from "../../src/core/session-manager.ts";
|
||||
|
||||
const UUID_V7_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
||||
import { tmpdir } from "os";
|
||||
import { join } from "path";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { findMostRecentSession, loadEntriesFromFile, SessionManager } from "../../src/core/session-manager.js";
|
||||
import { findMostRecentSession, loadEntriesFromFile, SessionManager } from "../../src/core/session-manager.ts";
|
||||
|
||||
describe("loadEntriesFromFile", () => {
|
||||
let tempDir: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { type LabelEntry, SessionManager } from "../../src/core/session-manager.js";
|
||||
import { type LabelEntry, SessionManager } from "../../src/core/session-manager.ts";
|
||||
|
||||
describe("SessionManager labels", () => {
|
||||
it("sets and gets labels", () => {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user