@miphamai/cli 0.39.0 → 0.39.1

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@miphamai/cli",
3
- "version": "0.39.0",
3
+ "version": "0.39.1",
4
4
  "description": "Mipham Code — Multi-model open-core intelligent coding terminal by MiphamAI",
5
5
  "keywords": [
6
6
  "ai",
@@ -1,4 +1,472 @@
1
- // ── 常量唯一真源:@mipham/shared/constants ──
2
- // DEFAULT_PROVIDERS / PROTOCOL_LABELS / TOOL_CATEGORIES / OLLAMA_PRESET_MODELS
3
- // 及路径(.mipham/memory)/Artifact 常量均在此包,CLI 直接 re-export。
4
- export * from '@mipham/shared/constants'
1
+ import type { ProviderConfig, ModelInfo } from './types'
2
+ import miphamModelsSnapshot from './mipham-models.json' with { type: 'json' }
3
+
4
+ // Build MiphamAI provider from synced model definitions.
5
+ // Source of truth: Engine GET /v1/mipham-models → packages/shared/src/mipham-models.json
6
+ // Run `bun run scripts/sync-mipham-models.ts` to update from Engine.
7
+ const MIPHAM_MODELS: ModelInfo[] = miphamModelsSnapshot.models.map((m) => ({
8
+ id: m.id,
9
+ name: m.name,
10
+ providerId: m.providerId,
11
+ contextWindow: m.contextWindow,
12
+ maxOutput: m.maxOutput,
13
+ vision: m.vision,
14
+ status: m.status as 'active' | 'upcoming' | 'deprecated',
15
+ }))
16
+
17
+ const MIPHAM_PROVIDER: ProviderConfig = {
18
+ id: miphamModelsSnapshot.provider.id,
19
+ name: miphamModelsSnapshot.provider.name,
20
+ protocol: miphamModelsSnapshot.provider.protocol as 'openai-compatible',
21
+ baseUrl: miphamModelsSnapshot.provider.baseUrl,
22
+ apiKey: miphamModelsSnapshot.provider.apiKey,
23
+ models: MIPHAM_MODELS,
24
+ status: miphamModelsSnapshot.provider.status as 'active' | 'upcoming',
25
+ }
26
+
27
+ // Providers: alphabetical by id. Models: Lite/Flash → Text → Vision → Code → Pro/Think
28
+ export const DEFAULT_PROVIDERS: ProviderConfig[] = [
29
+ {
30
+ id: 'anthropic',
31
+ name: 'Anthropic Claude',
32
+ protocol: 'anthropic',
33
+ apiKey: '${ANTHROPIC_API_KEY}',
34
+ models: [
35
+ {
36
+ id: 'claude-haiku-4-5-20251001',
37
+ name: 'Claude Haiku 4.5',
38
+ providerId: 'anthropic',
39
+ contextWindow: 200_000,
40
+ maxOutput: 32_000,
41
+ vision: true,
42
+ status: 'active',
43
+ },
44
+ {
45
+ id: 'claude-sonnet-4-6',
46
+ name: 'Claude Sonnet 4.6',
47
+ providerId: 'anthropic',
48
+ contextWindow: 1_000_000,
49
+ maxOutput: 128_000,
50
+ vision: true,
51
+ status: 'active',
52
+ },
53
+ {
54
+ id: 'claude-opus-4-8',
55
+ name: 'Claude Opus 4.8',
56
+ providerId: 'anthropic',
57
+ contextWindow: 1_000_000,
58
+ maxOutput: 128_000,
59
+ vision: true,
60
+ status: 'active',
61
+ },
62
+ {
63
+ id: 'claude-fable-5',
64
+ name: 'Claude Fable 5',
65
+ providerId: 'anthropic',
66
+ contextWindow: 1_000_000,
67
+ maxOutput: 128_000,
68
+ vision: true,
69
+ status: 'active',
70
+ },
71
+ {
72
+ id: 'claude-sonnet-5',
73
+ name: 'Claude Sonnet 5',
74
+ providerId: 'anthropic',
75
+ contextWindow: 1_000_000,
76
+ maxOutput: 128_000,
77
+ vision: true,
78
+ status: 'active',
79
+ },
80
+ {
81
+ id: 'claude-opus-5',
82
+ name: 'Claude Opus 5',
83
+ providerId: 'anthropic',
84
+ contextWindow: 1_000_000,
85
+ maxOutput: 128_000,
86
+ vision: true,
87
+ status: 'active',
88
+ },
89
+ ],
90
+ },
91
+ {
92
+ id: 'deepseek',
93
+ name: 'DeepSeek',
94
+ protocol: 'openai-compatible',
95
+ baseUrl: 'https://api.deepseek.com/v1',
96
+ apiKey: '${DEEPSEEK_API_KEY}',
97
+ models: [
98
+ {
99
+ id: 'deepseek-v4-flash',
100
+ name: 'DeepSeek V4 Flash',
101
+ providerId: 'deepseek',
102
+ contextWindow: 1_000_000,
103
+ maxOutput: 384_000,
104
+ vision: false,
105
+ status: 'active',
106
+ },
107
+ {
108
+ id: 'deepseek-v4-pro',
109
+ name: 'DeepSeek V4 Pro',
110
+ providerId: 'deepseek',
111
+ contextWindow: 1_000_000,
112
+ maxOutput: 384_000,
113
+ vision: false,
114
+ status: 'active',
115
+ },
116
+ ],
117
+ },
118
+ {
119
+ id: 'google',
120
+ name: 'Google Gemini',
121
+ protocol: 'openai-compatible',
122
+ baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
123
+ apiKey: '${GEMINI_API_KEY}',
124
+ models: [
125
+ {
126
+ id: 'gemini-3.0-flash',
127
+ name: 'Gemini 3.0 Flash',
128
+ providerId: 'google',
129
+ contextWindow: 1_000_000,
130
+ maxOutput: 64_000,
131
+ vision: true,
132
+ status: 'active',
133
+ },
134
+ {
135
+ id: 'gemini-3.0-pro',
136
+ name: 'Gemini 3.0 Pro',
137
+ providerId: 'google',
138
+ contextWindow: 1_000_000,
139
+ maxOutput: 128_000,
140
+ vision: true,
141
+ status: 'active',
142
+ },
143
+ {
144
+ id: 'gemini-2.5-pro',
145
+ name: 'Gemini 2.5 Pro',
146
+ providerId: 'google',
147
+ contextWindow: 1_000_000,
148
+ maxOutput: 64_000,
149
+ vision: true,
150
+ status: 'active',
151
+ },
152
+ ],
153
+ },
154
+ {
155
+ id: 'doubao',
156
+ name: '豆包 (字节跳动)',
157
+ protocol: 'openai-compatible',
158
+ baseUrl: 'https://ark.cn-beijing.volces.com/api/v3',
159
+ apiKey: '${DOUBAO_API_KEY}',
160
+ models: [
161
+ {
162
+ id: 'doubao-seed-1.6-flash',
163
+ name: '豆包 Seed 1.6 Flash',
164
+ providerId: 'doubao',
165
+ contextWindow: 256_000,
166
+ maxOutput: 128_000,
167
+ vision: true,
168
+ status: 'active',
169
+ },
170
+ {
171
+ id: 'doubao-seed-2.0-mini',
172
+ name: '豆包 Seed 2.0 Mini',
173
+ providerId: 'doubao',
174
+ contextWindow: 256_000,
175
+ maxOutput: 128_000,
176
+ vision: true,
177
+ status: 'active',
178
+ },
179
+ {
180
+ id: 'doubao-seed-2.0-lite',
181
+ name: '豆包 Seed 2.0 Lite',
182
+ providerId: 'doubao',
183
+ contextWindow: 256_000,
184
+ maxOutput: 128_000,
185
+ vision: true,
186
+ status: 'active',
187
+ },
188
+ {
189
+ id: 'doubao-seed-1.6',
190
+ name: '豆包 Seed 1.6',
191
+ providerId: 'doubao',
192
+ contextWindow: 256_000,
193
+ maxOutput: 128_000,
194
+ vision: true,
195
+ status: 'active',
196
+ },
197
+ {
198
+ id: 'doubao-seed-2.0-pro',
199
+ name: '豆包 Seed 2.0 Pro',
200
+ providerId: 'doubao',
201
+ contextWindow: 256_000,
202
+ maxOutput: 128_000,
203
+ vision: true,
204
+ status: 'active',
205
+ },
206
+ {
207
+ id: 'doubao-seed-2.0-code',
208
+ name: '豆包 Seed 2.0 Code',
209
+ providerId: 'doubao',
210
+ contextWindow: 256_000,
211
+ maxOutput: 128_000,
212
+ vision: true,
213
+ status: 'active',
214
+ },
215
+ ],
216
+ },
217
+ {
218
+ id: 'hunyuan',
219
+ name: '腾讯混元',
220
+ protocol: 'openai-compatible',
221
+ baseUrl: 'https://api.hunyuan.cloud.tencent.com/v1',
222
+ apiKey: '${HUNYUAN_API_KEY}',
223
+ models: [
224
+ {
225
+ id: 'hunyuan-lite',
226
+ name: '混元 Lite (免费)',
227
+ providerId: 'hunyuan',
228
+ contextWindow: 256_000,
229
+ maxOutput: 6_000,
230
+ vision: false,
231
+ status: 'active',
232
+ },
233
+ {
234
+ id: 'hunyuan-turbos-latest',
235
+ name: '混元 TurboS',
236
+ providerId: 'hunyuan',
237
+ contextWindow: 32_000,
238
+ maxOutput: 16_000,
239
+ vision: false,
240
+ status: 'active',
241
+ },
242
+ {
243
+ id: 'hunyuan-2.0-instruct-20251111',
244
+ name: '混元 2.0 Instruct',
245
+ providerId: 'hunyuan',
246
+ contextWindow: 144_000,
247
+ maxOutput: 16_000,
248
+ vision: false,
249
+ status: 'active',
250
+ },
251
+ {
252
+ id: 'hunyuan-t1-vision-20250916',
253
+ name: '混元 T1 Vision',
254
+ providerId: 'hunyuan',
255
+ contextWindow: 32_000,
256
+ maxOutput: 64_000,
257
+ vision: true,
258
+ status: 'active',
259
+ },
260
+ {
261
+ id: 'hunyuan-a13b',
262
+ name: '混元 A13B',
263
+ providerId: 'hunyuan',
264
+ contextWindow: 224_000,
265
+ maxOutput: 32_000,
266
+ vision: false,
267
+ status: 'active',
268
+ },
269
+ {
270
+ id: 'hunyuan-2.0-thinking-20251109',
271
+ name: '混元 2.0 Think',
272
+ providerId: 'hunyuan',
273
+ contextWindow: 192_000,
274
+ maxOutput: 64_000,
275
+ vision: false,
276
+ status: 'active',
277
+ },
278
+ {
279
+ id: 'hunyuan-t1-latest',
280
+ name: '混元 T1 (推理)',
281
+ providerId: 'hunyuan',
282
+ contextWindow: 32_000,
283
+ maxOutput: 64_000,
284
+ vision: false,
285
+ status: 'active',
286
+ },
287
+ {
288
+ id: 'hy3-preview',
289
+ name: '混元 Hy3 Preview',
290
+ providerId: 'hunyuan',
291
+ contextWindow: 256_000,
292
+ maxOutput: 128_000,
293
+ vision: false,
294
+ status: 'active',
295
+ },
296
+ ],
297
+ },
298
+ {
299
+ id: 'kimi',
300
+ name: 'Kimi (月之暗面)',
301
+ protocol: 'openai-compatible',
302
+ baseUrl: 'https://api.moonshot.cn/v1',
303
+ apiKey: '${KIMI_API_KEY}',
304
+ models: [
305
+ {
306
+ id: 'kimi-latest',
307
+ name: 'Kimi Latest',
308
+ providerId: 'kimi',
309
+ contextWindow: 128_000,
310
+ maxOutput: 16_000,
311
+ vision: true,
312
+ status: 'active',
313
+ },
314
+ {
315
+ id: 'kimi-k3',
316
+ name: 'Kimi K3',
317
+ providerId: 'kimi',
318
+ contextWindow: 1_000_000,
319
+ maxOutput: 128_000,
320
+ vision: true,
321
+ status: 'active',
322
+ },
323
+ {
324
+ id: 'moonshot-v1-8k',
325
+ name: 'Moonshot v1 8K',
326
+ providerId: 'kimi',
327
+ contextWindow: 8_000,
328
+ maxOutput: 4_000,
329
+ vision: false,
330
+ status: 'active',
331
+ },
332
+ {
333
+ id: 'moonshot-v1-32k',
334
+ name: 'Moonshot v1 32K',
335
+ providerId: 'kimi',
336
+ contextWindow: 32_000,
337
+ maxOutput: 4_000,
338
+ vision: false,
339
+ status: 'active',
340
+ },
341
+ {
342
+ id: 'moonshot-v1-128k',
343
+ name: 'Moonshot v1 128K',
344
+ providerId: 'kimi',
345
+ contextWindow: 128_000,
346
+ maxOutput: 4_000,
347
+ vision: false,
348
+ status: 'active',
349
+ },
350
+ ],
351
+ },
352
+ MIPHAM_PROVIDER,
353
+ {
354
+ id: 'ollama',
355
+ name: 'Ollama (本地)',
356
+ protocol: 'openai-compatible',
357
+ baseUrl: 'http://localhost:11434/v1',
358
+ apiKey: 'ollama-local',
359
+ models: [], // 动态发现,初值为空
360
+ },
361
+ {
362
+ id: 'openai',
363
+ name: 'OpenAI',
364
+ protocol: 'openai-compatible',
365
+ baseUrl: 'https://api.openai.com/v1',
366
+ apiKey: '${OPENAI_API_KEY}',
367
+ models: [
368
+ {
369
+ id: 'gpt-5.4-mini',
370
+ name: 'GPT-5.4 Mini',
371
+ providerId: 'openai',
372
+ contextWindow: 400_000,
373
+ maxOutput: 32_000,
374
+ vision: true,
375
+ status: 'active',
376
+ },
377
+ {
378
+ id: 'gpt-5.4',
379
+ name: 'GPT-5.4',
380
+ providerId: 'openai',
381
+ contextWindow: 1_050_000,
382
+ maxOutput: 128_000,
383
+ vision: true,
384
+ status: 'active',
385
+ },
386
+ {
387
+ id: 'gpt-5.5',
388
+ name: 'GPT-5.5',
389
+ providerId: 'openai',
390
+ contextWindow: 1_050_000,
391
+ maxOutput: 128_000,
392
+ vision: true,
393
+ status: 'active',
394
+ },
395
+ {
396
+ id: 'gpt-5.3-codex',
397
+ name: 'GPT-5.3 Codex',
398
+ providerId: 'openai',
399
+ contextWindow: 400_000,
400
+ maxOutput: 64_000,
401
+ vision: false,
402
+ status: 'active',
403
+ },
404
+ ],
405
+ },
406
+ {
407
+ id: 'qwen',
408
+ name: '通义千问',
409
+ protocol: 'openai-compatible',
410
+ baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
411
+ apiKey: '${QWEN_API_KEY}',
412
+ models: [
413
+ {
414
+ id: 'qwen-plus',
415
+ name: 'Qwen Plus',
416
+ providerId: 'qwen',
417
+ contextWindow: 128_000,
418
+ maxOutput: 32_000,
419
+ vision: true,
420
+ status: 'active',
421
+ },
422
+ {
423
+ id: 'qwen-max',
424
+ name: 'Qwen Max',
425
+ providerId: 'qwen',
426
+ contextWindow: 128_000,
427
+ maxOutput: 32_000,
428
+ vision: true,
429
+ status: 'active',
430
+ },
431
+ ],
432
+ },
433
+ ]
434
+
435
+ export const PROTOCOL_LABELS: Record<string, string> = {
436
+ 'openai-compatible': 'OpenAI Compatible',
437
+ anthropic: 'Anthropic',
438
+ custom: 'Custom Protocol',
439
+ }
440
+
441
+ export const TOOL_CATEGORIES = [
442
+ 'file',
443
+ 'exec',
444
+ 'agent',
445
+ 'network',
446
+ 'system',
447
+ 'artifact',
448
+ 'scheduling',
449
+ ] as const
450
+ export const CONFIG_FILE_NAME = 'config.yml'
451
+ export const MIPHAM_DIR = '.mipham'
452
+ export const USER_CONFIG_DIR = '.mipham'
453
+ export const MEMORY_DIR = 'memory'
454
+
455
+ /** 预置 Ollama 模型 — 与 ollama list 结果合并去重后展示 */
456
+ export const OLLAMA_PRESET_MODELS = [
457
+ { id: 'om-v5-flash', source: 'MiphamAI' },
458
+ { id: 'om-v5-pro', source: 'MiphamAI' },
459
+ { id: 'om-v5-visual', source: 'MiphamAI' },
460
+ { id: 'om-v5-apex', source: 'MiphamAI' },
461
+ { id: 'deepseek-r1:70b', source: '热门' },
462
+ { id: 'deepseek-v3', source: '热门' },
463
+ { id: 'qwen2.5:72b', source: '热门' },
464
+ { id: 'qwen3', source: '热门' },
465
+ ] as const
466
+
467
+ // ── Artifact Constants ──
468
+ export const ARTIFACTS_DIR = 'artifacts'
469
+ export const ARTIFACT_PORT = 9876
470
+ export const ARTIFACT_MAX_PORT_TRIES = 10
471
+ export const ARTIFACT_MAX_SIZE = 5 * 1024 * 1024 // 5MB
472
+ export const ARTIFACT_ALLOWED_EXTENSIONS = ['.html', '.svg']
@@ -0,0 +1,50 @@
1
+ {
2
+ "_comment": "Mipham model definitions — synced from Engine GET /v1/mipham-models. Run 'bun run scripts/sync-mipham-models.ts' to update.",
3
+ "provider": {
4
+ "id": "mipham",
5
+ "name": "MiphamAI",
6
+ "protocol": "openai-compatible",
7
+ "baseUrl": "https://api.onemipham.com/v1",
8
+ "apiKey": "${MIPHAM_API_KEY}",
9
+ "status": "active"
10
+ },
11
+ "models": [
12
+ {
13
+ "id": "om-v5-flash",
14
+ "name": "OM V5 Flash",
15
+ "providerId": "mipham",
16
+ "contextWindow": 16384,
17
+ "maxOutput": 4096,
18
+ "vision": false,
19
+ "status": "active"
20
+ },
21
+ {
22
+ "id": "om-v5-pro",
23
+ "name": "OM V5 Pro",
24
+ "providerId": "mipham",
25
+ "contextWindow": 131072,
26
+ "maxOutput": 16384,
27
+ "vision": false,
28
+ "status": "active"
29
+ },
30
+ {
31
+ "id": "om-v5-visual",
32
+ "name": "OM V5 Visual",
33
+ "providerId": "mipham",
34
+ "contextWindow": 65536,
35
+ "maxOutput": 4096,
36
+ "vision": true,
37
+ "status": "active"
38
+ },
39
+ {
40
+ "id": "om-v5-apex",
41
+ "name": "OM V5 Apex",
42
+ "providerId": "mipham",
43
+ "contextWindow": 200000,
44
+ "maxOutput": 32768,
45
+ "vision": false,
46
+ "status": "active"
47
+ }
48
+ ],
49
+ "synced_at": "2026-08-04T00:00:00Z"
50
+ }
@@ -1,8 +1,443 @@
1
- // ── CLI 内部类型(引用活服务,不可共享)──
2
- // 纯数据类型(Message/StreamChunk/ToolResult/配置/常量等)见 @mipham/shared —— 唯一真源。
3
- export * from '@mipham/shared/types'
4
- import type { HookDefinition, ToolCategory, ToolPermission, ToolResult } from '@mipham/shared'
1
+ // ── 类型唯一真源(vendored from packages/shared/src/types.ts)──
2
+ // 为让 npm 包自包含(运行时无需 @mipham/shared workspace 依赖),
3
+ // 此处从 packages/shared/src/types.ts 同步。改动共享类型时两边同步。
4
+ // ── Provider Types ──
5
+ export type ProtocolType = 'openai-compatible' | 'anthropic' | 'custom'
6
+
7
+ export interface ModelInfo {
8
+ id: string
9
+ name: string
10
+ providerId: string
11
+ contextWindow: number
12
+ maxOutput: number
13
+ vision: boolean
14
+ status: 'active' | 'upcoming' | 'deprecated'
15
+ }
16
+
17
+ export interface ProviderConfig {
18
+ id: string
19
+ name: string
20
+ protocol: ProtocolType
21
+ baseUrl?: string
22
+ apiKey: string
23
+ models: ModelInfo[]
24
+ status?: 'active' | 'upcoming'
25
+ }
26
+
27
+ // ── Message Types ──
28
+ export interface TextContent {
29
+ type: 'text'
30
+ text: string
31
+ }
32
+ export interface ImageContent {
33
+ type: 'image_url'
34
+ image_url: { url: string }
35
+ }
36
+ export interface ToolUseContent {
37
+ type: 'tool_use'
38
+ id: string
39
+ name: string
40
+ input: Record<string, unknown>
41
+ }
42
+ export interface ToolResultContent {
43
+ type: 'tool_result'
44
+ tool_use_id: string
45
+ content: string
46
+ }
47
+ export interface ThinkingContent {
48
+ type: 'thinking'
49
+ thinking: string
50
+ }
51
+ export type ContentBlock =
52
+ TextContent | ImageContent | ToolUseContent | ToolResultContent | ThinkingContent
53
+
54
+ export interface Message {
55
+ role: 'system' | 'user' | 'assistant'
56
+ content: string | ContentBlock[]
57
+ /** DeepSeek reasoning tokens — must be passed back to the API in multi-turn conversations. */
58
+ reasoning_content?: string
59
+ }
60
+
61
+ // ── Tool Types ──
62
+ export type ToolPermission = 'auto' | 'ask' | 'bypass'
63
+ export type ToolCategory =
64
+ 'file' | 'exec' | 'agent' | 'network' | 'system' | 'artifact' | 'scheduling'
65
+
66
+ // ── Artifact Types ──
67
+ export interface ArtifactEntry {
68
+ name: string
69
+ path: string
70
+ url: string
71
+ size: number
72
+ type: 'html' | 'svg'
73
+ createdAt: string
74
+ sessionId: string
75
+ versions?: string[] // version tags e.g. ['v1', 'v2']
76
+ versionCount?: number
77
+ }
78
+
79
+ export interface ArtifactManifest {
80
+ version: 1
81
+ artifacts: ArtifactEntry[]
82
+ port?: number
83
+ }
84
+
85
+ export interface ToolResult {
86
+ success: boolean
87
+ content: string
88
+ error?: string
89
+ }
90
+
91
+ // ── Task Notification Types ──
92
+ export interface TaskNotification {
93
+ taskId: string
94
+ status: 'started' | 'completed' | 'failed'
95
+ description: string
96
+ content?: string
97
+ error?: string
98
+ }
99
+
100
+ // ── Stream Types ──
101
+ export interface StreamChunk {
102
+ type:
103
+ | 'text'
104
+ | 'tool_use'
105
+ | 'tool_result'
106
+ | 'thinking'
107
+ | 'stop'
108
+ | 'error'
109
+ | 'warning'
110
+ | 'task_notification'
111
+ | 'usage'
112
+ content?: string
113
+ toolUse?: ToolUseContent
114
+ tool_use_id?: string
115
+ error?: string
116
+ /** DeepSeek reasoning tokens accumulated during this stream. */
117
+ reasoning_content?: string
118
+ /** Anthropic thinking block content (DeepSeek Anthropic endpoint). */
119
+ thinking?: string
120
+ /** Background task notification payload (type: 'task_notification'). */
121
+ taskNotification?: TaskNotification
122
+ /** API-reported input token count (type: 'usage'). */
123
+ inputTokens?: number
124
+ /** API-reported output token count (type: 'usage'). */
125
+ outputTokens?: number
126
+ }
127
+
128
+ // ── Config Types ──
129
+ export interface MiphamConfig {
130
+ version: string
131
+ defaultProvider: string
132
+ defaultModel: string
133
+ permission: ToolPermission
134
+ /** Org-level permission restrictions (forbiddenModes, maxAllowedMode). */
135
+ permissionRestrictions?: PermissionRestrictions
136
+ providers: ProviderConfig[]
137
+ skills?: { paths: string[]; mcpServers: McpServerConfig[] }
138
+ marketplace?: {
139
+ /** If set, only allow installs from matching repos (e.g. ["One-Mipham/*"]) */
140
+ strictKnownMarketplaces?: string[]
141
+ /** Block installs from matching repos (e.g. ["malicious-org/*"]) */
142
+ blockedMarketplaces?: string[]
143
+ }
144
+ /** Phase 9 feature flags. All default to true. */
145
+ features?: Partial<FeatureFlags>
146
+ /** Phase 10 CRSI feature flags. All default to true. */
147
+ crsi?: Partial<CrsiConfig>
148
+ }
149
+
150
+ export interface FeatureFlags {
151
+ mcp: { oauthEnabled: boolean }
152
+ context: { useRealTokenizer: boolean; adaptiveThresholds: boolean }
153
+ }
154
+
155
+ export interface CrsiConfig {
156
+ /** Extract and inject experience rules into agent system prompts. */
157
+ ruleInjection: boolean
158
+ /** Intercept tool calls and auto-fix known failure patterns before execution. */
159
+ preToolHook: boolean
160
+ /** Analyze agent outcomes for recurring failure patterns. */
161
+ autoPatternAnalysis: boolean
162
+ /** Auto-degrade/disable low-effectiveness rules based on success-rate tracking. */
163
+ autoRuleManagement: boolean
164
+ }
165
+
166
+ export interface McpServerConfig {
167
+ name: string
168
+ /** stdio: executable to spawn (mutually exclusive with `url`). */
169
+ command?: string
170
+ /** stdio: args passed to `command`. */
171
+ args?: string[]
172
+ /** HTTP: Streamable HTTP endpoint (mutually exclusive with `command`). */
173
+ url?: string
174
+ /** HTTP: extra request headers (e.g. Authorization). */
175
+ headers?: Record<string, string>
176
+ env?: Record<string, string>
177
+ auth?: {
178
+ type: 'oauth'
179
+ authorizationUrl: string
180
+ tokenUrl: string
181
+ clientId: string
182
+ scopes?: string[]
183
+ redirectPort?: number
184
+ }
185
+ }
186
+
187
+ // ── Hook Types ──
188
+ export type HookEvent =
189
+ | 'PreToolUse'
190
+ | 'PostToolUse'
191
+ | 'PostToolUseFailure'
192
+ | 'SessionStart'
193
+ | 'SessionEnd'
194
+ | 'Notification'
195
+ | 'Stop'
196
+ | 'UserPromptSubmit'
197
+ | 'PreCompact'
198
+ | 'PostCompact'
199
+ | 'ConfigChange'
200
+ | 'SubagentStart'
201
+ | 'SubagentStop'
202
+ | 'PreInference'
203
+
204
+ export type HookType = 'command' | 'http' | 'code' | 'mcp_tool'
205
+
206
+ export interface HookConfig {
207
+ type: HookType
208
+ command?: string
209
+ args?: string[]
210
+ url?: string
211
+ method?: 'GET' | 'POST'
212
+ headers?: Record<string, string>
213
+ mcpServer?: string
214
+ mcpTool?: string
215
+ continueOnBlock?: boolean
216
+ }
217
+
218
+ export interface HookDefinition {
219
+ event: HookEvent
220
+ toolName?: string
221
+ handler: (context: HookContext) => Promise<HookResult>
222
+ }
223
+
224
+ export interface HookContext {
225
+ event: HookEvent
226
+ toolName?: string
227
+ toolInput?: Record<string, unknown>
228
+ toolResult?: ToolResult
229
+ sessionId: string
230
+ userPrompt?: string
231
+ configKey?: string
232
+ configValue?: unknown
233
+ /** PreInference: full conversation messages for DLP inspection. */
234
+ messages?: Array<{ role: string; content: string }>
235
+ /** PreInference: recent tool calls and their results. */
236
+ toolCalls?: Array<{
237
+ name: string
238
+ input: Record<string, unknown>
239
+ resultPreview: string
240
+ }>
241
+ /** PreInference: current provider ID. */
242
+ provider?: string
243
+ /** PreInference: current model ID. */
244
+ model?: string
245
+ }
246
+
247
+ export interface HookResult {
248
+ allowed: boolean
249
+ reason?: string
250
+ modifiedInput?: Record<string, unknown>
251
+ decision?: 'allow' | 'block'
252
+ permissionDecision?: 'allow' | 'deny' | 'ask' | 'defer'
253
+ additionalContext?: string
254
+ updatedOutput?: string
255
+ }
256
+
257
+ // ── Instruction Types ──
258
+ export interface InstructionFile {
259
+ path: string
260
+ level: 'group' | 'company' | 'project' | 'directory' | 'user'
261
+ privacy: 'public' | 'project' | 'private'
262
+ language: string
263
+ content: string
264
+ frontmatter: Record<string, unknown>
265
+ }
266
+
267
+ // ── Permission Types ──
268
+ /** Six explicit permission modes matching Claude Code's permission architecture */
269
+ export type PermissionMode =
270
+ 'default' | 'acceptEdits' | 'plan' | 'auto' | 'dontAsk' | 'bypassPermissions'
5
271
 
272
+ /** Backward-compatible alias: PermissionMode plus legacy 'ask' and 'bypass' */
273
+ export type PermissionLevel = PermissionMode | 'ask' | 'bypass'
274
+
275
+ /** Org-level restrictions that cap or forbid specific permission modes. */
276
+ export interface PermissionRestrictions {
277
+ /** Modes that may not be entered (cycle skips them). */
278
+ forbiddenModes?: PermissionMode[]
279
+ /** Ceiling — modes ranked higher (more permissive) than this are treated as forbidden. */
280
+ maxAllowedMode?: PermissionMode
281
+ }
282
+
283
+ export interface PermissionConfig {
284
+ mode: PermissionMode
285
+ allow: string[]
286
+ deny: string[]
287
+ /** Optional org-level restrictions enforced on every mode transition. */
288
+ restrictions?: PermissionRestrictions
289
+ }
290
+
291
+ export interface PermissionRuleEntry {
292
+ pattern: string // e.g., "Bash(git:*)"
293
+ level: 'allow' | 'deny' | 'ask'
294
+ compiled: RegExp
295
+ }
296
+
297
+ export interface PermissionRule {
298
+ toolName: string
299
+ level: PermissionLevel
300
+ pattern?: string
301
+ }
302
+
303
+ // ── Inference Hook (DLP) Types ──
304
+
305
+ /** Configuration for the PreInference DLP hook, loaded from config.yml. */
306
+ export interface InferenceHookConfig {
307
+ /** DLP server endpoint (HTTPS). Empty = feature disabled. */
308
+ endpoint: string
309
+ /** HMAC signing secret (format: mis_<random>). */
310
+ signing_secret: string
311
+ /** Request timeout in milliseconds. Default 5000. */
312
+ timeout: number
313
+ /** Failure posture: 'fail-closed' blocks on error, 'fail-open' allows. */
314
+ on_failure: 'fail-closed' | 'fail-open'
315
+ /** Organization identifier (optional, sent in payload). */
316
+ organization_id: string
317
+ /** Additional custom headers to send with each request. */
318
+ headers: Record<string, string>
319
+ }
320
+
321
+ /** Outgoing request to the DLP server. */
322
+ export interface InferenceCheckRequest {
323
+ type: 'inference_check'
324
+ id: string
325
+ created_at: string
326
+ data: {
327
+ type: 'pre_inference'
328
+ session_id: string
329
+ organization_id?: string
330
+ provider: string
331
+ model: string
332
+ messages: Array<{ role: string; content: string }>
333
+ tool_calls: Array<{
334
+ name: string
335
+ input: Record<string, unknown>
336
+ result_preview: string
337
+ }>
338
+ }
339
+ }
340
+
341
+ /** Response from the DLP server. */
342
+ export interface InferenceCheckResponse {
343
+ verdict: 'allow' | 'deny'
344
+ reason?: string
345
+ }
346
+
347
+ // ── Credential Masking Types ──
348
+
349
+ /** Full-file masking rule: entire file content replaced with sentinel. */
350
+ export interface CredentialFullMaskRule {
351
+ path: string
352
+ mode: 'full'
353
+ }
354
+
355
+ /** Per-extract-pattern configuration with optional field-based extraction. */
356
+ export interface CredentialExtractPattern {
357
+ /** Regex pattern to match. When `field` is set, applied to field value only. */
358
+ pattern: string
359
+ /** Optional replacement string. Defaults to CREDENTIAL_SENTINEL. */
360
+ replacement?: string
361
+ /** 🆕 JSON key to extract before applying pattern. If unset, pattern runs against full content. */
362
+ field?: string
363
+ }
364
+
365
+ /** Extract-based masking rule: only regex-matched tokens are replaced. */
366
+ export interface CredentialExtractRule {
367
+ path: string
368
+ mode: 'extract'
369
+ extract: CredentialExtractPattern[]
370
+ /** 🆕 Behavior when no extract pattern matches: 'mask' (replace all) or 'passthrough' (keep). Default: 'mask'. */
371
+ onExtractNoMatch?: 'mask' | 'passthrough'
372
+ }
373
+
374
+ /** 🆕 JWT-aware masking rule: decode payload and mask specified claims. */
375
+ export interface JwtMaskingRule {
376
+ path: string
377
+ type: 'jwt'
378
+ decode: 'jwt'
379
+ /** Claim names to mask in the JWT payload (e.g. ["sub", "email"]). */
380
+ maskClaims: string[]
381
+ }
382
+
383
+ /** 🆕 AWS credential pair masking rule: detect key pairs and optionally re-sign. */
384
+ export interface AwsMaskingRule {
385
+ path: string
386
+ type: 'aws'
387
+ awsPairs: boolean
388
+ sigv4: boolean
389
+ }
390
+
391
+ export type CredentialFileRule =
392
+ CredentialFullMaskRule | CredentialExtractRule | JwtMaskingRule | AwsMaskingRule
393
+
394
+ /** Configuration for credential masking, loaded from config.yml. */
395
+ export interface CredentialMaskingConfig {
396
+ enabled: boolean
397
+ files: CredentialFileRule[]
398
+ output_scrubbing: {
399
+ enabled: boolean
400
+ patterns: string[]
401
+ }
402
+ env_filter: {
403
+ enabled: boolean
404
+ patterns: string[]
405
+ }
406
+ }
407
+
408
+ // ── Cross-Session Messaging Types ──
409
+
410
+ /** Controls how inbound cross-session messages are handled. */
411
+ export type CrossSessionInbound = 'allow' | 'ask' | 'deny'
412
+
413
+ /** 🆕 Configuration for cross-session messaging. */
414
+ export interface CrossSessionConfig {
415
+ crossSessionInbound: CrossSessionInbound
416
+ dialogExpiry: number // seconds
417
+ }
418
+
419
+ /** Session information exposed via ListAgents. */
420
+ export interface SessionInfo {
421
+ id: string
422
+ name: string
423
+ machine: string
424
+ pid: number
425
+ startedAt: string
426
+ cwd?: string
427
+ provider?: string
428
+ model?: string
429
+ }
430
+
431
+ // ── Background Agent Types ──
432
+
433
+ export interface BackgroundAgentConfig {
434
+ auto_commit: boolean
435
+ auto_push: boolean
436
+ auto_worktree: boolean
437
+ commit_coauthors: boolean
438
+ }
439
+
440
+ // ── CLI 内部类型(引用活服务,不可共享)──
6
441
  export interface ToolContext {
7
442
  cwd: string
8
443
  sessionId: string
package/src/ui/input.tsx CHANGED
@@ -231,7 +231,14 @@ export function InputBar({
231
231
  if (submittedHistory.length === 0) return
232
232
  // Save current draft the first time we enter history browsing
233
233
  if (historyIndexRef.current === -1) {
234
- savedDraftRef.current = value
234
+ // Flush any pending throttle so its deferred setValue doesn't overwrite
235
+ // history navigation (paste → immediate ↑ race). Save the latest value
236
+ // from the ref — state `value` is stale inside the throttle window.
237
+ if (onChangeTimerRef.current) {
238
+ clearTimeout(onChangeTimerRef.current)
239
+ onChangeTimerRef.current = null
240
+ }
241
+ savedDraftRef.current = valueRef.current
235
242
  }
236
243
  const newIndex = Math.min(historyIndexRef.current + 1, submittedHistory.length - 1)
237
244
  historyIndexRef.current = newIndex