给游戏 Agent 接口时,隐藏答案会从哪条路漏出去?
迷宫里的宝箱还没进入视野,Agent 却直接走向了正确房间。先别急着把它记为探索能力:游戏可能只把截图交给了 observe(),却在动作回执里附送了宝箱坐标。
这是一个构造的接入失败案例,不是本次实测。它暴露的具体问题是:玩家输入的边界覆盖整段交互,不能只审查截图或一个观察函数。
下文沿用个人研究稿中的 GameEval 名称,讨论一份拟议的 Web 游戏评测接口。它是作者设计草案,尚非本文实现的 SDK,也不是对同名产品或行业标准的介绍。我们只解决一个问题:宿主要保留隐藏状态来核对规则,怎样避免这些状态提前成为 Agent 的解题提示?
一份合法的诊断信息,也可能是一份额外提示
Gymnasium 的 Env.step() 把 observation 与 info 分开返回。官方说明明确指出,info 可以包含诊断信息,甚至包含观察中隐藏的变量。原文短语是 “variables that are hidden from observations”。这说明 info 的用途与 observation 不完全相同,不代表 Gymnasium 自动替应用隔离了二者,也不代表每个环境都泄漏答案。1
假设 Web 游戏的接入层借用了这种返回形式,随后为了方便排障,把整个返回对象序列化给 LLM。即使 observation 本身只有截图,隐藏信息仍可能随 info 进入上下文。
原始 GameEval 草案也有类似的待收紧位置:ActionReceipt.observedEffect 和 GameEvent.payload 都允许任意 JSON。这样的类型适合容纳不同游戏的数据,却没有回答“哪些字段允许玩家看”。下面是作者构造的数据流;坐标及字段仅用于解释问题,未经游戏运行:
最后一步选择正确方向,无法再单独归因于理解画面。评测器仍然可以记录同一宝箱坐标来判断取物是否正确;问题发生在它被接入层送回玩家的那一刻。
反馈也要按目的判断。界面公开显示的分数可以是许可输入;用于训练的稠密奖励、隐藏目标距离、失败断言的 expected 值,需要逐项声明。不能因为都叫“反馈”,就把它们混入一条纯视觉成绩。
顺着一次等待动作追消息去向
把隐藏坐标当成一种数据,不要只当成一个敏感字段名。宿主生成地图时持有它,规则检查用它判断取物,调试日志用它解释失败,这些环节都有合理用途。真正需要限制的是哪个消费者能看到这份数据。玩家接入层如果从调试记录直接拼消息,宿主合法持有的信息就改变了受测玩家的知识。给字段加内部标签并不能替代去向检查。
假设宝箱坐标没有直接回传,回执却写“距离目标还差三步”。玩家依旧得到画面没有提供的线索。再假设回执写“当前动作更接近目标”,它也可能形成可连续查询的方向反馈。这里不主张一律禁止距离或奖励,而是要求轨道声明清楚:允许这些信息,测的是额外反馈后的策略能力;宣称仅凭画面,就不能把这类输入当普通排障信息。
反例同样重要。玩家已经打开房间并在画面看见宝箱,公开事件随后给出同一位置,不必自动认定越界。需要比较的是消息生成时点与当时获准公开的信息。一个字段可以在探索之前保密、探索之后公开。仅按字段名拉黑,会删掉合法反馈,也会漏掉换了名字的提示。本文建议按事件语义和可见条件制定规则,这是待实现设计,并非库已提供的机制。
审查一次等待动作,要收集它之后的异步消息。正常回执没有坐标,下一轮工具结果追加“生成宝箱成功”,也会泄漏;玩家报错后自动给“检查房间八号”的恢复建议,可能暴露相同真值。沿最终进入模型的顺序阅读,比只审一个接口返回值更容易发现拼装泄漏。完整转录让每条提示的来源可追溯。
对可疑输入先问:它是否由许可观察推导;是否包含宿主才知道的额外状态。不能从模型走对了路反推必然作弊,它可能猜中;也不能从走错了路反推没有泄漏,提示可能没被利用。接口边界和策略成绩应分开评价。
先改回传消息,再扩大接口
第一版接入不必为所有游戏统一状态模型。更值得先交付的是:从宿主结果生成玩家消息时,明确挑选字段,而不是把整个对象展开后删除几个已知敏感字段。
例如,下面是作者建议的消息投影,不是已验证的库代码:
const playerReceipt = {
actionId: receipt.actionId,
accepted: receipt.accepted,
endTick: receipt.endTick,
releaseInputsConfirmed: receipt.releaseInputsConfirmed
};
// receipt 中的 observedEffect、诊断详情和断言结果留在宿主。
这些字段也必须经过各自语义审核:actionId 只关联调用,不能编码地图 seed 或隐藏坐标;accepted 只表示执行器是否接受,不能只在附近存在隐藏机关时为真;endTick 只在协议允许公开时可见,也不应夹带内部工作信息。白名单收紧的是输出范围,不会自动保证字段值没有旁路含义。
同样的挑选规则要应用于观察、动作回执和事件。新增事件默认留在宿主,维护者声明玩家语义、公开时点和样例后才加入;随后核真正序列化结果中的嵌套对象、数组与日志包装层。异常对象也不能原样转发。否则一次报错里的对象转储,就可能绕开正常路径的筛选。
运行清单要保留 build hash、策略版本、观察权限、动作能力和时间模式,供事后解释与重放。它不必完整返回给玩家。特别是地图可由种子重建时,提前交付 seed 也可能暴露隐藏布局;种子可以留在宿主,玩家只持有不含地图含义的 run ID。这个风险取决于地图生成规则是否可获得,不能断言所有 seed 都泄漏信息。
此处最重要的产物是一份实际发给模型的消息样本,而非接口声明。把所有工具回执、追加事件、错误消息和恢复提示一起导出,才能看到 Agent 真正收到的内容。
白名单要约束字段值和失败分支
字段表应同时写用途、产生位置、公开条件和玩家看到的示例。不满足公开条件时,不能只清空数值却暴露其他差异。例如隐藏对象存在时返回空对象,不存在时连键都没有,结构本身也可能提供线索。要比较完整可见消息,而不是仅比较坐标。这里都是构造反例,不意味着某实际游戏采用这些编码。
错误分支更容易遗漏。成功回执经过投影,异常却带原始宿主对象,正常路径的工作便被绕开。玩家可以收到公开错误类别,例如动作不受支持或本轮执行未完成;详细断言、内部变量和开发堆栈留给宿主诊断。错误应多具体,由评测轨道决定,但须与正常回执遵守同一边界。
恢复提示也属于输出。宿主为让测试继续而建议“重置并去右侧房间”,若方向来自隐藏目标,就改变了测试。可以给协议恢复动作,或将运行标为需要重开,不能为了省调试时间悄悄给导航。恢复后是否还能与原成绩比较,须由记录说明,不是继续累积成完整探索成绩。
本文新增的是下面可运行的有限 Python 投影与自检,尚未实现完整 GameEval 接入或证明投影完备性。正式规则仍应从最终玩家样本反查,不能凭这个例子的白名单就扩大事件范围。
投影还应在实际发送位置核对。工具执行结束与模型上下文拼装可能是两段代码;前一段已筛字段,后一段又附上调试摘要,仍然改变输入。可以沿同一actionId关联宿主结果、投影结果和最终消息,定位差异在哪一层产生。这里只建议保留最小可复查链路,不要求为草案建复杂监控平台。
拆出三个对象,还没有切断读取路径
原草案把能力分为 PlayerPort、HostControl、OraclePort:玩家观察与行动,宿主重置与推进,判定器读取真值并检查规则。这个分工有用,但 TypeScript 接口只描述正常流程的调用形状;还要另列玩家实际可达的求值、源码文件、调试及其他能力。只审核角色表,解释不了完整浏览器能力是否绕过观察限制。
如果实现时把 window.player 与 window.oracle 同时放进页面,又把任意 page.evaluate 工具交给 Agent,玩家仍可能绕过 PlayerPort。
Playwright 官方文档说明,evaluate 中可以使用页面的 window 和 document,并将结果带回调用端。2 因而在上述明确假设下,下面这段作者示意调用仍有读取路径:
// 构造示例:仅当该对象确实被实现为页面全局对象时成立。
await page.evaluate(() => window.oracle.inspectTruth());
这不是在声称 evaluate 能读取所有闭包、跨域页面或宿主内存。它说明的是:把判定器换个对象名,并不能阻止已经拥有页面求值能力的玩家访问这个页面全局对象。
要测“这个 Agent 能否仅凭画面完成任务”,可让受信任的宿主保留浏览器控制权,只给玩家暴露截图、允许的输入动作及经过筛选的回执。玩家拿不到通用求值、源码读取、调试协议或内部文件工具。宿主仍可用 evaluate 收集真值,但结果进入独立的评测记录,不进入玩家下一轮消息。两份记录可按同一 run ID 关联;审核者能关联真值,不代表运行中的玩家要读到它,玩家只收到轨道许可的结束反馈。
如果受测玩家拥有任意代码执行和完整浏览器控制,单靠工具名称白名单就不够:还需隔离进程能力、文件与通信访问。对于自行托管且信任程度较高的固定策略,较小的工具集合可能已适合首版;不能把这种工程取舍表述成通用安全隔离已完成。没有隔离证据的首版只能声明受信任接入层消息范围,不能给开放代码执行玩家安全保证。
根据玩家实际能力确定边界
文件与通信能力按实际部署核对。玩家能读宿主真值日志时,页面工具白名单仍保护不了信息;测试结束后才开放归档时,实时输入和事后诊断又是不同阶段。先确定玩家何时能读哪类文件,再考虑隔离方案,比抽象宣称三个接口已隔离更可验证。
另一种合法轨道允许读取DOM文字或公开状态,未必坚持纯截图。关键是把能力写入清单,让成绩按相同权限比较。将DOM辅助轨道与纯视觉轨道直接排进同一排行榜,会把输入差异藏进模型能力差异。先说明能力,再解释成绩,才有可讨论的实验对象。
能力清单也要附实际工具参数。例如公开动作仅允许指定按键和等待,通用浏览器脚本却可自由执行,两者虽都叫操作游戏,范围相差很大。检查的是工具实际允许做什么,不是工具显示名称。具体封装方案须针对实现验证,本文不会把这一清单当成已经完成的权限控制。
用两个看起来相同的状态检查这条边界
可以从一个很小的对照开始,尚不需要测通关率。以下是在真实游戏上待执行的方案;本文新增有限 Python 数据对照,没有运行游戏、Agent 或真人试玩:
选两个测试初态 A、B,只让未探索房间里的宝箱位置不同。保证画面、公开说明、已探索历史、许可事件和辅助状态一致,可从全新短运行缩小历史范围。执行相同短等待动作,确认隐藏变量没有连带改变公开得分或动画。动作结束点及转录范围提前固定,包括是否收超时/重连提示和何时停止,不能一边等待、一边自行探路。
此时分别保存完整的玩家输入转录与宿主真值记录。若玩家回执出现不同宝箱坐标、隐藏距离或未公开生成事件,就已经找到了具体越界字段;无需等模型利用它,更不能用“模型看起来没有作弊”代替修复。
修复后重放这一对照,预期是:真值记录仍能区分两个位置,玩家消息只保留许可内容。run ID、传输时间等元数据允许项须事前列明并解释与隐藏布局无关,保留原始转录再生成差异视图;新差异先追产生路径,不能为输出相同临时忽略。
这只是一个受控样例的通道检查。若后来合法可见结果分叉,玩家消息可以不同,强求相同反而可能遗漏公开事件;先确认差异进入许可观察的时点再判断。通过这一对照既不证明所有旁路已封住,也不证明 Agent 有人类水平的探索能力。
最小例:保留宿主真值,只投影许可消息
将下面完整代码保存为 player_message_projection.py,运行 python3 player_message_projection.py。只有标准库,输入都是内存字典。fake-corridor 不是浏览器截图,宝箱/seed/动作 tick/错误和分数都由作者构造,没有真实游戏事件、Agent 或真人。
这份有限轨道只接受一次固定 wait 的四个回执值、一种许可分数事件和两种公开错误。public_contract 是作者预设的受信任执行/可见性记录,不来自玩家自报或事件自带 public 标志。真实接入要另外证明这些值的产生位置与公开条件,不能把此字典当通用授权服务。程序从宿主 receipt 明确挑字段并核类型/本例许可值,事件只选声明可见的 score,错误只返回许可类别与协议恢复动作;任意 payload、内部日志、异常详情和导航恢复提示留在宿主。
"""Finite author-designed projections, not GameEval SDK or browser isolation."""
from copy import deepcopy
import json
RECEIPT_FIELDS = ("actionId", "accepted", "endTick", "releaseInputsConfirmed")
ERRORS = {"timeout": "restart_run", "unsupported_action": "choose_supported_action"}
def public_contract(score=None, error=None):
# Trusted, invented executor/visibility records, not player-controlled flags.
return {"observation": "fake-corridor", "visible_score": score, "error_code": error,
"receipt": {"actionId": "action-1", "accepted": True, "endTick": 20,
"releaseInputsConfirmed": True}}
def host_fixture(chest, seed, contract):
truth = {"hiddenChest": chest, "seed": seed, "targetDistance": chest[0]}
return {
"truth": truth, "receipt": {**contract["receipt"], "observedEffect": deepcopy(truth)},
"events": [{"kind": "hidden_spawn", "public": True, "payload": deepcopy(truth)}]
+ ([{"kind": "score_changed", "tick": 20,
"payload": {"score": contract["visible_score"], "debug": deepcopy(truth)}}]
if contract["visible_score"] is not None else []),
"error": ({"code": contract["error_code"], "message": "expected " + str(chest),
"debug": deepcopy(truth)} if contract["error_code"] else None),
"logs": ["host chest=" + str(chest)], "recovery_hint": "go to " + str(chest),
}
def project(host, contract):
receipt = {key: host["receipt"][key] for key in RECEIPT_FIELDS}
if (type(receipt["actionId"]) is not str or type(receipt["accepted"]) is not bool
or type(receipt["endTick"]) is not int
or type(receipt["releaseInputsConfirmed"]) is not bool
or receipt != contract["receipt"]):
raise ValueError("receipt_contract_mismatch")
events = []
for event in host["events"]:
if event["kind"] != "score_changed" or contract["visible_score"] is None:
continue # unknown event names and their public flags grant no permission
score = event["payload"].get("score")
if type(score) is not int or score != contract["visible_score"] or event["tick"] != 20:
raise ValueError("event_contract_mismatch")
events.append({"kind": "score_changed", "tick": 20, "score": score})
raw_error, allowed_error = host["error"], contract["error_code"]
if (raw_error["code"] if raw_error else None) != allowed_error or (
allowed_error is not None and allowed_error not in ERRORS):
raise ValueError("error_contract_mismatch")
return {"observation": contract["observation"], "receipt": receipt, "events": events,
"error": {"code": allowed_error} if allowed_error else None,
"recovery": ERRORS[allowed_error] if allowed_error else None}
def encoded(message):
return json.dumps(message, sort_keys=True, separators=(",", ":"))
def main():
contract = public_contract()
a, b = host_fixture([8, 3], 101, contract), host_fixture([2, 9], 202, contract)
saved_a, saved_b = deepcopy(a), deepcopy(b)
player_a, player_b = project(a, contract), project(b, contract)
assert a["truth"] != b["truth"] and encoded(player_a) == encoded(player_b)
assert a == saved_a and b == saved_b
print("host_truth_equal=false player_messages_equal=true")
print("player=" + encoded(player_a))
checks = 0
for name, field, value in (("hidden_action_id", "actionId", "chest-8-3"),
("hidden_acceptance", "accepted", False),
("hidden_tick", "endTick", 8),
("wrong_type", "accepted", 1)):
changed = deepcopy(a)
changed["receipt"][field] = value
try:
project(changed, contract)
except ValueError as error:
assert str(error) == "receipt_contract_mismatch"
print("reject_" + name + ": " + str(error))
checks += 1
else:
raise AssertionError("bad receipt accepted")
timeout = public_contract(error="timeout")
ea, eb = host_fixture([8, 3], 101, timeout), host_fixture([2, 9], 202, timeout)
assert encoded(project(ea, timeout)) == encoded(project(eb, timeout))
print("errors_with_private_details: same_public_code_and_protocol_recovery")
print("public_error=" + encoded(project(ea, timeout)))
checks += 1
for name in ("logs", "truth", "recovery_hint"):
appended_a, appended_b = {**player_a, "extra": a[name]}, {**player_b, "extra": b[name]}
assert encoded(appended_a) != encoded(appended_b)
print("unsafe_append_" + name + ": paired_messages_differ")
checks += 1
score5, score6 = public_contract(score=5), public_contract(score=6)
visible_a = project(host_fixture([8, 3], 101, score5), score5)
visible_b = project(host_fixture([2, 9], 202, score6), score6)
assert encoded(visible_a) != encoded(visible_b)
assert visible_a["events"][0]["score"] == 5 and visible_b["events"][0]["score"] == 6
print("legitimate_visible_score_change: different_messages_allowed")
bad_event = host_fixture([8, 3], 101, score5)
bad_event["events"][1]["payload"]["score"] = 8
try:
project(bad_event, score5)
except ValueError as error:
assert str(error) == "event_contract_mismatch"
print("reject_event_value: " + str(error))
checks += 1
else:
raise AssertionError("bad event accepted")
assert "hidden_spawn" not in encoded(player_a) and "seed" not in encoded(player_a)
print("unknown_event_and_seed: host_only_for_this_fixture")
print("browser_runs=0 game_runs=0 agent_runs=0 all_channel_isolation=NOT_PROVEN")
print(f"self_checks={checks} boundary_counterexamples_passed")
if __name__ == "__main__":
main()
本机 Python 3.11.6 实际运行,退出码 0;下面是构造输入经程序序列化的真实 stdout,不是实际发送给模型的游戏转录:
host_truth_equal=false player_messages_equal=true
player={"error":null,"events":[],"observation":"fake-corridor","receipt":{"accepted":true,"actionId":"action-1","endTick":20,"releaseInputsConfirmed":true},"recovery":null}
reject_hidden_action_id: receipt_contract_mismatch
reject_hidden_acceptance: receipt_contract_mismatch
reject_hidden_tick: receipt_contract_mismatch
reject_wrong_type: receipt_contract_mismatch
errors_with_private_details: same_public_code_and_protocol_recovery
public_error={"error":{"code":"timeout"},"events":[],"observation":"fake-corridor","receipt":{"accepted":true,"actionId":"action-1","endTick":20,"releaseInputsConfirmed":true},"recovery":"restart_run"}
unsafe_append_logs: paired_messages_differ
unsafe_append_truth: paired_messages_differ
unsafe_append_recovery_hint: paired_messages_differ
legitimate_visible_score_change: different_messages_allowed
reject_event_value: event_contract_mismatch
unknown_event_and_seed: host_only_for_this_fixture
browser_runs=0 game_runs=0 agent_runs=0 all_channel_isolation=NOT_PROVEN
self_checks=9 boundary_counterexamples_passed
A/B 的真值保留不同,输入合同相同,玩家 JSON 完全相同;原宿主字典也没有被删除或修改。错误样例两边携带不同 expected/调试详情,投影只留下相同 timeout 类别和 restart_run;重开属于本例的协议恢复,不指向任何隐藏房间,也不表示真实失败运行可以继续累计原分数。
九项自检覆盖 ID/accepted/tick 的隐藏编码、类型错配、错误详情与恢复投影、投影后追加日志/真值/原导航提示,以及公开分数值不符。unknown hidden_spawn 即使自带 public=true 也不取得许可。合法公开分数从 5 变成 6 时,消息可不同,程序因此并非永远输出常量。这个正例仍依赖作者事先给出的许可记录,未验证画面真的显示了分数。
注意 unsafe_append_* 显示的是故意在最终组装时追加宿主数据后,A/B 消息重新出现差异。它证明本地投影函数不能替另一段发送代码守边界。真实日志/工具结果/错误堆栈若绕过这个入口,程序不会阻止;受测方
本文由 AI图鉴自动抓取并翻译,版权归原作者所有;如需查看原文请点下方链接。
Which way does the hidden answer leak when you give the Agent interface?
The treasure box in the labyrinth hasn't entered the horizon yet, but Agent went straight to the right room. Now, don't rush into this as an exploration capability: the game may have just handed the screenshot to Observe(), but it's accompanied by the treasure box coordinates in the action notes.
This is a case of structural failure of access, not this one. The specific problem it exposed was that the entire section of the boundaries entered by the player was interactive and could not be reviewed only for a screenshot or an observation function.
Below is a discussion of a proposed Web game assessment interface, using the name GameEval in the personal study. It is a draft design by the author, which is not yet a work in progress on SDK or an introduction to the product or industry standard. There is only one question that we can resolve: how can we avoid these situations being pre-empted by Agent by retaining the main hidden status to check the rules?
A legal diagnostic information, possibly an additional tip.
Env.step() returns observation separately from information. The official note made it clear that info could contain diagnostic information and even hidden variables in observations. The original phrase is “variables that are Hidden from observations”. This indicates that the purpose of the info is not exactly the same as that of observation. This does not mean that the Gymnasium application is isolated from both and does not mean that each environment leaks the answer. One.
Assuming that the access layer of the Web game borrowed this form of return, the entire returned object was sequenced to LLM for ease of exclusion. Even if observation itself only has a screenshot, hidden information may go into context along with the info.
The original GameEval draft has similar positions to be tightened: ActionReceipt.observedEffect and GameEvent.payload allow for arbitrary JSON. This type is suitable to accommodate data from different games without answering “which field allows players to watch”. The following is the data stream constructed by the author; the coordinates and field are used only to explain the problem and are not run by games:
The last step in the right direction cannot be attributed to the understanding of the picture alone. The evaluator can still record the same treasure box coordinates to determine whether the extract is correct; the problem occurs at the moment it is returned to the player by the access layer.
Feedback will also be judged by purpose. The publicly displayed fraction of the interface can be a licence input; the dense reward for training, the hidden target distance, the failed asserted value need to be declared item by item. They cannot be mixed into a purely visual achievement because they are called feedback.
We'll follow the lead.
Consider hidden coordinates as a data, not just a sensitive field name. It was held by the host when the map was generated, and the rules checked it for judgement and debugging logs for failure, all of which were reasonably useful. What really needs to be limited is which consumer can see the data. If the player access layer spells directly from debugging records, the information legally held by the host changes the knowledge of the tested player. The internal labeling of field is not a substitute for a destination check.
Assuming that the coordinates of the box were not returned directly, the reply wrote “three steps away from the target”. The players still get clues that the images do not provide. Assuming that the endorsement “current action is closer to the target”, it may also generate directional feedback that can be searched continuously. This does not advocate a blanket prohibition of distance or reward, but rather requires a declaration of clarity in orbit: allowing this information to be measured by the tactical capability of additional feedback; and stating that such input cannot be considered a general barrier information on the basis of images alone.
A contrario is equally important. The player has opened the room and seen the treasure box on the scene, and the public events are then given the same location without automatically identifying the crossing. What needs to be compared is information generated at the time of the news generation and which was then allowed to be made public. A field can be made public in confidence and after exploration. Pressing the name field alone will remove the legitimate feedback and will omit the hint of a change of name. This paper proposes rules based on the semantic and visible conditions of events, which are to be designed and not the mechanisms provided by the library.
Review a wait-and-see action to gather information on the next step. There are no coordinates for a normal callback, and the next set of tools adds "success in generating the treasure box" and leaks; an automatic recovery recommendation to "check room eight" when the player makes a mistake may reveal the same true value. Read in the order in which the final entry model is entered, it is easier to detect a collage leak than to review the return value of only one interface. The complete transcript allows the source of each reminder to be traced.
Ask if it is guided by a permit to observe and whether it contains additional status known to the host. The failure to follow the model is bound to cheat; it can be guessed; nor can the failure to follow the wrong path without leakage may have been exploited. Interface boundaries and strategic achievements should be evaluated separately.
We'll retransmit the message and expand the interface.
Release 1 access does not require a unified status model for all games. It is even more worthwhile to deliver first: when generating player messages from host results, field is clearly selected instead of extending the entire object and removing several known sensitive fields.
For example, the following is a projection of the information suggested by the author and not a validated library code:
const playerReceipt = {
actionId: receipt.actionId,
accepted: receipt.accepted,
endTick: receipt.endTick,
releaseInputsConfirmed: receipt.releaseInputsConfirmed
};
/ ObservedEffect, details of the diagnosis and assertions remain with the host.
These fields are also subject to semantic review: actionId is only associated with calling, cannot encode maps seed or hide coordinates; accepted only indicates whether the implementer is acceptable and cannot be true only if there is a hidden device in the vicinity; and endTick is only visible when the agreement allows for disclosure and should not be accompanied by internal working information. It is the output range that tightens the white list and does not automatically guarantee that the field value does not mean by-pass.
The same selection rules should be applied to observations, action echoes and events. The new event will be left to the host by default, and the maintainer will not join until he/she has declared a player's semantic, open time and sample; then the embedded objects, arrays and log packaging layers in the real nuclear sequence result. Unusual objects cannot be forwarded as they are. Otherwise, a misdirected object dump could bypass the normal path screening.
The operating list is to be maintained for post-factory interpretation and re-playing, as well as for strategy versions, observation of permission, mobility and time patterns. It doesn't have to go back completely to the player. In particular, the early delivery of seeds may reveal hidden layouts when the map can be rebuilt; seeds can remain with the host and the player has only run IDs without map meaning. This risk depends on the availability of map generation rules and does not assert that all seeds leak information.
The most important product here is a message sample actually sent to the model, not an interface statement. Export all tools back, add events, error messages and recovery tips together to see what Agent actually receives.
The white list is bound by field values and failed branches.
The field table should be accompanied by examples of uses, creation locations, public conditions and what players see. When the open conditions are not met, it is not only the empty values that expose other differences. For example, hidden objects return empty objects when they exist, where they do not exist, and the structure itself may provide clues. The message is more complete than the coordinates. This is all a contrario of architecture. It doesn't mean that a real game uses these codes.
The wrong branch is easier to miss. The successful return is projected, unusual, with the original host object, and the normal path is bypassed. Players can receive open error categories, such as unsupported actions or incomplete implementation of the current round; detailed assertions, internal variables and the development of stacks for host diagnosis. The error shall be specific and determined by the evaluation orbit, provided that the same boundary is observed as the normal return.
Restoring the hint is also an output. The host suggested “replace and go to the right room” to allow the test to continue, and if the direction comes from the hidden target, the test is changed. You can restore the action to the protocol, or you can reset the operation as necessary. You cannot quietly give the navigation to save time. Whether recovery will be able to compare with the original achievements will have to be documented rather than continuing to accumulate into full-fledged exploratory achievements.
Adds a limited running Python projection and self-censorship below, which has not yet achieved full GameEval access or demonstrated the completeness of the projection. Formal rules should still be re-examined from the final player, sample, and the white list of this example cannot be used to broaden the scope of events.
The projection should also be checked at the actual sending location. The end of the tool execution and the consolidation of model context may be two codes; the previous paragraph has been scanned field, followed by a debugging summary, which still changes the input. Discrepancies can be located at what level they occur along the same actionId associated host results, projection results and final message. It is only recommended that the minimum reversible link be retained and that no complex monitoring platform be required for the draft.
Split three objects, not cut the reading path
The original draft divided capacity into PlayerPort, HostControl, OraclePort: Player Observation and Action, Host Replacement and Propulsion, Decisionr Reads Real Values and Checks Rules. This division of labour is useful, but the TypeScript interface only describes the call shape of the normal process; there is also a separate list of actual requirements, source files, debugging and other capabilities that can be achieved by the player. Reviewing role sheets alone does not explain whether the full browser capability circumvents the observation limits.
If you put a window.player on the page at the same time as a window.oracle and hand over any page.evaluate tool to Agent, the player may still bypass PlayerPort.
Playwright official document indicates that you can use the window and document of the page in evaluate and bring the result back to the caller. 2 Thus, under the clear assumptions above, the following author indicates that the call still has a reading path:
/ tectonic example: is only established when the object is actually achieved as a page-wide object.
await page.evaluate(() => window.oracle.inspectTruth());
This is not about claiming that evaluate can read all closed, cross-domain pages or host memory. It states that changing the decorator to an object does not prevent a player who already has the ability to value the page from accessing the global object.
To measure whether this Agent is capable of accomplishing its task on image only, it allows trusted hosts to retain browser control, only exposes the player to screenshots, permitted input actions and filtered responses. Player does not have a common request, source reading, debug protocol or internal file tool. The host can still collect the real values by using evaluate, but the results go into independent evaluation records and do not enter the player's next round of messages. Both records can be associated with the same run ID; the examiner can relate to the real value, not because the running player wants to read it, and the player only receives the end feedback from the orbit permit.
If the detector has any code to execute and complete browser control, the white list of the name of the tool is not enough: there is also a need to isolate process capabilities, documents and communications access. For a fixed strategy that is self-custody and with a high level of confidence, a smaller collection of tools may be suitable for the first edition; such an option cannot be expressed as a generic security isolation. The first version, which does not contain evidence of isolation, can only state the range of trusted access layers and does not give security to open code enforcers.
Boundary determination based on actual player capabilities
Document and communications capacity is reconciled with actual deployment. When a player can read the host's true log, the white list of the page tool still does not protect the information; when the archive is open after the test, the real-time input and subsequent diagnosis are at different stages. First, determining when a player can read the type of document, then considering the isolation option, is more verifiable than the abstract assertion that the three interfaces are isolated.
Another legitimate orbit allows for the reading of the DOM text or open state without necessarily insisting on a pure cut-off. The key is to include competencies in the list so that achievements can be compared with the same permission. The direct placement of the DOM aided orbit on the same list as the pure visual orbit would hide input differences into model capabilities. A description of capabilities and an explanation of achievements is needed before there are subjects to be discussed.
The list of capabilities should also be accompanied by practical tool parameters. For example, while public action allows only specified buttons and waiting, the General Browser script is free to perform, although both are called operational games and vary widely. The check is what the tool actually allows to do, not the name of the tool. The specific containment programme is designed to achieve certification and this list will not be considered as having been controlled by permission.
Check this boundary with two conditions that look the same.
It can start with a very small comparison, which does not yet require customs clearance. Below are the options to be implemented on the real game; this is a limited Python data comparison that is not run, Agent or real person test:
Select two tests for primary A and B only to place the unexplored box in different locations. To ensure that images, public statements, explored history, permissible events and auxiliary status are consistent, and that the historical scope can be reduced by new short run. Performs the same short waiting action to confirm that hidden variables are not accompanied by changes in public scores or animations. The end point of the action and the transfer range are fixed in advance, including whether or not to receive timeout/reconnection tips and when to stop, and cannot wait and search the road.
Saves the full player input transfer and host real value records, respectively. If the player returns with different treasure box coordinates, hidden distance or unpublicly generated events, a specific cross-border field has been found; it is not necessary to wait for a model to use it, let alone replace it with a “model that does not appear to cheat”.
Replaying this contrast after repair is expected to be that the true value record can still distinguish between two locations and that the player message retains only the authorized content. Run ID, transfer time, etc. metadata permissions must be listed and explained in advance and not related to hidden layouts, keeping the original transfer and generating the difference view; new differences follow the path and cannot be temporarily ignored for the same output.
It's just a controlled sample of access checks. If the results are subsequently legally visible, the player ' s message can be different, and the same may be enforced without the public event; the difference is determined when the permitted observation is made. This contrast does not prove that all bypass roads are sealed, nor does it prove that Agent has a human-level ability to explore.
Minimal example: maintain host real, only projectivate message
Saves the full code below as player message projection.py, running python3 player message projection.py. Only the standard library. The input is a memory dictionary. fake-corridor is not a browser screenshot, the treasure box/seed/action tick/ error and score are constructed by the author and there are no real game events, Agent or real people.
This limited orbit accepts only four returns from a fixed wait, one permitted fraction event and two public errors. Public contract is a trusted execution/visibility record pre-set by the author and does not come from the player's self-reported or event's own public sign. Real access requires additional proof of the location and public conditions under which these values are generated, and the dictionary cannot be treated as a universal authorized service. The program selects field explicitly from the host receipt and the nuclear type / permission value. The event selects only the score that is visible in the statement, and the error returns only to the licence category and the protocol to resume the action; any payload, internal log, abnormal details and navigational restoration hint remain with the host.
"""Finite author-designed projections, not GameEval SDK or browser isolation."""
from copy import deepcopy
import json
RECEIPT_FIELDS = ("actionId", "accepted", "endTick", "releaseInputsConfirmed")
ERRORS = {"timeout": "restart_run", "unsupported_action": "choose_supported_action"}
def public_contract(score=None, error=None):
# Trusted, invented executor/visibility records, not player-controlled flags.
return {"observation": "fake-corridor", "visible_score": score, "error_code": error,
"receipt": {"actionId": "action-1", "accepted": True, "endTick": 20,
"releaseInputsConfirmed": True}}
def host_fixture(chest, seed, contract):
truth = {"hiddenChest": chest, "seed": seed, "targetDistance": chest[0]}
return {
"truth": truth, "receipt": {**contract["receipt"], "observedEffect": deepcopy(truth)},
"events": [{"kind": "hidden_spawn", "public": True, "payload": deepcopy(truth)}]
+ ([{"kind": "score_changed", "tick": 20,
"payload": {"score": contract["visible_score"], "debug": deepcopy(truth)}}]
if contract["visible_score"] is not None else []),
"error": ({"code": contract["error_code"], "message": "expected " + str(chest),
"debug": deepcopy(truth)} if contract["error_code"] else None),
"logs": ["host chest=" + str(chest)], "recovery_hint": "go to " + str(chest),
}
def project(host, contract):
receipt = {key: host["receipt"][key] for key in RECEIPT_FIELDS}
if (type(receipt["actionId"]) is not str or type(receipt["accepted"]) is not bool
or type(receipt["endTick"]) is not int
or type(receipt["releaseInputsConfirmed"]) is not bool
or receipt != contract["receipt"]):
raise ValueError("receipt_contract_mismatch")
events = []
for event in host["events"]:
if event["kind"] != "score_changed" or contract["visible_score"] is None:
continue # unknown event names and their public flags grant no permission
score = event["payload"].get("score")
if type(score) is not int or score != contract["visible_score"] or event["tick"] != 20:
raise ValueError("event_contract_mismatch")
events.append({"kind": "score_changed", "tick": 20, "score": score})
raw_error, allowed_error = host["error"], contract["error_code"]
if (raw_error["code"] if raw_error else None) != allowed_error or (
allowed_error is not None and allowed_error not in ERRORS):
raise ValueError("error_contract_mismatch")
return {"observation": contract["observation"], "receipt": receipt, "events": events,
"error": {"code": allowed_error} if allowed_error else None,
"recovery": ERRORS[allowed_error] if allowed_error else None}
def encoded(message):
return json.dumps(message, sort_keys=True, separators=(",", ":"))
def main():
contract = public_contract()
a, b = host_fixture([8, 3], 101, contract), host_fixture([2, 9], 202, contract)
saved_a, saved_b = deepcopy(a), deepcopy(b)
player_a, player_b = project(a, contract), project(b, contract)
assert a["truth"] != b["truth"] and encoded(player_a) == encoded(player_b)
assert a == saved_a and b == saved_b
print("host_truth_equal=false player_messages_equal=true")
print("player=" + encoded(player_a))
checks = 0
for name, field, value in (("hidden_action_id", "actionId", "chest-8-3"),
("hidden_acceptance", "accepted", False),
("hidden_tick", "endTick", 8),
("wrong_type", "accepted", 1)):
changed = deepcopy(a)
changed["receipt"][field] = value
try:
project(changed, contract)
except ValueError as error:
assert str(error) == "receipt_contract_mismatch"
print("reject_" + name + ": " + str(error))
checks += 1
else:
raise AssertionError("bad receipt accepted")
timeout = public_contract(error="timeout")
ea, eb = host_fixture([8, 3], 101, timeout), host_fixture([2, 9], 202, timeout)
assert encoded(project(ea, timeout)) == encoded(project(eb, timeout))
print("errors_with_private_details: same_public_code_and_protocol_recovery")
print("public_error=" + encoded(project(ea, timeout)))
checks += 1
for name in ("logs", "truth", "recovery_hint"):
appended_a, appended_b = {**player_a, "extra": a[name]}, {**player_b, "extra": b[name]}
assert encoded(appended_a) != encoded(appended_b)
print("unsafe_append_" + name + ": paired_messages_differ")
checks += 1
score5, score6 = public_contract(score=5), public_contract(score=6)
visible_a = project(host_fixture([8, 3], 101, score5), score5)
visible_b = project(host_fixture([2, 9], 202, score6), score6)
assert encoded(visible_a) != encoded(visible_b)
assert visible_a["events"][0]["score"] == 5 and visible_b["events"][0]["score"] == 6
print("legitimate_visible_score_change: different_messages_allowed")
bad_event = host_fixture([8, 3], 101, score5)
bad_event["events"][1]["payload"]["score"] = 8
try:
project(bad_event, score5)
except ValueError as error:
assert str(error) == "event_contract_mismatch"
print("reject_event_value: " + str(error))
checks += 1
else:
raise AssertionError("bad event accepted")
assert "hidden_spawn" not in encoded(player_a) and "seed" not in encoded(player_a)
print("unknown_event_and_seed: host_only_for_this_fixture")
print("browser_runs=0 game_runs=0 agent_runs=0 all_channel_isolation=NOT_PROVEN")
print(f"self_checks={checks} boundary_counterexamples_passed")
if __name__ == "__main__":
main()
This machine Python 3.11.6 is actually running, exit code 0; the following is the construction of a real stdout that is sequenced by the program and is not a game transfer actually sent to the model:
host_truth_equal=false player_messages_equal=true
player={"error":null,"events":[],"observation":"fake-corridor","receipt":{"accepted":true,"actionId":"action-1","endTick":20,"releaseInputsConfirmed":true},"recovery":null}
reject_hidden_action_id: receipt_contract_mismatch
reject_hidden_acceptance: receipt_contract_mismatch
reject_hidden_tick: receipt_contract_mismatch
reject_wrong_type: receipt_contract_mismatch
errors_with_private_details: same_public_code_and_protocol_recovery
public_error={"error":{"code":"timeout"},"events":[],"observation":"fake-corridor","receipt":{"accepted":true,"actionId":"action-1","endTick":20,"releaseInputsConfirmed":true},"recovery":"restart_run"}
unsafe_append_logs: paired_messages_differ
unsafe_append_truth: paired_messages_differ
unsafe_append_recovery_hint: paired_messages_differ
legitimate_visible_score_change: different_messages_allowed
reject_event_value: event_contract_mismatch
unknown_event_and_seed: host_only_for_this_fixture
browser_runs=0 game_runs=0 agent_runs=0 all_channel_isolation=NOT_PROVEN
self_checks=9 boundary_counterexamples_passed
The true value of A/B remains different, with the same input contract and the same player JSON; the original host dictionary has not been deleted or modified. The error sample carries different excepted/debugged details on both sides, leaving only the same timeout category and restart run; restarting an agreement that belongs to this example does not point to any hidden room, nor does it mean that a true failure can continue to accumulate the original score.
Nine self-checks over the hidden ID/accepted/tick code, the type mismatch, the error details do not match the restoration projection, the post-simulation additional log/real/old navigational hint, and the open fraction values. Unknown Hidden spawn does not get permission even with his own public=true. When the legitimate open score changes from 5 to 6, the message is different and the program does not always output constants. This case is still based on a record of prior authorization given by the author, and the unverified image really shows scores.
Note that unsafe append * shows a recurrence of discrepancies in A/B messages after intentionally adding host data to the final assembly. It proves that the local projection function cannot send a code for another part of the boundary. The program will not stop if the real log/tool result/ error stack is bypassed by this portal; the detector
Auto-fetched and translated by AI Atlas. All rights belong to the original author.