mstro-app 0.1.58 → 0.2.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 (100) hide show
  1. package/bin/commands/login.js +27 -14
  2. package/bin/commands/logout.js +35 -1
  3. package/bin/commands/status.js +1 -1
  4. package/bin/mstro.js +5 -108
  5. package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
  6. package/dist/server/cli/headless/claude-invoker.js +432 -103
  7. package/dist/server/cli/headless/claude-invoker.js.map +1 -1
  8. package/dist/server/cli/headless/index.d.ts +2 -1
  9. package/dist/server/cli/headless/index.d.ts.map +1 -1
  10. package/dist/server/cli/headless/index.js +2 -0
  11. package/dist/server/cli/headless/index.js.map +1 -1
  12. package/dist/server/cli/headless/prompt-utils.d.ts +5 -8
  13. package/dist/server/cli/headless/prompt-utils.d.ts.map +1 -1
  14. package/dist/server/cli/headless/prompt-utils.js +40 -5
  15. package/dist/server/cli/headless/prompt-utils.js.map +1 -1
  16. package/dist/server/cli/headless/runner.d.ts +1 -1
  17. package/dist/server/cli/headless/runner.d.ts.map +1 -1
  18. package/dist/server/cli/headless/runner.js +29 -7
  19. package/dist/server/cli/headless/runner.js.map +1 -1
  20. package/dist/server/cli/headless/stall-assessor.d.ts +77 -1
  21. package/dist/server/cli/headless/stall-assessor.d.ts.map +1 -1
  22. package/dist/server/cli/headless/stall-assessor.js +336 -20
  23. package/dist/server/cli/headless/stall-assessor.js.map +1 -1
  24. package/dist/server/cli/headless/tool-watchdog.d.ts +67 -0
  25. package/dist/server/cli/headless/tool-watchdog.d.ts.map +1 -0
  26. package/dist/server/cli/headless/tool-watchdog.js +296 -0
  27. package/dist/server/cli/headless/tool-watchdog.js.map +1 -0
  28. package/dist/server/cli/headless/types.d.ts +80 -1
  29. package/dist/server/cli/headless/types.d.ts.map +1 -1
  30. package/dist/server/cli/improvisation-session-manager.d.ts +109 -2
  31. package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
  32. package/dist/server/cli/improvisation-session-manager.js +737 -132
  33. package/dist/server/cli/improvisation-session-manager.js.map +1 -1
  34. package/dist/server/index.js +5 -10
  35. package/dist/server/index.js.map +1 -1
  36. package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
  37. package/dist/server/mcp/bouncer-integration.js +18 -0
  38. package/dist/server/mcp/bouncer-integration.js.map +1 -1
  39. package/dist/server/mcp/security-audit.d.ts +2 -2
  40. package/dist/server/mcp/security-audit.d.ts.map +1 -1
  41. package/dist/server/mcp/security-audit.js +12 -8
  42. package/dist/server/mcp/security-audit.js.map +1 -1
  43. package/dist/server/mcp/security-patterns.d.ts.map +1 -1
  44. package/dist/server/mcp/security-patterns.js +9 -4
  45. package/dist/server/mcp/security-patterns.js.map +1 -1
  46. package/dist/server/routes/improvise.js +6 -6
  47. package/dist/server/routes/improvise.js.map +1 -1
  48. package/dist/server/services/analytics.d.ts +2 -0
  49. package/dist/server/services/analytics.d.ts.map +1 -1
  50. package/dist/server/services/analytics.js +13 -3
  51. package/dist/server/services/analytics.js.map +1 -1
  52. package/dist/server/services/platform.d.ts.map +1 -1
  53. package/dist/server/services/platform.js +4 -9
  54. package/dist/server/services/platform.js.map +1 -1
  55. package/dist/server/services/sandbox-utils.d.ts +6 -0
  56. package/dist/server/services/sandbox-utils.d.ts.map +1 -0
  57. package/dist/server/services/sandbox-utils.js +72 -0
  58. package/dist/server/services/sandbox-utils.js.map +1 -0
  59. package/dist/server/services/settings.d.ts +6 -0
  60. package/dist/server/services/settings.d.ts.map +1 -1
  61. package/dist/server/services/settings.js +21 -0
  62. package/dist/server/services/settings.js.map +1 -1
  63. package/dist/server/services/terminal/pty-manager.d.ts +3 -51
  64. package/dist/server/services/terminal/pty-manager.d.ts.map +1 -1
  65. package/dist/server/services/terminal/pty-manager.js +14 -100
  66. package/dist/server/services/terminal/pty-manager.js.map +1 -1
  67. package/dist/server/services/websocket/handler.d.ts +36 -15
  68. package/dist/server/services/websocket/handler.d.ts.map +1 -1
  69. package/dist/server/services/websocket/handler.js +452 -223
  70. package/dist/server/services/websocket/handler.js.map +1 -1
  71. package/dist/server/services/websocket/types.d.ts +6 -2
  72. package/dist/server/services/websocket/types.d.ts.map +1 -1
  73. package/hooks/bouncer.sh +11 -4
  74. package/package.json +4 -1
  75. package/server/cli/headless/claude-invoker.ts +602 -119
  76. package/server/cli/headless/index.ts +7 -1
  77. package/server/cli/headless/prompt-utils.ts +37 -5
  78. package/server/cli/headless/runner.ts +30 -8
  79. package/server/cli/headless/stall-assessor.ts +453 -22
  80. package/server/cli/headless/tool-watchdog.ts +390 -0
  81. package/server/cli/headless/types.ts +84 -1
  82. package/server/cli/improvisation-session-manager.ts +884 -143
  83. package/server/index.ts +5 -10
  84. package/server/mcp/bouncer-integration.ts +28 -0
  85. package/server/mcp/security-audit.ts +12 -8
  86. package/server/mcp/security-patterns.ts +8 -2
  87. package/server/routes/improvise.ts +6 -6
  88. package/server/services/analytics.ts +13 -3
  89. package/server/services/platform.test.ts +0 -10
  90. package/server/services/platform.ts +4 -10
  91. package/server/services/sandbox-utils.ts +78 -0
  92. package/server/services/settings.ts +25 -0
  93. package/server/services/terminal/pty-manager.ts +16 -127
  94. package/server/services/websocket/handler.ts +515 -251
  95. package/server/services/websocket/types.ts +10 -4
  96. package/dist/server/services/terminal/tmux-manager.d.ts +0 -82
  97. package/dist/server/services/terminal/tmux-manager.d.ts.map +0 -1
  98. package/dist/server/services/terminal/tmux-manager.js +0 -352
  99. package/dist/server/services/terminal/tmux-manager.js.map +0 -1
  100. package/server/services/terminal/tmux-manager.ts +0 -426
