From 6dd2f43d913fb56496c54608fe493120cdb53560 Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Mon, 19 Oct 2026 08:40:20 +0000 Subject: [PATCH] Run Codex Subagents and Skills natively --- .../agent/codex/harness_config_test.go | 12 ++---- .../internal/agent/codex/hosted_skills.go | 41 ------------------- .../agent/codex/hosted_skills_test.go | 36 ---------------- .../internal/agent/codex/native_hooks.go | 39 ------------------ .../internal/agent/codex/preparation.go | 2 +- apps/daemon/internal/agent/codex/protocol.go | 20 +-------- .../agent/codex/protocol_wire_test.go | 26 ------------ .../internal/agent/codex/session_plan.go | 4 -- .../internal/agent/codex/session_run.go | 17 -------- .../internal/agent/codex/subagent_profile.go | 39 ++++-------------- .../agent/codex/subagent_profile_test.go | 35 +++++++++------- .../agent/codex/subagent_snapshots.go | 3 +- contracts/agents-api/environments.md | 2 +- contracts/agents-api/subagents.md | 2 +- contracts/agents-api/zh/environments.md | 4 +- contracts/agents-api/zh/subagents.md | 4 +- 16 files changed, 44 insertions(+), 242 deletions(-) delete mode 100644 apps/daemon/internal/agent/codex/hosted_skills.go delete mode 100644 apps/daemon/internal/agent/codex/hosted_skills_test.go delete mode 100644 apps/daemon/internal/agent/codex/native_hooks.go diff --git a/apps/daemon/internal/agent/codex/harness_config_test.go b/apps/daemon/internal/agent/codex/harness_config_test.go index cf2c28dfb..7b9150ccc 100644 --- a/apps/daemon/internal/agent/codex/harness_config_test.go +++ b/apps/daemon/internal/agent/codex/harness_config_test.go @@ -8,6 +8,7 @@ import ( "github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto" "github.com/MiniMax-AI/OpenAgentCore/internal/harnessconfig" "slices" + "strings" "testing" ) @@ -40,7 +41,7 @@ func TestHarnessConfigConflictFailsBeforePreparation(t *testing.T) { } } -func TestReasoningEffortReachesEveryNativeTurn(t *testing.T) { +func TestNativeTurnsCarryNoCollaborationMode(t *testing.T) { for _, resumeID := range []string{"", "fixture-native-thread"} { t.Run("resume="+resumeID, func(t *testing.T) { req, cfg, root := preparationFixture(t) @@ -78,13 +79,8 @@ func TestReasoningEffortReachesEveryNativeTurn(t *testing.T) { if frame.Method != "turn/start" { continue } - var wire struct { - CollaborationMode struct { - Settings map[string]any `json:"settings"` - } `json:"collaborationMode"` - } - if json.Unmarshal(frame.Params, &wire) != nil || wire.CollaborationMode.Settings["reasoning_effort"] != "high" { - t.Fatal("Turn lost the frozen native reasoning effort", string(frame.Params)) + if strings.Contains(string(frame.Params), "ollaborationMode") { + t.Fatal("turn/start must not carry a collaboration mode", string(frame.Params)) } } method := "thread/start" diff --git a/apps/daemon/internal/agent/codex/hosted_skills.go b/apps/daemon/internal/agent/codex/hosted_skills.go deleted file mode 100644 index 7b20452cb..000000000 --- a/apps/daemon/internal/agent/codex/hosted_skills.go +++ /dev/null @@ -1,41 +0,0 @@ -package codex - -import ( - "fmt" - "io/fs" - "os" - "path/filepath" - - "github.com/MiniMax-AI/OpenAgentCore/apps/daemon/internal/localworkspace" - "github.com/MiniMax-AI/OpenAgentCore/internal/agentcapabilities" -) - -func verifyHostedSkills(skills []agentcapabilities.InstalledSkill) error { - for _, skill := range skills { - if err := verifyHostedSkillLayout(localworkspace.SkillPath(skill)); err != nil { - return err - } - } - return nil -} - -func verifyHostedSkillLayout(root string) error { - // Native dependency declarations may start MCP installation outside the - // workspace tool sandbox. They are not qualified by an inert Skill upload. - if _, err := os.Lstat(filepath.Join(root, "agents", "openai.yaml")); err == nil { - return fmt.Errorf("codex: native Skill configuration is unsupported") - } else if !os.IsNotExist(err) { - return err - } - // Extra roots are scanned recursively. One public Skill must not silently - // introduce additional native Skills with their own activation metadata. - return filepath.WalkDir(root, func(path string, entry fs.DirEntry, err error) error { - if err != nil { - return err - } - if entry.Name() == "SKILL.md" && path != filepath.Join(root, "SKILL.md") { - return fmt.Errorf("codex: nested native Skills are unsupported") - } - return nil - }) -} diff --git a/apps/daemon/internal/agent/codex/hosted_skills_test.go b/apps/daemon/internal/agent/codex/hosted_skills_test.go deleted file mode 100644 index 0c7455687..000000000 --- a/apps/daemon/internal/agent/codex/hosted_skills_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package codex - -import ( - "os" - "path/filepath" - "testing" -) - -func TestHostedSkillDoesNotActivateAdditionalNativeResources(t *testing.T) { - for _, tc := range []struct { - name string - files []string - rejected bool - }{ - {"ordinary supporting files", []string{"SKILL.md", "scripts/check.py", "references/guide.md"}, false}, - {"native dependency configuration", []string{"SKILL.md", "agents/openai.yaml"}, true}, - {"nested discovery", []string{"SKILL.md", "references/other/SKILL.md"}, true}, - {"nested dependencies", []string{"SKILL.md", "references/other/SKILL.md", "references/other/agents/openai.yaml"}, true}, - } { - t.Run(tc.name, func(t *testing.T) { - root := t.TempDir() - for _, name := range tc.files { - path := filepath.Join(root, name) - if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(path, []byte("fixture"), 0400); err != nil { - t.Fatal(err) - } - } - if err := verifyHostedSkillLayout(root); (err != nil) != tc.rejected { - t.Fatalf("rejected=%v err=%v", tc.rejected, err) - } - }) - } -} diff --git a/apps/daemon/internal/agent/codex/native_hooks.go b/apps/daemon/internal/agent/codex/native_hooks.go deleted file mode 100644 index 962ca1969..000000000 --- a/apps/daemon/internal/agent/codex/native_hooks.go +++ /dev/null @@ -1,39 +0,0 @@ -package codex - -import ( - "context" - "encoding/json" - "errors" - "time" -) - -type nativeToolHook struct { - EventName string `json:"eventName"` - Command string `json:"command"` - Matcher string `json:"matcher"` - Enabled bool `json:"enabled"` - IsManaged bool `json:"isManaged"` - Async bool `json:"async"` - SourcePath string `json:"sourcePath"` - TrustStatus string `json:"trustStatus"` -} - -func readNativeToolHooks(ctx context.Context, rpc *JSONRPCClient, cwd string) ([]nativeToolHook, error) { - operation, cancel := context.WithTimeout(ctx, 10*time.Second) - defer cancel() - raw, err := rpc.Request(operation, "hooks/list", map[string]any{"cwds": []string{cwd}}) - if err != nil { - return nil, errors.New("codex: initialized tool hook unavailable") - } - var response struct { - Data []struct { - CWD string `json:"cwd"` - Hooks []nativeToolHook `json:"hooks"` - Errors []json.RawMessage `json:"errors"` - } `json:"data"` - } - if json.Unmarshal(raw, &response) != nil || len(response.Data) != 1 || response.Data[0].CWD != cwd || len(response.Data[0].Errors) != 0 { - return nil, errors.New("codex: initialized tool hook configuration unavailable") - } - return response.Data[0].Hooks, nil -} diff --git a/apps/daemon/internal/agent/codex/preparation.go b/apps/daemon/internal/agent/codex/preparation.go index d16310b83..e62039ef7 100644 --- a/apps/daemon/internal/agent/codex/preparation.go +++ b/apps/daemon/internal/agent/codex/preparation.go @@ -92,7 +92,7 @@ func newPreparation(parent context.Context, req proto.PromptRequestPayload, cfg } } if s.observeSubagentIdentities { - if err := verifySubagentObservationProfile(cancelCtx, rpc, plan.Cwd); err != nil { + if err := verifySubagentObservationProfile(cancelCtx, rpc); err != nil { return p.preparationFailed(err) } } diff --git a/apps/daemon/internal/agent/codex/protocol.go b/apps/daemon/internal/agent/codex/protocol.go index fbf8b2c2d..002aa4c92 100644 --- a/apps/daemon/internal/agent/codex/protocol.go +++ b/apps/daemon/internal/agent/codex/protocol.go @@ -164,28 +164,12 @@ type UserInput struct { } type TurnStartParams struct { - ThreadID string `json:"threadId"` - Input []UserInput `json:"input"` - CollaborationMode *CollaborationMode `json:"collaborationMode,omitempty"` + ThreadID string `json:"threadId"` + Input []UserInput `json:"input"` // OutputSchema constrains this Turn's final assistant message natively. OutputSchema json.RawMessage `json:"outputSchema,omitempty"` } -type CollaborationModeKind string - -const CollaborationModeDefault CollaborationModeKind = "default" - -type CollaborationMode struct { - Mode CollaborationModeKind `json:"mode"` - Settings CollaborationModeSettings `json:"settings"` -} - -type CollaborationModeSettings struct { - ReasoningEffort string `json:"reasoning_effort,omitempty"` - Model string `json:"model"` - DeveloperInstructions *string `json:"developer_instructions"` -} - type TurnInterruptParams struct { ThreadID string `json:"threadId"` TurnID string `json:"turnId"` diff --git a/apps/daemon/internal/agent/codex/protocol_wire_test.go b/apps/daemon/internal/agent/codex/protocol_wire_test.go index 895888bab..d22371f20 100644 --- a/apps/daemon/internal/agent/codex/protocol_wire_test.go +++ b/apps/daemon/internal/agent/codex/protocol_wire_test.go @@ -49,29 +49,3 @@ func TestThreadStartParams_ModelProviderIsCamelCaseField(t *testing.T) { t.Fatalf("snake_case model_provider leaked: %s", body) } } - -func TestTurnStartParams_CollaborationModeWireShape(t *testing.T) { - developerInstructions := "stay within the configured workspace" - params := TurnStartParams{ - ThreadID: "thread-1", - Input: []UserInput{{Type: UserInputText, Text: "ask me a question"}}, - CollaborationMode: &CollaborationMode{ - Mode: CollaborationModeDefault, - Settings: CollaborationModeSettings{ - Model: "MiniMax-M3", - DeveloperInstructions: &developerInstructions, - }, - }, - } - raw, err := json.Marshal(params) - if err != nil { - t.Fatalf("marshal: %v", err) - } - body := string(raw) - if !strings.Contains(body, `"collaborationMode":{"mode":"default","settings":{"model":"MiniMax-M3","developer_instructions":"stay within the configured workspace"}}`) { - t.Fatalf("collaboration mode missing or malformed: %s", body) - } - if strings.Contains(body, `"collaboration_mode"`) { - t.Fatalf("snake_case collaboration_mode leaked: %s", body) - } -} diff --git a/apps/daemon/internal/agent/codex/session_plan.go b/apps/daemon/internal/agent/codex/session_plan.go index e627fa585..b38c5e461 100644 --- a/apps/daemon/internal/agent/codex/session_plan.go +++ b/apps/daemon/internal/agent/codex/session_plan.go @@ -65,10 +65,6 @@ func prepareSessionPlan(ctx context.Context, req proto.PromptRequestPayload, cfg } var skillRoots []string if req.LocalEnvironment != nil && len(req.LocalEnvironment.Skills) > 0 { - if err := verifyHostedSkills(req.LocalEnvironment.Skills); err != nil { - plan.Cleanup() - return SessionPlan{}, nil, err - } for _, skill := range req.LocalEnvironment.Skills { skillRoots = append(skillRoots, localworkspace.SkillPath(skill)) } diff --git a/apps/daemon/internal/agent/codex/session_run.go b/apps/daemon/internal/agent/codex/session_run.go index d370ccb67..f69043f39 100644 --- a/apps/daemon/internal/agent/codex/session_run.go +++ b/apps/daemon/internal/agent/codex/session_run.go @@ -3,7 +3,6 @@ package codex import ( "context" "fmt" - "strings" "time" "github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto" @@ -25,22 +24,6 @@ func (s *Session) startNative(ctx context.Context, plan SessionPlan, req proto.P Input: input, OutputSchema: plan.OutputSchema, } - model := strings.TrimSpace(s.resolvedModel) - if model == "" { - return fmt.Errorf("codex: collaboration mode requires a resolved model") - } - var developerInstructions *string - if plan.SystemPrompt != "" { - developerInstructions = &plan.SystemPrompt - } - turnParams.CollaborationMode = &CollaborationMode{ - Mode: CollaborationModeDefault, - Settings: CollaborationModeSettings{ - ReasoningEffort: plan.ModelReasoningEffort, - Model: model, - DeveloperInstructions: developerInstructions, - }, - } turnCtx, turnCancel := context.WithTimeout(ctx, 10*time.Second) _, ackErr := s.rpc.requestWithResult(turnCtx, "turn/start", turnParams, s.bindTurnResult) turnCancel() diff --git a/apps/daemon/internal/agent/codex/subagent_profile.go b/apps/daemon/internal/agent/codex/subagent_profile.go index 68afc9626..cf7093ab5 100644 --- a/apps/daemon/internal/agent/codex/subagent_profile.go +++ b/apps/daemon/internal/agent/codex/subagent_profile.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "errors" - "slices" stdstrconv "strconv" "github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto" @@ -14,14 +13,8 @@ func configureSubagentObservations(plan *SessionPlan, req proto.PromptRequestPay if !req.ObserveSubagentIdentities || req.DisableSubagents { return nil } - for _, feature := range []string{"hooks", "plugins", "code_mode", "code_mode_only", "code_mode_prewarm", "multi_agent_v2"} { - if !slices.Contains(plan.DisableFeatures, feature) { - plan.DisableFeatures = append(plan.DisableFeatures, feature) - } - plan.ExtraConfig = append(plan.ExtraConfig, [2]string{"features." + feature, "false"}) - } plan.EnableFeatures = append(plan.EnableFeatures, "multi_agent") - plan.ExtraConfig = append(plan.ExtraConfig, [2]string{"features.multi_agent", "true"}, [2]string{"agents.max_depth", "64"}) + plan.ExtraConfig = append(plan.ExtraConfig, [2]string{"features.multi_agent", "true"}) if req.MaxConcurrentSubagents != nil { if *req.MaxConcurrentSubagents < 1 { return errors.New("codex: invalid subagent concurrency limit") @@ -31,38 +24,24 @@ func configureSubagentObservations(plan *SessionPlan, req proto.PromptRequestPay return nil } -func verifySubagentObservationProfile(ctx context.Context, rpc *JSONRPCClient, cwd string) error { - hooks, err := readNativeToolHooks(ctx, rpc, cwd) - if err != nil { - return err - } - if len(hooks) != 0 { - return errors.New("codex: subagent observation requires hooks disabled") - } - +// Code-mode collaboration calls and multi_agent_v2 leave no persisted receipt, so +// managed requirements that force them cannot be observed. +func verifySubagentObservationProfile(ctx context.Context, rpc *JSONRPCClient) error { raw, err := rpc.Request(ctx, "configRequirements/read", nil) var response struct { Requirements *struct { - AllowManagedHooksOnly bool `json:"allowManagedHooksOnly"` - Features map[string]bool `json:"featureRequirements"` + Features map[string]bool `json:"featureRequirements"` } `json:"requirements"` } if err != nil || json.Unmarshal(raw, &response) != nil { - return errors.New("codex: subagent hook requirements unavailable") + return errors.New("codex: subagent feature requirements unavailable") } if response.Requirements == nil { - if len(hooks) != 0 { - return errors.New("codex: managed-only subagent hooks are not enforced") - } return nil } - requirements := response.Requirements - if (len(hooks) != 0 || requirements.Features["hooks"]) && !requirements.AllowManagedHooksOnly { - return errors.New("codex: managed-only subagent hooks are not enforced") - } - for _, feature := range []string{"plugins", "code_mode", "code_mode_only", "code_mode_prewarm", "multi_agent_v2"} { - if requirements.Features[feature] { - return errors.New("codex: required feature conflicts with subagent observation profile") + for _, feature := range []string{"code_mode", "code_mode_only", "code_mode_prewarm", "multi_agent_v2"} { + if response.Requirements.Features[feature] { + return errors.New("codex: required feature conflicts with subagent observation") } } return nil diff --git a/apps/daemon/internal/agent/codex/subagent_profile_test.go b/apps/daemon/internal/agent/codex/subagent_profile_test.go index 14ee5aa5a..b7c964888 100644 --- a/apps/daemon/internal/agent/codex/subagent_profile_test.go +++ b/apps/daemon/internal/agent/codex/subagent_profile_test.go @@ -8,19 +8,15 @@ import ( "github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto" ) -func TestSubagentProfileOverridesUnsafeNativeFeatures(t *testing.T) { +func TestSubagentProfileEnablesOnlyMultiAgent(t *testing.T) { limit := 6 plan := SessionPlan{} if err := configureSubagentObservations(&plan, proto.PromptRequestPayload{ObserveSubagentIdentities: true, MaxConcurrentSubagents: &limit}); err != nil { t.Fatal(err) } - for _, feature := range []string{"hooks", "plugins", "code_mode", "code_mode_only", "code_mode_prewarm", "multi_agent_v2"} { - if slices.Contains(plan.EnableFeatures, feature) || !slices.Contains(plan.DisableFeatures, feature) { - t.Fatal(feature, plan) - } - } - if !slices.Contains(plan.ExtraConfig, [2]string{"agents.max_threads", "6"}) || !slices.Contains(plan.ExtraConfig, [2]string{"agents.max_depth", "64"}) { - t.Fatal(plan.ExtraConfig) + want := [][2]string{{"features.multi_agent", "true"}, {"agents.max_threads", "6"}} + if !slices.Equal(plan.EnableFeatures, []string{"multi_agent"}) || len(plan.DisableFeatures) != 0 || !slices.Equal(plan.ExtraConfig, want) { + t.Fatal(plan) } } @@ -35,26 +31,26 @@ func TestSubagentProfileDoesNotDisableRequiredToolEnvironment(t *testing.T) { } } -func TestSubagentProfileRejectsRequirementsThatPermitResultRewrite(t *testing.T) { +func TestSubagentProfileRejectsRequirementsThatForceUnobservableFeatures(t *testing.T) { for _, test := range []struct { name string requirements any allowed bool }{ - {"ordinary disabled hooks", nil, true}, - {"managed-only hooks", map[string]any{"allowManagedHooksOnly": true, "featureRequirements": map[string]bool{"hooks": true}}, true}, - {"unrestricted forced hooks", map[string]any{"allowManagedHooksOnly": false, "featureRequirements": map[string]bool{"hooks": true}}, false}, + {"no requirements", nil, true}, + {"forced hooks", map[string]any{"featureRequirements": map[string]bool{"hooks": true}}, true}, + {"forced plugins", map[string]any{"featureRequirements": map[string]bool{"plugins": true}}, true}, {"forced code mode", map[string]any{"featureRequirements": map[string]bool{"code_mode": true}}, false}, - {"forced plugins", map[string]any{"featureRequirements": map[string]bool{"plugins": true}}, false}, + {"forced multi_agent_v2", map[string]any{"featureRequirements": map[string]bool{"multi_agent_v2": true}}, false}, } { t.Run(test.name, func(t *testing.T) { client, server, cleanup := NewTestClient() defer cleanup() done := make(chan error, 1) - go func() { done <- verifySubagentObservationProfile(t.Context(), client.JSONRPCClient, "/workspace") }() + go func() { done <- verifySubagentObservationProfile(t.Context(), client.JSONRPCClient) }() decoder := json.NewDecoder(server.FromClient) encoder := json.NewEncoder(server.ToClient) - for _, result := range []any{map[string]any{"data": []any{map[string]any{"cwd": "/workspace", "hooks": []any{}, "errors": []any{}}}}, map[string]any{"requirements": test.requirements}} { + for _, result := range []any{map[string]any{"requirements": test.requirements}} { var request JsonRpcRequest if err := decoder.Decode(&request); err != nil { t.Fatal(err) @@ -69,3 +65,12 @@ func TestSubagentProfileRejectsRequirementsThatPermitResultRewrite(t *testing.T) }) } } + +func TestSubagentItemSnapshotSkipsUnmappedItemTypes(t *testing.T) { + for _, item := range []string{`{"type":"imageView","id":"i1","path":"/x.png"}`, `{"type":"contextCompaction","id":"c1"}`} { + kind, _, _, err := subagentItemSnapshot(json.RawMessage(item), "completed") + if err != nil || kind != "" { + t.Fatal(item, kind, err) + } + } +} diff --git a/apps/daemon/internal/agent/codex/subagent_snapshots.go b/apps/daemon/internal/agent/codex/subagent_snapshots.go index 9f3eda46c..bbe4310d1 100644 --- a/apps/daemon/internal/agent/codex/subagent_snapshots.go +++ b/apps/daemon/internal/agent/codex/subagent_snapshots.go @@ -307,6 +307,7 @@ func subagentItemSnapshot(raw json.RawMessage, turnStatus string) (string, json. body, err := json.Marshal(map[string]any{"status": status, "summary": summary}) return "reasoning", body, item.ID, err default: - return "", nil, "", errors.New("codex: unsupported child Item observation") + // Item types without a neutral vocabulary are skipped, as in the root projection. + return "", nil, "", nil } } diff --git a/contracts/agents-api/environments.md b/contracts/agents-api/environments.md index 10a35831e..81ea9a57a 100644 --- a/contracts/agents-api/environments.md +++ b/contracts/agents-api/environments.md @@ -380,7 +380,7 @@ Skills and their immutable versions are Project resources, independent of Sessio - Bundles are encrypted with the service cipher, bound to Project, Skill and version. Metadata reads never load or decrypt bundles. Deleting a Skill reclaims its versions without affecting frozen Sessions. - References resolve inside the Session creation transaction, after the upsert establishes ownership. Referenced resources are locked in a stable order, and the version, metadata and bytes freeze together. Public reference metadata, unresolved Template intent and the resolved Runtime bundle stay distinct; a reference is never reported as inline. -Inline and referenced Skills use the same confidential snapshot and installer. The Runtime installs them under `skills/` in its capability root before setup and native execution. Adapters register only the selected Skill roots: Codex as explicit extra roots, MiniMax Code through its native catalog, and Claude as one controlled envelope per package with real directories and immutable hard links. Skill roots activate no MCP servers; only [Plugin MCP](#plugin-mcp) does. Codex nested `SKILL.md` discovery, `agents/openai.yaml` dependency configuration and Claude inline shell preprocessing fail adapter preparation. Native Harness configuration is never passed through wholesale. +Inline and referenced Skills use the same confidential snapshot and installer. The Runtime installs them under `skills/` in its capability root before setup and native execution. Adapters register only the selected Skill roots: Codex as explicit extra roots, MiniMax Code through its native catalog, and Claude as one controlled envelope per package with real directories and immutable hard links. Skill roots activate no MCP servers; only [Plugin MCP](#plugin-mcp) does. Codex scans each Skill root recursively as it does locally, so a nested `SKILL.md` registers another native Skill. Claude inline shell preprocessing fails adapter preparation. Native Harness configuration is never passed through wholesale. ### Plugin MCP diff --git a/contracts/agents-api/subagents.md b/contracts/agents-api/subagents.md index b9badee82..c7b4f7ccb 100644 --- a/contracts/agents-api/subagents.md +++ b/contracts/agents-api/subagents.md @@ -52,7 +52,7 @@ An adapter freezes root output before child settlement, keeps its native owner a [Harness capabilities](./harness-capabilities.md#tools) lists rejected tool combinations. -**Codex.** The adapter enables the native `multi_agent` feature with a nesting depth of 64 and maps the concurrency limit to `agents.max_threads`. It disables native hooks, plugins, code mode and `multi_agent_v2`, and refuses to start if the native hook list is not empty or managed requirements force a conflicting feature. Close and reopen facts come from direct tool output correlated with the same call's persisted completion, so they need native persisted receipts. Native Turn times have second precision. Child file work can finish under the same owner after the root Turn finishes. Cancellation continues under the same owner after a caller deadline; a later call can confirm settlement without repeating the native interrupt. +**Codex.** The adapter enables the native `multi_agent` feature and maps the concurrency limit to `agents.max_threads`; nesting depth and other native features keep their Codex defaults. It refuses to start if managed requirements force code mode or `multi_agent_v2`, whose collaboration calls leave no persisted receipt. Child Items without a neutral Item type, such as image views and context compaction, are not projected. Close and reopen facts come from direct tool output correlated with the same call's persisted completion, so they need native persisted receipts. Native Turn times have second precision. Child file work can finish under the same owner after the root Turn finishes. Cancellation continues under the same owner after a caller deadline; a later call can confirm settlement without repeating the native interrupt. **Claude Code.** The pinned SDK's native Agent and SendMessage calls run the single child type `oac_worker`, which inherits the model and, on a workspace placement, the parent's native tools, and otherwise has only Agent and SendMessage; the bridge's `subagent_resources` feature gates it. Children use native Bash with the parent's launching-user permissions. Private child records establish parentage, the first own input time and later own Turns; inherited parent context is excluded. The query owner admits children before start and keeps their history through settlement. Confirmed cancellation writes an immutable effect receipt because a native abort can leave no terminal record. There is no close operation: completed or cancelled children stay active. Messages to running children, background work, other child profiles and per-call model overrides are rejected. The [Claude SDK adapter](https://github.com/MiniMax-AI/OpenAgentCore/blob/main/packages/claude-sdk-adapter/README.md#subagents) documents the details. diff --git a/contracts/agents-api/zh/environments.md b/contracts/agents-api/zh/environments.md index bd04f7e46..152e0e3ca 100644 --- a/contracts/agents-api/zh/environments.md +++ b/contracts/agents-api/zh/environments.md @@ -1,7 +1,7 @@ --- title: "Environment 与模板" source: contracts/agents-api/environments.md -source_hash: 20bf8194519cb0c830b88e76c9fea28a64ec1615c1535c8ff8abb5668c086e00 +source_hash: 2208cbf1f6bd39e888618323077ac9ce865f2aa412a111b3c040631427f30479 --- Environment 是 Session 的执行资源,包括 Harness 运行所在的机器、工作区以及已完成准备的能力。Session 通过其 `environment` 配置创建 Environment;不存在独立的 create 调用。Environment 模板是 Session 创建时解析的可复用准备配置。本契约涵盖这两类资源、两种放置方式、输入接纳、能力准备、Skills、Plugins 和 MCP 连接来源。 @@ -382,7 +382,7 @@ Skills 及其不可变版本是项目资源,独立于 Session 和原生安装 - Bundle 使用服务密码进行加密,并绑定到项目、Skill 和版本。元数据读取绝不会加载或解密 Bundle。删除 Skill 会回收其版本,但不会影响已冻结的 Session。 - 引用会在 Session 创建事务中、upsert 建立所有权之后解析。被引用的资源按稳定顺序锁定,版本、元数据和字节会一并冻结。公开引用元数据、未解析的模板意图和已解析的 Runtime Bundle 始终彼此独立;引用绝不会报告为内联内容。 -内联和引用的 Skills 使用相同的机密快照与安装程序。Runtime 会在设置和原生执行之前,将它们安装到能力根目录下的 `skills/`。适配器只注册所选的 Skill 根目录:Codex 将其注册为显式额外根目录,MiniMax Code 通过其原生目录进行注册,Claude 则为每个包使用一个受控封装,其中包含实际目录和不可变硬链接。Skill 根目录不会启用 MCP 服务器,只有 [Plugin MCP](#plugin-mcp) 会启用。Codex 的嵌套 `SKILL.md` 发现、`agents/openai.yaml` 依赖项配置以及 Claude 内联 shell 预处理都会导致适配器准备失败。原生 Harness 配置绝不会整体透传。 +内联和引用的 Skills 使用相同的机密快照与安装程序。Runtime 会在设置和原生执行之前,将它们安装到能力根目录下的 `skills/`。适配器只注册所选的 Skill 根目录:Codex 将其注册为显式额外根目录,MiniMax Code 通过其原生目录进行注册,Claude 则为每个包使用一个受控封装,其中包含实际目录和不可变硬链接。Skill 根目录不会启用 MCP 服务器,只有 [Plugin MCP](#plugin-mcp) 会启用。Codex 与本地运行一样递归扫描每个 Skill 根目录,因此嵌套的 `SKILL.md` 会注册另一个原生 Skill。Claude 内联 shell 预处理会导致适配器准备失败。原生 Harness 配置绝不会整体透传。 ### Plugin MCP {#plugin-mcp} diff --git a/contracts/agents-api/zh/subagents.md b/contracts/agents-api/zh/subagents.md index e41cf6022..2cec0afdf 100644 --- a/contracts/agents-api/zh/subagents.md +++ b/contracts/agents-api/zh/subagents.md @@ -1,7 +1,7 @@ --- title: "子 Agent" source: contracts/agents-api/subagents.md -source_hash: 130f61f7c544d334df4e80734049bea624951d861a786995196735c8ecf14758 +source_hash: 2f66b6d6e9bb613eec6fcf07eb15638e46b8ac4bc8933e33e750b6e5685fd44b --- 启用 `multi_agent.enabled` 后,Harness 可以启动原生子 Agent。Core 通过锁定版本的 SDK(见 [`upstream.json`](https://github.com/MiniMax-AI/OpenAgentCore/blob/main/contracts/agents-api/upstream.json))中的六项 Subagent 读取操作公开它们,并根据适配器观察结果记录它们。当 `multi_agent.enabled=false` 时,Runtime 会移除原生子 Agent 工具。[Harness capabilities](harness-capabilities.md) 列出各 Harness 在哪些组合中支持子 Agent。 @@ -54,7 +54,7 @@ Core 根据已授权的 Session 绑定关系分配合公开 ID 和所有权。 [Harness capabilities](harness-capabilities.md#tools) 列出被拒绝的工具组合。 -**Codex.** 适配器会启用原生 `multi_agent` 特性,将嵌套深度设为 64,并把并发限制映射到 `agents.max_threads`。它会禁用原生钩子、插件、代码模式和 `multi_agent_v2`;如果原生钩子列表非空,或托管配置要求强制启用冲突特性,则拒绝启动。关闭和重新打开的事实来自直接工具输出,并与同一次调用的持久化完成记录相关联,因此需要原生持久化回执。原生 Turn 时间具有秒级精度。根级 Turn 完成后,子级文件工作可以在同一所有者下完成。超过调用方截止时间后,取消操作仍会在同一所有者下继续;后续调用可以确认已经收敛,而无需重复执行原生中断。 +**Codex.** 适配器会启用原生 `multi_agent` 特性,并把并发限制映射到 `agents.max_threads`;嵌套深度和其他原生特性保持 Codex 默认值。如果托管配置要求强制启用代码模式或 `multi_agent_v2`(它们的协作调用不留下持久化回执),则拒绝启动。没有中立 Item 类型的子级 Item(例如图像查看和上下文压缩)不会被投影。关闭和重新打开的事实来自直接工具输出,并与同一次调用的持久化完成记录相关联,因此需要原生持久化回执。原生 Turn 时间具有秒级精度。根级 Turn 完成后,子级文件工作可以在同一所有者下完成。超过调用方截止时间后,取消操作仍会在同一所有者下继续;后续调用可以确认已经收敛,而无需重复执行原生中断。 **Claude Code.** 锁定版本 SDK 的原生 Agent 和 SendMessage 调用会运行唯一的子级类型 `oac_worker`;该类型继承模型;在工作区位置上继承父级的原生工具,否则只有 Agent 和 SendMessage;bridge 的 `subagent_resources` 特性控制是否启用它。子级使用原生 Bash,权限继承自父级的启动用户。私有子级记录用于确定父子关系、首次自身输入时间及后续自身 Turns;继承的父级上下文会被排除。查询所有者会在启动前接纳子级,并将子级历史保留至工作收敛。已确认的取消操作会写入不可变的操作回执,因为原生中止可能不会留下终态记录。不存在关闭操作:已完成或已取消的子级保持 active 状态。向运行中的子级发送消息、使用后台工作、采用其他子级配置以及按次覆盖模型都会被拒绝。[Claude SDK adapter](https://github.com/MiniMax-AI/OpenAgentCore/blob/main/packages/claude-sdk-adapter/README.md#subagents) 记录了详细信息。