@alashi/provider-claude 0.3.0 → 0.4.0

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.
package/dist/index.d.mts CHANGED
@@ -9,6 +9,7 @@ interface ClaudeCodeProviderOptions {
9
9
  declare class ClaudeCodeProvider implements AIProvider {
10
10
  #private;
11
11
  readonly name: string;
12
+ readonly supportsApprovals: boolean;
12
13
  constructor(options?: ClaudeCodeProviderOptions);
13
14
  createSession(options?: SessionOptions): Promise<AISession>;
14
15
  history(sessionId: string): Promise<ChatMessage[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/claude-provider.ts","../src/transcripts.ts"],"mappings":";;UAsBiB;;EAEf,kBAAkB,QAAQ;;EAE1B;;cAoGW,8BAA8B;;WAChC;EAIT,YAAY,UAAS;EAKrB,cAAoB,UAAS,iBAAsB,QAAQ;EAQ3D,QAAc,oBAAoB,QAAQ;;EAK1C,kBAAwB,uBAAuB,QAAQ;;;;;EAQvD,kBACE,aAAa,qBACb;IAAW;MACV;;;;;;;;;;;;iBCnJW,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":";;UA0BiB;;EAEf,kBAAkB,QAAQ;;EAE1B;;cAqNW,8BAA8B;;WAChC;WACA;EAIT,YAAY,UAAS;EAKrB,cAAoB,UAAS,iBAAsB,QAAQ;EAQ3D,QAAc,oBAAoB,QAAQ;;EAK1C,kBAAwB,uBAAuB,QAAQ;;;;;EAQvD,kBACE,aAAa,qBACb;IAAW;MACV;;;;;;;;;;;;iBCzQW,eAAe;;;;;;iBAkBT,yBACpB,+BACA,sBACC,QAAQ;;;;;;;;;;iBAwBW,yBACpB,sBAAsB,qBACtB;EAAW;EAAa;IACvB"}
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
+ import { createHash, randomUUID } from "node:crypto";
1
2
  import { AppSdkError } from "@alashi/apps-sdk";
2
3
  import { query } from "@anthropic-ai/claude-agent-sdk";
3
4
  import { mkdir, readFile, readdir, rename, rm, stat, symlink, writeFile } from "node:fs/promises";
4
5
  import { homedir, tmpdir } from "node:os";
5
6
  import { basename, join } from "node:path";
6
- import { createHash } from "node:crypto";
7
7
  //#region src/event-mapping.ts
8
8
  /**
9
9
  * Converts the Agent SDK's per-session usage totals into per-turn deltas.
@@ -63,6 +63,16 @@ function mapMessage(message, tally) {
63
63
  sessionId: message.session_id,
64
64
  model: message.model
65
65
  }];
66
+ if (message.subtype === "compact_boundary") {
67
+ const meta = message.compact_metadata;
68
+ return [{
69
+ type: "compaction",
70
+ trigger: meta.trigger,
71
+ preTokens: meta.pre_tokens,
72
+ ...meta.post_tokens !== void 0 && { postTokens: meta.post_tokens },
73
+ ...meta.duration_ms !== void 0 && { durationMs: meta.duration_ms }
74
+ }];
75
+ }
66
76
  return [];
67
77
  case "stream_event": {
68
78
  const event = message.event;
@@ -343,8 +353,13 @@ async function importSessionTranscripts(transcripts, options) {
343
353
  }
344
354
  //#endregion
345
355
  //#region src/claude-provider.ts
346
- function toQueryOptions(options, skillsPluginDir) {
347
- const out = {
356
+ function toQueryOptions(options, skillsPluginDir, canUseTool) {
357
+ const out = canUseTool ? {
358
+ permissionMode: "default",
359
+ canUseTool,
360
+ settingSources: [],
361
+ includePartialMessages: true
362
+ } : {
348
363
  permissionMode: "dontAsk",
349
364
  settingSources: [],
350
365
  includePartialMessages: true
@@ -382,10 +397,18 @@ var ClaudeSession = class {
382
397
  #busy = false;
383
398
  #closed = false;
384
399
  #tally = new UsageTally();
400
+ #askTools;
401
+ #sessionAllowed = /* @__PURE__ */ new Set();
402
+ #pendingApprovals = /* @__PURE__ */ new Map();
403
+ #outOfBand = [];
404
+ #wakeOutOfBand;
405
+ #sdkNext;
385
406
  constructor(options, skillsPluginDir) {
407
+ this.#askTools = new Set(options.askTools ?? []);
408
+ const canUseTool = this.#askTools.size > 0 ? this.#canUseTool : void 0;
386
409
  this.#query = query({
387
410
  prompt: this.#queue,
388
- options: toQueryOptions(options, skillsPluginDir)
411
+ options: toQueryOptions(options, skillsPluginDir, canUseTool)
389
412
  });
390
413
  this.#iterator = this.#query[Symbol.asyncIterator]();
391
414
  }
@@ -398,6 +421,63 @@ var ClaudeSession = class {
398
421
  this.#busy = true;
399
422
  return this.#run(input);
400
423
  }
424
+ #canUseTool = (toolName, input, { signal, toolUseID }) => {
425
+ if (this.#closed) return Promise.resolve({
426
+ behavior: "deny",
427
+ message: "Session is closed"
428
+ });
429
+ if (this.#sessionAllowed.has(toolName)) return Promise.resolve({ behavior: "allow" });
430
+ if (!this.#askTools.has(toolName)) return Promise.resolve({
431
+ behavior: "deny",
432
+ message: `Tool "${toolName}" is not permitted by this app`
433
+ });
434
+ const approvalId = toolUseID || randomUUID();
435
+ return new Promise((resolve) => {
436
+ this.#pendingApprovals.set(approvalId, {
437
+ tool: toolName,
438
+ resolve
439
+ });
440
+ signal.addEventListener("abort", () => {
441
+ this.#settleApproval(approvalId, { behavior: "deny" }, "session_closed");
442
+ });
443
+ this.#pushOutOfBand({
444
+ type: "approval_required",
445
+ approvalId,
446
+ tool: toolName,
447
+ input
448
+ });
449
+ });
450
+ };
451
+ async respondToApproval(approvalId, decision) {
452
+ if (!this.#pendingApprovals.has(approvalId)) throw new AppSdkError(`Unknown or already resolved approval "${approvalId}"`);
453
+ const reason = decision.behavior === "deny" ? decision.reason ?? "user" : "user";
454
+ this.#settleApproval(approvalId, decision, reason);
455
+ }
456
+ #settleApproval(approvalId, decision, reason) {
457
+ const pending = this.#pendingApprovals.get(approvalId);
458
+ if (!pending) return;
459
+ this.#pendingApprovals.delete(approvalId);
460
+ if (decision.behavior === "allow") {
461
+ if (decision.scope === "session" || decision.scope === "always") this.#sessionAllowed.add(pending.tool);
462
+ pending.resolve({ behavior: "allow" });
463
+ } else pending.resolve({
464
+ behavior: "deny",
465
+ message: decision.message ?? "Denied by user"
466
+ });
467
+ this.#pushOutOfBand({
468
+ type: "approval_resolved",
469
+ approvalId,
470
+ behavior: decision.behavior,
471
+ reason
472
+ });
473
+ }
474
+ #pushOutOfBand(event) {
475
+ this.#outOfBand.push(event);
476
+ this.#wakeOutOfBand?.();
477
+ }
478
+ #denyAllPending() {
479
+ for (const approvalId of [...this.#pendingApprovals.keys()]) this.#settleApproval(approvalId, { behavior: "deny" }, "session_closed");
480
+ }
401
481
  async *#run(input) {
402
482
  try {
403
483
  this.#queue.push({
@@ -409,7 +489,16 @@ var ClaudeSession = class {
409
489
  parent_tool_use_id: null
410
490
  });
411
491
  while (true) {
412
- const next = await this.#iterator.next();
492
+ yield* this.#outOfBand.splice(0);
493
+ this.#sdkNext ??= this.#iterator.next();
494
+ const wake = new Promise((resolve) => {
495
+ this.#wakeOutOfBand = resolve;
496
+ });
497
+ if (this.#outOfBand.length > 0) continue;
498
+ const winner = await Promise.race([this.#sdkNext.then((result) => ({ sdk: result })), wake.then(() => void 0)]);
499
+ if (winner === void 0) continue;
500
+ this.#sdkNext = void 0;
501
+ const next = winner.sdk;
413
502
  if (next.done) {
414
503
  yield {
415
504
  type: "result",
@@ -423,24 +512,31 @@ var ClaudeSession = class {
423
512
  if (event.type === "session") this.#id = event.sessionId;
424
513
  yield event;
425
514
  }
426
- if (message.type === "result") return;
515
+ if (message.type === "result") {
516
+ yield* this.#outOfBand.splice(0);
517
+ return;
518
+ }
427
519
  }
428
520
  } finally {
521
+ this.#wakeOutOfBand = void 0;
429
522
  this.#busy = false;
430
523
  }
431
524
  }
432
525
  async interrupt() {
526
+ this.#denyAllPending();
433
527
  await this.#query.interrupt();
434
528
  }
435
529
  async close() {
436
530
  if (this.#closed) return;
437
531
  this.#closed = true;
532
+ this.#denyAllPending();
438
533
  this.#queue.end();
439
534
  await this.#iterator.return?.(void 0)?.catch(() => void 0);
440
535
  }
441
536
  };
442
537
  var ClaudeCodeProvider = class {
443
538
  name = "claude-code";
539
+ supportsApprovals = true;
444
540
  #defaults;
445
541
  #projectsDir;
446
542
  constructor(options = {}) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["#costUsd","#models","#done","#resolvers","#values","#query","#queue","#iterator","#id","#closed","#busy","#run","#tally","#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 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 {\n type AgentEvent,\n type AIProvider,\n type AISession,\n AppSdkError,\n type ChatMessage,\n type SessionOptions,\n type SessionTranscript,\n} from '@alashi/apps-sdk';\nimport {\n type Options,\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\nfunction toQueryOptions(options: SessionOptions, skillsPluginDir?: string): Options {\n const out: Options = {\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 (options.allowedTools !== undefined) out.allowedTools = options.allowedTools;\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 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\n constructor(options: SessionOptions, skillsPluginDir?: string) {\n this.#query = query({ prompt: this.#queue, options: toQueryOptions(options, skillsPluginDir) });\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 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 const next = await this.#iterator.next();\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') return;\n }\n } finally {\n this.#busy = false;\n }\n }\n\n async interrupt(): Promise<void> {\n await this.#query.interrupt();\n }\n\n async close(): Promise<void> {\n if (this.#closed) return;\n this.#closed = true;\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 #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","#canUseTool","#query","#queue","#iterator","#id","#closed","#busy","#run","#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} from '@alashi/apps-sdk';\nimport {\n type CanUseTool,\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\nfunction toQueryOptions(\n options: SessionOptions,\n skillsPluginDir?: string,\n canUseTool?: CanUseTool,\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 (options.allowedTools !== undefined) out.allowedTools = options.allowedTools;\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 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 #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 const canUseTool = this.#askTools.size > 0 ? this.#canUseTool : undefined;\n this.#query = query({\n prompt: this.#queue,\n options: toQueryOptions(options, skillsPluginDir, canUseTool),\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 #canUseTool: CanUseTool = (toolName, input, { signal, toolUseID }) => {\n if (this.#closed) return Promise.resolve({ behavior: 'deny', message: 'Session is closed' });\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 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({ type: 'approval_required', approvalId, tool: toolName, input });\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 (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 #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.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Claude Code AIProvider for alashi, built on the Claude Agent SDK",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -24,7 +24,7 @@
24
24
  "dist"
25
25
  ],
26
26
  "dependencies": {
27
- "@alashi/apps-sdk": "^0.8.0",
27
+ "@alashi/apps-sdk": "^0.10.0",
28
28
  "@anthropic-ai/claude-agent-sdk": "^0.3.232"
29
29
  },
30
30
  "devDependencies": {