| 1 | package control |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "reasonix/internal/attachment" |
| 6 | "strings" |
| 7 | |
| 8 | "reasonix/internal/sessioninbox" |
| 9 | ) |
| 10 | |
| 11 | func (c *Controller) freezeInboxReferences(ctx context.Context, submit string, explicit []string) (string, []string, []string, []ImageReferenceFailure) { |
| 12 | var line strings.Builder |
| 13 | line.WriteString(submit) |
| 14 | for _, path := range explicit { |
| 15 | path = strings.TrimSpace(path) |
| 16 | if path != "" { |
| 17 | line.WriteString(" @") |
| 18 | line.WriteString(EscapeRefPath(path)) |
| 19 | } |
| 20 | } |
| 21 | input := line.String() |
| 22 | if !c.HasRefs(input) { |
| 23 | return "", nil, nil, nil |
| 24 | } |
| 25 | resolved := c.resolveUnscopedRefsForTurn(ctx, input) |
| 26 | return resolved.block, resolved.images, resolved.errs, resolved.imageErrs |
| 27 | } |
| 28 | |
| 29 | func (c *Controller) freezeInboxEnvelopeReferences(ctx context.Context, env *sessioninbox.PromptEnvelope, submit string, explicit []string, attachments ...SubmissionAttachment) error { |
| 30 | var line strings.Builder |
| 31 | line.WriteString(submit) |
| 32 | for _, path := range explicit { |
| 33 | path = strings.TrimSpace(path) |
| 34 | if path != "" { |
| 35 | line.WriteString(" @") |
| 36 | line.WriteString(EscapeRefPath(path)) |
| 37 | } |
| 38 | } |
| 39 | frozen := map[string]*attachment.AttachmentRef{} |
| 40 | for alias, digest := range env.ImageSourceRefs { |
| 41 | for _, input := range env.ImageInputs { |
| 42 | if input.Attachment != nil && input.Attachment.Content.Digest == digest { |
| 43 | frozen[alias] = input.Attachment |
| 44 | break |
| 45 | } |
| 46 | } |
| 47 | } |
| 48 | var inherited []attachment.Source |
| 49 | if len(attachments) == 0 { |
| 50 | for _, id := range env.AttachmentIdentities { |
| 51 | key := "attachment:" + id |
| 52 | if ref := frozen[key]; ref != nil { |
| 53 | inherited = append(inherited, attachment.Source{Existing: ref, Path: key, DisplayName: ref.DisplayName}) |
| 54 | } |
| 55 | } |
| 56 | } |
| 57 | prepared, failures := c.prepareSubmissionImagesContext(ctx, SubmissionRequest{Input: line.String(), Attachments: attachments, frozenSources: frozen, inheritedSources: inherited}) |
| 58 | if len(failures) > 0 { |
| 59 | return ImageReferenceFailures(failures) |
| 60 | } |
| 61 | var imageErrs []ImageReferenceFailure |
| 62 | env.FrozenRefBlock, env.FrozenImages, env.ReferenceErrors, imageErrs = c.freezeInboxReferences(contextWithPreparedImageReferences(ctx, prepared), submit, explicit) |
| 63 | if len(imageErrs) > 0 { |
| 64 | return ImageReferenceFailures(imageErrs) |
| 65 | } |
| 66 | if len(prepared.inputs) > 0 { |
| 67 | env.ImageInputs = prepared.inputs |
| 68 | env.ImageSourceRefs = prepared.byPath |
| 69 | env.FrozenImages = nil |
| 70 | } else if len(env.ImageInputs) > 0 { |
| 71 | var sources []attachment.Source |
| 72 | for _, input := range env.ImageInputs { |
| 73 | if input.Attachment != nil { |
| 74 | sources = append(sources, attachment.Source{Existing: input.Attachment, DisplayName: input.Attachment.DisplayName}) |
| 75 | } |
| 76 | } |
| 77 | if _, err := c.attachmentService().PrepareBatch(ctx, sources); err != nil { |
| 78 | return ImageReferenceFailures(imageFailuresFromAttachment(err)) |
| 79 | } |
| 80 | } |
| 81 | return nil |
| 82 | } |
| 83 | |
| 84 | func (c *Controller) RefreshInboxReferences(id string) error { |
| 85 | st, err := c.ensureInbox() |
| 86 | if err != nil { |
| 87 | return err |
| 88 | } |
| 89 | _, env, err := st.ReadItem(id) |
| 90 | if err != nil { |
| 91 | return err |
| 92 | } |
| 93 | env.Refs = nil |
| 94 | if err := c.freezeInboxEnvelopeReferences(context.Background(), &env, env.SubmitText, env.ExplicitRefs); err != nil { |
| 95 | return err |
| 96 | } |
| 97 | _, err = st.UpdateItem(id, env) |
| 98 | if err == nil && len(env.ReferenceErrors) > 0 { |
| 99 | reason := strings.Join(env.ReferenceErrors, "; ") |
| 100 | err = st.SetState(id, sessioninbox.StateBlocked, reason) |
| 101 | _ = st.SetPaused(true) |
| 102 | } |
| 103 | return err |
| 104 | } |
| 105 | |
| 106 | func applyInboxReferences(env sessioninbox.PromptEnvelope) (submit string, images []string, blockReason string, err error) { |
| 107 | if len(env.ReferenceErrors) > 0 { |
| 108 | return "", nil, strings.Join(env.ReferenceErrors, "; "), nil |
| 109 | } |
| 110 | submit = env.SubmitText |
| 111 | images = append([]string(nil), env.FrozenImages...) |
| 112 | if len(env.ImageInputs) > 0 { |
| 113 | images = nil |
| 114 | } |
| 115 | if env.FrozenRefBlock != "" { |
| 116 | submit = "Referenced context:\n\n" + env.FrozenRefBlock + "\n\n" + submit |
| 117 | return submit, images, "", nil |
| 118 | } |
| 119 | if len(env.Refs) == 0 { |
| 120 | return submit, images, "", nil |
| 121 | } |
| 122 | legacyBlock, bodies, materializeErr := sessioninbox.MaterializeRefs(context.Background(), "", env.Refs) |
| 123 | if materializeErr != nil || legacyBlock != "" { |
| 124 | return "", nil, legacyBlock, materializeErr |
| 125 | } |
| 126 | return sessioninbox.ApplyFrozenRefs(submit, bodies), images, "", nil |
| 127 | } |
| 128 |