picoclaw/pkg/agent/session_recorder.go
dj-oyu 7b70d70252 feat(tasks-2): implement subagent orchestration container model
Add Container Model for subagent↔conductor communication:
- ContainerMessage + channel-based escalation (ask_conductor, submit_plan)
- Deliberate vs Exploratory preset workflows with SubagentPlanState
- AnswerSubagentTool + ReviewSubagentPlanTool for conductor side
- SessionRecorder extensions (RecordQuestion, RecordPlanSubmit)
- Environment context injection (MEMORY.md extraction, plan context)
- Orchestration guidance with escalation tools documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 01:36:25 +09:00

121 lines
2.5 KiB
Go

package agent
import (
"github.com/sipeed/picoclaw/pkg/providers"
"github.com/sipeed/picoclaw/pkg/session"
"github.com/sipeed/picoclaw/pkg/tools"
)
// sessionRecorderImpl bridges tools.SessionRecorder → session.SessionStore.
type sessionRecorderImpl struct {
adapter *session.LegacyAdapter
}
var _ tools.SessionRecorder = (*sessionRecorderImpl)(nil)
func newSessionRecorder(adapter *session.LegacyAdapter) *sessionRecorderImpl {
return &sessionRecorderImpl{adapter: adapter}
}
func (r *sessionRecorderImpl) RecordFork(conductorKey, subagentKey, taskID, label string) error {
store := r.adapter.Store()
return store.Fork(conductorKey, subagentKey, &session.CreateOpts{
ForkTurnID: taskID,
Label: label,
})
}
func (r *sessionRecorderImpl) RecordSubagentTurn(subagentKey string, messages []providers.Message) error {
store := r.adapter.Store()
turn := &session.Turn{
Kind: session.TurnNormal,
Messages: messages,
}
return store.Append(subagentKey, turn)
}
func (r *sessionRecorderImpl) RecordCompletion(subagentKey, status, result string) error {
store := r.adapter.Store()
return store.SetStatus(subagentKey, status)
}
func (r *sessionRecorderImpl) RecordReport(conductorKey, subagentKey, senderID, content string) error {
store := r.adapter.Store()
turn := &session.Turn{
Kind: session.TurnReport,
OriginKey: subagentKey,
Author: senderID,
Messages: []providers.Message{
{Role: "user", Content: content},
},
}
if err := store.Append(conductorKey, turn); err != nil {
return err
}
// Advance LegacyAdapter's stored counter so flush loop doesn't double-write.
r.adapter.AdvanceStored(conductorKey, 1)
return nil
}
func (r *sessionRecorderImpl) RecordQuestion(conductorKey, subagentKey, taskID, question string) error {
store := r.adapter.Store()
turn := &session.Turn{
Kind: session.TurnQuestion,
OriginKey: subagentKey,
Author: taskID,
Messages: []providers.Message{
{Role: "user", Content: question},
},
}
if err := store.Append(conductorKey, turn); err != nil {
return err
}
r.adapter.AdvanceStored(conductorKey, 1)
return nil
}
func (r *sessionRecorderImpl) RecordPlanSubmit(conductorKey, subagentKey, taskID, planText string) error {
store := r.adapter.Store()
turn := &session.Turn{
Kind: session.TurnPlanSubmit,
OriginKey: subagentKey,
Author: taskID,
Messages: []providers.Message{
{Role: "user", Content: planText},
},
}
if err := store.Append(conductorKey, turn); err != nil {
return err
}
r.adapter.AdvanceStored(conductorKey, 1)
return nil
}