|
1 | 1 | # Magec - TODO |
2 | 2 |
|
3 | | -## IN PROGRESS: Client Config — DefaultAgent + ThreadHistoryLimit |
| 3 | +## ~~Client Config — DefaultAgent + ThreadHistoryLimit~~ ✅ |
4 | 4 |
|
5 | | -### Goal |
6 | | -Two new fields in Discord and Slack client config: |
7 | | -1. **`defaultAgent`** — the agent that starts active on boot, persisted to store when user runs `!agent <id>` |
8 | | -2. **`threadHistoryLimit`** — number of previous thread messages passed as context to the agent (1–100 for Discord, 1–1000 for Slack) |
9 | | - |
10 | | -Telegram is excluded from `threadHistoryLimit` because it accumulates all messages via ADK session directly (no mention filter). Telegram does get `defaultAgent`. |
11 | | - |
12 | | -### Current state of active agent (before this change) |
13 | | -- Stored as `map[channelID]agentID` (Discord/Slack) or `map[chatID]agentID` (Telegram) in memory only |
14 | | -- Lost on restart — fallback is always `clientDef.AllowedAgents[0]` |
15 | | -- `!agent <id>` only updates the in-memory map, does NOT persist |
16 | | - |
17 | | -### Design decisions |
18 | | -- `defaultAgent` is **global per client** (not per channel) — whoever configures the client knows which model/agent it will use |
19 | | -- `!agent <id>` does two things: updates in-memory map AND persists `defaultAgent` to the store via the store's update mechanism |
20 | | -- `getActiveAgentID` fallback order: in-memory map → `defaultAgent` from config → `allowedAgents[0]` |
21 | | -- `threadHistoryLimit` replaces the hardcoded `50` in `fetchThreadContext`; defaults to `50` if not set |
22 | | - |
23 | | -### Files to modify |
24 | | - |
25 | | -**`server/store/types.go`**: |
26 | | -- Add `DefaultAgent string` and `ThreadHistoryLimit int` to `DiscordClientConfig` and `SlackClientConfig` |
27 | | -- Add `DefaultAgent string` to `TelegramClientConfig` |
28 | | - |
29 | | -**`server/clients/discord/spec.go`**: |
30 | | -- Add `defaultAgent` (string, description: "Default agent ID to use on startup") to JSON Schema |
31 | | -- Add `threadHistoryLimit` (integer, min: 1, max: 100, description: "Thread history messages passed to the agent as context") to JSON Schema |
32 | | - |
33 | | -**`server/clients/slack/spec.go`**: |
34 | | -- Same as Discord but max: 1000 |
35 | | - |
36 | | -**`server/clients/telegram/spec.go`** (if exists): |
37 | | -- Add `defaultAgent` only |
38 | | - |
39 | | -**`server/clients/discord/bot.go`**: |
40 | | -- `getActiveAgentID`: fallback chain → in-memory map → `c.clientDef.Config.Discord.DefaultAgent` → `allowedAgents[0]` |
41 | | -- `setActiveAgentID`: keep updating in-memory map + call store update to persist `DefaultAgent` |
42 | | -- `fetchThreadContext`: replace literal `50` with `c.clientDef.Config.Discord.ThreadHistoryLimit` (with fallback to 50 if 0) |
43 | | - |
44 | | -**`server/clients/slack/bot.go`**: |
45 | | -- Same pattern as Discord |
46 | | - |
47 | | -**`server/clients/telegram/bot.go`**: |
48 | | -- `getActiveAgentID`: fallback chain → in-memory map → `c.clientDef.Config.Telegram.DefaultAgent` → `allowedAgents[0]` |
49 | | -- `setActiveAgentID`: persist `DefaultAgent` to store |
50 | | - |
51 | | -### How to persist DefaultAgent to store |
52 | | -The store exposes `UpdateClient(def ClientDefinition) error` (or equivalent). After `!agent <id>`: |
53 | | -1. Copy `c.clientDef` |
54 | | -2. Set `clientDef.Config.Discord.DefaultAgent = agentID` (or Slack/Telegram) |
55 | | -3. Call store update |
56 | | -The store's `persist()` writes to disk automatically. |
57 | | - |
58 | | -Check exact store method signature in `server/store/store.go` before implementing. |
59 | | - |
60 | | -### Frontend (admin-ui) |
61 | | -- Discord client form: add "Default agent" text field + "Thread history messages" number input (1–100) |
62 | | -- Slack client form: same but max 1000 |
63 | | -- Telegram client form: add "Default agent" text field only |
64 | | -- Labels: "Default agent" and "Thread history messages" |
65 | | -- Tooltip for thread history: "Number of previous thread messages passed to the agent as context. Use lower values for smaller models." |
| 5 | +Implemented. `defaultAgent` persisted to store on `!agent <id>` with fallback chain (in-memory → defaultAgent → allowedAgents[0]). `threadHistoryLimit` replaces hardcoded 50 in Discord/Slack `fetchThreadContext`. Shared schema helpers in `server/clients/provider.go`. |
66 | 6 |
|
67 | 7 | --- |
68 | 8 |
|
@@ -606,68 +546,15 @@ If a single person uses Discord AND Telegram, they'd have two `userID`s and two |
606 | 546 |
|
607 | 547 | ## Low Priority |
608 | 548 |
|
609 | | -### Skill Card View Formatter |
| 549 | +### ~~Skill Card View Formatter~~ ✅ |
610 | 550 |
|
611 | | -**Problem**: Skills follow the [Agent Skills Specification](https://agentskills.io/specification) with YAML frontmatter (`name`, `description`, `license`, `compatibility`, `metadata`) followed by a markdown body. Some skills are non-canonical (no frontmatter, arbitrary markdown) and still valid. Rendering the raw SKILL.md in a card looks ugly — `---` delimiters, long markdown bodies, and code blocks don't belong in a card preview. |
612 | | - |
613 | | -**Solution**: Parse SKILL.md frontmatter and render structured card data instead of raw markdown. |
614 | | - |
615 | | -**Card layout**: |
616 | | -``` |
617 | | -┌─────────────────────────────────────┐ |
618 | | -│ 🔧 skill-name │ |
619 | | -│ │ |
620 | | -│ Description from frontmatter, │ |
621 | | -│ truncated to 2-3 lines... │ |
622 | | -│ │ |
623 | | -│ 📁 N files · 🐍 scripts · License │ |
624 | | -└─────────────────────────────────────┘ |
625 | | -``` |
626 | | - |
627 | | -- **Title**: `name` from frontmatter |
628 | | -- **Description**: `description` from frontmatter (truncated, max ~200 chars displayed) |
629 | | -- **Footer badges**: file count, content indicators (has `scripts/`, `references/`, `assets/`), license if present |
630 | | -- **Never render** the markdown body in the card |
631 | | - |
632 | | -**Fallback for non-canonical skills** (no valid YAML frontmatter): |
633 | | -- **Title**: directory name of the skill |
634 | | -- **Description**: first non-empty line of the markdown body (stripped of `#` heading markers), or "No description" |
635 | | -- **Footer**: file count only |
636 | | - |
637 | | -**Notes**: |
638 | | -- Parse frontmatter with a YAML parser — content between the first two `---` lines |
639 | | -- Spec defines `name` max 64 chars, `description` max 1024 chars — truncate to ~200 in card |
640 | | -- `compatibility` field could render as small tags if present |
641 | | - |
642 | | -**Modify**: `frontend/admin-ui/` (skill card component) |
| 551 | +Implemented. Frontend parses YAML frontmatter from `instructions` field via `lib/frontmatter.js`. Canonical skills (valid frontmatter with `name`) render structured cards with description, license/compatibility badges, and file count. Non-canonical skills fall back to store name/description + truncated instructions. |
643 | 552 |
|
644 | 553 | --- |
645 | 554 |
|
646 | | -### Skill Package Upload (ZIP/tar.gz) |
647 | | - |
648 | | -**Problem**: Current uploader only allows uploading files one at a time with no folder support. Many skills consist of multiple files (`scripts/`, `references/`, `assets/`, templates) that need directory structure. Uploading a complex skill is painful and error-prone. |
649 | | - |
650 | | -**Solution**: Support uploading a skill as a compressed package that preserves the full directory tree. |
651 | | - |
652 | | -**Upload flow**: |
653 | | -1. User uploads a `.zip` or `.tar.gz` in the Admin GUI |
654 | | -2. Backend extracts and validates: must contain a `SKILL.md` at the root (or one level deep if the archive wraps the skill in a single top-level directory) |
655 | | -3. Store the full tree — scripts, references, assets, everything |
656 | | -4. Parse `SKILL.md` frontmatter for the Skill Card View (see Skill Card View Formatter) |
657 | | -5. Expose a file browser in the skill detail view so the user can see what's inside |
658 | | - |
659 | | -**Requirements**: |
660 | | -- Preserve directory structure as-is on extraction |
661 | | -- The backend must resolve relative paths referenced in SKILL.md (e.g. `./reference/mcp_best_practices.md`, `./templates/viewer.html`) so the agent can read them at runtime |
662 | | -- Scripts are stored but **not executed** — available for the agent to read/reference, not to run |
663 | | -- Support both single-file skills (simple upload, same as today) and multi-file package upload |
664 | | -- Also support drag-and-drop of folders as an alternative (`webkitdirectory` or File System Access API) |
665 | | - |
666 | | -**Notes**: |
667 | | -- Many skills reference files with relative paths (`./reference/`, `./scripts/`) — the storage layer must preserve these paths so they resolve correctly when the agent loads them into context |
668 | | -- Backend needs a batch upload endpoint that handles paths relative to the skill root |
| 555 | +### ~~Skill Package Upload (ZIP/tar.gz)~~ ✅ |
669 | 556 |
|
670 | | -**Modify**: `frontend/admin-ui/` (upload component), `server/api/admin/` (upload endpoint), `server/store/` (skill storage) |
| 557 | +Implemented. `POST /skills/{id}/package` extracts ZIP or tar.gz, requires `SKILL.md` at root (or one level deep — auto-stripped). Preserves directory structure in `data/skills/{id}/`. If SKILL.md has valid frontmatter, `name` and `description` are extracted for the store; otherwise name defaults to archive filename. `SkillDialog.vue` has Manual | Package segmented toggle — Package mode shows a drop zone for compressed files. |
671 | 558 |
|
672 | 559 | --- |
673 | 560 |
|
|
0 commit comments