Enable kaya-board tools on Tasks mention replies.
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

Match Telegram chat tool surface (journal, tasks, steam/discord/node) so diary requests hit write_journal instead of a board-only comment.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
alex 2026-09-22 16:45:03 +03:00
parent 856e5a3cab
commit 3a2a0c1c34
7 changed files with 51 additions and 34 deletions

View File

@ -14,4 +14,4 @@ AGENT_PROFILE_PATH=profiles/kaya.yaml
HEALTH_LISTEN_ADDR=:8087
METRICS_LISTEN_ADDR=:9097
SHUTDOWN_TIMEOUT=15s
RESPONSE_TIMEOUT=120s
RESPONSE_TIMEOUT=180s

View File

@ -10,8 +10,8 @@ Long-lived daemon:
2. On `comment.added` where mentions include agent `kaya` and author ≠ `kaya`:
- dedupes by comment id
- GET task + comments via REST
- calls `l-ai-core` `AIService.RunAgent` (tools disabled) with Kaya persona
- POST comment with `Idempotency-Key: mention-reply:<comment_id>`
- calls `l-ai-core` `AIService.RunAgent` with tools enabled (`tool_profile=kaya-board`: journal, tasks, steam/faceit, discord, node — same surface as Telegram chat; no gitea/evolver)
- POST comment with `Idempotency-Key: mention-reply:<comment_id>` (do not use `add_comment` for the reply itself)
No Telegram / Live Chat integration.
@ -35,6 +35,7 @@ No Telegram / Live Chat integration.
| `AGENT_PROFILE_PATH` | no | `profiles/kaya.yaml` |
| `HEALTH_LISTEN_ADDR` | no | `:8087` |
| `METRICS_LISTEN_ADDR` | no | `:9097` |
| `RESPONSE_TIMEOUT` | no | `180s` |
## Build

View File

