@miphamai/cli 0.38.0 → 0.39.0

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.
Files changed (60) hide show
  1. package/bin/mipham.ts +25 -0
  2. package/package.json +1 -1
  3. package/src/agent/sub-agent.ts +4 -3
  4. package/src/config/defaults.ts +19 -1
  5. package/src/config/keys-manager.ts +11 -1
  6. package/src/config/loader.ts +20 -10
  7. package/src/core/context.ts +86 -56
  8. package/src/core/credential-masker/output-scrub.ts +5 -0
  9. package/src/core/crsi-sandbox.ts +19 -7
  10. package/src/core/engine.ts +50 -33
  11. package/src/core/instructions.ts +4 -42
  12. package/src/core/permission.ts +7 -1
  13. package/src/core/red-team.ts +4 -4
  14. package/src/core/self-critique.ts +3 -1
  15. package/src/core/session-log.ts +225 -0
  16. package/src/core/session-store.ts +119 -34
  17. package/src/daemon/auth.ts +9 -6
  18. package/src/daemon/cors.ts +24 -16
  19. package/src/daemon/rate-limiter.ts +4 -0
  20. package/src/daemon/server.ts +24 -3
  21. package/src/index.tsx +54 -28
  22. package/src/mcp/http-transport.ts +2 -0
  23. package/src/mcp/oauth.ts +1 -1
  24. package/src/providers/llm-replay.ts +36 -0
  25. package/src/providers/llm.ts +16 -0
  26. package/src/providers/registry.ts +2 -1
  27. package/src/security/gate.ts +10 -0
  28. package/src/security/url.ts +37 -14
  29. package/src/shared/constants.ts +4 -486
  30. package/src/shared/types.ts +6 -437
  31. package/src/skills/fork-executor.ts +3 -1
  32. package/src/skills/loader.ts +2 -1
  33. package/src/skills/seam.ts +18 -0
  34. package/src/tools/agent/agent.ts +1 -0
  35. package/src/tools/agent/skill.ts +1 -0
  36. package/src/tools/exec/bash.ts +140 -128
  37. package/src/tools/exec/git.ts +17 -0
  38. package/src/tools/file/read.ts +73 -66
  39. package/src/tools/file/write.ts +1 -1
  40. package/src/tools/index.ts +54 -132
  41. package/src/tools/network/web-fetch.ts +41 -26
  42. package/src/tools/seam.ts +25 -0
  43. package/src/tools/validation.ts +91 -0
  44. package/src/ui/commands.ts +17 -5
  45. package/src/vajra/compose/assemble.ts +19 -0
  46. package/src/vajra/compose/bundle.ts +39 -0
  47. package/src/vajra/compose/dump.ts +5 -0
  48. package/src/vajra/compose/index.ts +4 -0
  49. package/src/vajra/compose/mount.ts +30 -0
  50. package/src/vajra/context.ts +183 -0
  51. package/src/vajra/events.ts +13 -0
  52. package/src/vajra/index.ts +4 -0
  53. package/src/vajra/leaf/plan-runner.ts +94 -0
  54. package/src/vajra/service.ts +15 -0
  55. package/src/workflow/primitives/agent.ts +3 -1
  56. package/src/workflow/runtime.ts +23 -15
  57. package/src/workflow/sandbox.ts +51 -9
  58. package/src/core/compaction-progress.ts +0 -205
  59. package/src/core/context-compact.ts +0 -124
  60. package/src/core/context-drain.ts +0 -113
