@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.
- package/bin/mipham.ts +25 -0
- package/package.json +1 -1
- package/src/agent/sub-agent.ts +4 -3
- package/src/config/defaults.ts +19 -1
- package/src/config/keys-manager.ts +11 -1
- package/src/config/loader.ts +20 -10
- package/src/core/context.ts +86 -56
- package/src/core/credential-masker/output-scrub.ts +5 -0
- package/src/core/crsi-sandbox.ts +19 -7
- package/src/core/engine.ts +50 -33
- package/src/core/instructions.ts +4 -42
- package/src/core/permission.ts +7 -1
- package/src/core/red-team.ts +4 -4
- package/src/core/self-critique.ts +3 -1
- package/src/core/session-log.ts +225 -0
- package/src/core/session-store.ts +119 -34
- package/src/daemon/auth.ts +9 -6
- package/src/daemon/cors.ts +24 -16
- package/src/daemon/rate-limiter.ts +4 -0
- package/src/daemon/server.ts +24 -3
- package/src/index.tsx +54 -28
- package/src/mcp/http-transport.ts +2 -0
- package/src/mcp/oauth.ts +1 -1
- package/src/providers/llm-replay.ts +36 -0
- package/src/providers/llm.ts +16 -0
- package/src/providers/registry.ts +2 -1
- package/src/security/gate.ts +10 -0
- package/src/security/url.ts +37 -14
- package/src/shared/constants.ts +4 -486
- package/src/shared/types.ts +6 -437
- package/src/skills/fork-executor.ts +3 -1
- package/src/skills/loader.ts +2 -1
- package/src/skills/seam.ts +18 -0
- package/src/tools/agent/agent.ts +1 -0
- package/src/tools/agent/skill.ts +1 -0
- package/src/tools/exec/bash.ts +140 -128
- package/src/tools/exec/git.ts +17 -0
- package/src/tools/file/read.ts +73 -66
- package/src/tools/file/write.ts +1 -1
- package/src/tools/index.ts +54 -132
- package/src/tools/network/web-fetch.ts +41 -26
- package/src/tools/seam.ts +25 -0
- package/src/tools/validation.ts +91 -0
- package/src/ui/commands.ts +17 -5
- package/src/vajra/compose/assemble.ts +19 -0
- package/src/vajra/compose/bundle.ts +39 -0
- package/src/vajra/compose/dump.ts +5 -0
- package/src/vajra/compose/index.ts +4 -0
- package/src/vajra/compose/mount.ts +30 -0
- package/src/vajra/context.ts +183 -0
- package/src/vajra/events.ts +13 -0
- package/src/vajra/index.ts +4 -0
- package/src/vajra/leaf/plan-runner.ts +94 -0
- package/src/vajra/service.ts +15 -0
- package/src/workflow/primitives/agent.ts +3 -1
- package/src/workflow/runtime.ts +23 -15
- package/src/workflow/sandbox.ts +51 -9
- package/src/core/compaction-progress.ts +0 -205
- package/src/core/context-compact.ts +0 -124
- package/src/core/context-drain.ts +0 -113
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import type { ContextManager, Summarizer } from './context'
|
|
2
|
-
import { snipMessages } from './context-snip'
|
|
3
|
-
import { CompactionProgressTracker } from './compaction-progress'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Layer 3: Reactive Compact — API-based summarization.
|
|
7
|
-
*
|
|
8
|
-
* When the context exceeds the compaction threshold, uses an LLM summarizer
|
|
9
|
-
* to condense the conversation history, keeping recent messages intact.
|
|
10
|
-
*
|
|
11
|
-
* After compaction, the context contains:
|
|
12
|
-
* 1. A summary message of the truncated history
|
|
13
|
-
* 2. The most recent N messages (kept intact)
|
|
14
|
-
* 3. The system prompt (unchanged)
|
|
15
|
-
*/
|
|
16
|
-
/**
|
|
17
|
-
* Adaptive keep-recent count: scales with context window size.
|
|
18
|
-
* 128K → 20, 200K → 20, 500K → 40, 1M → 80
|
|
19
|
-
*/
|
|
20
|
-
function getKeepRecent(contextWindow: number): number {
|
|
21
|
-
return Math.max(20, Math.floor(contextWindow / 12500))
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface CompactResult {
|
|
25
|
-
/** Messages before compaction */
|
|
26
|
-
beforeCount: number
|
|
27
|
-
/** Messages after compaction */
|
|
28
|
-
afterCount: number
|
|
29
|
-
/** Whether compaction was actually performed */
|
|
30
|
-
compacted: boolean
|
|
31
|
-
/** Progress summary for display */
|
|
32
|
-
summary: string
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export async function reactiveCompact(
|
|
36
|
-
context: ContextManager,
|
|
37
|
-
summarizer: Summarizer,
|
|
38
|
-
heading: string,
|
|
39
|
-
keepRecent?: number,
|
|
40
|
-
): Promise<CompactResult> {
|
|
41
|
-
// Use explicit override, or adaptive default based on context window
|
|
42
|
-
const effectiveKeepRecent = keepRecent ?? getKeepRecent(context.getMaxTokens())
|
|
43
|
-
const messages = context.getMessages()
|
|
44
|
-
const tokensBefore = context.getEstimatedTokens()
|
|
45
|
-
|
|
46
|
-
if (messages.length <= effectiveKeepRecent + 4) {
|
|
47
|
-
return {
|
|
48
|
-
beforeCount: messages.length,
|
|
49
|
-
afterCount: messages.length,
|
|
50
|
-
compacted: false,
|
|
51
|
-
summary: `Context has ${messages.length} messages — no compaction needed (threshold: ${effectiveKeepRecent + 4}).`,
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// ── Progress tracker ──
|
|
56
|
-
const tracker = new CompactionProgressTracker(messages.length, tokensBefore)
|
|
57
|
-
tracker.update({ phase: 'snip', message: 'Snipping empty pairs...', percent: 5 })
|
|
58
|
-
|
|
59
|
-
// First, run snip to remove empty pairs
|
|
60
|
-
const { messages: snipped } = snipMessages(messages)
|
|
61
|
-
tracker.update({
|
|
62
|
-
phase: 'snip',
|
|
63
|
-
message: `Snipped ${messages.length - snipped.length} empty pairs`,
|
|
64
|
-
percent: 15,
|
|
65
|
-
messagesAfter: snipped.length,
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
if (snipped.length <= effectiveKeepRecent + 4) {
|
|
69
|
-
const afterCount = snipped.length
|
|
70
|
-
tracker.complete(afterCount, tokensBefore)
|
|
71
|
-
return {
|
|
72
|
-
beforeCount: messages.length,
|
|
73
|
-
afterCount,
|
|
74
|
-
compacted: true,
|
|
75
|
-
summary: `Snip-only: ${messages.length} → ${afterCount} messages (removed ${messages.length - afterCount} empty pairs).`,
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Split: old messages to summarize, recent messages to keep
|
|
80
|
-
const toSummarize = snipped.slice(0, -effectiveKeepRecent)
|
|
81
|
-
const toKeep = snipped.slice(-effectiveKeepRecent)
|
|
82
|
-
|
|
83
|
-
tracker.update({
|
|
84
|
-
phase: 'reactive-compact',
|
|
85
|
-
message: `Summarizing ${toSummarize.length} messages...`,
|
|
86
|
-
percent: 20,
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
// Generate summary with retry logic
|
|
90
|
-
let summary: string
|
|
91
|
-
try {
|
|
92
|
-
summary = await CompactionProgressTracker.retrySummarizer(
|
|
93
|
-
() => summarizer(toSummarize, heading),
|
|
94
|
-
tracker,
|
|
95
|
-
toSummarize.length,
|
|
96
|
-
)
|
|
97
|
-
} catch {
|
|
98
|
-
// On summarizer failure, do a simple truncation
|
|
99
|
-
tracker.update({
|
|
100
|
-
message: `Summarizer unavailable — truncating to last ${effectiveKeepRecent} messages.`,
|
|
101
|
-
percent: 80,
|
|
102
|
-
})
|
|
103
|
-
summary = `Earlier conversation (${toSummarize.length} messages) omitted due to context limits.`
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Rebuild messages: summary + recent
|
|
107
|
-
const summaryMsg = {
|
|
108
|
-
role: 'user' as const,
|
|
109
|
-
content: `[Earlier conversation summary — ${heading}]: ${summary.slice(0, 8000)}`,
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const newMessages = [summaryMsg, ...toKeep]
|
|
113
|
-
context.replaceMessages(newMessages)
|
|
114
|
-
|
|
115
|
-
const tokensAfter = context.getEstimatedTokens()
|
|
116
|
-
tracker.complete(newMessages.length, tokensAfter)
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
beforeCount: messages.length,
|
|
120
|
-
afterCount: newMessages.length,
|
|
121
|
-
compacted: true,
|
|
122
|
-
summary: tracker.getSummary(),
|
|
123
|
-
}
|
|
124
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import type { ContextManager } from './context'
|
|
2
|
-
import type { CompactionProgressTracker } from './compaction-progress'
|
|
3
|
-
|
|
4
|
-
const MINIMAL_KEEP = 5
|
|
5
|
-
let drainLevel = 0
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Layer 4: Emergency Drain — 413 error recovery.
|
|
9
|
-
*
|
|
10
|
-
* Called when a 413 (context too large) error is received.
|
|
11
|
-
* Progressively strips messages until the context fits:
|
|
12
|
-
*
|
|
13
|
-
* Level 1: Drop earliest 50% of messages
|
|
14
|
-
* Level 2+: Keep only system prompt + last 5 messages
|
|
15
|
-
*
|
|
16
|
-
* Returns drain result with progress summary.
|
|
17
|
-
*/
|
|
18
|
-
export interface DrainResult {
|
|
19
|
-
/** Whether recovery was possible */
|
|
20
|
-
recovered: boolean
|
|
21
|
-
/** Messages before drain */
|
|
22
|
-
beforeCount: number
|
|
23
|
-
/** Messages after drain */
|
|
24
|
-
afterCount: number
|
|
25
|
-
/** Current drain level */
|
|
26
|
-
level: number
|
|
27
|
-
/** Human-readable summary */
|
|
28
|
-
summary: string
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export async function emergencyDrain(
|
|
32
|
-
context: ContextManager,
|
|
33
|
-
progress?: CompactionProgressTracker,
|
|
34
|
-
): Promise<DrainResult> {
|
|
35
|
-
const messages = context.getMessages()
|
|
36
|
-
|
|
37
|
-
if (messages.length <= MINIMAL_KEEP) {
|
|
38
|
-
return {
|
|
39
|
-
recovered: false,
|
|
40
|
-
beforeCount: messages.length,
|
|
41
|
-
afterCount: messages.length,
|
|
42
|
-
level: drainLevel,
|
|
43
|
-
summary: `Cannot drain further: already at minimal ${messages.length} messages.`,
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
progress?.update({
|
|
48
|
-
phase: 'emergency-drain',
|
|
49
|
-
message: `Emergency drain level ${drainLevel + 1}: recovering from 413 error...`,
|
|
50
|
-
percent: 50,
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
if (drainLevel === 0) {
|
|
54
|
-
// First attempt: drop earliest 50%
|
|
55
|
-
const keepCount = Math.max(MINIMAL_KEEP, Math.floor(messages.length / 2))
|
|
56
|
-
const kept = messages.slice(-keepCount)
|
|
57
|
-
context.replaceMessages(kept)
|
|
58
|
-
drainLevel++
|
|
59
|
-
progress?.update({
|
|
60
|
-
phase: 'emergency-drain',
|
|
61
|
-
message: `Drain level 1: dropped ${messages.length - keepCount} messages (${messages.length} → ${keepCount})`,
|
|
62
|
-
percent: 80,
|
|
63
|
-
messagesAfter: keepCount,
|
|
64
|
-
})
|
|
65
|
-
return {
|
|
66
|
-
recovered: true,
|
|
67
|
-
beforeCount: messages.length,
|
|
68
|
-
afterCount: keepCount,
|
|
69
|
-
level: drainLevel,
|
|
70
|
-
summary: `Emergency drain level 1: ${messages.length} → ${keepCount} messages (dropped earliest 50%).`,
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Subsequent attempts: keep only system + last MINIMAL_KEEP messages
|
|
75
|
-
const kept = messages.slice(-MINIMAL_KEEP)
|
|
76
|
-
|
|
77
|
-
// Add a summary placeholder if we're dropping a lot
|
|
78
|
-
if (messages.length > MINIMAL_KEEP * 2) {
|
|
79
|
-
const summaryMsg = {
|
|
80
|
-
role: 'user' as const,
|
|
81
|
-
content: `[Emergency context drain level ${drainLevel + 1}: ${messages.length - MINIMAL_KEEP} earlier messages discarded due to token limit.]`,
|
|
82
|
-
}
|
|
83
|
-
context.replaceMessages([summaryMsg, ...kept])
|
|
84
|
-
} else {
|
|
85
|
-
context.replaceMessages(kept)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
drainLevel++
|
|
89
|
-
progress?.update({
|
|
90
|
-
phase: 'emergency-drain',
|
|
91
|
-
message: `Drain level ${drainLevel}: ${messages.length} → ${kept.length} messages`,
|
|
92
|
-
percent: 90,
|
|
93
|
-
messagesAfter: kept.length,
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
return {
|
|
97
|
-
recovered: true,
|
|
98
|
-
beforeCount: messages.length,
|
|
99
|
-
afterCount: kept.length,
|
|
100
|
-
level: drainLevel,
|
|
101
|
-
summary: `Emergency drain level ${drainLevel}: ${messages.length} → ${kept.length} messages.`,
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/** Reset drain level (call at session start). */
|
|
106
|
-
export function resetDrainLevel(): void {
|
|
107
|
-
drainLevel = 0
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/** Get current drain level for display. */
|
|
111
|
-
export function getDrainLevel(): number {
|
|
112
|
-
return drainLevel
|
|
113
|
-
}
|