import { tool } from '@opencode-ai/plugin';
import { StructuredError } from '@agentuity/core';
import { existsSync } from 'node:fs';
import { homedir, platform } from 'node:os';
import { join } from 'node:path';
import { z } from 'zod';
import { loadAllSkills } from '../skills';
import { agents } from '../agents';
import { loadCoderConfig, getDefaultConfig, mergeConfig, validateAndWarnConfigs } from '../config';
import { createSessionHooks } from './hooks/session';
import { createToolHooks, getCoderProfile } from './hooks/tools';
import { createKeywordHooks } from './hooks/keyword';
import { createParamsHooks } from './hooks/params';
import { createCadenceHooks } from './hooks/cadence';
import { createSessionMemoryHooks } from './hooks/session-memory';
import { createCompletionHooks } from './hooks/completion';
import { OpenCodeDBReader } from '../sqlite';
import { TmuxSessionManager } from '../tmux';
import { checkAuth } from '../services/auth';
const sessionInputSchema = z.object({
    sessionID: z.string().optional(),
});
// ─────────────────────────────────────────────────────────────────────────────
// Memory Share Tool Errors
// ─────────────────────────────────────────────────────────────────────────────
const MemoryShareAuthError = StructuredError('MemoryShareAuthError', 'Authentication required to share memory content')();
const MemoryShareCLIError = StructuredError('MemoryShareCLIError', 'CLI command failed to create stream')();
const MemoryShareError = StructuredError('MemoryShareError', 'Failed to create public memory share')();
const OpenCodeDashboardUnavailableError = StructuredError('OpenCodeDashboardUnavailableError', 'OpenCode SQLite database is not available. Requires OpenCode v1.2.0+ with SQLite storage.');
// Sandbox environment detection
const SANDBOX_ID = process.env.AGENTUITY_SANDBOX_ID;
const IN_SANDBOX = !!SANDBOX_ID;
// Sandbox context injected into Lead, Builder, and Architect prompts
const SANDBOX_CONTEXT = IN_SANDBOX
    ? `
## Sandbox Environment

You are running inside an Agentuity Sandbox (ID: ${SANDBOX_ID}).

**Permissions:** All file operations are allowed without prompts.

**File Locations:**
- Working directory: \`/home/agentuity\`
- Temp files: \`/home/agentuity/tmp/\` (preferred over \`/tmp/\`)
- Artifacts: \`/home/agentuity/.agentuity/\`

**Tips:**
- No permission prompts - you can read/write freely
- Sandbox is isolated - safe to experiment
- Use \`/home/agentuity/\` paths for all file operations
`
    : '';
