From c6d2050ff081e4e86c59679950a589538518ff02 Mon Sep 17 00:00:00 2001 From: chenmingxuan Date: Thu, 29 Oct 2026 16:32:28 +0800 Subject: [PATCH] fix: release 2.4.4 with collapsible task outcome details --- DESIGN.md | 1 + README.md | 2 +- apps/showcase_ui/package-lock.json | 4 +- apps/showcase_ui/package.json | 2 +- .../agent-stream/agent-stream.component.html | 4 +- .../agent-stream/agent-stream.component.scss | 1 - .../agent-stream/agent-stream.component.ts | 3 +- .../automation-panel.component.html | 4 +- .../automation-panel.component.scss | 1 - .../automation-panel.component.spec.ts | 7 +- .../automation-panel.component.ts | 3 +- .../task-history.component.spec.ts | 24 ++++++- .../task-history/task-history.component.ts | 6 +- .../task-outcome.component.spec.ts | 71 +++++++++++++++++++ .../task-outcome/task-outcome.component.ts | 62 ++++++++++++++++ docs/releases/2.4.4.md | 25 +++++++ pyproject.toml | 2 +- uv.lock | 2 +- 18 files changed, 204 insertions(+), 20 deletions(-) create mode 100644 apps/showcase_ui/src/app/components/task-outcome/task-outcome.component.spec.ts create mode 100644 apps/showcase_ui/src/app/components/task-outcome/task-outcome.component.ts create mode 100644 docs/releases/2.4.4.md diff --git a/DESIGN.md b/DESIGN.md index 5e8cb1d..99da36c 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -155,6 +155,7 @@ Flash 的最终 AI 判定与分析独立配置,默认关闭;首页功能配 - 通常以 4、6、8、12、16、24px 组织内部间距;沿用组件现有网格和断点。子容器允许收缩,长内容使用 `min-width: 0` 及合适换行。 - 探索面板在 600px 以下收紧外边距并允许操作换行;模型选择控件宽度跟随容器。窄屏仍可辨认状态、选择来源和发送指导。 - 列表后台刷新保留滚动、选择和折叠状态;首次加载与静默更新分开,避免周期性加载提示挤动内容。实时连接复用现有页面可见性管理。 +- 结束任务的错误与阻塞说明默认只占一行,点击展开安全 Markdown 正文与已有依据,长正文限高滚动。摘要优先采用已保存的有效结论原因,否则取执行错误首行;不新增 AI 复盘请求,不改写原始记录。展开说明不触发任务切换,刷新保留展开状态。 ## Elevation diff --git a/README.md b/README.md index 854cc18..de91c79 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ 基于 [ARTEMIS](https://github.com/google/artemis) 扩展的 Android AI 自动化测试平台。用自然语言描述任务,即可通过简体中文网页或 MCP 驱动真机、模拟器执行操作并收集结果。模型统一使用 **OpenAI Responses API**。 -**2.4.3**:异常模型输出耗尽重试后只失败当前任务,避免触发全局暂停;补充请求关联和有界参数摘要,便于定位流式异常。见 [2.4.3 发行说明](docs/releases/2.4.3.md)。人工评审插件保持 1.1.0;列表优化见 [2.4.2](docs/releases/2.4.2.md),安装修复见 [2.4.1](docs/releases/2.4.1.md)。 +**2.4.4**:任务结束说明默认显示一行摘要,点击展开 Markdown 正文和已记录依据,避免失败长文本占满列表;不新增 AI 复盘请求。见 [2.4.4 发行说明](docs/releases/2.4.4.md)。人工评审插件保持 1.1.0;批次异常隔离见 [2.4.3](docs/releases/2.4.3.md),列表优化见 [2.4.2](docs/releases/2.4.2.md),安装修复见 [2.4.1](docs/releases/2.4.1.md)。 ## 主要功能 diff --git a/apps/showcase_ui/package-lock.json b/apps/showcase_ui/package-lock.json index 32042e3..3617cee 100644 --- a/apps/showcase_ui/package-lock.json +++ b/apps/showcase_ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "artemis-showcase-ui", - "version": "2.4.3", + "version": "2.4.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "artemis-showcase-ui", - "version": "2.4.3", + "version": "2.4.4", "dependencies": { "@angular/cdk": "^22.2.4", "@angular/common": "^22.2.3", diff --git a/apps/showcase_ui/package.json b/apps/showcase_ui/package.json index 2c741e9..344663e 100644 --- a/apps/showcase_ui/package.json +++ b/apps/showcase_ui/package.json @@ -1,6 +1,6 @@ { "name": "artemis-showcase-ui", - "version": "2.4.3", + "version": "2.4.4", "scripts": { "ng": "ng", "start": "ng serve --proxy-config proxy.conf.json", diff --git a/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html b/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html index 346642e..fb3f763 100644 --- a/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html +++ b/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html @@ -129,9 +129,7 @@ limitations under the License.
{{ session.display_name || session.initial_goal || '未命名任务' }}
- @if (session.error && getTaskStatus(session) === 'failed') { -
{{session.error}}
- } +
任务目标与预期

