@@ -48,7 +48,8 @@ func (r *LocalRuntime) runHarnessAgent(ctx context.Context, sess *session.Sessio
4848
4949 // Harnesses own their context; run lifecycle hooks but do not forward injected instructions.
5050 r .executeTurnStartHooks (ctx , sess , a , events )
51- messages := harnessInputMessages (sess )
51+ harnessSessionID := harnessSessionIDFor (sess , a )
52+ messages := harnessInputMessages (sess , harnessSessionID )
5253 stop , msg , rewritten := r .executeBeforeLLMCallHooks (ctx , sess , a , modelID , 1 , messages )
5354 if stop {
5455 slog .WarnContext (ctx , "before_llm_call hook signalled run termination" ,
@@ -61,8 +62,6 @@ func (r *LocalRuntime) runHarnessAgent(ctx context.Context, sess *session.Sessio
6162 messages = rewritten
6263 }
6364 messages = r .applyBeforeLLMCallTransforms (ctx , sess , a , modelID , messages )
64-
65- harnessSessionID := harnessSessionIDFor (sess , a )
6665 prompt := strings .TrimSpace (harnessPrompt (messages ))
6766 if prompt == "" {
6867 msg := "cannot run external harness without a user prompt"
@@ -421,7 +420,22 @@ func (r *LocalRuntime) rememberHarnessSessionID(ctx context.Context, sess *sessi
421420 }
422421}
423422
424- func harnessInputMessages (sess * session.Session ) []chat.Message {
423+ // harnessInputMessages selects the messages to use as the harness prompt.
424+ // On resume (harnessSessionID != "") only the latest non-implicit user turn is
425+ // sent; the harness already holds prior context from its own session.
426+ // On a fresh session (harnessSessionID == "") the full delegated context is
427+ // included — system/task message plus all user messages — because the harness
428+ // receives no system prompt of its own and the task can only arrive via prompt.
429+ func harnessInputMessages (sess * session.Session , harnessSessionID string ) []chat.Message {
430+ if harnessSessionID != "" {
431+ return latestUserTurn (sess )
432+ }
433+ return freshHarnessMessages (sess )
434+ }
435+
436+ // latestUserTurn returns the most recent non-implicit user message, used on
437+ // resume turns where the harness already holds the preceding context.
438+ func latestUserTurn (sess * session.Session ) []chat.Message {
425439 for _ , item := range slices .Backward (sess .MessagesSnapshot ()) {
426440 if item .Message != nil && ! item .Message .Implicit && item .Message .Message .Role == chat .MessageRoleUser {
427441 return []chat.Message {item .Message .Message }
@@ -430,13 +444,46 @@ func harnessInputMessages(sess *session.Session) []chat.Message {
430444 return nil
431445}
432446
447+ // freshHarnessMessages collects the full delegated context for a new harness
448+ // session: all system and user messages (including implicit ones) in order.
449+ // Returns nil when the session contains no meaningful content — i.e. only an
450+ // implicit filler with no system/task message and no real user text — so the
451+ // empty-prompt guard in runHarnessAgent can still catch that degenerate case.
452+ func freshHarnessMessages (sess * session.Session ) []chat.Message {
453+ var msgs []chat.Message
454+ hasMeaningful := false
455+ for _ , item := range sess .MessagesSnapshot () {
456+ if item .Message == nil {
457+ continue
458+ }
459+ msg := item .Message .Message
460+ switch msg .Role {
461+ case chat .MessageRoleSystem :
462+ msgs = append (msgs , msg )
463+ hasMeaningful = true
464+ case chat .MessageRoleUser :
465+ msgs = append (msgs , msg )
466+ if ! item .Message .Implicit {
467+ hasMeaningful = true
468+ }
469+ }
470+ }
471+ if ! hasMeaningful {
472+ return nil
473+ }
474+ return msgs
475+ }
476+
477+ // harnessPrompt concatenates the content of all supplied messages (system and
478+ // user) to form the prompt string sent to the external harness binary.
433479func harnessPrompt (messages []chat.Message ) string {
434- for _ , message := range slices .Backward (messages ) {
435- if message .Role == chat .MessageRoleUser {
436- return harnessMessageContent (message )
480+ var parts []string
481+ for _ , message := range messages {
482+ if content := harnessMessageContent (message ); content != "" {
483+ parts = append (parts , content )
437484 }
438485 }
439- return ""
486+ return strings . Join ( parts , " \n \n " )
440487}
441488
442489func harnessMessageContent (msg chat.Message ) string {
0 commit comments