79 lines
5.8 KiB
Markdown
79 lines
5.8 KiB
Markdown
# canonical-tool-invocation-store Specification
|
|
|
|
## Purpose
|
|
定义 Harness ToolBoundary 与 canonical invocation store 的统一执行边界,包括 Run/Tool preflight、PROJECTING/READY/ERROR 生命周期、evidence status、框架 Tool Call ID、TTL、容量和有界 Agent 投影。
|
|
|
|
## Requirements
|
|
### Requirement: ToolBoundary SHALL enforce explicit preflight before execution
|
|
The Harness SHALL reject a Tool call before invoking the executor when the envelope has a blank/unsafe framework `tool_call_id`, a run ID different from RunContext, invalid JSON object input, unauthorized access, non-read-only access, an inactive/deadline-expired Run, duplicate canonical key, or exhausted Tool/Run budget.
|
|
|
|
#### Scenario: Cross-Run Tool Call is rejected
|
|
- **WHEN** an envelope run ID differs from the explicit RunContext run ID
|
|
- **THEN** ToolBoundary returns a safe `ERROR`, does not create a canonical record, and does not invoke the Tool
|
|
|
|
#### Scenario: Unauthorized or writable Tool is rejected
|
|
- **WHEN** `authorized=false` or `readOnly=false`
|
|
- **THEN** ToolBoundary returns `ERROR` before execution and does not expose the request to an Agent
|
|
|
|
### Requirement: Canonical invocation SHALL keep complete data in one record
|
|
The store SHALL save the complete request JSON, raw Tool response, bounded Agent result, framework `tool_call_id`, run ID, tool name, lifecycle status, evidence status, timestamps, and error code in one canonical record. The raw response SHALL NOT be replaced by a preview, and SHALL NOT be returned to the Agent.
|
|
|
|
#### Scenario: Tool begins execution
|
|
- **WHEN** preflight succeeds and the Tool is about to execute
|
|
- **THEN** one record is created with `status=PROJECTING`, the complete request, the exact framework ID, and no Agent result yet
|
|
|
|
#### Scenario: Projector succeeds
|
|
- **WHEN** the executor returns raw data and the projector returns a bounded result
|
|
- **THEN** the same record contains raw data and Agent result with `status=READY`, and ToolBoundary returns only the bounded Agent result
|
|
|
|
### Requirement: Invocation lifecycle and evidence semantics SHALL be enforced centrally
|
|
The store SHALL allow only `PROJECTING -> READY` or `PROJECTING -> ERROR`. READY SHALL require `EVIDENCE_FOUND` or `NO_EVIDENCE`; ERROR SHALL use `evidence_status=ERROR` and SHALL NOT be referencable. `NO_EVIDENCE` SHALL remain a scoped negative observation and SHALL NOT be upgraded to success or retried by the boundary.
|
|
|
|
#### Scenario: No evidence projection completes
|
|
- **WHEN** a projector returns a valid bounded result with `NO_EVIDENCE`
|
|
- **THEN** the record becomes `READY`, preserves the result scope, and remains eligible only for a negative observation
|
|
|
|
#### Scenario: Projection fails
|
|
- **WHEN** the projector throws or returns an invalid evidence status
|
|
- **THEN** the same record becomes `ERROR`, stores a safe error code, and no Agent result is returned
|
|
|
|
### Requirement: Tool Call ID and Run ownership SHALL be preserved
|
|
The boundary SHALL use the exact framework `tool_call_id` with the RunContext run ID to create the canonical key. It SHALL reject missing, unsafe, duplicate, and cross-Run references and SHALL never generate or replace a fallback ID.
|
|
|
|
#### Scenario: Duplicate Tool Call ID is submitted
|
|
- **WHEN** a second invocation uses the same valid run ID and framework Tool Call ID
|
|
- **THEN** the second Tool is not executed and returns `ERROR` without overwriting the first record
|
|
|
|
#### Scenario: Framework ID is preserved
|
|
- **WHEN** a valid envelope passes preflight
|
|
- **THEN** the key and canonical record contain the exact supplied `tool_call_id`
|
|
|
|
### Requirement: TTL and size limits SHALL fail closed
|
|
The Redis implementation SHALL set TTL only when a canonical record is created. Reads SHALL NOT refresh TTL; updates SHALL preserve only the current remaining TTL. Request/raw/record and Agent-result byte limits SHALL be enforced in UTF-8. Raw overflow SHALL produce `ERROR/RESULT_TOO_LARGE` without silent truncation; Agent projection overflow SHALL produce the same error.
|
|
|
|
#### Scenario: Read does not renew TTL
|
|
- **WHEN** a canonical record is read before expiration
|
|
- **THEN** its expiry remains at or before the original expiry and no expire/refresh operation is issued
|
|
|
|
#### Scenario: Raw response is too large
|
|
- **WHEN** the executor returns raw data beyond the record limit
|
|
- **THEN** the record becomes `ERROR` with `RESULT_TOO_LARGE`, the raw payload is not silently truncated, and the projector is not invoked
|
|
|
|
#### Scenario: Agent result is too large
|
|
- **WHEN** a projector returns a result beyond the Agent projection limit
|
|
- **THEN** the record becomes `ERROR/RESULT_TOO_LARGE` and the oversized result is not returned to the Agent
|
|
|
|
### Requirement: Tool and store failures SHALL return safe errors
|
|
Execution, serialization, Redis, duplicate, preflight, and projection failures SHALL return a bounded error code without raw payload, credentials, internal stack trace, or Redis key to the Agent. If raw data was safely stored before a projection failure, it remains Harness-only canonical data.
|
|
|
|
#### Scenario: Tool execution throws
|
|
- **WHEN** the executor raises an exception after PROJECTING begins
|
|
- **THEN** the record becomes `ERROR` with a stable execution error code and ToolBoundary returns no raw response
|
|
|
|
### Requirement: Stage 3A SHALL remain reusable and independent from legacy audit
|
|
The boundary and canonical store SHALL be usable by later RAG/log/MySQL projectors without modifying legacy `ToolInvocationRecorder`, JPA `ToolInvocation`, Chat/AIOps, Controller, or public protocol. Redis access SHALL remain inside the store implementation and no Agent-facing API SHALL expose the Redis client.
|
|
|
|
#### Scenario: Fake projector tests pass
|
|
- **WHEN** Fake Tool/Projector tests cover success, no evidence, error, duplicate, TTL, and capacity cases
|
|
- **THEN** later Tool-specific changes can depend on the same boundary without copying lifecycle or Redis logic, while legacy call sites remain unchanged
|