diff --git a/core/engine.go b/core/engine.go index b8c67ec..a075493 100644 --- a/core/engine.go +++ b/core/engine.go @@ -814,6 +814,20 @@ func (e *Engine) ExecuteCronJob(job *CronJob) error { break } } + // Fallback: in multi-workspace mode the stored session key may be prefixed + // with the workspace path (e.g. "/home/user/project:slack:C123:U456"). + // Search for a known platform name within the key and strip the prefix. + if targetPlatform == nil { + for _, p := range e.platforms { + needle := ":" + p.Name() + ":" + if idx := strings.Index(sessionKey, needle); idx >= 0 { + targetPlatform = p + platformName = p.Name() + sessionKey = sessionKey[idx+1:] // strip workspace prefix + break + } + } + } if targetPlatform == nil { return fmt.Errorf("platform %q not found for session %q", platformName, sessionKey) } @@ -998,6 +1012,20 @@ func (e *Engine) ExecuteHeartbeat(sessionKey, prompt string, silent bool) error break } } + // Fallback: in multi-workspace mode the stored session key may be prefixed + // with the workspace path (e.g. "/home/user/project:slack:C123:U456"). + // Search for a known platform name within the key and strip the prefix. + if targetPlatform == nil { + for _, p := range e.platforms { + needle := ":" + p.Name() + ":" + if idx := strings.Index(sessionKey, needle); idx >= 0 { + targetPlatform = p + platformName = p.Name() + sessionKey = sessionKey[idx+1:] // strip workspace prefix + break + } + } + } if targetPlatform == nil { return fmt.Errorf("platform %q not found for session %q", platformName, sessionKey) } diff --git a/core/engine_test.go b/core/engine_test.go index a75b539..a7387e7 100644 --- a/core/engine_test.go +++ b/core/engine_test.go @@ -8107,6 +8107,53 @@ func TestExecuteCronJob_ResolvesCronReplyTarget(t *testing.T) { } } +func TestExecuteCronJob_WorkspacePrefixedSessionKey(t *testing.T) { + dir := t.TempDir() + store, err := NewCronStore(dir) + if err != nil { + t.Fatalf("NewCronStore() error = %v", err) + } + scheduler := NewCronScheduler(store) + + platform := &stubCronReplyTargetPlatform{ + stubPlatformEngine: stubPlatformEngine{n: "slack"}, + } + agentSession := newResultAgentSession("done") + agent := &resultAgent{session: agentSession} + + e := NewEngine("test", agent, []Platform{platform}, "", LangEnglish) + defer e.cancel() + e.cronScheduler = scheduler + + // Simulate a session key that was stored with a workspace prefix + // (as happens in multi-workspace mode). + prefixedKey := "/home/user/workspace/myproject:slack:C123:U456" + job := &CronJob{ + ID: "job-ws", + SessionKey: prefixedKey, + Prompt: "daily standup", + Description: "Standup", + } + if err := store.Add(job); err != nil { + t.Fatalf("store.Add() error = %v", err) + } + + if err := e.ExecuteCronJob(job); err != nil { + t.Fatalf("ExecuteCronJob() with workspace-prefixed key error = %v", err) + } + + // The platform should have received the cron start notice and agent reply. + sent := platform.getSent() + if len(sent) < 1 { + t.Fatalf("expected at least one message sent to platform, got %d", len(sent)) + } + + // Stored session key must remain unchanged. + if job.SessionKey != prefixedKey { + t.Fatalf("job.SessionKey = %q, want unchanged %q", job.SessionKey, prefixedKey) + } +} + func TestExtractSessionKeyParts(t *testing.T) { tests := []struct { name string