{"version":3,"file":"intercom-bridge.d.ts","sourceRoot":"","sources":["../../../src/intercom/intercom-bridge.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAwB,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGpG,eAAO,MAAM,6BAA6B,2CAA2C,CAAC;AAWtF,eAAO,MAAM,0BAA0B,2BAA2B,CAAC;AACnE,eAAO,MAAM,sBAAsB,oCAAoC,CAAC;AAaxE,MAAM,WAAW,mBAAmB;IACnC,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,kBAAkB,CAAC;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB;IACxC,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,kBAAkB,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,0BAA0B,EAAE,OAAO,CAAC;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,0BAA0B;IACnC,MAAM,EAAE,eAAe,CAAC,gBAAgB,CAAC,CAAC;IAC1C,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IACtC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,4BAA4B,CAC3C,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,SAAS,EAAE,MAAM,EACjB,iBAAiB,qBAA0C,GACzD,MAAM,CAQR;AAYD,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGlG;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,kBAAkB,CAG5E;AAiDD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,0BAA0B,GAAG,wBAAwB,CAelG;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,GAAG,mBAAmB,CA+B5F;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,mBAAmB,GAAG,WAAW,CAsBvG","sourcesContent":["import * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { AgentConfig } from \"../agents/agents.ts\";\nimport type { ExtensionConfig, IntercomBridgeConfig, IntercomBridgeMode } from \"../shared/types.ts\";\nimport { getAgentDir } from \"../shared/utils.ts\";\n\nexport const NATIVE_INTERCOM_EXTENSION_DIR = \"native:pi-subagents-supervisor-channel\";\n\nfunction defaultAgentDir(): string {\n\treturn getAgentDir();\n}\n\nfunction defaultSubagentConfigDir(agentDir = defaultAgentDir()): string {\n\treturn path.join(agentDir, \"extensions\", \"subagent\");\n}\n\nconst DEFAULT_INTERCOM_TARGET_PREFIX = \"subagent-chat\";\nexport const PI_INTERCOM_SESSION_ID_ENV = \"PI_INTERCOM_SESSION_ID\";\nexport const INTERCOM_BRIDGE_MARKER = \"Intercom orchestration channel:\";\nconst DEFAULT_INTERCOM_BRIDGE_TEMPLATE = `The inherited thread is reference-only. Do not continue that conversation or send questions, status updates, or completion handoffs to the supervisor in normal assistant text.\n\nUse contact_supervisor first. It resolves the supervisor session \"{orchestratorTarget}\" and run metadata automatically.\n- Need a decision, blocked, approval, or product/API/scope ambiguity: contact_supervisor({ reason: \"need_decision\", message: \"<question>\" })\n- Need structured supervisor input rather than a freeform reply: contact_supervisor({ reason: \"interview_request\", message: \"<what input is needed>\", interview: { title: \"...\", questions: [] } })\n- After contact_supervisor with reason \"need_decision\" or \"interview_request\", stay alive and continue only after the reply arrives. Do not finish your final response with a choose-one question.\n- Do not ask for clarification when the only conflict is review-only/no-edit versus progress-writing or artifact-writing instructions. If an output path is configured but no write-capable tool is available, return the complete artifact in your final response; the runtime will persist it. Do not contact the supervisor merely because you cannot write that output path directly.\n- Meaningful progress or unexpected discoveries that change the plan: contact_supervisor({ reason: \"progress_update\", message: \"UPDATE: <summary>\" })\n- Generic intercom is lower-level plumbing/fallback only: intercom({ action: \"ask\", to: \"{orchestratorTarget}\", message: \"<question>\" })\n\nDo not use contact_supervisor or intercom for routine completion handoffs. If no coordination is needed, return a focused task result.`;\n\nexport interface IntercomBridgeState {\n\tactive: boolean;\n\tmode: IntercomBridgeMode;\n\tresultDelivery: boolean;\n\torchestratorTarget?: string;\n\textensionDir: string;\n\tinstruction: string;\n}\n\nexport interface IntercomBridgeDiagnostic {\n\tactive: boolean;\n\tmode: IntercomBridgeMode;\n\twantsIntercom: boolean;\n\tsupervisorChannelAvailable: boolean;\n\textensionDir: string;\n\torchestratorTarget?: string;\n\treason?: string;\n}\n\ninterface ResolveIntercomBridgeInput {\n\tconfig: ExtensionConfig[\"intercomBridge\"];\n\tcontext: \"fresh\" | \"fork\" | undefined;\n\torchestratorTarget?: string;\n\tcwd?: string;\n\tsettingsDir?: string;\n\tagentDir?: string;\n}\n\nexport function resolveIntercomSessionTarget(\n\tsessionName: string | undefined,\n\tsessionId: string,\n\tintercomSessionId = process.env[PI_INTERCOM_SESSION_ID_ENV],\n): string {\n\tconst trimmedName = sessionName?.trim();\n\tif (trimmedName) return trimmedName;\n\tconst fallbackSessionId = intercomSessionId?.trim() || sessionId;\n\tconst normalizedSessionId = fallbackSessionId.startsWith(\"session-\")\n\t\t? fallbackSessionId.slice(\"session-\".length)\n\t\t: fallbackSessionId;\n\treturn `${DEFAULT_INTERCOM_TARGET_PREFIX}-${normalizedSessionId.slice(0, 8)}`;\n}\n\nfunction sanitizeIntercomTargetPart(value: string): string {\n\treturn (\n\t\tvalue\n\t\t\t.trim()\n\t\t\t.toLowerCase()\n\t\t\t.replace(/[^a-z0-9_-]+/g, \"-\")\n\t\t\t.replace(/^-+|-+$/g, \"\") || \"agent\"\n\t);\n}\n\nexport function resolveSubagentIntercomTarget(runId: string, agent: string, index?: number): string {\n\tconst stepSuffix = index !== undefined ? `-${index + 1}` : \"\";\n\treturn `subagent-${sanitizeIntercomTargetPart(agent)}-${sanitizeIntercomTargetPart(runId)}${stepSuffix}`;\n}\n\nexport function resolveIntercomBridgeMode(value: unknown): IntercomBridgeMode {\n\tif (value === \"off\" || value === \"always\" || value === \"fork-only\") return value;\n\treturn \"always\";\n}\n\nfunction resolveIntercomBridgeConfig(value: ExtensionConfig[\"intercomBridge\"]): Required<IntercomBridgeConfig> {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) {\n\t\treturn { mode: \"always\", instructionFile: \"\", resultDelivery: false };\n\t}\n\treturn {\n\t\tmode: resolveIntercomBridgeMode(value.mode),\n\t\tinstructionFile: typeof value.instructionFile === \"string\" ? value.instructionFile : \"\",\n\t\tresultDelivery: value.resultDelivery === true,\n\t};\n}\n\nfunction expandTilde(filePath: string): string {\n\treturn filePath.startsWith(\"~/\") ? path.join(os.homedir(), filePath.slice(2)) : filePath;\n}\n\nfunction resolveInstructionTemplate(instructionFile: string, settingsDir: string): string {\n\tif (!instructionFile) return DEFAULT_INTERCOM_BRIDGE_TEMPLATE;\n\tconst expandedPath = expandTilde(instructionFile);\n\tconst resolvedPath = path.isAbsolute(expandedPath) ? expandedPath : path.resolve(settingsDir, expandedPath);\n\ttry {\n\t\treturn fs.readFileSync(resolvedPath, \"utf-8\");\n\t} catch (error) {\n\t\tconsole.warn(\n\t\t\t`Failed to read intercom bridge instructionFile at '${resolvedPath}'. Using default instructions.`,\n\t\t\terror,\n\t\t);\n\t\treturn DEFAULT_INTERCOM_BRIDGE_TEMPLATE;\n\t}\n}\n\nfunction buildIntercomBridgeInstruction(orchestratorTarget: string, template: string): string {\n\tconst instruction = template.replaceAll(\"{orchestratorTarget}\", orchestratorTarget).trim();\n\tif (instruction.startsWith(INTERCOM_BRIDGE_MARKER)) return instruction;\n\treturn `${INTERCOM_BRIDGE_MARKER}\\n${instruction}`;\n}\n\nfunction inactiveReason(\n\tmode: IntercomBridgeMode,\n\tcontext: \"fresh\" | \"fork\" | undefined,\n\torchestratorTarget: string | undefined,\n): string | undefined {\n\tif (mode === \"off\") return \"bridge mode is off\";\n\tif (mode === \"fork-only\" && context !== \"fork\") return \"bridge mode is fork-only and context is not fork\";\n\tif (!orchestratorTarget) return \"orchestrator target is not available\";\n\treturn undefined;\n}\n\nexport function diagnoseIntercomBridge(input: ResolveIntercomBridgeInput): IntercomBridgeDiagnostic {\n\tconst config = resolveIntercomBridgeConfig(input.config);\n\tconst mode = config.mode;\n\tconst orchestratorTarget = input.orchestratorTarget?.trim();\n\tconst wantsIntercom = mode !== \"off\" && !(mode === \"fork-only\" && input.context !== \"fork\");\n\tconst reason = inactiveReason(mode, input.context, orchestratorTarget);\n\treturn {\n\t\tactive: reason === undefined,\n\t\tmode,\n\t\twantsIntercom,\n\t\tsupervisorChannelAvailable: true,\n\t\textensionDir: NATIVE_INTERCOM_EXTENSION_DIR,\n\t\t...(orchestratorTarget ? { orchestratorTarget } : {}),\n\t\t...(reason ? { reason } : {}),\n\t};\n}\n\nexport function resolveIntercomBridge(input: ResolveIntercomBridgeInput): IntercomBridgeState {\n\tconst config = resolveIntercomBridgeConfig(input.config);\n\tconst mode = config.mode;\n\tconst orchestratorTarget = input.orchestratorTarget?.trim();\n\tconst agentDir = path.resolve(input.agentDir ?? defaultAgentDir());\n\tconst settingsDir = path.resolve(input.settingsDir ?? defaultSubagentConfigDir(agentDir));\n\tconst defaultInstruction = buildIntercomBridgeInstruction(\n\t\torchestratorTarget || \"{orchestratorTarget}\",\n\t\tDEFAULT_INTERCOM_BRIDGE_TEMPLATE,\n\t);\n\tconst reason = inactiveReason(mode, input.context, orchestratorTarget);\n\tif (reason || !orchestratorTarget) {\n\t\treturn {\n\t\t\tactive: false,\n\t\t\tmode,\n\t\t\tresultDelivery: config.resultDelivery,\n\t\t\textensionDir: NATIVE_INTERCOM_EXTENSION_DIR,\n\t\t\tinstruction: defaultInstruction,\n\t\t};\n\t}\n\treturn {\n\t\tactive: true,\n\t\tmode,\n\t\tresultDelivery: config.resultDelivery,\n\t\torchestratorTarget,\n\t\textensionDir: NATIVE_INTERCOM_EXTENSION_DIR,\n\t\tinstruction: buildIntercomBridgeInstruction(\n\t\t\torchestratorTarget,\n\t\t\tresolveInstructionTemplate(config.instructionFile, settingsDir),\n\t\t),\n\t};\n}\n\nexport function applyIntercomBridgeToAgent(agent: AgentConfig, bridge: IntercomBridgeState): AgentConfig {\n\tif (!bridge.active || !bridge.orchestratorTarget) return agent;\n\n\tconst bridgeTools = [\"intercom\", \"contact_supervisor\"];\n\tconst tools =\n\t\tagent.tools && agent.tools.length > 0\n\t\t\t? [...agent.tools, ...bridgeTools.filter((tool) => !agent.tools?.includes(tool))]\n\t\t\t: agent.tools;\n\tconst instruction = bridge.instruction;\n\tconst trimmedPrompt = agent.systemPrompt?.trim() || \"\";\n\tconst systemPrompt = trimmedPrompt.includes(INTERCOM_BRIDGE_MARKER)\n\t\t? trimmedPrompt\n\t\t: trimmedPrompt\n\t\t\t? `${trimmedPrompt}\\n\\n${instruction}`\n\t\t\t: instruction;\n\n\tif (tools === agent.tools && systemPrompt === agent.systemPrompt) return agent;\n\treturn {\n\t\t...agent,\n\t\ttools,\n\t\tsystemPrompt,\n\t};\n}\n"]}