@alashi/provider-claude 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/claude-provider.ts","../src/transcripts.ts"],"mappings":";;UA4BiB;;EAEf,kBAAkB,QAAQ;;EAE1B;;cAwSW,8BAA8B;;WAChC;WACA;WACA;EAIT,YAAY,UAAS;EAKrB,cAAoB,UAAS,iBAAsB,QAAQ;EAQ3D,QAAc,oBAAoB,QAAQ;;EAK1C,kBAAwB,uBAAuB,QAAQ;;;;;EAQvD,kBACE,aAAa,qBACb;IAAW;MACV;;;;;;;;;;;;iBC/VW,eAAe;;;;;;iBAkBT,yBACpB,+BACA,sBACC,QAAQ;;;;;;;;;;iBAwBW,yBACpB,sBAAsB,qBACtB;EAAW;EAAa;IACvB"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/claude-provider.ts","../src/transcripts.ts"],"mappings":";;UA4BiB;;EAEf,kBAAkB,QAAQ;;EAE1B;;cAoTW,8BAA8B;;WAChC;WACA;WACA;EAIT,YAAY,UAAS;EAKrB,cAAoB,UAAS,iBAAsB,QAAQ;EAQ3D,QAAc,oBAAoB,QAAQ;;EAK1C,kBAAwB,uBAAuB,QAAQ;;;;;EAQvD,kBACE,aAAa,qBACb;IAAW;MACV;;;;;;;;;;;;iBC3WW,eAAe;;;;;;iBAkBT,yBACpB,+BACA,sBACC,QAAQ;;;;;;;;;;iBAwBW,yBACpB,sBAAsB,qBACtB;EAAW;EAAa;IACvB"}
package/dist/index.mjs CHANGED
@@ -353,6 +353,18 @@ async function importSessionTranscripts(transcripts, options) {
353
353
  }
354
354
  //#endregion
355
355
  //#region src/claude-provider.ts
356
+ const ENGINE = "claude-code";
357
+ /**
358
+ * Claude Code's tool calls in the canonical vocabulary (`CANONICAL_TOOLS`).
359
+ * The vocabulary is modelled on Claude Code's own tools, so this is the
360
+ * identity — kept as the one place an engine's names would be translated.
361
+ */
362
+ function canonicalCall(tool, input) {
363
+ return {
364
+ tool,
365
+ input
366
+ };
367
+ }
356
368
  /** Engine-internal tools the gateway hook allows without consulting the host. */
357
369
  const ENGINE_INTERNAL_TOOLS = /* @__PURE__ */ new Set(["ToolSearch"]);