{{r.goal || '未记录'}}

@if(r.expected){
预期结果

{{r.expected}}

}
@@ -119,7 +119,7 @@ @if(j.human_reviewed){人工已审} - @if(j.error){{{j.error}}} + } } diff --git a/apps/showcase_ui/src/app/components/automation-panel/automation-panel.component.scss b/apps/showcase_ui/src/app/components/automation-panel/automation-panel.component.scss index ba490f8..9d09a49 100644 --- a/apps/showcase_ui/src/app/components/automation-panel/automation-panel.component.scss +++ b/apps/showcase_ui/src/app/components/automation-panel/automation-panel.component.scss @@ -52,7 +52,6 @@ details{margin:14px 0;}summary{color:var(--app-text-secondary, #475569);cursor:p .report-heading{display:flex;align-items:flex-start;gap:8px;}.report-heading .report-title{flex:1;min-width:0;} .report-title{display:block;font-size:14px;line-height:1.5;color:var(--app-text-strong, #0f172a);overflow-wrap:anywhere;} .report-badges{display:flex;gap:7px;flex-wrap:wrap;margin-top:9px;span{padding:3px 8px;border:1px solid var(--app-primary-border, #bfdbfe);border-radius:20px;background:var(--app-primary-soft, #eff6ff);color:var(--app-primary-text, #1e40af);font-size:12px;&.failed{background:var(--app-error-soft, #fff1f2);border-color:var(--app-error-border, #fecdd3);color:var(--app-error-text, #be123c);}}} -.report-error{margin-top:11px;padding:10px 12px;background:var(--app-error-soft, #fff1f2);border-left:3px solid #e11d48;border-radius:6px;color:var(--app-error-text, #9f1239);strong{font-size:12px;}p{margin:4px 0 0;color:var(--app-error-text, #9f1239);white-space:pre-wrap;}} .report-meta{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:10px;margin:12px 0;div{min-width:0;padding:10px 12px;border:1px solid var(--app-border, #e2e8f0);border-radius:8px;background:var(--app-surface, #fff);}strong{font-size:12px;color:var(--app-text-secondary, #475569);}p{margin:5px 0 0;white-space:pre-wrap;color:var(--app-text-control, #1e293b);}} .report-content{margin-top:14px;padding:14px;border:1px solid var(--app-border, #e2e8f0);border-radius:10px;background:var(--app-surface, #fff);h3{margin-bottom:12px;}} .report-body{overflow-x:auto;overflow-wrap:anywhere;color:var(--app-text-control, #1e293b);line-height:1.75;} diff --git a/apps/showcase_ui/src/app/components/automation-panel/automation-panel.component.spec.ts b/apps/showcase_ui/src/app/components/automation-panel/automation-panel.component.spec.ts index 21fdfe1..9a120cb 100644 --- a/apps/showcase_ui/src/app/components/automation-panel/automation-panel.component.spec.ts +++ b/apps/showcase_ui/src/app/components/automation-panel/automation-panel.component.spec.ts @@ -176,7 +176,12 @@ describe('AutomationPanelComponent batch deletion', () => { }); fixture.detectChanges(); const root = fixture.nativeElement as HTMLElement; - expect(root.querySelector('.report-error')?.textContent).toContain('连续 3 次'); + const explanation = root.querySelector('app-task-outcome details')!; + expect(explanation.open).toBeFalse(); + expect(explanation.querySelector('summary')?.textContent).toContain('连续 3 次'); + explanation.querySelector('summary')!.click(); + expect(explanation.open).toBeTrue(); + expect(explanation.querySelector('.body')?.textContent).toContain('连续 3 次'); expect(root.querySelector('.report-body h2')?.textContent).toBe('结果'); expect(root.querySelector('.report-body')?.textContent).toContain('第一行'); expect(root.querySelector('.report-body')?.textContent).toContain('第二行'); diff --git a/apps/showcase_ui/src/app/components/automation-panel/automation-panel.component.ts b/apps/showcase_ui/src/app/components/automation-panel/automation-panel.component.ts index c260e79..b4c96c2 100644 --- a/apps/showcase_ui/src/app/components/automation-panel/automation-panel.component.ts +++ b/apps/showcase_ui/src/app/components/automation-panel/automation-panel.component.ts @@ -1,3 +1,4 @@ +import { TaskOutcomeComponent } from '../task-outcome/task-outcome.component'; import { Component, DestroyRef, computed, effect, inject, output, signal } from '@angular/core'; import { FavoriteButtonComponent } from '../favorite-button/favorite-button.component'; import { ReportMarkdownComponent } from '../report-markdown/report-markdown.component'; @@ -24,7 +25,7 @@ interface ArchiveProgress { } @Component({ - selector: 'app-automation-panel', standalone: true, imports: [FavoriteButtonComponent, ReportMarkdownComponent, CommonModule, FormsModule, TestResultBadgeComponent, TestResultCardComponent, TaskPluginsComponent, EvidenceSourcesComponent], + selector: 'app-automation-panel', standalone: true, imports: [TaskOutcomeComponent,FavoriteButtonComponent, ReportMarkdownComponent, CommonModule, FormsModule, TestResultBadgeComponent, TestResultCardComponent, TaskPluginsComponent, EvidenceSourcesComponent], templateUrl: './automation-panel.component.html', styleUrl: './automation-panel.component.scss' }) export class AutomationPanelComponent { diff --git a/apps/showcase_ui/src/app/components/task-history/task-history.component.spec.ts b/apps/showcase_ui/src/app/components/task-history/task-history.component.spec.ts index 221ab52..1cb6753 100644 --- a/apps/showcase_ui/src/app/components/task-history/task-history.component.spec.ts +++ b/apps/showcase_ui/src/app/components/task-history/task-history.component.spec.ts @@ -18,6 +18,28 @@ describe('历史任务检索', () => { }); afterEach(() => http.verify()); + it('历史错误独立折叠,展开不选择任务且后台刷新保留展开状态', () => { + const fixture = TestBed.createComponent(TaskHistoryComponent); + const row = {session_id:'blocked', name:'前置条件缺失', status:'failed', start_time:100, + error:'[web-blocked] FlashRunner failed: **目标未绑定**\n\n- 未执行删除'}; + http.expectOne(r => r.url === '/api/automation/history').flush({total:1,items:[row]}); + fixture.detectChanges(); + const host = fixture.nativeElement as HTMLElement; + const details = host.querySelector('app-task-outcome details')!; + expect(details.closest('button')).toBeNull(); + expect(details.open).toBeFalse(); + details.querySelector('summary')!.click(); + expect(details.open).toBeTrue(); + expect(TestBed.inject(AgentService).selectSession).not.toHaveBeenCalled(); + fixture.componentInstance.load(true); + http.expectOne(r => r.url === '/api/automation/history').flush({total:1,items:[{...row}]}); + fixture.detectChanges(); + expect(host.querySelector('app-task-outcome details')).toBe(details); + expect(details.open).toBeTrue(); + expect(details.querySelector('strong')!.textContent).toBe('目标未绑定'); + fixture.destroy(); + }); + it('已终止探索可删除,确认范围后刷新,取消不发请求', () => { const fixture = TestBed.createComponent(TaskHistoryComponent); const row = {session_id:'stopped', status:'cancelled', exploration_kind:'exploration', exploration_status:'paused', start_time:100}; @@ -268,7 +290,7 @@ describe('历史任务检索', () => { expect(row.getAttribute('title')).toBe('请打开设置首页并核对两个真实分类'); expect(row.textContent).toContain('1.1 打开并核对设置'); expect(row.textContent).toContain('总耗时 1分5秒'); - expect(row.textContent).toContain('任务执行超时'); + expect(fixture.nativeElement.querySelector('app-task-outcome summary').textContent).toContain('任务执行超时'); expect(fixture.nativeElement.querySelector('.status-failed .history-status').textContent).toContain('失败'); fixture.destroy(); }); diff --git a/apps/showcase_ui/src/app/components/task-history/task-history.component.ts b/apps/showcase_ui/src/app/components/task-history/task-history.component.ts index 4adf722..d11d52a 100644 --- a/apps/showcase_ui/src/app/components/task-history/task-history.component.ts +++ b/apps/showcase_ui/src/app/components/task-history/task-history.component.ts @@ -1,3 +1,4 @@ +import { TaskOutcomeComponent } from '../task-outcome/task-outcome.component'; import { TaskIdCopyComponent } from '../task-id-copy/task-id-copy.component'; import { FavoriteButtonComponent } from '../favorite-button/favorite-button.component'; import { TaskModeBadgeComponent } from '../task-mode-badge/task-mode-badge.component'; @@ -9,13 +10,13 @@ import { HttpClient, HttpParams } from '@angular/common/http'; import { AgentService } from '../../services/agent.service'; @Component({ - selector:'app-task-history', standalone:true, imports:[CommonModule,FormsModule,TaskIdCopyComponent,TestResultBadgeComponent,TaskModeBadgeComponent,FavoriteButtonComponent], + selector:'app-task-history', standalone:true, imports:[TaskOutcomeComponent,CommonModule,FormsModule,TaskIdCopyComponent,TestResultBadgeComponent,TaskModeBadgeComponent,FavoriteButtonComponent], template:`

历史任务 {{total()}} 条{{loading() ? '正在查询…' : ''}}

@if(error()){

{{error()}}

} @for(group of groups(); track group.key){
{{group.title}} 本页 {{group.rows.length}} 条 - @for(row of group.rows; track row.session_id){
@if(agent.canDeleteSession(row)){}
} + @for(row of group.rows; track row.session_id){
@if(agent.canDeleteSession(row)){}
}
} @if(!loading() && !total()){

没有符合条件的历史任务。

}
{{page}} / {{pages()}}
@@ -51,7 +52,6 @@ import { AgentService } from '../../services/agent.service'; .status-completed .history-status,.status-success .history-status{background:var(--app-success-soft, #dcfce7);color:var(--app-success-text, #166534)} .status-failed .history-status{background:var(--app-error-soft, #fee2e2);color:var(--app-error-text, #991b1b)} .status-cancelled .history-status,.status-interrupted .history-status{background:var(--app-warning-soft, #fef3c7);color:var(--app-warning-text, #92400e)} - .history-error{font-size:11px;color:var(--app-error-text, #b91c1c);line-height:1.4} .delete-history{position:absolute;right:5px;top:8px;border:0;padding:3px;color:var(--app-text-muted, #64748b);cursor:pointer;font-size:12px} .delete-history .material-symbols-outlined{font-size:16px} .delete-history:hover{color:var(--app-error-text, #b91c1c)} diff --git a/apps/showcase_ui/src/app/components/task-outcome/task-outcome.component.spec.ts b/apps/showcase_ui/src/app/components/task-outcome/task-outcome.component.spec.ts new file mode 100644 index 0000000..6ce7e10 --- /dev/null +++ b/apps/showcase_ui/src/app/components/task-outcome/task-outcome.component.spec.ts @@ -0,0 +1,71 @@ +import { TestBed } from '@angular/core/testing'; +import { TaskOutcomeComponent } from './task-outcome.component'; + +describe('任务结束说明', () => { + function create(status = 'failed', error = '[web-example] FlashRunner failed: **前置受阻**\n\n- 未找到目标设备\n- 未执行删除') { + const fixture = TestBed.createComponent(TaskOutcomeComponent); + fixture.componentRef.setInput('status', status); + fixture.componentRef.setInput('error', error); + fixture.detectChanges(); + return fixture; + } + + it('默认只显示一行摘要,展开后渲染完整 Markdown,不选择外层任务', () => { + const fixture = create(); + const host = fixture.nativeElement as HTMLElement; + host.style.width = '280px'; + const details = host.querySelector('details')!; + expect(details.open).toBeFalse(); + expect(host.querySelector('.summary-text')!.textContent).toBe('前置受阻'); + expect(host.textContent).not.toContain('FlashRunner failed'); + expect(host.querySelector('button details')).toBeNull(); + const select = jasmine.createSpy('外层任务选择'); + host.addEventListener('click', select); + host.querySelector('summary')!.click(); + expect(details.open).toBeTrue(); + expect(select).not.toHaveBeenCalled(); + expect(host.querySelector('.body strong')!.textContent).toBe('前置受阻'); + expect(host.querySelectorAll('.body li').length).toBe(2); + fixture.componentRef.setInput('error', '[web-example] FlashRunner failed: **更新说明**\n\n- 已有记录'); + fixture.detectChanges(); + expect(host.querySelector('details')).toBe(details); + expect(details.open).toBeTrue(); + host.querySelector('summary')!.click(); + expect(details.open).toBeFalse(); + }); + + it('优先采用已有结论,同时保留不同的执行错误和依据', () => { + const fixture = create(); + fixture.componentRef.setInput('result', {status:'blocked', reason:'目标设备未绑定', evidence:['截图中仅有 **添加设备**']}); + fixture.detectChanges(); + const host = fixture.nativeElement as HTMLElement; + expect(host.querySelector('.summary-text')!.textContent).toBe('目标设备未绑定'); + expect(host.textContent).toContain('未执行删除'); + expect(host.textContent).toContain('已记录依据'); + expect(host.textContent).toContain('添加设备'); + }); + + it('完成但结论阻塞也展示说明,运行中和无内容时不展示', () => { + const fixture = create('running'); + expect(fixture.nativeElement.querySelector('details')).toBeNull(); + fixture.componentRef.setInput('status', 'completed'); + fixture.componentRef.setInput('error', ''); + fixture.componentRef.setInput('result', {status:'blocked',reason:'缺少预置数据',evidence:[]}); + fixture.detectChanges(); + expect(fixture.nativeElement.querySelector('.summary-text').textContent).toBe('缺少预置数据'); + fixture.componentRef.setInput('result', null); + fixture.detectChanges(); + expect(fixture.nativeElement.querySelector('details')).toBeNull(); + }); + + it('不重复相同说明,不执行错误正文中的 HTML 或危险链接', () => { + const error = '**失败**\n\n[打开](javascript:alert(1))'; + const fixture = create('failed', error); + fixture.componentRef.setInput('result', {status:'blocked',reason:error,evidence:[]}); + fixture.detectChanges(); + const host = fixture.nativeElement as HTMLElement; + expect(host.querySelectorAll('app-report-markdown').length).toBe(1); + expect(host.querySelector('script')).toBeNull(); + expect(host.querySelector('a[href^="javascript:"]')).toBeNull(); + }); +}); diff --git a/apps/showcase_ui/src/app/components/task-outcome/task-outcome.component.ts b/apps/showcase_ui/src/app/components/task-outcome/task-outcome.component.ts new file mode 100644 index 0000000..fe7124c --- /dev/null +++ b/apps/showcase_ui/src/app/components/task-outcome/task-outcome.component.ts @@ -0,0 +1,62 @@ +import { Component, computed, input } from '@angular/core'; +import { CaseTestResult } from '../../core/models/session.model'; +import { ReportMarkdownComponent } from '../report-markdown/report-markdown.component'; + +/** Read-only presentation of persisted results; opening it never requests a model. */ +@Component({ + selector: 'app-task-outcome', + standalone: true, + imports: [ReportMarkdownComponent], + template: `@if (visible()) { +
+ + + {{summary()}} + 详情 + +
+ @if (reason()) { } + @if (message() && message() !== reason()) { + @if (reason()) {

执行记录

} + + } + @if (result()?.evidence?.length) { +

已记录依据

+
    @for (item of result()!.evidence; track $index) {
  • }
+ } +
+
+ }`, + styles: [` + :host { display: block; min-width: 0; max-width: 100%; } + details { margin: 4px 0; font-size: 12px; color: var(--app-text-secondary, #475569); } + summary { display: flex; align-items: center; gap: 6px; min-width: 0; min-height: 26px; cursor: pointer; list-style: none; border-radius: 4px; } + summary::-webkit-details-marker { display: none; } + summary:focus-visible { outline: 2px solid var(--app-primary-text, #1a73e8); outline-offset: 2px; } + .summary-text { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + .chevron { flex: none; font-size: 18px; line-height: 20px; } + details[open] .chevron { transform: rotate(90deg); } + .hint { flex: none; font-size: 11px; color: var(--app-text-muted, #64748b); } + .body { max-height: 280px; overflow: auto; padding: 8px 10px; margin-top: 4px; border: 1px solid var(--app-border, #e2e8f0); border-radius: 6px; background: var(--app-surface, #fff); cursor: text; } + .label { font-size: 11px; color: var(--app-text-muted, #64748b); margin: 10px 0 4px; } + ul { margin: 4px 0; padding-left: 20px; } + `] +}) +export class TaskOutcomeComponent { + error = input(); + result = input(); + status = input(''); + // Strip only the known SDK envelope, preserving the actual explanation and diagnostics. + private explanation(value: string | null | undefined): string { + return (value || '').replace(/^\[[^\]\r\n]+\]\s*FlashRunner failed:\s*/, '').trim(); + } + message = computed(() => this.explanation(this.error())); + reason = computed(() => this.explanation(this.result()?.reason)); + visible = computed(() => ['completed', 'success', 'failed', 'cancelled', 'interrupted', 'skipped'].includes(this.status()) + && !!(this.message() || (this.result()?.status !== 'passed' && this.reason()))); + summary = computed(() => { + const line = (this.reason() || this.message()).split(/\r?\n/).find(part => part.trim()) || ''; + return line.replace(/^\s{0,3}(?:#{1,6}\s+|[-*+]\s+|>\s*)/, '') + .replace(/!?\[([^\]]+)\]\([^)]*\)/g, '$1').replace(/(\*\*|__|~~|`)/g, ''); + }); +} diff --git a/docs/releases/2.4.4.md b/docs/releases/2.4.4.md new file mode 100644 index 0000000..28b82de --- /dev/null +++ b/docs/releases/2.4.4.md @@ -0,0 +1,25 @@ +# ARTEMIS 2.4.4 + +本版改善任务结束后错误和阻塞说明的阅读体验,基于 2.4.3。人工评审插件保持 **1.1.0**,SDK、其他插件及第三方依赖版本不变。 + +## 更新 + +- 历史任务、任务切换列表和批次任务默认只显示一行说明,超长内容省略,点击可展开;报告异常说明采用相同折叠展示。 +- 展开后安全渲染 Markdown 正文、列表和表格,并展示已有结论及已记录依据;长正文限高滚动。 +- 列表摘要优先使用已保存的结论原因,否则使用执行错误首行;展示时移除 FlashRunner 固定技术前缀,原始记录保持不变。 +- 仅展示已结束任务的已有信息,不新增 AI 复盘或模型请求。展开说明不触发任务切换,列表后台刷新保留展开状态。 + +本版不改变任务执行、模型重试或批次停止策略,继续保留 2.4.3 的异常输出隔离与参数保护。 + +## 验证 + +- 前端回归:353 项通过,生产构建通过。 +- 内置浏览器使用隔离样例验证默认折叠、Markdown 展开、长内容、无错误完成任务、批次布局、浅色/深色与窄视口。 +- 最终源码 ZIP 的隔离解压、独立空 npm 缓存、官方源与国内镜像安装和生产构建结果随附件 `INSTALL-VERIFICATION.md` 提供。 +- 未复跑用户业务任务、调用真实模型或进行高并发测试;本次没有 Python 行为变更,未重跑 Python 全量回归。 + +## 升级 + +已有安装请等待任务结束,运行 `update-cn.bat` 或 `update.bat` 后重启。新安装下载 `artemis-2.4.4-source.zip`,按 `SHA256SUMS.txt` 校验后启动。请勿直接覆盖旧安装目录。 + +人工评审插件 1.1.0 无需更新。本次发布不构建 Docker,不重启用户服务。 diff --git a/pyproject.toml b/pyproject.toml index 1e07ef5..24b7aed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ [project] name = "artemis" -version = "2.4.3" +version = "2.4.4" description = "Autonomous AI Assistant for Mobile Automation" authors = [ diff --git a/uv.lock b/uv.lock index 0875852..9bb28f3 100644 --- a/uv.lock +++ b/uv.lock @@ -248,7 +248,7 @@ wheels = [ [[package]] name = "artemis" -version = "2.4.3" +version = "2.4.4" source = { editable = "." } dependencies = [ { name = "adbutils" },