@@ -1,71 +1,14 @@
1
- // ── Provider Types ──
2
- export type ProtocolType = 'openai-compatible' | 'anthropic' | 'custom'
3
-
4
- export interface ModelInfo {
5
- id: string
6
- name: string
7
- providerId: string
8
- contextWindow: number
9
- maxOutput: number
10
- vision: boolean
11
- status: 'active' | 'upcoming' | 'deprecated'
12
- }
13
-
14
- export interface ProviderConfig {
15
- id: string
16
- name: string
17
- protocol: ProtocolType
18
- baseUrl?: string
19
- apiKey: string
20
- models: ModelInfo[]
21
- status?: 'active' | 'upcoming'
22
- }
23
-
24
- // ── Message Types ──
25
- export interface TextContent {
26
- type: 'text'
27
- text: string
28
- }
29
- export interface ImageContent {
30
- type: 'image_url'
31
- image_url: { url: string }
32
- }
33
- export interface ToolUseContent {
34
- type: 'tool_use'
35
- id: string
36
- name: string
37
- input: Record<string, unknown>
38
- }
39
- export interface ToolResultContent {
40
- type: 'tool_result'
41
- tool_use_id: string
42
- content: string
43
- }
44
- export interface ThinkingContent {
45
- type: 'thinking'
46
- thinking: string
47
- }
48
- export type ContentBlock =
49
- TextContent | ImageContent | ToolUseContent | ToolResultContent | ThinkingContent
50
-
51
- export interface Message {
52
- role: 'system' | 'user' | 'assistant'
53
- content: string | ContentBlock[]
54
- /** DeepSeek reasoning tokens — must be passed back to the API in multi-turn conversations. */
55
- reasoning_content?: string
56
- }
57
-
58
- // ── Tool Types ──
59
- export type ToolPermission = 'auto' | 'ask' | 'bypass'
60
- export type ToolCategory =
61
- 'file' | 'exec' | 'agent' | 'network' | 'system' | 'artifact' | 'scheduling'
1
+ // ── CLI 内部类型(引用活服务,不可共享)──
2
+ // 纯数据类型(Message/StreamChunk/ToolResult/配置/常量等)见 @mipham/shared —— 唯一真源。
3
+ export * from '@mipham/shared/types'
4
+ import type { HookDefinition, ToolCategory, ToolPermission, ToolResult } from '@mipham/shared'
62
5
 
