Execute one parsed DSML tool call and return the text that will be appended as * the tool-role result. UI visualization already happened while streaming; this * function is only about side effects and the model-visible observation. */
| 7141 | int start_line = 0, end_line = 0, delta = 0; |
| 7142 | agent_old_new_line_effect(data, len, out, out_len, offset, remove_len, |
| 7143 | &start_line, &end_line, &delta); |
| 7144 | char *result = agent_edit_result(path, start_line, end_line, delta, |
| 7145 | out, out_len, kind); |
| 7146 | free(out); |
| 7147 | return result; |
| 7148 | } |
| 7149 | |
| 7150 | /* Old/new editing is intentionally conservative: exact old text must be unique. |
| 7151 | * For large replacements, old may contain one [upto] marker: the head must be |
| 7152 | * unique, and the tail must be unique after that head before the whole span is |
| 7153 | * replaced. */ |
| 7154 | static char *agent_tool_edit(agent_worker *w, const agent_tool_call *call) { |
| 7155 | const char *path = agent_tool_arg_value(call, "path"); |
| 7156 | if (!path || !path[0]) return xstrdup("Tool error: edit requires path\n"); |
| 7157 | const char *old = agent_tool_arg_value(call, "old"); |
| 7158 | const char *new_text = agent_tool_arg_value(call, "new"); |
| 7159 | if (!old || !old[0]) return xstrdup("Tool error: edit requires non-empty old text\n"); |
| 7160 | if (!new_text) return xstrdup("Tool error: edit requires new text\n"); |
| 7161 | |
| 7162 | char err[256]; |
| 7163 | char *data = NULL; |
| 7164 | size_t len = 0; |
| 7165 | if (agent_read_file_bytes(path, &data, &len, err, sizeof(err)) != 0) { |
| 7166 | agent_buf b = {0}; |
| 7167 | agent_buf_puts(&b, "Tool error: "); |
| 7168 | agent_buf_puts(&b, err); |
| 7169 | agent_buf_puts(&b, "\n"); |
| 7170 | return agent_buf_take(&b); |
| 7171 | } |
| 7172 | |
| 7173 | const char *match = NULL; |
| 7174 | size_t match_len = 0; |
| 7175 | bool anchored = false; |
| 7176 | bool allow_upto = w && w->cfg && w->cfg->edit_upto; |
| 7177 | if (!agent_edit_find_old_span(data, len, old, allow_upto, |
| 7178 | &match, &match_len, |
| 7179 | &anchored, err, sizeof(err))) |
| 7180 | { |
| 7181 | free(data); |
| 7182 | agent_buf b = {0}; |
| 7183 | agent_buf_puts(&b, "Tool error: "); |
| 7184 | agent_buf_puts(&b, err); |
| 7185 | agent_buf_puts(&b, "\n"); |
| 7186 | return agent_buf_take(&b); |
| 7187 | } |
| 7188 | |
| 7189 | char *result = agent_apply_file_splice(path, data, len, |
| 7190 | (size_t)(match - data), match_len, |
| 7191 | new_text, |
| 7192 | anchored ? "anchored old/new replacement" |
| 7193 | : "old/new replacement"); |
| 7194 | free(data); |
| 7195 | return result; |
| 7196 | } |
| 7197 | |
| 7198 | typedef struct { |
| 7199 | const char *query; |
| 7200 | const char *glob; |
no test coverage detected