Enable kaya-board tools on Tasks mention replies.
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
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:
parent
856e5a3cab
commit
3a2a0c1c34
@ -14,4 +14,4 @@ AGENT_PROFILE_PATH=profiles/kaya.yaml
|
|||||||
HEALTH_LISTEN_ADDR=:8087
|
HEALTH_LISTEN_ADDR=:8087
|
||||||
METRICS_LISTEN_ADDR=:9097
|
METRICS_LISTEN_ADDR=:9097
|
||||||
SHUTDOWN_TIMEOUT=15s
|
SHUTDOWN_TIMEOUT=15s
|
||||||
RESPONSE_TIMEOUT=120s
|
RESPONSE_TIMEOUT=180s
|
||||||
|
|||||||
@ -10,8 +10,8 @@ Long-lived daemon:
|
|||||||
2. On `comment.added` where mentions include agent `kaya` and author ≠ `kaya`:
|
2. On `comment.added` where mentions include agent `kaya` and author ≠ `kaya`:
|
||||||
- dedupes by comment id
|
- dedupes by comment id
|
||||||
- GET task + comments via REST
|
- GET task + comments via REST
|
||||||
- calls `l-ai-core` `AIService.RunAgent` (tools disabled) with Kaya persona
|
- 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>`
|
- POST comment with `Idempotency-Key: mention-reply:<comment_id>` (do not use `add_comment` for the reply itself)
|
||||||
|
|
||||||
No Telegram / Live Chat integration.
|
No Telegram / Live Chat integration.
|
||||||
|
|
||||||
@ -35,6 +35,7 @@ No Telegram / Live Chat integration.
|
|||||||
| `AGENT_PROFILE_PATH` | no | `profiles/kaya.yaml` |
|
| `AGENT_PROFILE_PATH` | no | `profiles/kaya.yaml` |
|
||||||
| `HEALTH_LISTEN_ADDR` | no | `:8087` |
|
| `HEALTH_LISTEN_ADDR` | no | `:8087` |
|
||||||
| `METRICS_LISTEN_ADDR` | no | `:9097` |
|
| `METRICS_LISTEN_ADDR` | no | `:9097` |
|
||||||
|
| `RESPONSE_TIMEOUT` | no | `180s` |
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
|
|||||||
@ -35,7 +35,7 @@ func Load() (Config, error) {
|
|||||||
MetricsListenAddr: env("METRICS_LISTEN_ADDR", ":9097"),
|
MetricsListenAddr: env("METRICS_LISTEN_ADDR", ":9097"),
|
||||||
HealthListenAddr: env("HEALTH_LISTEN_ADDR", ":8087"),
|
HealthListenAddr: env("HEALTH_LISTEN_ADDR", ":8087"),
|
||||||
ShutdownTimeout: envDur("SHUTDOWN_TIMEOUT", 15*time.Second),
|
ShutdownTimeout: envDur("SHUTDOWN_TIMEOUT", 15*time.Second),
|
||||||
ResponseTimeout: envDur("RESPONSE_TIMEOUT", 120*time.Second),
|
ResponseTimeout: envDur("RESPONSE_TIMEOUT", 180*time.Second),
|
||||||
}
|
}
|
||||||
if c.TasksWSURL == "" {
|
if c.TasksWSURL == "" {
|
||||||
return c, errors.New("TASKS_WS_URL is required")
|
return c, errors.New("TASKS_WS_URL is required")
|
||||||
|
|||||||
@ -59,17 +59,25 @@ func (r *CoreRunner) RunAgent(ctx context.Context, req RunRequest) (RunResult, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
toolsEnabled := req.ToolsEnabled
|
toolsEnabled := req.ToolsEnabled
|
||||||
maxIters := uint32(1)
|
maxIters := req.MaxToolIterations
|
||||||
maxCalls := uint32(0)
|
maxCalls := req.MaxTotalToolCalls
|
||||||
if toolsEnabled {
|
if !toolsEnabled {
|
||||||
|
maxIters = 1
|
||||||
|
maxCalls = 0
|
||||||
|
} else {
|
||||||
|
if maxIters == 0 {
|
||||||
maxIters = 12
|
maxIters = 12
|
||||||
|
}
|
||||||
|
if maxCalls == 0 {
|
||||||
maxCalls = 20
|
maxCalls = 20
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := r.api.RunAgent(r.auth(ctx), &corepb.AgentRequest{
|
resp, err := r.api.RunAgent(r.auth(ctx), &corepb.AgentRequest{
|
||||||
Messages: msgs,
|
Messages: msgs,
|
||||||
ModelName: req.Model,
|
ModelName: req.Model,
|
||||||
AgentId: req.AgentID,
|
AgentId: req.AgentID,
|
||||||
|
ToolProfile: req.ToolProfile,
|
||||||
ToolLoop: &corepb.AgentToolLoopOptions{
|
ToolLoop: &corepb.AgentToolLoopOptions{
|
||||||
ToolsEnabled: toolsEnabled,
|
ToolsEnabled: toolsEnabled,
|
||||||
MaxToolIterations: maxIters,
|
MaxToolIterations: maxIters,
|
||||||
|
|||||||
@ -18,6 +18,9 @@ type RunRequest struct {
|
|||||||
TimeoutSeconds uint32
|
TimeoutSeconds uint32
|
||||||
Metadata map[string]string
|
Metadata map[string]string
|
||||||
ToolsEnabled bool
|
ToolsEnabled bool
|
||||||
|
ToolProfile string
|
||||||
|
MaxToolIterations uint32
|
||||||
|
MaxTotalToolCalls uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
type RunResult struct {
|
type RunResult struct {
|
||||||
|
|||||||
@ -101,8 +101,9 @@ func (p Profile) SystemPrompt() string {
|
|||||||
}
|
}
|
||||||
b.WriteString("You reply only to task comment @mentions. Be helpful and concise. ")
|
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("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("Tools match Telegram chat (journal, tasks, steam/faceit, discord, node) — use them when needed. ")
|
||||||
b.WriteString("Do not invent task state that is not in the provided context. ")
|
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")
|
b.WriteString("Output only the comment body to post — no preamble or quotes.\n")
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -154,7 +154,8 @@ func (l *Loop) handle(ctx context.Context, ev tasksws.Event) error {
|
|||||||
|
|
||||||
sys := l.Profile.SystemPrompt()
|
sys := l.Profile.SystemPrompt()
|
||||||
sys += " Avoid @mentioning other agents unless you need a concrete action — never @mention just to acknowledge."
|
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" {
|
if authorType == "human" {
|
||||||
sys += " A human mentioned you — always give a real reply."
|
sys += " A human mentioned you — always give a real reply."
|
||||||
}
|
}
|
||||||
@ -170,7 +171,10 @@ func (l *Loop) handle(ctx context.Context, ev tasksws.Event) error {
|
|||||||
IdempotencyKey: "mention-reply:" + commentID,
|
IdempotencyKey: "mention-reply:" + commentID,
|
||||||
TimeoutSeconds: timeoutSec,
|
TimeoutSeconds: timeoutSec,
|
||||||
Metadata: map[string]string{"trace_reason": "kaya_mention_reply", "comment_id": commentID, "task_id": taskID},
|
Metadata: map[string]string{"trace_reason": "kaya_mention_reply", "comment_id": commentID, "task_id": taskID},
|
||||||
ToolsEnabled: false,
|
ToolsEnabled: true,
|
||||||
|
ToolProfile: "kaya-board",
|
||||||
|
MaxToolIterations: 10,
|
||||||
|
MaxTotalToolCalls: 18,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("RunAgent: %w", err)
|
return fmt.Errorf("RunAgent: %w", err)
|
||||||
@ -184,7 +188,7 @@ func (l *Loop) handle(ctx context.Context, ev tasksws.Event) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("post comment: %w", err)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,7 +211,7 @@ func (l *Loop) shouldReplyToAgentMention(ctx context.Context, task Task, mention
|
|||||||
model = "deepseek-v4-flash"
|
model = "deepseek-v4-flash"
|
||||||
}
|
}
|
||||||
user := "Task: " + task.Title + "\nTrigger comment from another agent:\n" + trimRunes(mentionBody, 800) +
|
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{
|
res, err := l.Core.RunAgent(ctx, inference.RunRequest{
|
||||||
Messages: []inference.ChatMessage{
|
Messages: []inference.ChatMessage{
|
||||||
{Role: "system", Content: "You gate Kaya's board replies to agent @mentions. Prefer false. Output JSON only."},
|
{Role: "system", Content: "You gate Kaya's board replies to agent @mentions. Prefer false. Output JSON only."},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user