Browse Source

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	companion/src/app.rs
Alvin Unreal 1 month ago
parent
commit
8b5815da7e

+ 26 - 0
companion/src/app.rs

@@ -87,6 +87,12 @@ fn choose_session(sessions: &[SessionInfo]) -> Option<usize> {
         .or_else(|| sessions.first().map(|_| 0))
 }
 
+fn clamp_viewport_pos(pos: egui::Pos2, win_w: f32, win_h: f32, screen: [f32; 2]) -> egui::Pos2 {
+    let x_max = (screen[0] - win_w - GAP).max(GAP);
+    let y_max = (screen[1] - win_h - GAP).max(GAP);
+    egui::pos2(pos.x.clamp(GAP, x_max), pos.y.clamp(GAP, y_max))
+}
+
 pub struct CompanionApp {
     state_path: std::path::PathBuf,
     sessions: Vec<SessionInfo>,
@@ -155,6 +161,14 @@ impl CompanionApp {
             .retain(|s| s.pid.map(is_pid_alive).unwrap_or(!has_modern));
     }
 
+    fn update_screen_from_ctx(&mut self, ctx: &egui::Context) {
+        if let Some(size) = ctx.input(|i| i.viewport().monitor_size) {
+            if 1.0 < size.x && 1.0 < size.y {
+                self.screen = [size.x, size.y];
+            }
+        }
+    }
+
     fn initial_pos(&self, win_w: f32, win_h: f32) -> [f32; 2] {
         let (x, y) = match self.position.as_str() {
             "bottom-left" => (GAP, self.screen[1] - win_h - GAP),
@@ -171,6 +185,7 @@ impl CompanionApp {
 impl eframe::App for CompanionApp {
     fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
         self.poll();
+        self.update_screen_from_ctx(ctx);
 
         let quit = ctx.data(|d| {
             d.get_temp::<bool>(egui::Id::new("companion_quit"))
@@ -223,7 +238,18 @@ impl eframe::App for CompanionApp {
             rows as u32,
         );
         if self.applied_size.as_ref() != Some(&size_layout) {
+            let old_outer_rect = ctx.input(|i| i.viewport().outer_rect);
+            let monitor_size = ctx.input(|i| i.viewport().monitor_size);
+            let screen = monitor_size
+                .filter(|size| 1.0 < size.x && 1.0 < size.y)
+                .map(|size| [size.x, size.y])
+                .unwrap_or(self.screen);
             ctx.send_viewport_cmd(egui::ViewportCommand::InnerSize(egui::vec2(win_w, win_h)));
+            if let Some(rect) = old_outer_rect {
+                ctx.send_viewport_cmd(egui::ViewportCommand::OuterPosition(clamp_viewport_pos(
+                    rect.min, win_w, win_h, screen,
+                )));
+            }
             self.applied_size = Some(size_layout);
         }
 

+ 2 - 0
src/config/constants.ts

@@ -54,6 +54,8 @@ export const DEFAULT_MAX_SUBAGENT_DEPTH = 3;
 // Workflow reminders
 export const PHASE_REMINDER_TEXT = `!IMPORTANT! Scheduler workflow: plan lanes/dependencies → dispatch background specialists → track task IDs → wait for hook-driven completion → reconcile terminal results → verify. Do not poll running jobs, consume running-job output, or advance dependent work. !END!`;
 
+export const PHASE_REMINDER = `<internal_reminder>${PHASE_REMINDER_TEXT}</internal_reminder>`;
+
 export const WRITABLE_FILE_OPERATIONS_RULES = `**File Operations Rules**:
 - Prefer dedicated file tools for normal code work: glob/grep/ast_grep_search for discovery, read for file contents, and edit/write/apply_patch for targeted source changes.
 - Use bash for execution and automation: git, package managers, tests, builds, scripts, diagnostics, and shell-native filesystem operations.

+ 2 - 2
src/hooks/phase-reminder/index.ts

@@ -5,11 +5,11 @@
  * mutating the cached system prompt or prepending request-local content ahead
  * of the user's actual turn.
  */
-import { PHASE_REMINDER_TEXT } from '../../config/constants';
+import { PHASE_REMINDER } from '../../config/constants';
 import { SLIM_INTERNAL_INITIATOR_MARKER } from '../../utils';
 import type { MessageWithParts } from '../types';
 
-export const PHASE_REMINDER = `<internal_reminder>${PHASE_REMINDER_TEXT}</internal_reminder>`;
+export { PHASE_REMINDER };
 
 /**
  * Creates the experimental.chat.messages.transform hook for phase reminder injection.

+ 3 - 9
src/hooks/post-file-tool-nudge/index.ts

@@ -3,7 +3,7 @@
  * Catches the "inspect/edit files → implement myself" anti-pattern.
  */
 
-import { PHASE_REMINDER_TEXT } from '../../config/constants';
+import { PHASE_REMINDER } from '../../config/constants';
 
 interface ToolExecuteAfterInput {
   tool: string;
@@ -29,17 +29,11 @@ export function createPostFileToolNudgeHook(
       return;
     }
 
-    if (output.output.includes(PHASE_REMINDER_TEXT)) {
+    if (output.output.includes(PHASE_REMINDER)) {
       return;
     }
 
-    output.output = [
-      output.output,
-      '',
-      '<internal_reminder>',
-      PHASE_REMINDER_TEXT,
-      '</internal_reminder>',
-    ].join('\n');
+    output.output = `${output.output}\n\n${PHASE_REMINDER}`;
   }
 
   return {