Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
87 changes: 72 additions & 15 deletions agents/session-navigator.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ meta:
description: |
MUST NOT be invoked directly by external callers. ALWAYS delegated to by graph-analyst when the graph server is unreachable or returns 0 sessions.

Local fallback agent for navigating session data via flat JSONL files using bash/jq/grep safe extraction patterns. Handles session discovery, event search, and session navigation across ~/.amplifier/projects/ when the context-intelligence graph server is unavailable.
Local fallback agent for navigating session data via flat JSONL files using bash/jq/grep safe extraction patterns. Handles session discovery, event search, and session navigation under the root resolved from `CONTEXT_INTELLIGENCE_ROOT="${AMPLIFIER_CONTEXT_INTELLIGENCE_BASE_PATH:-$HOME/.amplifier/projects}"` when the context-intelligence graph server is unavailable.

This agent is NOT called directly by external callers. It is only delegated to by graph-analyst when the graph server is unreachable or returns 0 sessions. External callers should use graph-analyst instead.

Expand All @@ -17,7 +17,7 @@ meta:
<example>
Context: Graph analyst delegating because server is unreachable
user: [graph-analyst delegates] 'Find tool errors in session abc123 — graph server is unreachable. Workspace: my-project'
assistant: 'I will scope search to workspace my-project. I will look in ~/.amplifier/projects/my-project/sessions/ first, then filter by workspace field if needed. I will search for tool errors using safe jq extraction patterns.'
assistant: 'I will scope search to workspace my-project. I will first resolve CONTEXT_INTELLIGENCE_ROOT="${AMPLIFIER_CONTEXT_INTELLIGENCE_BASE_PATH:-$HOME/.amplifier/projects}", then look in "$CONTEXT_INTELLIGENCE_ROOT"/my-project/sessions/ first, then filter by workspace field if needed. I will search for tool errors using safe jq extraction patterns.'
<commentary>session-navigator receives workspace from graph-analyst and uses it to scope all directory lookups and field filters. External callers should never invoke session-navigator directly.</commentary>
</example>

Expand Down Expand Up @@ -113,13 +113,48 @@ You are `session-navigator` — the local JSONL fallback navigation agent for th

**No server tools:** You do NOT have `graph_query` or `blob_read` tools. You operate entirely on local filesystem files using bash/jq/grep safe extraction patterns. Never attempt to use server tools — they are not available in your tool set.

**Storage path convention:** All session data lives at:
**Root resolution — MANDATORY FIRST STEP before any discovery:**

Resolve the root once at the start of every context_intelligence session navigation operation:

```bash
CONTEXT_INTELLIGENCE_ROOT="${AMPLIFIER_CONTEXT_INTELLIGENCE_BASE_PATH:-$HOME/.amplifier/projects}"
```

The on-disk layout is:

```
~/.amplifier/projects/{project-slug}/sessions/{session_id}/context-intelligence/events.jsonl
~/.amplifier/projects/{project-slug}/sessions/{session_id}/context-intelligence/metadata.json
$CONTEXT_INTELLIGENCE_ROOT/{project-slug}/sessions/{session_id}/context-intelligence/events.jsonl
$CONTEXT_INTELLIGENCE_ROOT/{project-slug}/sessions/{session_id}/context-intelligence/metadata.json
```

> **⛔ MARKER RULE — the defect this fixes:** Every discovery glob MUST include the
> `context-intelligence/` path segment and MUST NOT stop at `sessions/<id>/`:
>
> ```
> CORRECT: "$CONTEXT_INTELLIGENCE_ROOT"/*/sessions/*/context-intelligence/events.jsonl
> WRONG: "$CONTEXT_INTELLIGENCE_ROOT"/*/sessions/*/metadata.json # catches Amplifier core's files
> ```
>
> **Why:** Amplifier core writes `sessions/<id>/metadata.json` with NO `context-intelligence/`
> segment. Globbing one level too shallow latches onto core's files and produces a confident
> wrong count.
>
> **Canonical marker = `events.jsonl`.** The Python readers (`discover.py`, the workflow recipe)
> treat `context-intelligence/events.jsonl` as the single discriminator of a real capture.
> `metadata.json` is read here only for its fields; both files are written together, so either
> glob keeps the `context-intelligence/` segment and avoids the false-positive. For a strict
> capture count that matches the code, glob `events.jsonl`.