358
370
  function toQueryOptions(options, skillsPluginDir, canUseTool, gateHook) {
@@ -440,9 +452,10 @@ var ClaudeSession = class {
440
452
  let verdict;
441
453
  try {
442
454
  verdict = await this.#gate({
443
- tool: hookInput.tool_name,
444
- input: hookInput.tool_input,
445
- toolUseId: hookInput.tool_use_id
455
+ ...canonicalCall(hookInput.tool_name, hookInput.tool_input),
456
+ toolUseId: hookInput.tool_use_id,
457
+ engine: ENGINE,
458
+ nativeTool: hookInput.tool_name
446
459
  });
447
460
  } catch (err) {
448
461
  return decide("deny", { permissionDecisionReason: `Tool gateway error: ${String(err)}` });
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["#costUsd","#models","#done","#resolvers","#values","#askTools","#gate","#canUseTool","#query","#queue","#gateHook","#iterator","#id","#closed","#busy","#run","#asked","#sessionAllowed","#pendingApprovals","#settleApproval","#pushOutOfBand","#outOfBand","#wakeOutOfBand","#sdkNext","#tally","#denyAllPending","#defaults","#projectsDir"],"sources":["../src/event-mapping.ts","../src/history.ts","../src/message-queue.ts","../src/skills-plugin.ts","../src/transcripts.ts","../src/claude-provider.ts"],"sourcesContent":["import type { AgentEvent, ModelTokenUsage } from '@alashi/apps-sdk';\nimport type { ModelUsage, SDKMessage } from '@anthropic-ai/claude-agent-sdk';\n\n/**\n * Converts the Agent SDK's per-session usage totals into per-turn deltas.\n * `total_cost_usd` and `modelUsage` on result messages are cumulative across\n * the turns of a streaming-input session, so recording them as-is would\n * count early turns once per subsequent turn. Each session owns one tally;\n * a total that shrinks (mid-session /clear) is treated as a fresh start.\n */\nexport class UsageTally {\n #costUsd = 0;\n #models = new Map<string, ModelUsage>();\n\n costDelta(total: number | undefined): number | undefined {\n if (typeof total !== 'number' || !Number.isFinite(total)) return undefined;\n const delta = total >= this.#costUsd ? total - this.#costUsd : total;\n this.#costUsd = total;\n return delta;\n }\n\n modelUsageDelta(\n totals: Record<string, ModelUsage> | undefined,\n ): Record<string, ModelTokenUsage> | undefined {\n if (totals === undefined) return undefined;\n const deltas: Record<string, ModelTokenUsage> = {};\n for (const [model, cur] of Object.entries(totals)) {\n const prev = this.#models.get(model);\n this.#models.set(model, cur);\n // A shrinking token total means the session's counters restarted, so\n // the current totals themselves are this turn's usage.\n const reset = prev !== undefined && tokenSum(cur) < tokenSum(prev);\n const base = prev !== undefined && !reset ? prev : undefined;\n const delta: ModelTokenUsage = {\n inputTokens: diff(cur.inputTokens, base?.inputTokens),\n outputTokens: diff(cur.outputTokens, base?.outputTokens),\n cacheReadInputTokens: diff(cur.cacheReadInputTokens, base?.cacheReadInputTokens),\n cacheCreationInputTokens: diff(\n cur.cacheCreationInputTokens,\n base?.cacheCreationInputTokens,\n ),\n costUsd: diff(cur.costUSD, base?.costUSD),\n };\n if (\n delta.inputTokens > 0 ||\n delta.outputTokens > 0 ||\n (delta.cacheReadInputTokens ?? 0) > 0 ||\n (delta.cacheCreationInputTokens ?? 0) > 0 ||\n (delta.costUsd ?? 0) > 0\n ) {\n deltas[model] = delta;\n }\n }\n return Object.keys(deltas).length > 0 ? deltas : undefined;\n }\n}\n\nfunction tokenSum(usage: ModelUsage): number {\n return (\n usage.inputTokens +\n usage.outputTokens +\n usage.cacheReadInputTokens +\n usage.cacheCreationInputTokens\n );\n}\n\nfunction diff(cur: number | undefined, prev: number | undefined): number {\n if (typeof cur !== 'number' || !Number.isFinite(cur)) return 0;\n return Math.max(0, cur - (prev ?? 0));\n}\n\n/**\n * Maps a raw Agent SDK message to zero or more provider-neutral events.\n * With includePartialMessages on, text/thinking arrive as stream_event\n * deltas, so the whole-message assistant blocks are skipped to avoid\n * emitting the same text twice. When a `tally` is given, result events\n * report per-turn cost/token deltas instead of the SDK's session totals.\n */\nexport function mapMessage(message: SDKMessage, tally?: UsageTally): AgentEvent[] {\n switch (message.type) {\n case 'system': {\n if (message.subtype === 'init') {\n return [{ type: 'session', sessionId: message.session_id, model: message.model }];\n }\n if (message.subtype === 'compact_boundary') {\n const meta = message.compact_metadata;\n return [\n {\n type: 'compaction',\n trigger: meta.trigger,\n preTokens: meta.pre_tokens,\n ...(meta.post_tokens !== undefined && { postTokens: meta.post_tokens }),\n ...(meta.duration_ms !== undefined && { durationMs: meta.duration_ms }),\n },\n ];\n }\n return [];\n }\n case 'stream_event': {\n const event = message.event;\n if (event.type === 'content_block_delta') {\n if (event.delta.type === 'text_delta') {\n return [{ type: 'text', text: event.delta.text }];\n }\n if (event.delta.type === 'thinking_delta') {\n return [{ type: 'thinking', text: event.delta.thinking }];\n }\n }\n return [];\n }\n case 'assistant': {\n const events: AgentEvent[] = [];\n for (const block of message.message.content) {\n if (block.type === 'tool_use') {\n events.push({ type: 'tool_use', name: block.name, input: block.input });\n }\n }\n return events;\n }\n case 'user': {\n const content = message.message.content;\n if (!Array.isArray(content)) return [];\n const events: AgentEvent[] = [];\n for (const block of content) {\n if (typeof block === 'object' && block !== null && block.type === 'tool_result') {\n events.push({\n type: 'tool_result',\n output: block.content,\n isError: block.is_error ?? false,\n });\n }\n }\n return events;\n }\n case 'result': {\n const costUsd = tally ? tally.costDelta(message.total_cost_usd) : message.total_cost_usd;\n const modelUsage = tally?.modelUsageDelta(message.modelUsage);\n if (message.subtype === 'success') {\n return [\n {\n type: 'result',\n ok: !message.is_error,\n text: message.result,\n costUsd,\n turns: message.num_turns,\n ...(modelUsage !== undefined && { modelUsage }),\n },\n ];\n }\n return [\n {\n type: 'result',\n ok: false,\n error: message.subtype,\n costUsd,\n turns: message.num_turns,\n ...(modelUsage !== undefined && { modelUsage }),\n },\n ];\n }\n default:\n return [];\n }\n}\n","import { readdir, readFile, stat } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { ChatMessage } from '@alashi/apps-sdk';\n\n// Also a traversal guard: sessionId is used as a filename.\nexport const SESSION_ID_PATTERN: RegExp =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;\n\ninterface TranscriptLine {\n type?: string;\n isMeta?: boolean;\n timestamp?: string;\n message?: {\n role?: string;\n content?: unknown;\n };\n}\n\nexport function defaultProjectsDir(): string {\n return join(homedir(), '.claude', 'projects');\n}\n\n/**\n * Session transcripts live under a project dir named after the session's cwd\n * (see `projectDirName` in transcripts.ts), but a session may have been started\n * with a different cwd than the one we are asked about, so scan every project\n * dir rather than computing a single candidate path.\n */\nexport async function findSessionFile(\n projectsDir: string,\n sessionId: string,\n): Promise<string | undefined> {\n let dirs: string[];\n try {\n dirs = await readdir(projectsDir);\n } catch {\n return undefined;\n }\n for (const dir of dirs) {\n const candidate = join(projectsDir, dir, `${sessionId}.jsonl`);\n try {\n await stat(candidate);\n return candidate;\n } catch {\n // Not in this project dir; keep scanning.\n }\n }\n return undefined;\n}\n\nfunction textFromContent(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n return content\n .filter(\n (block): block is { type: 'text'; text: string } =>\n typeof block === 'object' &&\n block !== null &&\n (block as { type?: string }).type === 'text' &&\n typeof (block as { text?: unknown }).text === 'string',\n )\n .map((block) => block.text)\n .join('');\n}\n\nfunction toChatMessage(line: TranscriptLine): ChatMessage | undefined {\n if (line.isMeta) return undefined;\n if (line.type !== 'user' && line.type !== 'assistant') return undefined;\n const role = line.type;\n if (line.message?.role !== role) return undefined;\n const text = textFromContent(line.message.content);\n // Tool-result user lines and tool-use-only assistant lines have no text.\n if (text === '') return undefined;\n return { role, text, ...(line.timestamp && { timestamp: line.timestamp }) };\n}\n\n/**\n * Reads a session's past messages from Claude Code's own transcript\n * (~/.claude/projects/<project>/<sessionId>.jsonl). The JSONL layout is a\n * Claude Code internal, so parsing is deliberately forgiving: unknown or\n * malformed lines are skipped rather than failing the whole read.\n */\nexport async function readSessionHistory(\n sessionId: string,\n projectsDir: string = defaultProjectsDir(),\n): Promise<ChatMessage[]> {\n if (!SESSION_ID_PATTERN.test(sessionId)) return [];\n const file = await findSessionFile(projectsDir, sessionId);\n if (!file) return [];\n\n const messages: ChatMessage[] = [];\n for (const line of (await readFile(file, 'utf8')).split('\\n')) {\n if (line.trim() === '') continue;\n let parsed: TranscriptLine;\n try {\n parsed = JSON.parse(line) as TranscriptLine;\n } catch {\n continue;\n }\n const message = toChatMessage(parsed);\n if (message) messages.push(message);\n }\n return messages;\n}\n","/** Push-based async iterable used as the streaming-input prompt for query(). */\nexport class AsyncMessageQueue<T> implements AsyncIterable<T> {\n #values: T[] = [];\n #resolvers: Array<(result: IteratorResult<T>) => void> = [];\n #done = false;\n\n push(value: T): void {\n if (this.#done) throw new Error('Queue is closed');\n const resolver = this.#resolvers.shift();\n if (resolver) resolver({ value, done: false });\n else this.#values.push(value);\n }\n\n end(): void {\n if (this.#done) return;\n this.#done = true;\n for (const resolver of this.#resolvers.splice(0)) {\n resolver({ value: undefined, done: true });\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return {\n next: (): Promise<IteratorResult<T>> => {\n const value = this.#values.shift();\n if (value !== undefined) return Promise.resolve({ value, done: false });\n if (this.#done) return Promise.resolve({ value: undefined, done: true });\n return new Promise((resolve) => {\n this.#resolvers.push(resolve);\n });\n },\n };\n }\n}\n","import { createHash } from 'node:crypto';\nimport { mkdir, readdir, rm, stat, symlink, writeFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { basename, join } from 'node:path';\n\n/**\n * With settingSources [] the engine discovers skills only through plugins,\n * so app-declared skill dirs are wrapped in a synthesized local plugin whose\n * skills/ dir symlinks them. Each declared dir may be a single skill\n * (contains SKILL.md) or a parent holding several skill folders.\n */\n\nconst cache = new Map<string, Promise<string>>();\n\nasync function isSkill(dir: string): Promise<boolean> {\n return stat(join(dir, 'SKILL.md')).then(\n (s) => s.isFile(),\n () => false,\n );\n}\n\nasync function build(pluginDir: string, skillDirs: string[]): Promise<string> {\n // Rebuild from scratch: symlinks are cheap and stale links are worse.\n await rm(pluginDir, { recursive: true, force: true });\n const skillsDir = join(pluginDir, 'skills');\n await mkdir(join(pluginDir, '.claude-plugin'), { recursive: true });\n await mkdir(skillsDir, { recursive: true });\n await writeFile(\n join(pluginDir, '.claude-plugin', 'plugin.json'),\n JSON.stringify({\n name: 'alashi-app-skills',\n description: 'Skills declared by the alashi app manifest',\n }),\n 'utf8',\n );\n for (const dir of skillDirs) {\n if (await isSkill(dir)) {\n await symlink(dir, join(skillsDir, basename(dir)), 'dir');\n continue;\n }\n for (const entry of await readdir(dir, { withFileTypes: true })) {\n const child = join(dir, entry.name);\n if (await isSkill(child)) await symlink(child, join(skillsDir, entry.name), 'dir');\n }\n }\n return pluginDir;\n}\n\n/** Plugin dir for a set of skill dirs; built once per process per set. */\nexport function ensureSkillsPlugin(skillDirs: string[]): Promise<string> {\n const key = createHash('sha256')\n .update(JSON.stringify([...skillDirs].sort()))\n .digest('hex')\n .slice(0, 12);\n let plugin = cache.get(key);\n if (!plugin) {\n plugin = build(join(tmpdir(), `alashi-skills-${key}`), skillDirs);\n plugin.catch(() => cache.delete(key));\n cache.set(key, plugin);\n }\n return plugin;\n}\n","import { mkdir, readFile, rename, stat, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { SessionTranscript } from '@alashi/apps-sdk';\nimport { findSessionFile, SESSION_ID_PATTERN } from './history.js';\n\n/**\n * Claude Code stores one JSONL transcript per session at\n * `~/.claude/projects/<project>/<sessionId>.jsonl`, where `<project>` is the\n * session's cwd with every non-alphanumeric character replaced by `-`:\n * `/Users/x/.alashi/apps/sre-app/data` -> `-Users-x--alashi-apps-sre-app-data`\n * (note the doubled `-` where `/.` appears). The munging is a Claude Code\n * internal, established empirically.\n */\nexport function projectDirName(cwd: string): string {\n return cwd.replace(/[^a-zA-Z0-9]/g, '-');\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Raw transcripts for the given sessions, in input order. Ids with no\n * transcript on disk are omitted (the contract treats unknown ids as absent,\n * not as an error), as are ids that fail the session-id guard.\n */\nexport async function exportSessionTranscripts(\n sessionIds: readonly string[],\n projectsDir: string,\n): Promise<SessionTranscript[]> {\n const transcripts: SessionTranscript[] = [];\n for (const sessionId of sessionIds) {\n if (!SESSION_ID_PATTERN.test(sessionId)) continue;\n const file = await findSessionFile(projectsDir, sessionId);\n if (!file) continue;\n try {\n transcripts.push({ sessionId, content: await readFile(file, 'utf8') });\n } catch {\n // Vanished or unreadable between the scan and the read: treat as absent.\n }\n }\n return transcripts;\n}\n\n/**\n * Restores transcripts into the project dir for `cwd` on *this* machine.\n *\n * Never overwrites: the engine's own state is authoritative, so an import must\n * not clobber a live session's transcript. Ids that fail the session-id guard\n * are skipped rather than throwing, so one bad entry cannot abort a restore.\n * The `cwd` values recorded *inside* the JSONL lines are left alone — they are\n * a Claude Code internal and the history reader does not consult them.\n */\nexport async function importSessionTranscripts(\n transcripts: readonly SessionTranscript[],\n options: { cwd: string; projectsDir: string },\n): Promise<void> {\n const writable = transcripts.filter((t) => SESSION_ID_PATTERN.test(t.sessionId));\n if (writable.length === 0) return;\n\n const projectDir = join(options.projectsDir, projectDirName(options.cwd));\n await mkdir(projectDir, { recursive: true });\n for (const { sessionId, content } of writable) {\n const dest = join(projectDir, `${sessionId}.jsonl`);\n if (await exists(dest)) continue;\n // Temp file + rename, so a reader never sees a half-written transcript\n // (same pattern as the host's store writeJson).\n const tmp = `${dest}.tmp`;\n await writeFile(tmp, content, 'utf8');\n await rename(tmp, dest);\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport {\n type AgentEvent,\n type AIProvider,\n type AISession,\n type ApprovalDecision,\n AppSdkError,\n type ChatMessage,\n type SessionOptions,\n type SessionTranscript,\n type ToolGate,\n} from '@alashi/apps-sdk';\nimport {\n type CanUseTool,\n type HookCallback,\n type Options,\n type PermissionResult,\n type Query,\n query,\n type SDKMessage,\n type SDKUserMessage,\n} from '@anthropic-ai/claude-agent-sdk';\nimport { mapMessage, UsageTally } from './event-mapping.js';\nimport { defaultProjectsDir, readSessionHistory } from './history.js';\nimport { AsyncMessageQueue } from './message-queue.js';\nimport { ensureSkillsPlugin } from './skills-plugin.js';\nimport { exportSessionTranscripts, importSessionTranscripts } from './transcripts.js';\n\nexport interface ClaudeCodeProviderOptions {\n /** Defaults applied to every session; per-session options win. */\n sessionDefaults?: Partial<SessionOptions>;\n /** Where Claude Code stores session transcripts (for history reads). */\n claudeProjectsDir?: string;\n}\n\n/** Engine-internal tools the gateway hook allows without consulting the host. */\nconst ENGINE_INTERNAL_TOOLS: ReadonlySet<string> = new Set(['ToolSearch']);\n\nfunction toQueryOptions(\n options: SessionOptions,\n skillsPluginDir?: string,\n canUseTool?: CanUseTool,\n gateHook?: HookCallback,\n): Options {\n const out: Options = canUseTool\n ? {\n // 'default' routes any tool outside allowedTools through canUseTool,\n // where ask-tier tools suspend for interactive approval.\n permissionMode: 'default',\n canUseTool,\n settingSources: [],\n includePartialMessages: true,\n }\n : {\n // Non-interactive: manifest-declared allowedTools are approved, everything else denied.\n permissionMode: 'dontAsk',\n // Apps run isolated from the user's personal/project Claude Code settings.\n settingSources: [],\n // Text arrives as stream_event deltas (see event-mapping.ts).\n includePartialMessages: true,\n };\n if (options.systemPrompt !== undefined) out.systemPrompt = options.systemPrompt;\n if (options.model !== undefined) out.model = options.model;\n if (options.mcpServers !== undefined) out.mcpServers = options.mcpServers;\n if (gateHook) {\n // Gateway mode: every call — built-in, MCP, subagent, even the engine's\n // \"safe\" read-only commands — passes the PreToolUse hook, and no engine\n // allowlist exists to skip it. The hook's `ask` falls through to\n // canUseTool for the interactive leg.\n out.hooks = { PreToolUse: [{ hooks: [gateHook] }] };\n } else if (options.allowedTools !== undefined) {\n out.allowedTools = options.allowedTools;\n }\n if (options.maxTurns !== undefined) out.maxTurns = options.maxTurns;\n if (options.cwd !== undefined) out.cwd = options.cwd;\n if (options.resume !== undefined) out.resume = options.resume;\n if (options.agents !== undefined && Object.keys(options.agents).length > 0) {\n out.agents = options.agents;\n // Subagents are invoked through the Agent tool; approve it alongside them\n // (gateway mode: the host's gate decides, like any other tool).\n if (!gateHook) out.allowedTools = [...new Set([...(out.allowedTools ?? []), 'Agent'])];\n }\n if (skillsPluginDir !== undefined) {\n out.plugins = [{ type: 'local', path: skillsPluginDir }];\n out.skills = 'all';\n }\n return out;\n}\n\n/**\n * One long-lived Claude Code session per chat (streaming input mode), so\n * follow-up turns reuse the process and its prompt cache instead of paying\n * session startup per message. Turns are serialized: one send() at a time.\n */\nclass ClaudeSession implements AISession {\n #queue: AsyncMessageQueue<SDKUserMessage> = new AsyncMessageQueue();\n #query: Query;\n #iterator: AsyncIterator<SDKMessage>;\n #id = '';\n #busy = false;\n #closed = false;\n // The SDK reports cost/tokens as session-cumulative totals; the tally\n // turns them into the per-turn deltas the AgentEvent contract promises.\n #tally = new UsageTally();\n #askTools: Set<string>;\n #gate: ToolGate | undefined;\n // Calls the gate answered `ask`, keyed by tool use id, awaiting canUseTool.\n #asked = new Map<string, { ruleId?: string; reason?: string }>();\n #sessionAllowed = new Set<string>();\n #pendingApprovals = new Map<\n string,\n { tool: string; resolve: (result: PermissionResult) => void }\n >();\n // Approval events are born outside the SDK message stream (the stream is\n // blocked inside canUseTool while one is pending), so they travel through\n // this side buffer and get merged into the turn's event stream by #run.\n #outOfBand: AgentEvent[] = [];\n #wakeOutOfBand: (() => void) | undefined;\n // Held across race iterations so the SDK iterator is never pulled twice.\n #sdkNext: Promise<IteratorResult<SDKMessage>> | undefined;\n\n constructor(options: SessionOptions, skillsPluginDir?: string) {\n this.#askTools = new Set(options.askTools ?? []);\n this.#gate = options.toolGate;\n const canUseTool = this.#gate || this.#askTools.size > 0 ? this.#canUseTool : undefined;\n this.#query = query({\n prompt: this.#queue,\n options: toQueryOptions(\n options,\n skillsPluginDir,\n canUseTool,\n this.#gate ? this.#gateHook : undefined,\n ),\n });\n this.#iterator = this.#query[Symbol.asyncIterator]();\n }\n\n get id(): string {\n return this.#id;\n }\n\n send(input: string): AsyncIterable<AgentEvent> {\n if (this.#closed) throw new AppSdkError('Session is closed');\n if (this.#busy) throw new AppSdkError('Session is busy with another turn');\n this.#busy = true;\n return this.#run(input);\n }\n\n /** PreToolUse hook: asks the host's gate about every tool call. */\n #gateHook: HookCallback = async (hookInput) => {\n if (hookInput.hook_event_name !== 'PreToolUse' || !this.#gate) return {};\n const decide = (\n permissionDecision: 'allow' | 'deny' | 'ask',\n extra: { permissionDecisionReason?: string; updatedInput?: Record<string, unknown> } = {},\n ) => ({\n hookSpecificOutput: { hookEventName: 'PreToolUse' as const, permissionDecision, ...extra },\n });\n if (this.#closed) return decide('deny', { permissionDecisionReason: 'Session is closed' });\n // Engine plumbing, not a capability: ToolSearch only loads deferred tool\n // schemas (every MCP tool needs it first), and the loaded tool's own call\n // still passes the gate. Gating it would make default-deny apps unable to\n // reach any MCP tool.\n if (ENGINE_INTERNAL_TOOLS.has(hookInput.tool_name)) return decide('allow');\n let verdict: Awaited<ReturnType<ToolGate>>;\n try {\n verdict = await this.#gate({\n tool: hookInput.tool_name,\n input: hookInput.tool_input,\n toolUseId: hookInput.tool_use_id,\n });\n } catch (err) {\n // Fail closed: a broken gateway must never let a call through.\n return decide('deny', { permissionDecisionReason: `Tool gateway error: ${String(err)}` });\n }\n if (verdict.decision === 'allow') {\n return decide('allow', verdict.input ? { updatedInput: verdict.input } : {});\n }\n if (verdict.decision === 'deny') {\n return decide('deny', { permissionDecisionReason: verdict.reason });\n }\n this.#asked.set(hookInput.tool_use_id, {\n ...(verdict.ruleId !== undefined && { ruleId: verdict.ruleId }),\n ...(verdict.reason !== undefined && { reason: verdict.reason }),\n });\n return decide('ask');\n };\n\n #canUseTool: CanUseTool = (toolName, input, { signal, toolUseID }) => {\n if (this.#closed) return Promise.resolve({ behavior: 'deny', message: 'Session is closed' });\n let label: { ruleId?: string; reason?: string } = {};\n if (this.#gate) {\n // Only calls the gate itself answered `ask` may prompt; anything else\n // reaching here bypassed the hook and is refused (defense in depth).\n // Session-scope grants live in the host's gateway, not here.\n const asked = this.#asked.get(toolUseID);\n if (!asked) {\n return Promise.resolve({\n behavior: 'deny',\n message: `Tool \"${toolName}\" was not cleared by the tool gateway`,\n });\n }\n this.#asked.delete(toolUseID);\n label = asked;\n } else {\n if (this.#sessionAllowed.has(toolName)) return Promise.resolve({ behavior: 'allow' });\n if (!this.#askTools.has(toolName)) {\n return Promise.resolve({\n behavior: 'deny',\n message: `Tool \"${toolName}\" is not permitted by this app`,\n });\n }\n }\n const approvalId = toolUseID || randomUUID();\n return new Promise<PermissionResult>((resolve) => {\n this.#pendingApprovals.set(approvalId, { tool: toolName, resolve });\n signal.addEventListener('abort', () => {\n this.#settleApproval(approvalId, { behavior: 'deny' }, 'session_closed');\n });\n this.#pushOutOfBand({\n type: 'approval_required',\n approvalId,\n tool: toolName,\n input,\n ...label,\n });\n });\n };\n\n async respondToApproval(approvalId: string, decision: ApprovalDecision): Promise<void> {\n if (!this.#pendingApprovals.has(approvalId)) {\n throw new AppSdkError(`Unknown or already resolved approval \"${approvalId}\"`);\n }\n const reason = decision.behavior === 'deny' ? (decision.reason ?? 'user') : 'user';\n this.#settleApproval(approvalId, decision, reason);\n }\n\n #settleApproval(\n approvalId: string,\n decision: ApprovalDecision,\n reason: 'user' | 'timeout' | 'session_closed',\n ): void {\n const pending = this.#pendingApprovals.get(approvalId);\n if (!pending) return;\n this.#pendingApprovals.delete(approvalId);\n if (decision.behavior === 'allow') {\n if (!this.#gate && (decision.scope === 'session' || decision.scope === 'always')) {\n this.#sessionAllowed.add(pending.tool);\n }\n pending.resolve({ behavior: 'allow' });\n } else {\n pending.resolve({ behavior: 'deny', message: decision.message ?? 'Denied by user' });\n }\n this.#pushOutOfBand({\n type: 'approval_resolved',\n approvalId,\n behavior: decision.behavior,\n reason,\n });\n }\n\n #pushOutOfBand(event: AgentEvent): void {\n this.#outOfBand.push(event);\n this.#wakeOutOfBand?.();\n }\n\n #denyAllPending(): void {\n for (const approvalId of [...this.#pendingApprovals.keys()]) {\n this.#settleApproval(approvalId, { behavior: 'deny' }, 'session_closed');\n }\n }\n\n async *#run(input: string): AsyncGenerator<AgentEvent> {\n try {\n this.#queue.push({\n type: 'user',\n message: { role: 'user', content: input },\n parent_tool_use_id: null,\n });\n while (true) {\n yield* this.#outOfBand.splice(0);\n this.#sdkNext ??= this.#iterator.next();\n const wake = new Promise<void>((resolve) => {\n this.#wakeOutOfBand = resolve;\n });\n // An event pushed while the generator was suspended in yield* above\n // would have woken a stale waker; re-check before racing.\n if (this.#outOfBand.length > 0) continue;\n const winner = await Promise.race([\n this.#sdkNext.then((result) => ({ sdk: result })),\n wake.then(() => undefined),\n ]);\n if (winner === undefined) continue; // out-of-band event arrived; drain at loop top\n this.#sdkNext = undefined;\n const next = winner.sdk;\n if (next.done) {\n yield { type: 'result', ok: false, error: 'Session ended unexpectedly' };\n return;\n }\n const message = next.value;\n for (const event of mapMessage(message, this.#tally)) {\n if (event.type === 'session') this.#id = event.sessionId;\n yield event;\n }\n if (message.type === 'result') {\n yield* this.#outOfBand.splice(0);\n return;\n }\n }\n } finally {\n this.#wakeOutOfBand = undefined;\n this.#busy = false;\n }\n }\n\n async interrupt(): Promise<void> {\n this.#denyAllPending();\n await this.#query.interrupt();\n }\n\n async close(): Promise<void> {\n if (this.#closed) return;\n this.#closed = true;\n this.#denyAllPending();\n this.#queue.end();\n await this.#iterator.return?.(undefined)?.catch(() => undefined);\n }\n}\n\nexport class ClaudeCodeProvider implements AIProvider {\n readonly name: string = 'claude-code';\n readonly supportsApprovals: boolean = true;\n readonly supportsToolGate: boolean = true;\n readonly #defaults: Partial<SessionOptions>;\n readonly #projectsDir: string | undefined;\n\n constructor(options: ClaudeCodeProviderOptions = {}) {\n this.#defaults = options.sessionDefaults ?? {};\n this.#projectsDir = options.claudeProjectsDir;\n }\n\n async createSession(options: SessionOptions = {}): Promise<AISession> {\n const merged = { ...this.#defaults, ...options };\n const skillsPluginDir = merged.skillDirs?.length\n ? await ensureSkillsPlugin(merged.skillDirs)\n : undefined;\n return new ClaudeSession(merged, skillsPluginDir);\n }\n\n async history(sessionId: string): Promise<ChatMessage[]> {\n return readSessionHistory(sessionId, this.#projectsDir ?? defaultProjectsDir());\n }\n\n /** Snapshots Claude Code's own JSONL transcripts; unknown ids are omitted. */\n async exportTranscripts(sessionIds: string[]): Promise<SessionTranscript[]> {\n return exportSessionTranscripts(sessionIds, this.#projectsDir ?? defaultProjectsDir());\n }\n\n /**\n * Restores transcripts into the project dir Claude Code derives from `cwd`\n * (the app's data dir on this machine). Existing transcripts are left alone.\n */\n async importTranscripts(\n transcripts: SessionTranscript[],\n options: { cwd: string },\n ): Promise<void> {\n return importSessionTranscripts(transcripts, {\n cwd: options.cwd,\n projectsDir: this.#projectsDir ?? defaultProjectsDir(),\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["#costUsd","#models","#done","#resolvers","#values","#askTools","#gate","#canUseTool","#query","#queue","#gateHook","#iterator","#id","#closed","#busy","#run","#asked","#sessionAllowed","#pendingApprovals","#settleApproval","#pushOutOfBand","#outOfBand","#wakeOutOfBand","#sdkNext","#tally","#denyAllPending","#defaults","#projectsDir"],"sources":["../src/event-mapping.ts","../src/history.ts","../src/message-queue.ts","../src/skills-plugin.ts","../src/transcripts.ts","../src/claude-provider.ts"],"sourcesContent":["import type { AgentEvent, ModelTokenUsage } from '@alashi/apps-sdk';\nimport type { ModelUsage, SDKMessage } from '@anthropic-ai/claude-agent-sdk';\n\n/**\n * Converts the Agent SDK's per-session usage totals into per-turn deltas.\n * `total_cost_usd` and `modelUsage` on result messages are cumulative across\n * the turns of a streaming-input session, so recording them as-is would\n * count early turns once per subsequent turn. Each session owns one tally;\n * a total that shrinks (mid-session /clear) is treated as a fresh start.\n */\nexport class UsageTally {\n #costUsd = 0;\n #models = new Map<string, ModelUsage>();\n\n costDelta(total: number | undefined): number | undefined {\n if (typeof total !== 'number' || !Number.isFinite(total)) return undefined;\n const delta = total >= this.#costUsd ? total - this.#costUsd : total;\n this.#costUsd = total;\n return delta;\n }\n\n modelUsageDelta(\n totals: Record<string, ModelUsage> | undefined,\n ): Record<string, ModelTokenUsage> | undefined {\n if (totals === undefined) return undefined;\n const deltas: Record<string, ModelTokenUsage> = {};\n for (const [model, cur] of Object.entries(totals)) {\n const prev = this.#models.get(model);\n this.#models.set(model, cur);\n // A shrinking token total means the session's counters restarted, so\n // the current totals themselves are this turn's usage.\n const reset = prev !== undefined && tokenSum(cur) < tokenSum(prev);\n const base = prev !== undefined && !reset ? prev : undefined;\n const delta: ModelTokenUsage = {\n inputTokens: diff(cur.inputTokens, base?.inputTokens),\n outputTokens: diff(cur.outputTokens, base?.outputTokens),\n cacheReadInputTokens: diff(cur.cacheReadInputTokens, base?.cacheReadInputTokens),\n cacheCreationInputTokens: diff(\n cur.cacheCreationInputTokens,\n base?.cacheCreationInputTokens,\n ),\n costUsd: diff(cur.costUSD, base?.costUSD),\n };\n if (\n delta.inputTokens > 0 ||\n delta.outputTokens > 0 ||\n (delta.cacheReadInputTokens ?? 0) > 0 ||\n (delta.cacheCreationInputTokens ?? 0) > 0 ||\n (delta.costUsd ?? 0) > 0\n ) {\n deltas[model] = delta;\n }\n }\n return Object.keys(deltas).length > 0 ? deltas : undefined;\n }\n}\n\nfunction tokenSum(usage: ModelUsage): number {\n return (\n usage.inputTokens +\n usage.outputTokens +\n usage.cacheReadInputTokens +\n usage.cacheCreationInputTokens\n );\n}\n\nfunction diff(cur: number | undefined, prev: number | undefined): number {\n if (typeof cur !== 'number' || !Number.isFinite(cur)) return 0;\n return Math.max(0, cur - (prev ?? 0));\n}\n\n/**\n * Maps a raw Agent SDK message to zero or more provider-neutral events.\n * With includePartialMessages on, text/thinking arrive as stream_event\n * deltas, so the whole-message assistant blocks are skipped to avoid\n * emitting the same text twice. When a `tally` is given, result events\n * report per-turn cost/token deltas instead of the SDK's session totals.\n */\nexport function mapMessage(message: SDKMessage, tally?: UsageTally): AgentEvent[] {\n switch (message.type) {\n case 'system': {\n if (message.subtype === 'init') {\n return [{ type: 'session', sessionId: message.session_id, model: message.model }];\n }\n if (message.subtype === 'compact_boundary') {\n const meta = message.compact_metadata;\n return [\n {\n type: 'compaction',\n trigger: meta.trigger,\n preTokens: meta.pre_tokens,\n ...(meta.post_tokens !== undefined && { postTokens: meta.post_tokens }),\n ...(meta.duration_ms !== undefined && { durationMs: meta.duration_ms }),\n },\n ];\n }\n return [];\n }\n case 'stream_event': {\n const event = message.event;\n if (event.type === 'content_block_delta') {\n if (event.delta.type === 'text_delta') {\n return [{ type: 'text', text: event.delta.text }];\n }\n if (event.delta.type === 'thinking_delta') {\n return [{ type: 'thinking', text: event.delta.thinking }];\n }\n }\n return [];\n }\n case 'assistant': {\n const events: AgentEvent[] = [];\n for (const block of message.message.content) {\n if (block.type === 'tool_use') {\n events.push({ type: 'tool_use', name: block.name, input: block.input });\n }\n }\n return events;\n }\n case 'user': {\n const content = message.message.content;\n if (!Array.isArray(content)) return [];\n const events: AgentEvent[] = [];\n for (const block of content) {\n if (typeof block === 'object' && block !== null && block.type === 'tool_result') {\n events.push({\n type: 'tool_result',\n output: block.content,\n isError: block.is_error ?? false,\n });\n }\n }\n return events;\n }\n case 'result': {\n const costUsd = tally ? tally.costDelta(message.total_cost_usd) : message.total_cost_usd;\n const modelUsage = tally?.modelUsageDelta(message.modelUsage);\n if (message.subtype === 'success') {\n return [\n {\n type: 'result',\n ok: !message.is_error,\n text: message.result,\n costUsd,\n turns: message.num_turns,\n ...(modelUsage !== undefined && { modelUsage }),\n },\n ];\n }\n return [\n {\n type: 'result',\n ok: false,\n error: message.subtype,\n costUsd,\n turns: message.num_turns,\n ...(modelUsage !== undefined && { modelUsage }),\n },\n ];\n }\n default:\n return [];\n }\n}\n","import { readdir, readFile, stat } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { ChatMessage } from '@alashi/apps-sdk';\n\n// Also a traversal guard: sessionId is used as a filename.\nexport const SESSION_ID_PATTERN: RegExp =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;\n\ninterface TranscriptLine {\n type?: string;\n isMeta?: boolean;\n timestamp?: string;\n message?: {\n role?: string;\n content?: unknown;\n };\n}\n\nexport function defaultProjectsDir(): string {\n return join(homedir(), '.claude', 'projects');\n}\n\n/**\n * Session transcripts live under a project dir named after the session's cwd\n * (see `projectDirName` in transcripts.ts), but a session may have been started\n * with a different cwd than the one we are asked about, so scan every project\n * dir rather than computing a single candidate path.\n */\nexport async function findSessionFile(\n projectsDir: string,\n sessionId: string,\n): Promise<string | undefined> {\n let dirs: string[];\n try {\n dirs = await readdir(projectsDir);\n } catch {\n return undefined;\n }\n for (const dir of dirs) {\n const candidate = join(projectsDir, dir, `${sessionId}.jsonl`);\n try {\n await stat(candidate);\n return candidate;\n } catch {\n // Not in this project dir; keep scanning.\n }\n }\n return undefined;\n}\n\nfunction textFromContent(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n return content\n .filter(\n (block): block is { type: 'text'; text: string } =>\n typeof block === 'object' &&\n block !== null &&\n (block as { type?: string }).type === 'text' &&\n typeof (block as { text?: unknown }).text === 'string',\n )\n .map((block) => block.text)\n .join('');\n}\n\nfunction toChatMessage(line: TranscriptLine): ChatMessage | undefined {\n if (line.isMeta) return undefined;\n if (line.type !== 'user' && line.type !== 'assistant') return undefined;\n const role = line.type;\n if (line.message?.role !== role) return undefined;\n const text = textFromContent(line.message.content);\n // Tool-result user lines and tool-use-only assistant lines have no text.\n if (text === '') return undefined;\n return { role, text, ...(line.timestamp && { timestamp: line.timestamp }) };\n}\n\n/**\n * Reads a session's past messages from Claude Code's own transcript\n * (~/.claude/projects/<project>/<sessionId>.jsonl). The JSONL layout is a\n * Claude Code internal, so parsing is deliberately forgiving: unknown or\n * malformed lines are skipped rather than failing the whole read.\n */\nexport async function readSessionHistory(\n sessionId: string,\n projectsDir: string = defaultProjectsDir(),\n): Promise<ChatMessage[]> {\n if (!SESSION_ID_PATTERN.test(sessionId)) return [];\n const file = await findSessionFile(projectsDir, sessionId);\n if (!file) return [];\n\n const messages: ChatMessage[] = [];\n for (const line of (await readFile(file, 'utf8')).split('\\n')) {\n if (line.trim() === '') continue;\n let parsed: TranscriptLine;\n try {\n parsed = JSON.parse(line) as TranscriptLine;\n } catch {\n continue;\n }\n const message = toChatMessage(parsed);\n if (message) messages.push(message);\n }\n return messages;\n}\n","/** Push-based async iterable used as the streaming-input prompt for query(). */\nexport class AsyncMessageQueue<T> implements AsyncIterable<T> {\n #values: T[] = [];\n #resolvers: Array<(result: IteratorResult<T>) => void> = [];\n #done = false;\n\n push(value: T): void {\n if (this.#done) throw new Error('Queue is closed');\n const resolver = this.#resolvers.shift();\n if (resolver) resolver({ value, done: false });\n else this.#values.push(value);\n }\n\n end(): void {\n if (this.#done) return;\n this.#done = true;\n for (const resolver of this.#resolvers.splice(0)) {\n resolver({ value: undefined, done: true });\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return {\n next: (): Promise<IteratorResult<T>> => {\n const value = this.#values.shift();\n if (value !== undefined) return Promise.resolve({ value, done: false });\n if (this.#done) return Promise.resolve({ value: undefined, done: true });\n return new Promise((resolve) => {\n this.#resolvers.push(resolve);\n });\n },\n };\n }\n}\n","import { createHash } from 'node:crypto';\nimport { mkdir, readdir, rm, stat, symlink, writeFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { basename, join } from 'node:path';\n\n/**\n * With settingSources [] the engine discovers skills only through plugins,\n * so app-declared skill dirs are wrapped in a synthesized local plugin whose\n * skills/ dir symlinks them. Each declared dir may be a single skill\n * (contains SKILL.md) or a parent holding several skill folders.\n */\n\nconst cache = new Map<string, Promise<string>>();\n\nasync function isSkill(dir: string): Promise<boolean> {\n return stat(join(dir, 'SKILL.md')).then(\n (s) => s.isFile(),\n () => false,\n );\n}\n\nasync function build(pluginDir: string, skillDirs: string[]): Promise<string> {\n // Rebuild from scratch: symlinks are cheap and stale links are worse.\n await rm(pluginDir, { recursive: true, force: true });\n const skillsDir = join(pluginDir, 'skills');\n await mkdir(join(pluginDir, '.claude-plugin'), { recursive: true });\n await mkdir(skillsDir, { recursive: true });\n await writeFile(\n join(pluginDir, '.claude-plugin', 'plugin.json'),\n JSON.stringify({\n name: 'alashi-app-skills',\n description: 'Skills declared by the alashi app manifest',\n }),\n 'utf8',\n );\n for (const dir of skillDirs) {\n if (await isSkill(dir)) {\n await symlink(dir, join(skillsDir, basename(dir)), 'dir');\n continue;\n }\n for (const entry of await readdir(dir, { withFileTypes: true })) {\n const child = join(dir, entry.name);\n if (await isSkill(child)) await symlink(child, join(skillsDir, entry.name), 'dir');\n }\n }\n return pluginDir;\n}\n\n/** Plugin dir for a set of skill dirs; built once per process per set. */\nexport function ensureSkillsPlugin(skillDirs: string[]): Promise<string> {\n const key = createHash('sha256')\n .update(JSON.stringify([...skillDirs].sort()))\n .digest('hex')\n .slice(0, 12);\n let plugin = cache.get(key);\n if (!plugin) {\n plugin = build(join(tmpdir(), `alashi-skills-${key}`), skillDirs);\n plugin.catch(() => cache.delete(key));\n cache.set(key, plugin);\n }\n return plugin;\n}\n","import { mkdir, readFile, rename, stat, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { SessionTranscript } from '@alashi/apps-sdk';\nimport { findSessionFile, SESSION_ID_PATTERN } from './history.js';\n\n/**\n * Claude Code stores one JSONL transcript per session at\n * `~/.claude/projects/<project>/<sessionId>.jsonl`, where `<project>` is the\n * session's cwd with every non-alphanumeric character replaced by `-`:\n * `/Users/x/.alashi/apps/sre-app/data` -> `-Users-x--alashi-apps-sre-app-data`\n * (note the doubled `-` where `/.` appears). The munging is a Claude Code\n * internal, established empirically.\n */\nexport function projectDirName(cwd: string): string {\n return cwd.replace(/[^a-zA-Z0-9]/g, '-');\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Raw transcripts for the given sessions, in input order. Ids with no\n * transcript on disk are omitted (the contract treats unknown ids as absent,\n * not as an error), as are ids that fail the session-id guard.\n */\nexport async function exportSessionTranscripts(\n sessionIds: readonly string[],\n projectsDir: string,\n): Promise<SessionTranscript[]> {\n const transcripts: SessionTranscript[] = [];\n for (const sessionId of sessionIds) {\n if (!SESSION_ID_PATTERN.test(sessionId)) continue;\n const file = await findSessionFile(projectsDir, sessionId);\n if (!file) continue;\n try {\n transcripts.push({ sessionId, content: await readFile(file, 'utf8') });\n } catch {\n // Vanished or unreadable between the scan and the read: treat as absent.\n }\n }\n return transcripts;\n}\n\n/**\n * Restores transcripts into the project dir for `cwd` on *this* machine.\n *\n * Never overwrites: the engine's own state is authoritative, so an import must\n * not clobber a live session's transcript. Ids that fail the session-id guard\n * are skipped rather than throwing, so one bad entry cannot abort a restore.\n * The `cwd` values recorded *inside* the JSONL lines are left alone — they are\n * a Claude Code internal and the history reader does not consult them.\n */\nexport async function importSessionTranscripts(\n transcripts: readonly SessionTranscript[],\n options: { cwd: string; projectsDir: string },\n): Promise<void> {\n const writable = transcripts.filter((t) => SESSION_ID_PATTERN.test(t.sessionId));\n if (writable.length === 0) return;\n\n const projectDir = join(options.projectsDir, projectDirName(options.cwd));\n await mkdir(projectDir, { recursive: true });\n for (const { sessionId, content } of writable) {\n const dest = join(projectDir, `${sessionId}.jsonl`);\n if (await exists(dest)) continue;\n // Temp file + rename, so a reader never sees a half-written transcript\n // (same pattern as the host's store writeJson).\n const tmp = `${dest}.tmp`;\n await writeFile(tmp, content, 'utf8');\n await rename(tmp, dest);\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport {\n type AgentEvent,\n type AIProvider,\n type AISession,\n type ApprovalDecision,\n AppSdkError,\n type ChatMessage,\n type SessionOptions,\n type SessionTranscript,\n type ToolGate,\n} from '@alashi/apps-sdk';\nimport {\n type CanUseTool,\n type HookCallback,\n type Options,\n type PermissionResult,\n type Query,\n query,\n type SDKMessage,\n type SDKUserMessage,\n} from '@anthropic-ai/claude-agent-sdk';\nimport { mapMessage, UsageTally } from './event-mapping.js';\nimport { defaultProjectsDir, readSessionHistory } from './history.js';\nimport { AsyncMessageQueue } from './message-queue.js';\nimport { ensureSkillsPlugin } from './skills-plugin.js';\nimport { exportSessionTranscripts, importSessionTranscripts } from './transcripts.js';\n\nexport interface ClaudeCodeProviderOptions {\n /** Defaults applied to every session; per-session options win. */\n sessionDefaults?: Partial<SessionOptions>;\n /** Where Claude Code stores session transcripts (for history reads). */\n claudeProjectsDir?: string;\n}\n\nconst ENGINE = 'claude-code';\n\n/**\n * Claude Code's tool calls in the canonical vocabulary (`CANONICAL_TOOLS`).\n * The vocabulary is modelled on Claude Code's own tools, so this is the\n * identity — kept as the one place an engine's names would be translated.\n */\nexport function canonicalCall(tool: string, input: unknown): { tool: string; input: unknown } {\n return { tool, input };\n}\n\n/** Engine-internal tools the gateway hook allows without consulting the host. */\nconst ENGINE_INTERNAL_TOOLS: ReadonlySet<string> = new Set(['ToolSearch']);\n\nfunction toQueryOptions(\n options: SessionOptions,\n skillsPluginDir?: string,\n canUseTool?: CanUseTool,\n gateHook?: HookCallback,\n): Options {\n const out: Options = canUseTool\n ? {\n // 'default' routes any tool outside allowedTools through canUseTool,\n // where ask-tier tools suspend for interactive approval.\n permissionMode: 'default',\n canUseTool,\n settingSources: [],\n includePartialMessages: true,\n }\n : {\n // Non-interactive: manifest-declared allowedTools are approved, everything else denied.\n permissionMode: 'dontAsk',\n // Apps run isolated from the user's personal/project Claude Code settings.\n settingSources: [],\n // Text arrives as stream_event deltas (see event-mapping.ts).\n includePartialMessages: true,\n };\n if (options.systemPrompt !== undefined) out.systemPrompt = options.systemPrompt;\n if (options.model !== undefined) out.model = options.model;\n if (options.mcpServers !== undefined) out.mcpServers = options.mcpServers;\n if (gateHook) {\n // Gateway mode: every call — built-in, MCP, subagent, even the engine's\n // \"safe\" read-only commands — passes the PreToolUse hook, and no engine\n // allowlist exists to skip it. The hook's `ask` falls through to\n // canUseTool for the interactive leg.\n out.hooks = { PreToolUse: [{ hooks: [gateHook] }] };\n } else if (options.allowedTools !== undefined) {\n out.allowedTools = options.allowedTools;\n }\n if (options.maxTurns !== undefined) out.maxTurns = options.maxTurns;\n if (options.cwd !== undefined) out.cwd = options.cwd;\n if (options.resume !== undefined) out.resume = options.resume;\n if (options.agents !== undefined && Object.keys(options.agents).length > 0) {\n out.agents = options.agents;\n // Subagents are invoked through the Agent tool; approve it alongside them\n // (gateway mode: the host's gate decides, like any other tool).\n if (!gateHook) out.allowedTools = [...new Set([...(out.allowedTools ?? []), 'Agent'])];\n }\n if (skillsPluginDir !== undefined) {\n out.plugins = [{ type: 'local', path: skillsPluginDir }];\n out.skills = 'all';\n }\n return out;\n}\n\n/**\n * One long-lived Claude Code session per chat (streaming input mode), so\n * follow-up turns reuse the process and its prompt cache instead of paying\n * session startup per message. Turns are serialized: one send() at a time.\n */\nclass ClaudeSession implements AISession {\n #queue: AsyncMessageQueue<SDKUserMessage> = new AsyncMessageQueue();\n #query: Query;\n #iterator: AsyncIterator<SDKMessage>;\n #id = '';\n #busy = false;\n #closed = false;\n // The SDK reports cost/tokens as session-cumulative totals; the tally\n // turns them into the per-turn deltas the AgentEvent contract promises.\n #tally = new UsageTally();\n #askTools: Set<string>;\n #gate: ToolGate | undefined;\n // Calls the gate answered `ask`, keyed by tool use id, awaiting canUseTool.\n #asked = new Map<string, { ruleId?: string; reason?: string }>();\n #sessionAllowed = new Set<string>();\n #pendingApprovals = new Map<\n string,\n { tool: string; resolve: (result: PermissionResult) => void }\n >();\n // Approval events are born outside the SDK message stream (the stream is\n // blocked inside canUseTool while one is pending), so they travel through\n // this side buffer and get merged into the turn's event stream by #run.\n #outOfBand: AgentEvent[] = [];\n #wakeOutOfBand: (() => void) | undefined;\n // Held across race iterations so the SDK iterator is never pulled twice.\n #sdkNext: Promise<IteratorResult<SDKMessage>> | undefined;\n\n constructor(options: SessionOptions, skillsPluginDir?: string) {\n this.#askTools = new Set(options.askTools ?? []);\n this.#gate = options.toolGate;\n const canUseTool = this.#gate || this.#askTools.size > 0 ? this.#canUseTool : undefined;\n this.#query = query({\n prompt: this.#queue,\n options: toQueryOptions(\n options,\n skillsPluginDir,\n canUseTool,\n this.#gate ? this.#gateHook : undefined,\n ),\n });\n this.#iterator = this.#query[Symbol.asyncIterator]();\n }\n\n get id(): string {\n return this.#id;\n }\n\n send(input: string): AsyncIterable<AgentEvent> {\n if (this.#closed) throw new AppSdkError('Session is closed');\n if (this.#busy) throw new AppSdkError('Session is busy with another turn');\n this.#busy = true;\n return this.#run(input);\n }\n\n /** PreToolUse hook: asks the host's gate about every tool call. */\n #gateHook: HookCallback = async (hookInput) => {\n if (hookInput.hook_event_name !== 'PreToolUse' || !this.#gate) return {};\n const decide = (\n permissionDecision: 'allow' | 'deny' | 'ask',\n extra: { permissionDecisionReason?: string; updatedInput?: Record<string, unknown> } = {},\n ) => ({\n hookSpecificOutput: { hookEventName: 'PreToolUse' as const, permissionDecision, ...extra },\n });\n if (this.#closed) return decide('deny', { permissionDecisionReason: 'Session is closed' });\n // Engine plumbing, not a capability: ToolSearch only loads deferred tool\n // schemas (every MCP tool needs it first), and the loaded tool's own call\n // still passes the gate. Gating it would make default-deny apps unable to\n // reach any MCP tool.\n if (ENGINE_INTERNAL_TOOLS.has(hookInput.tool_name)) return decide('allow');\n let verdict: Awaited<ReturnType<ToolGate>>;\n try {\n verdict = await this.#gate({\n ...canonicalCall(hookInput.tool_name, hookInput.tool_input),\n toolUseId: hookInput.tool_use_id,\n engine: ENGINE,\n nativeTool: hookInput.tool_name,\n });\n } catch (err) {\n // Fail closed: a broken gateway must never let a call through.\n return decide('deny', { permissionDecisionReason: `Tool gateway error: ${String(err)}` });\n }\n if (verdict.decision === 'allow') {\n return decide('allow', verdict.input ? { updatedInput: verdict.input } : {});\n }\n if (verdict.decision === 'deny') {\n return decide('deny', { permissionDecisionReason: verdict.reason });\n }\n this.#asked.set(hookInput.tool_use_id, {\n ...(verdict.ruleId !== undefined && { ruleId: verdict.ruleId }),\n ...(verdict.reason !== undefined && { reason: verdict.reason }),\n });\n return decide('ask');\n };\n\n #canUseTool: CanUseTool = (toolName, input, { signal, toolUseID }) => {\n if (this.#closed) return Promise.resolve({ behavior: 'deny', message: 'Session is closed' });\n let label: { ruleId?: string; reason?: string } = {};\n if (this.#gate) {\n // Only calls the gate itself answered `ask` may prompt; anything else\n // reaching here bypassed the hook and is refused (defense in depth).\n // Session-scope grants live in the host's gateway, not here.\n const asked = this.#asked.get(toolUseID);\n if (!asked) {\n return Promise.resolve({\n behavior: 'deny',\n message: `Tool \"${toolName}\" was not cleared by the tool gateway`,\n });\n }\n this.#asked.delete(toolUseID);\n label = asked;\n } else {\n if (this.#sessionAllowed.has(toolName)) return Promise.resolve({ behavior: 'allow' });\n if (!this.#askTools.has(toolName)) {\n return Promise.resolve({\n behavior: 'deny',\n message: `Tool \"${toolName}\" is not permitted by this app`,\n });\n }\n }\n const approvalId = toolUseID || randomUUID();\n return new Promise<PermissionResult>((resolve) => {\n this.#pendingApprovals.set(approvalId, { tool: toolName, resolve });\n signal.addEventListener('abort', () => {\n this.#settleApproval(approvalId, { behavior: 'deny' }, 'session_closed');\n });\n this.#pushOutOfBand({\n type: 'approval_required',\n approvalId,\n tool: toolName,\n input,\n ...label,\n });\n });\n };\n\n async respondToApproval(approvalId: string, decision: ApprovalDecision): Promise<void> {\n if (!this.#pendingApprovals.has(approvalId)) {\n throw new AppSdkError(`Unknown or already resolved approval \"${approvalId}\"`);\n }\n const reason = decision.behavior === 'deny' ? (decision.reason ?? 'user') : 'user';\n this.#settleApproval(approvalId, decision, reason);\n }\n\n #settleApproval(\n approvalId: string,\n decision: ApprovalDecision,\n reason: 'user' | 'timeout' | 'session_closed',\n ): void {\n const pending = this.#pendingApprovals.get(approvalId);\n if (!pending) return;\n this.#pendingApprovals.delete(approvalId);\n if (decision.behavior === 'allow') {\n if (!this.#gate && (decision.scope === 'session' || decision.scope === 'always')) {\n this.#sessionAllowed.add(pending.tool);\n }\n pending.resolve({ behavior: 'allow' });\n } else {\n pending.resolve({ behavior: 'deny', message: decision.message ?? 'Denied by user' });\n }\n this.#pushOutOfBand({\n type: 'approval_resolved',\n approvalId,\n behavior: decision.behavior,\n reason,\n });\n }\n\n #pushOutOfBand(event: AgentEvent): void {\n this.#outOfBand.push(event);\n this.#wakeOutOfBand?.();\n }\n\n #denyAllPending(): void {\n for (const approvalId of [...this.#pendingApprovals.keys()]) {\n this.#settleApproval(approvalId, { behavior: 'deny' }, 'session_closed');\n }\n }\n\n async *#run(input: string): AsyncGenerator<AgentEvent> {\n try {\n this.#queue.push({\n type: 'user',\n message: { role: 'user', content: input },\n parent_tool_use_id: null,\n });\n while (true) {\n yield* this.#outOfBand.splice(0);\n this.#sdkNext ??= this.#iterator.next();\n const wake = new Promise<void>((resolve) => {\n this.#wakeOutOfBand = resolve;\n });\n // An event pushed while the generator was suspended in yield* above\n // would have woken a stale waker; re-check before racing.\n if (this.#outOfBand.length > 0) continue;\n const winner = await Promise.race([\n this.#sdkNext.then((result) => ({ sdk: result })),\n wake.then(() => undefined),\n ]);\n if (winner === undefined) continue; // out-of-band event arrived; drain at loop top\n this.#sdkNext = undefined;\n const next = winner.sdk;\n if (next.done) {\n yield { type: 'result', ok: false, error: 'Session ended unexpectedly' };\n return;\n }\n const message = next.value;\n for (const event of mapMessage(message, this.#tally)) {\n if (event.type === 'session') this.#id = event.sessionId;\n yield event;\n }\n if (message.type === 'result') {\n yield* this.#outOfBand.splice(0);\n return;\n }\n }\n } finally {\n this.#wakeOutOfBand = undefined;\n this.#busy = false;\n }\n }\n\n async interrupt(): Promise<void> {\n this.#denyAllPending();\n await this.#query.interrupt();\n }\n\n async close(): Promise<void> {\n if (this.#closed) return;\n this.#closed = true;\n this.#denyAllPending();\n this.#queue.end();\n await this.#iterator.return?.(undefined)?.catch(() => undefined);\n }\n}\n\nexport class ClaudeCodeProvider implements AIProvider {\n readonly name: string = 'claude-code';\n readonly supportsApprovals: boolean = true;\n readonly supportsToolGate: boolean = true;\n readonly #defaults: Partial<SessionOptions>;\n readonly #projectsDir: string | undefined;\n\n constructor(options: ClaudeCodeProviderOptions = {}) {\n this.#defaults = options.sessionDefaults ?? {};\n this.#projectsDir = options.claudeProjectsDir;\n }\n\n async createSession(options: SessionOptions = {}): Promise<AISession> {\n const merged = { ...this.#defaults, ...options };\n const skillsPluginDir = merged.skillDirs?.length\n ? await ensureSkillsPlugin(merged.skillDirs)\n : undefined;\n return new ClaudeSession(merged, skillsPluginDir);\n }\n\n async history(sessionId: string): Promise<ChatMessage[]> {\n return readSessionHistory(sessionId, this.#projectsDir ?? defaultProjectsDir());\n }\n\n /** Snapshots Claude Code's own JSONL transcripts; unknown ids are omitted. */\n async exportTranscripts(sessionIds: string[]): Promise<SessionTranscript[]> {\n return exportSessionTranscripts(sessionIds, this.#projectsDir ?? defaultProjectsDir());\n }\n\n /**\n * Restores transcripts into the project dir Claude Code derives from `cwd`\n * (the app's data dir on this machine). Existing transcripts are left alone.\n */\n async importTranscripts(\n transcripts: SessionTranscript[],\n options: { cwd: string },\n ): Promise<void> {\n return importSessionTranscripts(transcripts, {\n cwd: options.cwd,\n projectsDir: this.#projectsDir ?? defaultProjectsDir(),\n });\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alashi/provider-claude",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Claude Code AIProvider for alashi, built on the Claude Agent SDK",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -24,8 +24,8 @@
24
24
  "dist"
25
25
  ],
26
26
  "dependencies": {
27
- "@alashi/apps-sdk": "^0.11.0",
28
- "@anthropic-ai/claude-agent-sdk": "^0.3.232"
27
+ "@alashi/apps-sdk": "^0.12.1",
28
+ "@anthropic-ai/claude-agent-sdk": "^0.3.291"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^26.2.0",