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:
chenmingxuan committed 2026-10-18 14:18:37 +08:00
1 parent 2328a4df0c
commit e1ea099592
128 files changed
+4944 -700

No files matched your search

@@ -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"
@@ -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');
}
@@ -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('功能配置加载失败,请刷新页面重试。')
});
}
@@ -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('配置加载失败,请刷新页面重试。')
});