aether-code 0.43.3 → 0.43.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aether-code",
3
- "version": "0.43.3",
3
+ "version": "0.43.4",
4
4
  "description": "Uncensored AI coding agent for your terminal — Claude Code alternative with MCP support. Reads code, writes files, runs commands. Drives IDA Pro, Roblox Studio, Wireshark, Blender, and any MCP server. No refusal layer.",
5
5
  "homepage": "https://trynoguard.com",
6
6
  "repository": {
package/src/agent.js CHANGED
@@ -160,6 +160,8 @@ export async function runAgent({
160
160
  const announced = new Set();
161
161
  let lastWasText = false;
162
162
  const stripper = makeTokenStripper();
163
+ let textEmitted = false; // track if real content has been emitted this turn
164
+ const CHANNEL_WORD_SET = new Set(["thought","analysis","final","commentary","thinking","return","assistant","user","system","developer","tool"]);
163
165
 
164
166
  // Select skills for this turn against the current user prompt + any
165
167
  // paths the model has read so far. Prepend the matching skills' bodies
@@ -180,12 +182,19 @@ export async function runAgent({
180
182
  tools,
181
183
  model,
182
184
  onDelta: (text) => {
183
- // Buffered strip of leaked model channel/control tokens (which can
184
- // be split across stream chunks) before display.
185
185
  const clean = stripper.push(text);
186
186
  if (!clean) return;
187
- // Don't open a "● " bullet for leading whitespace (e.g. when a whole
188
- // turn's text was suppressed as a leak, leaving only a stray newline).
187
+ // Before any real content, strip stray channel name words that leak
188
+ // as the first tokens (e.g. "thought\nthought\n" from harmony).
189
+ if (!textEmitted) {
190
+ const stripped = clean.replace(/^[\s]*(thought|analysis|final|commentary|thinking|return|assistant|user|system|developer|tool)[\s]*/gi, "");
191
+ if (!stripped.trim()) return;
192
+ textEmitted = true;
193
+ stopSpin();
194
+ process.stdout.write("\n" + stripped);
195
+ lastWasText = true;
196
+ return;
197
+ }
189
198
  if (!lastWasText && !clean.trim()) return;
190
199
  stopSpin();
191
200
  if (!lastWasText) {
@@ -216,7 +225,10 @@ export async function runAgent({
216
225
  stopSpin(); // ensure it's cleared even if the turn produced no output
217
226
 
218
227
  // Flush any held-back partial token, then close the line.
219
- const tail = stripper.flush();
228
+ let tail = stripper.flush();
229
+ if (tail && !textEmitted) {
230
+ tail = tail.replace(/^[\s]*(thought|analysis|final|commentary|thinking|return|assistant|user|system|developer|tool)[\s]*/gi, "");
231
+ }
220
232
  if (tail && (lastWasText || tail.trim())) {
221
233
  if (!lastWasText) { process.stdout.write("\n"); lastWasText = true; }
222
234
  process.stdout.write(tail);
package/src/render.js CHANGED
@@ -199,9 +199,10 @@ export function toolSummary(name, result) {
199
199
  // (<|channel|>, <|tool_response|>, <channel|>) always have one. Real code like
200
200
  // <div>, Vec<T>, a < b has no pipe and is left untouched.
201
201
  const MODEL_TOKEN_RE = /<\|[a-z_]*\|?>|<[a-z_]+\|>/gi;
202
+ const CHANNEL_NAMES_RE = /^[ \t]*(thought|analysis|final|commentary|thinking|return|assistant|user|system|developer|tool)[ \t]*$/gim;
202
203
 
203
204
  export function stripModelTokens(text) {
204
- return text.replace(MODEL_TOKEN_RE, "");
205
+ return text.replace(MODEL_TOKEN_RE, "").replace(CHANNEL_NAMES_RE, "");
205
206
  }
206
207
 
207
208
  // Streaming-safe stripper: the leaked tokens (<|channel|>, <|tool_response|>, …)
@@ -212,7 +213,7 @@ export function makeTokenStripper() {
212
213
  let buf = "";
213
214
  return {
214
215
  push(text) {
215
- buf = (buf + text).replace(MODEL_TOKEN_RE, "");
216
+ buf = (buf + text).replace(MODEL_TOKEN_RE, "").replace(CHANNEL_NAMES_RE, "");
216
217
  const partial = buf.match(/<[|a-z_/]*$/i); // possible token start at the tail
217
218
  if (partial) {
218
219
  const emit = buf.slice(0, partial.index);
@@ -224,7 +225,7 @@ export function makeTokenStripper() {
224
225
  return emit;
225
226
  },
226
227
  flush() {
227
- const out = buf.replace(MODEL_TOKEN_RE, "");
228
+ const out = buf.replace(MODEL_TOKEN_RE, "").replace(CHANNEL_NAMES_RE, "");
228
229
  buf = "";
229
230
  return out;
230
231
  },