feat: add official Codex skill and harden task execution
Install and update artemis-use with task diagnostics and plugin guidance. Preserve cross-device file receipts, review canonical reports, guard stage transitions and reduce small history summaries. Improve exploration lifecycle, request recovery and browser live connections.
This commit is contained in:
1 parent
2328a4df0c
commit
e1ea099592
128 files changed
+4944
-700
No files matched your search
+1
-1
@@ -120,7 +120,7 @@ limitations under the License.
|
||||
<div class="video-body">
|
||||
<!-- 1. LIVE SCREEN STREAM (AUTOMATICALLY ACTIVE DURING RUNNING TEST) -->
|
||||
@if (agentService.recordingPlaybackStatus() === 'live') {
|
||||
@if (!liveStreamError()) {
|
||||
@if (!liveStreamError() && pageVisible()) {
|
||||
<div class="live-stream-container">
|
||||
<img
|
||||
class="live-screen-img"
|
||||
|
||||
+18
@@ -98,6 +98,24 @@ describe('FloatingVideoPlayerComponent video markers', () => {
|
||||
fixture.detectChanges();
|
||||
}
|
||||
|
||||
it('removes the live image while hidden and reconnects it on return', () => {
|
||||
const hidden = spyOnProperty(document, 'hidden', 'get').and.returnValue(false);
|
||||
agentService.recordingPlaybackStatus.set('live');
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.querySelector('img.live-screen-img')).not.toBeNull();
|
||||
|
||||
hidden.and.returnValue(true);
|
||||
document.dispatchEvent(new Event('visibilitychange'));
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.querySelector('img.live-screen-img')).toBeNull();
|
||||
|
||||
hidden.and.returnValue(false);
|
||||
document.dispatchEvent(new Event('visibilitychange'));
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.querySelector('img.live-screen-img')).not.toBeNull();
|
||||
expect(component.liveStreamError()).toBeFalse();
|
||||
});
|
||||
|
||||
function playButton(): HTMLButtonElement {
|
||||
return fixture.nativeElement.querySelector('.play-btn');
|
||||
}
|
||||
|
||||
+15
@@ -70,6 +70,21 @@ export class FloatingVideoPlayerComponent implements OnDestroy {
|
||||
public sources = computed(() => this.agentService.recordingSources?.() || []);
|
||||
public liveStreamUrl = signal<string>('/api/stream/device-live');
|
||||
public liveStreamError = signal<boolean>(false);
|
||||
public pageVisible = signal(!document.hidden);
|
||||
|
||||
@HostListener('document:visibilitychange')
|
||||
@HostListener('window:pageshow')
|
||||
onPageVisibilityChange(): void {
|
||||
this.pageVisible.set(!document.hidden);
|
||||
if (!document.hidden && this.agentService.recordingPlaybackStatus() === 'live') {
|
||||
this.retryLiveStream();
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('window:pagehide')
|
||||
onPageHide(): void {
|
||||
this.pageVisible.set(false);
|
||||
}
|
||||
public activeSegmentIndex = signal<number>(0);
|
||||
public currentVideoUrl = computed(() => {
|
||||
const segments = this.agentService.activeVideoSegments();
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Component, inject, OnInit, signal, output } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { finalize } from 'rxjs';
|
||||
import { finalize, timeout } from 'rxjs';
|
||||
import { UI_READ_TIMEOUT_MS } from '../../core/http-timeouts';
|
||||
|
||||
interface Capabilities {
|
||||
recording?: { codec: string; max_long_edge: number; fps: number; h265_kbps: number; h264_kbps: number; computer_scope: string; minimum_disk_mb: number; hevc_playback: boolean };
|
||||
@@ -98,7 +99,7 @@ export class CapabilitySettingsComponent implements OnInit {
|
||||
config: Capabilities = { adb_auto_connect: true, adb_discovery_interval_seconds: 10, adb_discovery_timeout_seconds: 5, video_enabled: false, recording_enabled: false, outputter_enabled: true, step_summarizer_enabled: true, screenshot_limit_enabled: true, screenshot_max_long_edge: 2048, task_timeout_seconds: 1800, observation_timeout_seconds: 60, observation_max_attempts: 3, device_command_timeout_seconds: 10, action_failure_attempts: 3, worker_cleanup_timeout_seconds: 30 };
|
||||
busy = signal(false); loading = signal(true); loaded = signal(false); message = signal(''); error = signal('');
|
||||
ngOnInit(): void {
|
||||
this.http.get<Capabilities>('/api/system/capability-settings').pipe(finalize(() => this.loading.set(false))).subscribe({
|
||||
this.http.get<Capabilities>('/api/system/capability-settings').pipe(timeout(UI_READ_TIMEOUT_MS), finalize(() => this.loading.set(false))).subscribe({
|
||||
next: config => { this.config = config; this.loaded.set(true); this.changed.emit(config); }, error: () => this.error.set('功能配置加载失败,请刷新页面重试。')
|
||||
});
|
||||
}
|
||||
|
||||
+14
-1
@@ -1,4 +1,5 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { UI_READ_TIMEOUT_MS } from '../../core/http-timeouts';
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { ModelSettingsComponent } from './model-settings.component';
|
||||
@@ -17,6 +18,18 @@ describe('持久化模型与功能设置', () => {
|
||||
});
|
||||
afterEach(() => http.verify());
|
||||
|
||||
it('配置请求排队超时后退出加载状态,不会发送保存请求', fakeAsync(() => {
|
||||
const fixture = TestBed.createComponent(ModelSettingsComponent);
|
||||
fixture.detectChanges();
|
||||
const request = http.expectOne('/api/system/model-settings');
|
||||
tick(UI_READ_TIMEOUT_MS);
|
||||
expect(request.cancelled).toBeTrue();
|
||||
expect(fixture.componentInstance.loading()).toBeFalse();
|
||||
expect(fixture.componentInstance.error()).toBeTruthy();
|
||||
expect(fixture.componentInstance.valid()).toBeFalse();
|
||||
http.expectNone('/api/system/model-settings');
|
||||
}));
|
||||
|
||||
it('加载配置后保留密钥,保存新模型且不发送空密钥', () => {
|
||||
const fixture = TestBed.createComponent(ModelSettingsComponent);
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Component, OnInit, inject, signal, output } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { finalize } from 'rxjs';
|
||||
import { finalize, timeout } from 'rxjs';
|
||||
import { UI_READ_TIMEOUT_MS } from '../../core/http-timeouts';
|
||||
import { SettingSliderComponent, REASONING_VALUES, REASONING_LABELS } from '../setting-slider/setting-slider.component';
|
||||
|
||||
interface ModelSettings {
|
||||
@@ -128,7 +129,7 @@ export class ModelSettingsComponent implements OnInit {
|
||||
message = signal(''); error = signal('');
|
||||
|
||||
ngOnInit(): void {
|
||||
this.http.get<ModelSettings>('/api/system/model-settings').pipe(finalize(() => this.loading.set(false))).subscribe({
|
||||
this.http.get<ModelSettings>('/api/system/model-settings').pipe(timeout(UI_READ_TIMEOUT_MS), finalize(() => this.loading.set(false))).subscribe({
|
||||
next: config => this.apply(config),
|
||||
error: () => this.error.set('配置加载失败,请刷新页面重试。')
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user