@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 +1 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +102 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
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[]>;
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/claude-provider.ts","../src/transcripts.ts"],"mappings":";;
|
|
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
|
-
|
|
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")
|
|
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 = {}) {
|
package/dist/index.mjs.map
CHANGED
|
@@ -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
|
+
"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.
|
|
27
|
+
"@alashi/apps-sdk": "^0.10.0",
|
|
28
28
|
"@anthropic-ai/claude-agent-sdk": "^0.3.232"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|