Skip to content

Commit 66ecdd3

Browse files
committed
feat: record canonical managed operation native-start evidence
Signed-off-by: huangruiteng <huangrt01@163.com>
1 parent f49b4a0 commit 66ecdd3

17 files changed

Lines changed: 359 additions & 18 deletions

‎apps/presentation/dashboard/smoke/action-review-plan-smoke.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,12 @@ check(managedFrame?.kind === "pending" && managedFrame.executionState === "manag
146146
check(managedFrame?.content.fields.some(field => field.value.includes("test-model@xhigh")) === true,
147147
"The shared managed profile survives the frontend schema transport");
148148
check(compileActionReviewPlan(managedPending).canApply === false, "Managed approval exposes no local execute control");
149+
const startedManaged = typedActionProposalSchema.parse({...managedPending,
150+
operation: {...managedPending.operation, host_start: {schema_version: "loopx_operation_host_start_v0", host_turn_id: "native-turn"}}});
151+
const startedFrame = compileActionReviewPlan(startedManaged).operationFrame;
152+
check(startedFrame?.kind === "pending" && startedFrame.executionState === "managed_turn_started",
153+
"Native accepted start survives schema transport without claiming consumption or execution");
154+
check(compileActionReviewPlan(startedManaged).canApply === false, "Accepted native start grants no apply control");
149155
check(compileActionReviewPlan(unknownAgentResult).interaction === "repair", "Delivered unknown submission is not completion");
150156
const reconciledAgentResult = typedActionProposalSchema.parse({...unknownAgentResult,
151157
operation: {...unknownAgentResult.operation, reconciliation: {outcome: "not_executed", simulation: false}}});

‎apps/presentation/dashboard/src/features/personal-workspace/channel-timeline.tsx‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,12 @@ export function ChannelTimeline({
121121
}
122122
if (item.kind === "proposal") {
123123
const appliedTeamPlan = item.proposal.actionKind === "team.plan" && item.proposal.status === "applied";
124+
const pendingOperation = item.proposal.reviewPlan?.operationFrame?.kind === "pending";
124125
return (
125126
<Fragment key={item.id}><button className={`personal-proposal-row is-${item.proposal.status}`} data-action-kind={item.proposal.actionKind} onClick={() => onSelect({ item: item.proposal, kind: "proposal" })} type="button">
126127
<span><Sparkles size={17} /></span>
127-
<span><small>{appliedTeamPlan ? (locale === "zh-CN" ? "团队分配 · 已记录" : "Team assignment · Recorded")
128+
<span><small>{pendingOperation ? t(`proposal.kind.${item.proposal.actionKind}`)
129+
: appliedTeamPlan ? (locale === "zh-CN" ? "团队分配 · 已记录" : "Team assignment · Recorded")
128130
: `${t(`proposal.kind.${item.proposal.actionKind}`)} · ${t(`proposal.status.${item.proposal.status}`)}`}</small><strong>{item.proposal.title}</strong>{item.proposal.impact ? <p>{item.proposal.impact}</p> : null}</span>
129131
<b>{item.proposal.status === "gated" && item.proposal.actionKind !== "operation.execute" ? t("timeline.review") : item.proposal.primaryLabel ?? t("timeline.reviewAndConfirm")}</b>
130132
</button>{showManagerTeamResults && onOpenGoalEvidence && appliedTeamPlan

‎apps/presentation/dashboard/src/features/personal-workspace/context-drawer.tsx‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1102,7 +1102,9 @@ export function ContextDrawer({ agents, attentionHistory = [], onSelectAttention
11021102
{selection.kind === "proposal" ? (
11031103
<>
11041104
{selection.item.actionKind === "team.plan" && selection.item.status === "applied" ? <TeamPlanResult proposal={selection.item} t={t} /> : <section className="personal-proposal-card">
1105-
<small>{selection.item.actionKind} · {selection.item.status}</small>
1105+
<small>{selection.item.reviewPlan?.operationFrame?.kind === "pending"
1106+
? t(`proposal.kind.${selection.item.actionKind}`)
1107+
: `${selection.item.actionKind} · ${selection.item.status}`}</small>
11061108
<h3>{selection.item.title}</h3>
11071109
{selection.item.impact ? <p>{selection.item.impact}</p> : null}
11081110
{selection.item.reviewPlan && !selection.item.reviewPlan.retryOriginal && selection.item.actionKind !== "team.plan" ? <p className="personal-proposal-explainer" data-action-review={selection.item.reviewPlan.interaction}>{operationUnknown

‎apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,7 @@ const en = {
684684
"proposal.field.operationState": "Operation state",
685685
"proposal.operationState.host_authentication_required": "Confirmed; original-host authentication unavailable",
686686
"proposal.operationState.managed_turn_pending": "Confirmed; waiting for the bound managed Turn",
687+
"proposal.operationState.managed_turn_started": "Native continuation accepted; authorization not yet consumed",
687688
"proposal.operationState.consumed_outcome_pending": "Authorization consumed; waiting for the real result",
688689
"proposal.operationState.submission_unknown": "Result unknown; reconcile the original operation, do not resubmit",
689690
"proposal.field.resultDelivery": "Result delivery",
@@ -730,6 +731,7 @@ const en = {
730731
"proposal.impact.operation": "The exact terms are read-only here. Confirm or reject the same immutable request in the bound Feishu group; confirmation consumes one canonical claim.",
731732
"proposal.impact.operationAuthorized": "Human confirmation is recorded, but the original host's authenticated tool transport is not connected. Thread flags or environment ids cannot authorize execution. No external result is recorded yet.",
732733
"proposal.impact.operationManagedPending": "Confirmation is bound to the selected managed session and execution profile, not the source conversation. The admitted Turn must consume it once through its owned tool connection. No execution or external result is established yet.",
734+
"proposal.impact.operationManagedStarted": "The bound native host accepted a continuation containing this confirmed operation. This first-start receipt is not proof that the Turn is still running, that authorization was consumed, or that an external effect occurred.",
733735
"proposal.impact.operationConsumed": "The authorization has been consumed. Wait for original external evidence; a retry or lost response must not grant another submission.",
734736
"proposal.impact.operationUnknown": "Submission may have had an external effect. Reconcile the original operation using its evidence; do not resubmit or treat card delivery as execution completion.",
735737
"proposal.primary.apply": "Confirm and apply",
@@ -1882,6 +1884,7 @@ const zhCN: Record<WorkspaceMessageKey, string> = {
18821884
"proposal.field.operationState": "操作状态",
18831885
"proposal.operationState.host_authentication_required": "已确认,原宿主身份认证尚未接通",
18841886
"proposal.operationState.managed_turn_pending": "已确认,等待绑定的受管回合",
1887+
"proposal.operationState.managed_turn_started": "原生续接已接受,授权尚未消费",
18851888
"proposal.operationState.consumed_outcome_pending": "授权已消费,等待真实结果",
18861889
"proposal.operationState.submission_unknown": "结果未知;核对原操作,不可重复提交",
18871890
"proposal.field.resultDelivery": "结果回传",
@@ -1928,6 +1931,7 @@ const zhCN: Record<WorkspaceMessageKey, string> = {
19281931
"proposal.impact.operation": "这里仅展示同一份不可变条款。请在已绑定的飞书群确认或拒绝;确认只会消费一个规范 claim。",
19291932
"proposal.impact.operationAuthorized": "用户确认已记录,但原宿主的认证工具通道尚未接通。线程参数或环境变量不能授权执行;目前尚无外部执行结果。",
19301933
"proposal.impact.operationManagedPending": "批准绑定选定的受管会话和执行配置,不绑定来源对话。通过准入的回合须在自有工具通道上消费一次;目前不代表已执行,也没有外部结果。",
1934+
"proposal.impact.operationManagedStarted": "绑定的原生宿主已接受包含本确认请求的续接。首次启动回执不证明回合仍在运行、授权已消费或已有外部效果。",
19311935
"proposal.impact.operationConsumed": "授权已消费。等待原始外部证据;重试或响应丢失均不得重新授予提交许可。",
19321936
"proposal.impact.operationUnknown": "提交可能已产生外部副作用。须以原始证据核对原操作,不可重提,也不能把卡片投递当作执行完成。",
19331937
"proposal.primary.apply": "确认并应用",

‎apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-page.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,8 @@ function workspaceProposal(proposal: TypedActionProposal, t: WorkspaceTranslate)
659659
? operationFrame?.kind === "pending" && operationFrame.executionState
660660
? t(operationFrame.executionState === "consumed_outcome_pending"
661661
? "proposal.impact.operationConsumed" : operationFrame.executionState === "managed_turn_pending"
662-
? "proposal.impact.operationManagedPending" : "proposal.impact.operationAuthorized")
662+
? "proposal.impact.operationManagedPending" : operationFrame.executionState === "managed_turn_started"
663+
? "proposal.impact.operationManagedStarted" : "proposal.impact.operationAuthorized")
663664
: operationFrame?.kind === "result" && operationFrame.resultKind === "unknown"
664665
? t("proposal.impact.operationUnknown") : t("proposal.impact.operation")
665666
: proposal.action_kind === "team.plan"

‎docs/architecture/rfcs/human-confirmed-domain-operations-v0.md‎

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -445,10 +445,23 @@ remain fail-closed before private reads or writes, even with matching
445445
`CODEX_THREAD_ID`, route flags, self-signed proof or an older runtime's actor
446446
success. No proof-import shortcut is exposed.
447447

448-
Immediate confirmation-triggered host wakeup is not implemented:
449-
`host_delivery: "not_attempted"` remains truthful. Continue through the
450-
existing admitted Turn/delegation route; later durable wakeup must reuse its
451-
original scheduling/session owner, not start a parallel resumed executor.
448+
An admitted operation-enabled Turn now automatically includes canonical
449+
confirmed-operation locators for its exact Goal/Agent/Todo/session/profile,
450+
filtered before inbox pagination. After the native `turn/start` response is
451+
accepted, the existing action store records immutable first-start evidence:
452+
confirmation event/time, claim, LoopX Turn key, native Turn and acceptance time.
453+
`host_delivery: "native_start_accepted"` means that observation only; it grants
454+
no consumption or effect authority and does not claim the Turn is still live.
455+
Receipt failure aborts before operation-tool dispatch; later Turns preserve the
456+
first observation. CLI/Inbox, Dashboard and Lark distinguish accepted native
457+
continuation from consumed authorization and an actual outcome.
458+
459+
Immediate confirmation-triggered wakeup is still not implemented. The first
460+
observation is from the existing admitted Turn/delegation route, not proof of
461+
an immediate callback-triggered wake. Before it exists, `host_delivery` remains
462+
`"not_attempted"`. Durable wakeup must reuse the original scheduling/session
463+
owner, not start a parallel resumed executor. Outcome return to the original
464+
source audience remains a separate delivery obligation.
452465
Before claiming the investment minimum loop, still prove installation,
453466
genuine human approval, bound native consumption, domain preflight and
454467
original-system evidence, accepted result and original-card/audience readback.

‎docs/architecture/rfcs/human-confirmed-domain-operations-v0.zh-CN.md‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,16 @@ context 调用,结果均由既有 typed result validator 接受)、规范
348348
私有读写前拒绝,即使 `CODEX_THREAD_ID`、路由、自签 proof 完全匹配或旧运行时
349349
意外返回 actor 成功,也没有 proof-import 捷径。
350350

351-
本切片不实现确认后的即时宿主唤醒,`host_delivery: "not_attempted"` 保持真实。
352-
沿既有已准入 Turn/delegation 续接;后续持久唤醒复用其调度/session owner,
353-
不启动平行 resumed 执行者。宣称投研最小闭环前,仍须证明安装、真实用户批准、
351+
启用 operation 的已准入 Turn 自动携带其精确 Goal/Agent/Todo/session/profile 的
352+
规范确认请求定位信息,并在 Inbox 分页前过滤范围。仅在原生 `turn/start` 返回接受后,
353+
既有 action store 才记录不可覆盖的首次启动证据:确认事件/时间、claim、LoopX Turn key、
354+
原生 Turn 和接受时间。`host_delivery: "native_start_accepted"` 只证明该观察,
355+
不授予消费或外部效果权限,也不声称回合仍在运行。回执失败时先停止、不分发 operation 工具;
356+
后续回合保留首次观察。CLI/Inbox、Dashboard 和 Lark 区分原生续接接受、授权消费和真实结果。
357+
358+
确认后的即时唤醒仍未实现。首次观察来自既有已准入 Turn/delegation,不能冒充
359+
即时 callback 触发;未观察前 `host_delivery: "not_attempted"` 保持真实。
360+
后续持久唤醒复用原调度/session owner,不启动平行 resumed 执行者;
361+
向原来源受众返回结果仍是独立交付义务。宣称投研最小闭环前,仍须证明安装、真实用户批准、
354362
绑定原生消费、垂域提交前检查与原系统证据、结果验收及原卡/受众读回。
355363
Core PR 仍须 owner review,不在合并前自行安装。

‎loopx/chat_action_store.py‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,29 @@ def _agent_operation_plan(
10681068
except EffectRuntimeRemoteError as exc:
10691069
raise ActionConflictError(str(exc)) from exc
10701070

1071+
def record_agent_operation_host_start(
1072+
self, proposal_id: str, *, actor: Mapping[str, Any],
1073+
binding_current: bool, turn_key: str,
1074+
) -> dict[str, Any]:
1075+
"""Internal native transport observation; never a caller proof or permit."""
1076+
token = _opaque_id(proposal_id, field="proposal_id")
1077+
with exclusive_file_lock(self.path, operation="observe_operation_host_start"):
1078+
payload = self._read()
1079+
proposal = payload["proposals"].get(token)
1080+
if not isinstance(proposal, dict):
1081+
raise KeyError("typed operation was not found")
1082+
plan = self._agent_operation_plan(
1083+
proposal, action="observe_host_start", actor=dict(actor),
1084+
binding_current=binding_current, turn_key=turn_key,
1085+
)
1086+
receipt = plan.pop("write_host_start", None)
1087+
if receipt is not None:
1088+
proposal["operation"]["host_start"] = receipt
1089+
proposal["updated_at"] = _utc_now()
1090+
self._write(payload)
1091+
plan.update(host_start=receipt, host_delivery="native_start_accepted")
1092+
return plan
1093+
10711094
def consume_agent_operation(
10721095
self,
10731096
proposal_id: str,

‎loopx/control_plane/collaboration/operation_handoff.py‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ def pending_operation_handoffs(
7979
scope: CollaborationGoalScope | None = None,
8080
cursor: str | None = None,
8181
cursor_scope: str,
82+
executor_route: Mapping[str, Any] | None = None,
8283
) -> dict[str, Any]:
8384
"""Project canonical tickets into the existing Inbox; do not copy authority."""
8485
store = (
@@ -155,6 +156,7 @@ def pending_operation_handoffs(
155156
"items": result,
156157
"cursor": cursor,
157158
"cursor_scope": cursor_scope,
159+
"executor_route": dict(executor_route) if executor_route is not None else None,
158160
},
159161
)
160162
)
@@ -171,6 +173,7 @@ def agent_operation_action(
171173
action: str,
172174
consumption_id: str | None = None,
173175
outcome: Mapping[str, Any] | None = None,
176+
turn_key: str | None = None,
174177
) -> dict[str, Any]:
175178
"""Internal locked IO seam, not a public caller-authentication endpoint.
176179
@@ -194,12 +197,12 @@ def agent_operation_action(
194197
goal_id=actor["goal_id"],
195198
agents=(actor["agent_id"],),
196199
caller_goal_ref=parameters.get("origin_goal_ref"),
197-
require_active=action == "consume",
200+
require_active=action in {"consume", "observe_host_start"},
198201
# Recovery-owner binding must stay valid through evidence commit too.
199202
# Use the same Goal -> registry -> action-store ordering as consumption.
200203
lock_registry=True,
201204
) as scope:
202-
if action == "consume":
205+
if action in {"consume", "observe_host_start"}:
203206
decide_collaboration_lifecycle(scope, operation="request_create")
204207
if goal_is_stopped(scope.goal):
205208
raise ActionConflictError("confirmed operation Goal is stopped")
@@ -258,6 +261,7 @@ def agent_operation_action(
258261
action,
259262
consumption_id,
260263
outcome,
264+
turn_key,
261265
)
262266

263267

@@ -272,6 +276,7 @@ def _commit_agent_operation(
272276
action: str,
273277
consumption_id: str | None,
274278
outcome: Mapping[str, Any] | None,
279+
turn_key: str | None,
275280
) -> dict[str, Any]:
276281
current = _binding(registry_path, parameters, runtime_root)
277282
actor_executor = (
@@ -313,6 +318,10 @@ def _commit_agent_operation(
313318
"outcome_report": proposal["operation"].get("outcome_report"),
314319
"reconciliation_report": proposal["operation"].get("reconciliation_report"),
315320
}
321+
if action == "observe_host_start":
322+
return store.record_agent_operation_host_start(
323+
proposal_id, actor=actor, binding_current=current, turn_key=str(turn_key or ""),
324+
)
316325
if action == "consume":
317326
return store.consume_agent_operation(
318327
proposal_id,

‎loopx/control_plane/presentation/action_review_plan.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export type OperationReviewFrame = OperationReviewFrameBase & (
4242
kind: "pending";
4343
attentionKind: "progress";
4444
interactionMode: "inform";
45-
executionState?: "host_authentication_required" | "managed_turn_pending" | "consumed_outcome_pending";
45+
executionState?: "host_authentication_required" | "managed_turn_pending" | "managed_turn_started" | "consumed_outcome_pending";
4646
}
4747
| {
4848
kind: "result";
@@ -332,7 +332,10 @@ export function compileOperationReviewFrame(proposalValue: unknown): OperationRe
332332
interactionMode: "inform",
333333
...(["agent_session", "managed_turn"].includes(String(objectValue(parameters.executor)?.kind))
334334
? {executionState: objectValue(operation.agent_handoff) ? "consumed_outcome_pending" as const
335-
: objectValue(parameters.executor)?.kind === "managed_turn" ? "managed_turn_pending" as const : "host_authentication_required" as const}
335+
: objectValue(parameters.executor)?.kind === "managed_turn"
336+
? objectValue(operation.host_start)?.schema_version === "loopx_operation_host_start_v0"
337+
? "managed_turn_started" as const : "managed_turn_pending" as const
338+
: "host_authentication_required" as const}
336339
: {}),
337340
};
338341
}

0 commit comments

Comments
 (0)