Skip to content

Commit 5f60f0f

Browse files
committed
feat: workspace naming & user isolation (ISS-39)
2 parents aa07e6c + a582cd4 commit 5f60f0f

249 files changed

Lines changed: 26124 additions & 14 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/scheduled_tasks.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"sessionId":"f9f1e5ad-00a2-4034-812b-4a9eb6a494b7","pid":741641,"acquiredAt":1776017431094}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
name: agent-optimize
3+
description: |
4+
Audit and optimize a Forge chat agent for MCP projects.
5+
Tests queries, checks schema/memory/patterns in Qdrant,
6+
updates behaviorRules, and validates improvements.
7+
Covers: schema cache, alias batching, tool patterns, knowledge graph.
8+
user_invocable: true
9+
argument_description: "<project-slug> [hubToken]"
10+
---
11+
12+
# Agent Optimize
13+
14+
Audit + optimize a Forge MCP chat agent in 7 phases.
15+
16+
## Phases
17+
18+
1. **Baseline** — Run `/chat-eval` to get gap metrics
19+
2. **Inspect** — Check Qdrant for schema sections, tool patterns, memories. See `references/qdrant-queries.md`
20+
3. **Config** — Fetch project agentConfig, review behaviorRules/strategies. See `references/config-review.md`
21+
4. **Test** — Run diverse queries (LOOKUP/SEARCH/SUMMARY/ACTION/CHAT). See `references/test-matrix.md`
22+
5. **Fix** — Update behaviorRules via API for failed patterns. See `references/rule-writing.md`
23+
6. **Slim** — Reduce tokens: compress behaviorRules + optimize RAG context. See `references/token-optimization.md`
24+
7. **Re-test** — Same queries, compare iterations/GQL calls/schema calls
25+
8. **Eval** — Run `/chat-eval` again, compare with baseline
26+
27+
## Key Targets
28+
29+
| Metric | Target |
30+
|--------|--------|
31+
| graphql_schema calls | 0 |
32+
| Avg iterations | ≤6 |
33+
| 502 timeouts | 0 |
34+
| Empty replies | 0 |
35+
| Tool patterns stored | Growing over time |
36+
| behaviorRules chars | Minimized (no redundancy with Qdrant) |
37+
38+
## Known Projects
39+
40+
| Project | Slug | API Key | Hub Token |
41+
|---------|------|---------|-----------||
42+
| Light Human | `portal-lh` | `fk_0186f0a601488027ac1661698e9a6126e720274a47e96759` | `3291\|PNi8lVJ3PhALqYflnvvrZe33gHLOxCtqrobuVgPEcfcd762e` |
43+
44+
## Quick Commands
45+
46+
```bash
47+
# Chat-eval baseline
48+
cd forge/strapi && npx tsx scripts/eval-chat-gaps.ts --project <slug> --user thanhnh --pass 'Falconx@@123' --limit 200
49+
50+
# Test a query
51+
curl -sS -g -X POST 'https://forge-api.sidcorp.co/api/chat' \
52+
-H 'Content-Type: application/json' -H 'X-Forge-API-Key: <key>' \
53+
--data-raw '{"message":"<query>","stream":false,"hubToken":"<token>"}' --max-time 180
54+
55+
# Trigger MCP sync
56+
curl -sS -X POST 'https://forge-api.sidcorp.co/api/knowledge/sync' -H 'X-Forge-API-Key: <key>' -d '{"projectSlug":"<slug>"}'
57+
```
58+
59+
## References
60+
61+
- `references/qdrant-queries.md` — Qdrant scroll/search commands for schema, memories, patterns
62+
- `references/config-review.md` — How to fetch/update agentConfig, what to check
63+
- `references/test-matrix.md` — Test query matrix and expected behavior per intent
64+
- `references/architecture.md` — Memory/edge/pattern data flow and key files
65+
- `references/common-fixes.md` — Solutions for schema loops, N+1, timeouts, empty replies
66+
- `references/token-optimization.md` — Compress behaviorRules without losing performance
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Architecture
2+
3+
## Data flow: Chat → Learning
4+
5+
```
6+
Chat turn completes (fire-and-forget):
7+
├── extractMemories() [LLM: gemini-flash]
8+
│ ├── facts[] → Qdrant memory (preference/correction/convention/tool_pattern)
9+
│ └── edges[] → Strapi DB knowledge_edges (subject→predicate→object)
10+
└── extractToolPatterns() [no LLM]
11+
└── successful GQL calls → Qdrant memory (tool_pattern)
12+
```
13+
14+
## Data flow: Query → Context
15+
16+
```
17+
User query → ragGate (intent + condense) → multiStrategySearch → reranker → context
18+
→ schema force-inject if MCP project missing schema in top results
19+
→ knowledge graph 1-hop edge expansion → edgeContext
20+
→ system prompt = base rules + behaviorRules + queryStrategy + RAG context + edges
21+
```
22+
23+
## Reranker boosts (source_type)
24+
25+
mcp_schema +0.20, skill +0.15, memory +0.12, knowledge +0.10, chat_session +0.08, issue +0.05
26+
27+
## Key files
28+
29+
| File | Purpose |
30+
|------|---------|
31+
| `services/agent/memory.ts` | extractMemories, addMemory, extractToolPatterns |
32+
| `services/agent/memory-lifecycle.ts` | Prune stale memories/edges (6h cron) |
33+
| `services/knowledge-graph/index.ts` | upsertEdge, queryEdges, extractIssueEdges |
34+
| `services/embeddings/reranker.ts` | Source-type boosts |
35+
| `services/agent/mcp-sync.ts` | MCP knowledge sync + schema sections |
36+
| `api/chat/services/chat-prompt-builder.ts` | RAG retrieval, schema force-inject |
37+
| `services/agent/system-prompt.ts` | Layered prompt builder |
38+
| `bootstrap/seeds/domain-templates.ts` | Template defaults (copy-on-apply) |
39+
40+
All paths relative to `forge/strapi/src/`.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Common Fixes
2+
3+
## Agent calls graphql_schema every conversation
4+
- Check Qdrant for `mcp_schema` entries — missing = trigger sync
5+
- Reranker boost is +0.20 for mcp_schema (highest)
6+
- Force-inject in chat-prompt-builder ensures schema in context for MCP projects
7+
- If schema sections are stale, re-sync: `POST /api/knowledge/sync`
8+
9+
## N+1 graphql_query calls (same query, different IDs)
10+
- Add alias batching rule to behaviorRules with **specific example** using actual query names
11+
- Generic: "NEVER loop graphql_query. Use aliases: `{ a1: query(id:1){...} a2: query(id:2){...} }`"
12+
- Batch up to 20 aliases per call
13+
14+
## 502 timeout (>100s Cloudflare limit)
15+
- Agent doing too many iterations before responding
16+
- Add behaviorRule with the efficient pattern: "1-2 GQL calls + code_run"
17+
- Ensure `code_run` in `enabledTools`
18+
- Check if query needs a new aggregation endpoint on the portal side
19+
20+
## Empty reply (0 chars)
21+
- Check `qualitySignals.iterationLogs` in chat-log DB for the failing query
22+
- Common causes:
23+
1. **Large tool results** — GQL returns 30k+ chars, fills context, no room for text output
24+
2. **TodoWrite burns iterations** — agent plans instead of acting (stream:false has no UI benefit)
25+
3. **stopReason=max_tokens** on final iter — output budget spent on tool_use JSON
26+
4. **stopReason=end_turn with textChars=0** — model chose to stop without text (model bug)
27+
- Diagnosis query: `GET /api/chat-logs?sort=createdAt:desc&filters[projectSlug][$eq]=<slug>` (JWT auth)
28+
- Fix: add rule to fetch minimal fields, then aggregate with code_run
29+
- Prefer `language: "javascript"` (python3 sometimes not found on prod)
30+
31+
## Tool patterns not stored
32+
- Check `extractToolPatterns` wired in `chat.ts` and `channels/handler.ts` post-turn
33+
- Only stores successful `*__graphql_query` calls with result > 50 chars
34+
- Dedup at 0.85 cosine — similar queries won't duplicate (expected)
35+
36+
## Knowledge graph edges empty for MCP projects
37+
- Normal for projects without Forge issues
38+
- Edges from chat only appear when users discuss structural relationships
39+
- Tool patterns (in Qdrant) are more valuable for MCP query optimization
40+
41+
## Rule writing tips
42+
- Reference exact query names and field names from the schema
43+
- Include a working GQL snippet, not just "use the right query"
44+
- One concern per rule — don't mix alias batching with field naming
45+
- Test the rule by running the exact GQL in the snippet before adding
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Config Review
2+
3+
## Fetch project agentConfig
4+
5+
```bash
6+
curl -sS -g 'https://forge-api.sidcorp.co/api/projects?filters[slug][$eq]=<slug>&fields[0]=agentConfig&fields[1]=documentId' \
7+
-H 'X-Forge-API-Key: <api-key>'
8+
```
9+
10+
## Update agentConfig
11+
12+
Requires JWT auth (API key is read-only for projects):
13+
14+
```bash
15+
JWT=$(curl -sS -g 'https://forge-api.sidcorp.co/api/auth/local' \
16+
-H 'Content-Type: application/json' \
17+
-d '{"identifier":"thanhnh","password":"Falconx@@123"}' | python3 -c "import json,sys; print(json.load(sys.stdin)['jwt'])")
18+
19+
curl -sS -g -X PUT "https://forge-api.sidcorp.co/api/projects/<DOC_ID>" \
20+
-H 'Content-Type: application/json' -H "Authorization: Bearer $JWT" \
21+
-d '{"data":{"agentConfig":{...}}}'
22+
```
23+
24+
## Checklist
25+
26+
**behaviorRules** — most impactful for MCP projects:
27+
- [ ] References exact GQL query names and field names (not generic advice)
28+
- [ ] Includes working GQL snippets for common patterns
29+
- [ ] Has alias batching rule with specific example
30+
- [ ] Has aggregation strategy: "1-2 GQL calls + code_run (JS)"
31+
- [ ] Domain terminology defined (acronyms, status mappings)
32+
33+
**queryStrategies** — all 6 intents covered:
34+
- [ ] LOOKUP: mentions filters, fields, MCP tools
35+
- [ ] SEARCH: mentions search parameter, code lookups
36+
- [ ] SUMMARY: mentions statistics queries, date defaults
37+
- [ ] CREATE: mentions confirmation before creating
38+
- [ ] ACTION: mentions conversation history
39+
- [ ] CHAT: no tool calls needed
40+
41+
**intentExamples** — 15-30 examples, multilingual if users speak multiple languages
42+
43+
**enabledTools**`code_run` required for aggregations, `forge_language` for language detection
44+
45+
**Note**: Domain templates are copy-on-apply. Updating the seed template does NOT update existing projects. Always update the project's config directly via API.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Qdrant Queries
2+
3+
Qdrant URL: `http://216.18.217.213:6333`, API key in `forge/strapi/.env``QDRANT_API_KEY`.
4+
Collection: `forge_embeddings`. Get project `documentId` from Strapi API first.
5+
6+
## Schema sections (mcp_schema)
7+
8+
```bash
9+
curl -sS -X POST '$QDRANT_URL/collections/forge_embeddings/points/scroll' \
10+
-H 'Content-Type: application/json' -H "api-key: $QDRANT_API_KEY" \
11+
-d '{"filter":{"must":[
12+
{"key":"project_id","match":{"value":"<DOC_ID>"}},
13+
{"key":"source_type","match":{"value":"mcp_schema"}}
14+
]},"limit":50,"with_payload":true,"with_vector":false}'
15+
```
16+
17+
Each MCP server should have multiple sections (split by `## HEADER`). Single entry = re-sync needed.
18+
19+
## Memories (tool_pattern, correction, preference, convention)
20+
21+
```bash
22+
# Same endpoint, filter source_type: "memory"
23+
{"key":"source_type","match":{"value":"memory"}}
24+
```
25+
26+
Check `metadata.category` distribution. Healthy project: 10+ tool_patterns after a few sessions.
27+
28+
## Source type reference
29+
30+
| source_type | Created by | Purpose |
31+
|-------------|-----------|---------|
32+
| `memory` | extractMemories, extractToolPatterns | Learned facts + working GQL patterns |
33+
| `mcp_schema` | syncMcpKnowledge Phase 5 | GraphQL schema sections per MCP server |
34+
| `hub_task/project/comment/config` | syncMcpKnowledge Phases 1-4 | MCP hub data |
35+
| `issue/skill/knowledge/chat_session` | Forge lifecycle hooks | Core project data |
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Test Matrix
2+
3+
Run 6-8 queries covering all intents. For each, check: iterations, GQL calls, schema calls, reply quality.
4+
5+
## Query template
6+
7+
```bash
8+
curl -sS -g -X POST 'https://forge-api.sidcorp.co/api/chat' \
9+
-H 'Content-Type: application/json' -H 'X-Forge-API-Key: <key>' \
10+
--data-raw '{"message":"<query>","stream":false,"hubToken":"<token>"}' --max-time 180
11+
```
12+
13+
Parse response: `iterations`, `toolCalls[].name`, `toolCalls[].isError`, `reply` length.
14+
15+
## Expected behavior per intent
16+
17+
| Intent | Max iters | Schema calls | Pattern |
18+
|--------|-----------|-------------|---------|
19+
| CHAT | 1 | 0 | No tool calls |
20+
| LOOKUP | 2-3 | 0 | 1-2 GQL with filters |
21+
| SEARCH | 2-3 | 0 | GQL with search param |
22+
| SUMMARY | 3-5 | 0 | Statistics queries or batched aliases |
23+
| SUMMARY-complex | 4-6 | 0 | Alias batch + code_run |
24+
| ACTION | 1-3 | 0 | Uses conversation context |
25+
26+
## Red flags
27+
28+
- `graphql_schema` in toolCalls → schema cache not working
29+
- Same GQL query called 4+ times → alias batching rule missing
30+
- `code_run` with `language: "python"` failing → use JS instead
31+
- Reply 0 chars → output token exhaustion, reduce data payload
32+
- 502 response → >100s Cloudflare timeout, too many iterations
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Token Optimization
2+
3+
Reduce token cost on two fronts: what's stored (embeddings) and what's sent to agent (retrieval).
4+
5+
## A. BehaviorRules Compression
6+
7+
Target: <3000 chars total.
8+
9+
| Rule content | Qdrant source | Action |
10+
|-------------|---------------|--------|
11+
| Inline status ID enums | `mcp_schema` ENUMS section | Remove IDs, keep name mappings only |
12+
| Full GQL examples | `tool_pattern` memories | Compress to query signatures |
13+
| "call graphql_schema" | Schema cache + force-inject | Change to "check schema in context" |
14+
15+
## B. RAG Context Optimization
16+
17+
**Key files:**
18+
- `services/rag-formatter/index.ts` — formats entries before injection
19+
- `services/agent/system-prompt.ts` `layerRelevantContext()` — MAX_CHARS=6000, 800c/entry
20+
21+
## C. Verification
22+
23+
After changes, test the same queries from Phase 4:
24+
- Schema calls must stay at 0
25+
- Iterations should not increase (≤2 more)
26+
- Reply quality must not degrade
27+
- Measure total inputTokens from response — should decrease
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
---
2+
name: antigravity-guide
3+
description: "Environment guide for Antigravity server-side agent. High-level CLI for Forge API, Windows environment, common pitfalls. Auto-loaded by all pipeline steps."
4+
user_invocable: false
5+
---
6+
7+
# Antigravity Environment Guide
8+
9+
You are running inside **Antigravity** on a **Windows** host.
10+
11+
## Environment
12+
13+
- **OS**: Windows (`c:\Canaran.Antigravity.Service_Sessions\<sessionId>\`)
14+
- **Repo**: Cloned into a subdirectory of your session root (not the root itself)
15+
- **Node.js**: Available
16+
- **Git**: Available
17+
- **Shell**: May NOT have Bash — use native file tools (read, write, list)
18+
19+
## Forge API — Use the CLI
20+
21+
**DO NOT use curl.** Use the high-level CLI instead:
22+
23+
```
24+
node forge-api.mjs get-issue <documentId>
25+
node forge-api.mjs update-issue <documentId> --status=confirmed --category=bug
26+
node forge-api.mjs update-issue <documentId> --plan=@plan.md --status=approved
27+
node forge-api.mjs update-issue <documentId> --data-file=payload.json
28+
node forge-api.mjs search-issues "keyword1 keyword2" --exclude=<documentId> --limit=10
29+
node forge-api.mjs list-comments <issueDocumentId> --limit=5
30+
node forge-api.mjs create-comment <issueDocumentId> --body=@file.md --author=Snorlax
31+
node forge-api.mjs create-comment <issueDocumentId> --body=@file.md --author=Snorlax --attachments=42,43
32+
node forge-api.mjs upload <filepath>
33+
```
34+
35+
### Flags
36+
37+
- `--status=`, `--category=`, `--priority=`, `--complexity=` — simple string values, safe inline
38+
- `--plan=@plan.md`, `--body=@file.md` — read value from file (for long text)
39+
- `--relations=@relations.json` — read JSON from file
40+
- `--data-file=payload.json` — read entire update body from JSON file
41+
- `--attachments=42,43,44` — comma-separated media IDs (from upload command)
42+
43+
### Complex values: always use file-based payloads
44+
45+
For any value containing JSON (relations, sessionContext, arrays, objects), write to a file first and reference it.
46+
47+
**Updating an issue with complex fields:**
48+
1. Write JSON file: `{ "status": "confirmed", "relations": [...] }`
49+
2. Pass via `--data-file`: `node forge-api.mjs update-issue <documentId> --data-file=payload.json`
50+
51+
**Creating a comment with long body:**
52+
1. Write body to `.md` file
53+
2. `node forge-api.mjs create-comment <issueDocId> --body=@comment.md --author=Snorlax`
54+
55+
### Uploading Files (Screenshots)
56+
57+
```
58+
node forge-api.mjs upload screenshot.png
59+
```
60+
Response: `{ "data": { "id": 42, "url": "...", "name": "screenshot.png" } }`
61+
62+
## File Operations
63+
64+
Use native tools, not Unix commands:
65+
- **Read file**: file read tool (not `cat`)
66+
- **List directory**: directory listing tool (not `ls`)
67+
- **Write file**: file write tool (not `echo >`)
68+
69+
## Common Errors
70+
71+
1. **JSON parse error** → write data to a file first, use `--data-file=` or `--field=@file.json`
72+
2. **"Bash not available"** → use native tools + `node forge-api.mjs`
73+
3. **API errors** → never use curl, always use the CLI
74+
4. **"command not found: cat/ls"** → use `type`/`dir` or native tools
75+
5. **Git fails**`cd` into repo dir first (not session root), then `git fetch && git pull`

0 commit comments

Comments
 (0)