Skip to content

refactor(agent-core-v2): decouple workspace from session DI via runtime binding - #2961

Open
sailist wants to merge 1 commit into
MoonshotAI:mainfrom
sailist:refact-007-08-13-workspace-os-backend-feature
Open

refactor(agent-core-v2): decouple workspace from session DI via runtime binding#2961
sailist wants to merge 1 commit into
MoonshotAI:mainfrom
sailist:refact-007-08-13-workspace-os-backend-feature

Conversation

@sailist

@sailist sailist commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue — the problem is explained below.

Problem

Workspace used to be a DI scope between App and Session: session lifecycle was owned by a Workspace-scoped handler, and every execution resource (fs, process, watch, terminal) was implicitly resolved against the local machine through App-level host services. One workspace could not carry multiple execution environments, and business ownership kept leaking into the DI topology.

What changed

  • DI topology: removed LifecycleScope.Workspace; the scope chain is now App → Session → Agent. An App-level SessionManager owns session create/resume/fork/close, and workspaces are managed as plain business objects (metadata, trust, dirs) instead of scopes.
  • Runtime layer: new runtime contract (identity, capabilities, status, fs/process/watch/terminal) with a per-workspace registry of immutable generations, lease-based access, capability-secured registration handles, and a provider × workspace attachment matrix. Ships a production local runtime and a deterministic fake for tests.
  • Program: each workspace owns a Program holding skills, agents, instructions, MCP config and baseline stdio MCP on a fixed local binding, with preparing/ready/degraded readiness.
  • Agent: agents hold a mutable, persisted runtime binding; subagents inherit a snapshot at creation. Tool availability reflects runtime status/capabilities, and all OS tools (Read/Write/Edit/Bash/Grep/Glob/ReadMediaFile) execute through the resolved runtime lease.
  • kap-server: fs actions, file download, workspace search, terminal create and WS fs-watch accept an optional runtime_id that defaults to local, so existing clients keep working. New GET/POST /api/v1/sessions/{id}/runtime endpoints expose the binding. The debug surface moves from workspace-scope reflection calls to read-only business snapshots.
  • SDK & apps: node-sdk gains session runtime inspect/switch APIs; the TUI adds a /runtime command; klient and acp-server are wired to the same binding.
  • Removals: workspace-scoped registrations, ancestor session seed adapters, the os-backend-driven tool policy, and the default session process runner.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

@changeset-bot

changeset-bot Bot commented Aug 16, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 9ef2b73

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

@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: 980703b65e

ℹ️ 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".

Comment on lines +141 to 144
lifecycle.onWillCreateSession((event) => {
const servers = event.readSeed(ISessionEphemeralMcpServers);
if (Object.keys(servers).length === 0) return;
const overlay = this.sessionOverlay(servers, {

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 Filter MCP session hooks by workspace ownership

When two workspaces have been materialized and a session is created with ephemeral MCP servers, every WorkspaceMcpService subscribes to the App-level ISessionManager.onWillCreateSession event and this callback never checks the session's workspaceId. All workspace instances therefore create overlays and repeatedly provide ISessionMcpHandle; the last listener wins, potentially combining the session overlay with another workspace's baseline MCP manager and runtime. Route the event only to the owning Program/controller, or at minimum reject events whose ISessionContext.workspaceId differs.

AGENTS.md reference: packages/agent-core-v2/AGENTS.md:L7-L7

Useful? React with 👍 / 👎.

Comment on lines 178 to 180
if (!('type' in server)) {
out[server.name] = {
transport: 'stdio',
command: server.command,
args: server.args,
env: namedPairsToRecord(server.env),
};
continue;
throw new Error(`ACP stdio MCP server ${server.name} does not declare a runtime identity`);
}

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 ACP stdio MCP support

When an ACP client supplies a standard stdio MCP server in session/new, load, resume, or fork, the server has no type discriminator or runtime field, so this unconditional throw rejects the entire request. Stdio is the ACP baseline and this server still describes it as supported during initialization; previously these entries were converted successfully. Assign the appropriate local/session runtime internally instead of requiring a wire field the ACP server shape cannot carry.

Useful? React with 👍 / 👎.

Comment on lines +155 to +158
const runtimeId = acpRuntimeProvider.bindSession(context.workspaceId, sessionId, context.cwd);
sessionWorkspaces.set(sessionId, context.workspaceId);
const agent = await ensureMainAgent(handle, { runtimeId });
agent.accessor.get(IAgentRuntimeBindingService).switch(runtimeId);

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 Keep local process fallback without terminal capability

For ACP clients that do not advertise the optional terminal capability, this callback still binds and switches every main agent to the process-only ACP runtime. That runtime advertises process, so Bash remains available, but AcpProcessService.spawn rejects every invocation with ACP terminal capability is unavailable; before this change such calls fell back to the local process service. Only switch to the ACP runtime when terminal support is enabled, or retain the local fallback.

Useful? React with 👍 / 👎.

Comment on lines +274 to +277
const relative = sw.runtime.path.relative(sw.view.workDir, event.path);
const path = relative === '' ? '.' : relative.split(sw.runtime.path.separator).join('/');
if (!isUnderAny(path, sw.union)) return;
sw.pending.push({ path, change: event.action, kind: event.kind });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restore ignore filtering for runtime file watches

When a client watches . in a repository, this direct runtime event path forwards every .git and gitignored change because the only filter is the requested-path union. The replaced WorkspaceFsWatchService explicitly excluded .git/ and loaded the workspace .gitignore; losing that filtering can turn ordinary Git operations or generated-file churn into noisy or truncated event.fs.changed batches. Apply equivalent ignore matching before adding the event to pending.

Useful? React with 👍 / 👎.

@sailist
sailist force-pushed the refact-007-08-13-workspace-os-backend-feature branch from 980703b to 9ef2b73 Compare August 16, 2026 02:38
@pkg-pr-new

pkg-pr-new Bot commented Aug 16, 2026

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

commit: 9ef2b73

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