chocolatito-code 1.6.12 → 1.6.13
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/dist/agent/cortes.d.ts +41 -0
- package/dist/agent/cortes.js +52 -0
- package/dist/agent/loop.js +35 -0
- package/dist/config/engine.js +7 -0
- package/dist/index.js +18 -0
- package/dist/ui/comandos.js +1 -0
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LA CAJA NEGRA DE LOS CORTES
|
|
3
|
+
*
|
|
4
|
+
* DE DONDE SALE
|
|
5
|
+
*
|
|
6
|
+
* Cinco respuestas seguidas cortadas a mitad de frase, y de todas ellas no
|
|
7
|
+
* quedo NADA con lo que trabajar: el aviso salia por pantalla, el usuario
|
|
8
|
+
* seguia tecleando y el rastro se perdia con el scroll. Al dia siguiente, la
|
|
9
|
+
* unica pregunta que importaba -"corta siempre por el mismo sitio?"- no se
|
|
10
|
+
* podia contestar.
|
|
11
|
+
*
|
|
12
|
+
* Aqui se apunta lo justo para poder contestarla la proxima vez, que es un
|
|
13
|
+
* renglon por corte:
|
|
14
|
+
*
|
|
15
|
+
* 2026-09-12T14:02:11Z modelo=deepseek-chat ms=1840 trozos=37
|
|
16
|
+
* texto=214 razonamiento=0 aviso=motor
|
|
17
|
+
*
|
|
18
|
+
* Con eso se distingue lo que hoy no se distingue:
|
|
19
|
+
*
|
|
20
|
+
* - Siempre a los mismos milisegundos -> algo lo esta cortando por tiempo.
|
|
21
|
+
* - Siempre en los mismos bytes -> un limite de tamano por el camino.
|
|
22
|
+
* - Numeros dispares -> el motor de arriba, que va a rachas.
|
|
23
|
+
* - `aviso=motor` -> lo dijo el proxy: el corte viene del motor.
|
|
24
|
+
* - `aviso=mudo` -> nadie dijo nada: se murio el proxy por el camino.
|
|
25
|
+
*
|
|
26
|
+
* NO se guarda ni una palabra de la conversacion. Solo cuentas: tamanos,
|
|
27
|
+
* tiempos y el nombre del motor. Un diagnostico no vale una fuga.
|
|
28
|
+
*/
|
|
29
|
+
export interface Corte {
|
|
30
|
+
modelo: string;
|
|
31
|
+
ms: number;
|
|
32
|
+
trozos: number;
|
|
33
|
+
bytesTexto: number;
|
|
34
|
+
bytesRazonamiento: number;
|
|
35
|
+
/** "motor" si el proxy lo anuncio; "mudo" si el stream murio sin decir nada. */
|
|
36
|
+
aviso: "motor" | "mudo";
|
|
37
|
+
}
|
|
38
|
+
export declare function rutaDelRegistro(home?: string): string;
|
|
39
|
+
export declare function apuntarCorte(corte: Corte, home?: string): string | null;
|
|
40
|
+
/** Lo apuntado, de lo mas nuevo a lo mas viejo. Para /cortes. */
|
|
41
|
+
export declare function leerCortes(limite?: number, home?: string): string[];
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
export function rutaDelRegistro(home = os.homedir()) {
|
|
5
|
+
return path.join(home, ".chocolatito", "cortes.log");
|
|
6
|
+
}
|
|
7
|
+
/** Cuantos cortes se guardan. Mas atras que esto ya no dice nada nuevo. */
|
|
8
|
+
const MAXIMO_DE_LINEAS = 200;
|
|
9
|
+
export function apuntarCorte(corte, home) {
|
|
10
|
+
const ruta = rutaDelRegistro(home);
|
|
11
|
+
const linea = [
|
|
12
|
+
new Date().toISOString(),
|
|
13
|
+
`modelo=${corte.modelo}`,
|
|
14
|
+
`ms=${corte.ms}`,
|
|
15
|
+
`trozos=${corte.trozos}`,
|
|
16
|
+
`texto=${corte.bytesTexto}`,
|
|
17
|
+
`razonamiento=${corte.bytesRazonamiento}`,
|
|
18
|
+
`aviso=${corte.aviso}`,
|
|
19
|
+
].join(" ") + "\n";
|
|
20
|
+
try {
|
|
21
|
+
fs.mkdirSync(path.dirname(ruta), { recursive: true });
|
|
22
|
+
let previo = "";
|
|
23
|
+
try {
|
|
24
|
+
previo = fs.readFileSync(ruta, "utf-8");
|
|
25
|
+
}
|
|
26
|
+
catch { }
|
|
27
|
+
// Se recorta al vuelo: un registro que crece sin fin acaba siendo otro
|
|
28
|
+
// problema, y aqui lo viejo no aporta.
|
|
29
|
+
const lineas = (previo + linea).split("\n").filter((l) => l.trim().length > 0);
|
|
30
|
+
const recortado = lineas.slice(-MAXIMO_DE_LINEAS).join("\n") + "\n";
|
|
31
|
+
fs.writeFileSync(ruta, recortado, "utf-8");
|
|
32
|
+
return ruta;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Si no se puede escribir, no pasa nada: esto es un extra, no el aviso.
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Lo apuntado, de lo mas nuevo a lo mas viejo. Para /cortes. */
|
|
40
|
+
export function leerCortes(limite = 20, home) {
|
|
41
|
+
try {
|
|
42
|
+
return fs
|
|
43
|
+
.readFileSync(rutaDelRegistro(home), "utf-8")
|
|
44
|
+
.split("\n")
|
|
45
|
+
.filter((l) => l.trim().length > 0)
|
|
46
|
+
.slice(-limite)
|
|
47
|
+
.reverse();
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
package/dist/agent/loop.js
CHANGED
|
@@ -10,6 +10,7 @@ import { canRunInParallel } from "../tools/safety.js";
|
|
|
10
10
|
import { createEngineClient, explicarErrorDelMotor, motivoDeParadaDelError, } from "../config/engine.js";
|
|
11
11
|
import { anotarHuecosOcupados, esperarAlHueco, liberarHuecos, mensajeDeEspera, segundosDeEspera, } from "./huecos.js";
|
|
12
12
|
import { MarkdownStream } from "../ui/markdownStream.js";
|
|
13
|
+
import { apuntarCorte } from "./cortes.js";
|
|
13
14
|
import { checkQuota, recordUsage, mensajeDeCorte, quotaBadge } from "../config/quota.js";
|
|
14
15
|
import { ReasoningStream } from "../ui/reasoningStream.js";
|
|
15
16
|
import { PermissionManager, commandSignature, isDangerousCommand } from "../config/permissions.js";
|
|
@@ -630,6 +631,10 @@ export class AgentLoop {
|
|
|
630
631
|
* respuesta de un motor que no mande esa marca.
|
|
631
632
|
*/
|
|
632
633
|
let llegoElFinal = false;
|
|
634
|
+
// Para la caja negra: sin estos dos numeros, un corte no se puede
|
|
635
|
+
// comparar con el siguiente, y comparar es lo unico que da un patron.
|
|
636
|
+
let trozos = 0;
|
|
637
|
+
const empezoEn = Date.now();
|
|
633
638
|
const rawToolCalls = [];
|
|
634
639
|
const reasoningStream = new ReasoningStream();
|
|
635
640
|
// Por `salida`, no por stdout: mientras se escribe la respuesta el spinner
|
|
@@ -641,6 +646,7 @@ export class AgentLoop {
|
|
|
641
646
|
// fijo, asi que la respuesta sale por encima sin taparlo y el contador
|
|
642
647
|
// de segundos sigue a la vista mientras el modelo habla, que es
|
|
643
648
|
// exactamente cuando uno quiere saber cuanto lleva.
|
|
649
|
+
trozos++;
|
|
644
650
|
const delta = chunk.choices?.[0]?.delta;
|
|
645
651
|
// Handle real-time reasoning stream
|
|
646
652
|
if (delta?.reasoning_content) {
|
|
@@ -714,6 +720,23 @@ export class AgentLoop {
|
|
|
714
720
|
}
|
|
715
721
|
if (esDemasiadasEnCurso(streamErr))
|
|
716
722
|
anotarHuecosOcupados(streamErr?.error?.retry_after_s);
|
|
723
|
+
if (String(streamErr?.error?.code ?? "") === "motor_corto_la_respuesta") {
|
|
724
|
+
apuntarCorte({
|
|
725
|
+
modelo: this.currentEspectro.underlyingModel,
|
|
726
|
+
ms: Date.now() - empezoEn,
|
|
727
|
+
trozos,
|
|
728
|
+
bytesTexto: Buffer.byteLength(content, "utf8"),
|
|
729
|
+
bytesRazonamiento: Buffer.byteLength(reasoning, "utf8"),
|
|
730
|
+
aviso: "motor",
|
|
731
|
+
});
|
|
732
|
+
// Lo que llego es trabajo util aunque este a medias.
|
|
733
|
+
if (content.trim()) {
|
|
734
|
+
this.messages.push({
|
|
735
|
+
role: "assistant",
|
|
736
|
+
content: `${content}\n\n[Aqui se corto: el motor cerro la respuesta sin terminarla.]`,
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
}
|
|
717
740
|
const claroStream = explicarErrorDelMotor(streamErr);
|
|
718
741
|
if (claroStream) {
|
|
719
742
|
salida.linea(chalk.yellow(`
|
|
@@ -807,11 +830,23 @@ export class AgentLoop {
|
|
|
807
830
|
: reasoning.trim()
|
|
808
831
|
? "Se corto antes de escribir nada: solo llego el razonamiento."
|
|
809
832
|
: "Se corto antes de que llegara nada.";
|
|
833
|
+
// Se apunta antes de hablar: si el usuario vuelve a teclear, el aviso se
|
|
834
|
+
// pierde con el scroll, pero el renglon se queda.
|
|
835
|
+
const registro = apuntarCorte({
|
|
836
|
+
modelo: this.currentEspectro.underlyingModel,
|
|
837
|
+
ms: Date.now() - empezoEn,
|
|
838
|
+
trozos,
|
|
839
|
+
bytesTexto: Buffer.byteLength(content, "utf8"),
|
|
840
|
+
bytesRazonamiento: Buffer.byteLength(reasoning, "utf8"),
|
|
841
|
+
aviso: "mudo",
|
|
842
|
+
});
|
|
810
843
|
const aviso = "La respuesta se corto: la conexion con el motor se cerro sin decir por que (no llego " +
|
|
811
844
|
`ni el motivo de fin ni el recuento del gasto). ${hastaDonde} ` +
|
|
812
845
|
"No lo reintento solo porque cada intento ocupa uno de los cuatro huecos de tu licencia " +
|
|
813
846
|
"durante varios minutos; vuelve a pedirmelo y sigo.";
|
|
814
847
|
salida.linea(chalk.yellow(`\n ⚠ ${aviso}\n`));
|
|
848
|
+
if (registro)
|
|
849
|
+
salida.linea(chalk.gray(` (apuntado en ${registro} — /cortes para verlos)\n`));
|
|
815
850
|
return parada("error-motor", aviso);
|
|
816
851
|
}
|
|
817
852
|
// El modelo se quedo sin presupuesto de salida y no llego a pedir nada.
|
package/dist/config/engine.js
CHANGED
|
@@ -125,6 +125,13 @@ export function explicarErrorDelMotor(err) {
|
|
|
125
125
|
cuando +
|
|
126
126
|
" No es un problema de tu conexion: si interrumpiste una tarea, su hueco se libera solo.");
|
|
127
127
|
}
|
|
128
|
+
// El proxy avisa cuando el motor cierra la respuesta sin terminarla. Es el
|
|
129
|
+
// unico caso en el que se puede decir de QUIEN fue el corte: el cliente, desde
|
|
130
|
+
// fuera, solo ve un cuerpo que se acaba, igual que uno que termino bien.
|
|
131
|
+
if (String(err?.error?.code ?? "") === "motor_corto_la_respuesta") {
|
|
132
|
+
return ((mensaje || "El motor cerro la respuesta antes de terminarla.") +
|
|
133
|
+
" Lo que llego se queda arriba; vuelve a pedirlo y normalmente sale a la primera.");
|
|
134
|
+
}
|
|
128
135
|
if (tipo === "cuota") {
|
|
129
136
|
return mensaje || "Has agotado la cuota de uso incluida en tu suscripción.";
|
|
130
137
|
}
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ import { SessionManager } from "./sessions/manager.js";
|
|
|
33
33
|
import { prepararRestauracion, formatearOpcionesDeSesion } from "./sessions/resume.js";
|
|
34
34
|
import { auditGitDiff, formatAuditReport } from "./tools/gitAudit.js";
|
|
35
35
|
import { instalarKit, listarKits, quitarKit, crearKit } from "./kits/manager.js";
|
|
36
|
+
import { leerCortes, rutaDelRegistro } from "./agent/cortes.js";
|
|
36
37
|
import { globalUndoManager } from "./agent/undoManager.js";
|
|
37
38
|
import { setAutoApprove } from "./ui/permissionPrompt.js";
|
|
38
39
|
import { onModeChange, getMode, modeAnnouncement } from "./ui/modes.js";
|
|
@@ -616,6 +617,23 @@ ${sessionData}
|
|
|
616
617
|
console.log("");
|
|
617
618
|
continue;
|
|
618
619
|
}
|
|
620
|
+
if (trimmed === "/cortes") {
|
|
621
|
+
const lineas = leerCortes(20);
|
|
622
|
+
console.log(chalk.bold.hex("#D97757")("\n✂ RESPUESTAS QUE SE CORTARON"));
|
|
623
|
+
if (lineas.length === 0) {
|
|
624
|
+
console.log(chalk.gray(" Ninguna. Bien.\n"));
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
console.log(chalk.gray(` ${rutaDelRegistro()}\n`));
|
|
628
|
+
for (const l of lineas)
|
|
629
|
+
console.log(" " + chalk.gray(l));
|
|
630
|
+
console.log(chalk.gray("\n aviso=motor -> lo dijo el proxy: el corte viene del motor de arriba." +
|
|
631
|
+
"\n aviso=mudo -> nadie dijo nada: se cayo la conexion o el proxy por el camino." +
|
|
632
|
+
"\n Si ms y trozos salen casi iguales en todos, algo corta por tiempo o tamano;" +
|
|
633
|
+
"\n si salen dispares, es el motor y va a rachas.\n"));
|
|
634
|
+
}
|
|
635
|
+
continue;
|
|
636
|
+
}
|
|
619
637
|
if (trimmed === "/kit" || trimmed.startsWith("/kit ")) {
|
|
620
638
|
const partes = trimmed.split(/s+/).slice(1);
|
|
621
639
|
const accion = (partes[0] || "").toLowerCase();
|
package/dist/ui/comandos.js
CHANGED
|
@@ -32,6 +32,7 @@ export const SLASH_COMMANDS = [
|
|
|
32
32
|
{ name: "/skills", description: "Ver skills disponibles y cómo usarlos" },
|
|
33
33
|
{ name: "/kit", description: "Montar tu Chocolatito: instalar, crear o quitar kits (skills + reglas + MCP)", args: "[instalar|crear|quitar] [nombre]" },
|
|
34
34
|
{ name: "/aprende", description: "Guardar lo que acabamos de hacer como skill reutilizable", args: "[en pocas palabras]" },
|
|
35
|
+
{ name: "/cortes", description: "Ver las respuestas que se cortaron y de dónde vino el corte" },
|
|
35
36
|
{ name: "/memory", description: "Consultar la memoria persistente transversal (~/.chocolatito/memory/)" },
|
|
36
37
|
{ name: "/effort", description: "Regular potencia del motor (1.0, 1.5, 2.0, 2.5)", args: "[nivel]" },
|
|
37
38
|
{ name: "/goal", description: "Ejecutar meta compleja autónoma con auto-corrección", args: "<meta>" },
|
package/extension/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"minimum_chrome_version": "102",
|
|
4
4
|
"name": "Chocolatito Code",
|
|
5
|
-
"version": "1.6.
|
|
5
|
+
"version": "1.6.13",
|
|
6
6
|
"description": "Deja que Chocolatito Code trabaje dentro de tu Chrome, en segundo plano y con tus sesiones ya iniciadas.",
|
|
7
7
|
"permissions": [
|
|
8
8
|
"tabs",
|
package/package.json
CHANGED