// Agents that should receive sandbox context in their prompts
const SANDBOX_AWARE_AGENTS = ['lead', 'builder', 'architect'];
export async function createCoderPlugin(ctx) {
    ctx.client.app.log({
        body: {
            service: 'agentuity-coder',
            level: 'info',
            message: 'Agentuity Coder plugin initializing',
        },
    });
    const userConfig = await loadCoderConfig();
    const coderConfig = mergeConfig(getDefaultConfig(), userConfig);
    const resolvedDbPath = resolveOpenCodeDBPath();
    const dbReader = new OpenCodeDBReader(resolvedDbPath ? { dbPath: resolvedDbPath } : undefined);
    // Shared Map: chat.params stores the user's message text per session,
    // chat.message reads it for trigger detection (avoids scanning model output).
    const lastUserMessages = new Map();
    const sessionHooks = createSessionHooks(ctx, coderConfig);
    const toolHooks = createToolHooks(ctx, coderConfig);
    const keywordHooks = createKeywordHooks(ctx, coderConfig);
    const paramsHooks = createParamsHooks(ctx, coderConfig);
    const completionHooks = createCompletionHooks(ctx, coderConfig);
    const tmuxManager = coderConfig.tmux?.enabled
        ? new TmuxSessionManager(ctx, coderConfig.tmux, {
            onLog: (message) => ctx.client.app.log({
                body: {
                    service: 'agentuity-coder',
                    level: 'info',
                    message,
                },
            }),
        })
        : undefined;
    // Create hooks for session routing and compaction behavior
    const cadenceHooks = createCadenceHooks(ctx, coderConfig, dbReader, lastUserMessages);
    // Session memory hooks handle checkpointing and compaction for non-Cadence sessions
    // Orchestration (deciding which module handles which session) happens below in the hooks
    const sessionMemoryHooks = createSessionMemoryHooks(ctx, coderConfig, dbReader);
    const configHandler = createConfigHandler(coderConfig);
    // Create plugin tools using the @opencode-ai/plugin tool helper
    const tools = createTools(dbReader);
    // Create a logger for shutdown handler
    const shutdownLogger = (message) => ctx.client.app.log({
        body: {
            service: 'agentuity-coder',
            level: 'info',
            message: `[shutdown] ${message}`,
        },
    });
    registerShutdownHandler(tmuxManager, shutdownLogger, dbReader);
    // Show startup toast (fire and forget, don't block)
    try {
        ctx.client.tui.showToast({
            body: { message: '🚀 Agentuity Coder ready', variant: 'success' },
        });
    }
    catch {
        // Toast may not be available
    }
    return {
        ...(tools ? { tool: tools } : {}),
        config: configHandler,
        'chat.message': async (input, output) => {
            completionHooks.onMessage(input);
            await keywordHooks.onMessage(input, output);
            await sessionHooks.onMessage(input, output);
            await cadenceHooks.onMessage(input, output);
        },
        'chat.params': async (input, output) => {
            completionHooks.onParams(input);
            await paramsHooks.onParams(input, output);
        },
        'tool.execute.before': toolHooks.before,
        'tool.execute.after': toolHooks.after,
        'shell.env': async (_input, output) => {
            if (typeof output !== 'object' || output === null) {
                return;
            }
            const profile = getCoderProfile();
            const out = output;
            out.env ??= {};
            out.env.AGENTUITY_PROFILE = profile;
            out.env.AGENTUITY_AGENT_MODE = 'opencode';
            if (resolvedDbPath) {
                out.env.OPENCODE_DB_PATH = resolvedDbPath;
            }
            const sessionId = process.env.AGENTUITY_OPENCODE_SESSION;
            if (sessionId) {
                out.env.AGENTUITY_OPENCODE_SESSION = sessionId;
            }
        },
        'command.execute.before': async (input, _output) => {
            const result = sessionInputSchema.safeParse(input);
            if (result.success && result.data.sessionID) {
                process.env.AGENTUITY_OPENCODE_SESSION = result.data.sessionID;
            }
        },
        event: async (input) => {
            // Orchestrate: route to appropriate module based on session type
            const sessionId = extractSessionIdFromEvent(input);
            if (sessionId) {
                // Try lazy restore from KV if not in memory (survives plugin restarts)
                if (!cadenceHooks.isActiveCadenceSession(sessionId)) {
                    await cadenceHooks.tryRestoreFromKV(sessionId);
                }
                if (cadenceHooks.isActiveCadenceSession(sessionId)) {
                    await cadenceHooks.onEvent(input);
                }
                else {
                    // Non-Cadence sessions - handle session.compacted for checkpointing
                    await sessionMemoryHooks.onEvent(input);
                }
            }
        },
        'experimental.session.compacting': async (input, output) => {
            // Try lazy restore from KV if not in memory (survives plugin restarts)
            if (!cadenceHooks.isActiveCadenceSession(input.sessionID)) {
                await cadenceHooks.tryRestoreFromKV(input.sessionID);
            }
            // Orchestrate: route to appropriate module based on session type
            if (cadenceHooks.isActiveCadenceSession(input.sessionID)) {
                await cadenceHooks.onCompacting(input, output);
            }
            else {
                await sessionMemoryHooks.onCompacting(input, output);
            }
        },
    };
}
function createConfigHandler(coderConfig) {
    return async (config) => {
        const agentConfigs = createAgentConfigs(coderConfig);
        const commands = createCommands();
        const loadedSkills = await loadAllSkills(coderConfig.skills);
        const skillCommands = createSkillCommands(loadedSkills);
        // Merge agent configs: our defaults first, then user's opencode.json overrides on top
        // This allows users to customize any agent via their opencode.json
        const userAgentConfigs = config.agent;
        const mergedAgents = { ...agentConfigs };
        // Shallow merge user overrides on top of our defaults (nested objects like tools are replaced, not merged)
        if (userAgentConfigs) {
            for (const [name, userConfig] of Object.entries(userAgentConfigs)) {
                if (mergedAgents[name]) {
                    // Merge user config on top of our default
                    mergedAgents[name] = {
                        ...mergedAgents[name],
                        ...userConfig,
                    };
                }
                else {
                    // User defined a new agent not in our defaults
                    mergedAgents[name] = userConfig;
                }
            }
        }
        config.agent = mergedAgents;
        // Validate merged configs and warn about mismatches
        validateAndWarnConfigs(mergedAgents);
        // Permission configuration for external directories
        // Memory agent and other operations may need to write temp files for CLI piping
        if (IN_SANDBOX) {
            // In sandbox, allow all permissions without prompts
            config.permission = {
                '*': 'allow',
                external_directory: {
                    '/home/agentuity/**': 'allow',
                    '*': 'allow',
                },
            };
        }
        else {
            // For non-sandbox environments, auto-allow temp directory writes
            // This prevents blocking prompts when Memory agent writes large JSON for CLI piping
            const existingPermissions = config.permission ?? {};
            const existingExternalDir = existingPermissions.external_directory ?? {};
            // Normalize TMPDIR: strip trailing slashes, then append /**
            const tmpdir = process.env.TMPDIR?.replace(/\/+$/, '');
            const tmpdirPattern = tmpdir ? `${tmpdir}/**` : null;
            config.permission = {
                ...existingPermissions,
                external_directory: {
                    ...existingExternalDir,
                    '/tmp/**': 'allow',
                    // Also allow OS-specific temp directories
                    ...(tmpdirPattern ? { [tmpdirPattern]: 'allow' } : {}),
                },
            };
        }
        // Compaction config: increase reserved token buffer to accommodate our enriched
        // compaction prompts (planning state, image descriptions, tool summaries, diagnostics).
        // Default OpenCode reserved buffer is too small for the context we inject.
        const existingCompaction = (config.compaction ?? {});
        const existingReserved = existingCompaction.reserved;
        config.compaction = {
            ...existingCompaction,
            reserved: typeof existingReserved === 'number' ? existingReserved : 40_000,
        };
        config.command = {
            ...config.command,
            ...commands,
            ...skillCommands,
        };
    };
}
function createAgentConfigs(_config) {
    const result = {};
    for (const agent of Object.values(agents)) {
        // Convert tools.exclude to Open Code format (tool: false)
        const tools = {};
        if (agent.tools?.exclude) {
            for (const tool of agent.tools.exclude) {
                tools[tool] = false;
            }
        }
        // Inject sandbox context into specific agents when running in sandbox
        const shouldInjectSandbox = IN_SANDBOX && SANDBOX_AWARE_AGENTS.includes(agent.role);
        const prompt = shouldInjectSandbox
            ? `${agent.systemPrompt}\n${SANDBOX_CONTEXT}`
            : agent.systemPrompt;
        // Use agent defaults directly - user overrides happen in createConfigHandler
        result[agent.displayName] = {
            description: agent.description,
            model: agent.defaultModel,
            prompt,
            mode: agent.mode ?? 'subagent',
            ...(Object.keys(tools).length > 0 ? { tools } : {}),
            ...(agent.variant ? { variant: agent.variant } : {}),
            ...(agent.temperature !== undefined ? { temperature: agent.temperature } : {}),
            ...(agent.maxSteps !== undefined ? { maxSteps: agent.maxSteps } : {}),
            ...(agent.reasoningEffort ? { reasoningEffort: agent.reasoningEffort } : {}),
            ...(agent.thinking ? { thinking: agent.thinking } : {}),
            ...(agent.hidden ? { hidden: agent.hidden } : {}),
            ...(agent.fallbackModels?.length ? { fallbackModels: agent.fallbackModels } : {}),
        };
    }
    return result;
}
function createCommands() {
    return {
        'agentuity-coder': {
            name: 'agentuity-coder',
            description: 'Run a task with the Agentuity Coder agent team (use @Agentuity Coder Lead, @Agentuity Coder Scout, etc.)',
            template: `<coder-mode>
You are the Agentuity Coder Lead agent orchestrating the Agentuity Coder team.

## Your Team (use @mentions to invoke)
- **@Agentuity Coder Scout**: Explore codebase, find patterns, research docs (read-only)
- **@Agentuity Coder Builder**: Implement features, write code, run tests
- **@Agentuity Coder Architect**: Complex autonomous tasks, Cadence mode (GPT Codex)
- **@Agentuity Coder Reviewer**: Review changes, catch issues, apply fixes
- **@Agentuity Coder Memory**: Store context, remember decisions, extract conclusions
- **@Agentuity Coder Expert**: Agentuity CLI, SDK, Services, and Documentation specialist
- **@Agentuity Coder Runner**: Run lint/build/test commands, returns structured results
- **@Agentuity Coder Product**: Clarify requirements, validate features, track progress

## Task
$ARGUMENTS

## Guidelines
1. Use @Agentuity Coder Scout first to understand context
2. Use @Agentuity Coder Product to clarify requirements if unclear
3. Delegate implementation to @Agentuity Coder Builder (or Architect for complex work)
4. Delegate lint/build/test commands to @Agentuity Coder Runner for structured results
5. Have @Agentuity Coder Reviewer check the work
6. Use @Agentuity Coder Expert for Agentuity CLI, SDK, Services, and Documentation questions
7. Only use cloud services when genuinely helpful
8. **When done, tell @Agentuity Coder Memory to memorialize the session**
</coder-mode>`,
            agent: 'Agentuity Coder Lead',
            argumentHint: '"task description"',
        },
        'agentuity-memory-save': {
            name: 'agentuity-memory-save',
            description: 'Save the current session to memory for future recall',
            template: `Memorialize this session. Summarize what was accomplished in this conversation:
- Problem/task that was addressed
- Key decisions and their rationale  
- Corrections/mistakes (user corrected agent or agent corrected user)
- Patterns and approaches used
- Solutions implemented
- Files and folders referenced
- Open questions or follow-ups

Save to vector storage using the agentuity-opencode-sessions namespace. Store any corrections prominently in agentuity-opencode-memory KV.

$ARGUMENTS`,
            agent: 'Agentuity Coder Memory',
            argumentHint: '(optional additional context)',
        },
        'agentuity-memory-share': {
            name: 'agentuity-memory-share',
            description: '🔗 Share memory content publicly with a shareable URL',
            template: `User wants to share content publicly.

**You have current session context. Memory does not (unless given a session ID).**

- Current session → Handle directly: compile content, call \`agentuity_memory_share\`
- Stored content (specific ID, past work) → Delegate to Memory
- Long Cadence cycle? → Ask Memory for past compactions to include

User's request: $ARGUMENTS`,
            agent: 'Agentuity Coder Lead',
            argumentHint: '"share a summary of this session" or "share the auth decisions with 1 hour TTL"',
        },
        // ─────────────────────────────────────────────────────────────────────
        // Agentuity Cloud Service Commands
        // ─────────────────────────────────────────────────────────────────────
        'agentuity-cloud': {
            name: 'agentuity-cloud',
            description: '☁️ Agentuity cloud services (KV, Storage, Vector, Sandbox, DB, SSH, etc.)',
            template: `You are the Agentuity Coder Expert helping with Agentuity cloud services.

Use the \`agentuity\` CLI to execute the user's request.

## Available Services
| Service | CLI | Purpose |
|---------|-----|---------|
| KV | \`agentuity cloud kv\` | Key-value storage (namespaces, keys) |
| Storage | \`agentuity cloud storage\` | Object/file storage (buckets) |
| Vector | \`agentuity cloud vector\` | Embeddings & semantic search |
| Sandbox | \`agentuity cloud sandbox\` | Isolated execution environments |
| Database | \`agentuity cloud db\` | Postgres databases |
| SSH | \`agentuity cloud ssh\` | SSH into deployments/sandboxes |
| Deployments | \`agentuity cloud deployment\` | Manage deployments |
| Agents | \`agentuity cloud agent\` | Cloud agent management |
| Sessions | \`agentuity cloud session\` | Agent session data |
| Threads | \`agentuity cloud thread\` | Conversation threads |

## Guidelines
1. First check auth: \`agentuity auth whoami\`
2. Prefer \`--json\` for programmatic output
3. List/inspect before creating new resources
4. Explain what commands you're running

## User Request
$ARGUMENTS`,
            agent: 'Agentuity Coder Expert',
            subtask: true,
            argumentHint: '"list kv namespaces" or "upload file.txt to storage"',
        },
        'agentuity-sandbox': {
            name: 'agentuity-sandbox',
            description: '🏖️ Agentuity sandboxes (isolated execution environments)',
            template: `You are the Agentuity Coder Expert helping with Agentuity sandboxes.

Use the \`agentuity cloud sandbox\` CLI commands to help the user.

## Common Commands
\`\`\`bash
agentuity cloud sandbox runtime list --json                            # List available runtimes (bun:1, python:3.14, etc.)
agentuity cloud sandbox run [--memory 1Gi] [--cpu 1000m] \\
  [--runtime <name>] [--runtimeId <id>] \\
  [--name <name>] [--description <text>] \\
  -- <command>                                                         # One-shot execution
agentuity cloud sandbox create --json [--memory 1Gi] [--cpu 1000m] \\
  [--network] [--runtime <name>] [--runtimeId <id>] \\
  [--name <name>] [--description <text>]                              # Create persistent sandbox
agentuity cloud sandbox list --json                                   # List sandboxes (includes telemetry)
agentuity cloud sandbox exec <id> -- <command>                        # Run in existing sandbox
agentuity cloud sandbox files <id> [path] --json                      # List files
agentuity cloud sandbox cp ./local <id>:/home/agentuity               # Copy files to sandbox
agentuity cloud sandbox delete <id> --json                            # Delete sandbox
agentuity cloud sandbox snapshot create <id> \\
  [--name <name>] [--description <text>] [--tag <tag>]                # Save sandbox state
\`\`\`

## Guidelines
1. First check auth: \`agentuity auth whoami\`
2. Use \`--json\` for programmatic output
3. Explain what commands you're running
4. Default working directory inside sandboxes: \`/home/agentuity\`
5. Use \`runtime list\` to find runtimes, then pass \`--runtime\` or \`--runtimeId\` on \`run\`/\`create\`
6. Use \`--name\` and \`--description\` for better tracking
7. Snapshot \`--tag\` defaults to \`latest\`, max 128 chars, must match \`^[a-zA-Z0-9][a-zA-Z0-9._-]*$\`
8. Telemetry fields from \`list\`/\`get\`: \`cpuTimeMs\`, \`memoryByteSec\`, \`networkEgressBytes\`, \`networkEnabled\`, \`mode\`

## User Request
$ARGUMENTS`,
            agent: 'Agentuity Coder Expert',
            subtask: true,
            argumentHint: '"run bun test" or "create a sandbox with 2Gi memory"',
        },
        // ─────────────────────────────────────────────────────────────────────
        // Agentuity Cadence Commands (Long-Running Tasks)
        // ─────────────────────────────────────────────────────────────────────
        'agentuity-cadence': {
            name: 'agentuity-cadence',
            description: '🔄 Start a long-running Cadence loop (autonomous task completion)',
            template: `[CADENCE MODE]

You are the Agentuity Coder Lead in **Cadence mode** — a long-running autonomous loop.

## Your Team (use @mentions to invoke)
- **@Agentuity Coder Scout**: Explore codebase, find patterns, research docs (read-only)
- **@Agentuity Coder Architect**: Complex autonomous implementation (GPT Codex with high reasoning) — **USE THIS FOR CADENCE**
- **@Agentuity Coder Builder**: Quick fixes, simple changes (for minor iterations only)
- **@Agentuity Coder Reviewer**: Review changes, catch issues, apply fixes
- **@Agentuity Coder Memory**: Store context, remember decisions, checkpoints, extract conclusions
- **@Agentuity Coder Expert**: Agentuity CLI, SDK, Services, and Documentation specialist
- **@Agentuity Coder Runner**: Run lint/build/test commands, returns structured results
- **@Agentuity Coder Product**: Clarify requirements, validate features, track progress, Cadence briefings

## Task
$ARGUMENTS

## Cadence Workflow

1. **FIRST: Establish PRD with Product** (REQUIRED):
   - Ask @Agentuity Coder Product to establish/validate the PRD for this task
   - Product will check for existing PRD or create one
   - This defines "what" we're building and success criteria

2. **Initialize loop state**:
   - Generate loop ID (format: \`lp_short_name_01\`)
   - Store in KV: \`agentuity cloud kv set agentuity-opencode-tasks "loop:{loopId}:state" '{...}'\`
   - Link session planning to PRD via \`prdKey\`

3. **Each iteration**:
   - Ask @Agentuity Coder Memory for relevant context
   - Use @Agentuity Coder Scout to understand what's needed
   - For complex planning, use extended thinking (ultrathink) — ground in PRD requirements
   - Delegate implementation to **@Agentuity Coder Architect** (preferred for Cadence)
   - Have @Agentuity Coder Reviewer verify the work
   - Tell @Agentuity Coder Memory to store checkpoint

4. **When truly complete**, output:
\`\`\`
<promise>DONE</promise>
\`\`\`

5. **Finalize**:
   - Tell @Agentuity Coder Product to update the PRD with completed work
   - Tell @Agentuity Coder Memory to memorialize the session

## Guidelines
- **Product first** — Always establish PRD before starting work
- **Use Architect for implementation** — Architect has GPT Codex with maximum reasoning, ideal for autonomous work
- Use regular Builder only for trivial fixes within an iteration
- Ask Memory for context at each iteration start
- Store checkpoints at each iteration end
- If stuck on architecture, use extended thinking (ultrathink) for deep planning
- Use @Agentuity Coder Expert for sandbox/cloud operations
- Respect max iterations (50 default)

## Lead-of-Leads (Parallel Work)
If the task has **independent workstreams** that can run in parallel (e.g., "build auth, payments, and notifications"):
1. Ask @Agentuity Coder Product to create PRD with workstreams
2. Spawn child Leads using the \`task\` tool (issue multiple task calls in a single response for parallel work)
3. Each child Lead claims a workstream, works autonomously, marks done when complete
4. Monitor progress via PRD workstream status
5. Do integration work when all children complete

**Don't use Lead-of-Leads for:** small tasks, sequential work, or work requiring tight coordination.`,
            agent: 'Agentuity Coder Lead',
            argumentHint: 'build the new auth feature with tests',
        },
    };
}
function createSkillCommands(skills) {
    const commands = {};
    for (const skill of skills) {
        const baseDir = normalizeBaseDir(skill.resolvedPath);
        commands[skill.name] = {
            name: skill.name,
            description: skill.metadata.description,
            template: `<skill-instruction>
Base directory for this skill: ${baseDir}/
File references (@path) in this skill are relative to this directory.

${skill.content}
</skill-instruction>

<user-request>
$ARGUMENTS
</user-request>`,
            ...(skill.metadata.agent ? { agent: skill.metadata.agent } : {}),
            ...(skill.metadata.model ? { model: skill.metadata.model } : {}),
            ...(skill.metadata['argument-hint']
                ? { argumentHint: skill.metadata['argument-hint'] }
                : {}),
            ...(skill.metadata.subtask ? { subtask: true } : {}),
        };
    }
    return commands;
}
function normalizeBaseDir(path) {
    return path.replace(/[\\/]+$/, '');
}
function createTools(dbReader) {
    // Use the schema from @opencode-ai/plugin's tool helper to avoid Zod version mismatches
    const s = tool.schema;
    const sessionDashboard = tool({
        description: 'Inspect a parent session dashboard from the local OpenCode SQLite database. Useful for Lead-of-Leads monitoring and nested session visibility.',
        args: {
            session_id: s.string().describe('Parent session ID to inspect'),
        },
        async execute(args) {
            if (!dbReader || !dbReader.isAvailable()) {
                const err = new OpenCodeDashboardUnavailableError();
                return JSON.stringify({
                    success: false,
                    error: err._tag,
                    message: err.message,
                });
            }
            const dashboard = dbReader.getSessionDashboard(args.session_id);
            const builtTree = buildDashboardTree(dbReader, dashboard.sessions);
            return JSON.stringify({
                success: true,
                sessionId: args.session_id,
                totalCost: dashboard.totalCost,
                summary: computeHealthSummary(builtTree),
                sessions: builtTree,
            });
        },
    });
    const memoryShare = tool({
        description: `Share memory content publicly via Agentuity Cloud Streams.

Creates a public URL that can be shared with anyone - no authentication required to access.
The content is stored in Agentuity's durable stream storage with optional TTL.

Use this when:
- User wants to share context with another agent/session
- User wants to export a summary, compaction, or session for external use
- User explicitly asks to "share" or "make public" some memory content

Returns the public URL that can be copied and used anywhere.`,
        args: {
            content: s.string().describe('The content to share publicly'),
            namespace: s
                .string()
                .optional()
                .describe('Stream namespace (default: agentuity-opencode-shares)'),
            ttl_seconds: s
                .number()
                .optional()
                .describe('TTL in seconds (60-31536000, or omit for 30-day default)'),
            content_type: s.string().optional().describe('Content type (default: text/markdown)'),
            metadata: s
                .record(s.string(), s.string())
                .optional()
                .describe('Optional metadata key-value pairs'),
            compress: s.boolean().optional().describe('Enable gzip compression'),
            region: s.string().optional().describe('Cloud region (use, usc, usw). Default: usc'),
        },
        async execute(args, context) {
            // Get the profile first - this ensures checkAuth() and CLI use the same profile
            const profile = getCoderProfile();
            const originalProfile = process.env.AGENTUITY_PROFILE;
            const sessionId = context.sessionID;
            try {
                // Set profile before auth check so checkAuth reads the correct config
                process.env.AGENTUITY_PROFILE = profile;
                // Check auth first
                const authResult = await checkAuth();
                if (!authResult.ok) {
                    const err = new MemoryShareAuthError({ reason: authResult.error });
                    return JSON.stringify({
                        success: false,
                        error: err.message,
                        errorTag: err._tag,
                        details: { reason: authResult.error },
                    });
                }
                // Build CLI command
                const namespace = args.namespace ?? 'agentuity-opencode-shares';
                const contentType = args.content_type ?? 'text/markdown';
                const cliArgs = ['agentuity', '--json', 'cloud', 'stream', 'create', namespace, '-'];
                cliArgs.push('--content-type', contentType);
                cliArgs.push('--region', args.region ?? 'usc');
                if (args.ttl_seconds !== undefined) {
                    cliArgs.push('--ttl', String(args.ttl_seconds));
                }
                if (args.compress) {
                    cliArgs.push('--compress');
                }
                if (args.metadata && Object.keys(args.metadata).length > 0) {
                    const metadataStr = Object.entries(args.metadata)
                        .map(([k, v]) => `${k}=${v}`)
                        .join(',');
                    cliArgs.push('--metadata', metadataStr);
                }
                const proc = Bun.spawn(cliArgs, {
                    stdin: 'pipe',
                    stdout: 'pipe',
                    stderr: 'pipe',
                    env: {
                        ...process.env,
                        AGENTUITY_PROFILE: profile,
                        AGENTUITY_AGENT_MODE: 'opencode',
                        ...(sessionId ? { AGENTUITY_OPENCODE_SESSION: sessionId } : {}),
                    },
                });
                // Write content to stdin (Bun's FileSink API)
                proc.stdin.write(new TextEncoder().encode(args.content));
                proc.stdin.end();
                const [stdout, stderr, exitCode] = await Promise.all([
                    new Response(proc.stdout).text(),
                    new Response(proc.stderr).text(),
                    proc.exited,
                ]);
                if (exitCode !== 0) {
                    const err = new MemoryShareCLIError({
                        exitCode,
                        stderr: stderr || `CLI exited with code ${exitCode}`,
                    });
                    return JSON.stringify({
                        success: false,
                        error: err.message,
                        errorTag: err._tag,
                        details: { exitCode, stderr },
                    });
                }
                // Parse JSON response from CLI
                const result = JSON.parse(stdout);
                return JSON.stringify({
                    success: true,
                    url: result.url,
                    id: result.id,
                    namespace: result.namespace,
                    sizeBytes: result.sizeBytes,
                    expiresAt: result.expiresAt,
                });
            }
            catch (error) {
                const reason = error instanceof Error ? error.message : 'Failed to create stream';
                const err = new MemoryShareError({ reason });
                return JSON.stringify({
                    success: false,
                    error: err.message,
                    errorTag: err._tag,
                    details: { reason },
                });
            }
            finally {
                // Restore original profile
                if (originalProfile !== undefined) {
                    process.env.AGENTUITY_PROFILE = originalProfile;
                }
                else {
                    delete process.env.AGENTUITY_PROFILE;
                }
            }
        },
    });
    return {
        agentuity_session_dashboard: sessionDashboard,
        agentuity_memory_share: memoryShare,
    };
}
// ─────────────────────────────────────────────────────────────────────────────
// Helper Functions
// ─────────────────────────────────────────────────────────────────────────────
function extractSessionIdFromEvent(input) {
    if (typeof input !== 'object' || input === null)
        return undefined;
    const inp = input;
    if (!inp.event?.properties)
        return undefined;
    return (inp.event.properties.sessionId ??
        inp.event.properties.sessionID);
}
function parseDisplayTitle(rawTitle) {
    try {
        const parsed = JSON.parse(rawTitle);
        if (typeof parsed === 'object' && parsed !== null) {
            if (typeof parsed.description === 'string')
                return parsed.description;
            if (typeof parsed.title === 'string')
                return parsed.title;
            if (typeof parsed.name === 'string')
                return parsed.name;
        }
        // Parsed but no useful field — return as-is
        return rawTitle;
    }
    catch {
        // Not JSON, return as-is
        return rawTitle;
    }
}
function getCurrentActivity(reader, sessionId) {
    // First check active tools
    const activeTools = reader.getActiveToolCalls(sessionId);
    const firstTool = activeTools[0];
    if (firstTool) {
        return `Running ${firstTool.tool}`;
    }
    // Fall back to latest message
    const latestMsg = reader.getLatestMessage(sessionId);
    if (!latestMsg)
        return null;
    if (latestMsg.error)
        return `Error: ${latestMsg.error.substring(0, 100)}`;
    // Try to get latest text part for a snippet
    const textParts = reader.getTextParts(sessionId, { limit: 1 });
    const firstPart = textParts[0];
    if (firstPart) {
        const text = firstPart.text.trim();
        if (text.length > 80)
            return text.substring(0, 77) + '...';
        return text;
    }
    return null;
}
function buildDashboardTree(reader, sessions) {
    return sessions.map((node) => {
        const status = reader.getSessionStatus(node.session.id);
        const activeTools = reader.getActiveToolCalls(node.session.id);
        return {
            session: {
                id: node.session.id,
                title: node.session.title,
                displayTitle: parseDisplayTitle(node.session.title),
                parentId: node.session.parentId,
                timeUpdated: node.session.timeUpdated,
                timeUpdatedISO: new Date(node.session.timeUpdated).toISOString(),
            },
            status: status.status,
            lastActivity: status.lastActivity,
            lastActivityISO: new Date(status.lastActivity).toISOString(),
            currentActivity: getCurrentActivity(reader, node.session.id),
            messageCount: node.messageCount,
            activeToolCount: node.activeToolCount,
            activeTools: activeTools.map((t) => ({ tool: t.tool, status: t.status })),
            todoSummary: node.todoSummary ?? { total: 0, pending: 0, completed: 0 },
            costSummary: node.costSummary,
            children: buildDashboardTree(reader, node.children),
        };
    });
}
function computeHealthSummary(nodes) {
    const summary = {
        active: 0,
        idle: 0,
        error: 0,
        archived: 0,
        compacting: 0,
        total: 0,
    };
    function walk(nodeList) {
        for (const node of nodeList) {
            summary.total++;
            const status = node.status;
            if (status in summary) {
                summary[status]++;
            }
            if (node.children.length > 0)
                walk(node.children);
        }
    }
    walk(nodes);
    return summary;
}
function resolveOpenCodeDBPath() {
    const envPath = process.env.OPENCODE_DB_PATH;
    if (envPath) {
        if (isMemoryPath(envPath))
            return envPath;
        if (existsSync(envPath))
            return envPath;
    }
    const home = homedir();
    const candidates = [];
    const currentPlatform = platform();
    if (currentPlatform === 'darwin') {
        candidates.push(join(home, 'Library', 'Application Support', 'opencode', 'opencode.db'));
    }
    if (currentPlatform === 'win32') {
        const appData = process.env.APPDATA ?? join(home, 'AppData', 'Roaming');
        const localAppData = process.env.LOCALAPPDATA ?? join(home, 'AppData', 'Local');
        candidates.push(join(appData, 'opencode', 'opencode.db'));
        candidates.push(join(localAppData, 'opencode', 'opencode.db'));
    }
    // Linux default
    candidates.push(join(home, '.local', 'share', 'opencode', 'opencode.db'));
    for (const candidate of candidates) {
        if (existsSync(candidate)) {
            return candidate;
        }
    }
    return null;
}
function isMemoryPath(path) {
    return path === ':memory:' || path.includes('mode=memory');
}
function registerShutdownHandler(tmuxManager, logger, dbReader) {
    if (typeof process === 'undefined') {
        logger?.('[shutdown] process is undefined, cannot register handlers');
        return;
    }
    const log = logger ?? (() => { });
    let shutdownCalled = false;
    log(`Registering shutdown handlers (PID: ${process.pid}, tmuxManager: ${tmuxManager ? 'yes' : 'no'})`);
    log(`Current tracked sessions in tmuxManager: ${tmuxManager ? 'checking...' : 'N/A'}`);
    const shutdown = (signal) => {
        // Prevent multiple shutdown calls
        if (shutdownCalled) {
            log(`Shutdown already in progress, ignoring ${signal ?? 'unknown'} signal`);
            return;
        }
        shutdownCalled = true;
        log(`Shutdown triggered by ${signal ?? 'unknown'} signal`);
        if (tmuxManager) {
            try {
                log('Cleaning up tmux sessions...');
                // Use sync version to ensure cleanup completes before process exits
                tmuxManager.cleanupSync();
                log('Tmux cleanup complete');
            }
            catch (error) {
                log(`Tmux cleanup error: ${error}`);
            }
        }
        if (dbReader) {
            try {
                log('Closing OpenCode DB reader...');
                dbReader.close();
                log('OpenCode DB reader closed');
            }
            catch (error) {
                log(`OpenCode DB reader error: ${error}`);
            }
        }
        log('Shutdown complete');
    };
    process.once('beforeExit', () => shutdown('beforeExit'));
    process.once('SIGINT', () => shutdown('SIGINT'));
    process.once('SIGTERM', () => shutdown('SIGTERM'));
    process.once('SIGHUP', () => shutdown('SIGHUP')); // Handle tmux pane close
    process.once('exit', () => shutdown('exit')); // Also handle exit event for extra safety
    log('Shutdown handlers registered for: beforeExit, SIGINT, SIGTERM, SIGHUP, exit');
}
//# sourceMappingURL=plugin.js.map