63
6
  export interface ToolContext {
64
7
  cwd: string
65
8
  sessionId: string
66
9
  provider: string
67
10
  model: string
68
- skillsLoader?: import('../skills/loader').SkillsLoader
11
+ skillsLoader?: import('../skills/seam').Skills
69
12
  registry?: import('../providers/registry').ProviderRegistry
70
13
  toolRegistry?: Map<string, ToolDefinition>
71
14
  artifactServer?: import('../artifacts/server').ArtifactServer
@@ -73,35 +16,11 @@ export interface ToolContext {
73
16
  backgroundAgentRegistry?: import('../agent/background-registry').BackgroundAgentRegistry
74
17
  permissionSystem?: import('../core/permission').PermissionSystem
75
18
  ruleEngine?: import('../core/rule-engine').ExperienceRuleEngine
19
+ llm?: import('../providers/llm').Llm
76
20
  /** Files read this session — used by Write tool to check read-before-write */
77
21
  readFiles?: Set<string>
78
22
  }
79
23
 
80
- // ── Artifact Types ──
81
- export interface ArtifactEntry {
82
- name: string
83
- path: string
84
- url: string
85
- size: number
86
- type: 'html' | 'svg'
87
- createdAt: string
88
- sessionId: string
89
- versions?: string[] // version tags e.g. ['v1', 'v2']
90
- versionCount?: number
91
- }
92
-
93
- export interface ArtifactManifest {
94
- version: 1
95
- artifacts: ArtifactEntry[]
96
- port?: number
97
- }
98
-
99
- export interface ToolResult {
100
- success: boolean
101
- content: string
102
- error?: string
103
- }
104
-
105
24
  export interface ToolDefinition {
106
25
  name: string
107
26
  description: string
@@ -111,103 +30,6 @@ export interface ToolDefinition {
111
30
  execute: (params: Record<string, unknown>, ctx: ToolContext) => Promise<ToolResult>
112
31
  }
113
32
 
114
- // ── Task Notification Types ──
115
- export interface TaskNotification {
116
- taskId: string
117
- status: 'started' | 'completed' | 'failed'
118
- description: string
119
- content?: string
120
- error?: string
121
- }
122
-
123
- // ── Stream Types ──
124
- export interface StreamChunk {
125
- type:
126
- | 'text'
127
- | 'tool_use'
128
- | 'tool_result'
129
- | 'thinking'
130
- | 'stop'
131
- | 'error'
132
- | 'warning'
133
- | 'task_notification'
134
- | 'usage'
135
- content?: string
136
- toolUse?: ToolUseContent
137
- tool_use_id?: string
138
- error?: string
139
- /** DeepSeek reasoning tokens accumulated during this stream. */
140
- reasoning_content?: string
141
- /** Anthropic thinking block content (DeepSeek Anthropic endpoint). */
142
- thinking?: string
143
- /** Background task notification payload (type: 'task_notification'). */
144
- taskNotification?: TaskNotification
145
- /** API-reported input token count (type: 'usage'). */
146
- inputTokens?: number
147
- /** API-reported output token count (type: 'usage'). */
148
- outputTokens?: number
149
- }
150
-
151
- // ── Config Types ──
152
- export interface MiphamConfig {
153
- version: string
154
- defaultProvider: string
155
- defaultModel: string
156
- permission: ToolPermission
157
- /** Org-level permission restrictions (forbiddenModes, maxAllowedMode). */
158
- permissionRestrictions?: PermissionRestrictions
159
- providers: ProviderConfig[]
160
- skills?: { paths: string[]; mcpServers: McpServerConfig[] }
161
- marketplace?: {
162
- /** If set, only allow installs from matching repos (e.g. ["One-Mipham/*"]) */
163
- strictKnownMarketplaces?: string[]
164
- /** Block installs from matching repos (e.g. ["malicious-org/*"]) */
165
- blockedMarketplaces?: string[]
166
- }
167
- /** Phase 9 feature flags. All default to true. */
168
- features?: Partial<FeatureFlags>
169
- /** Phase 10 CRSI feature flags. All default to true. */
170
- crsi?: Partial<CrsiConfig>
171
- }
172
-
173
- export interface FeatureFlags {
174
- mcp: { oauthEnabled: boolean }
175
- context: { useRealTokenizer: boolean; adaptiveThresholds: boolean }
176
- }
177
-
178
- export interface CrsiConfig {
179
- /** Extract and inject experience rules into agent system prompts. */
180
- ruleInjection: boolean
181
- /** Intercept tool calls and auto-fix known failure patterns before execution. */
182
- preToolHook: boolean
183
- /** Analyze agent outcomes for recurring failure patterns. */
184
- autoPatternAnalysis: boolean
185
- /** Auto-degrade/disable low-effectiveness rules based on success-rate tracking. */
186
- autoRuleManagement: boolean
187
- }
188
-
189
- export interface McpServerConfig {
190
- name: string
191
- /** stdio: executable to spawn (mutually exclusive with `url`). */
192
- command?: string
193
- /** stdio: args passed to `command`. */
194
- args?: string[]
195
- /** HTTP: Streamable HTTP endpoint (mutually exclusive with `command`). */
196
- url?: string
197
- /** HTTP: extra request headers (e.g. Authorization). */
198
- headers?: Record<string, string>
199
- env?: Record<string, string>
200
- auth?: {
201
- type: 'oauth'
202
- authorizationUrl: string
203
- tokenUrl: string
204
- clientId: string
205
- scopes?: string[]
206
- redirectPort?: number
207
- }
208
- }
209
-
210
- // ── Skill Types ──
211
33
  export interface SkillDefinition {
212
34
  name: string
213
35
  description: string
@@ -229,256 +51,3 @@ export interface SkillDefinition {
229
51
  /** The markdown body content of the skill file (instructions for the AI to follow). */
230
52
  body?: string
231
53
  }
232
-
233
- // ── Hook Types ──
234
- export type HookEvent =
235
- | 'PreToolUse'
236
- | 'PostToolUse'
237
- | 'PostToolUseFailure'
238
- | 'SessionStart'
239
- | 'SessionEnd'
240
- | 'Notification'
241
- | 'Stop'
242
- | 'UserPromptSubmit'
243
- | 'PreCompact'
244
- | 'PostCompact'
245
- | 'ConfigChange'
246
- | 'SubagentStart'
247
- | 'SubagentStop'
248
- | 'PreInference'
249
-
250
- export type HookType = 'command' | 'http' | 'code' | 'mcp_tool'
251
-
252
- export interface HookConfig {
253
- type: HookType
254
- command?: string
255
- args?: string[]
256
- url?: string
257
- method?: 'GET' | 'POST'
258
- headers?: Record<string, string>
259
- mcpServer?: string
260
- mcpTool?: string
261
- continueOnBlock?: boolean
262
- }
263
-
264
- export interface HookDefinition {
265
- event: HookEvent
266
- toolName?: string
267
- handler: (context: HookContext) => Promise<HookResult>
268
- }
269
-
270
- export interface HookContext {
271
- event: HookEvent
272
- toolName?: string
273
- toolInput?: Record<string, unknown>
274
- toolResult?: ToolResult
275
- sessionId: string
276
- userPrompt?: string
277
- configKey?: string
278
- configValue?: unknown
279
- /** PreInference: full conversation messages for DLP inspection. */
280
- messages?: Array<{ role: string; content: string }>
281
- /** PreInference: recent tool calls and their results. */
282
- toolCalls?: Array<{
283
- name: string
284
- input: Record<string, unknown>
285
- resultPreview: string
286
- }>
287
- /** PreInference: current provider ID. */
288
- provider?: string
289
- /** PreInference: current model ID. */
290
- model?: string
291
- }
292
-
293
- export interface HookResult {
294
- allowed: boolean
295
- reason?: string
296
- modifiedInput?: Record<string, unknown>
297
- decision?: 'allow' | 'block'
298
- permissionDecision?: 'allow' | 'deny' | 'ask' | 'defer'
299
- additionalContext?: string
300
- updatedOutput?: string
301
- }
302
-
303
- // ── Instruction Types ──
304
- export interface InstructionFile {
305
- path: string
306
- level: 'group' | 'company' | 'project' | 'directory' | 'user'
307
- privacy: 'public' | 'project' | 'private'
308
- language: string
309
- content: string
310
- frontmatter: Record<string, unknown>
311
- }
312
-
313
- // ── Permission Types ──
314
- /** Six explicit permission modes matching Claude Code's permission architecture */
315
- export type PermissionMode =
316
- 'default' | 'acceptEdits' | 'plan' | 'auto' | 'dontAsk' | 'bypassPermissions'
317
-
318
- /** Backward-compatible alias: PermissionMode plus legacy 'ask' and 'bypass' */
319
- export type PermissionLevel = PermissionMode | 'ask' | 'bypass'
320
-
321
- /** Org-level restrictions that cap or forbid specific permission modes. */
322
- export interface PermissionRestrictions {
323
- /** Modes that may not be entered (cycle skips them). */
324
- forbiddenModes?: PermissionMode[]
325
- /** Ceiling — modes ranked higher (more permissive) than this are treated as forbidden. */
326
- maxAllowedMode?: PermissionMode
327
- }
328
-
329
- export interface PermissionConfig {
330
- mode: PermissionMode
331
- allow: string[]
332
- deny: string[]
333
- /** Optional org-level restrictions enforced on every mode transition. */
334
- restrictions?: PermissionRestrictions
335
- }
336
-
337
- export interface PermissionRuleEntry {
338
- pattern: string // e.g., "Bash(git:*)"
339
- level: 'allow' | 'deny' | 'ask'
340
- compiled: RegExp
341
- }
342
-
343
- export interface PermissionRule {
344
- toolName: string
345
- level: PermissionLevel
346
- pattern?: string
347
- }
348
-
349
- // ── Inference Hook (DLP) Types ──
350
-
351
- /** Configuration for the PreInference DLP hook, loaded from config.yml. */
352
- export interface InferenceHookConfig {
353
- /** DLP server endpoint (HTTPS). Empty = feature disabled. */
354
- endpoint: string
355
- /** HMAC signing secret (format: mis_<random>). */
356
- signing_secret: string
357
- /** Request timeout in milliseconds. Default 5000. */
358
- timeout: number
359
- /** Failure posture: 'fail-closed' blocks on error, 'fail-open' allows. */
360
- on_failure: 'fail-closed' | 'fail-open'
361
- /** Organization identifier (optional, sent in payload). */
362
- organization_id: string
363
- /** Additional custom headers to send with each request. */
364
- headers: Record<string, string>
365
- }
366
-
367
- /** Outgoing request to the DLP server. */
368
- export interface InferenceCheckRequest {
369
- type: 'inference_check'
370
- id: string
371
- created_at: string
372
- data: {
373
- type: 'pre_inference'
374
- session_id: string
375
- organization_id?: string
376
- provider: string
377
- model: string
378
- messages: Array<{ role: string; content: string }>
379
- tool_calls: Array<{
380
- name: string
381
- input: Record<string, unknown>
382
- result_preview: string
383
- }>
384
- }
385
- }
386
-
387
- /** Response from the DLP server. */
388
- export interface InferenceCheckResponse {
389
- verdict: 'allow' | 'deny'
390
- reason?: string
391
- }
392
-
393
- // ── Credential Masking Types ──
394
-
395
- /** Full-file masking rule: entire file content replaced with sentinel. */
396
- export interface CredentialFullMaskRule {
397
- path: string
398
- mode: 'full'
399
- }
400
-
401
- /** Per-extract-pattern configuration with optional field-based extraction. */
402
- export interface CredentialExtractPattern {
403
- /** Regex pattern to match. When `field` is set, applied to field value only. */
404
- pattern: string
405
- /** Optional replacement string. Defaults to CREDENTIAL_SENTINEL. */
406
- replacement?: string
407
- /** 🆕 JSON key to extract before applying pattern. If unset, pattern runs against full content. */
408
- field?: string
409
- }
410
-
411
- /** Extract-based masking rule: only regex-matched tokens are replaced. */
412
- export interface CredentialExtractRule {
413
- path: string
414
- mode: 'extract'
415
- extract: CredentialExtractPattern[]
416
- /** 🆕 Behavior when no extract pattern matches: 'mask' (replace all) or 'passthrough' (keep). Default: 'mask'. */
417
- onExtractNoMatch?: 'mask' | 'passthrough'
418
- }
419
-
420
- /** 🆕 JWT-aware masking rule: decode payload and mask specified claims. */
421
- export interface JwtMaskingRule {
422
- path: string
423
- type: 'jwt'
424
- decode: 'jwt'
425
- /** Claim names to mask in the JWT payload (e.g. ["sub", "email"]). */
426
- maskClaims: string[]
427
- }
428
-
429
- /** 🆕 AWS credential pair masking rule: detect key pairs and optionally re-sign. */
430
- export interface AwsMaskingRule {
431
- path: string
432
- type: 'aws'
433
- awsPairs: boolean
434
- sigv4: boolean
435
- }
436
-
437
- export type CredentialFileRule =
438
- CredentialFullMaskRule | CredentialExtractRule | JwtMaskingRule | AwsMaskingRule
439
-
440
- /** Configuration for credential masking, loaded from config.yml. */
441
- export interface CredentialMaskingConfig {
442
- enabled: boolean
443
- files: CredentialFileRule[]
444
- output_scrubbing: {
445
- enabled: boolean
446
- patterns: string[]
447
- }
448
- env_filter: {
449
- enabled: boolean
450
- patterns: string[]
451
- }
452
- }
453
-
454
- // ── Cross-Session Messaging Types ──
455
-
456
- /** Controls how inbound cross-session messages are handled. */
457
- export type CrossSessionInbound = 'allow' | 'ask' | 'deny'
458
-
459
- /** 🆕 Configuration for cross-session messaging. */
460
- export interface CrossSessionConfig {
461
- crossSessionInbound: CrossSessionInbound
462
- dialogExpiry: number // seconds
463
- }
464
-
465
- /** Session information exposed via ListAgents. */
466
- export interface SessionInfo {
467
- id: string
468
- name: string
469
- machine: string
470
- pid: number
471
- startedAt: string
472
- cwd?: string
473
- provider?: string
474
- model?: string
475
- }
476
-
477
- // ── Background Agent Types ──
478
-
479
- export interface BackgroundAgentConfig {
480
- auto_commit: boolean
481
- auto_push: boolean
482
- auto_worktree: boolean
483
- commit_coauthors: boolean
484
- }
@@ -1,5 +1,6 @@
1
1
  import { SubAgent } from '../agent/sub-agent'
2
2
  import type { ProviderRegistry } from '../providers/registry'
3
+ import type { Llm } from '../providers/llm'
3
4
  import type { ToolDefinition, SkillDefinition } from '../shared/index.ts'
4
5
  import type { AgentDefinition } from '../agent/types'
5
6
  import type { PermissionSystem } from '../core/permission'
@@ -20,6 +21,7 @@ export async function executeForkedSkill(
20
21
  registry: ProviderRegistry,
21
22
  toolRegistry: Map<string, ToolDefinition>,
22
23
  permissionSystem?: PermissionSystem,
24
+ llm?: Llm,
23
25
  ): Promise<string> {
24
26
  const agentDef: AgentDefinition = {
25
27
  name: `skill:${skill.name}`,
@@ -32,7 +34,7 @@ export async function executeForkedSkill(
32
34
  source: 'builtin',
33
35
  }
34
36
 
35
- const sub = new SubAgent(registry, toolRegistry, permissionSystem)
37
+ const sub = new SubAgent(registry, toolRegistry, permissionSystem, undefined, undefined, llm)
36
38
  const prompt = args
37
39
  ? `Execute the "${skill.name}" skill with arguments: ${args}`
38
40
  : `Execute the "${skill.name}" skill.`
@@ -3,6 +3,7 @@ import { join } from 'node:path'
3
3
  import { homedir } from 'node:os'
4
4
  import { parse as parseYaml } from 'yaml'
5
5
  import type { SkillDefinition } from '../shared/index.ts'
6
+ import type { Skills } from './seam'
6
7
  import { sanitizeSkillDescription, sanitizeSkillBody, checkSkillShadow } from './sanitizer.js'
7
8
 
8
9
  interface FrontmatterResult {
@@ -21,7 +22,7 @@ function parseFrontmatter(raw: string): FrontmatterResult {
21
22
  }
22
23
  }
23
24
 
24
- export class SkillsLoader {
25
+ export class SkillsLoader implements Skills {
25
26
  private skills = new Map<string, SkillDefinition>()
26
27
 
27
28
  loadBuiltin(basePath: string): void {
@@ -0,0 +1,18 @@
1
+ import type { Context, Disposer } from '../vajra'
2
+ import type { SkillDefinition } from '../shared'
3
+
4
+ /** 技能加载缝的 capability —— 读能力。换 provider = 换技能来源,engine 零 fork 跟随。 */
5
+ export interface Skills {
6
+ get(name: string): SkillDefinition | undefined
7
+ list(): SkillDefinition[]
8
+ has(name: string): boolean
9
+ buildSystemReminder(maxTokens?: number): string
10
+ }
11
+
12
+ /** 缝键:ctx.skills。 */
13
+ export const SKILLS_KEY = 'skills'
14
+
15
+ /** 把一个 Skills(如 SkillsLoader 或测试替身)挂载为 ctx.skills。 */
16
+ export function mountSkills(ctx: Context, skills: Skills): Disposer {
17
+ return ctx.provide(SKILLS_KEY, skills)
18
+ }
@@ -82,6 +82,7 @@ export const agentTool: ToolDefinition = {
82
82
  ctx.permissionSystem,
83
83
  undefined,
84
84
  ctx.ruleEngine,
85
+ ctx.llm,
85
86
  )
86
87
  const result = await sub.execute(prompt, description, {
87
88
  type: agentType,
@@ -54,6 +54,7 @@ export const skillTool: ToolDefinition = {
54
54
  registry,
55
55
  ctx.toolRegistry || new Map(),
56
56
  ctx.permissionSystem,
57
+ ctx.llm,
57
58
  )
58
59
  // Return to AI as internal context
59
60
  return { success: true, content: `[Forked skill "${skillName}" result]:\n${result}` }