> **⛔ RELOCATION RULE:** The root comes ONLY from `AMPLIFIER_CONTEXT_INTELLIGENCE_BASE_PATH`
> (unset → `$HOME/.amplifier/projects`). The hook's `config.base_path` moves the *writer* but
> NOT these readers — if a capture seems missing, confirm the writer was relocated via the env
> var, not `config.base_path` alone.

> **⛔ FAIL-LOUD RULE:** When zero captures are found, say exactly `"looked in <root>, found 0"` —
> never report a confident count from a shallower glob, never silently fall back to a different
> path.

Every `events.jsonl` line and every `metadata.json` file contains a `workspace` field. The graph-analyst will pass the active workspace when it delegates to you. **Always scope your search to that workspace.**

### Workspace Scoping — Do This First
Expand All @@ -129,15 +164,23 @@ When a workspace is provided by the caller, apply it immediately before any othe
**Step 1 — Try directory-first lookup** (fast, covers the common case where workspace equals the project slug):

```bash
ls ~/.amplifier/projects/{WORKSPACE}/sessions/ 2>/dev/null
CONTEXT_INTELLIGENCE_ROOT="${AMPLIFIER_CONTEXT_INTELLIGENCE_BASE_PATH:-$HOME/.amplifier/projects}"
ls "$CONTEXT_INTELLIGENCE_ROOT"/{WORKSPACE}/sessions/ 2>/dev/null
```

If this directory exists and contains sessions, work within it exclusively.
> **Guard:** A `sessions/<id>/` directory entry only counts as a context_intelligence capture
> when `sessions/<id>/context-intelligence/` also exists. `ls sessions/` may list directories
> from Amplifier core with no `context-intelligence/` subdir — do not count those as
> context_intelligence sessions.

If this directory exists and contains sessions with `context-intelligence/` subdirs, work within it exclusively.

**Step 2 — If that directory is empty or missing**, the workspace was set explicitly and differs from the project slug. Scan across all project directories and filter by the `workspace` field in `metadata.json`:

```bash
for f in ~/.amplifier/projects/*/sessions/*/context-intelligence/metadata.json; do
CONTEXT_INTELLIGENCE_ROOT="${AMPLIFIER_CONTEXT_INTELLIGENCE_BASE_PATH:-$HOME/.amplifier/projects}"
for ev in "$CONTEXT_INTELLIGENCE_ROOT"/*/sessions/*/context-intelligence/events.jsonl; do
f="${ev%/events.jsonl}/metadata.json" # canonical marker = events.jsonl; fields from sibling
jq -r 'select(.workspace == "{WORKSPACE}") | input_filename' "$f" 2>/dev/null
done
```
Expand All @@ -155,26 +198,35 @@ done
Find sessions by ID, project slug, date, or agent name, always scoped to the provided workspace.

