Skip to content

refactor(agent-core-v2): absorb the human layer and replace the agent loop with a facade over its turn state machine - #3580

Merged
sailist merged 12 commits into
MoonshotAI:mainfrom
sailist:refact-151-09-06-cherry-pick-110-fork
Sep 6, 2026
Merged

refactor(agent-core-v2): absorb the human layer and replace the agent loop with a facade over its turn state machine#3580
sailist merged 12 commits into
MoonshotAI:mainfrom
sailist:refact-151-09-06-cherry-pick-110-fork

Conversation

@sailist

@sailist sailist commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

N/A — internal architecture refactor, no user-facing behavior change intended.

Problem

The human execution layer (hand-written xstate turn/agent machines and the llm layer) graduated as an architecture experiment but lived outside production agent-core-v2, which ran its own step-request Loop — two parallel execution models for the same agent runtime. This PR absorbs the layer into v2 as src/human/ and replaces the v2 loop's execution core with its turn state machine, so a single execution model remains.

What changed

Absorb the human layer into v2 (src/human)

  • The layer's sources move into packages/agent-core-v2/src/human/; its standalone package is deleted.
  • v2's vendored src/kosong is mostly deleted; the parts still in use are renamed to src/llm-adapter/.
  • Imports rewired across agent-core-v2, klient, kap-server, node-sdk, acp-server, vis, and kimi-inspect; import-boundary check updated (src/llm-adapter and the new src/agent/loop/machine are the only adapter scopes allowed past the human vocabulary modules).
  • The media attachment name preservation from feat(protocol): preserve media attachment names #3548 is carried into the human message contract (name?: string on image/video URL parts), so its wire/projection behavior does not regress.
  • Follow-up dedup: usage helpers and error classification in llm-adapter now come from #human/llm/* instead of being duplicated.

