diff --git a/landing/components/release-links.generated.ts b/landing/components/release-links.generated.ts index 6a273c61..99cf515d 100644 --- a/landing/components/release-links.generated.ts +++ b/landing/components/release-links.generated.ts @@ -12,7 +12,7 @@ export type PlatformDownload = { assets: PlatformDownloadAsset[]; }; -export const releaseVersion = "0.4.151"; +export const releaseVersion = "0.4.154"; export const platformDownloads: PlatformDownload[] = [ { @@ -23,13 +23,13 @@ export const platformDownloads: PlatformDownload[] = [ { id: "macos-arm64-dmg", label: "Apple Silicon DMG", - href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/AgentCompany-0.4.151-arm64.dmg", + href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/AgentCompany-0.4.154-arm64.dmg", note: "arm64", }, { id: "macos-x64-dmg", label: "Intel Mac DMG", - href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/AgentCompany-0.4.151.dmg", + href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/AgentCompany-0.4.154.dmg", note: "x64", }, ], @@ -42,7 +42,7 @@ export const platformDownloads: PlatformDownload[] = [ { id: "windows-universal-installer", label: "Windows Universal Installer", - href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/AgentCompany.Setup.0.4.151.exe", + href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/AgentCompany.Setup.0.4.154.exe", note: "arm64 + x64 + ia32", }, ], @@ -55,37 +55,37 @@ export const platformDownloads: PlatformDownload[] = [ { id: "linux-x64-appimage", label: "Linux x64 AppImage", - href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/AgentCompany-0.4.151.AppImage", + href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/AgentCompany-0.4.154.AppImage", note: "x64", }, { id: "linux-x64-deb", label: "Linux x64 DEB", - href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/agent-company_0.4.151_amd64.deb", + href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/agent-company_0.4.154_amd64.deb", note: "x64", }, { id: "linux-x64-rpm", label: "Linux x64 RPM", - href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/agent-company-0.4.151.x86_64.rpm", + href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/agent-company-0.4.154.x86_64.rpm", note: "x64", }, { id: "linux-arm64-appimage", label: "Linux ARM64 AppImage", - href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/AgentCompany-0.4.151-arm64.AppImage", + href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/AgentCompany-0.4.154-arm64.AppImage", note: "arm64", }, { id: "linux-arm64-deb", label: "Linux ARM64 DEB", - href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/agent-company_0.4.151_arm64.deb", + href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/agent-company_0.4.154_arm64.deb", note: "arm64", }, { id: "linux-arm64-rpm", label: "Linux ARM64 RPM", - href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/agent-company-0.4.151.aarch64.rpm", + href: "https://pub-a19a3a2c1d2e4fa3a9c120b184024448.r2.dev/AC/agent-company-0.4.154.aarch64.rpm", note: "arm64", }, ], diff --git a/package.json b/package.json index d6397193..e529c6a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-company", - "version": "0.4.151", + "version": "0.4.154", "private": true, "type": "module", "description": "Cross-platform Electron desktop control plane for local-first AI agent companies.", diff --git a/scripts/release-cloudflare.mjs b/scripts/release-cloudflare.mjs index 6ffaed6a..d09fee40 100644 --- a/scripts/release-cloudflare.mjs +++ b/scripts/release-cloudflare.mjs @@ -11,8 +11,11 @@ const rootDir = join(__dirname, ".."); const packageJsonPath = join(rootDir, "package.json"); const landingDir = join(rootDir, "landing"); const localReleaseDir = join(rootDir, "local-release"); +const bossOutDir = join(rootDir, "boss-build", "build", "out"); const remoteWorkflowFile = ".github/workflows/remote-release-platforms.yml"; const remoteWorkflowName = "remote-release-platforms.yml"; +const remoteBossWorkflowFile = ".github/workflows/remote-boss-platforms.yml"; +const remoteBossWorkflowName = "remote-boss-platforms.yml"; const pagesProjectName = "agentcompany"; const pagesBranch = "main"; const versionPattern = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/; @@ -209,7 +212,21 @@ async function ensureRemoteBuildPrerequisites(repo) { if (!existsSync(join(rootDir, remoteWorkflowFile))) { throw new Error(`Required workflow file missing locally: ${remoteWorkflowFile}`); } + if (!existsSync(join(rootDir, remoteBossWorkflowFile))) { + throw new Error(`Required workflow file missing locally: ${remoteBossWorkflowFile}`); + } await assertRemoteWorkflowExists(repo); + try { + await run("gh", [ + "api", + `repos/${repo.nameWithOwner}/contents/${remoteBossWorkflowFile}?ref=${repo.defaultBranch}`, + ], { capture: true }); + } catch { + throw new Error( + `Remote workflow ${remoteBossWorkflowFile} was not found on ${repo.defaultBranch}. ` + + "Commit and push this Boss automation workflow before using scripts/release-cloudflare.mjs.", + ); + } await run("gh", ["auth", "status"], { capture: true }); await run("npx", ["wrangler", "whoami", "--json"], { capture: true }); } @@ -281,7 +298,7 @@ async function createSnapshotBranch(repo, branchName, version) { return tempDir; } -async function waitForWorkflowRun(repo, branchName, timeoutMs) { +async function waitForWorkflowRun(repo, workflowName, branchName, timeoutMs) { const startedAt = Date.now(); while (Date.now() - startedAt < timeoutMs) { const runs = await ghJson([ @@ -290,7 +307,7 @@ async function waitForWorkflowRun(repo, branchName, timeoutMs) { "--repo", repo.nameWithOwner, "--workflow", - remoteWorkflowName, + workflowName, "--branch", branchName, "--limit", @@ -322,6 +339,24 @@ async function dispatchRemoteWorkflow(repo, branchName, releaseTag, releaseTitle ]); } +async function dispatchRemoteBossWorkflow(repo, branchName, version, releaseTag, releaseTitle) { + await run("gh", [ + "workflow", + "run", + remoteBossWorkflowName, + "--repo", + repo.nameWithOwner, + "--ref", + branchName, + "-f", + `version=${version}`, + "-f", + `release_tag=${releaseTag}`, + "-f", + `release_title=${releaseTitle}`, + ]); +} + async function waitForWorkflowCompletion(repo, runId, timeoutMs) { const startedAt = Date.now(); while (Date.now() - startedAt < timeoutMs) { @@ -356,6 +391,20 @@ async function downloadRemoteAssets(repo, releaseTag) { return downloadRoot; } +async function stageBossRemoteAssets(downloadRoot) { + await mkdir(bossOutDir, { recursive: true }); + const files = await readdir(downloadRoot); + for (const fileName of files) { + if (fileName.endsWith(".exe") || fileName.endsWith(".blockmap")) { + await cp(join(downloadRoot, fileName), join(bossOutDir, fileName)); + } + } +} + +async function buildBossMac(version) { + await run("node", ["boss-build/build-boss-edition.mjs", `--platform=mac`, `--version=${version}`]); +} + async function stageRemoteAssets(downloadRoot) { const winDir = join(localReleaseDir, "win"); const linuxDir = join(localReleaseDir, "linux"); @@ -505,7 +554,7 @@ async function main() { try { await dispatchRemoteWorkflow(repo, branchName, releaseTag, releaseTitle); - const runSummary = await waitForWorkflowRun(repo, branchName, 60_000); + const runSummary = await waitForWorkflowRun(repo, remoteWorkflowName, branchName, 60_000); const runInfo = await waitForWorkflowCompletion(repo, runSummary.databaseId, 45 * 60 * 1000); if (runInfo.conclusion !== "success") { throw new Error(`Remote workflow failed: ${runInfo.url}`); @@ -519,8 +568,23 @@ async function main() { const deployUrl = await buildAndDeployLanding(); await verifyPublishedAssets(nextVersion, deployUrl); + await mkdir(bossOutDir, { recursive: true }); + await rm(join(bossOutDir, "AgentCompany-Boss-Setup-" + nextVersion + ".exe"), { force: true }).catch(() => {}); + const bossReleaseTag = `boss-transport-${nextVersion}-${timestamp}`; + const bossReleaseTitle = `Boss Release ${nextVersion}`; + await dispatchRemoteBossWorkflow(repo, branchName, nextVersion, bossReleaseTag, bossReleaseTitle); + await buildBossMac(nextVersion); + const bossRunSummary = await waitForWorkflowRun(repo, remoteBossWorkflowName, branchName, 60_000); + const bossRunInfo = await waitForWorkflowCompletion(repo, bossRunSummary.databaseId, 30 * 60 * 1000); + if (bossRunInfo.conclusion !== "success") { + throw new Error(`Boss remote workflow failed: ${bossRunInfo.url}`); + } + const bossDownloadRoot = await downloadRemoteAssets(repo, bossReleaseTag); + await stageBossRemoteAssets(bossDownloadRoot); + if (!options.keepTransportRelease) { await cleanupRemoteTransportRelease(repo, releaseTag); + await cleanupRemoteTransportRelease(repo, bossReleaseTag); } if (options.deleteRemoteBranch) { await cleanupRemoteBranch(repo, branchName); @@ -532,6 +596,7 @@ async function main() { version: nextVersion, remoteBranch: branchName, remoteWorkflowRun: runInfo.url, + bossOutDir, releaseTag, deployUrl, updateUrl: process.env.AGENTCOMPANY_UPDATE_URL ?? null, diff --git a/src/main/connector-definitions.ts b/src/main/connector-definitions.ts index 2e7f2b48..4672790d 100644 --- a/src/main/connector-definitions.ts +++ b/src/main/connector-definitions.ts @@ -29,7 +29,7 @@ const claudeCompatModule: ServerAdapterModule = { execute: claudeExecute, testEnvironment: claudeTestEnvironment, sessionCodec: claudeSessionCodec, - nativeContextManagement: true, + nativeContextManagement: false, models: claudeModels, }; diff --git a/src/main/session-management-service.ts b/src/main/session-management-service.ts index 78125f43..0f081215 100644 --- a/src/main/session-management-service.ts +++ b/src/main/session-management-service.ts @@ -10,7 +10,7 @@ interface SessionManagementDependencies { // D-01/D-02: Exported for testing and external override. Conservative defaults. export const DEFAULT_THRESHOLDS = { maxRuns: 20, - maxTokens: 500_000, + maxTokens: 150_000, maxAgeHours: 72, }; diff --git a/src/main/worker/run-log-filter.ts b/src/main/worker/run-log-filter.ts new file mode 100644 index 00000000..6eb23296 --- /dev/null +++ b/src/main/worker/run-log-filter.ts @@ -0,0 +1,58 @@ +/** + * Run-log noise filter. + * + * CLI adapters emit stdout/stderr containing system-level JSON messages, + * hook lifecycle events, skill injection notices, and assistant internals + * (thinking/tool_use). This module strips those lines before they reach + * the user-facing run log on disk. + */ + +const RE_SKILL_INJECTION = /^\[agent-company\] Injected managed skill /; +const RE_OMC_HOOK = /^\[omc:hooks\]/; +const RE_AGENT_COMPANY_LOG = /^\[agent-company\] (?:heartbeat|hook_|system_)/; + +/** Matches system JSON except `init` subtype (which carries session metadata). */ +const RE_SYSTEM_JSON = /^\{"type"\s*:\s*"system"\s*,\s*"subtype"\s*:\s*"(?!init")/; + +/** Matches assistant thinking/tool_use JSON — internal reasoning, not user-visible output. */ +const RE_ASSISTANT_INTERNAL = + /^\{"type"\s*:\s*"assistant"\s*,\s*"subtype"\s*:\s*"(?:thinking|tool_use)"/; + +const RE_COMPACTING = /^Compacting conversation\.\.\./; + +function isNoisyLine(line: string): boolean { + const trimmed = line.trimStart(); + if (trimmed.length === 0) return false; + + return ( + RE_SKILL_INJECTION.test(trimmed) || + RE_OMC_HOOK.test(trimmed) || + RE_AGENT_COMPANY_LOG.test(trimmed) || + RE_SYSTEM_JSON.test(trimmed) || + RE_ASSISTANT_INTERNAL.test(trimmed) || + RE_COMPACTING.test(trimmed) + ); +} + +/** + * Filters a raw log chunk by removing noisy lines. + * @returns The cleaned chunk, or `null` if the entire chunk is noise. + */ +export function filterRunLogChunk( + _stream: "stdout" | "stderr", + chunk: string, +): string | null { + const lines = chunk.split("\n"); + const kept: string[] = []; + + for (const line of lines) { + if (!isNoisyLine(line)) { + kept.push(line); + } + } + + if (kept.length === 0) return null; + + const result = kept.join("\n"); + return result.length === 0 ? null : result; +} diff --git a/src/main/worker/worker-message-handler.ts b/src/main/worker/worker-message-handler.ts index 9c780d71..8efe8c46 100644 --- a/src/main/worker/worker-message-handler.ts +++ b/src/main/worker/worker-message-handler.ts @@ -3,6 +3,7 @@ import { getConnectorDefinition } from "../connectors"; import type { WorkerServiceDependencies, workerMessageSchema } from "./worker-types"; import { createCircuitBreaker } from "./circuit-breaker"; import { handleFailureEscalation, handleSuccessReset } from "./failure-escalation"; +import { filterRunLogChunk } from "./run-log-filter"; const LEASE_RENEWAL_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes @@ -61,7 +62,10 @@ export async function handleWorkerMessage( } if (event.type === "run-log") { - await db.appendRunLog(event.runId, event.stream, event.chunk); + const filtered = filterRunLogChunk(event.stream, event.chunk); + if (filtered === null) return; + + await db.appendRunLog(event.runId, event.stream, filtered); const runState = db.getRunAuthState(event.runId); if (runState) { persistRunEvent(db, logger, { @@ -70,10 +74,10 @@ export async function handleWorkerMessage( agentId: runState.agentId, eventType: "run_log", stream: event.stream, - message: event.chunk, + message: filtered, }); } - emitEvent(event); + emitEvent({ ...event, chunk: filtered }); // Throttled lease renewal: keep execution lock alive for long-running tasks. // Without this, tasks exceeding the 15-minute stale lock timeout get reclaimed.