```bash
# Resolve root first (required before any snippet below)
CONTEXT_INTELLIGENCE_ROOT="${AMPLIFIER_CONTEXT_INTELLIGENCE_BASE_PATH:-$HOME/.amplifier/projects}"

# List sessions in a workspace (directory-first path)
for f in ~/.amplifier/projects/my-project/sessions/*/context-intelligence/metadata.json; do
for ev in "$CONTEXT_INTELLIGENCE_ROOT"/my-project/sessions/*/context-intelligence/events.jsonl; do
f="${ev%/events.jsonl}/metadata.json" # canonical marker = events.jsonl; fields from sibling
jq -r '[.session_id, .workspace, .status, .started_at, .agent_name // "(root)"] | join("\t")' "$f" 2>/dev/null
done | sort -t$'\t' -k4

# List sessions scoped by workspace field (cross-project scan)
for f in ~/.amplifier/projects/*/sessions/*/context-intelligence/metadata.json; do
for ev in "$CONTEXT_INTELLIGENCE_ROOT"/*/sessions/*/context-intelligence/events.jsonl; do
f="${ev%/events.jsonl}/metadata.json" # canonical marker = events.jsonl; fields from sibling
jq -r 'select(.workspace == "my-project") | [.session_id, .status, .started_at, .agent_name // "(root)"] | join("\t")' "$f" 2>/dev/null
done | sort -t$'\t' -k3

# Find a session by partial ID (within a workspace)
find ~/.amplifier/projects/my-project/sessions -maxdepth 1 -name "*PARTIAL_ID*" -type d
# NOTE: a sessions/<id>/ directory only counts as a context_intelligence capture when
# sessions/<id>/context-intelligence/ also exists. Always confirm the subdir:
find "$CONTEXT_INTELLIGENCE_ROOT"/my-project/sessions -maxdepth 1 -name "*PARTIAL_ID*" -type d \
| while read -r d; do [ -d "$d/context-intelligence" ] && echo "$d"; done

# Find sessions by agent name within a workspace
for f in ~/.amplifier/projects/my-project/sessions/*/context-intelligence/metadata.json; do
for ev in "$CONTEXT_INTELLIGENCE_ROOT"/my-project/sessions/*/context-intelligence/events.jsonl; do
f="${ev%/events.jsonl}/metadata.json" # canonical marker = events.jsonl; fields from sibling
jq -r 'select(.agent_name == "TARGET_AGENT") | .session_id' "$f" 2>/dev/null
done

# Confirm the workspace of a specific session
jq -r '.workspace' ~/.amplifier/projects/my-project/sessions/SESSION_ID/context-intelligence/metadata.json
jq -r '.workspace' "$CONTEXT_INTELLIGENCE_ROOT"/my-project/sessions/SESSION_ID/context-intelligence/metadata.json
```

### Event Search
Expand Down Expand Up @@ -207,12 +259,16 @@ wc -l < events.jsonl
Trace parent-child chains via `parent_id`, trace delegation trees via `delegate:agent_spawned`/`delegate:agent_completed`.

