Merge d981a2d68b into 412705783d
This commit is contained in:
commit
9ce35ac908
6 changed files with 76 additions and 0 deletions
|
|
@ -236,6 +236,13 @@ func (al *AgentLoop) allocateRouteSession(route routing.ResolvedRoute, msg bus.I
|
|||
})
|
||||
}
|
||||
|
||||
func originTopicID(origin *bus.InboundContext) string {
|
||||
if origin == nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(origin.TopicID)
|
||||
}
|
||||
|
||||
func (al *AgentLoop) processSystemMessage(
|
||||
ctx context.Context,
|
||||
msg bus.InboundMessage,
|
||||
|
|
|
|||
|
|
@ -522,6 +522,7 @@ toolLoop:
|
|||
ts.opts.Dispatch.MessageID(),
|
||||
ts.opts.Dispatch.ReplyToMessageID(),
|
||||
)
|
||||
execCtx = tools.WithToolTopicID(execCtx, originTopicID(ts.opts.Dispatch.InboundContext))
|
||||
execCtx = tools.WithToolSessionContext(
|
||||
execCtx,
|
||||
ts.agent.ID,
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ type toolCtxKey struct{ name string }
|
|||
var (
|
||||
ctxKeyChannel = &toolCtxKey{"channel"}
|
||||
ctxKeyChatID = &toolCtxKey{"chatID"}
|
||||
ctxKeyTopicID = &toolCtxKey{"topicID"}
|
||||
ctxKeyMessageID = &toolCtxKey{"messageID"}
|
||||
ctxKeyReplyToMessageID = &toolCtxKey{"replyToMessageID"}
|
||||
ctxKeyAgentID = &toolCtxKey{"agentID"}
|
||||
|
|
@ -59,6 +60,12 @@ func WithToolContext(ctx context.Context, channel, chatID string) context.Contex
|
|||
return ctx
|
||||
}
|
||||
|
||||
// WithToolTopicID returns a child context carrying the inbound topic/thread id.
|
||||
func WithToolTopicID(ctx context.Context, topicID string) context.Context {
|
||||
ctx = context.WithValue(ctx, ctxKeyTopicID, topicID)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// WithToolMessageContext returns a child context carrying inbound message IDs.
|
||||
func WithToolMessageContext(ctx context.Context, messageID, replyToMessageID string) context.Context {
|
||||
ctx = context.WithValue(ctx, ctxKeyMessageID, messageID)
|
||||
|
|
@ -100,6 +107,12 @@ func ToolChatID(ctx context.Context) string {
|
|||
return v
|
||||
}
|
||||
|
||||
// ToolTopicID extracts the inbound topic/thread id from ctx, or "" if unset.
|
||||
func ToolTopicID(ctx context.Context) string {
|
||||
v, _ := ctx.Value(ctxKeyTopicID).(string)
|
||||
return v
|
||||
}
|
||||
|
||||
// ToolMessageID extracts the current inbound message ID from ctx, or "" if unset.
|
||||
func ToolMessageID(ctx context.Context) string {
|
||||
v, _ := ctx.Value(ctxKeyMessageID).(string)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ func WithToolContext(ctx context.Context, channel, chatID string) context.Contex
|
|||
return toolshared.WithToolContext(ctx, channel, chatID)
|
||||
}
|
||||
|
||||
func WithToolTopicID(ctx context.Context, topicID string) context.Context {
|
||||
return toolshared.WithToolTopicID(ctx, topicID)
|
||||
}
|
||||
|
||||
func WithToolMessageContext(ctx context.Context, messageID, replyToMessageID string) context.Context {
|
||||
return toolshared.WithToolMessageContext(ctx, messageID, replyToMessageID)
|
||||
}
|
||||
|
|
@ -69,6 +73,10 @@ func ToolChatID(ctx context.Context) string {
|
|||
return toolshared.ToolChatID(ctx)
|
||||
}
|
||||
|
||||
func ToolTopicID(ctx context.Context) string {
|
||||
return toolshared.ToolTopicID(ctx)
|
||||
}
|
||||
|
||||
func ToolMessageID(ctx context.Context) string {
|
||||
return toolshared.ToolMessageID(ctx)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -372,6 +372,13 @@ func (t *ExecTool) runSync(ctx context.Context, command, cwd string) *ToolResult
|
|||
if cwd != "" {
|
||||
cmd.Dir = cwd
|
||||
}
|
||||
cmd.Env = append(os.Environ(),
|
||||
"PICOCLAW_TOOL_CHANNEL="+ToolChannel(ctx),
|
||||
"PICOCLAW_TOOL_CHAT_ID="+ToolChatID(ctx),
|
||||
"PICOCLAW_TOOL_TOPIC_ID="+ToolTopicID(ctx),
|
||||
"PICOCLAW_TOOL_MESSAGE_ID="+ToolMessageID(ctx),
|
||||
"PICOCLAW_TOOL_REPLY_TO_MESSAGE_ID="+ToolReplyToMessageID(ctx),
|
||||
)
|
||||
|
||||
prepareCommandForTermination(cmd)
|
||||
|
||||
|
|
@ -484,6 +491,13 @@ func (t *ExecTool) runBackground(ctx context.Context, command, cwd string, ptyEn
|
|||
if cwd != "" {
|
||||
cmd.Dir = cwd
|
||||
}
|
||||
cmd.Env = append(os.Environ(),
|
||||
"PICOCLAW_TOOL_CHANNEL="+ToolChannel(ctx),
|
||||
"PICOCLAW_TOOL_CHAT_ID="+ToolChatID(ctx),
|
||||
"PICOCLAW_TOOL_TOPIC_ID="+ToolTopicID(ctx),
|
||||
"PICOCLAW_TOOL_MESSAGE_ID="+ToolMessageID(ctx),
|
||||
"PICOCLAW_TOOL_REPLY_TO_MESSAGE_ID="+ToolReplyToMessageID(ctx),
|
||||
)
|
||||
|
||||
prepareCommandForTermination(cmd)
|
||||
|
||||
|
|
@ -1073,6 +1087,19 @@ func (t *ExecTool) guardCommand(command, cwd string) string {
|
|||
for _, loc := range matchIndices {
|
||||
raw := cmd[loc[0]:loc[1]]
|
||||
|
||||
// Skip slash-containing relative command segments like
|
||||
// "scripts/foo.sh". The absolute-path regex matches the "/foo.sh"
|
||||
// substring inside that token, but this is not an absolute path.
|
||||
if strings.HasPrefix(raw, "/") && loc[0] > 0 {
|
||||
prev := cmd[loc[0]-1]
|
||||
if (prev >= 'a' && prev <= 'z') ||
|
||||
(prev >= 'A' && prev <= 'Z') ||
|
||||
(prev >= '0' && prev <= '9') ||
|
||||
prev == '_' || prev == '.' || prev == '-' {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Skip URL path components that look like they're from web URLs.
|
||||
// When a URL like "https://github.com" is parsed, the regex captures
|
||||
// "//github.com" as a match (the path portion after "https:").
|
||||
|
|
|
|||
|
|
@ -703,6 +703,26 @@ func TestShellTool_URLBypassPrevented(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestShellTool_RelativeScriptPathNotMisclassifiedAsAbsolute(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
scriptsDir := filepath.Join(tmpDir, "scripts")
|
||||
require.NoError(t, os.MkdirAll(scriptsDir, 0o755))
|
||||
scriptPath := filepath.Join(scriptsDir, "echo.sh")
|
||||
require.NoError(t, os.WriteFile(scriptPath, []byte("#!/bin/sh\necho ok\n"), 0o755))
|
||||
|
||||
tool, err := NewExecTool(tmpDir, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
result := tool.Execute(context.Background(), map[string]any{
|
||||
"action": "run",
|
||||
"command": "scripts/echo.sh",
|
||||
"cwd": tmpDir,
|
||||
})
|
||||
if result.IsError && strings.Contains(result.ForLLM, "path outside working dir") {
|
||||
t.Fatalf("relative script path should not be blocked: %s", result.ForLLM)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShellTool_Background_ReturnsImmediately(t *testing.T) {
|
||||
tool, err := NewExecTool("", false)
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue