import type { ExecutionContinuationEnvelope } from "@tickernelz/paperclip-pro-shared"; import { type ChildProcess } from "node:child_process"; import { type LocalProcessSandboxOptions } from "./local-process-sandbox.js"; import { type SshRemoteExecutionSpec } from "./ssh.js"; import type { PaperclipAccessMode } from "./paperclip-mcp.js"; import type { AdapterExecutionContext, AgentRuntimeIdentity, AdapterRuntimeToolAccess, AdapterSkillSnapshot } from "./types.js"; import type { BoundLiveStdinChannel } from "./live-stdin-channel.js"; export declare function buildRuntimeToolsEnv(access: AdapterRuntimeToolAccess | null | undefined): Record; export interface RunProcessResult { exitCode: number | null; signal: string | null; timedOut: boolean; stdout: string; stderr: string; pid: number | null; startedAt: string | null; finishedAt?: string | null; durationMs?: number | null; errorCode?: string | null; terminalResultCleanup?: TerminalResultCleanupEvidence | null; } export interface TerminalResultCleanupOptions { hasTerminalResult: (output: { stdout: string; stderr: string; }) => boolean; graceMs?: number; } export declare const UNMANAGED_BACKGROUND_TASK_STOP_REASON = "unmanaged_background_task_stopped"; export declare const UNMANAGED_BACKGROUND_TASK_LIVENESS_REASON = "unmanaged background task stopped; no durable live path"; export interface TerminalResultCleanupEvidence { kind: "terminal_result_cleanup"; stopped: true; stopReason: typeof UNMANAGED_BACKGROUND_TASK_STOP_REASON; reason: typeof UNMANAGED_BACKGROUND_TASK_LIVENESS_REASON; terminalResultSeen: boolean; signal: NodeJS.Signals | null; forceKilled: boolean; } interface RunningProcess { child: ChildProcess; graceSec: number; processGroupId: number | null; } type RemoteExecutionSpec = SshRemoteExecutionSpec; export declare function signalRunningProcess(running: Pick, signal: NodeJS.Signals): void; export declare const runningProcesses: Map; export declare const MAX_CAPTURE_BYTES: number; export declare const MAX_EXCERPT_BYTES: number; export declare function isPaperclipRuntimeEnvKey(key: string): boolean; export declare function isForbiddenConfigEnvKey(key: string): boolean; export declare function resolvePaperclipInstanceRootForAdapter(input?: { homeDir?: string; instanceId?: string; env?: NodeJS.ProcessEnv; }): string; /** The heartbeat contract, rendered against the Paperclip surface the adapter actually armed. */ export declare function paperclipAgentPromptTemplate(access: PaperclipAccessMode): string; export declare const DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE: string; export declare const DEFAULT_PAPERCLIP_CONVERSATION_PROMPT_TEMPLATE: string; export declare const WATCHDOG_DEFAULT_MANDATE: string; type PaperclipWakeTaskWatchdogLeaf = { id: string | null; identifier: string | null; title: string | null; status: string | null; priority: string | null; role: string | null; summary: string | null; }; type PaperclipWakeTaskWatchdogCapabilities = { operations: string[]; deniedOperations: string[]; targetScope: { watchedIssueId: string | null; watchedIssueIdentifier: string | null; watchdogIssueId: string | null; includeNonWatchdogDescendants: boolean; excludedOriginKinds: string[]; } | null; }; export type PaperclipWakeTaskWatchdogContext = { watchedIssueId: string | null; watchedIssueIdentifier: string | null; watchedIssueTitle: string | null; stopFingerprint: string | null; terminalLeafSummaries: PaperclipWakeTaskWatchdogLeaf[]; customInstructions: string | null; capabilities: PaperclipWakeTaskWatchdogCapabilities | null; }; export interface PaperclipSkillEntry { key: string; runtimeName: string; source: string; versionId?: string | null; currentVersionId?: string | null; sourceStatus?: "available" | "missing"; missingDetail?: string | null; } export interface PaperclipDesiredSkillEntry { key: string; versionId: string | null; } export interface InstalledSkillTarget { targetPath: string | null; kind: "symlink" | "directory" | "file"; } export interface MaterializedPaperclipSkillCopyResult { copiedFiles: number; skippedSymlinks: string[]; } interface PersistentSkillSnapshotOptions { adapterType: string; availableEntries: PaperclipSkillEntry[]; desiredSkills: string[]; installed: Map; skillsHome: string; locationLabel?: string | null; installedDetail?: string | null; missingDetail: string; externalConflictDetail: string; externalDetail: string; warnings?: string[]; } interface RuntimeMountedSkillSnapshotOptions { adapterType: string; availableEntries: PaperclipSkillEntry[]; desiredSkills: string[]; configuredDetail: string | ((entry: PaperclipSkillEntry) => string | null); missingDetail?: string; mode?: "ephemeral" | "unsupported"; supported?: boolean; unsupportedDetail?: string | ((entry: PaperclipSkillEntry) => string | null); warnings?: string[]; externalInstalled?: Map; externalLocationLabel?: string | null; externalDetail?: string; skillsHome?: string; } /** * True when a runtime skill entry's files are unavailable (failed * materialization, deleted version snapshot). Adapters must skip these at * mount time: their `source` path does not exist, so symlinking produces a * dangling link and content hashing throws. */ export declare function isPaperclipSkillSourceMissing(entry: PaperclipSkillEntry): boolean; export declare function parseObject(value: unknown): Record; export declare function asString(value: unknown, fallback: string): string; export declare function asNumber(value: unknown, fallback: number): number; export declare function asBoolean(value: unknown, fallback: boolean): boolean; export declare function asStringArray(value: unknown): string[]; export declare function parseJson(value: string): Record | null; export declare function appendWithCap(prev: string, chunk: string, cap?: number): string; export declare function appendWithByteCap(prev: string, chunk: string, cap?: number): string; export declare function resolvePathValue(obj: Record, dottedPath: string): string; export declare function renderTemplate(template: string, data: Record): string; export declare function joinPromptSections(sections: Array, separator?: string): string; type PaperclipWakeIssue = { id: string | null; identifier: string | null; title: string | null; description: string | null; descriptionTruncated: boolean; status: string | null; workMode: string | null; priority: string | null; }; type PaperclipWakeExecutionPrincipal = { type: "agent" | "user" | null; agentId: string | null; userId: string | null; }; type PaperclipWakeExecutionStage = { wakeRole: "reviewer" | "approver" | "executor" | null; stageId: string | null; stageType: string | null; currentParticipant: PaperclipWakeExecutionPrincipal | null; returnAssignee: PaperclipWakeExecutionPrincipal | null; reviewRequest: { instructions: string; } | null; lastDecisionOutcome: string | null; allowedActions: string[]; }; type PaperclipWakeComment = { id: string | null; issueId: string | null; body: string; bodyTruncated: boolean; createdAt: string | null; authorType: string | null; authorId: string | null; }; type PaperclipWakePlanReviewAuthor = { type: string | null; id: string | null; }; type PaperclipWakeAnnotationDelta = { id: string | null; issueId: string | null; threadId: string | null; documentKey: string | null; revisionNumber: number | null; quote: string; prefix: string; suffix: string; threadStatus: string | null; anchorState: string | null; anchorConfidence: string | null; body: string; bodyTruncated: boolean; createdAt: string | null; author: PaperclipWakePlanReviewAuthor | null; }; type PaperclipWakePlanReviewComment = { id: string | null; threadId: string | null; body: string; bodyTruncated: boolean; author: PaperclipWakePlanReviewAuthor | null; createdAt: string | null; updatedAt: string | null; }; type PaperclipWakePlanReviewThread = { id: string | null; documentKey: string | null; documentId: string | null; status: string | null; revisionId: string | null; revisionNumber: number | null; anchorState: string | null; anchorConfidence: string | null; selectedText: string; selectedTextTruncated: boolean; prefixText: string; prefixTextTruncated: boolean; suffixText: string; suffixTextTruncated: boolean; author: PaperclipWakePlanReviewAuthor | null; commentCount: number; comments: PaperclipWakePlanReviewComment[]; commentsTruncated: boolean; createdAt: string | null; updatedAt: string | null; }; type PaperclipWakePlanReviewInteractionTarget = { issueId: string | null; documentId: string | null; key: string | null; revisionId: string | null; revisionNumber: number | null; }; type PaperclipWakePlanReviewInteractionResult = { outcome: string | null; reason: string | null; note: string | null; commentId: string | null; }; type PaperclipWakePlanReviewInteraction = { id: string | null; kind: string | null; status: string | null; continuationPolicy: string | null; sourceCommentId: string | null; sourceRunId: string | null; target: PaperclipWakePlanReviewInteractionTarget | null; acceptedTargetRevision: PaperclipWakePlanReviewInteractionTarget | null; result: PaperclipWakePlanReviewInteractionResult | null; resolvedAt: string | null; }; type PaperclipWakePlanReviewContext = { documentKey: string | null; issueId: string | null; latestRevisionId: string | null; latestRevisionNumber: number | null; threads: PaperclipWakePlanReviewThread[]; interaction: PaperclipWakePlanReviewInteraction | null; totals: { openThreadCount: number; includedThreadCount: number; omittedThreadCount: number; commentCount: number; includedCommentCount: number; omittedCommentCount: number; }; limits: { maxThreads: number; maxComments: number; maxBodyChars: number; maxTotalBodyChars: number; maxAnchorTextChars: number; } | null; truncated: boolean; }; type PaperclipWakeDocumentReviewContext = { issueId: string | null; documents: Array; totals: PaperclipWakePlanReviewContext["totals"]; limits: PaperclipWakePlanReviewContext["limits"]; truncated: boolean; }; type PaperclipWakeContinuationSummary = { key: string | null; title: string | null; body: string; bodyTruncated: boolean; updatedAt: string | null; }; type PaperclipWakeLivenessContinuation = { attempt: number | null; maxAttempts: number | null; sourceRunId: string | null; state: string | null; reason: string | null; instruction: string | null; }; type PaperclipWakeChildIssueSummary = { id: string | null; identifier: string | null; title: string | null; status: string | null; priority: string | null; summary: string | null; }; type PaperclipWakeBlockerSummary = { id: string | null; identifier: string | null; title: string | null; status: string | null; priority: string | null; }; type PaperclipWakeTreeHoldSummary = { holdId: string | null; rootIssueId: string | null; mode: string | null; reason: string | null; }; type PaperclipWakeCheckboxSelection = { prompt: string | null; selectedOptionIds: string[]; selectedOptions: Array<{ id: string; label: string; description: string | null; }>; }; type PaperclipWakeQuestionResponse = { interactionId: string; summaryMarkdown: string; truncated: boolean; }; type PaperclipWakeExternalChatQuestionResponse = { schema: "paperclip.external_chat_question_response.v1"; interactionId: string; responseDeliveryId: string; sourceRunId: string; sourceCommentId: string; endpointId: string; conversationId: string; bindingSha256: string; }; type PaperclipWakeExecutionWorkspace = { branchName: string | null; }; type PaperclipWakeToolResult = { actionRequestId: string; toolName: string; resultSummary: string; error: string | null; declineReason: string | null; }; type PaperclipWakeAgentMessage = { untrustedToolResults?: PaperclipWakeToolResult[]; text: string; source: string | null; pluginKey: string | null; sessionId: string | null; }; type PaperclipWakeRecovery = { cause: string | null; failureSummary: string | null; originalAssignee: { id: string | null; name: string | null; } | null; attemptCount: number | null; maxAttempts: number | null; nextAction: string | null; routingFallbackReason: string | null; }; export type PaperclipExternalChatProvider = "slack" | "github" | "discord" | "microsoft-teams" | "telegram" | "imessage-photon" | "speko"; type PaperclipWakePayload = { executionContinuation: ExecutionContinuationEnvelope | null; reason: string | null; originalReason: string | null; recovery: PaperclipWakeRecovery | null; issue: PaperclipWakeIssue | null; checkedOutByHarness: boolean; externalChatExecutionBound: boolean; externalChatProvider: PaperclipExternalChatProvider | null; externalChatQuestionResponse: PaperclipWakeExternalChatQuestionResponse | null; skillTest: boolean; simplifiedEnglishInteractions: boolean; dependencyBlockedInteraction: boolean; treeHoldInteraction: boolean; activeTreeHold: PaperclipWakeTreeHoldSummary | null; unresolvedBlockerIssueIds: string[]; unresolvedBlockerSummaries: PaperclipWakeBlockerSummary[]; executionStage: PaperclipWakeExecutionStage | null; continuationSummary: PaperclipWakeContinuationSummary | null; planReviewContext: PaperclipWakePlanReviewContext | null; documentReviewContext: PaperclipWakeDocumentReviewContext | null; dispositionRepair: PaperclipWakeLivenessContinuation | null; livenessContinuation: PaperclipWakeLivenessContinuation | null; taskWatchdog: PaperclipWakeTaskWatchdogContext | null; interactionId: string | null; sourceRunId: string | null; interactionKind: string | null; interactionStatus: string | null; externalInteractionContinuation: boolean; checkboxSelection: PaperclipWakeCheckboxSelection | null; questionResponse: PaperclipWakeQuestionResponse | null; executionWorkspace: PaperclipWakeExecutionWorkspace | null; agentMessage: PaperclipWakeAgentMessage | null; annotationDeltas: PaperclipWakeAnnotationDelta[]; childIssueSummaries: PaperclipWakeChildIssueSummary[]; childIssueSummaryTruncated: boolean; commentIds: string[]; latestCommentId: string | null; comments: PaperclipWakeComment[]; requestedCount: number; includedCount: number; missingCount: number; truncated: boolean; fallbackFetchNeeded: boolean; }; export declare function normalizePaperclipWakePayload(value: unknown): PaperclipWakePayload | null; export declare function stringifyPaperclipWakePayload(value: unknown, options?: { omitIssueDescription?: boolean; }): string | null; /** Source ownership only. Canonical task, plan, response and event data stay in their existing fields. */ export interface PaperclipTurnContext { version: 1; assignment: { owner: "task_markdown"; description?: { id: string; revision: string | null; }; }; events: { owner: "wake_prompt"; comments: Array<{ id: string; revision: string | null; }>; }; } /** True when the structured prompt owns current wake comments. */ export declare function paperclipWakeCommentsArePromptOwned(value: unknown): boolean; export declare function isPaperclipRecoveryWakePayload(value: unknown): boolean; /** * Recognize only the closed, server-attested answered-chat shape for prompting. * The server must independently authorize the marker against durable state; * this shape check is not a grant of publication, task, or tool authority. */ export declare function isPaperclipExternalChatQuestionResponseTurn(value: unknown): boolean; /** * Returns true only for an ordinary external-chat task wake that the trusted * Paperclip harness has already authenticated, bound to a concrete issue, and * checked out for this run. Provider-like text elsewhere in the payload cannot * opt a turn into this contract. */ export declare function isPaperclipExternalChatTurn(value: unknown): boolean; /** * Returns true for either an inline-complete external-chat turn or the exact * overflow shape that a native runner can satisfy through its closed reader. * Callers must not assume the reader exists outside the native-runner lane. */ export declare function isPaperclipExternalChatContractTurn(value: unknown): boolean; export declare function readPaperclipIssueWorkModeFromContext(value: unknown): string | null; export declare function isAssignmentShapedPaperclipWakeReason(reason: string | null | undefined): boolean; export declare function hydrateFreshSessionHandoff(ctx: Pick, options?: { resumedSession?: boolean; }): Promise; export declare function selectInitialCommunicationGuidance(context: Record | null | undefined, options?: { resumedSession?: boolean; }): string; export declare function selectPaperclipTaskMarkdown(context: Record | null | undefined, options?: { resumedSession?: boolean; includeCommunicationGuidance?: boolean; }): string; /** * Select Paperclip-owned sections together, at the actual provider attempt. * Assignment Markdown owns the brief; the wake renderer owns current events. * Adapters choose carriers and templates, not a second task/context policy. * Recompute with resumedSession=false when recovery starts a fresh attempt. */ export declare function selectPaperclipPromptSections(context: Record | null | undefined, options?: { resumedSession?: boolean; includeCommunicationGuidance?: boolean; /** @deprecated Generic execution instructions are no longer injected. */ includeExecutionContract?: boolean; nativeWakeReaderAvailable?: boolean; paperclipAccess?: PaperclipAccessMode; }): { taskContextNote: string; wakePrompt: string; }; export declare function renderPaperclipWakePrompt(value: unknown, options?: Parameters[1]): string; declare function renderPaperclipWakePromptBody(value: unknown, options?: { resumedSession?: boolean; /** @deprecated Generic execution instructions are no longer injected. */ includeExecutionContract?: boolean; conversationMode?: boolean; nativeWakeReaderAvailable?: boolean; suppressIssueDescription?: boolean; paperclipAccess?: PaperclipAccessMode; }): string; export declare function redactEnvForLogs(env: Record): Record; export declare function redactCommandTextForLogs(command: string): string; export declare function buildInvocationEnvForLogs(env: Record, options?: { runtimeEnv?: NodeJS.ProcessEnv | Record; includeRuntimeKeys?: string[]; resolvedCommand?: string | null; resolvedCommandEnvKey?: string; }): Record; export declare const AGENT_IDENTITY_ENV_KEYS: string[]; export declare function buildAgentIdentityEnv(identity?: AgentRuntimeIdentity): Record; export declare function buildPaperclipEnv(agent: { id: string; companyId: string; }, options?: { sameHost?: boolean; }, identity?: AgentRuntimeIdentity): Record; export declare function applyPaperclipWorkspaceEnv(env: Record, input: { workspaceCwd?: string | null; workspaceSource?: string | null; workspaceStrategy?: string | null; workspaceId?: string | null; workspaceRepoUrl?: string | null; workspaceRepoRef?: string | null; workspaceBranch?: string | null; workspaceWorktreePath?: string | null; agentHome?: string | null; }): Record; export declare function shapePaperclipWorkspaceEnvForExecution(input: { workspaceCwd?: string | null; workspaceWorktreePath?: string | null; workspaceHints?: Array>; executionTargetIsRemote?: boolean; executionCwd?: string | null; /** * On a remote target, the map of referenced (mentioned) project id to the staged in-sandbox * directory that received that project's tree (`project-`). A non-anchor hint whose * `projectId` has an entry repoints its `cwd` to the staged directory. A non-anchor hint with no * entry loses its `cwd`, so the agent never receives a path the transport did not stage. The map * is empty on a local target and defaults to empty, so a caller that passes nothing keeps the * previous behavior (every non-anchor hint loses its `cwd` on a remote target). */ stagedProjectDirs?: Record; }): { workspaceCwd: string | null; workspaceWorktreePath: string | null; workspaceHints: Array>; }; export declare function rewriteWorkspaceCwdEnvVarsForExecution(input: { env: Record; workspaceCwd?: string | null; executionCwd?: string | null; executionTargetIsRemote?: boolean; }): Record; export declare function refreshPaperclipWorkspaceEnvForExecution(input: { env: Record; envConfig?: Record; workspaceCwd?: string | null; workspaceSource?: string | null; workspaceStrategy?: string | null; workspaceId?: string | null; workspaceRepoUrl?: string | null; workspaceRepoRef?: string | null; workspaceBranch?: string | null; workspaceWorktreePath?: string | null; workspaceHints?: Array>; agentHome?: string | null; executionTargetIsRemote?: boolean; executionCwd?: string | null; /** Referenced-project id to staged in-sandbox directory map; see {@link shapePaperclipWorkspaceEnvForExecution}. */ stagedProjectDirs?: Record; }): { workspaceCwd: string | null; workspaceWorktreePath: string | null; workspaceHints: Array>; }; export declare function sanitizeInheritedPaperclipEnv(baseEnv: NodeJS.ProcessEnv): NodeJS.ProcessEnv; export declare function defaultPathForPlatform(): "/usr/local/bin:/opt/homebrew/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin" | "C:\\Windows\\System32;C:\\Windows;C:\\Windows\\System32\\Wbem"; export declare function resolveCommandForLogs(command: string, cwd: string, env: NodeJS.ProcessEnv, options?: { remoteExecution?: RemoteExecutionSpec | null; }): Promise; export declare function sanitizeSshRemoteEnv(env: Record, inheritedEnv?: NodeJS.ProcessEnv): Record; export declare function ensurePathInEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv; export declare function ensureAbsoluteDirectory(cwd: string, opts?: { createIfMissing?: boolean; }): Promise; export declare function resolvePaperclipSkillsDir(moduleDir: string, additionalCandidates?: string[]): Promise; export declare function listPaperclipSkillEntries(moduleDir: string, additionalCandidates?: string[]): Promise; export declare function readInstalledSkillTargets(skillsHome: string): Promise>; export declare function buildRuntimeMountedSkillSnapshot(options: RuntimeMountedSkillSnapshotOptions): AdapterSkillSnapshot; export declare function buildPersistentSkillSnapshot(options: PersistentSkillSnapshotOptions): AdapterSkillSnapshot; export declare function readPaperclipRuntimeSkillEntries(config: Record, moduleDir: string, additionalCandidates?: string[]): Promise; export declare function readPaperclipSkillMarkdown(moduleDir: string, skillKey: string): Promise; export declare function readPaperclipSkillSyncPreference(config: Record): { explicit: boolean; desiredSkills: string[]; desiredSkillEntries: PaperclipDesiredSkillEntry[]; }; export declare function resolvePaperclipDesiredSkillNames(config: Record, availableEntries: Array<{ key: string; runtimeName?: string | null; }>): string[]; /** * Legacy adapters call the Paperclip API through the operational skill. Keep * that skill mounted even when an agent predates skill preferences or carries * an explicit empty desired set. Native runners provide the same authority * through their protocol and must continue to use the configurable-only * resolver above. */ export declare const PAPERCLIP_OPERATIONAL_SKILL_KEY = "paperclipai/paperclip/paperclip"; export declare const PAPERCLIP_FEEDBACK_SKILL_KEYS: readonly ["paperclipai/paperclip/complain", "paperclipai/paperclip/suggestion-box"]; /** * Native Paperclip Runner sessions receive the control-plane contract through * PRP, so carrying the legacy operational skill into their stored preference * is redundant and invalid. Normalize it away at persistence boundaries. * Legacy adapters remain unchanged because their runtime resolver mounts the * operational skill automatically, including after switching back. */ export declare function normalizePaperclipOperationalSkillPreference(adapterType: string, config: Record): Record; /** Apply the persisted defaults and skill contract for the native runner. */ export declare function normalizePaperclipRunnerAdapterConfig(adapterType: string, config: Record): Record; export declare function resolveLegacyPaperclipDesiredSkillNames(config: Record, availableEntries: Array<{ key: string; runtimeName?: string | null; }>): string[]; export declare function writePaperclipSkillSyncPreference(config: Record, desiredSkills: Array): Record; export declare function ensurePaperclipSkillSymlink(source: string, target: string, linkSkill?: (source: string, target: string) => Promise): Promise<"created" | "repaired" | "skipped">; export declare function relinkPaperclipSkillsFromOtherInstalls(skillsHome: string, entries: ReadonlyArray<{ source: string; runtimeName: string; }>): Promise; export declare function materializePaperclipSkillCopy(source: string, target: string): Promise; export declare function removeMaintainerOnlySkillSymlinks(skillsHome: string, allowedSkillNames: Iterable): Promise; export declare function ensureCommandResolvable(command: string, cwd: string, env: NodeJS.ProcessEnv, options?: { remoteExecution?: RemoteExecutionSpec | null; }): Promise; export declare function runChildProcess(runId: string, command: string, args: string[], opts: { cwd: string; env: Record; timeoutSec: number; graceSec: number; onLog: (stream: "stdout" | "stderr", chunk: string) => Promise; onLogError?: (err: unknown, runId: string, message: string) => void; onSpawn?: (meta: { pid: number; processGroupId: number | null; startedAt: string; }) => Promise; terminalResultCleanup?: TerminalResultCleanupOptions; stdin?: string; liveStdin?: BoundLiveStdinChannel | null; remoteExecution?: RemoteExecutionSpec | null; localProcessSandbox?: LocalProcessSandboxOptions | null; }): Promise; export {}; //# sourceMappingURL=server-utils.d.ts.map