```bash
# Resolve root first (required before any snippet below)
CONTEXT_INTELLIGENCE_ROOT="${AMPLIFIER_CONTEXT_INTELLIGENCE_BASE_PATH:-$HOME/.amplifier/projects}"

# Check if session is root or child, and confirm its workspace
jq -r '{parent_id, workspace, status}' metadata.json

# Find child sessions within a workspace
PARENT_ID="YOUR_SESSION_ID_HERE"
for f in ~/.amplifier/projects/my-project/sessions/*/context-intelligence/metadata.json; do
for ev in "$CONTEXT_INTELLIGENCE_ROOT"/my-project/sessions/*/context-intelligence/events.jsonl; do
f="${ev%/events.jsonl}/metadata.json" # canonical marker = events.jsonl; fields from sibling
jq -r "select(.parent_id == \"$PARENT_ID\") | [.session_id, .agent_name // \"(root)\", .status, .workspace] | join(\"\t\")" "$f" 2>/dev/null
done

Expand All @@ -237,8 +293,9 @@ Since session-navigator is active when no server is configured, you must locate
2. Or read from bundle config YAML under `hook-context-intelligence.config`: `context_intelligence_server_url` and `context_intelligence_api_key`

```bash
CONTEXT_INTELLIGENCE_ROOT="${AMPLIFIER_CONTEXT_INTELLIGENCE_BASE_PATH:-$HOME/.amplifier/projects}"
context-intelligence-upload \
--path ~/.amplifier/projects/my-project \
--path "$CONTEXT_INTELLIGENCE_ROOT"/my-project \
--server-url "https://your-server.example.com" \
--api-key "your-api-key"
```
Expand Down
25 changes: 24 additions & 1 deletion behaviors/context-intelligence-logging.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,30 @@ hooks:
- delegate:agent_completed
- delegate:agent_cancelled
- delegate:error
# base_path: ~/.amplifier/projects (auto-resolved; uncomment to override)
# base_path — relocation root for ALL captures.
#
# This binding is how relocation reaches the readers too: discover.py, the
# workflow recipe, and the navigation skills resolve their root ONLY from
# the AMPLIFIER_CONTEXT_INTELLIGENCE_BASE_PATH env var. Binding base_path to
# the same var keeps the writer and readers in lock-step.
#
# REQUIRES the host app to expand ${VAR:default} placeholders before mount
# (the hook does not self-expand — same contract as url/api_key above). The
# Amplifier app-cli does this. On a host that does NOT expand the ${VAR:}
# (colon-empty-default) form, the unexpanded literal falls back to the
# default SILENTLY, and the §C.3 consistency check in on_session_ready warns
# LOUD if the var was actually set — so a broken binding is never silent.
#
# ⚠ Do NOT relocate by hard-coding a path here instead of the env var:
# the writer would move but the env-only readers would not follow, and they
# would report "found 0". Always relocate via the env var.
#
# ⚠ Relocation granularity is PER-PROCESS, not per-session: every session in
# one process shares the one env value. A host that needs different roots per
# session must use separate processes. At session start the hook logs (INFO)
# the active capture root whenever relocation is in effect, so you can confirm
# it took effect.
base_path: "${AMPLIFIER_CONTEXT_INTELLIGENCE_BASE_PATH:}"
# project_slug: (auto-resolved from working directory; uncomment to override)
# exclude_events: [] (optional fnmatch patterns; uncomment and list events to suppress)
#
Expand Down
8 changes: 4 additions & 4 deletions bundle.dot
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ digraph context_intelligence {
nodesep=0.6
ranksep=0.7
bgcolor="white"
source_hash="ff1d6a9692415633ceafa3389694c17aa82f8f3d8296b1f7c440c9fb815ea5d2"
source_hash="18e3046d9999d7c21d8a948dbb2f0e83ae35c8b72a73e2e2793227571f9c088c"

node [fontname="Helvetica", fontsize=11, style="filled,rounded"]
edge [fontname="Helvetica", fontsize=9]
Expand All @@ -23,8 +23,8 @@ digraph context_intelligence {

beh_context_intelligence_analysis_behavior [label="context-intelligence-analysis-behavior\n1 tools\n~864 tok", shape=box, fillcolor="#e0f2f1", style="filled,rounded"]
beh_context_intelligence_design_behavior [label="context-intelligence-design-behavior\n1 tools\n~331 tok", shape=box, fillcolor="#e0f2f1", style="filled,rounded"]
beh_context_intelligence_logging_behavior [label="context-intelligence-logging-behavior\n1 tools\n~1184 tok", shape=box, fillcolor="#e0f2f1", style="filled,rounded"]
beh_context_intelligence_navigation_behavior [label="context-intelligence-navigation-behavior\n2 tools\n~561 tok", shape=box, fillcolor="#e0f2f1", style="filled,rounded"]
beh_context_intelligence_logging_behavior [label="context-intelligence-logging-behavior\n1 tools\n~1661 tok", shape=box, fillcolor="#e0f2f1", style="filled,rounded"]
beh_context_intelligence_navigation_behavior [label="context-intelligence-navigation-behavior\n2 tools\n~617 tok", shape=box, fillcolor="#e0f2f1", style="filled,rounded"]
beh_context_intelligence_behavior [label="context-intelligence-behavior\n~236 tok", shape=box, fillcolor="#e0f2f1", style="filled,rounded"]
}

Expand All @@ -37,7 +37,7 @@ digraph context_intelligence {
agt_context_intelligence_design_facilitator [label="context-intelligence-design-facilitator\n~187 tok desc", shape=box, fillcolor="#c8e6c9", style="filled,rounded"]
agt_context_intelligence_tool_designer [label="context-intelligence-tool-designer\n~198 tok desc", shape=box, fillcolor="#c8e6c9", style="filled,rounded"]
agt_graph_analyst [label="graph-analyst\n~543 tok desc", shape=box, fillcolor="#c8e6c9", style="filled,rounded"]
agt_session_navigator [label="session-navigator\n~366 tok desc", shape=box, fillcolor="#c8e6c9", style="filled,rounded"]
agt_session_navigator [label="session-navigator\n~422 tok desc", shape=box, fillcolor="#c8e6c9", style="filled,rounded"]
}

subgraph cluster_modules {
Expand Down
Binary file modified bundle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions context_intelligence/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
resolve_config,
)
from context_intelligence.reconstruct import (
DiskScanResult,
build_disk_only_metadata,
discover_sessions,
extract_events,
Expand All @@ -45,6 +46,7 @@
"extract_transcript",
"extract_metadata",
"build_disk_only_metadata",
"DiskScanResult",
"discover_sessions",
"workspace_slug",
"sessions_dir_for_project",
Expand Down
Loading
Loading