Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/empty-mayflies-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"dashboard": patch
"@gram/client": patch
"server": patch
---

Adds an initial pass "POC" implementation of Gram hooks for tool capture
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@ package-lock.json
/examples/*/pnpm-lock.yaml
.worktrees
.granary/
.claude/hooks/*
9 changes: 9 additions & 0 deletions .mise-tasks/install/hooks.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
# Install Gram Claude hooks - wrapper for the standalone install script
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"

# Run the standalone install script from the repo
exec "$REPO_ROOT/hooks/install.sh"
51 changes: 51 additions & 0 deletions .speakeasy/out.openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14405,6 +14405,14 @@ components:
- tool_urn
- created_at
- updated_at
HookResult:
type: object
properties:
ok:
type: boolean
description: Whether the hook was received successfully
required:
- ok
InfoResponseBody:
type: object
properties:
Expand Down Expand Up @@ -15440,6 +15448,40 @@ components:
- credits
- included_credits
- has_active_subscription
PostToolUseFailurePayload:
type: object
properties:
tool_error:
description: The error from the tool
tool_input:
description: The input to the tool
tool_name:
type: string
description: The name of the tool that failed
required:
- tool_name
PostToolUsePayload:
type: object
properties:
tool_input:
description: The input to the tool
tool_name:
type: string
description: The name of the tool that was invoked
tool_response:
description: The response from the tool
required:
- tool_name
PreToolUsePayload:
type: object
properties:
tool_input:
description: The input to the tool
tool_name:
type: string
description: The name of the tool being invoked
required:
- tool_name
Project:
type: object
properties:
Expand Down Expand Up @@ -16767,6 +16809,9 @@ components:
ToolCallSummary:
type: object
properties:
event_source:
type: string
description: Event source (from attributes.gram.event.source)
gram_urn:
type: string
description: Gram URN associated with this tool call
Expand All @@ -16782,6 +16827,12 @@ components:
type: integer
description: Earliest log timestamp in Unix nanoseconds
format: int64
tool_name:
type: string
description: Tool name (from attributes.gram.tool.name)
tool_source:
type: string
description: Tool call source (from attributes.gram.tool_call.source)
trace_id:
type: string
description: Trace ID (32 hex characters)
Expand Down
2 changes: 1 addition & 1 deletion .speakeasy/workflow.lock
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
speakeasyVersion: 1.700.2
speakeasyVersion: 1.733.4
sources:
Gram-Internal:
sourceNamespace: gram-api-description
Expand Down
24 changes: 13 additions & 11 deletions client/dashboard/src/pages/logs/TraceRow.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import {
ToolCallSummary,
TelemetryLogRecord,
ToolCallSummary,
} from "@gram/client/models/components";
import { ChevronDownIcon, ChevronRightIcon } from "lucide-react";
import { StatusBadge } from "./StatusBadge";
import { TraceLogsList } from "./TraceLogsList";
import {
formatNanoTimestamp,
getStatusInfo,
getSourceFromUrn,
getToolNameFromUrn,
getStatusInfo,
getToolIcon,
getToolNameFromUrn,
} from "./utils";
import { TraceLogsList } from "./TraceLogsList";
import { StatusBadge } from "./StatusBadge";

interface TraceRowProps {
trace: ToolCallSummary;
Expand All @@ -27,9 +27,9 @@ export function TraceRow({
onLogClick,
}: TraceRowProps) {
const { isSuccess } = getStatusInfo(trace);
const sourceName = getSourceFromUrn(trace.gramUrn);
const toolName = getToolNameFromUrn(trace.gramUrn);
const ToolIcon = getToolIcon(trace.gramUrn);
const sourceName = trace.toolSource || getSourceFromUrn(trace.gramUrn);
const toolName = trace.toolName || getToolNameFromUrn(trace.gramUrn);
const ToolIcon = getToolIcon(trace);

return (
<div className="border-b border-border/50 last:border-b-0">
Expand All @@ -55,9 +55,11 @@ export function TraceRow({
{/* Icon + Source badge + Tool name */}
<div className="flex items-center gap-2 flex-1 min-w-0">
<ToolIcon className="size-4 shrink-0" strokeWidth={1.5} />
<span className="shrink-0 px-1.5 py-0.5 text-xs font-medium rounded bg-muted text-muted-foreground">
{sourceName}
</span>
{sourceName && (
<span className="shrink-0 px-1.5 py-0.5 text-xs font-medium rounded bg-muted text-muted-foreground">
{sourceName}
</span>
)}
<span className="text-sm font-mono truncate">{toolName}</span>
</div>

Expand Down
32 changes: 21 additions & 11 deletions client/dashboard/src/pages/logs/utils.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { dateTimeFormatters } from "@/lib/dates";
import {
ToolCallSummary,
TelemetryLogRecord,
ToolCallSummary,
} from "@gram/client/models/components";
import { dateTimeFormatters } from "@/lib/dates";
import {
FileCode,
SquareTerminal as HookIcon,
LucideIcon,
PencilRuler,
SquareFunction,
LucideIcon,
Hammer as ToolIcon,
} from "lucide-react";

/**
Expand Down Expand Up @@ -98,16 +100,24 @@ export function getToolNameFromUrn(urn: string): string {
/**
* Get the appropriate icon for a tool based on its URN
*/
export function getToolIcon(urn: string): LucideIcon {
const { kind } = parseGramUrn(urn);
if (kind === "http") {
return FileCode;
export function getToolIcon(trace: ToolCallSummary): LucideIcon {
if (trace.gramUrn) {
const { kind } = parseGramUrn(trace.gramUrn);
if (kind === "http") {
return FileCode;
}
if (kind === "prompt") {
return PencilRuler;
}
// Otherwise it's a function tool
return SquareFunction;
}
if (kind === "prompt") {
return PencilRuler;

if (trace.eventSource === "hook") {
return HookIcon;
}
// Otherwise it's a function tool
return SquareFunction;

return ToolIcon;
}

/**
Expand Down
5 changes: 4 additions & 1 deletion client/sdk/.npmignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 17 additions & 17 deletions client/sdk/.speakeasy/gen.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions client/sdk/.speakeasy/gen.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading