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:
@@ -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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user