mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-16 04:18:17 +08:00
cover revoked account before queued Goal replay
This commit is contained in:
parent
3a7bbe4645
commit
cc3aa3e765
@ -18,7 +18,7 @@ Prefix: `/api/v1/goals/{goalId}/json-acceptance`. An enabled account with conver
|
||||
|
||||
Publication requires an active or paused goal and a slot referenced by a current requirement. Content must be a strict JSON object: duplicate keys, trailing documents, nesting beyond 32 levels and UTF-8 content over 1 MiB are rejected. Each goal can retain at most 32 versions; the limit rejects new publication instead of overwriting history. Each version expires after 24 hours. Republishing identical bytes still creates a new version. Reload after a generation conflict rather than automatically overwriting another publication. Retries can reuse a suitable current version and refresh its check binding; reaching the quota still permits checking and completing with that version. If the version is expired or its content must change and all 32 versions are used, further publication remains unavailable.
|
||||
|
||||
Managed bodies live independently in the database. Ordinary workspace files, cache paths and hashes in text are not substitutes. No publication API edits historical bodies; bodies and pointers commit together. SHA-256 identifies content and supports integrity checks; it does not isolate an attacker with database credentials or host privileges. The database and service host are trusted foundations of this limited protocol. The JSON service contract has been exercised on H2, MySQL 8.0.46 and PostgreSQL 16.14. MySQL and PostgreSQL each passed 72 JSON protocol cases and 13 opt-in HTTP approval/authentication cases with V201 on cycle062. The MySQL run isolated an existing V192 migration failure using a test-only migration copy; PostgreSQL used the original Kingbase migration tree while skipping an unrelated bundled-skill import failure. These are protocol tests, not confirmation that an unmodified full installation succeeds. The proprietary Kingbase engine has not been tested.
|
||||
Managed bodies live independently in the database. Ordinary workspace files, cache paths and hashes in text are not substitutes. No publication API edits historical bodies; bodies and pointers commit together. SHA-256 identifies content and supports integrity checks; it does not isolate an attacker with database credentials or host privileges. The database and service host are trusted foundations of this limited protocol. The JSON service contract has been exercised on H2, MySQL 8.0.46 and PostgreSQL 16.14. MySQL and PostgreSQL each passed 72 JSON protocol cases with V201 on cycle062 and 15 opt-in HTTP approval/authentication cases on cycle065. The MySQL run isolated an existing V192 migration failure using a test-only migration copy; PostgreSQL used the original Kingbase migration tree while skipping an unrelated bundled-skill import failure. These are protocol tests, not confirmation that an unmodified full installation succeeds. The proprietary Kingbase engine has not been tested.
|
||||
|
||||
## Agent publication
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
仅当前要求引用的槽可发布,Goal 必须 active 或 paused。正文必须是严格 JSON 对象,拒绝重复键、尾随文档、超过 32 层的嵌套及超过 1 MiB 的 UTF-8 内容。每个 Goal 最多保存 32 个版本;达到配额拒绝继续发布,不覆盖旧版本。每版有效期 24 小时,重复发布同样正文也产生新版本。客户端遇到 generation 冲突应重新读取,不自动覆盖他人发布。重试可以复用适用的当前版本并更新检查绑定;达到配额后仍可检查当前版本并在合格时完成。如果版本已过期或正文必须修改且32个版本均已使用,则不能继续发布。
|
||||
|
||||
这些版本独立存储在数据库,不能用普通工作区文件、缓存路径或文字中的 hash 替代。发布接口不支持更新历史正文;所有版本与槽指针同事务保存。SHA-256 用于标识及完整性核对,不能隔离拥有数据库凭据或宿主权限的攻击者;数据库和服务宿主是此有限协议的可信基础。JSON 服务契约已在 H2、MySQL 8.0.46 和 PostgreSQL 16.14 上实测。MySQL与PostgreSQL在带V201的cycle062源码上各通过72项JSON协议案例及13项显式启用的HTTP审批/认证案例。MySQL 使用仅修正既有 V192 失败的临时迁移副本;PostgreSQL 使用原始 Kingbase 迁移树,跳过无关的内置技能导入失败。这是协议验证,不能代表未修改的完整安装成功;尚未实测 Kingbase 专有引擎。
|
||||
这些版本独立存储在数据库,不能用普通工作区文件、缓存路径或文字中的 hash 替代。发布接口不支持更新历史正文;所有版本与槽指针同事务保存。SHA-256 用于标识及完整性核对,不能隔离拥有数据库凭据或宿主权限的攻击者;数据库和服务宿主是此有限协议的可信基础。MySQL与PostgreSQL在带V201的cycle062源码上各通过72项JSON协议案例,在cycle065各通过15项显式启用的HTTP审批/认证案例。MySQL 使用仅修正既有 V192 失败的临时迁移副本;PostgreSQL 使用原始 Kingbase 迁移树,跳过无关的内置技能导入失败。这是协议验证,不能代表未修改的完整安装成功;尚未实测 Kingbase 专有引擎。
|
||||
|
||||
## 代理发布
|
||||
|
||||
|
||||
@ -19,7 +19,8 @@ class GoalJsonExternalApprovalIntegrationTest extends GoalJsonHttpRuntimeIntegra
|
||||
"false,legacy-terminal-approval,true", "true,legacy-terminal-approval,true",
|
||||
"false,originless-terminal-approval,true", "true,originless-terminal-approval,true",
|
||||
"false,late-terminal-approval,true", "true,late-terminal-approval,true",
|
||||
"false,queued-terminal-approval,true", "true,queued-terminal-approval,true"})
|
||||
"false,queued-terminal-approval,true", "true,queued-terminal-approval,true",
|
||||
"false,queued-revoked-approval,true", "true,queued-revoked-approval,true"})
|
||||
void authenticatedGoalCompletesThroughHttpOrScheduledProductionRuntime(
|
||||
boolean plan, String entry, boolean accepted) throws Exception {
|
||||
super.authenticatedGoalCompletesThroughHttpOrScheduledProductionRuntime(plan, entry, accepted);
|
||||
|
||||
@ -94,7 +94,8 @@ class GoalJsonHttpRuntimeIntegrationTest {
|
||||
"false,legacy-terminal-approval,true", "true,legacy-terminal-approval,true",
|
||||
"false,originless-terminal-approval,true", "true,originless-terminal-approval,true",
|
||||
"false,late-terminal-approval,true", "true,late-terminal-approval,true",
|
||||
"false,queued-terminal-approval,true", "true,queued-terminal-approval,true"})
|
||||
"false,queued-terminal-approval,true", "true,queued-terminal-approval,true",
|
||||
"false,queued-revoked-approval,true", "true,queued-revoked-approval,true"})
|
||||
void authenticatedGoalCompletesThroughHttpOrScheduledProductionRuntime(boolean plan, String entry, boolean accepted) throws Exception {
|
||||
boolean approval = entry.endsWith("approval");
|
||||
boolean doubleApproval = entry.equals("scheduled-double-approval");
|
||||
@ -102,13 +103,15 @@ class GoalJsonHttpRuntimeIntegrationTest {
|
||||
boolean terminal = entry.contains("terminal-");
|
||||
boolean lateTerminal = entry.startsWith("late-");
|
||||
boolean queuedTerminal = entry.startsWith("queued-terminal-");
|
||||
boolean queuedRevoked = entry.startsWith("queued-revoked-");
|
||||
boolean queuedPreflightRejected = queuedTerminal || queuedRevoked;
|
||||
boolean detached = entry.contains("detached");
|
||||
boolean foreign = entry.contains("foreign");
|
||||
boolean supervised = entry.startsWith("supervised");
|
||||
boolean scheduled = entry.startsWith("scheduled") || entry.equals("recovered") || supervised;
|
||||
boolean reuse = entry.equals("reuse");
|
||||
boolean recheck = entry.equals("recheck");
|
||||
boolean queued = entry.equals("queued") || queuedTerminal;
|
||||
boolean queued = entry.equals("queued") || queuedPreflightRejected;
|
||||
boolean recovered = entry.equals("recovered") || entry.equals("supervised-recovered");
|
||||
String username = "http-json-" + UUID.randomUUID();
|
||||
String conversation = UUID.randomUUID().toString();
|
||||
@ -327,7 +330,7 @@ class GoalJsonHttpRuntimeIntegrationTest {
|
||||
assertTrue(coordinator.settle(run, outcome, java.time.LocalDateTime.now()));
|
||||
assertEquals("waiting_approval", continuations.get(goal.getId()).state());
|
||||
waiting = outcome.toString();
|
||||
} else if (queuedTerminal) {
|
||||
} else if (queuedPreflightRejected) {
|
||||
String queuedToken = token;
|
||||
var initialTurn = java.util.concurrent.CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
@ -347,8 +350,12 @@ class GoalJsonHttpRuntimeIntegrationTest {
|
||||
assertEquals(userId, jdbc.queryForObject("SELECT requester_user_id FROM mate_conversation_input_queue WHERE id=?", Long.class, queueId));
|
||||
assertEquals(goal.getId(), jdbc.queryForObject(
|
||||
"SELECT selected_goal_id FROM mate_conversation_input_queue WHERE id=?", Long.class, queueId));
|
||||
goals.abandon(goal.getId(), username);
|
||||
assertEquals(GoalStatus.ABANDONED, goals.getById(goal.getId()).getStatus());
|
||||
if (queuedTerminal) {
|
||||
goals.abandon(goal.getId(), username);
|
||||
assertEquals(GoalStatus.ABANDONED, goals.getById(goal.getId()).getStatus());
|
||||
} else {
|
||||
jdbc.update("UPDATE mate_user SET enabled=FALSE WHERE id=?", userId);
|
||||
}
|
||||
String initialAnswer = plan
|
||||
? "{\"needs_planning\":false,\"direct_answer\":\"Initial fixture turn finished.\"}"
|
||||
: "Initial fixture turn finished.";
|
||||
@ -380,16 +387,16 @@ class GoalJsonHttpRuntimeIntegrationTest {
|
||||
waiting = requestBody("POST", "/api/v1/chat/stream", token,
|
||||
Map.of("agentId", String.valueOf(agentId), "conversationId", conversation, "message", message));
|
||||
}
|
||||
JsonNode pending = request("GET", "/api/v1/chat/" + conversation + "/pending-approvals", token, null).path("data");
|
||||
if (queuedTerminal) {
|
||||
assertEquals(0, pending.size(), waiting);
|
||||
if (queuedPreflightRejected) {
|
||||
assertTrue(waiting.contains("queued_input_skipped"), waiting);
|
||||
assertEquals(0, calls.get(), "A terminal queued Goal must not invoke the model");
|
||||
assertEquals(GoalStatus.ABANDONED, goals.getById(goal.getId()).getStatus());
|
||||
assertEquals(0, calls.get(), "A stale queued Goal must not invoke the model");
|
||||
assertEquals(queuedTerminal ? GoalStatus.ABANDONED : GoalStatus.ACTIVE,
|
||||
goals.getById(goal.getId()).getStatus());
|
||||
assertEquals(0, jdbc.queryForObject("SELECT COUNT(*) FROM mate_tool_approval WHERE conversation_id=?", Integer.class, conversation));
|
||||
assertEquals(0, jdbc.queryForObject("SELECT COUNT(*) FROM mate_goal_json_artifact WHERE goal_id=?", Integer.class, goal.getId()));
|
||||
return;
|
||||
}
|
||||
JsonNode pending = request("GET", "/api/v1/chat/" + conversation + "/pending-approvals", token, null).path("data");
|
||||
assertEquals(1, pending.size(), waiting);
|
||||
String pendingId = pending.get(0).path("pendingId").asText();
|
||||
assertEquals("getManagedGoalJsonSlots", pending.get(0).path("toolName").asText());
|
||||
|
||||
Loading…
Reference in New Issue
Block a user