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>
121 lines
2.5 KiB
Go
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
|
|
}
|