@@ -26,14 +26,20 @@ export {
26
26
  } from './prompt-utils.js';
27
27
  // Main runner class
28
28
  export { HeadlessRunner } from './runner.js';
29
+ // Tool watchdog
30
+ export { ToolWatchdog } from './tool-watchdog.js';
29
31
  // Types
30
32
  export type {
33
+ ExecutionCheckpoint,
31
34
  ExecutionResult,
32
35
  HeadlessConfig,
33
36
  ImageAttachment,
34
- ResolvedHeadlessConfig,
37
+ PendingToolMap,
38
+ ResolvedHeadlessConfig,
35
39
  SessionResult,
36
40
  SessionState,
41
+ ToolDurationTracker,
42
+ ToolTimeoutProfile,
37
43
  ToolUseAccumulator,
38
44
  ToolUseEvent
39
45
  } from './types.js';
@@ -7,16 +7,19 @@
7
7
  * Utilities for enriching prompts with context from previous conversation.
8
8
  */
9
9
 
10
+ import { assessApproval } from './stall-assessor.js';
10
11
  import type { ImageAttachment, PromptContext } from './types.js';
11
12
 
12
13
  /**
13
- * Enrich prompt with context from previous conversation
14
+ * Enrich prompt with context from previous conversation.
15
+ * Async because ambiguous short prompts are classified by Haiku.
14
16
  */
