import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { AgentTickClient } from "./src/agentTickClient"; import { registerAskUserTools } from "./src/askUserTool"; import { resolveAgentTickConfig } from "./src/config"; import { resolveAgentTickExtensionConfig } from "./src/extensionConfig"; import { handleSanctionToolCall } from "./src/sanctions"; import { createAgentTickSessionContext } from "./src/session"; import { StatusReporter, summarizeMirrorFailure } from "./src/status"; import { registerStatusHooks } from "./src/statusHooks"; let hasNotifiedAgentTickDisabled = false; function createAgentTickClient(ctx?: { ui?: { notify?: (message: string, level?: string) => void } }): AgentTickClient | null { const config = resolveAgentTickConfig(); if (!config) { const explicitlyDisabled = process.env.PI_AGENT_TICK_DISABLED === "1" || process.env.PI_AGENT_TICK_DISABLED === "true"; if (!explicitlyDisabled && ctx?.ui?.notify && !hasNotifiedAgentTickDisabled) { hasNotifiedAgentTickDisabled = true; ctx.ui.notify("Agent Tick mirroring disabled — run `agent-tick login` to enable phone/web prompts", "info"); } return null; } return new AgentTickClient(config); } export default function(pi: ExtensionAPI) { const extensionConfig = resolveAgentTickExtensionConfig({ onWarning: (warning) => { const message = warning.path ? `${warning.message} (${warning.path})` : warning.message; // Config warnings are best-effort because extension load can happen // before a UI context exists. console.warn(message); }, }); const sessionContext = createAgentTickSessionContext(); const statusClient = createAgentTickClient(); const statusReporter = statusClient && extensionConfig.status.enabled ? new StatusReporter(statusClient, 5000, 60_000, sessionContext, (error) => { // Best-effort, once-per-session: surface remote Activity mirror // failures (e.g. schema drift) to Pi logs so missing phone/web // status is not silent. Local Pi status continues regardless. console.warn(`Agent Tick could not send remote Activity (${summarizeMirrorFailure(error)}). Local Pi status continues.`); }) : null; registerStatusHooks(pi, statusReporter, extensionConfig, sessionContext); const maybeOn = (pi as unknown as { on?: (event: string, handler: (...args: any[]) => any) => void }).on; if (typeof maybeOn === "function") { maybeOn.call(pi, "tool_call", (event: any, ctx: any) => handleSanctionToolCall({ event, ctx, createClient: createAgentTickClient, statusReporter, sessionContext, config: extensionConfig.sanctions, })); } registerAskUserTools(pi, { statusReporter, createAgentTickClient, sessionContext, }); }