/** Domain types split from shared/types.ts (compatible facade). */ import * as os from "node:os"; import * as path from "node:path"; import type { MaxOutputConfig } from "./basic.ts"; import type { ArtifactConfig } from "./artifacts.ts"; export const DEFAULT_MAX_OUTPUT: Required = { bytes: 200 * 1024, lines: 5000, }; export const DEFAULT_ARTIFACT_CONFIG: ArtifactConfig = { enabled: true, dir: "project", includeInput: true, includeOutput: true, includeJsonl: false, includeTranscript: true, includeMetadata: true, cleanupDays: 7, }; function sanitizeTempScopeSegment(value: string): string { const sanitized = value .trim() .replace(/[^A-Za-z0-9._-]+/g, "-") .replace(/^-+|-+$/g, ""); return sanitized || "unknown"; } export function resolveTempScopeId(options?: { env?: NodeJS.ProcessEnv; getuid?: (() => number) | undefined; userInfo?: (() => { username?: string | null }) | undefined; homedir?: (() => string) | undefined; }): string { const env = options?.env ?? process.env; const getuid = options && Object.hasOwn(options, "getuid") ? options.getuid : process.getuid?.bind(process); if (typeof getuid === "function") { return `uid-${getuid()}`; } for (const key of ["USERNAME", "USER", "LOGNAME"] as const) { const value = env[key]; if (value) return `user-${sanitizeTempScopeSegment(value)}`; } const userInfo = options && Object.hasOwn(options, "userInfo") ? options.userInfo : os.userInfo; try { const username = userInfo?.().username; if (username) return `user-${sanitizeTempScopeSegment(username)}`; } catch { // Fall through to home-directory-based scoping. } const homedir = env.USERPROFILE ?? env.HOME; if (homedir) return `home-${sanitizeTempScopeSegment(homedir)}`; const resolveHomedir = options && Object.hasOwn(options, "homedir") ? options.homedir : os.homedir; try { const fallbackHomedir = resolveHomedir?.(); if (fallbackHomedir) return `home-${sanitizeTempScopeSegment(fallbackHomedir)}`; } catch { // Fall through to the last-resort shared scope. } return "shared"; } const MAX_PARALLEL = 8; export const MAX_CONCURRENCY = 4; export const TEMP_ROOT_DIR = path.join(os.tmpdir(), `pi-agents-flow-${resolveTempScopeId()}`); export const RESULTS_DIR = path.join(TEMP_ROOT_DIR, "async-subagent-results"); export const ASYNC_DIR = path.join(TEMP_ROOT_DIR, "async-subagent-runs"); export const CHAIN_RUNS_DIR = path.join(TEMP_ROOT_DIR, "chain-runs"); export const TEMP_ARTIFACTS_DIR = path.join(TEMP_ROOT_DIR, "artifacts"); export const SLASH_RESULT_TYPE = "subagent-slash-result"; export const SLASH_TEXT_RESULT_TYPE = "subagent-slash-text-result"; export const SLASH_SUBAGENT_REQUEST_EVENT = "subagent:slash:request"; export const SLASH_SUBAGENT_STARTED_EVENT = "subagent:slash:started"; export const SLASH_SUBAGENT_RESPONSE_EVENT = "subagent:slash:response"; export const SLASH_SUBAGENT_UPDATE_EVENT = "subagent:slash:update"; export const SLASH_SUBAGENT_CANCEL_EVENT = "subagent:slash:cancel"; export const POLL_INTERVAL_MS = 250; export const DEFAULT_SUBAGENT_MAX_DEPTH = 2; export const SUBAGENT_ACTIONS = ["list", "get", "models", "create", "update", "delete", "eject", "disable", "enable", "reset", "status", "grant-spawn-budget", "interrupt", "resume", "steer", "stop", "append-step", "approve-checkpoint", "reject-checkpoint", "doctor", "watchdog.status", "watchdog.check", "watchdog.configure", "watchdog.recommend-model", "schedule", "schedule-list", "schedule-status", "schedule-cancel"] as const; export const DEFAULT_FORK_PREAMBLE = "You are a delegated subagent running from a fork of the parent session. " + "Treat the inherited conversation as reference-only context, not a live thread to continue. " + "Do not continue or answer prior messages as if they are waiting for a reply. " + "Your sole job is to execute the task below and return a focused result for that task using your tools."; function normalizeTopLevelParallelValue(value: unknown): number | undefined { const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN; if (!Number.isInteger(parsed) || parsed < 1) return undefined; return parsed; } export function resolveTopLevelParallelMaxTasks(value: unknown): number { return normalizeTopLevelParallelValue(value) ?? MAX_PARALLEL; } export function resolveTopLevelParallelConcurrency( override: unknown, configValue: unknown, ): number { return normalizeTopLevelParallelValue(override) ?? normalizeTopLevelParallelValue(configValue) ?? MAX_CONCURRENCY; } export function getAsyncConfigPath(suffix: string): string { return path.join(TEMP_ROOT_DIR, `async-cfg-${suffix}.json`); } export function wrapForkTask(task: string, preamble?: string | false): string { if (preamble === false) return task; const effectivePreamble = preamble ?? DEFAULT_FORK_PREAMBLE; const wrappedPrefix = `${effectivePreamble}\n\nTask:\n`; if (task.startsWith(wrappedPrefix)) return task; return `${wrappedPrefix}${task}`; }