Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f056c8a325 | |||
| d94ee96836 | |||
| 1b05a1d05e | |||
| c079003719 | |||
| 0dbd279dfc | |||
| eb192eb161 | |||
| af099abbfa | |||
| 9bd4a1734c | |||
| 248003a445 | |||
| f0ef068910 | |||
| c2c5e1ced5 | |||
| c38d07044a | |||
| efbfc8085d | |||
| 2c778fb027 | |||
| ebb288a6a4 | |||
| 661867c307 | |||
| c0e233fe6c | |||
| 136e1ebceb | |||
| 688cf7a971 | |||
| c2ec978199 | |||
| 58c59b750e | |||
| 297d7bf1e7 | |||
| 64018f9661 | |||
| b48953f87c | |||
| 924d5d3fd0 | |||
| 5bc5328c06 | |||
| e6ed3cae2c | |||
| e072c66ce9 | |||
| 5bdf25cfd7 | |||
| e8d987c207 | |||
| cfd41b5a0d | |||
| f426944701 | |||
| ede6efbe73 | |||
| a1dd382489 | |||
| 7157a90d31 | |||
| ce70171b43 | |||
| 25d331ca0a | |||
| fc39080f27 | |||
| cba98be35f | |||
| 28a6fd97f8 | |||
| f138f648e7 | |||
| ceb6418877 | |||
| 04b738755f | |||
| d019e48f58 | |||
| ba9832a287 | |||
| 857acd4190 | |||
| 10f2f05437 | |||
| 271b7172bd | |||
| 5b2318acf3 | |||
| 8d710738e2 | |||
| 645ef30752 | |||
| c9bb5a93f0 | |||
| 3391ecf7d4 | |||
| 3f9c698a67 | |||
| 4e90465c45 | |||
| f4294660d2 | |||
| 6106b4e67c | |||
| 530538842e | |||
| 44148542f1 | |||
| 5e36b61534 | |||
| 072bf691d9 | |||
| 14f11e77a2 | |||
| 9e50947d02 | |||
| ec1f207021 | |||
| 6950e08506 |
+2
-6
@@ -237,14 +237,10 @@ GENARRATIVE_ENABLE_IMAGE_EDITOR_AGENT_SIDEBAR="false"
|
||||
|
||||
# 官网客户端下载检测渠道:dev、release 或自定义渠道;修改后重启 API 服务。
|
||||
# Windows/macOS 是系统维度,不填写 dev-win/dev-mac。
|
||||
# 客户端埋点也复用该渠道:dev 对应 https://dev.genarrative.world,release 对应 https://www.genarrative.world。
|
||||
# 埋点不接受其它渠道;本地 dev 且 GENARRATIVE_ENV 为 development(默认)/test/container 时允许 loopback 地址及可变端口。
|
||||
GENARRATIVE_CLIENT_DOWNLOAD_CHANNEL="dev"
|
||||
|
||||
# 客户端埋点接收绑定的公开 origin,由 API Server 运行时读取;修改后重启服务。
|
||||
# 必须与客户端登录地址一致,不带 /api、路径或尾部斜杠;未配置/非法时上传接口返回 503。
|
||||
# 本地端口按实际启动结果填写(端口漂移后需同步),localhost 与 127.0.0.1 不可混用。
|
||||
# dev 使用 https://dev.genarrative.world;release 使用 https://www.genarrative.world。
|
||||
GENARRATIVE_AGC_ANALYTICS_ORIGIN="http://127.0.0.1:8082"
|
||||
|
||||
# Optional: official VikingDB credentials for regenerating build-tag similarities
|
||||
# with the Python embedding script. The script auto-loads `.env.local` and uses
|
||||
# the fixed `bge-large-zh` embedding model.
|
||||
|
||||
+21
@@ -194,6 +194,14 @@ _Avoid_: 进度通知、快照轮询、第二套历史
|
||||
把项目对话历史条目与运行态事件转换成消息气泡和工具卡片的读取期转换;不持久化,也不构成事实源。
|
||||
_Avoid_: 投影缓存文件、已脱敏卡片库、第二套 reducer
|
||||
|
||||
**项目对话输入**:
|
||||
AGC 项目对话的输入只有自然语言回合(含 `@` 素材引用与附件);需要动作时由 Runtime 工具与确认卡承接,不从输入文本解析控制词。
|
||||
_Avoid_: 斜杠命令、聊天命令草稿、命令发现列表
|
||||
|
||||
**项目命令 id**:
|
||||
AGC 运行期工具与项目权限策略使用的稳定标识(`GAME_CREATION_APP_COMMANDS` 与 `GameCreationAppPermission`),由 Rust 运行期策略校验与 App 权限审计 / 项目前置条件判定消费,不是用户输入语法。
|
||||
_Avoid_: 把命令 id 当作可输入的聊天命令、为权限位补聊天入口
|
||||
|
||||
**引用候选**:
|
||||
输入区可以命中的对象集合(`@` 素材、`$` Skill),由宿主按种类注入;输入区不判断候选属于哪一类。
|
||||
_Avoid_: 输入区自己读项目清单或应用目录、把候选取值写死在组件里
|
||||
@@ -210,6 +218,18 @@ _Avoid_: 给附件或运行画面区域造候选、为它们保留输入区内
|
||||
引用在正文文本里的形态(`@显示名` / `$名称` / `@附件名`)及其反解析;出站与解析必须同一口径,token 前后各留一个空白。
|
||||
_Avoid_: 出站与解析各写一套、在空白边界之外再补兼容别名、让解析依赖具体种类的字段
|
||||
|
||||
**引用名**:
|
||||
引用自己的名字,同时就是它在正文里的 token(资源显示名、Skill 名、附件名);内部不允许出现空白,空白统一经 `normalizeMentionName` 折成 `-`。
|
||||
_Avoid_: 名字与 token 各存一份、靠补兼容别名或 `resourceId` 兜底来消化空白
|
||||
|
||||
**引用候选枚举**:
|
||||
一种引用种类当前就绪的全部可引用对象,与候选菜单共用同一份集合;区别只在没有查询过滤和条数上限。
|
||||
_Avoid_: 拿菜单查询当枚举、为粘贴另建一份候选清单
|
||||
|
||||
**引用粘贴解析**:
|
||||
把粘贴进来的纯文本按引用文本语法反解析回正文引用;只有身份唯一且逐字确认的 token 才成为引用,其余按原文保留。
|
||||
_Avoid_: 猜文件名或路径、为不确定的 token 挑一个候选、改写用户粘贴的其余文字
|
||||
|
||||
**引用输入区**:
|
||||
只负责编辑与渲染引用的共享输入组件;候选、身份解析与文本语法都来自注入的 provider,它不持有项目清单、不访问后端。
|
||||
_Avoid_: 输入区自己拉 Skill 目录、把选择器面板塞在输入区内部
|
||||
@@ -240,6 +260,7 @@ _Avoid_: 待发送附件列表与正文芯片并存、提交时再拼一遍附
|
||||
- **引用输入区** 由宿主注入的若干 **引用 provider** 组成;**引用候选** 与 **引用文本语法** 都来自 provider,输入区不判断引用种类。
|
||||
- **引用选择器** 不属于 **引用输入区**:它自己拿数据,确认后只通过输入区的插入缝交付引用。
|
||||
- 只有带触发符的 **引用 provider** 会产生候选;**静默 provider** 没有触发符,只能由外部插入或草稿回填进入正文。
|
||||
- **引用粘贴解析** 与出站显示是同一条 **引用文本语法** 的两端;**静默 provider** 的 token(`@附件名` / `@区域标签`)没有候选,因此粘贴时不重建。
|
||||
- **附件芯片** 是本轮附件的唯一事实源;附件导入失败时不产生芯片。
|
||||
|
||||
## Example dialogue
|
||||
|
||||
@@ -193,7 +193,10 @@ export interface AdminDashboardRangePayload {
|
||||
|
||||
export interface AdminDashboardMetricsPayload {
|
||||
generatedAssets: number;
|
||||
/** 已对冲退还(生成失败 / 精选审核返还 / LLM Router 冲正)的净消耗泥点。 */
|
||||
consumedMudPoints: number;
|
||||
/** 同期退还泥点,用于核对「消耗 + 退还」的毛消耗口径。 */
|
||||
refundedMudPoints: number;
|
||||
totalRegisteredUsers: number;
|
||||
newRegisteredUsers: number;
|
||||
newUserPaymentConversion: AdminDashboardPaymentConversionPayload;
|
||||
@@ -955,6 +958,8 @@ export interface AdminRechargeOrderEntryPayload {
|
||||
productTitle: string;
|
||||
productKind: string;
|
||||
amountCents: number;
|
||||
/** 真实支付金额(分):未支付 / 已关闭 / 已过期订单固定为 0,不能拿订单金额当实付。 */
|
||||
paidAmountCents: number;
|
||||
status: string;
|
||||
paymentChannel: string;
|
||||
paidAtMicros?: number | null;
|
||||
@@ -994,6 +999,8 @@ export interface AdminUserDetailResponse {
|
||||
phoneBound: boolean;
|
||||
wechatBound: boolean;
|
||||
historicalConsumedPoints: number;
|
||||
/** 累计充值金额(分):读取失败或命中读取上限时为 null,前端按未知展示。 */
|
||||
cumulativeRechargedCents?: number | null;
|
||||
canReconcileConsumption: boolean;
|
||||
wallet: AdminProfileWalletPayload;
|
||||
rechargeOrders: AdminRechargeOrderEntryPayload[];
|
||||
|
||||
@@ -51,6 +51,7 @@ const detail: AdminUserDetailResponse = {
|
||||
phoneBound: true,
|
||||
wechatBound: true,
|
||||
historicalConsumedPoints: 1234,
|
||||
cumulativeRechargedCents: 128800,
|
||||
canReconcileConsumption: true,
|
||||
wallet,
|
||||
rechargeOrders: [
|
||||
@@ -62,6 +63,7 @@ const detail: AdminUserDetailResponse = {
|
||||
productTitle: '60泥点',
|
||||
productKind: 'points',
|
||||
amountCents: 600,
|
||||
paidAmountCents: 600,
|
||||
status: 'paid',
|
||||
paymentChannel: 'wechat_native',
|
||||
paidAtMicros: 1_720_000_000_000_000,
|
||||
@@ -124,7 +126,11 @@ test('用户查看按钮按内部 ID 查询并展示脱敏资料、余额与退
|
||||
expect(screen.getByText('25', { selector: 'strong' })).toBeTruthy();
|
||||
expect(screen.getByText('历史花费')).toBeTruthy();
|
||||
expect(screen.getByText('1234', { selector: 'strong' })).toBeTruthy();
|
||||
expect(screen.getByText('累计充值')).toBeTruthy();
|
||||
expect(screen.getByText('¥1288.00')).toBeTruthy();
|
||||
expect(screen.getByText('order-1')).toBeTruthy();
|
||||
expect(screen.getByRole('columnheader', { name: '实付' })).toBeTruthy();
|
||||
expect(screen.getByRole('columnheader', { name: '发放泥点' })).toBeTruthy();
|
||||
|
||||
await user.keyboard('{Escape}');
|
||||
await waitFor(() =>
|
||||
@@ -133,6 +139,29 @@ test('用户查看按钮按内部 ID 查询并展示脱敏资料、余额与退
|
||||
await waitFor(() => expect(document.activeElement).toBe(trigger));
|
||||
});
|
||||
|
||||
test('累计充值读取不到时展示未知,不用订单列表近似', async () => {
|
||||
vi.mocked(getAdminUserDetail).mockResolvedValue({
|
||||
...detail,
|
||||
cumulativeRechargedCents: null,
|
||||
});
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<AdminUserReferenceButton
|
||||
token="admin-token"
|
||||
userId="user-1"
|
||||
onUnauthorized={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
await user.click(screen.getByRole('button', { name: '查看用户信息' }));
|
||||
await screen.findByText('陶泥用户');
|
||||
|
||||
expect(screen.getByText('累计充值')).toBeTruthy();
|
||||
expect(screen.getByText('累计充值').nextElementSibling?.textContent).toBe(
|
||||
'未知',
|
||||
);
|
||||
});
|
||||
|
||||
test('只有陶泥号时按 publicUserCode 查询用户', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
|
||||
@@ -364,6 +364,7 @@ export function AdminUserDetailDialog({
|
||||
<th>订单</th>
|
||||
<th>商品</th>
|
||||
<th>实付</th>
|
||||
<th>发放泥点</th>
|
||||
<th>退款</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
@@ -377,11 +378,13 @@ export function AdminUserDetailDialog({
|
||||
</span>
|
||||
<small>{formatMicros(order.createdAtMicros)}</small>
|
||||
</td>
|
||||
<td>{order.productTitle || order.productId}</td>
|
||||
<td>
|
||||
{order.productTitle || order.productId}
|
||||
<small>发放 {order.pointsDelta} 泥点</small>
|
||||
{order.paidAmountCents > 0
|
||||
? formatMoney(order.paidAmountCents)
|
||||
: '未支付'}
|
||||
</td>
|
||||
<td>{formatMoney(order.amountCents)}</td>
|
||||
<td>{order.pointsDelta} 泥点</td>
|
||||
<td>
|
||||
{formatMoney(order.cumulativeSuccessRefundCents)}
|
||||
<small>欠账 {order.unrecoveredPoints} 泥点</small>
|
||||
@@ -435,6 +438,14 @@ function UserIdentityHeader({ detail }: { detail: AdminUserDetailResponse }) {
|
||||
<dt>登录方式</dt>
|
||||
<dd>{detail.loginMethod || '-'}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>累计充值</dt>
|
||||
<dd>
|
||||
{typeof detail.cumulativeRechargedCents === 'number'
|
||||
? formatMoney(detail.cumulativeRechargedCents)
|
||||
: '未知'}
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>绑定状态</dt>
|
||||
<dd>
|
||||
|
||||
@@ -339,7 +339,10 @@ test.each([409, 503])(
|
||||
await confirmWrite();
|
||||
const message = await screen.findByRole('alert');
|
||||
const feedback = message.parentElement!;
|
||||
expect(document.activeElement).toBe(feedback);
|
||||
// 聚焦发生在 React passive effect 里(AdminAgcTemplatesPage 的 feedback 聚焦 useEffect),
|
||||
// 而 findByRole 在 alert 节点一挂上就返回,可能早于该 effect 执行;这里等聚焦落地,
|
||||
// 避免在 CI 负载下抢跑。断言口径不变:焦点最终必须落在提示区而不是弹窗面板。
|
||||
await waitFor(() => expect(document.activeElement).toBe(feedback));
|
||||
expect(feedback.tabIndex).toBe(-1);
|
||||
expect(focus).toHaveBeenCalledWith({ preventScroll: true });
|
||||
expect(viewport.scrollTop).toBe(20);
|
||||
|
||||
@@ -34,6 +34,7 @@ const dashboardResponse: AdminDashboardResponse = {
|
||||
metrics: {
|
||||
generatedAssets: 12,
|
||||
consumedMudPoints: 88,
|
||||
refundedMudPoints: 24,
|
||||
totalRegisteredUsers: 1200,
|
||||
newRegisteredUsers: 16,
|
||||
newUserPaymentConversion: {
|
||||
@@ -105,6 +106,8 @@ test('Dashboard 默认加载今日指标并支持运营汇总页签', async () =
|
||||
expect(await screen.findByText('总计数据')).toBeTruthy();
|
||||
expect(screen.getByText('时段数据')).toBeTruthy();
|
||||
expect(screen.getByText('本日生产素材数')).toBeTruthy();
|
||||
expect(screen.getByText('本日消耗泥点数')).toBeTruthy();
|
||||
expect(screen.getByText('本日退还泥点数')).toBeTruthy();
|
||||
expect(screen.getByText('总注册用户')).toBeTruthy();
|
||||
expect(screen.getByText('本日新增用户数')).toBeTruthy();
|
||||
expect(screen.getByText('新增用户转化与留存')).toBeTruthy();
|
||||
|
||||
@@ -128,6 +128,12 @@ export function AdminDashboardPage({
|
||||
value: metrics?.consumedMudPoints ?? 0,
|
||||
unit: '泥点',
|
||||
},
|
||||
{
|
||||
id: 'refunded-mud-points',
|
||||
label: `${rangePrefix(granularity)}退还泥点数`,
|
||||
value: metrics?.refundedMudPoints ?? 0,
|
||||
unit: '泥点',
|
||||
},
|
||||
{
|
||||
id: 'new-registered-users',
|
||||
label: `${rangePrefix(granularity)}新增用户数`,
|
||||
|
||||
@@ -64,6 +64,7 @@ const baseOrder: AdminRechargeOrderEntryPayload = {
|
||||
productTitle: '60泥点',
|
||||
productKind: 'points',
|
||||
amountCents: 600,
|
||||
paidAmountCents: 600,
|
||||
status: 'paid',
|
||||
paymentChannel: 'wechat_native',
|
||||
paidAtMicros: 1_720_000_000_000_000,
|
||||
@@ -129,6 +130,40 @@ beforeEach(() => {
|
||||
);
|
||||
});
|
||||
|
||||
test('未支付订单不显示实付金额,发放泥点单独成列', async () => {
|
||||
vi.mocked(listAdminRechargeOrders).mockResolvedValue({
|
||||
entries: [
|
||||
{
|
||||
...baseOrder,
|
||||
orderId: 'order-pending',
|
||||
status: 'pending',
|
||||
paidAtMicros: null,
|
||||
paidAmountCents: 0,
|
||||
pointsDelta: 0,
|
||||
},
|
||||
{ ...baseOrder, orderId: 'order-paid' },
|
||||
],
|
||||
});
|
||||
renderPage();
|
||||
|
||||
expect(
|
||||
await screen.findByRole('columnheader', { name: '实付' }),
|
||||
).toBeTruthy();
|
||||
expect(screen.getByRole('columnheader', { name: '发放泥点' })).toBeTruthy();
|
||||
|
||||
const unpaidRow = (await screen.findByText('order-pending')).closest(
|
||||
'tr',
|
||||
) as HTMLElement;
|
||||
const unpaidCells = within(unpaidRow).getAllByRole('cell');
|
||||
expect(unpaidCells[3]?.textContent).toContain('未支付');
|
||||
expect(unpaidCells[4]?.textContent).toBe('0 泥点');
|
||||
|
||||
const paidRow = screen.getByText('order-paid').closest('tr') as HTMLElement;
|
||||
const paidCells = within(paidRow).getAllByRole('cell');
|
||||
expect(paidCells[3]?.textContent).toBe('¥6.00');
|
||||
expect(paidCells[4]?.textContent).toBe('60 泥点');
|
||||
});
|
||||
|
||||
test('充值订单查询传递全部筛选字段', async () => {
|
||||
const user = userEvent.setup();
|
||||
renderPage();
|
||||
|
||||
@@ -658,7 +658,8 @@ export function AdminRechargeOrderPage({
|
||||
<th>用户</th>
|
||||
<th>订单</th>
|
||||
<th>支付</th>
|
||||
<th>金额 / 泥点</th>
|
||||
<th>实付</th>
|
||||
<th>发放泥点</th>
|
||||
<th>退款与追回</th>
|
||||
<th>钱包</th>
|
||||
<th>状态</th>
|
||||
@@ -715,7 +716,7 @@ export function AdminRechargeOrderPage({
|
||||
<div className="admin-refund-summary-grid">
|
||||
<Metric
|
||||
label="订单实付"
|
||||
value={formatMoney(refundOrder.amountCents)}
|
||||
value={formatMoney(refundOrder.paidAmountCents)}
|
||||
/>
|
||||
<Metric
|
||||
label="累计已退"
|
||||
@@ -1118,8 +1119,13 @@ function RechargeOrderRow({
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
<strong>{formatMoney(order.amountCents)}</strong>
|
||||
<small>发放 {order.pointsDelta} 泥点</small>
|
||||
<strong>{formatOrderPaidAmount(order)}</strong>
|
||||
{order.paidAmountCents > 0 ? null : (
|
||||
<small>订单 {formatMoney(order.amountCents)}</small>
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
<span>{order.pointsDelta} 泥点</span>
|
||||
</td>
|
||||
<td>
|
||||
<span>累计 {formatMoney(order.cumulativeSuccessRefundCents)}</span>
|
||||
@@ -1312,6 +1318,13 @@ function formatMoney(cents: number) {
|
||||
return `¥${(cents / 100).toFixed(2)}`;
|
||||
}
|
||||
|
||||
/** 实付只属于真正支付过的订单:未支付 / 已关闭 / 已过期订单显示“未支付”。 */
|
||||
function formatOrderPaidAmount(order: AdminRechargeOrderEntryPayload) {
|
||||
return order.paidAmountCents > 0
|
||||
? formatMoney(order.paidAmountCents)
|
||||
: '未支付';
|
||||
}
|
||||
|
||||
function formatCentsInput(cents: number) {
|
||||
return (cents / 100).toFixed(2);
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ export function AdminRedeemCodePage({
|
||||
|
||||
<div className="admin-form-row">
|
||||
<label className="admin-field">
|
||||
<span>奖励点数</span>
|
||||
<span>奖励泥点</span>
|
||||
<input
|
||||
min={1}
|
||||
step={1}
|
||||
@@ -319,7 +319,7 @@ export function AdminRedeemCodePage({
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Code</th>
|
||||
<th>奖励</th>
|
||||
<th>奖励泥点</th>
|
||||
<th>状态</th>
|
||||
<th>有效期</th>
|
||||
</tr>
|
||||
@@ -337,7 +337,7 @@ export function AdminRedeemCodePage({
|
||||
</button>
|
||||
<small>{redeemModeLabel(entry.mode)}</small>
|
||||
</td>
|
||||
<td>{entry.rewardPoints}</td>
|
||||
<td>{entry.rewardPoints} 泥点</td>
|
||||
<td>
|
||||
<span
|
||||
className={`admin-status ${redeemValidityClass(entry)}`}
|
||||
|
||||
@@ -15,20 +15,10 @@
|
||||
"skill-pack:test": "node --test scripts/check-skill-pack.test.mjs",
|
||||
"llm-status": "node scripts/run-cli-with-config.mjs --llm-status",
|
||||
"agent-task": "node scripts/run-cli-with-config.mjs --agent-task",
|
||||
"chat": "node scripts/run-cli-with-config.mjs --swarm-chat",
|
||||
"swarm": "node scripts/run-cli-with-config.mjs --swarm-chat",
|
||||
"config": "node scripts/game-creator-config-wizard.mjs",
|
||||
"test:chat": "node scripts/agent-swarm-test-chat.mjs --task \"制作一个可直接试玩的原创植物塔防小游戏:玩家选择并放置原创守卫阻挡敌人,完成波次后可以进入下一关并重新开始。主题、单位名称与视觉语言必须原创,不使用任何现有游戏角色、单位名、Logo 或受保护视觉语言。请自主完成正式产物、静态检查和双视口试玩验证。\" --no-open",
|
||||
"test:chat:manual": "node scripts/agent-swarm-test-chat.mjs",
|
||||
"agent-run": "node scripts/run-cli-with-config.mjs --agent-run",
|
||||
"agent-run:smoke": "node scripts/smoke-agent-run-local-provider.mjs",
|
||||
"agent-runtime:real-e2e": "node scripts/agent-runtime-real-e2e.mjs",
|
||||
"agent-runtime:collaboration-policy-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-collaboration-policy-mixed-recovery",
|
||||
"agent-runtime:mixed-swarm-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-static-isolated-autonomous-chat",
|
||||
"agent-runtime:supervisor-swarm-autonomous-chat-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-autonomous-chat",
|
||||
"agent-runtime:supervisor-autonomous-playable-lane-defense-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-autonomous-playable-lane-defense",
|
||||
"agent-runtime:supervisor-autonomous-playable-lane-defense-deterministic-e2e": "node scripts/agent-runtime-deterministic-playable-e2e.mjs",
|
||||
"agent-runtime:supervisor-autonomous-playable-lane-defense-deterministic-self-test": "node scripts/agent-runtime-deterministic-playable-e2e.mjs --self-test",
|
||||
"agent-runtime:supervisor-swarm-transient-retry-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-transient-retry",
|
||||
"agent-runtime:supervisor-swarm-final-reply-transient-retry-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-final-reply-transient-retry",
|
||||
"agent-runtime:supervisor-swarm-tool-plan-handoff-runner-kill-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-tool-plan-handoff-runner-kill",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,69 +1,8 @@
|
||||
import { createHash } from '../dependencies.mjs';
|
||||
import { isPlainObject } from '../harness/config.mjs';
|
||||
import {
|
||||
interactiveCliOutput,
|
||||
writeInteractiveCliLine,
|
||||
} from '../harness/process.mjs';
|
||||
import {
|
||||
shutdownWaiters,
|
||||
state,
|
||||
userInputAnswerText,
|
||||
} from '../runtime-state.mjs';
|
||||
import { shutdownWaiters, state } from '../runtime-state.mjs';
|
||||
import { disposableProjectPathVariants } from './runtime.mjs';
|
||||
|
||||
export async function answerRemainingInteractiveQuestions(session) {
|
||||
let answeredPromptCount = 1;
|
||||
const deadline = Date.now() + 60_000;
|
||||
while (Date.now() < deadline) {
|
||||
const output = interactiveCliOutput(session);
|
||||
if (output.includes(`[\u5df2\u56de\u7b54] ${state.userInput.requestId}`))
|
||||
return;
|
||||
const promptCount = output.split('或直接输入其他答案:').length - 1;
|
||||
while (answeredPromptCount < promptCount && answeredPromptCount < 3) {
|
||||
writeInteractiveCliLine(session, userInputAnswerText);
|
||||
answeredPromptCount += 1;
|
||||
}
|
||||
if (output.includes('[待确认]')) {
|
||||
throw codedError('user-input-unexpected-tool-confirmation');
|
||||
}
|
||||
if (session.closed) throw codedError('user-input-cli-closed-before-answer');
|
||||
await sleep(100);
|
||||
}
|
||||
throw codedError('user-input-answer-timeout');
|
||||
}
|
||||
|
||||
export function parseSingleSwarmTurnReport(output, codePrefix) {
|
||||
const reportLines = output
|
||||
.split(/\r?\n/u)
|
||||
.filter((line) => line.startsWith('[turn.report] '));
|
||||
assert(reportLines.length === 1, `${codePrefix}-turn-report-count-invalid`);
|
||||
const report = JSON.parse(reportLines[0].slice('[turn.report] '.length));
|
||||
assert(
|
||||
isPlainObject(report) &&
|
||||
JSON.stringify(Object.keys(report).sort()) ===
|
||||
JSON.stringify(
|
||||
[
|
||||
'schemaVersion',
|
||||
'outcome',
|
||||
'parentAgentId',
|
||||
'sessionId',
|
||||
'parentRunId',
|
||||
'runtimeCount',
|
||||
'busyRuntimeCount',
|
||||
'pendingTaskCount',
|
||||
'runningTaskCount',
|
||||
'waitingForConfirmationCount',
|
||||
'waitingForUserInputCount',
|
||||
'newAssistantMessageCount',
|
||||
'finalReplyChars',
|
||||
'reconciliationAgentCount',
|
||||
].sort(),
|
||||
),
|
||||
`${codePrefix}-turn-report-shape-invalid`,
|
||||
);
|
||||
return report;
|
||||
}
|
||||
|
||||
export function isFailedTask(task) {
|
||||
return (
|
||||
['failed', 'cancelled', 'budget-exhausted'].includes(task.status) ||
|
||||
|
||||
@@ -2419,24 +2419,6 @@ export function finalMessageId(agentId, sessionId, runId) {
|
||||
).slice(0, 32)}`;
|
||||
}
|
||||
|
||||
export function runtimePublicStatusMessageId(
|
||||
agentId,
|
||||
sessionId,
|
||||
runId,
|
||||
status,
|
||||
) {
|
||||
const correlationId = runtimeMessageCorrelationId(
|
||||
agentId,
|
||||
sessionId,
|
||||
runId,
|
||||
).slice(0, 32);
|
||||
const statusFingerprint = createHash('sha256')
|
||||
.update(status)
|
||||
.digest('hex')
|
||||
.slice(0, 16);
|
||||
return `runtime-public-status-${correlationId}-${statusFingerprint}`;
|
||||
}
|
||||
|
||||
export function backgroundTaskMessageId(agentId, sessionId, runId, source) {
|
||||
const fingerprint = createHash('sha256')
|
||||
.update(`${agentId}\n${sessionId}\n${runId}\n${source}`)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -37,26 +37,16 @@ import {
|
||||
state,
|
||||
steerRunnerKillAppDataSentinelFileName,
|
||||
steerRunnerKillAppDataSentinelSchema,
|
||||
supervisorAutonomousPlayableAppDataSentinelFileName,
|
||||
supervisorAutonomousPlayableAppDataSentinelSchema,
|
||||
supervisorSwarmAppDataSentinelFileName,
|
||||
supervisorSwarmAppDataSentinelSchema,
|
||||
supervisorSwarmAutonomousChatAppDataSentinelFileName,
|
||||
supervisorSwarmAutonomousChatAppDataSentinelSchema,
|
||||
supervisorSwarmCollaborationPolicyAppDataSentinelFileName,
|
||||
supervisorSwarmCollaborationPolicyAppDataSentinelSchema,
|
||||
supervisorSwarmFinalReplyTransientRetryAppDataSentinelFileName,
|
||||
supervisorSwarmFinalReplyTransientRetryAppDataSentinelSchema,
|
||||
supervisorSwarmRequiredAgentIds,
|
||||
supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelFileName,
|
||||
supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelSchema,
|
||||
supervisorSwarmToolPlanHandoffRunnerKillAppDataSentinelFileName,
|
||||
supervisorSwarmToolPlanHandoffRunnerKillAppDataSentinelSchema,
|
||||
supervisorSwarmTransientRetryAppDataSentinelFileName,
|
||||
supervisorSwarmTransientRetryAppDataSentinelSchema,
|
||||
supervisorSwarmTransientRetryTargetAgentId,
|
||||
userInputAppDataSentinelFileName,
|
||||
userInputAppDataSentinelSchema,
|
||||
webSearchAppDataSentinelFileName,
|
||||
webSearchAppDataSentinelSchema,
|
||||
windowsProcessHandleHelperSource,
|
||||
@@ -68,21 +58,14 @@ import { isProjectSkillSuite } from '../suites/project-skill.mjs';
|
||||
import { isResponseStreamSuite } from '../suites/response-stream.mjs';
|
||||
import { isScopedAgentsSuite } from '../suites/scoped-agents.mjs';
|
||||
import { isSteerRunnerKillSuite } from '../suites/steer-runner-kill.mjs';
|
||||
import { isSupervisorAutonomousPlayableLaneDefenseSuite } from '../suites/supervisor-autonomous-playable.mjs';
|
||||
import {
|
||||
isSupervisorSwarmAutonomousChatSuite,
|
||||
isSupervisorSwarmCollaborationPolicyMixedRecoverySuite,
|
||||
isSupervisorSwarmFinalReplyTransientRetrySuite,
|
||||
isSupervisorSwarmInitialTransientRetrySuite,
|
||||
isSupervisorSwarmInteractiveChatSuite,
|
||||
isSupervisorSwarmMixedHarnessSuite,
|
||||
isSupervisorSwarmStaticIsolatedAutonomousChatSuite,
|
||||
isSupervisorSwarmSuite,
|
||||
isSupervisorSwarmToolPlanHandoffRunnerKillSuite,
|
||||
isSupervisorSwarmTransientRetrySuite,
|
||||
rebuildSupervisorSwarmTranscriptScanner,
|
||||
} from '../suites/supervisor-swarm.mjs';
|
||||
import { isUserInputRuntimeSuite } from '../suites/user-input.mjs';
|
||||
import {
|
||||
isWebSearchSuite,
|
||||
sameEffectiveAgentLlmWithoutWebSearch,
|
||||
@@ -100,19 +83,10 @@ import { appendBounded, runProcess } from './process.mjs';
|
||||
import { decodeUtf8Fatal, isIsolatedRunnerSuite } from './reporting.mjs';
|
||||
import { killRunnerOnce, readRunnerStatus, runnerBootId } from './runtime.mjs';
|
||||
|
||||
const platformSessionFixtureEnv = 'GENARRATIVE_AGC_PLATFORM_SESSION_FIXTURE';
|
||||
const platformSessionFixtureMaxBytes = 16 * 1024;
|
||||
const isolatedPlatformSessionFixtureName =
|
||||
'.deterministic-platform-session.json';
|
||||
const platformSessionFixtureSchema =
|
||||
'genarrative-agc-platform-session-fixture.v1';
|
||||
|
||||
export function isolatedSuiteProtectsSourceAppData() {
|
||||
return (
|
||||
isSupervisorSwarmTransientRetrySuite() ||
|
||||
isSupervisorSwarmToolPlanHandoffRunnerKillSuite() ||
|
||||
isSupervisorAutonomousPlayableLaneDefenseSuite() ||
|
||||
isSupervisorSwarmInteractiveChatSuite()
|
||||
isSupervisorSwarmToolPlanHandoffRunnerKillSuite()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -120,27 +94,6 @@ export function isolatedSuiteUsesSiblingAppData() {
|
||||
return isWebSearchSuite() || isolatedSuiteProtectsSourceAppData();
|
||||
}
|
||||
|
||||
export function sameSupervisorPlayableProviderBinding(left, right) {
|
||||
const scalarFields = [
|
||||
'providerAgentMode',
|
||||
'providerModel',
|
||||
'providerApiKind',
|
||||
'providerReasoningEffort',
|
||||
'providerBaseUrlSha256',
|
||||
];
|
||||
return (
|
||||
left != null &&
|
||||
right != null &&
|
||||
scalarFields.every((field) => left[field] === right[field]) &&
|
||||
Array.isArray(left.boundAgentIds) &&
|
||||
Array.isArray(right.boundAgentIds) &&
|
||||
left.boundAgentIds.length === right.boundAgentIds.length &&
|
||||
left.boundAgentIds.every(
|
||||
(agentId, index) => agentId === right.boundAgentIds[index],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function isolatedSuiteAppDataProfile() {
|
||||
if (isSteerRunnerKillSuite()) {
|
||||
return {
|
||||
@@ -174,14 +127,6 @@ export function isolatedSuiteAppDataProfile() {
|
||||
codePrefix: 'context-compaction-appdata',
|
||||
};
|
||||
}
|
||||
if (isUserInputRuntimeSuite()) {
|
||||
return {
|
||||
prefix: '.agent-runtime-real-e2e-user-input-',
|
||||
sentinelName: userInputAppDataSentinelFileName,
|
||||
sentinelSchema: userInputAppDataSentinelSchema,
|
||||
codePrefix: 'user-input-appdata',
|
||||
};
|
||||
}
|
||||
if (isScopedAgentsSuite()) {
|
||||
return {
|
||||
prefix: '.agent-runtime-real-e2e-scoped-agents-',
|
||||
@@ -236,41 +181,6 @@ export function isolatedSuiteAppDataProfile() {
|
||||
codePrefix: 'supervisor-swarm-transient-retry-appdata',
|
||||
};
|
||||
}
|
||||
if (isSupervisorAutonomousPlayableLaneDefenseSuite()) {
|
||||
return {
|
||||
prefix: '.agent-runtime-real-e2e-supervisor-autonomous-playable-',
|
||||
sentinelName: supervisorAutonomousPlayableAppDataSentinelFileName,
|
||||
sentinelSchema: supervisorAutonomousPlayableAppDataSentinelSchema,
|
||||
codePrefix: 'supervisor-autonomous-playable-appdata',
|
||||
};
|
||||
}
|
||||
if (isSupervisorSwarmAutonomousChatSuite()) {
|
||||
return {
|
||||
prefix: '.agent-runtime-real-e2e-supervisor-swarm-autonomous-chat-',
|
||||
sentinelName: supervisorSwarmAutonomousChatAppDataSentinelFileName,
|
||||
sentinelSchema: supervisorSwarmAutonomousChatAppDataSentinelSchema,
|
||||
codePrefix: 'supervisor-swarm-autonomous-chat-appdata',
|
||||
};
|
||||
}
|
||||
if (isSupervisorSwarmStaticIsolatedAutonomousChatSuite()) {
|
||||
return {
|
||||
prefix:
|
||||
'.agent-runtime-real-e2e-supervisor-swarm-static-isolated-autonomous-chat-',
|
||||
sentinelName:
|
||||
supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelFileName,
|
||||
sentinelSchema:
|
||||
supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelSchema,
|
||||
codePrefix: 'supervisor-swarm-static-isolated-autonomous-chat-appdata',
|
||||
};
|
||||
}
|
||||
if (isSupervisorSwarmCollaborationPolicyMixedRecoverySuite()) {
|
||||
return {
|
||||
prefix: '.agent-runtime-real-e2e-supervisor-swarm-collaboration-policy-',
|
||||
sentinelName: supervisorSwarmCollaborationPolicyAppDataSentinelFileName,
|
||||
sentinelSchema: supervisorSwarmCollaborationPolicyAppDataSentinelSchema,
|
||||
codePrefix: 'supervisor-swarm-collaboration-policy-appdata',
|
||||
};
|
||||
}
|
||||
if (isSupervisorSwarmSuite()) {
|
||||
return {
|
||||
prefix: '.agent-runtime-real-e2e-supervisor-swarm-',
|
||||
@@ -505,120 +415,6 @@ export async function verifySourceAppDataDirectoryUntouched() {
|
||||
state.isolatedRunner.sourceAppDataDirectoryUntouched = true;
|
||||
}
|
||||
|
||||
async function readPlatformSessionFixtureForIsolatedSuite(sourceConfigDir) {
|
||||
const rawPath = process.env[platformSessionFixtureEnv];
|
||||
assert(
|
||||
isNonEmptyString(rawPath) && path.isAbsolute(rawPath),
|
||||
'supervisor-autonomous-playable-platform-session-fixture-missing',
|
||||
);
|
||||
const sourceRealPath = await fs.realpath(sourceConfigDir);
|
||||
const requestedPath = path.resolve(rawPath);
|
||||
const requestedMetadata = await fs.lstat(requestedPath).catch((error) => {
|
||||
if (error?.code === 'ENOENT') return null;
|
||||
throw error;
|
||||
});
|
||||
assert(
|
||||
requestedMetadata?.isFile() && !requestedMetadata.isSymbolicLink(),
|
||||
'supervisor-autonomous-playable-platform-session-fixture-not-regular',
|
||||
);
|
||||
assert(
|
||||
requestedMetadata.size <= platformSessionFixtureMaxBytes,
|
||||
'supervisor-autonomous-playable-platform-session-fixture-too-large',
|
||||
);
|
||||
const realPath = await fs.realpath(requestedPath);
|
||||
assert(
|
||||
isPathInside(sourceRealPath, realPath),
|
||||
'supervisor-autonomous-playable-platform-session-fixture-outside-config',
|
||||
);
|
||||
const bytes = await fs.readFile(realPath);
|
||||
assert(
|
||||
bytes.length <= platformSessionFixtureMaxBytes,
|
||||
'supervisor-autonomous-playable-platform-session-fixture-too-large',
|
||||
);
|
||||
let fixture;
|
||||
try {
|
||||
fixture = JSON.parse(
|
||||
decodeUtf8Fatal(bytes, 'platform-session-fixture-invalid-utf8'),
|
||||
);
|
||||
} catch (error) {
|
||||
throw codedError(
|
||||
'supervisor-autonomous-playable-platform-session-fixture-invalid',
|
||||
error,
|
||||
);
|
||||
}
|
||||
const expectedKeys = [
|
||||
'schemaVersion',
|
||||
'userId',
|
||||
'accessToken',
|
||||
'apiBaseUrl',
|
||||
'generation',
|
||||
];
|
||||
assert(
|
||||
isPlainObject(fixture) &&
|
||||
JSON.stringify(Object.keys(fixture).sort()) ===
|
||||
JSON.stringify([...expectedKeys].sort()) &&
|
||||
fixture.schemaVersion === platformSessionFixtureSchema &&
|
||||
isNonEmptyString(fixture.userId) &&
|
||||
isNonEmptyString(fixture.accessToken) &&
|
||||
isNonEmptyString(fixture.apiBaseUrl) &&
|
||||
Number.isSafeInteger(fixture.generation) &&
|
||||
fixture.generation > 0,
|
||||
'supervisor-autonomous-playable-platform-session-fixture-invalid',
|
||||
);
|
||||
return {
|
||||
sourcePath: realPath,
|
||||
bytes,
|
||||
fixture,
|
||||
sha256: createHash('sha256').update(bytes).digest('hex'),
|
||||
};
|
||||
}
|
||||
|
||||
async function installPlatformSessionFixtureIntoIsolatedAppData(
|
||||
sourceConfigDir,
|
||||
appDataDir,
|
||||
) {
|
||||
if (!isSupervisorAutonomousPlayableLaneDefenseSuite()) return;
|
||||
const source =
|
||||
await readPlatformSessionFixtureForIsolatedSuite(sourceConfigDir);
|
||||
const isolatedPath = path.join(
|
||||
appDataDir,
|
||||
isolatedPlatformSessionFixtureName,
|
||||
);
|
||||
await fs.copyFile(
|
||||
source.sourcePath,
|
||||
isolatedPath,
|
||||
fsConstants.COPYFILE_EXCL | fsConstants.COPYFILE_FICLONE,
|
||||
);
|
||||
await fs.chmod(isolatedPath, 0o600).catch(() => {});
|
||||
const isolatedMetadata = await fs.lstat(isolatedPath);
|
||||
assert(
|
||||
isolatedMetadata.isFile() &&
|
||||
!isolatedMetadata.isSymbolicLink() &&
|
||||
isolatedMetadata.size === source.bytes.length,
|
||||
'supervisor-autonomous-playable-platform-session-fixture-copy-invalid',
|
||||
);
|
||||
const isolatedBytes = await fs.readFile(isolatedPath);
|
||||
assert(
|
||||
createHash('sha256').update(isolatedBytes).digest('hex') === source.sha256,
|
||||
'supervisor-autonomous-playable-platform-session-fixture-copy-mismatch',
|
||||
);
|
||||
state.isolatedRunner.platformSessionFixtureSourcePath = source.sourcePath;
|
||||
state.isolatedRunner.platformSessionFixturePath = isolatedPath;
|
||||
state.isolatedRunner.platformSessionFixtureSha256 = source.sha256;
|
||||
state.isolatedRunner.platformSessionFixturePreviousEnv =
|
||||
Object.prototype.hasOwnProperty.call(process.env, platformSessionFixtureEnv)
|
||||
? process.env[platformSessionFixtureEnv]
|
||||
: undefined;
|
||||
process.env[platformSessionFixtureEnv] = isolatedPath;
|
||||
state.formalConfigPathTranscriptScanner?.addSecrets(
|
||||
absolutePathVariants(source.sourcePath, isolatedPath),
|
||||
);
|
||||
const previousLeakCount = state.transcriptScanner?.count ?? 0;
|
||||
state.secrets = [...new Set([...state.secrets, source.fixture.accessToken])];
|
||||
rebuildSupervisorSwarmTranscriptScanner();
|
||||
state.transcriptScanner.count = previousLeakCount;
|
||||
}
|
||||
|
||||
export async function prepareIsolatedSuiteAppData({
|
||||
streamAgentId = null,
|
||||
webSearchAgentId = null,
|
||||
@@ -773,8 +569,7 @@ export async function prepareIsolatedSuiteAppData({
|
||||
isScopedAgentsSuite() ||
|
||||
isProjectSkillSuite() ||
|
||||
isParallelReadSuite() ||
|
||||
isSupervisorSwarmSuite() ||
|
||||
isSupervisorAutonomousPlayableLaneDefenseSuite()
|
||||
isSupervisorSwarmSuite()
|
||||
? 'private-copy'
|
||||
: 'hardlink';
|
||||
try {
|
||||
@@ -891,10 +686,6 @@ export async function prepareIsolatedSuiteAppData({
|
||||
state.secrets = [...suiteSecrets];
|
||||
rebuildSupervisorSwarmTranscriptScanner();
|
||||
state.transcriptScanner.count = previousLeakCount;
|
||||
await installPlatformSessionFixtureIntoIsolatedAppData(
|
||||
sourceConfigDir,
|
||||
appDataDir,
|
||||
);
|
||||
const unexpectedEndpoint = await fs
|
||||
.lstat(path.join(appDataDir, runnerEndpointFileName))
|
||||
.catch((error) => {
|
||||
@@ -903,46 +694,6 @@ export async function prepareIsolatedSuiteAppData({
|
||||
});
|
||||
assert(!unexpectedEndpoint, `${profile.codePrefix}-endpoint-preexisted`);
|
||||
state.runtimeConfigDir = appDataDir;
|
||||
if (isSupervisorAutonomousPlayableLaneDefenseSuite()) {
|
||||
const isolatedConfig = await loadConfig(appDataDir);
|
||||
const expectedBinding = state.config.providerBinding;
|
||||
const agentIds = [projectSupervisorAgentId];
|
||||
const bindings = agentIds.map((agentId) => {
|
||||
const effective = effectiveAgentLlmConfig(isolatedConfig.config, agentId);
|
||||
assert(
|
||||
['apiKey', 'baseUrl', 'model', 'apiKind', 'reasoningEffort'].every(
|
||||
(key) => isNonEmptyString(effective[key]),
|
||||
),
|
||||
'supervisor-autonomous-playable-effective-provider-incomplete',
|
||||
);
|
||||
return {
|
||||
providerModel: effective.model.trim(),
|
||||
providerApiKind: effective.apiKind.trim(),
|
||||
providerReasoningEffort: effective.reasoningEffort.trim(),
|
||||
providerBaseUrlSha256: hashValue(effective.baseUrl.trim()),
|
||||
};
|
||||
});
|
||||
const effectiveBinding = {
|
||||
...bindings[0],
|
||||
boundAgentIds: [...agentIds].sort(),
|
||||
};
|
||||
assert(
|
||||
expectedBinding &&
|
||||
bindings.every(
|
||||
(binding) => JSON.stringify(binding) === JSON.stringify(bindings[0]),
|
||||
) &&
|
||||
sameSupervisorPlayableProviderBinding(
|
||||
effectiveBinding,
|
||||
expectedBinding,
|
||||
),
|
||||
'supervisor-autonomous-playable-effective-provider-binding-mismatch',
|
||||
);
|
||||
state.supervisorAutonomousPlayable.expectedProviderBinding = {
|
||||
...expectedBinding,
|
||||
};
|
||||
state.supervisorAutonomousPlayable.effectiveProviderBinding =
|
||||
effectiveBinding;
|
||||
}
|
||||
if (streamAgentId) {
|
||||
const isolatedConfig = await loadConfig(appDataDir);
|
||||
const isolatedEffective = effectiveAgentLlmConfig(
|
||||
@@ -981,22 +732,6 @@ export async function prepareIsolatedSuiteAppData({
|
||||
);
|
||||
state.webSearch.effectiveEnabled = true;
|
||||
}
|
||||
if (isUserInputRuntimeSuite()) {
|
||||
const isolatedConfig = await loadConfig(appDataDir);
|
||||
const isolatedEffective = effectiveAgentLlmConfig(
|
||||
isolatedConfig.config,
|
||||
projectSupervisorAgentId,
|
||||
);
|
||||
assert(
|
||||
isolatedEffective.model === 'gpt-5.5' &&
|
||||
['apiKey', 'baseUrl', 'model'].every(
|
||||
(key) =>
|
||||
typeof isolatedEffective[key] === 'string' &&
|
||||
isolatedEffective[key].trim().length > 0,
|
||||
),
|
||||
'user-input-effective-gpt-5-5-config-invalid',
|
||||
);
|
||||
}
|
||||
if (isScopedAgentsSuite()) {
|
||||
const isolatedConfig = await loadConfig(appDataDir);
|
||||
const isolatedEffective = effectiveAgentLlmConfig(
|
||||
@@ -1073,9 +808,8 @@ export async function prepareIsolatedSuiteAppData({
|
||||
effective.requestTimeoutMs > 0 &&
|
||||
Number.isSafeInteger(effective.maxRetries) &&
|
||||
effective.maxRetries >=
|
||||
(isSupervisorSwarmInteractiveChatSuite() ||
|
||||
(isSupervisorSwarmTransientRetrySuite() &&
|
||||
agentId !== supervisorSwarmTransientRetryTargetAgentId)
|
||||
agentId !== supervisorSwarmTransientRetryTargetAgentId
|
||||
? 0
|
||||
: 1) &&
|
||||
effective.maxRetries <= 3 &&
|
||||
@@ -1095,24 +829,6 @@ export async function prepareIsolatedSuiteAppData({
|
||||
{ llm: isolatedConfig.config.llm },
|
||||
'',
|
||||
);
|
||||
if (isSupervisorSwarmMixedHarnessSuite()) {
|
||||
assert(
|
||||
globalEffective.model === 'gpt-5.5' &&
|
||||
globalEffective.apiKind === 'openai_chat' &&
|
||||
isNonEmptyString(globalEffective.reasoningEffort) &&
|
||||
Number.isSafeInteger(globalEffective.requestTimeoutMs) &&
|
||||
globalEffective.requestTimeoutMs > 0 &&
|
||||
Number.isSafeInteger(globalEffective.maxRetries) &&
|
||||
globalEffective.maxRetries >= 0 &&
|
||||
globalEffective.maxRetries <= 3 &&
|
||||
Number.isSafeInteger(globalEffective.retryBackoffMs) &&
|
||||
globalEffective.retryBackoffMs > 0 &&
|
||||
['apiKey', 'baseUrl', 'model'].every((key) =>
|
||||
isNonEmptyString(globalEffective[key]),
|
||||
),
|
||||
'supervisor-swarm-mixed-default-provider-policy-invalid',
|
||||
);
|
||||
}
|
||||
const configuredAgentIds = Object.keys(
|
||||
isPlainObject(isolatedConfig.config.agentLlm)
|
||||
? isolatedConfig.config.agentLlm
|
||||
@@ -2000,31 +1716,6 @@ export async function verifyIsolatedSuiteConfigLinksUnchanged() {
|
||||
'isolated-source-config-changed-during-suite',
|
||||
);
|
||||
}
|
||||
const fixture = state.isolatedRunner;
|
||||
if (
|
||||
fixture.platformSessionFixtureSourcePath &&
|
||||
fixture.platformSessionFixturePath &&
|
||||
fixture.platformSessionFixtureSha256
|
||||
) {
|
||||
const [sourceMetadata, isolatedMetadata, sourceBytes, isolatedBytes] =
|
||||
await Promise.all([
|
||||
fs.lstat(fixture.platformSessionFixtureSourcePath),
|
||||
fs.lstat(fixture.platformSessionFixturePath),
|
||||
fs.readFile(fixture.platformSessionFixtureSourcePath),
|
||||
fs.readFile(fixture.platformSessionFixturePath),
|
||||
]);
|
||||
assert(
|
||||
sourceMetadata.isFile() &&
|
||||
!sourceMetadata.isSymbolicLink() &&
|
||||
isolatedMetadata.isFile() &&
|
||||
!isolatedMetadata.isSymbolicLink() &&
|
||||
createHash('sha256').update(sourceBytes).digest('hex') ===
|
||||
fixture.platformSessionFixtureSha256 &&
|
||||
createHash('sha256').update(isolatedBytes).digest('hex') ===
|
||||
fixture.platformSessionFixtureSha256,
|
||||
'supervisor-autonomous-playable-platform-session-fixture-changed',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function verifySourceConfigLinkCountsRestored() {
|
||||
@@ -2064,22 +1755,7 @@ export async function removeIsolatedSuiteAppData() {
|
||||
} catch (error) {
|
||||
ownershipError = error;
|
||||
}
|
||||
try {
|
||||
await fs.rm(appDataDir, { recursive: true, force: false });
|
||||
} finally {
|
||||
if (state.isolatedRunner.platformSessionFixtureSourcePath) {
|
||||
const previous = state.isolatedRunner.platformSessionFixturePreviousEnv;
|
||||
if (previous === undefined) {
|
||||
delete process.env[platformSessionFixtureEnv];
|
||||
} else {
|
||||
process.env[platformSessionFixtureEnv] = previous;
|
||||
}
|
||||
}
|
||||
state.isolatedRunner.platformSessionFixturePath = null;
|
||||
state.isolatedRunner.platformSessionFixtureSourcePath = null;
|
||||
state.isolatedRunner.platformSessionFixtureSha256 = null;
|
||||
state.isolatedRunner.platformSessionFixturePreviousEnv = undefined;
|
||||
}
|
||||
await fs.rm(appDataDir, { recursive: true, force: false });
|
||||
state.runtimeConfigDir = state.options.configDir;
|
||||
try {
|
||||
await verifySourceConfigLinkCountsRestored();
|
||||
|
||||
@@ -13,15 +13,10 @@ import {
|
||||
responseStreamSuite,
|
||||
scopedAgentsSuite,
|
||||
steerRunnerKillSuite,
|
||||
supervisorAutonomousPlayableLaneDefenseSuite,
|
||||
supervisorSwarmAutonomousChatSuite,
|
||||
supervisorSwarmCollaborationPolicyMixedRecoverySuite,
|
||||
supervisorSwarmFinalReplyTransientRetrySuite,
|
||||
supervisorSwarmStaticIsolatedAutonomousChatSuite,
|
||||
supervisorSwarmSuite,
|
||||
supervisorSwarmToolPlanHandoffRunnerKillSuite,
|
||||
supervisorSwarmTransientRetrySuite,
|
||||
userInputRuntimeSuite,
|
||||
webSearchSuite,
|
||||
} from '../runtime-state.mjs';
|
||||
import { collectApiKeys, isPathInside } from './io.mjs';
|
||||
@@ -58,7 +53,6 @@ export function parseArguments(args) {
|
||||
suite === responseStreamSuite ||
|
||||
suite === webSearchSuite ||
|
||||
suite === contextCompactionSuite ||
|
||||
suite === userInputRuntimeSuite ||
|
||||
suite === scopedAgentsSuite ||
|
||||
suite === projectSkillSuite ||
|
||||
suite === parallelReadSuite ||
|
||||
@@ -66,10 +60,6 @@ export function parseArguments(args) {
|
||||
suite === supervisorSwarmTransientRetrySuite ||
|
||||
suite === supervisorSwarmFinalReplyTransientRetrySuite ||
|
||||
suite === supervisorSwarmToolPlanHandoffRunnerKillSuite ||
|
||||
suite === supervisorSwarmAutonomousChatSuite ||
|
||||
suite === supervisorAutonomousPlayableLaneDefenseSuite ||
|
||||
suite === supervisorSwarmStaticIsolatedAutonomousChatSuite ||
|
||||
suite === supervisorSwarmCollaborationPolicyMixedRecoverySuite ||
|
||||
suite === steerRunnerKillSuite ||
|
||||
processSessionSuites.has(suite),
|
||||
'unsupported-suite',
|
||||
|
||||
@@ -2,7 +2,6 @@ import {
|
||||
assert,
|
||||
codedError,
|
||||
hashValue,
|
||||
sleep,
|
||||
throwIfShutdownRequested,
|
||||
} from '../assertions/core.mjs';
|
||||
import { fs, path, spawn, withLoopbackNoProxy } from '../dependencies.mjs';
|
||||
@@ -13,14 +12,9 @@ import {
|
||||
manifestPath,
|
||||
state,
|
||||
} from '../runtime-state.mjs';
|
||||
import {
|
||||
isSupervisorSwarmTransientRetrySuite,
|
||||
recordSupervisorSwarmChatSessionFailureDiagnostic,
|
||||
} from '../suites/supervisor-swarm.mjs';
|
||||
import { isSupervisorSwarmTransientRetrySuite } from '../suites/supervisor-swarm.mjs';
|
||||
import { isIsolatedRunnerSuite } from './reporting.mjs';
|
||||
|
||||
export const activeInteractiveCliSessions = new Set();
|
||||
|
||||
export async function prepareCliBinary() {
|
||||
const cargo = process.platform === 'win32' ? 'cargo.exe' : 'cargo';
|
||||
await runProcess(
|
||||
@@ -84,8 +78,8 @@ export function buildCliChildEnvironment() {
|
||||
NO_COLOR: '1',
|
||||
RUST_BACKTRACE: '0',
|
||||
};
|
||||
// The deterministic playable suite copies its account fixture into the
|
||||
// sibling isolated AppData directory. Set the path explicitly here so
|
||||
// The isolated runner copies its account fixture into the sibling isolated
|
||||
// AppData directory. Set the path explicitly here so
|
||||
// every CLI and the Runner it launches use the isolated copy, even if the
|
||||
// parent harness environment was restored or changed after setup.
|
||||
if (state.isolatedRunner.platformSessionFixturePath) {
|
||||
@@ -144,197 +138,6 @@ export function codedProcessError(code, details) {
|
||||
return error;
|
||||
}
|
||||
|
||||
export function startInteractiveCli(args) {
|
||||
assert(Boolean(state.cliBinary), 'interactive-cli-binary-not-ready');
|
||||
assert(Boolean(state.runtimeConfigDir), 'interactive-config-dir-not-ready');
|
||||
if (
|
||||
isIsolatedRunnerSuite() &&
|
||||
state.options?.configDir &&
|
||||
path.resolve(state.runtimeConfigDir) ===
|
||||
path.resolve(state.options.configDir)
|
||||
) {
|
||||
state.isolatedRunner.sourceConfigCliCallCount += 1;
|
||||
}
|
||||
const child = spawn(
|
||||
state.cliBinary,
|
||||
[...args, '--config-dir', state.runtimeConfigDir],
|
||||
{
|
||||
cwd: appRoot,
|
||||
env: buildCliChildEnvironment(),
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
},
|
||||
);
|
||||
return createInteractiveCliSession(child);
|
||||
}
|
||||
|
||||
export function createInteractiveCliSession(child) {
|
||||
activeCommandChildren.add(child);
|
||||
const session = {
|
||||
child,
|
||||
stdout: Buffer.alloc(0),
|
||||
stderr: Buffer.alloc(0),
|
||||
exited: false,
|
||||
exitInfo: null,
|
||||
exitPromise: null,
|
||||
closed: false,
|
||||
closeInfo: null,
|
||||
closePromise: null,
|
||||
stdioClosed: false,
|
||||
stdioCloseInfo: null,
|
||||
spawnError: null,
|
||||
stdinError: null,
|
||||
};
|
||||
activeInteractiveCliSessions.add(session);
|
||||
session.exitPromise = new Promise((resolve) => {
|
||||
const settle = (result) => {
|
||||
if (session.exited) return;
|
||||
activeCommandChildren.delete(child);
|
||||
session.exited = true;
|
||||
session.closed = true;
|
||||
session.exitInfo = result;
|
||||
session.closeInfo = result;
|
||||
resolve(result);
|
||||
};
|
||||
child.once('error', (error) => {
|
||||
session.spawnError = error;
|
||||
settle({ code: null, signal: null, error });
|
||||
});
|
||||
child.once('exit', (code, signal) => {
|
||||
settle({ code, signal, error: null });
|
||||
});
|
||||
});
|
||||
session.closePromise = new Promise((resolve) => {
|
||||
child.once('close', (code, signal) => {
|
||||
activeCommandChildren.delete(child);
|
||||
activeInteractiveCliSessions.delete(session);
|
||||
session.stdioClosed = true;
|
||||
session.stdioCloseInfo = {
|
||||
code,
|
||||
signal,
|
||||
error: session.spawnError,
|
||||
};
|
||||
resolve(session.stdioCloseInfo);
|
||||
});
|
||||
});
|
||||
child.stdin?.on('error', (error) => {
|
||||
session.stdinError ??= error;
|
||||
});
|
||||
child.stdout.on('data', (chunk) => {
|
||||
state.transcriptScanner?.scan('interactive-stdout', chunk);
|
||||
state.projectPathTranscriptScanner?.scan('interactive-stdout', chunk);
|
||||
state.formalConfigPathTranscriptScanner?.scan('interactive-stdout', chunk);
|
||||
session.stdout = appendBounded(session.stdout, chunk, commandOutputLimit);
|
||||
});
|
||||
child.stderr.on('data', (chunk) => {
|
||||
state.transcriptScanner?.scan('interactive-stderr', chunk);
|
||||
state.projectPathTranscriptScanner?.scan('interactive-stderr', chunk);
|
||||
state.formalConfigPathTranscriptScanner?.scan('interactive-stderr', chunk);
|
||||
session.stderr = appendBounded(session.stderr, chunk, commandOutputLimit);
|
||||
});
|
||||
return session;
|
||||
}
|
||||
|
||||
export function interactiveCliOutput(session) {
|
||||
return `${session.stdout.toString('utf8')}\n${session.stderr.toString('utf8')}`;
|
||||
}
|
||||
|
||||
export function writeInteractiveCliLine(session, line) {
|
||||
assert(!session.closed, 'interactive-cli-already-closed');
|
||||
assert(session.child.stdin.writable, 'interactive-cli-stdin-not-writable');
|
||||
session.child.stdin.write(`${line}\n`);
|
||||
}
|
||||
|
||||
export async function waitForInteractiveCliOutput(
|
||||
session,
|
||||
predicate,
|
||||
code,
|
||||
timeoutMs,
|
||||
{ allowAfterProcessExit = false } = {},
|
||||
) {
|
||||
const deadline = Date.now() + timeoutMs;
|
||||
while (Date.now() < deadline) {
|
||||
const output = interactiveCliOutput(session);
|
||||
if (predicate(output)) return output;
|
||||
if (session.exited && !allowAfterProcessExit) {
|
||||
if (session === state.supervisorSwarmCliSession) {
|
||||
recordSupervisorSwarmChatSessionFailureDiagnostic(session);
|
||||
}
|
||||
throw codedError(`${code}-cli-exited`);
|
||||
}
|
||||
if (session.stdioClosed) {
|
||||
if (session === state.supervisorSwarmCliSession) {
|
||||
recordSupervisorSwarmChatSessionFailureDiagnostic(session);
|
||||
}
|
||||
throw codedError(`${code}-cli-stdio-closed`);
|
||||
}
|
||||
await sleep(50);
|
||||
}
|
||||
throw codedError(code);
|
||||
}
|
||||
|
||||
export async function waitForInteractiveCliExit(session, timeoutMs) {
|
||||
const result = await Promise.race([
|
||||
session.exitPromise,
|
||||
sleep(timeoutMs).then(() => null),
|
||||
]);
|
||||
if (!result) throw codedError('interactive-cli-exit-timeout');
|
||||
if (result.error) throw codedError('interactive-cli-process-error');
|
||||
assert(
|
||||
result.code === 0 && result.signal === null,
|
||||
'interactive-cli-exit-invalid',
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function closeInteractiveCli(session) {
|
||||
if (!session) return null;
|
||||
if (session.exited) return session.exitInfo;
|
||||
if (
|
||||
session.child.stdin.writable &&
|
||||
!session.child.stdin.writableEnded &&
|
||||
!session.child.stdin.destroyed
|
||||
) {
|
||||
session.child.stdin.write('/quit\n');
|
||||
}
|
||||
let result = await Promise.race([
|
||||
session.exitPromise,
|
||||
sleep(3_000).then(() => null),
|
||||
]);
|
||||
if (!result && !session.exited) {
|
||||
session.child.kill('SIGTERM');
|
||||
result = await Promise.race([
|
||||
session.exitPromise,
|
||||
sleep(2_000).then(() => null),
|
||||
]);
|
||||
}
|
||||
if (!result && !session.exited) {
|
||||
session.child.kill('SIGKILL');
|
||||
result = await Promise.race([
|
||||
session.exitPromise,
|
||||
sleep(5_000).then(() => null),
|
||||
]);
|
||||
}
|
||||
assert(Boolean(result), 'interactive-cli-cleanup-timeout');
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function waitForInteractiveCliStdioClose(session, timeoutMs) {
|
||||
if (!session || session.stdioClosed) return session?.stdioCloseInfo ?? null;
|
||||
const result = await Promise.race([
|
||||
session.closePromise,
|
||||
sleep(timeoutMs).then(() => null),
|
||||
]);
|
||||
if (!result) throw codedError('interactive-cli-stdio-close-timeout');
|
||||
return result;
|
||||
}
|
||||
|
||||
export function destroyInteractiveCliOutputStreams(session) {
|
||||
if (!session) return;
|
||||
for (const stream of [session.child.stdout, session.child.stderr]) {
|
||||
if (stream && !stream.destroyed) stream.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
export async function runProcess(
|
||||
program,
|
||||
args,
|
||||
@@ -406,214 +209,6 @@ export async function runProcess(
|
||||
});
|
||||
}
|
||||
|
||||
export async function listSystemProcessIdentities() {
|
||||
if (process.platform === 'win32') {
|
||||
const systemRoot = process.env.SystemRoot ?? process.env.SYSTEMROOT;
|
||||
assert(
|
||||
typeof systemRoot === 'string' && path.isAbsolute(systemRoot),
|
||||
'owned-process-snapshot-system-root-invalid',
|
||||
);
|
||||
const powershell = path.join(
|
||||
systemRoot,
|
||||
'System32/WindowsPowerShell/v1.0/powershell.exe',
|
||||
);
|
||||
const metadata = await fs.lstat(powershell);
|
||||
assert(
|
||||
metadata.isFile() && !metadata.isSymbolicLink(),
|
||||
'owned-process-snapshot-powershell-invalid',
|
||||
);
|
||||
const result = await runProcess(
|
||||
powershell,
|
||||
[
|
||||
'-NoProfile',
|
||||
'-NonInteractive',
|
||||
'-Command',
|
||||
'$processes = @(Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,CreationDate,Name); $processes | ConvertTo-Json -Compress',
|
||||
],
|
||||
{
|
||||
cwd: appRoot,
|
||||
timeoutMs: 30_000,
|
||||
env: { ...process.env, NO_COLOR: '1', RUST_BACKTRACE: '0' },
|
||||
},
|
||||
);
|
||||
const parsed = JSON.parse(result.stdout);
|
||||
return (Array.isArray(parsed) ? parsed : [parsed])
|
||||
.map((record) => ({
|
||||
pid: Number(record?.ProcessId),
|
||||
parentPid: Number(record?.ParentProcessId),
|
||||
startedAt: String(record?.CreationDate ?? ''),
|
||||
name: String(record?.Name ?? ''),
|
||||
}))
|
||||
.filter(validSystemProcessIdentity);
|
||||
}
|
||||
assert(
|
||||
process.platform === 'linux' || process.platform === 'darwin',
|
||||
'owned-process-snapshot-platform-unsupported',
|
||||
);
|
||||
const result = await runProcess(
|
||||
'ps',
|
||||
['-A', '-o', 'pid=', '-o', 'ppid=', '-o', 'lstart=', '-o', 'comm='],
|
||||
{ cwd: appRoot, timeoutMs: 30_000 },
|
||||
);
|
||||
return result.stdout
|
||||
.split(/\r?\n/u)
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean)
|
||||
.map((line) => {
|
||||
const fields = line.split(/\s+/u);
|
||||
return {
|
||||
pid: Number(fields[0]),
|
||||
parentPid: Number(fields[1]),
|
||||
startedAt: fields.slice(2, 7).join(' '),
|
||||
name: fields.slice(7).join(' '),
|
||||
};
|
||||
})
|
||||
.filter(validSystemProcessIdentity);
|
||||
}
|
||||
|
||||
function validSystemProcessIdentity(record) {
|
||||
return (
|
||||
Number.isSafeInteger(record?.pid) &&
|
||||
record.pid > 0 &&
|
||||
Number.isSafeInteger(record.parentPid) &&
|
||||
record.parentPid >= 0 &&
|
||||
typeof record.startedAt === 'string' &&
|
||||
record.startedAt.length > 0 &&
|
||||
typeof record.name === 'string' &&
|
||||
record.name.length > 0
|
||||
);
|
||||
}
|
||||
|
||||
export function buildOwnedProcessCleanupSnapshot(
|
||||
processRecords,
|
||||
{ rootPids = [], runnerPid = null, helperPids = [] } = {},
|
||||
) {
|
||||
assert(
|
||||
Array.isArray(processRecords) &&
|
||||
Array.isArray(rootPids) &&
|
||||
Array.isArray(helperPids),
|
||||
'owned-process-snapshot-input-invalid',
|
||||
);
|
||||
const records = processRecords.filter(validSystemProcessIdentity);
|
||||
const byPid = new Map(records.map((record) => [record.pid, record]));
|
||||
const childrenByParent = new Map();
|
||||
for (const record of records) {
|
||||
const children = childrenByParent.get(record.parentPid) ?? [];
|
||||
children.push(record.pid);
|
||||
childrenByParent.set(record.parentPid, children);
|
||||
}
|
||||
const normalizedRunnerPid = Number.isSafeInteger(runnerPid)
|
||||
? runnerPid
|
||||
: null;
|
||||
const helperPidSet = new Set(
|
||||
helperPids.filter((pid) => Number.isSafeInteger(pid) && pid > 0),
|
||||
);
|
||||
const roots = [
|
||||
...new Set(
|
||||
[...rootPids, normalizedRunnerPid, ...helperPidSet].filter(
|
||||
(pid) => Number.isSafeInteger(pid) && pid > 0,
|
||||
),
|
||||
),
|
||||
];
|
||||
assert(roots.length > 0, 'owned-process-snapshot-root-missing');
|
||||
const ownedPids = new Set();
|
||||
const queue = [...roots];
|
||||
while (queue.length > 0) {
|
||||
const pid = queue.shift();
|
||||
if (ownedPids.has(pid)) continue;
|
||||
ownedPids.add(pid);
|
||||
queue.push(...(childrenByParent.get(pid) ?? []));
|
||||
}
|
||||
const identities = [...ownedPids]
|
||||
.map((pid) => byPid.get(pid))
|
||||
.filter(Boolean)
|
||||
.map((record) => ({
|
||||
pid: record.pid,
|
||||
startedAt: record.startedAt,
|
||||
name: record.name,
|
||||
kind: ownedProcessKind(record, normalizedRunnerPid, helperPidSet),
|
||||
}))
|
||||
.sort((left, right) => left.pid - right.pid);
|
||||
return {
|
||||
identities,
|
||||
observedCounts: countOwnedProcessKinds(identities),
|
||||
};
|
||||
}
|
||||
|
||||
function ownedProcessKind(record, runnerPid, helperPids) {
|
||||
if (record.pid === runnerPid) return 'runner';
|
||||
if (helperPids.has(record.pid)) return 'helper';
|
||||
const name = path.basename(record.name).toLowerCase();
|
||||
if (/^node(?:\.exe)?$/u.test(name)) return 'node';
|
||||
if (/^(?:chrome|chromium|msedge|google-chrome)(?:\.exe)?$/u.test(name)) {
|
||||
return 'browser';
|
||||
}
|
||||
return 'command';
|
||||
}
|
||||
|
||||
function countOwnedProcessKinds(identities) {
|
||||
const counts = {
|
||||
runner: 0,
|
||||
helper: 0,
|
||||
node: 0,
|
||||
browser: 0,
|
||||
command: 0,
|
||||
total: identities.length,
|
||||
};
|
||||
for (const identity of identities) counts[identity.kind] += 1;
|
||||
return counts;
|
||||
}
|
||||
|
||||
export function inspectOwnedProcessCleanupResiduals(
|
||||
snapshot,
|
||||
processRecords,
|
||||
{ activeCommandChildCount = 0, activeInteractiveCliSessionCount = 0 } = {},
|
||||
) {
|
||||
assert(
|
||||
Array.isArray(snapshot?.identities) && Array.isArray(processRecords),
|
||||
'owned-process-residual-input-invalid',
|
||||
);
|
||||
const currentByPid = new Map(
|
||||
processRecords
|
||||
.filter(validSystemProcessIdentity)
|
||||
.map((record) => [record.pid, record]),
|
||||
);
|
||||
const residualIdentities = snapshot.identities.filter((identity) => {
|
||||
const current = currentByPid.get(identity.pid);
|
||||
return (
|
||||
current?.startedAt === identity.startedAt &&
|
||||
current?.name === identity.name
|
||||
);
|
||||
});
|
||||
return {
|
||||
residualCounts: countOwnedProcessKinds(residualIdentities),
|
||||
activeCommandChildCount,
|
||||
activeInteractiveCliSessionCount,
|
||||
clean:
|
||||
residualIdentities.length === 0 &&
|
||||
activeCommandChildCount === 0 &&
|
||||
activeInteractiveCliSessionCount === 0,
|
||||
};
|
||||
}
|
||||
|
||||
export async function captureOwnedProcessCleanupSnapshot(options) {
|
||||
return buildOwnedProcessCleanupSnapshot(
|
||||
await listSystemProcessIdentities(),
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
export async function verifyOwnedProcessCleanupSnapshot(snapshot) {
|
||||
return inspectOwnedProcessCleanupResiduals(
|
||||
snapshot,
|
||||
await listSystemProcessIdentities(),
|
||||
{
|
||||
activeCommandChildCount: activeCommandChildren.size,
|
||||
activeInteractiveCliSessionCount: activeInteractiveCliSessions.size,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function appendBounded(current, chunk, limit) {
|
||||
const combined = Buffer.concat([current, chunk]);
|
||||
return combined.length <= limit
|
||||
|
||||
@@ -28,12 +28,10 @@ import {
|
||||
isGoalRuntimeSuite,
|
||||
} from '../suites/goal.mjs';
|
||||
import { isResponseStreamSuite } from '../suites/response-stream.mjs';
|
||||
import { isSupervisorAutonomousPlayableLaneDefenseSuite } from '../suites/supervisor-autonomous-playable.mjs';
|
||||
import {
|
||||
isSupervisorSwarmSuite,
|
||||
supervisorSwarmVerificationFixtureSource,
|
||||
} from '../suites/supervisor-swarm.mjs';
|
||||
import { isUserInputRuntimeSuite } from '../suites/user-input.mjs';
|
||||
import { isWebSearchSuite } from '../suites/web-search.mjs';
|
||||
import { createSentinelOwnedTempDirectory } from './app-data.mjs';
|
||||
import { effectiveAgentLlmConfig } from './config.mjs';
|
||||
@@ -41,19 +39,15 @@ import { runProcess } from './process.mjs';
|
||||
import { isIsolatedRunnerSuite } from './reporting.mjs';
|
||||
|
||||
export function requiredAgentIdsForSuite() {
|
||||
return isUserInputRuntimeSuite()
|
||||
? [projectSupervisorAgentId]
|
||||
: isSupervisorAutonomousPlayableLaneDefenseSuite()
|
||||
? [projectSupervisorAgentId]
|
||||
: isSupervisorSwarmSuite()
|
||||
? [
|
||||
projectSupervisorAgentId,
|
||||
supervisorSwarmDesignAgentId,
|
||||
supervisorSwarmQualityAgentId,
|
||||
]
|
||||
: isIsolatedRunnerSuite()
|
||||
? [mainAgentId]
|
||||
: [mainAgentId, 'quality-review'];
|
||||
return isSupervisorSwarmSuite()
|
||||
? [
|
||||
projectSupervisorAgentId,
|
||||
supervisorSwarmDesignAgentId,
|
||||
supervisorSwarmQualityAgentId,
|
||||
]
|
||||
: isIsolatedRunnerSuite()
|
||||
? [mainAgentId]
|
||||
: [mainAgentId, 'quality-review'];
|
||||
}
|
||||
|
||||
export function expectedProviderBindingForSuite(config) {
|
||||
@@ -120,11 +114,9 @@ export async function checkPrerequisites(config) {
|
||||
return {
|
||||
llmConfigured,
|
||||
providerBinding,
|
||||
chromeAvailable:
|
||||
!isIsolatedRunnerSuite() ||
|
||||
isSupervisorAutonomousPlayableLaneDefenseSuite()
|
||||
? Boolean(await findSupportedBrowser())
|
||||
: false,
|
||||
chromeAvailable: !isIsolatedRunnerSuite()
|
||||
? Boolean(await findSupportedBrowser())
|
||||
: false,
|
||||
editorApiConfigured,
|
||||
};
|
||||
}
|
||||
@@ -267,8 +259,7 @@ export async function seedDisposableProject({
|
||||
? supervisorSwarmVerificationFixtureSource()
|
||||
: isGoalRuntimeSuite() ||
|
||||
isResponseStreamSuite() ||
|
||||
isWebSearchSuite() ||
|
||||
isSupervisorAutonomousPlayableLaneDefenseSuite()
|
||||
isWebSearchSuite()
|
||||
? goalRevisionOneVerificationFixtureSource()
|
||||
: goalRevisionTwoVerificationFixtureSource(),
|
||||
),
|
||||
@@ -405,23 +396,6 @@ export function seededGameHtml() {
|
||||
`;
|
||||
}
|
||||
|
||||
export function productionDefaultGameIndexHtml() {
|
||||
return `<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Genarrative Game Draft</title>
|
||||
<style>
|
||||
body { margin: 0; display: grid; min-height: 100vh; place-items: center; background: #101827; color: #d9e7ff; font: 16px system-ui, sans-serif; }
|
||||
main { width: min(720px, calc(100vw - 32px)); }
|
||||
</style>
|
||||
</head>
|
||||
<body><main>还没有生成游戏。回到聊天输入创意并确认生成后,这里会写入可试玩原型。</main></body>
|
||||
</html>
|
||||
`;
|
||||
}
|
||||
|
||||
export function buildTaskPrompt(suite) {
|
||||
const editorAssetOutcome =
|
||||
suite === 'full'
|
||||
|
||||
@@ -20,9 +20,7 @@ import { isProjectSkillSuite } from '../suites/project-skill.mjs';
|
||||
import { isResponseStreamSuite } from '../suites/response-stream.mjs';
|
||||
import { isScopedAgentsSuite } from '../suites/scoped-agents.mjs';
|
||||
import { isSteerRunnerKillSuite } from '../suites/steer-runner-kill.mjs';
|
||||
import { isSupervisorAutonomousPlayableLaneDefenseSuite } from '../suites/supervisor-autonomous-playable.mjs';
|
||||
import { isSupervisorSwarmSuite } from '../suites/supervisor-swarm.mjs';
|
||||
import { isUserInputRuntimeSuite } from '../suites/user-input.mjs';
|
||||
import { isWebSearchSuite } from '../suites/web-search.mjs';
|
||||
import { isPathInside, readJson } from './io.mjs';
|
||||
|
||||
@@ -88,17 +86,12 @@ export function buildSummary() {
|
||||
config: state.config,
|
||||
blocked: state.blocked,
|
||||
run: {
|
||||
agentId:
|
||||
isUserInputRuntimeSuite() ||
|
||||
isSupervisorAutonomousPlayableLaneDefenseSuite() ||
|
||||
isSupervisorSwarmSuite()
|
||||
? projectSupervisorAgentId
|
||||
: mainAgentId,
|
||||
agentId: isSupervisorSwarmSuite()
|
||||
? projectSupervisorAgentId
|
||||
: mainAgentId,
|
||||
runIdHash: hashValue(state.initialRunId),
|
||||
sessionIdHash: hashValue(state.initialSessionId),
|
||||
runnerKilled: isSupervisorAutonomousPlayableLaneDefenseSuite()
|
||||
? false
|
||||
: state.runnerKilled,
|
||||
runnerKilled: state.runnerKilled,
|
||||
resumed: state.resumed,
|
||||
identityStable: state.identityStable,
|
||||
},
|
||||
@@ -110,11 +103,9 @@ export function buildSummary() {
|
||||
projectPathReportLeakCount: state.projectPathReportLeakCount,
|
||||
...(isWebSearchSuite() ||
|
||||
isContextCompactionSuite() ||
|
||||
isUserInputRuntimeSuite() ||
|
||||
isScopedAgentsSuite() ||
|
||||
isProjectSkillSuite() ||
|
||||
isParallelReadSuite() ||
|
||||
isSupervisorAutonomousPlayableLaneDefenseSuite() ||
|
||||
isSupervisorSwarmSuite() ||
|
||||
isSteerRunnerKillSuite()
|
||||
? {
|
||||
@@ -408,11 +399,9 @@ export function isIsolatedRunnerSuite() {
|
||||
isResponseStreamSuite() ||
|
||||
isWebSearchSuite() ||
|
||||
isContextCompactionSuite() ||
|
||||
isUserInputRuntimeSuite() ||
|
||||
isScopedAgentsSuite() ||
|
||||
isProjectSkillSuite() ||
|
||||
isParallelReadSuite() ||
|
||||
isSupervisorAutonomousPlayableLaneDefenseSuite() ||
|
||||
isSupervisorSwarmSuite()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -87,11 +87,7 @@ import {
|
||||
import { isProjectSkillSuite } from '../suites/project-skill.mjs';
|
||||
import { isScopedAgentsSuite } from '../suites/scoped-agents.mjs';
|
||||
import { isSteerRunnerKillSuite } from '../suites/steer-runner-kill.mjs';
|
||||
import {
|
||||
confirmSupervisorSwarmPendingActionsInChat,
|
||||
isSupervisorSwarmInteractiveChatSuite,
|
||||
isSupervisorSwarmSuite,
|
||||
} from '../suites/supervisor-swarm.mjs';
|
||||
import { isSupervisorSwarmSuite } from '../suites/supervisor-swarm.mjs';
|
||||
import { killRunnerPidOnce, verifyOwnedRunnerForKill } from './app-data.mjs';
|
||||
import { isPlainObject } from './config.mjs';
|
||||
import {
|
||||
@@ -1038,13 +1034,6 @@ export async function confirmPendingActions(
|
||||
allowedTools = null,
|
||||
shouldConfirm = () => true,
|
||||
) {
|
||||
if (isSupervisorSwarmInteractiveChatSuite()) {
|
||||
await confirmSupervisorSwarmPendingActionsInChat(
|
||||
allowedTools,
|
||||
shouldConfirm,
|
||||
);
|
||||
return;
|
||||
}
|
||||
for (const pending of await findPendingActions()) {
|
||||
if (state.confirmedActionIds.has(pending.actionId)) continue;
|
||||
if (!shouldConfirm(pending)) continue;
|
||||
|
||||
@@ -43,12 +43,6 @@ export const contextCompactionAppDataSentinelFileName =
|
||||
export const contextCompactionAppDataSentinelSchema =
|
||||
'genarrative-agent-runtime-real-e2e-context-compaction-appdata.v1';
|
||||
|
||||
export const userInputAppDataSentinelFileName =
|
||||
'.agent-runtime-real-e2e-user-input-appdata.json';
|
||||
|
||||
export const userInputAppDataSentinelSchema =
|
||||
'genarrative-agent-runtime-real-e2e-user-input-appdata.v1';
|
||||
|
||||
export const scopedAgentsAppDataSentinelFileName =
|
||||
'.agent-runtime-real-e2e-scoped-agents-appdata.json';
|
||||
|
||||
@@ -109,30 +103,6 @@ export const supervisorSwarmToolPlanHandoffCheckpointReachedFileName =
|
||||
export const supervisorSwarmToolPlanHandoffCheckpointReachedSchema =
|
||||
'game-creator-tool-plan-handoff-runner-kill-checkpoint-reached.v1';
|
||||
|
||||
export const supervisorSwarmAutonomousChatAppDataSentinelFileName =
|
||||
'.agent-runtime-real-e2e-supervisor-swarm-autonomous-chat-appdata.json';
|
||||
|
||||
export const supervisorSwarmAutonomousChatAppDataSentinelSchema =
|
||||
'genarrative-agent-runtime-real-e2e-supervisor-swarm-autonomous-chat-appdata.v1';
|
||||
|
||||
export const supervisorAutonomousPlayableAppDataSentinelFileName =
|
||||
'.agent-runtime-real-e2e-supervisor-autonomous-playable-appdata.json';
|
||||
|
||||
export const supervisorAutonomousPlayableAppDataSentinelSchema =
|
||||
'genarrative-agent-runtime-real-e2e-supervisor-autonomous-playable-appdata.v1';
|
||||
|
||||
export const supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelFileName =
|
||||
'.agent-runtime-real-e2e-supervisor-swarm-static-isolated-autonomous-chat-appdata.json';
|
||||
|
||||
export const supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelSchema =
|
||||
'genarrative-agent-runtime-real-e2e-supervisor-swarm-static-isolated-autonomous-chat-appdata.v1';
|
||||
|
||||
export const supervisorSwarmCollaborationPolicyAppDataSentinelFileName =
|
||||
'.agent-runtime-real-e2e-supervisor-swarm-collaboration-policy-appdata.json';
|
||||
|
||||
export const supervisorSwarmCollaborationPolicyAppDataSentinelSchema =
|
||||
'genarrative-agent-runtime-real-e2e-supervisor-swarm-collaboration-policy-appdata.v1';
|
||||
|
||||
export const mainAgentId = 'code-prototype';
|
||||
|
||||
export const projectSupervisorAgentId = 'project-supervisor';
|
||||
@@ -179,8 +149,6 @@ export const webSearchSuite = 'web-search';
|
||||
|
||||
export const contextCompactionSuite = 'context-compaction';
|
||||
|
||||
export const userInputRuntimeSuite = 'user-input-runtime';
|
||||
|
||||
export const scopedAgentsSuite = 'scoped-agents';
|
||||
|
||||
export const projectSkillSuite = 'project-skill';
|
||||
@@ -213,18 +181,6 @@ export const supervisorSwarmTransientRetryBackoffMs = 30_000;
|
||||
|
||||
export const supervisorSwarmTransientRetryPreDueGuardMs = 1_500;
|
||||
|
||||
export const supervisorSwarmAutonomousChatSuite =
|
||||
'supervisor-swarm-autonomous-chat';
|
||||
|
||||
export const supervisorAutonomousPlayableLaneDefenseSuite =
|
||||
'supervisor-autonomous-playable-lane-defense';
|
||||
|
||||
export const supervisorSwarmStaticIsolatedAutonomousChatSuite =
|
||||
'supervisor-swarm-static-isolated-autonomous-chat';
|
||||
|
||||
export const supervisorSwarmCollaborationPolicyMixedRecoverySuite =
|
||||
'supervisor-swarm-collaboration-policy-mixed-recovery';
|
||||
|
||||
export const runtimeContextBundleSchemaVersion =
|
||||
'game-creator-runtime-context-bundle.v5';
|
||||
|
||||
@@ -234,17 +190,6 @@ export const providerRequestLifecycleSchemaVersion =
|
||||
export const providerActionBatchSchemaVersion =
|
||||
'game-creator-provider-action-batch.v2';
|
||||
|
||||
export const runProfileBindingSchemaVersion =
|
||||
'game-creator-run-profile-binding.v1';
|
||||
|
||||
export const autonomousCompletionContractSchemaVersion =
|
||||
'game-creator-autonomous-completion-contract.v2';
|
||||
|
||||
export const autonomousPlaytestReceiptSchemaVersion =
|
||||
'game-creator-autonomous-playtest-receipt.v2';
|
||||
|
||||
export const autonomousGameBuildRunProfile = 'autonomous-game-build';
|
||||
|
||||
export const supervisorCollaborationPolicySchemaVersion =
|
||||
'game-creator-supervisor-collaboration-policy.v1';
|
||||
|
||||
@@ -287,12 +232,6 @@ export const contextCompactionTriggerTurns = new Set([4, 8]);
|
||||
|
||||
export const contextCompactionConstraintCanary = `GENARRATIVE_CONTEXT_CONSTRAINT_${randomUUID().replaceAll('-', '').slice(0, 20)}`;
|
||||
|
||||
export const userInputAnswerCanary = `GENARRATIVE_USER_CHOICE_${randomUUID()
|
||||
.replaceAll('-', '')
|
||||
.slice(0, 20)}`;
|
||||
|
||||
export const userInputAnswerText = `选择轻量像素风,优先保证移动端轮廓和动作可读性;确认标记 ${userInputAnswerCanary}`;
|
||||
|
||||
export const scopedAgentsRootCanary = `GENARRATIVE_SCOPE_ROOT_${randomUUID()
|
||||
.replaceAll('-', '')
|
||||
.slice(0, 20)}`;
|
||||
@@ -461,143 +400,6 @@ export const supervisorSwarmConfirmedTools = [
|
||||
'project.verify',
|
||||
];
|
||||
|
||||
export const supervisorSwarmAutonomousTask =
|
||||
'请把这个试玩项目推进到可以交给首批玩家体验的状态。以仓库现有的正式交付要求、临时检查要求和实际验证结果为准;按阶段生效的临时检查必须在各自生效后全部完成,不能把后续检查并入前置检查或漏掉。完成后简短说明交付内容、验证结论和仍需关注的问题。';
|
||||
|
||||
export const supervisorAutonomousPlayableLaneDefenseTask =
|
||||
'做一个植物大战僵尸式的塔防游戏,要能选择植物、阻挡敌人、正常闯关,并且生成后可以直接试玩。';
|
||||
|
||||
export const supervisorAutonomousPlayableSourceFieldMaxChars = 8_000;
|
||||
|
||||
export const supervisorAutonomousPlayableSourceTotalMaxChars = 10_000;
|
||||
|
||||
export const supervisorAutonomousPlayableSafeStatuses = new Set([
|
||||
'cancelling',
|
||||
'budget-exhausted',
|
||||
'cancelled',
|
||||
'completed',
|
||||
'failed',
|
||||
'idle',
|
||||
'needs-reconciliation',
|
||||
'paused',
|
||||
'pausing',
|
||||
'pending',
|
||||
'running',
|
||||
'waiting-for-confirmation',
|
||||
'waiting-for-user-input',
|
||||
]);
|
||||
|
||||
export const supervisorAutonomousPlayableSafePhases = new Set([
|
||||
'action',
|
||||
'budget-exhausted',
|
||||
'cancelled',
|
||||
'cancelling',
|
||||
'completion-contract-failed',
|
||||
'completed',
|
||||
'conversation-write-failed',
|
||||
'executing',
|
||||
'failed',
|
||||
'finalizing',
|
||||
'idle',
|
||||
'needs-reconciliation',
|
||||
'observation',
|
||||
'parent-terminal',
|
||||
'paused',
|
||||
'pausing',
|
||||
'planning',
|
||||
'provider-action-batch',
|
||||
'queued',
|
||||
'response',
|
||||
'running',
|
||||
'waiting-for-agent',
|
||||
'waiting-for-confirmation',
|
||||
'waiting-for-delegate-receipts',
|
||||
'waiting-for-isolated-join',
|
||||
'waiting-for-process-session',
|
||||
'waiting-for-provider-retry',
|
||||
'waiting-for-user-input',
|
||||
]);
|
||||
|
||||
export const supervisorAutonomousPlayableSafeDeliveryStatuses = new Set([
|
||||
'claimed-by-parent',
|
||||
'dispatched',
|
||||
'ready',
|
||||
'suppressed',
|
||||
]);
|
||||
|
||||
export const supervisorAutonomousPlayableSafeTerminalStatuses = new Set([
|
||||
'budget-exhausted',
|
||||
'cancelled',
|
||||
'completed',
|
||||
'failed',
|
||||
]);
|
||||
|
||||
export const supervisorAutonomousPlayableSafeTurnOutcomes = new Set([
|
||||
'failed',
|
||||
'incomplete',
|
||||
'needs-reconciliation',
|
||||
'settled',
|
||||
]);
|
||||
|
||||
export const laneDefensePlaytestRequiredAssertions = [
|
||||
'state-surface-valid',
|
||||
'level-positive',
|
||||
'start-control-clicked',
|
||||
'start-sequence-advanced',
|
||||
'start-phase-playing',
|
||||
'defender-option-control-clicked',
|
||||
'defender-selection-sequence-advanced',
|
||||
'defender-selection-recorded',
|
||||
'lane-cell-control-clicked',
|
||||
'defender-placement-sequence-advanced',
|
||||
'defender-count-increased',
|
||||
'enemies-present-after-placement',
|
||||
'speed-up-control-clicked',
|
||||
'battle-sequence-advanced',
|
||||
'battle-sequence-monotonic',
|
||||
'enemy-position-changed',
|
||||
'enemy-health-decreased',
|
||||
'phase-won',
|
||||
'next-level-control-clicked',
|
||||
'next-level-sequence-advanced',
|
||||
'level-increased',
|
||||
'restart-control-clicked',
|
||||
'restart-sequence-advanced',
|
||||
'restart-phase-ready-or-playing',
|
||||
];
|
||||
|
||||
export const supervisorSwarmAutonomousRoutingTerms = [
|
||||
supervisorSwarmDesignAgentId,
|
||||
supervisorSwarmQualityAgentId,
|
||||
'agent.delegate',
|
||||
'agent.run_status',
|
||||
'同一个 planning 轮次',
|
||||
'同轮',
|
||||
'并行',
|
||||
'返工',
|
||||
'repair',
|
||||
'一次',
|
||||
'两个 Agent',
|
||||
'两个专业',
|
||||
'Runner',
|
||||
'pidfd',
|
||||
'actionId',
|
||||
'delegationId',
|
||||
];
|
||||
|
||||
export const supervisorSwarmCollaborationPolicyControlTerms = [
|
||||
'.agent/collaboration-policy.json',
|
||||
supervisorCollaborationPolicySchemaVersion,
|
||||
'requiredInitialWave',
|
||||
'minStaticDelegates',
|
||||
'requiredStaticAgentIds',
|
||||
'minIsolatedChildren',
|
||||
'minIsolatedGroupsBeforeClaim',
|
||||
'orchestratorOnlyAfterDelegation',
|
||||
'policyFingerprint',
|
||||
'contractFingerprint',
|
||||
];
|
||||
|
||||
export const supervisorSwarmIsolatedReviews = [
|
||||
{
|
||||
path: 'e2e/isolated-a/evidence.txt',
|
||||
@@ -625,17 +427,6 @@ export const supervisorSwarmIsolatedReviews = [
|
||||
},
|
||||
];
|
||||
|
||||
export const supervisorSwarmIsolatedReviewGroups = [
|
||||
supervisorSwarmIsolatedReviews.slice(0, 2),
|
||||
supervisorSwarmIsolatedReviews.slice(2),
|
||||
];
|
||||
|
||||
export const supervisorSwarmInitialIsolatedReviews =
|
||||
supervisorSwarmIsolatedReviewGroups[0];
|
||||
|
||||
export const supervisorSwarmFollowupIsolatedReviews =
|
||||
supervisorSwarmIsolatedReviewGroups[1];
|
||||
|
||||
export const webSearchBaselineApiUrl =
|
||||
'https://api.github.com/repos/nodejs/node/releases/latest';
|
||||
|
||||
@@ -703,8 +494,6 @@ export const pollIntervalMs = 750;
|
||||
|
||||
export const runTimeoutMs = 30 * 60 * 1000;
|
||||
|
||||
export const supervisorAutonomousPlayableRunTimeoutMs = 60 * 60 * 1000;
|
||||
|
||||
export const supervisorSwarmTerminalSidecarCleanupTimeoutMs = 10_000;
|
||||
|
||||
export const processRunnerKillStartTimeoutMs = 5 * 60 * 1000;
|
||||
@@ -938,10 +727,6 @@ export class BlockedError extends Error {
|
||||
|
||||
export const isolatedRunnerState = {
|
||||
appDataDir: null,
|
||||
platformSessionFixturePath: null,
|
||||
platformSessionFixtureSourcePath: null,
|
||||
platformSessionFixtureSha256: null,
|
||||
platformSessionFixturePreviousEnv: undefined,
|
||||
ownerToken: null,
|
||||
createdAt: 0,
|
||||
current: null,
|
||||
@@ -968,9 +753,6 @@ export const state = {
|
||||
linuxPidfdPythonPath: null,
|
||||
windowsProcessHandlePowerShellPath: null,
|
||||
cleanupInProgress: false,
|
||||
userInputCliSession: null,
|
||||
supervisorSwarmCliSession: null,
|
||||
supervisorAutonomousPlayableCliSession: null,
|
||||
status: 'FAIL',
|
||||
suite: null,
|
||||
options: null,
|
||||
@@ -1082,23 +864,6 @@ export const state = {
|
||||
finalReplyFingerprint: null,
|
||||
reportLeakCount: 0,
|
||||
},
|
||||
userInput: {
|
||||
requestId: null,
|
||||
responseId: null,
|
||||
actionId: null,
|
||||
questionMessageId: null,
|
||||
answerMessageId: null,
|
||||
questionCount: 0,
|
||||
optionCount: 0,
|
||||
providerStartedBeforeKill: 0,
|
||||
providerStartedAfterRestart: 0,
|
||||
conversationCountBeforeKill: 0,
|
||||
conversationCountAfterRestart: 0,
|
||||
oldRunnerBootId: null,
|
||||
newRunnerBootId: null,
|
||||
privateValues: [],
|
||||
reportLeakCount: 0,
|
||||
},
|
||||
scopedAgents: {
|
||||
effectiveModel: null,
|
||||
effectiveApiKind: null,
|
||||
@@ -1120,20 +885,6 @@ export const state = {
|
||||
privateValues: [],
|
||||
reportLeakCount: 0,
|
||||
},
|
||||
supervisorAutonomousPlayable: {
|
||||
freshInitBaselineUsed: false,
|
||||
initialGameIndexSha256: null,
|
||||
expectedProviderBinding: null,
|
||||
effectiveProviderBinding: null,
|
||||
stdinWriteCount: 0,
|
||||
stdinEnded: false,
|
||||
stdinBytes: 0,
|
||||
turnReport: null,
|
||||
cliOutput: '',
|
||||
privateValues: [],
|
||||
reportLeakCount: 0,
|
||||
ownedProcessCleanupSnapshot: null,
|
||||
},
|
||||
supervisorSwarm: {
|
||||
effectiveModel: null,
|
||||
effectiveApiKind: null,
|
||||
@@ -1215,47 +966,7 @@ export const state = {
|
||||
toolPlanHandoffLifecycleClosedExactlyOnce: false,
|
||||
toolPlanHandoffAuditIdempotent: false,
|
||||
toolPlanHandoffRecoveredPlanFingerprintMatched: false,
|
||||
autonomousTaskRecipeFree: false,
|
||||
autonomousRepositoryRecipeFree: false,
|
||||
interactiveCliUsed: false,
|
||||
chatSessionUnexpectedlyClosed: false,
|
||||
chatSessionFailureKind: 'none',
|
||||
chatSessionExitCode: 'none',
|
||||
chatSessionCloseSignal: 'none',
|
||||
chatSessionProcessErrorCode: 'none',
|
||||
chatSessionStderrChars: 0,
|
||||
chatSessionStderrSha256: 'none',
|
||||
turnReport: null,
|
||||
mixedSpawnActionId: null,
|
||||
mixedSpawnRequestHash: null,
|
||||
mixedFollowupSpawnActionId: null,
|
||||
mixedFollowupSpawnRequestHash: null,
|
||||
staticIsolatedProviderRequestIds: [],
|
||||
staticIsolatedProviderOverlapObserved: false,
|
||||
preKillMixedIdentity: null,
|
||||
collaborationPolicyWritten: false,
|
||||
collaborationPolicySnapshotInitialRecord: null,
|
||||
collaborationPolicySnapshotInitialBytes: null,
|
||||
collaborationPolicySnapshotInitialBytesSha256: null,
|
||||
collaborationPolicySnapshotInitialIdentityHash: null,
|
||||
collaborationPolicySnapshotBindingInitialRecord: null,
|
||||
collaborationPolicySnapshotBindingInitialBytes: null,
|
||||
collaborationPolicySnapshotBindingInitialBytesSha256: null,
|
||||
chatSessionFailureDiagnostic: null,
|
||||
collaborationPolicyDriftFixtureWritten: false,
|
||||
collaborationPolicyDriftFixturePolicyFingerprint: null,
|
||||
collaborationPolicyDriftedMinIsolatedGroupsBeforeClaim: null,
|
||||
initialBatchRecoveryBoundaryObserved: false,
|
||||
initialBatchRecoveryOldRunnerBootId: null,
|
||||
initialBatchRecoveryNewRunnerBootId: null,
|
||||
initialBatchRecoveryPreKillIdentity: null,
|
||||
initialBatchRecoveryPostRecoveryIdentity: null,
|
||||
initialBatchRecoveryPreKillSideEffects: null,
|
||||
initialBatchRecoveryPostRecoverySideEffects: null,
|
||||
initialBatchRecoveryIdentityStable: false,
|
||||
initialBatchRecoveryConfirmationRestoredCount: 0,
|
||||
initialBatchRecoveryPidfdClaimCount: 0,
|
||||
initialBatchRecoveryPidfdSignalCount: 0,
|
||||
},
|
||||
confirmedActionIds: new Set(),
|
||||
cleanupPerformed: false,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
-1890
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user