Commit Graph

4 Commits

Author SHA1 Message Date
kami 57cf6f09f4 feat(kernel): enforce stage allowedTools at tool-call dispatch
A stage's allowedTools only shaped which tool definitions were offered to
the model; the dispatch path resolved returned tool calls straight from the
full registry and executed them without checking stage membership. A
hallucinated, jailbroken, or edited-transcript tool call for any registered
tool would run unchecked (violating invariant #5 / policy-is-absolute).

dispatchToolCalls now rejects any tool call whose name is not in the stage's
allowedTools (STAGE_COMPLETE_TOOL exempt), emitting ToolExecutionRejectedEvent
and feeding an error ContextEntry back to the model instead of executing.
Empty allowedTools means deny-all domain tools, consistent with offering
only STAGE_COMPLETE_TOOL at inference time.
2026-06-03 13:16:47 +04:00
kami d8e36b8282 feat(kernel): make approval steering actually affect the run
A steering note attached to a tool approval was recorded but only
consumed by the NEXT stage's prompt build (buildSteeringNoteEntries runs
once, before the tool loop), so on a single-stage task it had no visible
effect. Two changes:

- Approve+note: capture userDecision.userSteering at the gate and inject
  it as a context entry right after the tool result, so the same stage's
  loop re-infers with the note and the model acts on it.
- Reject: buildSteeringNoteEntries now also emits anti-repeat feedback for
  REJECTED decisions with no note, so the model does not re-propose the
  identical call on the retryable re-run (a bare reject previously fed
  back only "approval denied").

Integration test drives an approval with a steering note and asserts the
next inference's context carries it.
2026-06-03 01:44:08 +04:00
kami d03479cea7 feat: tool-declared output compression (Tier A)
Tools can declare how their output is compressed before it enters the
context pack, replacing uniform handling. Open-threads 6.3 / ADR-0003.

- core:tools/compression: ToolOutputCompressor interface,
  IdentityCompressor default, declarative OutputCompressionSpec +
  CompressionRule (StripBlankLines, StripLeadingWhitespace,
  DropMatching, HeadTail), and the pure DeclarativeCompressor engine
  (regexes compiled at construction; bypassOnError passes raw on
  non-zero exit; never throws on input).
- Tool gains `outputCompressor` (default IdentityCompressor). FileReadTool
  strips blanks + leading whitespace; ShellTool strips blanks + head/tail.
- SessionOrchestrator applies the resolved tool's compressor on the
  ToolResult -> ContextEntry path only. Raw output stays authoritative
  in the event log (invariant #6); compression is a pure function of
  (raw, exitCode, spec), so no new event and replay stays deterministic.
- Spec is @Serializable/config-shaped (JSON round-trip tested). No
  custom-tool-from-config path exists today, so first-party tools wire
  in code; TOML-declared compression rides along when that path lands.

Tier B (format-aware parsers: failures-only test output, git-log oneline)
is a documented per-tool extension behind the interface, not built here.
2026-05-31 21:20:16 +04:00
kami 545068d222 feat: plane-2 tool-call intent validation (path containment slice)
Implements the full vertical slice for invariant #9 tool-call assessment:
- core:toolintent — new module with ToolCallRule seam, ToolCallAssessor,
  WorldProbe/FileSystemWorldProbe, WorkspacePolicy, PathContainmentRule,
  and RiskMapping (assessment → RiskSummary / AssessedIssue)
- core:tools — ToolCallAssessmentRecord + ToolInvocationRecord.assessment
  field; DefaultToolReducer handles ToolCallAssessedEvent (replay proof)
- core:config — ToolsConfig gains workspaceRoot + privilegedLocations;
  ConfigLoader parses both; DEFAULT_PRIVILEGED_LOCATIONS built-in
- core:kernel — OrchestratorEngines gains toolCallAssessor/workspacePolicy/
  worldProbe fields; SessionOrchestrator.dispatchToolCalls runs
  runPlane2Assessment before the tier gate: BLOCK → hard-reject without
  executing; PROMPT_USER → elevates tier into approval path with plane2Risk
  in the ApprovalRequestedEvent
- apps/server — constructs PathContainmentRule + ToolCallAssessor +
  WorkspacePolicy from config and wires them into OrchestratorEngines

Assessment is recorded as ToolCallAssessedEvent (environment observed once,
facts stored, replay reads events — invariant #9). Assessor and WorldProbe
are only invoked on the live orchestrator path, never in replay.
2026-05-31 04:22:58 +04:00