builder

package
v0.3.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 21, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package builder provides shared scaffolding for the per-provider direct-emit hook handlers under internal/hooks/<provider>. Every provider translates incoming hook payloads into broker.RawEvent values, and most of that translation is identical across providers: computing a content-addressed event ID, assembling the session envelope, storing blobs with SHA-based hashes, synthesizing the assistant payload shape consumed by the attribution scorer.

This package owns those shared operations so each provider's direct_emit.go can focus on its provider-specific logic (tool-input parsing, payload fields, summary formatting rules).

Failure semantics. Hash-returning helpers never return an error. On any failure (nil blob putter, blob-store error, marshal error) they return an empty string, and the caller continues to assemble a well-formed broker.RawEvent with the missing hash field empty. This matches the direct-emit write-path contract that has been in place since the per-provider helpers were introduced: a local serialization failure is a data-quality issue for one field, not a runtime degradation the event stream should surface, and adding logging at this layer would produce noise without actionable signal. The silent-degradation rule is enforced by the signatures in this package; helpers that might grow an error return in the future should live in a different package.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BaseRawEvent

func BaseRawEvent(in BaseInput) broker.RawEvent

BaseRawEvent constructs the broker.RawEvent envelope fields that every provider wires the same way: event ID, source key, provider name, timestamps, session identifiers, metadata, project path, and model. The caller fills in the per-event fields (Kind, Role, Summary, PayloadHash, ProvenanceHash, ToolUsesJSON, TurnID, ToolUseID, ToolName, EventSource, FilePaths) on the returned value.

func ComputeEventID

func ComputeEventID(sourceKey string, event *hooks.Event) string

ComputeEventID returns a deterministic SHA-256 hex digest derived from the source key and stable hook context. Using ToolUseID (or TurnID when ToolUseID is empty) as the stable key means replayed hook deliveries for the same step produce the same event ID, and downstream INSERT OR IGNORE semantics suppress duplicates without the broker needing a separate deduplication pass.

The format of the hashed input is:

sourceKey + ":hook:" + HookPhase + ":" + ToolName + ":" + StableKey

where StableKey is ToolUseID if non-empty, otherwise TurnID.

func PutAndHash

func PutAndHash(ctx context.Context, bs api.BlobPutter, payload []byte) string

PutAndHash writes the payload to the blob store and returns its content hash. Returns an empty string when the blob store is nil or the Put call fails; the caller continues to assemble the event with the hash field left empty, matching the direct-emit silent-degradation contract.

func Redact

func Redact(s string) string

Redact wraps redact.String with the error policy every direct-emit helper already uses: on redaction failure, return the input string unchanged rather than surfacing the error. Returns the input untouched when it is empty so call sites do not have to guard individually.

func StorePromptPayload

func StorePromptPayload(ctx context.Context, bs api.BlobPutter, prompt string) string

StorePromptPayload persists the raw bytes of a user or subagent prompt and returns the content hash. Thin wrapper over PutAndHash named for intent so call sites read naturally.

func StoreWrappedHookProvenance

func StoreWrappedHookProvenance(ctx context.Context, bs api.BlobPutter, toolInput, toolResponse json.RawMessage) string

StoreWrappedHookProvenance stores the original hook payload under the shared envelope used by Claude, Copilot, Gemini, and Kiro CLI:

{ "tool_input": <toolInput>, "tool_response": <toolResponse> }

The tool_response field is omitted when toolResponse is empty, so hook phases that do not carry a response (for example pre-tool-use) produce a smaller, wrapper-only blob.

Cursor stores the raw payload without this wrapper and therefore keeps a small provider-local helper instead of calling this one.

Returns an empty string on marshal or blob-store failure.

func SynthesizeAssistantBlob

func SynthesizeAssistantBlob(ctx context.Context, bs api.BlobPutter, toolName string, inputJSON json.RawMessage) string

SynthesizeAssistantBlob marshals the canonical assistant payload shape consumed by the attribution scorer and stores it in the blob store. The shape is:

{
  "type": "assistant",
  "message": {
    "content": [
      { "type": "tool_use", "name": <toolName>, "input": <inputJSON> }
    ]
  }
}

inputJSON must already be normalized to the shape the scorer expects for the given tool. Providers whose hook payloads use different field names convert them to the canonical shape before calling this helper.

Returns an empty string on marshal or blob-store failure.

func TruncateClean

func TruncateClean(s string, max int) string

TruncateClean normalizes whitespace and truncates without an ellipsis. Used by the Copilot prompt and subagent-prompt paths. The steps are:

  1. Trim leading and trailing whitespace.
  2. Replace embedded newlines with single spaces.
  3. Strip carriage returns entirely (no substitution, which means "a\rb" becomes "ab" rather than "a b").
  4. Truncate to at most max bytes, with no ellipsis appended.

The asymmetry between newline handling (substituted) and carriage return handling (dropped) is preserved from the original agentcopilot.Truncate implementation and is asserted by the Copilot direct-emit tests.

func TruncateWithEllipsis

func TruncateWithEllipsis(s string, max int) string

TruncateWithEllipsis returns s unchanged when its length is at most max; otherwise it returns the first max bytes followed by "...". Used by the Claude, Cursor, Gemini, and Kiro CLI prompt paths. No whitespace normalization is applied; the input appears in the summary exactly as it was received.

Types

type BaseInput

type BaseInput struct {
	// Event is the source hook event. Required.
	Event *hooks.Event

	// SourceKey is the provider-specific identifier used for
	// content-addressed event IDs and for the SourceKey field on
	// the emitted RawEvent. Usually the transcript reference, with
	// per-provider fallbacks when a transcript is not available.
	SourceKey string

	// Provider is the agent provider name (for example, the value
	// exposed by each agent package as ProviderName).
	Provider string

	// ProviderSessionID identifies the session as the provider sees
	// it. Some providers derive this from the transcript path; some
	// use the hook-supplied session ID directly. The builder treats
	// it as opaque.
	ProviderSessionID string

	// ParentSessionID is optional and currently only set by Claude,
	// which can link subagent sessions back to their parent. Empty
	// for providers that do not track this relationship.
	ParentSessionID string

	// SessionMetaJSON is a pre-serialized JSON object of session
	// metadata. Providers vary in what they include, so the builder
	// does not try to assemble this itself.
	SessionMetaJSON string

	// SourceProjectPath is the repository root associated with the
	// event, usually decoded from the transcript path with a CWD
	// fallback.
	SourceProjectPath string
}

BaseInput carries the provider-specific fields that vary per provider when constructing the base broker.RawEvent envelope. Everything else (event ID computation, timestamp wiring) is derived from the hooks.Event itself.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL