DeepSeek Harness: Service Isolation and Scope

Last updated: 2026-08-31

In multi-Agent, multi-session environments, shared services are both a blessing and a curse — global services are convenient but conflict-prone, isolated services are safe but communication costs more. Cordis's scope system balances both: shared by default, isolated on demand.

💡 Tip: Scope's core principle is "shared by default, isolated on demand" — most services can be globally shared; only services needing isolation require isolate configuration. Non-isolation is the norm; isolation is the exception.

📋 Prerequisites: Completed 19-service.md, understand the Service base class

1. What You'll Learn


Isolation Scope

2. Global Scope

(1) Default Behavior

By default, all Services are global — there's only one instance across the entire DSH instance:

TYPESCRIPT
export default class CacheService extends Service {
  constructor(ctx: Context) {
    super(ctx, 'cache')  // Globally unique
  }
}

(2) Global Service Characteristics

Characteristic Description
Singleton Only one instance per process
Shared All sessions and requests share the same state
No isolation Data modified by session A is visible to session B

(3) Applicable Scenarios

(4) ▶ Example 4

TYPESCRIPT
// ❌ Global cache causes cross-session data leakage
export default class CacheService extends Service {
  private data = new Map<string, any>()
  
  set(key: string, value: any) {
    this.data.set(key, value)
  }
}

// Session A: ctx.cache.set('temp', 'secret-data')
// Session B: ctx.cache.get('temp') → 'secret-data' (leaked!)

3. Session Scope

(1) Concept

Session scope creates an independent service instance for each session:

TYPESCRIPT
export default class SessionCacheService extends Service {
  static scope = 'session'

  constructor(ctx: Context) {
    super(ctx, 'session-cache')
  }
}

(2) ▶ Example 2

100%
graph LR
    S1[Session A created] --> C1[CacheService A instance]
    S2[Session B created] --> C2[CacheService B instance]
    S1 --> D1[Session A destroyed → CacheService A destroyed]
    S2 --> D2[Session B destroyed → CacheService B destroyed]

(3) Scope Declaration

TYPESCRIPT
export default class SessionCacheService extends Service {
  static scope = 'session'
  // Or
  // static scope = Symbol('session')
}

(4) Applicable Scenarios

(5) Cross-Session Isolation Effect

TEXT 📖 Display only
Session A:
  ctx.sessionCache.set('key', 'value-A')

Session B:
  ctx.sessionCache.get('key') → undefined  (isolated)

Session A:
  ctx.sessionCache.get('key') → 'value-A'  (accessible within session)

4. Request Scope

(1) Concept

Request scope creates an independent instance for each tool call or LLM request:

TYPESCRIPT
export default class RequestContextService extends Service {
  static scope = 'request'

  constructor(ctx: Context) {
    super(ctx, 'request-context')
  }
}

(2) Request Scope Lifecycle

100%
graph LR
    R1[Request 1 starts] --> S1[Service instance 1]
    R2[Request 2 starts] --> S2[Service instance 2]
    R1 --> E1[Request 1 completes → instance 1 destroyed]
    R2 --> E2[Request 2 completes → instance 2 destroyed]

(3) Applicable Scenarios

(4) Three Scope Levels Comparison

Dimension Global Session Request
Instances 1 1 per session 1 per request
Lifecycle Process lifetime Session lifetime Request lifetime
State sharing Globally shared Within session Within request only
Memory Low Medium High
Best for Config/connection pools Session cache/history Permissions/timing

5. isolate Realm Configuration

(1) Realm Concept

A realm is Cordis's isolation domain — creating independent plugin spaces within the same DSH instance:

YAML
# cordis.yml
realms:
  agent-a:
    isolate: ['cache', 'tools']
    plugins:
      my-tool-a:
        $insert: ./plugins/tool-a
  
  agent-b:
    isolate: ['cache', 'tools']
    plugins:
      my-tool-b:
        $insert: ./plugins/tool-b

(2) isolate Field

The isolate list specifies which services get independent instances within the realm:

YAML
realms:
  my-realm:
    isolate:
      - cache       # cache service gets independent instance
      - tools       # tools service gets independent instance
      # Services not listed remain globally shared

(3) Service Instances Within a Realm

TEXT 📖 Display only
Global: llm (shared)
realm-a: cache (independent), tools (independent)
realm-b: cache (independent), tools (independent)

Plugins in realm-a → ctx.cache = realm-a's cache
Plugins in realm-b → ctx.cache = realm-b's cache
They don't affect each other

(4) Realm Use Cases

Scenario Description
Multi-Agent Different Agents have different tool sets and caches
Multi-tenant Different tenants' services are mutually isolated
Testing Test realm doesn't affect production realm
A/B testing Two realms use different service implementations

(5) Cross-Realm Communication

Realms are isolated by default but can communicate through global services:

TYPESCRIPT
// Global service (unaffected by isolate)
export default class EventBusService extends Service {
  constructor(ctx: Context) {
    super(ctx, 'event-bus')  // Not in isolate list → globally shared
  }

  emit(event: string, data: any) { /* ... */ }
  on(event: string, handler: Function) { /* ... */ }
}

// Plugin in realm-a
ctx.eventBus.emit('data-updated', { source: 'realm-a' })

// Plugin in realm-b
ctx.eventBus.on('data-updated', (data) => {
  ctx.logger.info(`received from ${data.source}`)
})

6. Scope and Agent Presets

(1) Agent Preset Concept

An Agent preset is a pre-defined Agent configuration including tool set, model parameters, and scope settings:

YAML
presets:
  coder:
    model: deepseek-coder
    tools: [file_edit, shell, search]
    mode: standard
    
  reviewer:
    model: deepseek-chat
    tools: [file_edit, search]
    mode: minimal
    isolate: [cache]

(2) Preset and Scope Relationship

Each preset can specify an isolate list, creating isolated service instances for that Agent:

TEXT 📖 Display only
coder Agent:  shared cache, independent tools
reviewer Agent: independent cache, shared tools

(3) Multi-Agent Scenario

YAML
# cordis.yml
agents:
  coder:
    preset: coder
    isolate: [tools]
    
  reviewer:
    preset: reviewer
    isolate: [tools, cache]

(4) Inter-Agent Collaboration

100%
graph TB
    subgraph Global
        LLM[llm Service]
        EVENT[event-bus Service]
    end
    subgraph Agent-Coder
        CT[tools Service]
        CC[cache Service]
    end
    subgraph Agent-Reviewer
        RT[tools Service]
        RC[cache Service]
    end
    CT --> LLM
    RT --> LLM
    CT --> EVENT
    RT --> EVENT

7. Multi-Agent Service Isolation

(1) Isolation Strategy Selection

TEXT 📖 Display only
Fully shared:     All services global → simple but potential conflicts
Fully isolated:   All services independent → safe but resource waste
Mixed isolation:  Core services shared + business services isolated → balanced approach

Recommended mixed isolation:

Service Type Isolation Strategy Reason
llm Shared API calls can be reused
sessions Shared Unified session management
tools Isolated Each Agent has different tool sets
cache Isolated Each Agent has independent cache
fs Shared Only one filesystem

(2) ▶ Example 2

YAML
agents:
  frontend-dev:
    preset: coder
    isolate: [tools, cache]
    tools:
      - file_edit
      - shell
      - search
    config:
      cache:
        maxSize: 100
  
  backend-dev:
    preset: coder
    isolate: [tools, cache]
    tools:
      - file_edit
      - shell
      - search
      - database
    config:
      cache:
        maxSize: 200

(3) Resource Consumption Estimation

Isolation Level Memory CPU Connections
Global shared 1x 1x 1x
2-Agent isolated ~2x ~1.5x ~2x
5-Agent isolated ~5x ~3x ~5x

(4) Isolation Leak Detection

TYPESCRIPT
// Monitor service instance counts
ctx.on('service/created', (name, instance) => {
  ctx.logger.info(`service created: ${name}, total instances: ${countInstances(name)}`)
})

// If a service's instance count far exceeds the Agent count, there may be a leak

❓ FAQ

Q Without declaring scope, is a service global by default?
A Yes. A Service without static scope is a global singleton by default.
Q Are session-scoped Services cleaned up when a session ends?
A Yes. When a session is destroyed, its Service instances are automatically cleaned up, including resources registered via ctx.effect.
Q Can a realm's isolate list be modified dynamically?
A No. Realm configuration is determined at startup; modifications require restart.
Q Can a Service exist in multiple scopes simultaneously?
A No. A Service is either global, session-level, or request-level. If you need cross-scope data sharing, use a global event bus.
Q Is the performance overhead of request scope significant?
A Creating and destroying Services per request has overhead. Only use request scope when truly needed; otherwise use global or session scope.
Q How to debug scope issues?
A Print the instance ID in the Service constructor: typescript constructor(ctx: Context) { super(ctx, 'cache') console.log(`cache instance created: ${this.id}, scope: ${this.scope}`) }

📖 Summary


📝 Exercises

1. ⭐ Basic: Write a session-scoped SessionStateService that maintains independent key-value storage per session. Start two sessions and verify data isolation.

2. ⭐⭐ Intermediate: Configure a realm that isolates cache and tools services. Access cache both inside and outside the realm, verifying you get different instances.

3. ⭐⭐⭐ Challenge: Design a dual-Agent system: coder and reviewer. Coder has file_edit/shell tools, reviewer only has file_edit/search tools. Both share llm and sessions, but cache and tool sets are independently isolated. Verify: coder's shell tool is invisible to reviewer, and their caches don't affect each other.

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