Agent loop replaced by the human turn state machine (facade, no queue state)

  • src/agent/loop no longer holds queue state: the StepRequest/admission system (stepRequest, stepRequestQueue, loopContinuation, handoffStep, prompt step requests, and the stepRetry engine) is deleted. Turn queueing, mid-turn steer, notifications, and reminders run entirely on the agent machine's internal state.
  • IAgentLoopService becomes a facade with intent-style methods (submit / steer / notify / cancel / status / quiescence) mapped onto machine events and snapshots; consumers (prompt, task, goal, external hooks, tool dedupe, full compaction) migrate to it.
  • A new adapter layer src/agent/loop/machine/ bridges the frozen src/human/ machines to v2 services: the layer's LlmRequester over IAgentLLMRequesterService (event bridging, error-kind mapping for machine retry decisions), tool definitions over IAgentToolExecutorService (permission/telemetry/truncation path unchanged), and an engine that normalizes machine emissions into step-granular events.
  • All existing wire surfaces are re-projected from machine events: turn events (started/step/completed/interrupted/deltas), durable turn ops (turnKey state incl. cancelled-queued turns), telemetry, hooks (onWillBeginStep/onDidFinishStep), TurnStepRetrying, and filtered error mapping. kap-server / klient / TUI see no protocol change.
  • Deliberate calls: maxSteps keeps exact v2 semantics via facade counting (the machine's reset-on-drain semantics are not passed through); context-overflow compaction stays a LoopErrorHandler because machine recovery proposals are synchronous and cannot host the async compaction flow.
  • acp-server test helpers: the scripted provider stub now implements gateway resolve(model) (it previously ducked the interface via casts), matching the requester-based model resolution.

Verification

  • test/agent/loop 65/65, test/agent 1858/1858, test/features 949/949, acp-server 149/149, klient 129/129 green with zero net test-count change.
  • Full monorepo suite on a remote runner: 20684 tests, the only remaining failures were the acp-server stub issue above (fixed in this stack) plus one pre-existing environment flake.
  • Repo-wide pnpm typecheck exit 0; check-no-comments, check-import-boundaries, and oxlint --type-aware clean.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue (external PRs: the issue must have a maintainer's /approve). — N/A, internal refactor.
  • I have added tests that prove my feature works. — Behavior is pinned by the existing loop/agent/features suites, kept green unchanged (net-zero test count).
  • Ran gen-changesets skill, or this PR needs no changeset. — No changeset: internal refactor with no user-perceivable change.
  • Ran gen-docs skill, or this PR needs no doc update. — No doc update.

@changeset-bot

changeset-bot Bot commented Sep 6, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: a98f90d

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Sep 6, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@a98f90d
npx https://pkg.pr.new/@moonshot-ai/kimi-code@a98f90d

commit: a98f90d

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5e38e379cf

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

progressHandlers.set(input.toolCall.id, input.onUpdate);
try {
let matched: ToolExecutionResult | undefined;
for await (const result of options.toolExecutor.execute([input.toolCall], {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve the full tool batch for pre-execution guards

When one model response contains AgentSwarm alongside another tool, or contains multiple AgentSwarm calls, invoking the executor separately with [input.toolCall] makes every BeforeToolExecuteEvent.toolCalls value a singleton. The guard in features/swarm/agent/swarmService.ts therefore cannot detect the forbidden mixed or repeated swarm batch and allows all calls to launch; retain the original response batch when adjudicating each call.

Useful? React with 👍 / 👎.

Comment on lines +115 to +118
if (
result.isError === true &&
(result.stopBatchAfterThis === true || result.stopTurn === true)
) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Stop successful terminal tools from running later calls

When a successful tool returns stopBatchAfterThis: true or stopTurn: true, this isError condition leaves batchStopped false, so the serialized calls queued after it still execute. For example, successful UpdateGoal completion/blocking and over-budget SetGoalBudget results carry these stop signals specifically to prevent later calls from running, so a model response containing one of them followed by another side-effecting tool violates the requested stop.

Useful? React with 👍 / 👎.

@sailist
sailist force-pushed the refact-151-09-06-cherry-pick-110-fork branch from 5e38e37 to ba05a69 Compare September 6, 2026 12:06
@sailist sailist changed the title refactor(agent-core-v2): absorb agent-core-v3 and replace the agent loop with the human turn state machine refactor(agent-core-v2): absorb the human layer and replace the agent loop with a facade over its turn state machine Sep 6, 2026
…ne tools adapter

- delegate each step's tool calls to the v2 tool executor as one whole
  batch: beginBatch arms the expected call set from the llm.done entry,
  per-actor execute calls collect into a pending barrier, and the full
  batch is submitted in tool-call order once complete
- drop the serial batchChain/batchStopped/skipped approximation so the
  executor's prepare-stage stopBatchAfterThis skip, veto short-circuit,
  and ToolScheduler access-based concurrency apply natively again
@sailist

sailist commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator Author

@codex

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5f19e2551b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

if (job === undefined || job.turn.state !== 'queued') return false;
const reservation = this.reservations.find((entry) => entry.id === turnId);
if (reservation === undefined || reservation.cancelled) return false;
reservation.cancelled = true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove canceled reservations held by quiescence

When a turn is submitted while an idle quiescence lease is held and then canceled before the lease is released, this only marks the unlaunched reservation as canceled and leaves it at the head of reservations. A later submission launches its machine prompt, but bindMachineTurn() consumes that stale canceled reservation, suppresses the machine turn, and leaves the new reservation permanently marked as launched with no prompt queued, so the subsequent turn never starts or settles. Remove an unlaunched reservation when canceling it, while retaining launched placeholders needed to align with the machine queue.

Useful? React with 👍 / 👎.

export function createMachineTools(options: CreateMachineToolsOptions): MachineTools {
const extras = new Map<string, ToolResultExtras>();
const progressHandlers = new Map<string, ((update: ToolUpdate) => void) | undefined>();
const knownNames = new Set(options.toolInfos.map((info) => info.name));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Refresh the tool-name set when registrations change

Fresh evidence after the earlier batch fix is that knownNames is still captured once when the engine is first created, while MCP reconnect/discovery and user/runtime tool activation can register tools later. Calls to those tools are consequently treated as unknown by the machine and excluded from expectedIds; the fallback in executeUnknownToolCall() then invokes the executor separately, so a response mixing a newly registered tool with AgentSwarm again hides the full batch from the swarm guard and can launch a forbidden mixed batch. Build the adapter from the current registry for each batch or otherwise update its definitions when registrations change.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant