Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
776397e
SentinelRunner: declarative step engine for pipeline sentinels
joelteply Feb 14, 2026
cd0f632
SentinelRunner: add tool calling, parallel execution, event triggers
joelteply Feb 14, 2026
8708ec0
SentinelRunner: add pipeline type, fix IPC logging
joelteply Feb 14, 2026
6f9a819
Sentinel: move pipeline execution to Rust, delete TypeScript infrastr…
joelteply Feb 14, 2026
7ccf129
Add CommandExecutor: universal command routing for ALL continuum-core…
joelteply Feb 14, 2026
748dffa
CommandExecutor: fix TypeScript command routing via Unix socket
joelteply Feb 14, 2026
91cc64f
Schema generator: fix descriptions, deduplicate, read READMEs
joelteply Feb 15, 2026
329a5cc
Fix JSDoc on all 35 commands with weak schema descriptions
joelteply Feb 15, 2026
4f43360
Sentinel architecture doc: add Three Pillars, expand Olympics, clean …
joelteply Feb 15, 2026
5245a12
Sentinel pipeline engine: add 4 new step types, enhance loops, unify …
joelteply Feb 15, 2026
0a48f43
Sentinel test suite: 101 tests covering all 9 step types, verified live
joelteply Feb 15, 2026
5d3d895
Sentinel agentic loop: extract AgentToolExecutor, add ai/agent comman…
joelteply Feb 15, 2026
ddd5dbc
Fix sentinel agentMode routing: bypass Rust registry for TypeScript c…
joelteply Feb 15, 2026
1fc462a
Unified text similarity: 3 TS Jaccard duplicates → 1 Rust implementation
joelteply Feb 15, 2026
1295a78
Combined validation gate: 4 TS gates → 1 Rust IPC call (garbage, loop…
joelteply Feb 15, 2026
d703e2f
Params helper: one source of truth for IPC parameter extraction acros…
joelteply Feb 15, 2026
3d74e96
Eliminate all production .unwrap() calls across continuum-core
joelteply Feb 15, 2026
a8037f2
Dead code removal + fix ai/sleep identity resolution
joelteply Feb 15, 2026
bb19dc8
Unify AI wire types: Rust as single source of truth via ts-rs
joelteply Feb 16, 2026
83cccf0
Fix configCache POJO bug + task polling thundering herd
joelteply Feb 16, 2026
70659c2
Migrate persona background timers from TS to single Rust tick loop
joelteply Feb 16, 2026
2a58bb7
Add ChannelTickConfig: ts-rs configurable tick loop cadence
joelteply Feb 16, 2026
eac4788
Unified evaluation gate: 5 TS gates → 1 Rust IPC call (<0.03ms)
joelteply Feb 16, 2026
ec6b228
Model selection: 4-tier priority chain migrated to Rust
joelteply Feb 16, 2026
0ac8932
Tool call parsing: 5 format adapters + correction + codec migrated to…
joelteply Feb 16, 2026
eeacfdd
Genome paging: LRU eviction + memory budget decisions migrated to Rust
joelteply Feb 16, 2026
c667c65
Post-inference adequacy: batch check replaces N individual IPC calls
joelteply Feb 16, 2026
8dce316
Cognition state consolidation: 7 DashMaps → 1, dead code removal, tex…
joelteply Feb 16, 2026
ba532d3
RAG budget as single authority for all LLM context
joelteply Feb 16, 2026
de62066
Rich identity prompts + tool prioritization fix
joelteply Feb 16, 2026
d20947b
Stable tool calling across all native providers
joelteply Feb 16, 2026
48afa8a
RAG improvements: identity prompts, prompt capture, garbage detection
joelteply Feb 16, 2026
0479ef0
Candle stability + RAG budget fixes + tool calling improvements
joelteply Feb 16, 2026
f8c983f
Provider-scoped ModelRegistry: fix cross-provider context window coll…
joelteply Feb 17, 2026
3c1bcc2
ModelCapabilities type system: enums and profiles for LoRA/PEFT/quant…
joelteply Feb 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
36 changes: 36 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,42 @@ sleep 10
./jtag ai/report # AI performance metrics
```

### Persona Logging (Cognition Visibility)

Persona logging is **opt-in** and controlled by `.continuum/logging.json`. Categories include `cognition` (thought process, tool decisions, agent loop traces) and `hippocampus` (memory/recall).

**Config file** (`.continuum/logging.json`):
```json
{
"version": 1,
"defaults": { "enabled": true, "categories": ["cognition"] },
"personas": {
"helper": { "enabled": true, "categories": ["cognition"] }
},
"system": { "enabled": true, "categories": [] }
}
```

**Commands**:
```bash
# Enable logging for a persona (persists to logging.json)
./jtag logging/enable --persona="helper" --category="cognition"

# Disable logging for a persona
./jtag logging/disable --persona="helper"

# Show logging status for all personas
./jtag logging/status

# Show logging status for a specific persona
./jtag logging/status --persona="helper"
```

**Log locations**:
- Per-persona cognition: `.continuum/jtag/logs/personas/<persona>/cognition.log`
- AI provider routing: `.continuum/jtag/logs/system/modules/ai_provider.log`
- Prompt captures (full LLM req/res): `.continuum/jtag/logs/prompt-captures.jsonl`

### System Logs
```bash
tail -f .continuum/sessions/user/shared/*/logs/server.log
Expand Down
183 changes: 180 additions & 3 deletions src/debug/jtag/.doc-staging/persona/sentinel-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -594,13 +594,190 @@ const CODE_SENTINEL_PERMISSIONS: SentinelPermissions = {

---

## Runtime Execution Model

### Workspace Structure

All sentinel execution happens within `.continuum/jtag/`:

```
.continuum/jtag/
├── logs/system/
│ ├── sentinels/ # All sentinel logs here
│ │ ├── {handle}/
│ │ │ ├── stdout.log
│ │ │ ├── stderr.log
│ │ │ ├── combined.log
│ │ │ └── steps.jsonl # Step-by-step results
│ │ └── index.log # Sentinel start/stop events
│ └── ...
├── sentinels/
│ ├── workspaces/ # Sentinel scratch space
│ │ └── {handle}/
│ │ ├── output/ # Files sentinel creates
│ │ ├── metadata.json # Pipeline definition, permissions
│ │ └── results.json # Final step results
│ └── definitions/ # Saved sentinel definitions
│ └── {id}.json
└── ...
```

**Key principle**: Sentinels write to their workspace by default. Access outside requires explicit permission.

---

### Filesystem Permission Model

```typescript
interface SentinelFilesystemConfig {
// Static whitelist (declared in pipeline definition)
read: string[]; // Glob patterns: ["src/**/*.ts", "package.json"]
write: string[]; // Default: ["$workspace/**"]
execute: string[]; // Commands: ["npm", "cargo", "git"]

// Dynamic access
requestDynamic: boolean; // Can request more at runtime
autoApprove: string[]; // Auto-approve patterns: ["$workspace/**"]
}
```

**Default sandbox**: Sentinels can ONLY write to `$workspace` (their handle's directory) unless explicitly granted more.

---

### Event-Based Permission Requests (Non-Blocking)

When a sentinel needs access outside its sandbox:

```
Step needs /some/external/path
├─→ emit: "sentinel:{handle}:permission:request"
│ payload: { path: "/some/external/path", access: "write", reason: "Save analysis" }
├─→ Sentinel continues with other steps (NON-BLOCKING)
│ OR marks step as "waiting:permission" and moves on
├─→ User/system responds:
│ emit: "sentinel:{handle}:permission:response"
│ payload: { path: "/some/external/path", granted: true, expires: "2026-02-14T12:00:00Z" }
└─→ Sentinel receives permission, executes deferred step
```

**No blocking waits.** Everything is handles, events, commands.

---

### Handle-Based Execution

Every sentinel execution returns a handle immediately:

```typescript
interface SentinelHandle {
id: string; // e.g., "aeb8fb01"
status: 'running' | 'completed' | 'failed' | 'cancelled' | 'waiting';
progress: number; // 0-100
currentStep?: number;
totalSteps?: number;

// Workspace paths
workspace: string; // .continuum/jtag/sentinels/workspaces/{handle}/
logsDir: string; // .continuum/jtag/logs/system/sentinels/{handle}/

// Timing
startTime: number;
endTime?: number;

// Results
exitCode?: number;
error?: string;
stepResults?: StepResult[]; // Available after completion
}
```

**Query via**: `sentinel/status --handle={id}`
**Results via**: `sentinel/results --handle={id}` (returns step outputs)

---

### Step Result Storage

Each step's output is captured and stored:

```typescript
interface StepResult {
stepIndex: number;
stepType: 'shell' | 'llm' | 'command' | 'condition' | 'loop';
success: boolean;
durationMs: number;

// Outputs
output?: string; // stdout or LLM response
error?: string; // stderr or error message
exitCode?: number; // For shell steps
data?: any; // Structured result data
}
```

Results written to:
- `.continuum/jtag/logs/system/sentinels/{handle}/steps.jsonl` (streaming)
- `.continuum/jtag/sentinels/workspaces/{handle}/results.json` (final)

---

### Concurrent Execution Limits

```typescript
interface SentinelRuntimeLimits {
maxConcurrentSentinels: number; // e.g., 4
maxStepsPerPipeline: number; // e.g., 100
maxStepTimeout: number; // e.g., 300_000 (5 min)
maxPipelineTimeout: number; // e.g., 3600_000 (1 hour)

// Resource limits per sentinel
maxMemoryMb: number; // e.g., 512
maxDiskMb: number; // e.g., 1024 (workspace size)
maxOpenFiles: number; // e.g., 100
}
```

---

### Inter-Sentinel Communication

Sentinels can emit events for other sentinels:

```typescript
// Pipeline step to emit event
{
type: 'emit',
event: 'codeanalysis:complete',
data: '{{steps.2.output}}' // Variable interpolation
}

// Another sentinel triggers on this
{
trigger: {
type: 'event',
event: 'codeanalysis:complete'
}
}
```

**Pattern**: Sentinels coordinate via events, not direct calls.

---

## Implementation Roadmap

### Phase 1: Foundation
1. ✅ Create SentinelUser base class (extends PersonaUser)
2. ⏭️ Implement tool registry for sentinel access
3. ⏭️ Create trigger system (events, schedules, requests)
4. ⏭️ Build permissions system
2. ✅ Implement Rust SentinelModule with pipeline execution
3. ⏭️ Move logs to `.continuum/jtag/logs/system/sentinels/`
4. ⏭️ Add step result storage and `sentinel/results` command
5. ⏭️ Implement workspace isolation (default sandbox)
6. ⏭️ Build event-based permission request system

### Phase 2: First Sentinel
5. ⏭️ Implement CodeSentinel (simplest, most useful)
Expand Down
8 changes: 7 additions & 1 deletion src/debug/jtag/browser/generated.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Browser Structure Registry - Auto-generated
*
* Contains 11 daemons and 204 commands and 2 adapters and 28 widgets.
* Contains 11 daemons and 205 commands and 2 adapters and 28 widgets.
* Generated by scripts/generate-structure.ts - DO NOT EDIT MANUALLY
*/

Expand All @@ -26,6 +26,7 @@ import { AgentListBrowserCommand } from './../commands/agent/list/browser/AgentL
import { AgentStartBrowserCommand } from './../commands/agent/start/browser/AgentStartBrowserCommand';
import { AgentStatusBrowserCommand } from './../commands/agent/status/browser/AgentStatusBrowserCommand';
import { AgentStopBrowserCommand } from './../commands/agent/stop/browser/AgentStopBrowserCommand';
import { AiAgentBrowserCommand } from './../commands/ai/agent/browser/AiAgentBrowserCommand';
import { BagOfWordsBrowserCommand } from './../commands/ai/bag-of-words/browser/BagOfWordsBrowserCommand';
import { AiContextSearchBrowserCommand } from './../commands/ai/context/search/browser/AiContextSearchBrowserCommand';
import { AiContextSliceBrowserCommand } from './../commands/ai/context/slice/browser/AiContextSliceBrowserCommand';
Expand Down Expand Up @@ -361,6 +362,11 @@ export const BROWSER_COMMANDS: CommandEntry[] = [
className: 'AgentStopBrowserCommand',
commandClass: AgentStopBrowserCommand
},
{
name: 'ai/agent',
className: 'AiAgentBrowserCommand',
commandClass: AiAgentBrowserCommand
},
{
name: 'ai/bag-of-words',
className: 'BagOfWordsBrowserCommand',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import type { CommandParams, CommandResult, JTAGContext, CommandInput} from '@system/core/types/JTAGTypes';
import { createPayload, transformPayload } from '@system/core/types/JTAGTypes';
import { SYSTEM_SCOPES } from '@system/core/types/SystemScopes';
import type { UUID } from '@system/core/types/CrossPlatformUUID';
import { Commands } from '../../../../system/core/shared/Commands';

Expand Down Expand Up @@ -46,6 +47,7 @@ export const createAdapterAdoptParams = (
personaId?: string;
}
): AdapterAdoptParams => createPayload(context, sessionId, {
userId: SYSTEM_SCOPES.SYSTEM,
scale: data.scale ?? 0,
traitType: data.traitType ?? '',
personaId: data.personaId ?? '',
Expand Down Expand Up @@ -95,6 +97,7 @@ export const createAdapterAdoptResult = (
error?: AdapterAdoptError;
}
): AdapterAdoptResult => createPayload(context, sessionId, {
userId: SYSTEM_SCOPES.SYSTEM,
adapterId: data.adapterId ?? '',
layerId: data.layerId ?? '',
personaId: data.personaId ?? '',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import type { CommandParams, CommandResult, JTAGContext, CommandInput} from '@system/core/types/JTAGTypes';
import { createPayload, transformPayload } from '@system/core/types/JTAGTypes';
import { SYSTEM_SCOPES } from '@system/core/types/SystemScopes';
import type { JTAGError } from '@system/core/types/ErrorTypes';
import type { UUID } from '@system/core/types/CrossPlatformUUID';
import { Commands } from '../../../../system/core/shared/Commands';
Expand Down Expand Up @@ -87,6 +88,7 @@ export const createAdapterSearchParams = (
sort?: AdapterSortBy;
}
): AdapterSearchParams => createPayload(context, sessionId, {
userId: SYSTEM_SCOPES.SYSTEM,
...data
});

Expand Down Expand Up @@ -129,6 +131,7 @@ export const createAdapterSearchResult = (
error?: JTAGError;
}
): AdapterSearchResult => createPayload(context, sessionId, {
userId: SYSTEM_SCOPES.SYSTEM,
results: data.results ?? [],
totalCount: data.totalCount ?? 0,
query: data.query ?? '',
Expand Down
3 changes: 3 additions & 0 deletions src/debug/jtag/commands/adapter/try/shared/AdapterTryTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import type { CommandParams, CommandResult, JTAGContext, CommandInput} from '@system/core/types/JTAGTypes';
import { createPayload, transformPayload } from '@system/core/types/JTAGTypes';
import { SYSTEM_SCOPES } from '@system/core/types/SystemScopes';
// Simple error type for result transport
export interface AdapterTryError {
type: string;
Expand Down Expand Up @@ -45,6 +46,7 @@ export const createAdapterTryParams = (
maxTokens?: number;
}
): AdapterTryParams => createPayload(context, sessionId, {
userId: SYSTEM_SCOPES.SYSTEM,
scale: data.scale ?? 0,
maxTokens: data.maxTokens ?? 0,
...data
Expand Down Expand Up @@ -93,6 +95,7 @@ export const createAdapterTryResult = (
error?: AdapterTryError;
}
): AdapterTryResult => createPayload(context, sessionId, {
userId: SYSTEM_SCOPES.SYSTEM,
adapterId: data.adapterId ?? '',
baselineOutput: data.baselineOutput ?? '',
adapterOutput: data.adapterOutput ?? '',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,12 +422,12 @@ export class AdapterTestServerCommand extends CommandBase<AdapterTestParams, Asy
}

result.success = true;
result.responseTime = Date.now() - startTime;
console.log(` ✅ Passed (${result.responseTime}ms)`);
result.responseTimeMs = Date.now() - startTime;
console.log(` ✅ Passed (${result.responseTimeMs}ms)`);
} catch (error) {
result.success = false;
result.error = error instanceof Error ? error.message : String(error);
result.responseTime = Date.now() - startTime;
result.responseTimeMs = Date.now() - startTime;
console.log(` ❌ Failed: ${result.error}`);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export interface CapabilityTestResult {
supported: boolean;
tested: boolean;
success?: boolean;
responseTime?: number;
responseTimeMs?: number;
error?: string;
details?: unknown;
}
Expand Down
Loading
Loading