create-theokit 1.2.3 → 1.2.5
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/package.json
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"typecheck": "tsc --noEmit"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"theokit": "^0.30.
|
|
19
|
-
"@theokit/agents": "^0.35.
|
|
18
|
+
"theokit": "^0.30.2",
|
|
19
|
+
"@theokit/agents": "^0.35.2",
|
|
20
20
|
"@theokit/sdk": "^2.13.0",
|
|
21
21
|
"@theokit/ui": "^1.0.0",
|
|
22
22
|
"@usetheo/ui": "^0.14.0",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Box, Text, useApp, useInput } from 'ink'
|
|
2
|
-
import { type ReactElement } from 'react'
|
|
2
|
+
import { useMemo, useState, type ReactElement } from 'react'
|
|
3
3
|
import {
|
|
4
4
|
AgentStreaming,
|
|
5
5
|
AppStatusBar,
|
|
@@ -10,8 +10,9 @@ import {
|
|
|
10
10
|
WelcomeBanner,
|
|
11
11
|
} from '@theokit/tui'
|
|
12
12
|
import { uiMessagesToChatThread } from '@theokit/tui/ai-sdk'
|
|
13
|
+
import type { UIMessage } from 'ai'
|
|
13
14
|
import { InProcessTransport, useAgent } from 'theokit/client'
|
|
14
|
-
import { streamAgentTurnInProcess } from 'theokit/server'
|
|
15
|
+
import { streamAgentTurnInProcess } from 'theokit/server/agent'
|
|
15
16
|
|
|
16
17
|
import * as chatAgent from '../agents/chat.js'
|
|
17
18
|
|
|
@@ -19,6 +20,18 @@ import * as chatAgent from '../agents/chat.js'
|
|
|
19
20
|
const MODEL = 'openai/gpt-4o-mini'
|
|
20
21
|
const CWD = process.cwd()
|
|
21
22
|
|
|
23
|
+
/** The agent's opening line — so the conversation starts warm instead of empty (like a coding-agent CLI). */
|
|
24
|
+
const GREETING: UIMessage = {
|
|
25
|
+
id: 'greeting',
|
|
26
|
+
role: 'assistant',
|
|
27
|
+
parts: [
|
|
28
|
+
{
|
|
29
|
+
type: 'text',
|
|
30
|
+
text: "Hi — I'm your TheoKit agent. Ask me anything and I'll stream a reply. Type below and press Enter · /clear resets · esc cancels.",
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
}
|
|
34
|
+
|
|
22
35
|
/** Resolve the provider key from the environment (OpenRouter / Anthropic / OpenAI). */
|
|
23
36
|
const apiKey = (): string =>
|
|
24
37
|
process.env.OPENROUTER_API_KEY ?? process.env.ANTHROPIC_API_KEY ?? process.env.OPENAI_API_KEY ?? ''
|
|
@@ -26,10 +39,10 @@ const apiKey = (): string =>
|
|
|
26
39
|
/**
|
|
27
40
|
* M46 — the terminal surface, composed from `@theokit/tui` the way a Claude Code / OpenCode / Codex CLI is:
|
|
28
41
|
* a `WelcomeBanner` header, a scrolling `<ChatThread>`, a live `<AgentStreaming>` indicator, the Claude-Code
|
|
29
|
-
* bordered `<ChatComposer
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* `@theokit/tui/ai-sdk` adapter
|
|
42
|
+
* bordered `<ChatComposer>`, and a persistent `<AppStatusBar>` footer. The agent is driven by the UNIFIED
|
|
43
|
+
* `useAgent` hook (M41). `useAgent` reconstructs only the ASSISTANT turns, so — like the web surface — we
|
|
44
|
+
* track the user's turns locally and INTERLEAVE them, then project the whole thread onto `<ChatThread>` via
|
|
45
|
+
* the `@theokit/tui/ai-sdk` adapter (so both your prompts and the replies show, in order).
|
|
33
46
|
*/
|
|
34
47
|
const transport = new InProcessTransport({
|
|
35
48
|
run: (input) => streamAgentTurnInProcess(chatAgent, apiKey(), input),
|
|
@@ -37,10 +50,25 @@ const transport = new InProcessTransport({
|
|
|
37
50
|
|
|
38
51
|
export function App(): ReactElement {
|
|
39
52
|
const agent = useAgent<{ message: string }>(transport)
|
|
53
|
+
const [userMessages, setUserMessages] = useState<UIMessage[]>([])
|
|
40
54
|
const streaming = agent.status === 'streaming'
|
|
41
55
|
const elapsed = useTurnElapsed(streaming)
|
|
42
56
|
const { exit } = useApp()
|
|
43
57
|
|
|
58
|
+
// Interleave local user turns with the reconstructed assistant turns (mirrors the web surface), after the
|
|
59
|
+
// opening greeting — so the full conversation history renders, your prompt included.
|
|
60
|
+
const thread = useMemo<UIMessage[]>(() => {
|
|
61
|
+
const out: UIMessage[] = [GREETING]
|
|
62
|
+
const turns = Math.max(userMessages.length, agent.messages.length)
|
|
63
|
+
for (let i = 0; i < turns; i++) {
|
|
64
|
+
const user = userMessages[i]
|
|
65
|
+
const assistant = agent.messages[i]
|
|
66
|
+
if (user !== undefined) out.push(user)
|
|
67
|
+
if (assistant !== undefined) out.push(assistant)
|
|
68
|
+
}
|
|
69
|
+
return out
|
|
70
|
+
}, [userMessages, agent.messages])
|
|
71
|
+
|
|
44
72
|
// Esc cancels a running turn, or quits when idle (the Claude Code affordance).
|
|
45
73
|
useInput((_input, key) => {
|
|
46
74
|
if (!key.escape) return
|
|
@@ -52,9 +80,14 @@ export function App(): ReactElement {
|
|
|
52
80
|
const trimmed = text.trim()
|
|
53
81
|
if (trimmed.length === 0) return
|
|
54
82
|
if (trimmed === '/clear') {
|
|
83
|
+
setUserMessages([])
|
|
55
84
|
agent.reset()
|
|
56
85
|
return
|
|
57
86
|
}
|
|
87
|
+
setUserMessages((prev) => [
|
|
88
|
+
...prev,
|
|
89
|
+
{ id: `u-${String(prev.length)}`, role: 'user', parts: [{ type: 'text', text: trimmed }] },
|
|
90
|
+
])
|
|
58
91
|
agent.send({ message: trimmed })
|
|
59
92
|
}
|
|
60
93
|
|
|
@@ -69,7 +102,7 @@ export function App(): ReactElement {
|
|
|
69
102
|
<Text dimColor>cwd: {CWD}</Text>
|
|
70
103
|
</WelcomeBanner>
|
|
71
104
|
|
|
72
|
-
<ChatThread messages={uiMessagesToChatThread(
|
|
105
|
+
<ChatThread messages={uiMessagesToChatThread(thread)} />
|
|
73
106
|
|
|
74
107
|
{streaming ? <AgentStreaming thought="Thinking…" elapsedSeconds={elapsed} showCancelHint /> : null}
|
|
75
108
|
{agent.error ? <Text color="red">⚠ {agent.error.message}</Text> : null}
|