@mobius-os/mobius 0.3.5 → 0.3.6

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
@@ -1,30 +1,50 @@
1
1
  {
2
2
  "name": "@mobius-os/mobius",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "type": "module",
5
5
  "description": "Mobius terminal client. Reuses Mobius frontend TypeScript types and jsonl entry shapes.",
6
6
  "bin": {
7
7
  "mobius": "bin/mobius-tui.js",
8
8
  "mobius-tui": "bin/mobius-tui.js"
9
9
  },
10
- "scripts": {
11
- "start": "tsx src/main.tsx"
12
- },
13
10
  "files": [
14
11
  "bin",
15
12
  "src",
16
13
  "README.md"
17
14
  ],
15
+ "scripts": {
16
+ "dev": "tsx src/main.tsx",
17
+ "start": "tsx src/main.tsx",
18
+ "typecheck": "tsc --noEmit",
19
+ "test:integration": "tsx tests/integration.test.ts",
20
+ "test:ui": "tsx tests/ui.test.tsx",
21
+ "test:flow": "tsx tests/flow.test.tsx",
22
+ "test:resume": "tsx tests/resume.test.tsx",
23
+ "test:aimux": "tsx tests/aimux.test.tsx",
24
+ "test:reconnect": "tsx tests/reconnect.test.tsx",
25
+ "test:screen": "tsx tests/screen.test.tsx",
26
+ "test:scroll": "tsx tests/scroll.test.tsx",
27
+ "test": "npm run typecheck && npm run test:ui && npm run test:integration"
28
+ },
18
29
  "dependencies": {
19
30
  "chalk": "^5.3.0",
20
31
  "cli-highlight": "2.1.11",
21
32
  "extract-zip": "^2.0.1",
22
33
  "ink": "5.2.0",
23
34
  "marked": "12.0.2",
24
- "react": "18.3.1",
25
- "tsx": "4.19.2"
35
+ "react": "18.3.1"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "18.19.34",
39
+ "@types/react": "18.3.3",
40
+ "ink-testing-library": "4.0.0",
41
+ "tsx": "4.19.2",
42
+ "typescript": "5.4.5"
26
43
  },
27
44
  "engines": {
28
45
  "node": ">=18"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public"
29
49
  }
30
50
  }
@@ -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 = `• Working (${elapsed} · esc to interrupt)`
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>
@@ -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') {