15
- export function enrichPromptWithContext(prompt: string, context: PromptContext): string {
17
+ export async function enrichPromptWithContext(prompt: string, context: PromptContext): Promise<string> {
16
18
  let enriched = prompt;
17
19
 
18
- // Detect if this is a continuation/approval prompt
19
- const isApprovalOrContinuation = isApprovalPrompt(prompt);
20
+ // Detect if this is a continuation/approval prompt.
21
+ // Fast regex path for obvious approvals, Haiku for ambiguous short prompts.
22
+ const isApprovalOrContinuation = await detectApproval(prompt);
20
23
 
21
24
  // Add accumulated knowledge from previous prompts
22
25
  if (context.accumulatedKnowledge) {
@@ -36,9 +39,38 @@ export function enrichPromptWithContext(prompt: string, context: PromptContext):
36
39
  }
37
40
 
38
41
  /**
39
- * Detect if a prompt is an approval or continuation
42
+ * Detect if a prompt is an approval or continuation.
43
+ * Layer 1: Regex fast path for obvious approvals (free, sync).
44
+ * Layer 2: Haiku assessment for ambiguous short prompts (<100 chars).
45
+ */
46
+ async function detectApproval(prompt: string): Promise<boolean> {
47
+ // Layer 1: fast regex path
48
+ if (isApprovalPromptFast(prompt)) return true;
49
+
50
+ // Layer 2: Haiku for short ambiguous prompts.
51
+ // Long prompts (>100 chars) are almost certainly new tasks, not approvals.
52
+ if (prompt.trim().length <= 100) {
53
+ try {
54
+ const claudeCmd = process.env.CLAUDE_COMMAND || 'claude';
55
+ const verdict = await assessApproval(prompt, claudeCmd, false);
56
+ return verdict.isApproval;
57
+ } catch {
58
+ // Haiku failed — fall through to false
59
+ }
60
+ }
61
+
62
+ return false;
63
+ }
64
+
65
+ /**
66
+ * Fast regex-based approval detection (sync, no API call).
67
+ * Catches obvious affirmatives. Ambiguous cases fall through to Haiku.
40
68
  */
41
69
  export function isApprovalPrompt(prompt: string): boolean {
70
+ return isApprovalPromptFast(prompt);
71
+ }
72
+
73
+ function isApprovalPromptFast(prompt: string): boolean {
42
74
  const lower = prompt.toLowerCase().trim();
43
75
 
44
76
  // Short affirmative responses
@@ -20,7 +20,7 @@ import type {
20
20
  } from './types.js';
21
21
 
22
22
  // Re-export types for backward compatibility
23
- export type { HeadlessConfig, ImageAttachment, SessionResult, SessionState, ToolUseEvent } from './types.js';
23
+ export type { ExecutionCheckpoint, HeadlessConfig, ImageAttachment, SessionResult, SessionState, ToolTimeoutProfile, ToolUseEvent } from './types.js';
24
24
 
25
25
  export class HeadlessRunner {
26
26
  private config: ResolvedHeadlessConfig;
@@ -33,9 +33,9 @@ export class HeadlessRunner {
33
33
  maxSessions: config.maxSessions || 50,
34
34
  maxRetries: config.maxRetries || 3,
35
35
  claudeCommand: config.claudeCommand || process.env.CLAUDE_COMMAND || 'claude',
36
- verbose: config.verbose || false,
37
- noColor: config.noColor || false,
38
- improvisationMode: config.improvisationMode || false,
36
+ verbose: !!config.verbose,
37
+ noColor: !!config.noColor,
38
+ improvisationMode: !!config.improvisationMode,
39
39
  movementNumber: config.movementNumber ?? 0,
40
40
  outputCallback: config.outputCallback,
41
41
  thinkingCallback: config.thinkingCallback,
@@ -51,6 +51,11 @@ export class HeadlessRunner {
51
51
  stallMaxExtensions: config.stallMaxExtensions ?? 3,
52
52
  stallHardCapMs: config.stallHardCapMs ?? 3_600_000,
53
53
  model: config.model,
54
+ toolTimeoutProfiles: config.toolTimeoutProfiles,
55
+ enableToolWatchdog: config.enableToolWatchdog !== false,
56
+ maxAutoRetries: config.maxAutoRetries ?? 2,
57
+ onToolTimeout: config.onToolTimeout,
58
+ sandboxed: config.sandboxed,
54
59
  };
55
60
  }
56
61
 
@@ -78,22 +83,36 @@ export class HeadlessRunner {
78
83
  const sessionId = `direct-${Date.now()}`;
79
84
 
80
85
  const enrichedPrompt = context
81
- ? enrichPromptWithContext(userPrompt, context)
86
+ ? await enrichPromptWithContext(userPrompt, context)
82
87
  : userPrompt;
83
88
 
84
89
  const result = await this.executePromptCommand(enrichedPrompt, 'main', 1);
85
90
 
86
91
  if (result.exitCode !== 0) {
92
+ // Build meaningful error: prefer stderr, fall back to non-JSON stdout lines
93
+ let errorMessage = result.error;
94
+ if (!errorMessage && result.output) {
95
+ const plainLines = result.output.split('\n')
96
+ .filter(l => l.trim() && !l.trim().startsWith('{'))
97
+ .join('\n')
98
+ .trim();
99
+ if (plainLines) {
100
+ errorMessage = plainLines.slice(0, 500);
101
+ }
102
+ }
87
103
  return {
88
104
  completed: false,
89
105
  needsHandoff: false,
90
106
  totalTokens: 0,
91
107
  sessionId,
92
- error: result.error || 'Execution failed',
108
+ error: errorMessage || `Claude exited with code ${result.exitCode}`,
93
109
  assistantResponse: result.assistantResponse,
94
110
  thinkingOutput: result.thinkingOutput,
95
111
  toolUseHistory: result.toolUseHistory,
96
- claudeSessionId: result.claudeSessionId
112
+ claudeSessionId: result.claudeSessionId,
113
+ nativeTimeoutCount: result.nativeTimeoutCount,
114
+ postTimeoutOutput: result.postTimeoutOutput,
115
+ resumeBufferedOutput: result.resumeBufferedOutput,
97
116
  };
98
117
  }
99
118
 
@@ -107,7 +126,10 @@ export class HeadlessRunner {
107
126
  assistantResponse: result.assistantResponse,
108
127
  thinkingOutput: result.thinkingOutput,
109
128
  toolUseHistory: result.toolUseHistory,
110
- claudeSessionId: result.claudeSessionId
129
+ claudeSessionId: result.claudeSessionId,
130
+ nativeTimeoutCount: result.nativeTimeoutCount,
131
+ postTimeoutOutput: result.postTimeoutOutput,
132
+ resumeBufferedOutput: result.resumeBufferedOutput,
111
133
  };
112
134
  }
113
135