The 26b is slow (~30s/call); the chat now shows the answer forming instead of freezing until it's done. The final prose streams over SSE; tool-calling turns stay structured (no partial tokens), so streaming kicks in for the narration. core (@commitea/core): - chat-client.complete gains an optional onToken — when set, it requests stream:true and parses the OpenAI SSE stream, emitting content deltas and assembling streamed tool-call argument fragments into the final result. - GiteaHttpResponse exposes the optional `body` stream (real fetch has it; stubs don't). agent-loop threads onToken to each completion. app: - model:chat forwards each delta to the renderer (event.sender.send); preload exposes model.onToken(cb) → unsubscribe. useChat accumulates the live stream into a growing bubble (with a cursor), replaced by the authoritative final content when the turn resolves. Unconfigured → scripted reply, unchanged. Verified: 118 core tests green (2 streaming: SSE content deltas + tool-call fragment assembly), desktop typecheck clean, 14 fixture e2e green. Live: a real turn against gemma-4-26b assembles the correct answer via the streaming path (live-reginald green) — the reply now renders token-by-token. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
95 lines
3.0 KiB
TypeScript
95 lines
3.0 KiB
TypeScript
import type {
|
|
AgentStep,
|
|
ChangeProposal,
|
|
ChatMessage,
|
|
CaptureProposal,
|
|
DependencyEdge,
|
|
DirectiveRecord,
|
|
GiteaIssue,
|
|
GiteaMilestone,
|
|
IssueChange,
|
|
LabelPlan,
|
|
LifecycleEvent,
|
|
ProposedIssue,
|
|
} from '@commitea/core'
|
|
|
|
/** The result of a write through the bridge. */
|
|
export type ApplyChangeResult =
|
|
| { ok: false; reason: 'unconfigured' }
|
|
| { ok: true; plan: LabelPlan; issue: GiteaIssue }
|
|
|
|
/** The result of filing captured issues. */
|
|
export type CreateIssuesResult =
|
|
| { ok: false; reason: 'unconfigured' }
|
|
| { ok: true; created: { number: number; title: string }[] }
|
|
|
|
/** The result of a capture_work decomposition. */
|
|
export type CaptureResult =
|
|
| { ok: false; reason: 'unconfigured' | 'error'; message?: string }
|
|
| ({ ok: true } & CaptureProposal)
|
|
|
|
/** A reconciled snapshot as it crosses the bridge. */
|
|
export interface SnapshotPayload {
|
|
configured: boolean
|
|
issues: GiteaIssue[]
|
|
milestones: GiteaMilestone[]
|
|
deps: DependencyEdge[]
|
|
/** Normalized lifecycle events keyed by issue number. */
|
|
timelines: Record<number, LifecycleEvent[]>
|
|
/** true when served from the persisted cache (offline / instant boot). */
|
|
stale?: boolean
|
|
/** ISO time the persisted snapshot was reconciled (present on cached reads). */
|
|
savedAt?: string
|
|
}
|
|
|
|
/** Boot payload — the persisted snapshot, or a marker that there's none yet. */
|
|
export type BootPayload =
|
|
| { configured: false }
|
|
| { configured: true; cached: false }
|
|
| ({ configured: true; cached: true } & Omit<SnapshotPayload, 'configured'>)
|
|
|
|
/** The gitea bridge exposed by the preload over IPC (main-process backed). */
|
|
export interface GiteaBridge {
|
|
status(): Promise<{ configured: boolean; repo: string | null }>
|
|
boot(): Promise<BootPayload>
|
|
reconcile(): Promise<SnapshotPayload>
|
|
getIssue(index: number): Promise<GiteaIssue | null>
|
|
applyChange(change: IssueChange): Promise<ApplyChangeResult>
|
|
createIssues(issues: ProposedIssue[]): Promise<CreateIssuesResult>
|
|
}
|
|
|
|
/** One agent turn's result. */
|
|
export type ChatResult =
|
|
| { ok: false; reason: 'unconfigured' | 'error'; message?: string }
|
|
| { ok: true; content: string; steps: AgentStep[]; proposals: ChangeProposal[] }
|
|
|
|
/** The model bridge (Reginald) exposed by the preload over IPC. */
|
|
export interface ModelBridge {
|
|
status(): Promise<{ configured: boolean; model: string | null }>
|
|
chat(messages: ChatMessage[]): Promise<ChatResult>
|
|
capture(braindump: string): Promise<CaptureResult>
|
|
/** Subscribe to streamed prose tokens; returns an unsubscribe fn. */
|
|
onToken(cb: (delta: string) => void): () => void
|
|
}
|
|
|
|
/** The result of reading the directive ledger. */
|
|
export type DirectivesResult =
|
|
| { ok: false; reason: 'unconfigured' | 'error'; message?: string }
|
|
| { ok: true; directives: DirectiveRecord[] }
|
|
|
|
/** The pm-state bridge (machine-derived state) exposed by the preload over IPC. */
|
|
export interface PmStateBridge {
|
|
directives(): Promise<DirectivesResult>
|
|
}
|
|
|
|
declare global {
|
|
interface Window {
|
|
commitea: {
|
|
platform: string
|
|
gitea: GiteaBridge
|
|
model: ModelBridge
|
|
pmstate: PmStateBridge
|
|
}
|
|
}
|
|
}
|