@mobius-os/mobius 0.3.5 → 0.3.7
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/README.md +4 -3
- package/package.json +1 -1
- package/src/components/Chat.tsx +14 -4
- package/src/lib/entry-view.ts +1 -1
package/README.md
CHANGED
|
@@ -169,6 +169,7 @@ mobius
|
|
|
169
169
|
|
|
170
170
|
If the PATH export is not already in the shell profile, add it to `~/.bashrc`
|
|
171
171
|
once. Using the explicit user prefix avoids the `EACCES ... /usr/local/lib/node_modules/mobius`
|
|
172
|
-
error produced by a root-owned npm global prefix. The built package
|
|
173
|
-
`tsx`
|
|
174
|
-
TypeScript entry point without retaining the source checkout's
|
|
172
|
+
error produced by a root-owned npm global prefix. The source and built package
|
|
173
|
+
both declare `tsx` as a runtime dependency, so production/global installs can
|
|
174
|
+
execute the TypeScript entry point without retaining the source checkout's
|
|
175
|
+
devDependencies.
|
package/package.json
CHANGED
package/src/components/Chat.tsx
CHANGED
|
@@ -12,7 +12,7 @@ import { Box, Static, Text, useInput, useStdout } from 'ink'
|
|
|
12
12
|
import { useChat } from '../hooks/useChat.js'
|
|
13
13
|
import { MobiusClient } from '../api.js'
|
|
14
14
|
import { renderMarkdownLines } from '../markdown.js'
|
|
15
|
-
import { viewsForEntry, dedupeUserEntries, toolLabel, type EntryView } from '../lib/entry-view.js'
|
|
15
|
+
import { viewsForEntry, dedupeUserEntries, toolLabel, isAssistantOutput, type EntryView } from '../lib/entry-view.js'
|
|
16
16
|
import type { ReadyState } from './PrepScreen.js'
|
|
17
17
|
import type { AnyEntry } from '../types.js'
|
|
18
18
|
import type { AimuxStatus } from '../aimux.js'
|
|
@@ -80,6 +80,14 @@ export function ChatScreen({ client, ready, webUserId, resumeSessionId, onClear,
|
|
|
80
80
|
// (the terminal's own scrollback holds history; no in-app pager needed).
|
|
81
81
|
const showWelcome = chat.entries.length === 0 && chat.pendingUser === null
|
|
82
82
|
|
|
83
|
+
// First query of a fresh session triggers the full backend bootstrap (lazy
|
|
84
|
+
// session creation, worker spawn, context load) before any output streams.
|
|
85
|
+
// Label that phase "Initializing for the first query" instead of "Working"
|
|
86
|
+
// so it reads as startup rather than a stuck agent. Once the first assistant
|
|
87
|
+
// output is observed (or the session is a resumed one with prior history),
|
|
88
|
+
// the indicator falls back to the normal Working label for every turn.
|
|
89
|
+
const firstQueryInFlight = !resumeSessionId && !chat.entries.some(isAssistantOutput)
|
|
90
|
+
|
|
83
91
|
// 用户输入去重 (对齐 web viewer/rounds.ts buildRounds): codex 同一提问的 3 形态
|
|
84
92
|
// (type:user / response_item.message[user] / event_msg.user_message) 合并成 1 条,
|
|
85
93
|
// 避免在累积视图里把同一条提问显示多次.
|
|
@@ -113,7 +121,7 @@ export function ChatScreen({ client, ready, webUserId, resumeSessionId, onClear,
|
|
|
113
121
|
</Static>
|
|
114
122
|
|
|
115
123
|
{chat.pendingUser !== null ? <UserLine text={chat.pendingUser} /> : null}
|
|
116
|
-
{chat.typing ? <WorkingIndicator /> : null}
|
|
124
|
+
{chat.typing ? <WorkingIndicator firstQuery={firstQueryInFlight} /> : null}
|
|
117
125
|
{chat.error ? <Text color="red">⚠ {chat.error}</Text> : null}
|
|
118
126
|
|
|
119
127
|
{chat.entries.length === 0 && chat.pendingUser === null && !showHelp
|
|
@@ -347,7 +355,7 @@ function UserLine({ text }: { text: string }) {
|
|
|
347
355
|
return <Box marginTop={1}><Text bold>{lines.join('\n')}</Text></Box>
|
|
348
356
|
}
|
|
349
357
|
|
|
350
|
-
function WorkingIndicator() {
|
|
358
|
+
function WorkingIndicator({ firstQuery }: { firstQuery: boolean }) {
|
|
351
359
|
const startedAt = useRef(Date.now())
|
|
352
360
|
const [animationFrame, setAnimationFrame] = useState(0)
|
|
353
361
|
useEffect(() => {
|
|
@@ -356,7 +364,9 @@ function WorkingIndicator() {
|
|
|
356
364
|
}, [])
|
|
357
365
|
const secs = Math.floor((Date.now() - startedAt.current) / 1000)
|
|
358
366
|
const elapsed = secs >= 60 ? `${Math.floor(secs / 60)}m ${String(secs % 60).padStart(2, '0')}s` : `${secs}s`
|
|
359
|
-
const label =
|
|
367
|
+
const label = firstQuery
|
|
368
|
+
? `• Initializing for the first query (${elapsed})`
|
|
369
|
+
: `• Working (${elapsed} · esc to interrupt)`
|
|
360
370
|
return (
|
|
361
371
|
<Box marginTop={1}>
|
|
362
372
|
<Text>{shimmerText(label, animationFrame)}</Text>
|
package/src/lib/entry-view.ts
CHANGED
|
@@ -669,7 +669,7 @@ export function stripUserFraming(text: string): string {
|
|
|
669
669
|
return after || text
|
|
670
670
|
}
|
|
671
671
|
|
|
672
|
-
function isAssistantOutput(e: AnyEntry): boolean {
|
|
672
|
+
export function isAssistantOutput(e: AnyEntry): boolean {
|
|
673
673
|
if (e?.type === 'assistant') return true
|
|
674
674
|
if (e?.type === 'event_msg' && e?.payload?.type === 'agent_message') return true
|
|
675
675
|
if (e?.type === 'response_item') {
|