shraga 0.1.12 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,47 +0,0 @@
1
- /**
2
- * Helpers shared across add-on engine adapters (multi-provider loop, native cursor).
3
- * Kept here so those engines don't duplicate event/transcript plumbing.
4
- */
5
- import type { WsEvent } from '../claude.ts';
6
- import type { ConvMessage } from '../sessions.ts';
7
-
8
- /** Surface inline preview images embedded in a tool result as a separate image event. */
9
- export function* extractPreviewImages(output: string, toolUseId: string): Generator<WsEvent> {
10
- if (!output.includes('preview') || !output.includes('data:image/')) return;
11
- try {
12
- const outer = JSON.parse(output);
13
- // Cursor MCP wraps: {status, value: {content: [{text: {text: "<inner-json>"}}]}}
14
- const innerRaw = outer?.value?.content?.[0]?.text?.text ?? outer?.value?.content?.[0]?.text;
15
- const parsed = typeof innerRaw === 'string' ? JSON.parse(innerRaw) : outer;
16
- const dataUrl = parsed?.preview;
17
- if (typeof dataUrl === 'string' && dataUrl.startsWith('data:image/')) {
18
- yield { type: 'tool_result_image', toolUseId, dataUrl } as any;
19
- }
20
- } catch {}
21
- }
22
-
23
- /** Flatten prior conversation turns into a compact <conversation-history> block for engines that lack native multi-turn state. */
24
- export function buildConversationSummary(conversation: ConvMessage[]): string {
25
- if (conversation.length <= 1) return '';
26
- // Skip the last message (it's the current user prompt, already in the prompt)
27
- const prior = conversation.slice(0, -1);
28
- const lines: string[] = ['<conversation-history>'];
29
- for (const msg of prior) {
30
- const texts = msg.blocks
31
- .filter((b: any) => b.type === 'text' && b.text)
32
- .map((b: any) => b.text.trim())
33
- .filter(Boolean);
34
- const tools = msg.blocks
35
- .filter((b: any) => b.type === 'tool_use')
36
- .map((b: any) => b.tool);
37
- const images = msg.blocks.filter((b: any) => b.type === 'image').length;
38
- if (texts.length || tools.length) {
39
- lines.push(`[${msg.role}]`);
40
- for (const t of texts) lines.push(t.length > 500 ? t.slice(0, 500) + '…' : t);
41
- if (tools.length) lines.push(`(tools used: ${tools.join(', ')})`);
42
- if (images) lines.push(`(${images} image(s) shown)`);
43
- }
44
- }
45
- lines.push('</conversation-history>');
46
- return lines.length > 2 ? lines.join('\n') : '';
47
- }
@@ -1,74 +0,0 @@
1
- // Voice-provider seam — the engine-level drop-in for the optional duplex (voice) brain.
2
- //
3
- // An add-on engine supports a "voice" turn: when a turn requests the voice channel it hands off to a
4
- // REFLEX/ACT/THINK duplex agent instead of the normal single agent. The engine knows HOW to drive such
5
- // a session (send a turn, cancel background workers, wait for idle) but the seam keeps CE from having
6
- // to know how one is BUILT — the duplex agent, its reflex/think model choices, and its voice runtime
7
- // import all belong to an optional add-on, not to CE.
8
- //
9
- // So the core owns only this seam contract: when a turn is in voice mode, the engine calls the
10
- // registered provider to build an opaque duplex session and then drives it. CE registers NOTHING here
11
- // — with no provider, voice mode is a silent no-op (the turn runs as a normal agent turn). An add-on
12
- // (shraga-ee) registers the provider that constructs the duplex agent and resolves the reflex/think
13
- // models off the opaque `directives`/`config` bags (whose voice keys CE no longer names).
14
- //
15
- // Sibling of `features.ts` / `turn-context.ts` and shaped like them: typed, optional, registered
16
- // before startup, idempotent, a no-op when nothing registers.
17
-
18
- /** Everything the core has assembled for the turn that a duplex builder needs. `directives`/`config`
19
- * are passed opaquely — the provider reads its own voice keys (e.g. `voiceModel`/`thinkModel`) off
20
- * them; the core never names them. */
21
- export interface VoiceBuildInput {
22
- /** The provider AI client (opaque to core — the add-on that registers the provider knows its type). */
23
- ai: unknown;
24
- /** The project-rooted filesystem the provider uses (opaque to core). */
25
- fs: unknown;
26
- /** The turn's host (notify/ask/confirm) — the duplex agent emits its worker/reflex events through it. */
27
- host: unknown;
28
- /** The picked model = the Act tier. */
29
- actModel: string;
30
- /** Opaque per-session directives bag — the provider reads its own voice keys. */
31
- directives: Record<string, unknown>;
32
- /** Opaque persisted agent config bag — the provider reads its own voice keys. */
33
- config: Record<string, unknown>;
34
- /** Assembled tool set for the Act/Think workers. */
35
- tools: unknown[];
36
- /** Security hooks for the workers. */
37
- hooks: unknown;
38
- /** System prompt the workers carry (shraga's real prompt). */
39
- systemPrompt: string;
40
- /** Max agent steps for the workers. */
41
- maxSteps: number;
42
- /** Subagents directory, if any. */
43
- agentsDir?: string;
44
- /** Turn abort signal (reflex + workers). */
45
- signal?: AbortSignal;
46
- /** Connected MCP server names (for the reflex's capability quick-look). */
47
- mcpServerNames: string[];
48
- /** Project root (for cursor warm-session provider options). */
49
- projectRoot: string;
50
- /** Chat session id (for cursor warm-session keying). */
51
- sessionId: string;
52
- }
53
-
54
- /**
55
- * Build an opaque duplex session for a voice turn. The return is driven by the core's run loop
56
- * (`send(prompt)`, background-worker cancel via `tasks`/`cancelTask`, `idle()`) — its concrete shape
57
- * is the add-on's `DuplexAgent`, kept opaque here so the core names none of it.
58
- */
59
- export type VoiceProvider = (input: VoiceBuildInput) => Promise<unknown> | unknown;
60
-
61
- let provider: VoiceProvider | null = null;
62
-
63
- /** Register the voice provider. Called by an optional add-on before startup. First registration wins
64
- * (idempotent), matching the other seams. */
65
- export function registerVoiceProvider(p: VoiceProvider): void {
66
- if (provider) return;
67
- provider = p;
68
- }
69
-
70
- /** The registered voice provider, or null when none is registered (CE's own state → voice mode is a
71
- * no-op and the turn runs as a normal agent turn). */
72
- export function getVoiceProvider(): VoiceProvider | null {
73
- return provider;
74
- }