merge: integrate remote main
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled

This commit is contained in:
2026-09-03 17:19:21 +08:00
58 changed files with 3989 additions and 199 deletions

View File

@@ -1,5 +1,6 @@
import { randomUUID } from 'node:crypto';
import { readFile } from 'node:fs/promises';
import { findPackageJSON } from 'node:module';
import path from 'node:path';
import { pathToFileURL } from 'node:url';
@@ -14,12 +15,36 @@ const runtimeRoot = runtimeRootFromArgs(process.argv.slice(2));
const runtimeModule = (...segments) => pathToFileURL(path.join(runtimeRoot, ...segments)).href;
const runtimePackageUrl = pathToFileURL(path.join(runtimeRoot, 'package.json')).href;
async function runtimePackageModule(packageName) {
const packageJsonPath = findPackageJSON(packageName, runtimePackageUrl);
if (!packageJsonPath) throw new Error(`Runtime package is unavailable: ${packageName}`);
const packageRoot = path.dirname(packageJsonPath);
const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8'));
const rootExport = packageJson.exports?.['.'];
const entry = (
typeof rootExport === 'string'
? rootExport
: rootExport?.import
) ?? packageJson.module ?? packageJson.main;
if (typeof entry !== 'string' || !entry.trim()) {
throw new Error(`Runtime package import entry is unavailable: ${packageName}`);
}
const entryPath = path.resolve(packageRoot, entry);
const entryRelative = path.relative(packageRoot, entryPath);
if (!entryRelative || entryRelative === '..' || entryRelative.startsWith(`..${path.sep}`)
|| path.isAbsolute(entryRelative)) {
throw new Error(`Runtime package import entry escapes its package: ${packageName}`);
}
return pathToFileURL(entryPath).href;
}
const outputGuard = await import(runtimeModule('dist', 'core', 'output-guard.js'));
outputGuard.takeOverStdout();
const piAiModule = await runtimePackageModule('@earendil-works/pi-ai');
const [pi, piAi, httpDispatcher, jsonEvents, jsonl, themeModule, shellModule] = await Promise.all([
import(runtimeModule('dist', 'index.js')),
import(import.meta.resolve('@earendil-works/pi-ai', runtimePackageUrl)),
import(piAiModule),
import(runtimeModule('dist', 'core', 'http-dispatcher.js')),
import(runtimeModule('dist', 'modes', 'json-event.js')),
import(runtimeModule('dist', 'modes', 'rpc', 'jsonl.js')),
@@ -46,6 +71,7 @@ function configureNetwork(settingsManager) {
const SERVER_CHANNEL = '@makelore/server';
const PROTOCOL_VERSION = 1;
const PROJECT_WRITE_LEASE_TOOL_NAMES = new Set(['bash', 'edit', 'write']);
const threads = new Map();
const openingThreads = new Set();
let shuttingDown = false;
@@ -106,6 +132,14 @@ function referencedEnvironmentNames(value) {
return names;
}
function serializeProjectWriteLeaseTools(session) {
session.agent.state.tools = session.agent.state.tools.map((tool) => (
PROJECT_WRITE_LEASE_TOOL_NAMES.has(tool.name) && tool.executionMode !== 'sequential'
? { ...tool, executionMode: 'sequential' }
: tool
));
}
async function credentialStoreFor(options, providerId) {
const models = JSON.parse(await readFile(path.join(options.configDir, 'models.json'), 'utf8'));
const provider = record(record(models)?.providers)?.[providerId];
@@ -370,6 +404,12 @@ class AgentThread {
async rebind() {
const session = this.runtime.session;
// Pi prepares every tool_call hook before starting a parallel tool batch.
// Makelore acquires its project write lease in that hook and releases it in
// tool_result, so two parallel mutation tools would otherwise wait on each
// other before either command can start. Mark only the built-in mutation
// tools sequential; read-only batches retain Pi's parallel execution.
serializeProjectWriteLeaseTools(session);
await session.bindExtensions({
uiContext: this.createExtensionUiContext(),
mode: 'rpc',
@@ -428,7 +468,14 @@ class AgentThread {
this.emit(success(id, 'prompt'));
},
}).catch((error) => {
if (!accepted) this.emit(failure(id, 'prompt', error));
if (!accepted) {
this.emit(failure(id, 'prompt', error));
return;
}
this.emit({
type: 'makelore_thread_error',
code: 'PROMPT_FAILED_AFTER_ACCEPTANCE',
});
});
return undefined;
}
@@ -454,6 +501,7 @@ class AgentThread {
thinkingLevel: session.thinkingLevel,
isStreaming: session.isStreaming,
isCompacting: session.isCompacting,
retryAttempt: session.retryAttempt,
steeringMode: session.steeringMode,
followUpMode: session.followUpMode,
sessionFile: session.sessionFile,