feat(interpreter): implement hybrid intent resolution with LLM and deterministic fallback

- Added new contracts for intent interpretation, including InterpreterOutput and ResolverMode.
- Implemented deterministic intent resolver with clarity checks for ambiguous references and empty input.
- Developed LLM intent resolver that communicates with an external model, handling JSON responses and fallback clarifications.
- Created an interpretTurn function to manage intent resolution based on the selected resolver mode.
- Introduced validation for interpreter output to ensure integrity before processing actions.
- Established a turn manager to orchestrate turn processing, including action validation and world state mutation.
- Added integration tests to verify the functionality of the new intent resolution system.

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-04-26 14:06:14 -04:00
parent ff9b86c3e9
commit fc10e46ccc
23 changed files with 1530 additions and 1012 deletions

View File

@@ -1,12 +1,12 @@
# CharacterGarden — Iterative Implementation Plan
# CharacterGarden — Iterative Implementation Plan (Updated)
## Copilot Operating Rules
## Planning Rules
Work in small, reviewable steps.
After every completed step:
After each completed step:
1. Update `thoughts.md`
1. Update thoughts.md
2. Record files changed
3. Record assumptions made
4. Record next step
@@ -16,388 +16,233 @@ Do not redesign the project without updating this plan.
---
# Phase 1 — Contracts First
# Phase 0 — Completed Baseline
## Step 1.1 — Create contracts folder
Status: COMPLETE
Create:
```txt
app/src/contracts/
```
Add:
```txt
app/src/contracts/action.ts
app/src/contracts/turn.ts
app/src/contracts/validation.ts
app/src/contracts/world.ts
app/src/contracts/entity.ts
```
Goal: all shared types live here.
- Contracts are established under app/src/contracts/
- Deterministic truth engine is active
- Rulebook-driven validation is active and editable
- Core actions supported: inspect, move, open, take, introduce, describe, transfer
- take createIfMissing path implemented with rulebook authorization
- transfer action implemented with ownership/location checks
- Turn orchestration moved behind turn manager seam
- Interpreter contract and first interpreter module created
- Docker container builds are passing for app and frontend
---
## Step 1.2 — Define Action contract
# Phase 1 — Intent Interpreter Boundary Hardening
In `action.ts`:
Status: IN PROGRESS
```ts
export type Action = {
actorId: string;
type: string;
targetId?: string;
locationId?: string;
metadata?: Record<string, unknown>;
};
```
## Step 1.1 — Strict interpreter envelope validation
No other action shape should be used.
Goal:
- Validate InterpreterOutput shape before it reaches validation/mutation.
- Reject malformed interpreter payloads deterministically.
Target files:
- app/src/contracts/intent.ts
- app/src/interpreter/interpretTurn.ts
- app/src/turns/turnManager.ts
## Step 1.2 — Clarification model expansion
Goal:
- Add structured clarification options and optional entity candidates.
- Distinguish unrecognized-intent from reference ambiguity consistently.
Target files:
- app/src/contracts/intent.ts
- app/src/interpreter/interpretTurn.ts
## Step 1.3 — API compatibility for unresolved turns
Goal:
- Ensure /api/turn returns interpreter status and clarification payload reliably.
- Keep existing fields backward-compatible for current frontend behavior.
Target files:
- app/src/turns/processTurn.ts
- app/src/index.ts
---
## Step 1.3 — Define ValidationResult contract
# Phase 2 — Turn Trace and Persistence Enrichment
In `validation.ts`:
Status: NOT STARTED
```ts
export type ValidationResult = {
actionIndex: number;
success: boolean;
reason?: string;
message?: string;
};
```
## Step 2.1 — Persist interpreter envelope per turn
Goal:
- Persist interpreter status, diagnostics, and clarification metadata.
Target files:
- app/src/db.ts
- app/src/contracts/turn.ts
- app/src/turns/turnManager.ts
## Step 2.2 — Extend read models and API snapshots
Goal:
- Include interpreter trace data in turn history returned by /api/state.
Target files:
- app/src/db.ts
- app/src/app.ts
- app/src/index.ts
## Step 2.3 — Frontend turn inspector updates
Goal:
- Display interpreter status and clarification prompts in timeline and latest result.
Target files:
- frontend/src/App.tsx
---
## Step 1.4 — Define Turn contract
# Phase 3 — Resolver Plug-in Architecture
In `turn.ts`:
Status: COMPLETE
```ts
import type { Action } from "./action";
import type { ValidationResult } from "./validation";
## Step 3.1 — Introduce resolver interface
export type Turn = {
id: string;
rawText: string;
actions: Action[];
validation: ValidationResult[];
createdAt: number;
};
```
Goal:
- Define a stable resolver interface that returns InterpreterOutput.
Target files:
- app/src/interpreter/resolveIntent.ts (new)
- app/src/interpreter/interpretTurn.ts
## Step 3.2 — Deterministic adapter extraction
Goal:
- Move current parser-backed behavior into a deterministic adapter.
Target files:
- app/src/interpreter/adapters/deterministicResolver.ts (new)
- app/src/interpreter/interpretTurn.ts
## Step 3.3 — LLM resolver adapter
Goal:
- Add LLM adapter behind config, without making it authoritative over deterministic validation.
Target files:
- app/src/interpreter/adapters/llmResolver.ts (new)
- app/src/app.ts
## Step 3.4 — Fallback strategy
Goal:
- Support deterministic fallback when LLM resolver fails or is low confidence.
Target files:
- app/src/interpreter/interpretTurn.ts
---
## Step 1.5 — Define Entity contract
# Phase 4 — Rulebook Governance and Compatibility
In `entity.ts`:
Status: IN PROGRESS
```ts
export type Entity = {
id: string;
name: string;
type: string;
attributes: Record<string, unknown>;
};
```
## Step 4.1 — Rulebook versioning
Goal:
- Add version field and migration path for existing saved rulebooks.
Status: COMPLETE
Target files:
- app/src/contracts/rulebook.ts
- app/src/defaultRulebook.ts
- app/src/db.ts
## Step 4.2 — Policy pack structure
Goal:
- Organize rules into policy packs (creation, transfer, social) while retaining current behavior.
Target files:
- app/src/defaultRulebook.ts
## Step 4.3 — Rulebook editor affordances
Goal:
- Surface policy grouping and version in the frontend editor.
Target files:
- frontend/src/App.tsx
---
## Step 1.6 — Define WorldState contract
# Phase 5 — Docker-First Test Harness
In `world.ts`:
Status: NOT STARTED
```ts
import type { Entity } from "./entity";
## Step 5.1 — Backend integration tests
export type WorldState = {
id: string;
entities: Record<string, Entity>;
metadata: Record<string, unknown>;
createdAt: number;
};
```
Goal:
---
- Cover deterministic scenarios:
- unauthorized createIfMissing denied
- authorized createIfMissing allowed
- transfer success and failure matrix
- unresolved intent clarification behavior
# Phase 2 — Enforce Layer Boundaries
Target files:
## Step 2.1 — Refactor truth engine imports
- app/src/** tests (new)
Update `truthEngine.ts` so it imports:
## Step 5.2 — Containerized test commands
```ts
import type { Action } from "./contracts/action";
import type { ValidationResult } from "./contracts/validation";
import type { WorldState } from "./contracts/world";
```
Goal:
Truth engine must only receive structured actions.
- Provide canonical Docker commands for app/frontend build and tests.
---
Target files:
## Step 2.2 — Remove text parsing from truth engine
- charactergarden/docker-compose.yml
- project.md
- thoughts.md
Search `truthEngine.ts` for:
## Step 5.3 — CI readiness checklist
* string parsing
* natural language interpretation
* prompt logic
* LLM calls
Goal:
Move any such logic out.
- Ensure all checks are containerized and reproducible.
Truth engine should expose:
Deliverable:
```ts
export function validateActions(
actions: Action[],
worldState: WorldState
): ValidationResult[] {
// deterministic validation only
}
```
---
## Step 2.3 — Create parser layer
Create:
```txt
app/src/parser/
app/src/parser/parseTextToActions.ts
```
Function:
```ts
import type { Action } from "../contracts/action";
export function parseTextToActions(text: string): Action[] {
// temporary simple parser
return [];
}
```
For now, returning `[]` is acceptable.
---
## Step 2.4 — Create world state engine
Create:
```txt
app/src/world/
app/src/world/applyActions.ts
```
Function:
```ts
import type { Action } from "../contracts/action";
import type { ValidationResult } from "../contracts/validation";
import type { WorldState } from "../contracts/world";
export function applyActions(
actions: Action[],
results: ValidationResult[],
worldState: WorldState
): WorldState {
// apply only successful actions
return worldState;
}
```
---
# Phase 3 — Build First Deterministic Test Domain
Use a simple door/key room before anything complex.
## Step 3.1 — Seed initial world
Create initial world state:
* actor: `player`
* room: `room_start`
* door: `door_1`
* key: `key_1`
Door starts locked.
Key starts in room.
Player starts in room.
---
## Step 3.2 — Support action types
Truth engine should recognize:
```txt
inspect
take
open
move
```
Unknown action types fail with:
```txt
reason: "unknown_action"
```
---
## Step 3.3 — Validate take action
Rules:
* Actor must exist
* Target must exist
* Target must be in same location
* Target must be takeable
Failure reasons:
* `actor_not_found`
* `target_not_found`
* `not_in_same_location`
* `not_takeable`
---
## Step 3.4 — Validate open action
Rules:
* Actor must exist
* Target must exist
* Target must be openable
* If locked, actor must have matching key
Failure reasons:
* `actor_not_found`
* `target_not_found`
* `not_openable`
* `locked_requires_key`
---
## Step 3.5 — Apply successful take
If `take` succeeds:
* move item into actor inventory
---
## Step 3.6 — Apply successful open
If `open` succeeds:
* set door attribute `open: true`
---
# Phase 4 — Wire Full Turn Processing
## Step 4.1 — Create turn processor
Create:
```txt
app/src/turns/processTurn.ts
```
Function:
```ts
export async function processTurn(rawText: string): Promise<Turn> {
// parse
// validate
// apply
// persist
// return turn
}
```
---
## Step 4.2 — Enforce pipeline order
The turn processor must call:
```txt
parseTextToActions
validateActions
applyActions
persistTurn
```
In that order.
No layer may skip ahead.
---
## Step 4.3 — Add debug response
API should return:
```ts
{
rawText,
actions,
validation,
worldState
}
```
This is for MVP debugging.
---
# Phase 5 — Persistence
## Step 5.1 — Add database tables
Minimum SQLite tables:
```sql
turns
actions
validation_results
entities
world_states
```
---
## Step 5.2 — Persist every turn
Each call to `processTurn` must save:
* raw text
* parsed actions
* validation results
* resulting world state snapshot
---
## Step 5.3 — Add reset endpoint
Add an endpoint to reset world state to seed state.
This is needed for testing.
- One documented command sequence that reproduces local validation.
---