chore: clear low-risk lint warnings
This commit is contained in:
@@ -18,9 +18,7 @@ import {
|
|||||||
updateScheduledTask,
|
updateScheduledTask,
|
||||||
} from './api';
|
} from './api';
|
||||||
import {
|
import {
|
||||||
LOCALES,
|
|
||||||
isLocale,
|
isLocale,
|
||||||
languageNames,
|
|
||||||
localeTags,
|
localeTags,
|
||||||
matchLocale,
|
matchLocale,
|
||||||
messages,
|
messages,
|
||||||
@@ -206,16 +204,6 @@ const ROOM_BOARD_FORMATTERS = {
|
|||||||
statusLabel,
|
statusLabel,
|
||||||
};
|
};
|
||||||
|
|
||||||
function queueLabel(
|
|
||||||
pendingTasks: number,
|
|
||||||
pendingMessages: boolean,
|
|
||||||
t: Messages,
|
|
||||||
) {
|
|
||||||
const parts = [`${pendingTasks} ${t.units.task}`];
|
|
||||||
if (pendingMessages) parts.push(t.units.messageShort);
|
|
||||||
return parts.join(' · ');
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildRoomOptions(snapshots: StatusSnapshot[]): RoomOption[] {
|
function buildRoomOptions(snapshots: StatusSnapshot[]): RoomOption[] {
|
||||||
const rooms = new Map<string, RoomOption>();
|
const rooms = new Map<string, RoomOption>();
|
||||||
for (const snapshot of snapshots) {
|
for (const snapshot of snapshots) {
|
||||||
@@ -234,33 +222,6 @@ function buildRoomOptions(snapshots: StatusSnapshot[]): RoomOption[] {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function LanguageSelector({
|
|
||||||
locale,
|
|
||||||
onLocaleChange,
|
|
||||||
t,
|
|
||||||
}: {
|
|
||||||
locale: Locale;
|
|
||||||
onLocaleChange: (locale: Locale) => void;
|
|
||||||
t: Messages;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<label className="language-select">
|
|
||||||
<span>{t.language.label}</span>
|
|
||||||
<select
|
|
||||||
aria-label={t.language.label}
|
|
||||||
onChange={(event) => onLocaleChange(event.target.value as Locale)}
|
|
||||||
value={locale}
|
|
||||||
>
|
|
||||||
{LOCALES.map((item) => (
|
|
||||||
<option key={item} value={item}>
|
|
||||||
{languageNames[item]}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function LoadingSkeleton({ t }: { t: Messages }) {
|
function LoadingSkeleton({ t }: { t: Messages }) {
|
||||||
return (
|
return (
|
||||||
<main className="shell shell-loading" aria-busy="true">
|
<main className="shell shell-loading" aria-busy="true">
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ import {
|
|||||||
extractAssistantText,
|
extractAssistantText,
|
||||||
normalizeStructuredOutput,
|
normalizeStructuredOutput,
|
||||||
readStdin,
|
readStdin,
|
||||||
type RunnerOutput,
|
|
||||||
writeOutput,
|
writeOutput,
|
||||||
} from './output-protocol.js';
|
} from './output-protocol.js';
|
||||||
import {
|
import {
|
||||||
@@ -409,7 +408,6 @@ async function runQuery(
|
|||||||
(message as { subtype?: string }).subtype === 'task_progress'
|
(message as { subtype?: string }).subtype === 'task_progress'
|
||||||
) {
|
) {
|
||||||
const tp = message as Record<string, unknown>;
|
const tp = message as Record<string, unknown>;
|
||||||
const summary = typeof tp.summary === 'string' ? tp.summary : '';
|
|
||||||
const description =
|
const description =
|
||||||
typeof tp.description === 'string' ? tp.description : '';
|
typeof tp.description === 'string' ? tp.description : '';
|
||||||
const mapped = buildTaskProgressOutput(
|
const mapped = buildTaskProgressOutput(
|
||||||
|
|||||||
@@ -488,7 +488,7 @@ server.tool(
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
async (args) => {
|
async (args) => {
|
||||||
let snapshotId = '';
|
let snapshotId: string;
|
||||||
try {
|
try {
|
||||||
snapshotId = computeVerificationSnapshotId(REPO_ROOT);
|
snapshotId = computeVerificationSnapshotId(REPO_ROOT);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ export function assertReadonlyWorkspaceRepoConnectivity(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let originUrl = '';
|
let originUrl: string;
|
||||||
try {
|
try {
|
||||||
originUrl = readGitOutput(
|
originUrl = readGitOutput(
|
||||||
['config', '--get', 'remote.origin.url'],
|
['config', '--get', 'remote.origin.url'],
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ async function measure(page: Page, sel: string) {
|
|||||||
|
|
||||||
async function evalExpr(page: Page, expr: string) {
|
async function evalExpr(page: Page, expr: string) {
|
||||||
const result = await page.evaluate((e) => {
|
const result = await page.evaluate((e) => {
|
||||||
// eslint-disable-next-line no-eval
|
|
||||||
return eval(e);
|
return eval(e);
|
||||||
}, expr);
|
}, expr);
|
||||||
console.log(JSON.stringify(result, null, 2));
|
console.log(JSON.stringify(result, null, 2));
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ function getStoredRoomRoleOverrideRows(
|
|||||||
agent_config_json: string | null;
|
agent_config_json: string | null;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
}> = [];
|
}>;
|
||||||
try {
|
try {
|
||||||
rows = database
|
rows = database
|
||||||
.prepare(
|
.prepare(
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ export function ensureLinuxReadonlySandboxAppArmorSupport(options?: {
|
|||||||
const sysctlContents =
|
const sysctlContents =
|
||||||
'# Managed by EJClaw setup to allow bubblewrap readonly sandboxing.\n' +
|
'# Managed by EJClaw setup to allow bubblewrap readonly sandboxing.\n' +
|
||||||
'kernel.apparmor_restrict_unprivileged_userns=0\n';
|
'kernel.apparmor_restrict_unprivileged_userns=0\n';
|
||||||
let existingContents: string | null = null;
|
let existingContents: string | null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
existingContents = readFileSyncFn(sysctlPath, 'utf-8');
|
existingContents = readFileSyncFn(sysctlPath, 'utf-8');
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import {
|
|||||||
ensureLinuxReadonlySandboxAppArmorSupport,
|
ensureLinuxReadonlySandboxAppArmorSupport,
|
||||||
getPlatform,
|
getPlatform,
|
||||||
getNodePath,
|
getNodePath,
|
||||||
getServiceManager,
|
|
||||||
} from './platform.js';
|
} from './platform.js';
|
||||||
import { getServiceDefs } from './service-defs.js';
|
import { getServiceDefs } from './service-defs.js';
|
||||||
import { setupLaunchd, setupLinux } from './service-installers.js';
|
import { setupLaunchd, setupLinux } from './service-installers.js';
|
||||||
@@ -30,7 +29,6 @@ export async function run(_args: string[]): Promise<void> {
|
|||||||
const platform = getPlatform();
|
const platform = getPlatform();
|
||||||
const nodePath = getNodePath();
|
const nodePath = getNodePath();
|
||||||
const homeDir = os.homedir();
|
const homeDir = os.homedir();
|
||||||
const serviceManager = getServiceManager();
|
|
||||||
|
|
||||||
logger.info({ platform, nodePath, projectRoot }, 'Setting up service');
|
logger.info({ platform, nodePath, projectRoot }, 'Setting up service');
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ vi.mock('./config.js', () => ({
|
|||||||
|
|
||||||
vi.mock('./env.js', () => ({
|
vi.mock('./env.js', () => ({
|
||||||
readEnvFile: mockReadEnvFile,
|
readEnvFile: mockReadEnvFile,
|
||||||
getEnv: vi.fn((key: string) => undefined),
|
getEnv: vi.fn((_key: string) => undefined),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('./codex-token-rotation.js', () => ({
|
vi.mock('./codex-token-rotation.js', () => ({
|
||||||
|
|||||||
@@ -487,9 +487,6 @@ export function prepareGroupEnvironment(
|
|||||||
const ownerCommonPairedRoomPrompt = isPairedRoom
|
const ownerCommonPairedRoomPrompt = isPairedRoom
|
||||||
? readOptionalPromptFile(projectRoot, 'owner-common-paired-room.md')
|
? readOptionalPromptFile(projectRoot, 'owner-common-paired-room.md')
|
||||||
: undefined;
|
: undefined;
|
||||||
const claudePairedRoomPrompt = isPairedRoom
|
|
||||||
? readPairedRoomPrompt('claude-code', projectRoot)
|
|
||||||
: undefined;
|
|
||||||
const globalClaudeMemory =
|
const globalClaudeMemory =
|
||||||
!isMain && fs.existsSync(globalClaudeMdPath)
|
!isMain && fs.existsSync(globalClaudeMdPath)
|
||||||
? fs.readFileSync(globalClaudeMdPath, 'utf-8').trim()
|
? fs.readFileSync(globalClaudeMdPath, 'utf-8').trim()
|
||||||
|
|||||||
@@ -13,14 +13,13 @@ import {
|
|||||||
prepareGroupEnvironment,
|
prepareGroupEnvironment,
|
||||||
} from './agent-runner-environment.js';
|
} from './agent-runner-environment.js';
|
||||||
import { runSpawnedAgentProcess } from './agent-runner-process.js';
|
import { runSpawnedAgentProcess } from './agent-runner-process.js';
|
||||||
import { getEnv } from './env.js';
|
|
||||||
export {
|
export {
|
||||||
type AvailableGroup,
|
type AvailableGroup,
|
||||||
writeGroupsSnapshot,
|
writeGroupsSnapshot,
|
||||||
writeTasksSnapshot,
|
writeTasksSnapshot,
|
||||||
} from './agent-runner-snapshot.js';
|
} from './agent-runner-snapshot.js';
|
||||||
import { logger } from './logger.js';
|
import { logger } from './logger.js';
|
||||||
import { AgentType, RegisteredGroup, RoomRoleContext } from './types.js';
|
import { RegisteredGroup, RoomRoleContext } from './types.js';
|
||||||
|
|
||||||
export interface AgentInput {
|
export interface AgentInput {
|
||||||
prompt: string;
|
prompt: string;
|
||||||
|
|||||||
@@ -125,8 +125,6 @@ vi.mock('discord.js', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
import { DiscordChannel, DiscordChannelOpts } from './discord.js';
|
import { DiscordChannel, DiscordChannelOpts } from './discord.js';
|
||||||
import { registerChannel } from './registry.js';
|
|
||||||
import { getEnv } from '../env.js';
|
|
||||||
import { logger } from '../logger.js';
|
import { logger } from '../logger.js';
|
||||||
|
|
||||||
// --- Test helpers ---
|
// --- Test helpers ---
|
||||||
@@ -787,7 +785,6 @@ describe('DiscordChannel', () => {
|
|||||||
|
|
||||||
await channel.sendMessage('dc:1234567890123456', 'Hello');
|
await channel.sendMessage('dc:1234567890123456', 'Hello');
|
||||||
|
|
||||||
const fetchedChannel =
|
|
||||||
await currentClient().channels.fetch('1234567890123456');
|
await currentClient().channels.fetch('1234567890123456');
|
||||||
expect(currentClient().channels.fetch).toHaveBeenCalledWith(
|
expect(currentClient().channels.fetch).toHaveBeenCalledWith(
|
||||||
'1234567890123456',
|
'1234567890123456',
|
||||||
|
|||||||
@@ -11,12 +11,7 @@ import {
|
|||||||
TextChannel,
|
TextChannel,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
|
|
||||||
import {
|
import { CACHE_DIR, DATA_DIR } from '../config.js';
|
||||||
ASSISTANT_NAME,
|
|
||||||
CACHE_DIR,
|
|
||||||
DATA_DIR,
|
|
||||||
TRIGGER_PATTERN,
|
|
||||||
} from '../config.js';
|
|
||||||
import { getEnv } from '../env.js';
|
import { getEnv } from '../env.js';
|
||||||
import { logger } from '../logger.js';
|
import { logger } from '../logger.js';
|
||||||
import { validateOutboundAttachments } from '../outbound-attachments.js';
|
import { validateOutboundAttachments } from '../outbound-attachments.js';
|
||||||
@@ -449,7 +444,7 @@ export class DiscordChannel implements Channel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let cleaned = outbound.cleanText
|
let cleaned = outbound.cleanText
|
||||||
.replace(/^[ \t]*[•\-\*][ \t]*$/gm, '') // remove empty bullet lines
|
.replace(/^[ \t]*[•*-][ \t]*$/gm, '') // remove empty bullet lines
|
||||||
.replace(/\n{3,}/g, '\n\n') // collapse excessive blank lines
|
.replace(/\n{3,}/g, '\n\n') // collapse excessive blank lines
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, it, expect, beforeEach } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
registerChannel,
|
registerChannel,
|
||||||
|
|||||||
@@ -179,13 +179,9 @@ export function applyCodexUsageToAccount(
|
|||||||
|
|
||||||
// Select the effective bucket
|
// Select the effective bucket
|
||||||
const primaryBucket = usage.find((l) => l.limitId === 'codex');
|
const primaryBucket = usage.find((l) => l.limitId === 'codex');
|
||||||
let effective: CodexRateLimit | null = null;
|
const effective = primaryBucket ?? (usage.length === 1 ? usage[0] : null);
|
||||||
|
|
||||||
if (primaryBucket) {
|
if (!effective) {
|
||||||
effective = primaryBucket;
|
|
||||||
} else if (usage.length === 1) {
|
|
||||||
effective = usage[0];
|
|
||||||
} else {
|
|
||||||
// Multiple unknown buckets — cannot determine which is authoritative
|
// Multiple unknown buckets — cannot determine which is authoritative
|
||||||
logger.warn(
|
logger.warn(
|
||||||
{ account: accountIndex + 1 },
|
{ account: accountIndex + 1 },
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import {
|
|||||||
getEffectiveRuntimeRoomMode,
|
getEffectiveRuntimeRoomMode,
|
||||||
getExplicitRoomMode,
|
getExplicitRoomMode,
|
||||||
getLatestMessageSeqAtOrBefore,
|
getLatestMessageSeqAtOrBefore,
|
||||||
getLatestPairedTaskForChat,
|
|
||||||
getLatestTurnNumber,
|
getLatestTurnNumber,
|
||||||
getLastRespondingAgentType,
|
getLastRespondingAgentType,
|
||||||
getRegisteredGroup,
|
getRegisteredGroup,
|
||||||
@@ -199,7 +198,7 @@ function getStoredRoleOverridesForLegacyMigration(
|
|||||||
agent_config_json: string | null;
|
agent_config_json: string | null;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
}> = [];
|
}>;
|
||||||
try {
|
try {
|
||||||
rows = database
|
rows = database
|
||||||
.prepare(
|
.prepare(
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ interface RequiredRoleMetadataInput {
|
|||||||
fallbackAgentType?: AgentType | null;
|
fallbackAgentType?: AgentType | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OptionalRoleMetadataInput extends RequiredRoleMetadataInput {}
|
type OptionalRoleMetadataInput = RequiredRoleMetadataInput;
|
||||||
|
|
||||||
function resolveFilledRequiredAgentType(
|
function resolveFilledRequiredAgentType(
|
||||||
input: RequiredRoleMetadataInput,
|
input: RequiredRoleMetadataInput,
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import {
|
|||||||
import {
|
import {
|
||||||
AgentType,
|
AgentType,
|
||||||
PairedProject,
|
PairedProject,
|
||||||
PairedRoomRole,
|
|
||||||
PairedTask,
|
PairedTask,
|
||||||
PairedTaskStatus,
|
PairedTaskStatus,
|
||||||
PairedTurnReservationIntentKind,
|
PairedTurnReservationIntentKind,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Database } from 'bun:sqlite';
|
import { Database } from 'bun:sqlite';
|
||||||
|
|
||||||
import { buildPairedTurnAttemptId } from './paired-turn-attempts.js';
|
import { buildPairedTurnAttemptId } from './paired-turn-attempts.js';
|
||||||
import { tableHasColumn, tryExecMigration } from './migrations/helpers.js';
|
import { tableHasColumn } from './migrations/helpers.js';
|
||||||
|
|
||||||
// Paired-turn provenance rebuild helpers extracted from the legacy schema
|
// Paired-turn provenance rebuild helpers extracted from the legacy schema
|
||||||
// bundle. These remain runtime helpers because v10 replays them during
|
// bundle. These remain runtime helpers because v10 replays them during
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ function getStoredRoomRoleOverrideRows(
|
|||||||
agent_config_json: string | null;
|
agent_config_json: string | null;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
}> = [];
|
}>;
|
||||||
try {
|
try {
|
||||||
rows = database
|
rows = database
|
||||||
.prepare(
|
.prepare(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
_setRegisteredGroupForTests,
|
_setRegisteredGroupForTests,
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ export async function checkGitHubActionsRun(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let failedJobs: string[] = [];
|
let failedJobs: string[];
|
||||||
try {
|
try {
|
||||||
failedJobs = await fetchFailedJobs(metadata);
|
failedJobs = await fetchFailedJobs(metadata);
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ describe('GroupQueue', () => {
|
|||||||
let concurrentCount = 0;
|
let concurrentCount = 0;
|
||||||
let maxConcurrent = 0;
|
let maxConcurrent = 0;
|
||||||
|
|
||||||
const processMessages = vi.fn(async (groupJid: string) => {
|
const processMessages = vi.fn(async (_groupJid: string) => {
|
||||||
concurrentCount++;
|
concurrentCount++;
|
||||||
maxConcurrent = Math.max(maxConcurrent, concurrentCount);
|
maxConcurrent = Math.max(maxConcurrent, concurrentCount);
|
||||||
// Simulate async work
|
// Simulate async work
|
||||||
@@ -282,7 +282,7 @@ describe('GroupQueue', () => {
|
|||||||
let maxActive = 0;
|
let maxActive = 0;
|
||||||
const completionCallbacks: Array<() => void> = [];
|
const completionCallbacks: Array<() => void> = [];
|
||||||
|
|
||||||
const processMessages = vi.fn(async (groupJid: string) => {
|
const processMessages = vi.fn(async (_groupJid: string) => {
|
||||||
activeCount++;
|
activeCount++;
|
||||||
maxActive = Math.max(maxActive, activeCount);
|
maxActive = Math.max(maxActive, activeCount);
|
||||||
await new Promise<void>((resolve) => completionCallbacks.push(resolve));
|
await new Promise<void>((resolve) => completionCallbacks.push(resolve));
|
||||||
@@ -360,7 +360,7 @@ describe('GroupQueue', () => {
|
|||||||
const executionOrder: string[] = [];
|
const executionOrder: string[] = [];
|
||||||
let resolveFirst: () => void;
|
let resolveFirst: () => void;
|
||||||
|
|
||||||
const processMessages = vi.fn(async (groupJid: string) => {
|
const processMessages = vi.fn(async (_groupJid: string) => {
|
||||||
if (executionOrder.length === 0) {
|
if (executionOrder.length === 0) {
|
||||||
// First call: block until we release it
|
// First call: block until we release it
|
||||||
await new Promise<void>((resolve) => {
|
await new Promise<void>((resolve) => {
|
||||||
|
|||||||
12
src/index.ts
12
src/index.ts
@@ -1,10 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
ASSISTANT_NAME,
|
ASSISTANT_NAME,
|
||||||
DATA_DIR,
|
|
||||||
IDLE_TIMEOUT,
|
IDLE_TIMEOUT,
|
||||||
POLL_INTERVAL,
|
POLL_INTERVAL,
|
||||||
SERVICE_ID,
|
SERVICE_ID,
|
||||||
isSessionCommandSenderAllowed,
|
|
||||||
STATUS_CHANNEL_ID,
|
STATUS_CHANNEL_ID,
|
||||||
STATUS_UPDATE_INTERVAL,
|
STATUS_UPDATE_INTERVAL,
|
||||||
TIMEZONE,
|
TIMEZONE,
|
||||||
@@ -19,14 +17,11 @@ import {
|
|||||||
} from './channels/registry.js';
|
} from './channels/registry.js';
|
||||||
import { writeGroupsSnapshot } from './agent-runner.js';
|
import { writeGroupsSnapshot } from './agent-runner.js';
|
||||||
import {
|
import {
|
||||||
type AssignRoomInput,
|
|
||||||
getAllTasks,
|
|
||||||
hasRecentRestartAnnouncement,
|
hasRecentRestartAnnouncement,
|
||||||
initDatabase,
|
initDatabase,
|
||||||
storeChatMetadata,
|
storeChatMetadata,
|
||||||
storeMessage,
|
storeMessage,
|
||||||
} from './db.js';
|
} from './db.js';
|
||||||
import { composeDashboardContent } from './dashboard-render.js';
|
|
||||||
import { GroupQueue } from './group-queue.js';
|
import { GroupQueue } from './group-queue.js';
|
||||||
import { resolveGroupIpcPath } from './group-folder.js';
|
import { resolveGroupIpcPath } from './group-folder.js';
|
||||||
import { startIpcWatcher } from './ipc.js';
|
import { startIpcWatcher } from './ipc.js';
|
||||||
@@ -34,11 +29,7 @@ import {
|
|||||||
deliverCanonicalOutboundMessage,
|
deliverCanonicalOutboundMessage,
|
||||||
deliverIpcOutboundMessage,
|
deliverIpcOutboundMessage,
|
||||||
} from './ipc-outbound-delivery.js';
|
} from './ipc-outbound-delivery.js';
|
||||||
import {
|
import { findChannel, formatOutbound } from './router.js';
|
||||||
findChannel,
|
|
||||||
formatOutbound,
|
|
||||||
normalizeMessageForDedupe,
|
|
||||||
} from './router.js';
|
|
||||||
import {
|
import {
|
||||||
buildRestartAnnouncement,
|
buildRestartAnnouncement,
|
||||||
buildInterruptedRestartAnnouncement,
|
buildInterruptedRestartAnnouncement,
|
||||||
@@ -50,7 +41,6 @@ import {
|
|||||||
} from './restart-context.js';
|
} from './restart-context.js';
|
||||||
import {
|
import {
|
||||||
isSenderAllowed,
|
isSenderAllowed,
|
||||||
isTriggerAllowed,
|
|
||||||
loadSenderAllowlist,
|
loadSenderAllowlist,
|
||||||
shouldDropMessage,
|
shouldDropMessage,
|
||||||
} from './sender-allowlist.js';
|
} from './sender-allowlist.js';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import { DATA_DIR, IPC_POLL_INTERVAL, TIMEZONE } from './config.js';
|
import { DATA_DIR, IPC_POLL_INTERVAL } from './config.js';
|
||||||
import { readJsonFile } from './utils.js';
|
import { readJsonFile } from './utils.js';
|
||||||
import { logger } from './logger.js';
|
import { logger } from './logger.js';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { logger } from './logger.js';
|
|
||||||
import {
|
import {
|
||||||
handleSessionCommand,
|
handleSessionCommand,
|
||||||
type SessionCommandDeps,
|
type SessionCommandDeps,
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
} from './config.js';
|
} from './config.js';
|
||||||
import { GroupQueue, GroupRunContext } from './group-queue.js';
|
import { GroupQueue, GroupRunContext } from './group-queue.js';
|
||||||
import { findChannel, formatMessages } from './router.js';
|
import { findChannel, formatMessages } from './router.js';
|
||||||
import { isTriggerAllowed, loadSenderAllowlist } from './sender-allowlist.js';
|
|
||||||
import { enqueueGenericFollowUpAfterDeliveryRetry as enqueueDeliveryRetryFollowUp } from './message-runtime-dispatch.js';
|
import { enqueueGenericFollowUpAfterDeliveryRetry as enqueueDeliveryRetryFollowUp } from './message-runtime-dispatch.js';
|
||||||
import { processOpenWorkItemDelivery } from './message-runtime-delivery.js';
|
import { processOpenWorkItemDelivery } from './message-runtime-delivery.js';
|
||||||
import { deliverCanonicalOutboundMessage } from './ipc-outbound-delivery.js';
|
import { deliverCanonicalOutboundMessage } from './ipc-outbound-delivery.js';
|
||||||
@@ -340,7 +339,6 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isMainGroup = group.isMain === true;
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const sinceSeqCursor = deps.getLastAgentTimestamps()[chatJid] || '0';
|
const sinceSeqCursor = deps.getLastAgentTimestamps()[chatJid] || '0';
|
||||||
const rawMissedMessages = getMessagesSinceSeq(
|
const rawMissedMessages = getMessagesSinceSeq(
|
||||||
|
|||||||
@@ -15,10 +15,7 @@ import {
|
|||||||
transitionPairedTaskStatus,
|
transitionPairedTaskStatus,
|
||||||
} from './paired-task-status.js';
|
} from './paired-task-status.js';
|
||||||
import { resolveOwnerCompletionSignal } from './paired-completion-signals.js';
|
import { resolveOwnerCompletionSignal } from './paired-completion-signals.js';
|
||||||
import {
|
import { hasCodeChangesSinceRef } from './paired-source-ref.js';
|
||||||
hasCodeChangesSinceRef,
|
|
||||||
resolveCanonicalSourceRef,
|
|
||||||
} from './paired-source-ref.js';
|
|
||||||
import { parseVisibleVerdict } from './paired-verdict.js';
|
import { parseVisibleVerdict } from './paired-verdict.js';
|
||||||
import type { PairedTask } from './types.js';
|
import type { PairedTask } from './types.js';
|
||||||
|
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ export function handleReviewerCompletion(args: {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'request_arbiter':
|
case 'request_arbiter': {
|
||||||
const arbiterLogMessage =
|
const arbiterLogMessage =
|
||||||
verdict === 'blocked' || verdict === 'needs_context'
|
verdict === 'blocked' || verdict === 'needs_context'
|
||||||
? 'Reviewer blocked/needs_context — requesting arbiter before escalating'
|
? 'Reviewer blocked/needs_context — requesting arbiter before escalating'
|
||||||
@@ -152,6 +152,7 @@ export function handleReviewerCompletion(args: {
|
|||||||
logContext: { taskId, verdict, summary: summary?.slice(0, 100) },
|
logContext: { taskId, verdict, summary: summary?.slice(0, 100) },
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case 'request_owner_changes':
|
case 'request_owner_changes':
|
||||||
transitionPairedTaskStatus({
|
transitionPairedTaskStatus({
|
||||||
|
|||||||
@@ -8,14 +8,12 @@ import {
|
|||||||
} from 'ejclaw-runners-shared';
|
} from 'ejclaw-runners-shared';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ARBITER_DEADLOCK_THRESHOLD,
|
|
||||||
ARBITER_AGENT_TYPE,
|
ARBITER_AGENT_TYPE,
|
||||||
CLAUDE_SERVICE_ID,
|
CLAUDE_SERVICE_ID,
|
||||||
CODEX_MAIN_SERVICE_ID,
|
CODEX_MAIN_SERVICE_ID,
|
||||||
CODEX_REVIEW_SERVICE_ID,
|
CODEX_REVIEW_SERVICE_ID,
|
||||||
DATA_DIR,
|
DATA_DIR,
|
||||||
PAIRED_CARRY_FORWARD_LATEST_OWNER_FINAL,
|
PAIRED_CARRY_FORWARD_LATEST_OWNER_FINAL,
|
||||||
PAIRED_MAX_ROUND_TRIPS,
|
|
||||||
REVIEWER_AGENT_TYPE,
|
REVIEWER_AGENT_TYPE,
|
||||||
} from './config.js';
|
} from './config.js';
|
||||||
import {
|
import {
|
||||||
@@ -26,8 +24,6 @@ import {
|
|||||||
getPairedTaskById,
|
getPairedTaskById,
|
||||||
getPairedTurnById,
|
getPairedTurnById,
|
||||||
getPairedTurnOutputs,
|
getPairedTurnOutputs,
|
||||||
getPairedWorkspace,
|
|
||||||
hasActiveCiWatcherForChat,
|
|
||||||
insertPairedTurnOutput,
|
insertPairedTurnOutput,
|
||||||
releasePairedTaskExecutionLease,
|
releasePairedTaskExecutionLease,
|
||||||
updatePairedTask,
|
updatePairedTask,
|
||||||
@@ -50,11 +46,9 @@ import {
|
|||||||
applyPairedTaskPatch,
|
applyPairedTaskPatch,
|
||||||
transitionPairedTaskStatus,
|
transitionPairedTaskStatus,
|
||||||
} from './paired-task-status.js';
|
} from './paired-task-status.js';
|
||||||
import { requestArbiterOrEscalate } from './paired-arbiter-request.js';
|
|
||||||
import { resolveCanonicalSourceRef } from './paired-source-ref.js';
|
import { resolveCanonicalSourceRef } from './paired-source-ref.js';
|
||||||
import {
|
import {
|
||||||
isOwnerWorkspaceRepairNeededError,
|
isOwnerWorkspaceRepairNeededError,
|
||||||
markPairedTaskReviewReady,
|
|
||||||
prepareReviewerWorkspaceForExecution,
|
prepareReviewerWorkspaceForExecution,
|
||||||
provisionOwnerWorkspaceForPairedTask,
|
provisionOwnerWorkspaceForPairedTask,
|
||||||
} from './paired-workspace-manager.js';
|
} from './paired-workspace-manager.js';
|
||||||
|
|||||||
@@ -16,11 +16,8 @@ import { transitionPairedTaskStatus } from './paired-task-status.js';
|
|||||||
import type { PairedTask, PairedWorkspace } from './types.js';
|
import type { PairedTask, PairedWorkspace } from './types.js';
|
||||||
import { ensureWorkspaceDependenciesInstalled } from './workspace-package-manager.js';
|
import { ensureWorkspaceDependenciesInstalled } from './workspace-package-manager.js';
|
||||||
|
|
||||||
const REVIEWER_SNAPSHOT_STALE_BLOCK_MESSAGE =
|
|
||||||
'Review workspace is out of date after owner changes. Retry the review once to resync against the latest owner workspace.';
|
|
||||||
const REVIEWER_SNAPSHOT_NOT_READY_BLOCK_MESSAGE =
|
const REVIEWER_SNAPSHOT_NOT_READY_BLOCK_MESSAGE =
|
||||||
'Review workspace is not ready yet. Wait for the owner to complete a turn so the reviewer workspace can be prepared.';
|
'Review workspace is not ready yet. Wait for the owner to complete a turn so the reviewer workspace can be prepared.';
|
||||||
const OWNER_WORKSPACE_REPAIR_PREFIX = 'BLOCKED\nOwner workspace needs repair.';
|
|
||||||
const REVIEWER_SNAPSHOT_DENY_SEGMENTS = new Set([
|
const REVIEWER_SNAPSHOT_DENY_SEGMENTS = new Set([
|
||||||
'.git',
|
'.git',
|
||||||
'.claude',
|
'.claude',
|
||||||
@@ -139,19 +136,6 @@ function isGitWorktreeClean(repoDir: string): boolean {
|
|||||||
return runGit(['status', '--short'], repoDir).length === 0;
|
return runGit(['status', '--short'], repoDir).length === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildOwnerWorkspaceRepairBlockMessage(args: {
|
|
||||||
workspaceDir: string;
|
|
||||||
currentBranch: string;
|
|
||||||
targetBranch: string;
|
|
||||||
reason: string;
|
|
||||||
}): string {
|
|
||||||
return `${OWNER_WORKSPACE_REPAIR_PREFIX}
|
|
||||||
Owner workspace branch mismatch: \`${args.currentBranch}\` -> expected \`${args.targetBranch}\`.
|
|
||||||
${args.reason}
|
|
||||||
Workspace: \`${args.workspaceDir}\`
|
|
||||||
Repair the owner workspace branch, then retry the task.`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildOwnerReanchorBackupPrefix(targetBranch: string): string {
|
function buildOwnerReanchorBackupPrefix(targetBranch: string): string {
|
||||||
const groupFolder = targetBranch.startsWith('codex/owner/')
|
const groupFolder = targetBranch.startsWith('codex/owner/')
|
||||||
? targetBranch.slice('codex/owner/'.length)
|
? targetBranch.slice('codex/owner/'.length)
|
||||||
@@ -286,10 +270,6 @@ function maybeRepairNamedOwnerWorkspaceBranch(args: {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureCleanDirectory(targetDir: string): void {
|
|
||||||
fs.mkdirSync(targetDir, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
type GitWorktreeEntry = {
|
type GitWorktreeEntry = {
|
||||||
worktreePath: string;
|
worktreePath: string;
|
||||||
head: string | null;
|
head: string | null;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, it, expect, beforeEach } from 'vitest';
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
|
||||||
import { _initTestDatabase, getAllChats, storeChatMetadata } from './db.js';
|
import { _initTestDatabase, storeChatMetadata } from './db.js';
|
||||||
import { getAvailableGroups, _setRegisteredGroups } from './index.js';
|
import { getAvailableGroups, _setRegisteredGroups } from './index.js';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
isSenderAllowed,
|
isSenderAllowed,
|
||||||
|
|||||||
@@ -81,12 +81,6 @@ export interface SessionCommandDeps {
|
|||||||
killProcess: () => boolean;
|
killProcess: () => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resultToText(result: string | object | null | undefined): string {
|
|
||||||
if (!result) return '';
|
|
||||||
const raw = typeof result === 'string' ? result : JSON.stringify(result);
|
|
||||||
return formatOutbound(raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
function agentResultToText(result: AgentResult): string {
|
function agentResultToText(result: AgentResult): string {
|
||||||
const raw = getAgentOutputText({
|
const raw = getAgentOutputText({
|
||||||
result: result.result ?? null,
|
result: result.result ?? null,
|
||||||
@@ -126,10 +120,6 @@ export async function handleSessionCommand(opts: {
|
|||||||
const command = cmdMsg
|
const command = cmdMsg
|
||||||
? extractSessionCommand(cmdMsg.content, triggerPattern)
|
? extractSessionCommand(cmdMsg.content, triggerPattern)
|
||||||
: null;
|
: null;
|
||||||
const normalizedCommandText = cmdMsg
|
|
||||||
? normalizeSessionCommandText(cmdMsg.content, triggerPattern)
|
|
||||||
: '';
|
|
||||||
|
|
||||||
if (!command || !cmdMsg) return { handled: false };
|
if (!command || !cmdMsg) return { handled: false };
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ export interface FastModeSnapshot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ROLE_KEYS = ['OWNER', 'REVIEWER', 'ARBITER'] as const;
|
const ROLE_KEYS = ['OWNER', 'REVIEWER', 'ARBITER'] as const;
|
||||||
type RoleKey = (typeof ROLE_KEYS)[number];
|
|
||||||
|
|
||||||
function envFilePath(): string {
|
function envFilePath(): string {
|
||||||
return path.join(process.cwd(), '.env');
|
return path.join(process.cwd(), '.env');
|
||||||
@@ -555,7 +554,7 @@ function readCodexFastMode(): boolean {
|
|||||||
if (!fs.existsSync(file)) return false;
|
if (!fs.existsSync(file)) return false;
|
||||||
const content = fs.readFileSync(file, 'utf-8');
|
const content = fs.readFileSync(file, 'utf-8');
|
||||||
// [features] section, look for fast_mode = true|false
|
// [features] section, look for fast_mode = true|false
|
||||||
const featuresMatch = content.match(/\[features\][\s\S]*?(?=^\[|\Z)/m);
|
const featuresMatch = content.match(/\[features\][\s\S]*?(?=^\[|$)/m);
|
||||||
const block = featuresMatch ? featuresMatch[0] : content;
|
const block = featuresMatch ? featuresMatch[0] : content;
|
||||||
const m = block.match(/^\s*fast_mode\s*=\s*(true|false)\s*$/m);
|
const m = block.match(/^\s*fast_mode\s*=\s*(true|false)\s*$/m);
|
||||||
return m ? m[1] === 'true' : false;
|
return m ? m[1] === 'true' : false;
|
||||||
@@ -663,7 +662,7 @@ export function addClaudeAccountFromToken(token: string): {
|
|||||||
if (parts.length < 2) {
|
if (parts.length < 2) {
|
||||||
throw new Error('invalid OAuth token: expected JWT format');
|
throw new Error('invalid OAuth token: expected JWT format');
|
||||||
}
|
}
|
||||||
let payload: Record<string, unknown> = {};
|
let payload: Record<string, unknown>;
|
||||||
try {
|
try {
|
||||||
payload = JSON.parse(
|
payload = JSON.parse(
|
||||||
Buffer.from(parts[1], 'base64').toString('utf-8'),
|
Buffer.from(parts[1], 'base64').toString('utf-8'),
|
||||||
|
|||||||
@@ -533,11 +533,7 @@ describe('evaluateStreamedOutput', () => {
|
|||||||
reason: '429',
|
reason: '429',
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = evaluateStreamedOutput(
|
evaluateStreamedOutput(errorOutput('429'), freshState(), claudeOpts);
|
||||||
errorOutput('429'),
|
|
||||||
freshState(),
|
|
||||||
claudeOpts,
|
|
||||||
);
|
|
||||||
// Claude uses classifyRotationTrigger, not detectCodexRotationTrigger
|
// Claude uses classifyRotationTrigger, not detectCodexRotationTrigger
|
||||||
expect(detectCodexRotationTrigger).not.toHaveBeenCalled();
|
expect(detectCodexRotationTrigger).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -42,11 +42,7 @@ import {
|
|||||||
formatSuspensionNotice,
|
formatSuspensionNotice,
|
||||||
suspendTask,
|
suspendTask,
|
||||||
} from './task-suspension.js';
|
} from './task-suspension.js';
|
||||||
import {
|
import { getTaskQueueJid, isGitHubCiTask } from './task-watch-status.js';
|
||||||
extractWatchCiTarget,
|
|
||||||
getTaskQueueJid,
|
|
||||||
isGitHubCiTask,
|
|
||||||
} from './task-watch-status.js';
|
|
||||||
import { ScheduledTask } from './types.js';
|
import { ScheduledTask } from './types.js';
|
||||||
import {
|
import {
|
||||||
hasTaskExceededMaxDuration,
|
hasTaskExceededMaxDuration,
|
||||||
@@ -163,13 +159,12 @@ async function runTask(
|
|||||||
);
|
);
|
||||||
|
|
||||||
let result: string | null = null;
|
let result: string | null = null;
|
||||||
let error: string | null = null;
|
let error: string | null;
|
||||||
const statusTracker = createTaskStatusTracker(task, {
|
const statusTracker = createTaskStatusTracker(task, {
|
||||||
sendTrackedMessage: deps.sendTrackedMessage,
|
sendTrackedMessage: deps.sendTrackedMessage,
|
||||||
editTrackedMessage: deps.editTrackedMessage,
|
editTrackedMessage: deps.editTrackedMessage,
|
||||||
});
|
});
|
||||||
const isClaudeAgent = context.taskAgentType === 'claude-code';
|
const isClaudeAgent = context.taskAgentType === 'claude-code';
|
||||||
const canRotateToken = isClaudeAgent && getTokenCount() > 1;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await statusTracker.update('checking');
|
await statusTracker.update('checking');
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
* All exhausted: surface error to caller
|
* All exhausted: surface error to caller
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import { DATA_DIR } from './config.js';
|
import { DATA_DIR } from './config.js';
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import {
|
|||||||
getLatestPairedTurnForTask,
|
getLatestPairedTurnForTask,
|
||||||
getRecentDeliveredWorkItemsForChat,
|
getRecentDeliveredWorkItemsForChat,
|
||||||
getRecentChatMessages,
|
getRecentChatMessages,
|
||||||
getRecentChatMessagesBatch,
|
|
||||||
getTaskById,
|
getTaskById,
|
||||||
hasMessage,
|
hasMessage,
|
||||||
storeChatMetadata,
|
storeChatMetadata,
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ async function readJsonObject(
|
|||||||
request: Request,
|
request: Request,
|
||||||
jsonResponse: JsonResponse,
|
jsonResponse: JsonResponse,
|
||||||
): Promise<Record<string, unknown> | Response> {
|
): Promise<Record<string, unknown> | Response> {
|
||||||
let body: unknown = null;
|
let body: unknown;
|
||||||
try {
|
try {
|
||||||
body = await request.json();
|
body = await request.json();
|
||||||
} catch {
|
} catch {
|
||||||
@@ -141,7 +141,7 @@ async function handleClaudeAccountAddRoute(
|
|||||||
): Promise<Response | null> {
|
): Promise<Response | null> {
|
||||||
if (request.method !== 'POST') return null;
|
if (request.method !== 'POST') return null;
|
||||||
|
|
||||||
let body: { token?: unknown } | null = null;
|
let body: { token?: unknown };
|
||||||
try {
|
try {
|
||||||
body = (await request.json()) as { token?: unknown };
|
body = (await request.json()) as { token?: unknown };
|
||||||
} catch {
|
} catch {
|
||||||
@@ -213,7 +213,7 @@ async function handleCodexCurrentRoute(
|
|||||||
): Promise<Response | null> {
|
): Promise<Response | null> {
|
||||||
if (request.method !== 'PUT') return null;
|
if (request.method !== 'PUT') return null;
|
||||||
|
|
||||||
let body: { index?: unknown } | null = null;
|
let body: { index?: unknown };
|
||||||
try {
|
try {
|
||||||
body = (await request.json()) as { index?: unknown };
|
body = (await request.json()) as { index?: unknown };
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
Reference in New Issue
Block a user