Skip to content

feat(harness): Initial Claude Code Support - #2602

Open
supreme-gg-gg wants to merge 12 commits into
kagent-dev:mainfrom
supreme-gg-gg:feat/claude-harness
Open

feat(harness): Initial Claude Code Support#2602
supreme-gg-gg wants to merge 12 commits into
kagent-dev:mainfrom
supreme-gg-gg:feat/claude-harness

Conversation

@supreme-gg-gg

@supreme-gg-gg supreme-gg-gg commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Basic support for running Claude Code on Substrate and add extensible interfaces for future harness like Codex.

  • Add a native Claude harness that runs the pinned Claude Code CLI and supports A2A streaming, cancellation, session resume, and durable state.
  • Add Claude template compilation for Anthropic, Amazon Bedrock, and Vertex AI, and shared local subagents
  • Add Claude support for remote MCP servers, standalone skills, and plugin-provided skills, with runtime-neutral resource materialization and configuration warnings.
  • Wire the Claude harness into the controller and build/release pipelines, including its container image and configurable controller logging.
  • Add comprehensive unit and E2E coverage for Claude interactions, persistence, fork and resume, cancellation, built-in tools, subagent routing, and MCP calls.
  • Merge adjacent streamed text parts in the UI so Claude responses render as continuous messages. (However, UI is not completely working due to ListTasks returns a task's messages and artifacts as two unordered lists, so a transcript cannot be replayed in order #2584)

@supreme-gg-gg supreme-gg-gg Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Note to reviewer: See #2603

@supreme-gg-gg supreme-gg-gg Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Note to reviewer: #2595 removed some functionality that the UI needs

@Charlesthebird Charlesthebird Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks! I added a bug issue here: #2613 that this PR should close.

Claude picked up that "FeedbackService" and "MemoryService" are not registered here - but they also don't seem to be used anywhere at the moment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

FeedbackService isn't used anywhere I think, but MemoryService is required by ADK to enable memory, I'll add it in a follow up

- [x] Durable Claude session resume between turns
- [x] Claude Code built-in tools
- [x] Shared local subagents
- [x] Standalone skills and plugin-provided skills

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Note to reviewer: This short list summarizes what has been done and what will come next for Claude harness.
Plugin provided skills are not entirely working due to #2604

@supreme-gg-gg
supreme-gg-gg marked this pull request as ready for review August 27, 2026 21:34
@supreme-gg-gg

Copy link
Copy Markdown
Contributor Author

Codex review

[P1] Terminate the entire Claude process groupgo/harness/claude/internal/driver/process.go:206-213

When a turn is canceled or parsing/event delivery fails while Claude has spawned a built-in tool process, signaling and killing only cmd.Process leaves its child processes running. Those orphaned commands can continue modifying the durable workspace or consuming resources after the task is reported canceled; start Claude in its own process group and terminate the group instead.

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Aug 27, 2026
Charlesthebird pushed a commit that referenced this pull request Aug 28, 2026
When I was working on #2602 I noticed some UI code was redundant after
removal in #2598. This PR removes them

---------

Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>

@Charlesthebird Charlesthebird left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I tested the UI fixes here and they are working well 🎉 - I haven't tested the claude harness parts though.

@EItanya EItanya left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Initial review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does this need to be nested inside of go/harness/claude/cmd/kagent-claude/main.go
Since it's the only main.go can't it just be go/harness/claude/cmd/main.go

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is mainly for the binary to keep the kagent-claude name instead of a generic cmd, I think it matches the Kagent CLI in go/core/cli/cmd/kagent

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You can always name the binary with -o

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's true I forgot, I'll move it 😓

Comment thread go/api/adk/types.go Outdated
}
// These aliases preserve the existing ADK configuration API while sharing one
// runtime-neutral resource model with other Harness adapters.
type AgentPluginConfig = agentplugin.Resources

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why do we need all of these aliases, can't we just use the actual types?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is there a more secure/lighter weight node runtime we could use so we don't have to deal with the CVEs from node:22-trixie-slim

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We don't need a node runtime since it's now a native binary, I've switched to an alpine image

const (
claudeConfigDirEnv = "CLAUDE_CONFIG_DIR"
disableUpdaterEnv = "DISABLE_AUTOUPDATER"
googleCredsEnv = "GOOGLE_APPLICATION_CREDENTIALS"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What's this for?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The Vertex AI credentials are loaded into a /tmp file instead of the ADC default path since home directory /data is durable and snapshotted and this avoids the credentials from being persisted


const (
claudeConfigDirEnv = "CLAUDE_CONFIG_DIR"
disableUpdaterEnv = "DISABLE_AUTOUPDATER"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also, what's this for?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This keeps the runtime on the pinned Claude version by the image; I've changed I to DISABLE_UPDATES so both auto and manual updates are disabled. This also ensures that harness-provided env var cannot override this to re-enable update

// Input contains compiler output and Actor-owned locations used to construct
// the Claude driver.
type Input struct {
Context context.Context

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

context shouldn't be in a struct generally, is this necessary, can we just pass it as an arg?

Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Comment thread go/adk/pkg/a2a/executor.go Outdated
// Work around upstream ADK's artifact-only event conversion: its callbacks can
// mutate an artifact but cannot replace it with another A2A event. Do this before
// a2a-go persists the update; remove when ADK exposes a general event converter.
if update, ok := event.(*a2atype.TaskArtifactUpdateEvent); ok && artifactContainsToolEvent(update.Artifact) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please swap this for #2621

@@ -0,0 +1,18 @@
//go:build !unix

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we need !unix?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've renamed this to use _windows.go suffix instead, since Claude Code can only run on either unix or windows

Comment on lines +28 to +30
if durableDir == "" {
return nil, fmt.Errorf("durable directory is required")
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is a claudism, it always checks args that it doesn't need to

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Have it run through all instances like this

supreme-gg-gg and others added 2 commits August 28, 2026 13:44
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants