feat(agent): automation-first sessions with step plan, option choice and inline approval
- agent.plan emits plan_updated; system prompt asks for a plan first and updates while executing - agent.propose_options registers a decision and suspends the run (waitForFact, no worker slot); the answer or timeout (recommended/stop) resumes it as the tool result - console-session runs get inline approvals for plan-bound capabilities: approve replays the call with the approval, reject fails it with the reason and records it on the decision - proposeFirst blocks execute capabilities until an option is chosen; E12 covers all paths Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
74c2ed45aa
commit
cf2c4ff0e6
14 files changed
+1360
-26
No files matched your search
@@ -22,8 +22,8 @@ import {
|
||||
type AgentRuntimePort,
|
||||
type CreateRunSpec,
|
||||
emptyUsage,
|
||||
type InjectedEventBody,
|
||||
type RunOutcome,
|
||||
type SubagentEventBody,
|
||||
} from "../runtime/port.ts";
|
||||
import { type ResolvedAgentSettings, resolveAgentSettings } from "../settings.ts";
|
||||
import { compactionFromSettings, type PiModelResolver, resolveModelFromSettings } from "./model.ts";
|
||||
@@ -58,6 +58,8 @@ export const DEFAULT_COMPACTION_INSTRUCTIONS =
|
||||
"保留:本次目标、约束与授权范围、证据引用(对象 ID/记录)、已完成的动作及其结果(已提交≠已入库)、剩余事项。不要保留任何凭证。";
|
||||
|
||||
const LANE = "main";
|
||||
/** 可挂起重放的能力:首次执行时写入的调用级 memo(重放时据此区分) */
|
||||
const STARTED_MEMO = "umt.started";
|
||||
const SUMMARY_LIMIT = 600;
|
||||
|
||||
/** Pi(pi-agent-core AgentHarness)实现的 AgentRuntimePort */
|
||||
@@ -152,15 +154,32 @@ class PiAgentRun implements AgentRunHandle {
|
||||
const { descriptor } = entry;
|
||||
const registry = this.options.registry;
|
||||
const actor = this.spec.actor;
|
||||
const gate = this.spec.replayGate;
|
||||
// 可挂起重放(见 CreateRunSpec.replayGate):对 harness 声明 safe,执行时用 memo 判断是否重放
|
||||
const gated = descriptor.replay === "never" && !!gate;
|
||||
return {
|
||||
name: entry.toolName,
|
||||
label: descriptor.title,
|
||||
description: `${descriptor.description}\n[能力 ${descriptor.id} v${descriptor.version};类别 ${descriptor.kind};风险 ${descriptor.riskLevel}]`,
|
||||
parameters: registry.inputJsonSchema(descriptor.id) as unknown as TSchema,
|
||||
replay: descriptor.replay,
|
||||
replay: gated ? "safe" : descriptor.replay,
|
||||
...(descriptor.kind === "execute" ? { executionMode: "sequential" as const } : {}),
|
||||
execute: async (toolCallId, params, _onUpdate, _toolCtx, invocation, context) => {
|
||||
const signal = context.abortSignal ?? this.abortController.signal;
|
||||
const invocationId = invocation.invocationId ?? toolCallId;
|
||||
if (gated) {
|
||||
const replayed = (await invocation.getMemo(STARTED_MEMO)) === true;
|
||||
if (!replayed) await invocation.setMemo(STARTED_MEMO, true);
|
||||
else if (!gate(descriptor.id, invocationId))
|
||||
throw new Error(
|
||||
JSON.stringify({
|
||||
error: {
|
||||
code: "interrupted",
|
||||
message: "执行被中断,外部结果未知;先用查询工具核对实际状态,不要直接重复提交",
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
try {
|
||||
const output = await registry.invoke(descriptor.id, params, {
|
||||
actorId: actor.actorId,
|
||||
@@ -169,7 +188,7 @@ class PiAgentRun implements AgentRunHandle {
|
||||
...(actor.allowedKinds ? { allowedKinds: actor.allowedKinds } : {}),
|
||||
signal,
|
||||
source: "agent",
|
||||
invocationId: invocation.invocationId ?? toolCallId,
|
||||
invocationId,
|
||||
agentRunId: this.runId,
|
||||
});
|
||||
return {
|
||||
@@ -303,7 +322,7 @@ class PiAgentRun implements AgentRunHandle {
|
||||
await this.lane.abort(BACKGROUND_CONTEXT).catch(() => undefined);
|
||||
}
|
||||
|
||||
appendEvent(body: SubagentEventBody): boolean {
|
||||
appendEvent(body: InjectedEventBody): boolean {
|
||||
if (this.closed) return false;
|
||||
this.emit(body);
|
||||
return true;
|
||||
|
||||
Reference in new issue
Block a user