pi-para 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +0 -9
- package/CHANGELOG.md +13 -0
- package/CONFIGURATION.md +0 -23
- package/README.md +1 -29
- package/SETUP.md +1 -1
- package/STRUCTURE.md +0 -9
- package/dist/atomic-write.d.ts +2 -0
- package/dist/atomic-write.d.ts.map +1 -0
- package/dist/atomic-write.js +18 -0
- package/dist/atomic-write.js.map +1 -0
- package/dist/capture.d.ts +34 -0
- package/dist/capture.d.ts.map +1 -0
- package/dist/capture.js +461 -0
- package/dist/capture.js.map +1 -0
- package/dist/cli-legacy.d.ts +4 -0
- package/dist/cli-legacy.d.ts.map +1 -0
- package/dist/cli-legacy.js +41 -0
- package/dist/cli-legacy.js.map +1 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +427 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands.d.ts +15 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +719 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +94 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +228 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +63 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +311 -0
- package/dist/context.js.map +1 -0
- package/dist/credentials.d.ts +27 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +85 -0
- package/dist/credentials.js.map +1 -0
- package/dist/daemon.d.ts +49 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +226 -0
- package/dist/daemon.js.map +1 -0
- package/dist/doctor.d.ts +20 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +176 -0
- package/dist/doctor.js.map +1 -0
- package/dist/frontmatter.d.ts +59 -0
- package/dist/frontmatter.d.ts.map +1 -0
- package/dist/frontmatter.js +170 -0
- package/dist/frontmatter.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +342 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest.d.ts +88 -0
- package/dist/ingest.d.ts.map +1 -0
- package/dist/ingest.js +105 -0
- package/dist/ingest.js.map +1 -0
- package/dist/link-discovery.d.ts +40 -0
- package/dist/link-discovery.d.ts.map +1 -0
- package/dist/link-discovery.js +122 -0
- package/dist/link-discovery.js.map +1 -0
- package/dist/link-utils.d.ts +30 -0
- package/dist/link-utils.d.ts.map +1 -0
- package/dist/link-utils.js +145 -0
- package/dist/link-utils.js.map +1 -0
- package/dist/lint.d.ts +39 -0
- package/dist/lint.d.ts.map +1 -0
- package/dist/lint.js +827 -0
- package/dist/lint.js.map +1 -0
- package/dist/maintainer.d.ts +24 -0
- package/dist/maintainer.d.ts.map +1 -0
- package/dist/maintainer.js +358 -0
- package/dist/maintainer.js.map +1 -0
- package/dist/model-resolver.d.ts +30 -0
- package/dist/model-resolver.d.ts.map +1 -0
- package/dist/model-resolver.js +71 -0
- package/dist/model-resolver.js.map +1 -0
- package/dist/paths.d.ts +20 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +33 -0
- package/dist/paths.js.map +1 -0
- package/dist/processor.d.ts +14 -0
- package/dist/processor.d.ts.map +1 -0
- package/dist/processor.js +361 -0
- package/dist/processor.js.map +1 -0
- package/dist/qmd-providers.d.ts +12 -0
- package/dist/qmd-providers.d.ts.map +1 -0
- package/dist/qmd-providers.js +45 -0
- package/dist/qmd-providers.js.map +1 -0
- package/dist/query.d.ts +69 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +120 -0
- package/dist/query.js.map +1 -0
- package/dist/raw.d.ts +57 -0
- package/dist/raw.d.ts.map +1 -0
- package/dist/raw.js +219 -0
- package/dist/raw.js.map +1 -0
- package/dist/redact.d.ts +21 -0
- package/dist/redact.d.ts.map +1 -0
- package/dist/redact.js +143 -0
- package/dist/redact.js.map +1 -0
- package/dist/repair.d.ts +11 -0
- package/dist/repair.d.ts.map +1 -0
- package/dist/repair.js +41 -0
- package/dist/repair.js.map +1 -0
- package/dist/scheduler/controls.d.ts +35 -0
- package/dist/scheduler/controls.d.ts.map +1 -0
- package/dist/scheduler/controls.js +98 -0
- package/dist/scheduler/controls.js.map +1 -0
- package/dist/scheduler/index.d.ts +48 -0
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/scheduler/index.js +193 -0
- package/dist/scheduler/index.js.map +1 -0
- package/dist/scheduler/leases.d.ts +22 -0
- package/dist/scheduler/leases.d.ts.map +1 -0
- package/dist/scheduler/leases.js +74 -0
- package/dist/scheduler/leases.js.map +1 -0
- package/dist/scheduler/session-capture.d.ts +33 -0
- package/dist/scheduler/session-capture.d.ts.map +1 -0
- package/dist/scheduler/session-capture.js +108 -0
- package/dist/scheduler/session-capture.js.map +1 -0
- package/dist/scheduler/state.d.ts +56 -0
- package/dist/scheduler/state.d.ts.map +1 -0
- package/dist/scheduler/state.js +210 -0
- package/dist/scheduler/state.js.map +1 -0
- package/dist/scope.d.ts +54 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/scope.js +161 -0
- package/dist/scope.js.map +1 -0
- package/dist/scripts/bulk-link-fix.d.ts +9 -0
- package/dist/scripts/bulk-link-fix.d.ts.map +1 -0
- package/dist/scripts/bulk-link-fix.js +106 -0
- package/dist/scripts/bulk-link-fix.js.map +1 -0
- package/dist/session-tools.d.ts +33 -0
- package/dist/session-tools.d.ts.map +1 -0
- package/dist/session-tools.js +211 -0
- package/dist/session-tools.js.map +1 -0
- package/dist/settings.d.ts +19 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +106 -0
- package/dist/settings.js.map +1 -0
- package/dist/setup.d.ts +29 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +90 -0
- package/dist/setup.js.map +1 -0
- package/dist/state.d.ts +54 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +176 -0
- package/dist/state.js.map +1 -0
- package/dist/status.d.ts +22 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +93 -0
- package/dist/status.js.map +1 -0
- package/dist/store.d.ts +71 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +282 -0
- package/dist/store.js.map +1 -0
- package/dist/summarize.d.ts +60 -0
- package/dist/summarize.d.ts.map +1 -0
- package/dist/summarize.js +266 -0
- package/dist/summarize.js.map +1 -0
- package/dist/tag-registry.d.ts +56 -0
- package/dist/tag-registry.d.ts.map +1 -0
- package/dist/tag-registry.js +146 -0
- package/dist/tag-registry.js.map +1 -0
- package/dist/templates/prompts.d.ts +51 -0
- package/dist/templates/prompts.d.ts.map +1 -0
- package/dist/templates/prompts.js +203 -0
- package/dist/templates/prompts.js.map +1 -0
- package/dist/tools.d.ts +26 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +1198 -0
- package/dist/tools.js.map +1 -0
- package/dist/watcher.d.ts +25 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +84 -0
- package/dist/watcher.js.map +1 -0
- package/dist/webui/client/dist/index.html +1885 -0
- package/dist/webui/server.d.ts +31 -0
- package/dist/webui/server.d.ts.map +1 -0
- package/dist/webui/server.js +566 -0
- package/dist/webui/server.js.map +1 -0
- package/dist/wiki-tool-guidance.d.ts +6 -0
- package/dist/wiki-tool-guidance.d.ts.map +1 -0
- package/dist/wiki-tool-guidance.js +70 -0
- package/dist/wiki-tool-guidance.js.map +1 -0
- package/dist/wiki.d.ts +108 -0
- package/dist/wiki.d.ts.map +1 -0
- package/dist/wiki.js +497 -0
- package/dist/wiki.js.map +1 -0
- package/package.json +2 -1
- package/scripts/build-assets.mjs +1 -13
- package/skills/setup/SKILL.md +1 -1
- package/src/cli.ts +0 -70
- package/src/config.ts +0 -25
- package/src/index.ts +1 -38
- package/src/templates/prompts.ts +0 -56
- package/scripts/gepa/dataset.py +0 -192
- package/scripts/gepa/lm_providers.py +0 -472
- package/scripts/gepa/metric.py +0 -279
- package/scripts/gepa/optimize.py +0 -367
- package/scripts/gepa/program.py +0 -99
- package/scripts/gepa/pyproject.toml +0 -19
- package/scripts/gepa/run-all.sh +0 -53
- package/scripts/gepa/wiki_reader.py +0 -113
- package/src/gepa/index.ts +0 -287
- package/src/gepa/optimized/capture-prompt.txt +0 -65
package/scripts/gepa/program.py
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
"""DSPy Module proxy for prompt optimization.
|
|
2
|
-
|
|
3
|
-
Each optimization target is wrapped in a lightweight proxy module.
|
|
4
|
-
The Signature's docstring (instruction) IS the prompt being optimized.
|
|
5
|
-
GEPA evolves the instruction to maximize the metric.
|
|
6
|
-
|
|
7
|
-
One LLM call per eval (not 10-50 like the full agent loop).
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
from __future__ import annotations
|
|
11
|
-
|
|
12
|
-
import dspy
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class WikiTaskOutput(dspy.Signature):
|
|
16
|
-
"""You are a wiki knowledge management agent for a PARA-structured knowledge base."""
|
|
17
|
-
|
|
18
|
-
task_context: str = dspy.InputField(
|
|
19
|
-
desc="Context describing the wiki task to perform"
|
|
20
|
-
)
|
|
21
|
-
output: str = dspy.OutputField(
|
|
22
|
-
desc="Structured wiki output following PARA conventions: "
|
|
23
|
-
"Topic, Key Facts, Insights, Connections with [[wikilinks]], Sources"
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class PromptProxy(dspy.Module):
|
|
28
|
-
"""Lightweight proxy for prompt/instruction optimization.
|
|
29
|
-
|
|
30
|
-
The Signature's instruction text is what GEPA evolves.
|
|
31
|
-
forward() runs a single dspy.Predict call (1 LLM call).
|
|
32
|
-
|
|
33
|
-
Usage:
|
|
34
|
-
proxy = PromptProxy.from_instruction(current_prompt_text)
|
|
35
|
-
# GEPA optimizes proxy → evolved instruction = optimized prompt
|
|
36
|
-
"""
|
|
37
|
-
|
|
38
|
-
def __init__(self, instruction: str | None = None):
|
|
39
|
-
super().__init__()
|
|
40
|
-
|
|
41
|
-
if instruction:
|
|
42
|
-
# Create a signature class with the instruction as docstring
|
|
43
|
-
class CustomSig(WikiTaskOutput):
|
|
44
|
-
pass
|
|
45
|
-
CustomSig.__doc__ = instruction
|
|
46
|
-
self.predict = dspy.Predict(CustomSig)
|
|
47
|
-
else:
|
|
48
|
-
self.predict = dspy.Predict(WikiTaskOutput)
|
|
49
|
-
|
|
50
|
-
def forward(self, task_context: str) -> dspy.Prediction:
|
|
51
|
-
return self.predict(task_context=task_context)
|
|
52
|
-
|
|
53
|
-
@classmethod
|
|
54
|
-
def from_instruction(cls, instruction: str) -> "PromptProxy":
|
|
55
|
-
"""Create a proxy from a prompt/instruction string."""
|
|
56
|
-
return cls(instruction=instruction)
|
|
57
|
-
|
|
58
|
-
def get_instruction(self) -> str | None:
|
|
59
|
-
"""Get the current instruction text."""
|
|
60
|
-
sig = getattr(self.predict, "signature", None)
|
|
61
|
-
if sig:
|
|
62
|
-
inst = getattr(sig, "instructions", None)
|
|
63
|
-
if inst and not inst.startswith("str(object"):
|
|
64
|
-
return inst
|
|
65
|
-
doc = getattr(sig, "__doc__", None)
|
|
66
|
-
if doc and not doc.startswith("str(object"):
|
|
67
|
-
return doc
|
|
68
|
-
return None
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def extract_evolved_instruction(optimized_proxy: PromptProxy) -> str | None:
|
|
72
|
-
"""Extract the GEPA-evolved instruction from an optimized proxy.
|
|
73
|
-
|
|
74
|
-
After GEPA optimization, the proxy's Predict signature contains
|
|
75
|
-
the evolved instruction text. This extracts it.
|
|
76
|
-
"""
|
|
77
|
-
# Check predict.signature first
|
|
78
|
-
if hasattr(optimized_proxy, "predict"):
|
|
79
|
-
pred = optimized_proxy.predict
|
|
80
|
-
for attr_name in ("extended_signature", "signature"):
|
|
81
|
-
sig = getattr(pred, attr_name, None)
|
|
82
|
-
if sig:
|
|
83
|
-
for field in ("instructions", "__doc__"):
|
|
84
|
-
text = getattr(sig, field, None)
|
|
85
|
-
if text and len(text) > 50 and not text.startswith("str(object"):
|
|
86
|
-
return text
|
|
87
|
-
|
|
88
|
-
# Fallback to named_predictors
|
|
89
|
-
try:
|
|
90
|
-
for name, predictor in optimized_proxy.named_predictors():
|
|
91
|
-
sig = getattr(predictor, "signature", None)
|
|
92
|
-
if sig:
|
|
93
|
-
inst = getattr(sig, "instructions", None) or getattr(sig, "__doc__", None)
|
|
94
|
-
if inst and len(inst) > 50 and not inst.startswith("str(object"):
|
|
95
|
-
return inst
|
|
96
|
-
except Exception:
|
|
97
|
-
pass
|
|
98
|
-
|
|
99
|
-
return None
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "pi-para-gepa"
|
|
3
|
-
version = "0.1.0"
|
|
4
|
-
description = "GEPA optimizer for pi-para prompts, tools, and skills"
|
|
5
|
-
requires-python = ">=3.11"
|
|
6
|
-
dependencies = [
|
|
7
|
-
"dspy>=2.6.0",
|
|
8
|
-
"anthropic>=0.52.0",
|
|
9
|
-
"openai>=1.30.0",
|
|
10
|
-
"pyyaml>=6.0.0",
|
|
11
|
-
]
|
|
12
|
-
|
|
13
|
-
[tool.hatch.build.targets.wheel]
|
|
14
|
-
packages = ["lm_providers", "wiki_reader", "dataset", "program", "metric", "optimize"]
|
|
15
|
-
include = ["*.py"]
|
|
16
|
-
|
|
17
|
-
[build-system]
|
|
18
|
-
requires = ["hatchling"]
|
|
19
|
-
build-backend = "hatchling.build"
|
package/scripts/gepa/run-all.sh
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Run GEPA optimization one target at a time.
|
|
3
|
-
# Default: Sonnet for task/judge (fast, cheap), Opus for reflection (smart mutations).
|
|
4
|
-
# Each target invocation re-reads auth.json for fresh token.
|
|
5
|
-
set -e
|
|
6
|
-
|
|
7
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
|
-
PROJECT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
9
|
-
MODEL="${1:-anthropic/claude-sonnet-4-20250514}"
|
|
10
|
-
REFLECTION_MODEL="${2:-anthropic/claude-opus-4-6}"
|
|
11
|
-
AUTO="${3:-light}"
|
|
12
|
-
|
|
13
|
-
echo "[gepa-all] Model: $MODEL | Reflection: $REFLECTION_MODEL | Budget: $AUTO"
|
|
14
|
-
echo "[gepa-all] Started at $(date)"
|
|
15
|
-
|
|
16
|
-
# Get target list
|
|
17
|
-
TARGETS=$(cd "$PROJECT_DIR" && node dist/cli.js gepa targets 2>/dev/null | awk '{print $1}')
|
|
18
|
-
|
|
19
|
-
TOTAL=$(echo "$TARGETS" | wc -l)
|
|
20
|
-
CURRENT=0
|
|
21
|
-
SUCCEEDED=0
|
|
22
|
-
FAILED=0
|
|
23
|
-
|
|
24
|
-
for TARGET in $TARGETS; do
|
|
25
|
-
CURRENT=$((CURRENT + 1))
|
|
26
|
-
echo ""
|
|
27
|
-
echo "================================================================"
|
|
28
|
-
echo "[gepa-all] [$CURRENT/$TOTAL] Optimizing: $TARGET"
|
|
29
|
-
echo "[gepa-all] $(date)"
|
|
30
|
-
echo "================================================================"
|
|
31
|
-
|
|
32
|
-
cd "$PROJECT_DIR"
|
|
33
|
-
if node dist/cli.js gepa optimize \
|
|
34
|
-
--target "$TARGET" \
|
|
35
|
-
--model "$MODEL" \
|
|
36
|
-
--reflection-model "$REFLECTION_MODEL" \
|
|
37
|
-
--auto "$AUTO" 2>&1; then
|
|
38
|
-
SUCCEEDED=$((SUCCEEDED + 1))
|
|
39
|
-
echo "[gepa-all] ✅ $TARGET done"
|
|
40
|
-
else
|
|
41
|
-
FAILED=$((FAILED + 1))
|
|
42
|
-
echo "[gepa-all] ❌ $TARGET failed"
|
|
43
|
-
fi
|
|
44
|
-
|
|
45
|
-
# Brief pause between targets to avoid rate limits
|
|
46
|
-
sleep 5
|
|
47
|
-
done
|
|
48
|
-
|
|
49
|
-
echo ""
|
|
50
|
-
echo "================================================================"
|
|
51
|
-
echo "[gepa-all] ALL DONE at $(date)"
|
|
52
|
-
echo "[gepa-all] Succeeded: $SUCCEEDED / $TOTAL, Failed: $FAILED"
|
|
53
|
-
echo "================================================================"
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
"""Read wiki pages from disk for training data.
|
|
2
|
-
|
|
3
|
-
Parses YAML frontmatter + markdown body from ~/.pi/wiki/{category}/{slug}.md.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from __future__ import annotations
|
|
7
|
-
|
|
8
|
-
import os
|
|
9
|
-
import re
|
|
10
|
-
from dataclasses import dataclass, field
|
|
11
|
-
from pathlib import Path
|
|
12
|
-
|
|
13
|
-
import yaml
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
PARA_CATEGORIES = ("projects", "areas", "resources", "archives")
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@dataclass
|
|
20
|
-
class WikiPage:
|
|
21
|
-
"""A parsed wiki page."""
|
|
22
|
-
category: str
|
|
23
|
-
slug: str
|
|
24
|
-
title: str
|
|
25
|
-
scope: list[str] = field(default_factory=list)
|
|
26
|
-
tags: list[str] = field(default_factory=list)
|
|
27
|
-
links: list[str] = field(default_factory=list)
|
|
28
|
-
body: str = ""
|
|
29
|
-
created: str = ""
|
|
30
|
-
updated: str = ""
|
|
31
|
-
|
|
32
|
-
@property
|
|
33
|
-
def path(self) -> str:
|
|
34
|
-
return f"{self.category}/{self.slug}"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def parse_page(filepath: str) -> WikiPage | None:
|
|
38
|
-
"""Parse a wiki markdown file into a WikiPage."""
|
|
39
|
-
try:
|
|
40
|
-
with open(filepath, "r", encoding="utf-8") as f:
|
|
41
|
-
content = f.read()
|
|
42
|
-
except (IOError, OSError):
|
|
43
|
-
return None
|
|
44
|
-
|
|
45
|
-
# Split frontmatter from body
|
|
46
|
-
fm_match = re.match(r"^---\s*\n(.*?\n)---\s*\n(.*)", content, re.DOTALL)
|
|
47
|
-
if not fm_match:
|
|
48
|
-
return None
|
|
49
|
-
|
|
50
|
-
try:
|
|
51
|
-
fm = yaml.safe_load(fm_match.group(1))
|
|
52
|
-
except yaml.YAMLError:
|
|
53
|
-
return None
|
|
54
|
-
|
|
55
|
-
if not isinstance(fm, dict):
|
|
56
|
-
return None
|
|
57
|
-
|
|
58
|
-
body = fm_match.group(2).strip()
|
|
59
|
-
path = Path(filepath)
|
|
60
|
-
slug = path.stem
|
|
61
|
-
category = path.parent.name
|
|
62
|
-
|
|
63
|
-
return WikiPage(
|
|
64
|
-
category=category,
|
|
65
|
-
slug=slug,
|
|
66
|
-
title=fm.get("title", slug),
|
|
67
|
-
scope=fm.get("scope", []) or [],
|
|
68
|
-
tags=fm.get("tags", []) or [],
|
|
69
|
-
links=fm.get("links", []) or [],
|
|
70
|
-
body=body,
|
|
71
|
-
created=str(fm.get("created", "")),
|
|
72
|
-
updated=str(fm.get("updated", "")),
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def load_all_pages(wiki_dir: str) -> list[WikiPage]:
|
|
77
|
-
"""Load all wiki pages from disk."""
|
|
78
|
-
pages = []
|
|
79
|
-
for cat in PARA_CATEGORIES:
|
|
80
|
-
cat_dir = os.path.join(wiki_dir, cat)
|
|
81
|
-
if not os.path.isdir(cat_dir):
|
|
82
|
-
continue
|
|
83
|
-
for fname in sorted(os.listdir(cat_dir)):
|
|
84
|
-
if not fname.endswith(".md"):
|
|
85
|
-
continue
|
|
86
|
-
page = parse_page(os.path.join(cat_dir, fname))
|
|
87
|
-
if page:
|
|
88
|
-
pages.append(page)
|
|
89
|
-
return pages
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def get_wiki_stats(wiki_dir: str) -> dict:
|
|
93
|
-
"""Get summary stats about the wiki."""
|
|
94
|
-
pages = load_all_pages(wiki_dir)
|
|
95
|
-
cat_counts = {}
|
|
96
|
-
all_scopes = set()
|
|
97
|
-
all_tags = set()
|
|
98
|
-
total_links = 0
|
|
99
|
-
|
|
100
|
-
for p in pages:
|
|
101
|
-
cat_counts[p.category] = cat_counts.get(p.category, 0) + 1
|
|
102
|
-
all_scopes.update(p.scope)
|
|
103
|
-
all_tags.update(p.tags)
|
|
104
|
-
total_links += len(p.links)
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
"total_pages": len(pages),
|
|
108
|
-
"categories": cat_counts,
|
|
109
|
-
"unique_scopes": sorted(all_scopes),
|
|
110
|
-
"unique_tags": sorted(all_tags)[:20], # top 20
|
|
111
|
-
"total_links": total_links,
|
|
112
|
-
"avg_links_per_page": round(total_links / max(len(pages), 1), 1),
|
|
113
|
-
}
|
package/src/gepa/index.ts
DELETED
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GEPA optimizer orchestrator — TypeScript side.
|
|
3
|
-
*
|
|
4
|
-
* 1. Extracts all prompt/tool/skill instruction texts from source
|
|
5
|
-
* 2. Writes targets.json for the Python DSPy GEPA script
|
|
6
|
-
* 3. Reads auth.json → passes API keys as env vars to uv subprocess
|
|
7
|
-
* 4. Calls `uv run scripts/gepa/optimize.py`
|
|
8
|
-
* 5. Reads results.json → saves optimized prompts to disk
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { spawn } from "node:child_process";
|
|
12
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from "node:fs";
|
|
13
|
-
import { homedir } from "node:os";
|
|
14
|
-
import { dirname, join, resolve } from "node:path";
|
|
15
|
-
|
|
16
|
-
// -- Types -------------------------------------------------------------------
|
|
17
|
-
|
|
18
|
-
interface GEPATarget {
|
|
19
|
-
name: string;
|
|
20
|
-
type: "prompt" | "tool" | "skill";
|
|
21
|
-
content: string;
|
|
22
|
-
sourceFile: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface GEPAResult {
|
|
26
|
-
target: string;
|
|
27
|
-
status: "success" | "error";
|
|
28
|
-
baseline_score?: number;
|
|
29
|
-
optimized_score?: number;
|
|
30
|
-
improvement_pct?: number;
|
|
31
|
-
optimized_instruction?: string;
|
|
32
|
-
original_instruction?: string;
|
|
33
|
-
model?: string;
|
|
34
|
-
auto?: string;
|
|
35
|
-
error?: string;
|
|
36
|
-
timestamp?: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
interface GEPAOutput {
|
|
40
|
-
results: GEPAResult[];
|
|
41
|
-
model: string;
|
|
42
|
-
auto: string;
|
|
43
|
-
wiki_dir: string;
|
|
44
|
-
timestamp: string;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface GEPAOptions {
|
|
48
|
-
target?: string;
|
|
49
|
-
/** Student model — runs the proxy (fast, cheap). */
|
|
50
|
-
studentModel?: string;
|
|
51
|
-
/** Teacher/reflection model — proposes mutations (smart, creative). */
|
|
52
|
-
teacherModel?: string;
|
|
53
|
-
/** Judge model — scores outputs (fast, cheap). Default: same as student. */
|
|
54
|
-
judgeModel?: string;
|
|
55
|
-
/** Shorthand: sets studentModel (backward compat with --model flag) */
|
|
56
|
-
model?: string;
|
|
57
|
-
/** Shorthand: sets teacherModel (backward compat with --reflection-model flag) */
|
|
58
|
-
reflectionModel?: string;
|
|
59
|
-
auto?: "light" | "medium" | "heavy";
|
|
60
|
-
maxMetricCalls?: number;
|
|
61
|
-
threads?: number;
|
|
62
|
-
seed?: number;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// -- Paths -------------------------------------------------------------------
|
|
66
|
-
|
|
67
|
-
const WIKI_DIR = join(homedir(), ".pi", "wiki");
|
|
68
|
-
const GEPA_DIR = join(WIKI_DIR, "gepa");
|
|
69
|
-
const INPUT_DIR = join(GEPA_DIR, "input");
|
|
70
|
-
const OUTPUT_DIR = join(GEPA_DIR, "output");
|
|
71
|
-
const OPTIMIZED_DIR = join(GEPA_DIR, "optimized");
|
|
72
|
-
const HISTORY_DIR = join(GEPA_DIR, "history");
|
|
73
|
-
|
|
74
|
-
// -- Package root ------------------------------------------------------------
|
|
75
|
-
|
|
76
|
-
function findPackageRoot(): string {
|
|
77
|
-
let dir = dirname(new URL(import.meta.url).pathname);
|
|
78
|
-
for (let i = 0; i < 5; i++) {
|
|
79
|
-
const pkgPath = join(dir, "package.json");
|
|
80
|
-
if (existsSync(pkgPath)) {
|
|
81
|
-
try {
|
|
82
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
83
|
-
if (pkg.name === "pi-para" || pkg.name === "@picassio/pi-para") return dir;
|
|
84
|
-
} catch {}
|
|
85
|
-
}
|
|
86
|
-
dir = dirname(dir);
|
|
87
|
-
}
|
|
88
|
-
return resolve(dirname(new URL(import.meta.url).pathname), "../..");
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// -- Helpers: extract strings from TS source ---------------------------------
|
|
92
|
-
|
|
93
|
-
function extractTSConst(source: string, name: string): string | null {
|
|
94
|
-
const m = source.match(new RegExp(`(?:export\\s+)?const\\s+${name}\\s*=\\s*\`([\\s\\S]*?)\`;`));
|
|
95
|
-
return m ? m[1].trim() : null;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function extractToolInstructions(source: string): Record<string, string> {
|
|
99
|
-
const tools: Record<string, string> = {};
|
|
100
|
-
const blocks = [...source.matchAll(
|
|
101
|
-
/registerTool\(\{[\s\S]*?name:\s*"(wiki_\w+)"[\s\S]*?(?=registerTool\(\{|$)/g,
|
|
102
|
-
)];
|
|
103
|
-
for (const block of blocks) {
|
|
104
|
-
const name = block[1].replace("wiki_", "wiki-");
|
|
105
|
-
const text = block[0];
|
|
106
|
-
const descStrs = [...(text.match(/description:\s*\n?\s*([\s\S]*?)(?:promptSnippet:|promptGuidelines:|parameters:)/)?.[1] ?? "").matchAll(/["'`]([^"'`]+)["'`]/g)];
|
|
107
|
-
const desc = descStrs.map(s => s[1]).join(" ");
|
|
108
|
-
const snippet = text.match(/promptSnippet:\s*["'`]([^"'`]+)["'`]/)?.[1] ?? "";
|
|
109
|
-
const guideMatch = text.match(/promptGuidelines:\s*\[([\s\S]*?)\]/);
|
|
110
|
-
const guides = guideMatch ? [...guideMatch[1].matchAll(/["'`]([^"'`]+)["'`]/g)].map(s => s[1]).join("\n") : "";
|
|
111
|
-
const combined = [`Tool: ${name}`, desc && `Description: ${desc.trim()}`, snippet && `Snippet: ${snippet}`, guides && `Guidelines:\n${guides}`].filter(Boolean).join("\n");
|
|
112
|
-
if (combined.length > 50) tools[name] = combined;
|
|
113
|
-
}
|
|
114
|
-
return tools;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function toKebab(name: string): string {
|
|
118
|
-
return name.replace(/_/g, "-").replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// -- Target extraction -------------------------------------------------------
|
|
122
|
-
|
|
123
|
-
export function extractTargets(packageRoot?: string): GEPATarget[] {
|
|
124
|
-
const root = packageRoot ?? findPackageRoot();
|
|
125
|
-
const targets: GEPATarget[] = [];
|
|
126
|
-
|
|
127
|
-
// Prompt templates
|
|
128
|
-
const promptsFile = join(root, "src", "templates", "prompts.ts");
|
|
129
|
-
if (existsSync(promptsFile)) {
|
|
130
|
-
const src = readFileSync(promptsFile, "utf-8");
|
|
131
|
-
for (const n of ["WIKI_SYSTEM_PROMPT","INGEST_PROMPT","QUERY_PROMPT","CAPTURE_SYSTEM_PROMPT","CAPTURE_PROMPT","EXPLICIT_CAPTURE_PROMPT","SUMMARIZE_SYSTEM_PROMPT","ITERATIVE_UPDATE_PROMPT","OVERVIEW_PROMPT","LINT_PROMPT"]) {
|
|
132
|
-
const c = extractTSConst(src, n);
|
|
133
|
-
if (c) targets.push({ name: toKebab(n), type: "prompt", content: c, sourceFile: promptsFile });
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Maintainer + processor prompts
|
|
138
|
-
for (const [file, constName, targetName] of [
|
|
139
|
-
["src/maintainer.ts", "MAINTENANCE_SYSTEM_PROMPT", "maintenance-system-prompt"],
|
|
140
|
-
["src/processor.ts", "CAPTURE_SYSTEM_PROMPT", "processor-capture-prompt"],
|
|
141
|
-
] as const) {
|
|
142
|
-
const p = join(root, file);
|
|
143
|
-
if (existsSync(p)) {
|
|
144
|
-
const c = extractTSConst(readFileSync(p, "utf-8"), constName);
|
|
145
|
-
if (c) targets.push({ name: targetName, type: "prompt", content: c, sourceFile: p });
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Tool instructions
|
|
150
|
-
const toolsFile = join(root, "src", "tools.ts");
|
|
151
|
-
if (existsSync(toolsFile)) {
|
|
152
|
-
for (const [n, c] of Object.entries(extractToolInstructions(readFileSync(toolsFile, "utf-8")))) {
|
|
153
|
-
targets.push({ name: `tool-${n}`, type: "tool", content: c, sourceFile: toolsFile });
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Skills
|
|
158
|
-
for (const s of ["para", "setup"]) {
|
|
159
|
-
const p = join(root, "skills", s, "SKILL.md");
|
|
160
|
-
if (existsSync(p)) {
|
|
161
|
-
const raw = readFileSync(p, "utf-8");
|
|
162
|
-
const body = raw.match(/^---\s*\n[\s\S]*?\n---\s*\n([\s\S]*)$/)?.[1]?.trim() ?? raw;
|
|
163
|
-
targets.push({ name: `skill-${s}`, type: "skill", content: body, sourceFile: p });
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return targets;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// -- Auth env vars -----------------------------------------------------------
|
|
171
|
-
|
|
172
|
-
function getAuthEnvVars(): Record<string, string> {
|
|
173
|
-
const env: Record<string, string> = {};
|
|
174
|
-
try {
|
|
175
|
-
const auth = JSON.parse(readFileSync(join(homedir(), ".pi", "agent", "auth.json"), "utf-8"));
|
|
176
|
-
if (auth.anthropic?.access) { env.ANTHROPIC_OAUTH_TOKEN = auth.anthropic.access; env.ANTHROPIC_API_KEY = auth.anthropic.access; }
|
|
177
|
-
else if (auth.anthropic?.key) env.ANTHROPIC_API_KEY = auth.anthropic.key;
|
|
178
|
-
if (auth.minimax?.key) env.MINIMAX_API_KEY = auth.minimax.key;
|
|
179
|
-
if (auth.openrouter?.key) env.OPENROUTER_API_KEY = auth.openrouter.key;
|
|
180
|
-
else if (auth.openrouter?.openrouter_key) env.OPENROUTER_API_KEY = auth.openrouter.openrouter_key;
|
|
181
|
-
} catch {
|
|
182
|
-
try {
|
|
183
|
-
const raw = readFileSync(join(homedir(), ".config", "qmd", "index.yml"), "utf-8");
|
|
184
|
-
const m = raw.match(/chat:[\s\S]*?key:\s*(.+)/);
|
|
185
|
-
if (m) { const k = m[1].trim(); if (raw.includes("minimaxi.com")) env.MINIMAX_CN_API_KEY = k; else env.MINIMAX_API_KEY = k; }
|
|
186
|
-
} catch {}
|
|
187
|
-
}
|
|
188
|
-
return env;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// -- Main orchestrator -------------------------------------------------------
|
|
192
|
-
|
|
193
|
-
export async function runGEPA(options: GEPAOptions = {}): Promise<GEPAOutput> {
|
|
194
|
-
const root = findPackageRoot();
|
|
195
|
-
const scriptsDir = join(root, "scripts", "gepa");
|
|
196
|
-
for (const d of [INPUT_DIR, OUTPUT_DIR, OPTIMIZED_DIR, HISTORY_DIR]) mkdirSync(d, { recursive: true });
|
|
197
|
-
|
|
198
|
-
// Load GEPA config from config.json (user-configurable defaults)
|
|
199
|
-
let gepaConfig: Record<string, unknown> = {};
|
|
200
|
-
try {
|
|
201
|
-
const cfg = JSON.parse(readFileSync(join(WIKI_DIR, "config.json"), "utf-8"));
|
|
202
|
-
gepaConfig = cfg.gepa ?? {};
|
|
203
|
-
} catch {}
|
|
204
|
-
|
|
205
|
-
// Resolution: CLI flag → options → config.json → default
|
|
206
|
-
const studentModel = options.studentModel ?? options.model
|
|
207
|
-
?? (gepaConfig.studentModel as string) ?? "anthropic/claude-sonnet-4-20250514";
|
|
208
|
-
const teacherModel = options.teacherModel ?? options.reflectionModel
|
|
209
|
-
?? (gepaConfig.teacherModel as string) ?? "anthropic/claude-opus-4-6";
|
|
210
|
-
const judgeModel = options.judgeModel
|
|
211
|
-
?? (gepaConfig.judgeModel as string) ?? studentModel;
|
|
212
|
-
const auto = options.auto ?? (gepaConfig.auto as "light" | "medium" | "heavy") ?? "light";
|
|
213
|
-
const threads = options.threads ?? (gepaConfig.threads as number) ?? 2;
|
|
214
|
-
const seed = options.seed ?? (gepaConfig.seed as number) ?? 42;
|
|
215
|
-
|
|
216
|
-
const allTargets = extractTargets(root);
|
|
217
|
-
let targets = options.target ? allTargets.filter(t => t.name === options.target) : allTargets;
|
|
218
|
-
if (options.target && targets.length === 0) throw new Error(`Target '${options.target}' not found. Available: ${allTargets.map(t => t.name).join(", ")}`);
|
|
219
|
-
|
|
220
|
-
console.log(`[gepa] Found ${targets.length} target(s) to optimize`);
|
|
221
|
-
console.log(`[gepa] Student: ${studentModel}, Teacher: ${teacherModel}, Judge: ${judgeModel}, Budget: ${auto}`);
|
|
222
|
-
|
|
223
|
-
const targetsFile = join(INPUT_DIR, "targets.json");
|
|
224
|
-
writeFileSync(targetsFile, JSON.stringify({ targets: targets.map(t => ({ name: t.name, type: t.type, content: t.content })) }, null, 2));
|
|
225
|
-
|
|
226
|
-
const outputFile = join(OUTPUT_DIR, "results.json");
|
|
227
|
-
const uvArgs = [
|
|
228
|
-
"run", "--project", scriptsDir, join(scriptsDir, "optimize.py"),
|
|
229
|
-
"--targets-file", targetsFile, "--wiki-dir", WIKI_DIR, "--output", outputFile,
|
|
230
|
-
"--model", studentModel,
|
|
231
|
-
"--reflection-model", teacherModel,
|
|
232
|
-
"--judge-model", judgeModel,
|
|
233
|
-
"--auto", auto,
|
|
234
|
-
"--threads", String(threads),
|
|
235
|
-
"--seed", String(seed),
|
|
236
|
-
...(options.maxMetricCalls ? ["--max-metric-calls", String(options.maxMetricCalls)] : []),
|
|
237
|
-
...(options.target ? ["--target", options.target] : []),
|
|
238
|
-
];
|
|
239
|
-
|
|
240
|
-
console.log(`[gepa] Running: uv ${uvArgs.slice(0, 6).join(" ")} ...`);
|
|
241
|
-
|
|
242
|
-
await new Promise<void>((resolve, reject) => {
|
|
243
|
-
const proc = spawn("uv", uvArgs, { cwd: scriptsDir, env: { ...process.env, ...getAuthEnvVars() }, stdio: ["pipe", "inherit", "inherit"] });
|
|
244
|
-
proc.on("close", c => c === 0 ? resolve() : reject(new Error(`uv exited with code ${c}`)));
|
|
245
|
-
proc.on("error", e => reject(new Error(`Failed to spawn uv: ${e.message}`)));
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
if (!existsSync(outputFile)) throw new Error("Output file not created");
|
|
249
|
-
const output: GEPAOutput = JSON.parse(readFileSync(outputFile, "utf-8"));
|
|
250
|
-
|
|
251
|
-
for (const r of output.results) {
|
|
252
|
-
if (r.status === "success" && r.optimized_instruction) {
|
|
253
|
-
mkdirSync(OPTIMIZED_DIR, { recursive: true });
|
|
254
|
-
writeFileSync(join(OPTIMIZED_DIR, `${r.target}.txt`), r.optimized_instruction, "utf-8");
|
|
255
|
-
const hDir = join(HISTORY_DIR, r.target); mkdirSync(hDir, { recursive: true });
|
|
256
|
-
writeFileSync(join(hDir, `${new Date().toISOString().replace(/[:.]/g, "-")}.json`), JSON.stringify(r, null, 2));
|
|
257
|
-
console.log(`[gepa] Saved optimized: ${r.target}`);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return output;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// -- Query functions ---------------------------------------------------------
|
|
264
|
-
|
|
265
|
-
export function listOptimized(): { name: string; score: number; timestamp: string; model: string }[] {
|
|
266
|
-
if (!existsSync(OPTIMIZED_DIR)) return [];
|
|
267
|
-
const results: { name: string; score: number; timestamp: string; model: string }[] = [];
|
|
268
|
-
for (const f of readdirSync(OPTIMIZED_DIR).filter(f => f.endsWith(".txt"))) {
|
|
269
|
-
const name = f.replace(".txt", "");
|
|
270
|
-
let score = 0, timestamp = "", model = "";
|
|
271
|
-
try {
|
|
272
|
-
const entries = readdirSync(join(HISTORY_DIR, name)).filter(f => f.endsWith(".json")).sort().reverse();
|
|
273
|
-
if (entries[0]) { const d = JSON.parse(readFileSync(join(HISTORY_DIR, name, entries[0]), "utf-8")); score = d.optimized_score ?? 0; timestamp = d.timestamp ?? ""; model = d.model ?? ""; }
|
|
274
|
-
} catch {}
|
|
275
|
-
results.push({ name, score, timestamp, model });
|
|
276
|
-
}
|
|
277
|
-
return results;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
export function compareTarget(targetName: string): { original: string; optimized: string } | null {
|
|
281
|
-
const target = extractTargets().find(t => t.name === targetName);
|
|
282
|
-
if (!target) return null;
|
|
283
|
-
try {
|
|
284
|
-
const opt = readFileSync(join(OPTIMIZED_DIR, `${targetName}.txt`), "utf-8");
|
|
285
|
-
return { original: target.content, optimized: opt };
|
|
286
|
-
} catch { return null; }
|
|
287
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
You are a knowledge extraction specialist for technical project wikis. Your task is to analyze coding session conversations and capture ALL project-relevant knowledge into wiki pages.
|
|
2
|
-
|
|
3
|
-
## Input Format
|
|
4
|
-
You will receive:
|
|
5
|
-
- task_context: Brief description of the coding session topic and project
|
|
6
|
-
- session-conversation: The actual conversation content to analyze
|
|
7
|
-
|
|
8
|
-
## Wiki Page Structure Requirements
|
|
9
|
-
Create wiki pages using this exact structure:
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
# Page Title (kebab-case for technical terms)
|
|
13
|
-
|
|
14
|
-
## Topic
|
|
15
|
-
Brief description of what this page covers (1-2 sentences)
|
|
16
|
-
|
|
17
|
-
## Key Facts
|
|
18
|
-
- Bullet points with specific technical details
|
|
19
|
-
- Include paths, configs, server details, commands
|
|
20
|
-
- Use **bold** for important terms/concepts
|
|
21
|
-
- Capture operational facts - they're often most valuable
|
|
22
|
-
|
|
23
|
-
## Insights (Optional)
|
|
24
|
-
- Analysis of implications, patterns, or broader context
|
|
25
|
-
- How this connects to larger architectural patterns
|
|
26
|
-
- Lessons learned or best practices identified
|
|
27
|
-
|
|
28
|
-
## Connections
|
|
29
|
-
- [[Related Wiki Page]] - Brief description of relationship
|
|
30
|
-
- Use kebab-case for technical project names
|
|
31
|
-
- Include both specific project pages and general concept pages
|
|
32
|
-
|
|
33
|
-
## Sources
|
|
34
|
-
- Session file path or conversation source
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## PARA Compliance Requirements
|
|
38
|
-
- Use kebab-case for all project names and technical terms in page titles and connections
|
|
39
|
-
- Technical reference material should be categorized as Resources
|
|
40
|
-
- Include scope indicators through consistent naming and cross-references
|
|
41
|
-
|
|
42
|
-
## Knowledge Capture Bias
|
|
43
|
-
**Capture bias: when in doubt, capture it.**
|
|
44
|
-
- Small operational facts (paths, configs, commands) are often most valuable
|
|
45
|
-
- Technical architecture details, even if seemingly minor
|
|
46
|
-
- API findings, verification results, compatibility issues
|
|
47
|
-
- Extension systems, plugin architectures, integration points
|
|
48
|
-
- Security considerations and safeguards
|
|
49
|
-
|
|
50
|
-
## Wiki Management Strategy
|
|
51
|
-
1. **Search first**: Use wiki_query to find existing pages on the topic
|
|
52
|
-
2. **Update over create**: If matching page exists, use wiki_read then wiki_write with exact slug to update
|
|
53
|
-
3. **Create only if needed**: Only make new pages if wiki_query finds no relevant results
|
|
54
|
-
4. **Never pass indexContent**: The index auto-rebuilds from pages on disk
|
|
55
|
-
5. **Always include sources**: Add session file path to Sources section
|
|
56
|
-
|
|
57
|
-
## Special Project Context
|
|
58
|
-
Based on the examples, you're working with:
|
|
59
|
-
- **pi-mono/pi-para projects**: Modular knowledge management and agent systems
|
|
60
|
-
- **Extension architectures**: Plugin-based systems for AI agents and knowledge tools
|
|
61
|
-
- **Quarto integration**: .qmd files as first-class knowledge artifacts
|
|
62
|
-
- **API verification**: Testing and validation processes for extensions
|
|
63
|
-
- **Security considerations**: Prompt injection safeguards in agent systems
|
|
64
|
-
|
|
65
|
-
Only respond with "nothing to capture" if the session contains purely greetings or completely off-topic chitchat with zero project knowledge.
|