@ -35,7 +35,7 @@ func Load() (Config, error) {
MetricsListenAddr: env("METRICS_LISTEN_ADDR", ":9097"),
HealthListenAddr: env("HEALTH_LISTEN_ADDR", ":8087"),
ShutdownTimeout: envDur("SHUTDOWN_TIMEOUT", 15*time.Second),
ResponseTimeout: envDur("RESPONSE_TIMEOUT", 120*time.Second),
ResponseTimeout: envDur("RESPONSE_TIMEOUT", 180*time.Second),
}
if c.TasksWSURL == "" {
return c, errors.New("TASKS_WS_URL is required")

View File

@ -59,17 +59,25 @@ func (r *CoreRunner) RunAgent(ctx context.Context, req RunRequest) (RunResult, e
}
toolsEnabled := req.ToolsEnabled
maxIters := uint32(1)
maxCalls := uint32(0)
if toolsEnabled {
maxIters = 12
maxCalls = 20
maxIters := req.MaxToolIterations
maxCalls := req.MaxTotalToolCalls
if !toolsEnabled {
maxIters = 1
maxCalls = 0
} else {
if maxIters == 0 {
maxIters = 12
}
if maxCalls == 0 {
maxCalls = 20
}
}
resp, err := r.api.RunAgent(r.auth(ctx), &corepb.AgentRequest{
Messages: msgs,
ModelName: req.Model,
AgentId: req.AgentID,
Messages: msgs,
ModelName: req.Model,
AgentId: req.AgentID,
ToolProfile: req.ToolProfile,
ToolLoop: &corepb.AgentToolLoopOptions{
ToolsEnabled: toolsEnabled,
MaxToolIterations: maxIters,

View File

@ -8,16 +8,19 @@ type ChatMessage struct {
}
type RunRequest struct {
Messages []ChatMessage
Model string
AgentID string
RequestID string
IdempotencyKey string
JSONSchema string
Repair bool
TimeoutSeconds uint32
Metadata map[string]string
ToolsEnabled bool
Messages []ChatMessage
Model string
AgentID string
RequestID string
IdempotencyKey string
JSONSchema string
Repair bool
TimeoutSeconds uint32
Metadata map[string]string
ToolsEnabled bool
ToolProfile string
MaxToolIterations uint32
MaxTotalToolCalls uint32
}
type RunResult struct {

View File

@ -101,8 +101,9 @@ func (p Profile) SystemPrompt() string {
}
b.WriteString("You reply only to task comment @mentions. Be helpful and concise. ")
b.WriteString("Match the language of the triggering comment (Russian or English). ")
b.WriteString("When asked to journal / diary a merged change: write a short factual note of what shipped and any PR URL from the thread. ")
b.WriteString("Do not invent task state that is not in the provided context. ")
b.WriteString("Tools match Telegram chat (journal, tasks, steam/faceit, discord, node) — use them when needed. ")
b.WriteString("When asked to journal / diary a merged change: call write_journal (persona Postgres diary), then a short board reply noting what shipped and any PR URL. ")
b.WriteString("Do not invent task state that is not in the provided context or tool results. ")
b.WriteString("Output only the comment body to post — no preamble or quotes.\n")
return b.String()
}

View File

@ -154,7 +154,8 @@ func (l *Loop) handle(ctx context.Context, ev tasksws.Event) error {
sys := l.Profile.SystemPrompt()
sys += " Avoid @mentioning other agents unless you need a concrete action — never @mention just to acknowledge."
sys += " If Mira asks you to journal a merged task, write a short diary-style board comment (what changed + PR if present)."
sys += " CRITICAL: Do NOT call add_comment — this process posts your reply text as the board comment."
sys += " If asked to journal / diary a merged task: call write_journal with a short factual entry (what shipped + PR URL if present), then still write a brief board reply."
if authorType == "human" {
sys += " A human mentioned you — always give a real reply."
}
@ -164,13 +165,16 @@ func (l *Loop) handle(ctx context.Context, ev tasksws.Event) error {
{Role: "system", Content: sys},
{Role: "user", Content: userPrompt},
},
Model: model,
AgentID: agentID,
RequestID: "mention-reply:" + commentID,
IdempotencyKey: "mention-reply:" + commentID,
TimeoutSeconds: timeoutSec,
Metadata: map[string]string{"trace_reason": "kaya_mention_reply", "comment_id": commentID, "task_id": taskID},
ToolsEnabled: false,
Model: model,
AgentID: agentID,
RequestID: "mention-reply:" + commentID,
IdempotencyKey: "mention-reply:" + commentID,
TimeoutSeconds: timeoutSec,
Metadata: map[string]string{"trace_reason": "kaya_mention_reply", "comment_id": commentID, "task_id": taskID},
ToolsEnabled: true,
ToolProfile: "kaya-board",
MaxToolIterations: 10,
MaxTotalToolCalls: 18,
})
if err != nil {
return fmt.Errorf("RunAgent: %w", err)
@ -184,7 +188,7 @@ func (l *Loop) handle(ctx context.Context, ev tasksws.Event) error {
if err != nil {
return fmt.Errorf("post comment: %w", err)
}
l.log().Info("mention reply posted", "comment_id", commentID, "task_id", taskID, "duration_ms", res.DurationMS)
l.log().Info("mention reply posted", "comment_id", commentID, "task_id", taskID, "duration_ms", res.DurationMS, "tools_used", res.ToolsUsed)
return nil
}
@ -207,7 +211,7 @@ func (l *Loop) shouldReplyToAgentMention(ctx context.Context, task Task, mention
model = "deepseek-v4-flash"
}
user := "Task: " + task.Title + "\nTrigger comment from another agent:\n" + trimRunes(mentionBody, 800) +
"\n\nReply true ONLY if you must add new substance (journal entry Mira asked for). Default false for thanks/repeats."
"\n\nReply true if you must act (e.g. Mira asked you to journal via write_journal, or you need tools). Default false for thanks/acks/repeats."
res, err := l.Core.RunAgent(ctx, inference.RunRequest{
Messages: []inference.ChatMessage{
{Role: "system", Content: "You gate Kaya's board replies to agent @mentions. Prefer false. Output JSON only."},