agen-vektor 0.3.22 → 0.3.24

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/README.md CHANGED
@@ -71,6 +71,20 @@ npm install -g agen-vektor
71
71
  vector
72
72
  ```
73
73
 
74
+ ### Update (existing install)
75
+
76
+ Sudah install VectorHead sebelumnya? Tinggal update paket global npm-nya lalu
77
+ verifikasi versi:
78
+
79
+ ```bash
80
+ npm install -g agen-vektor@latest
81
+ vector --version
82
+ ```
83
+
84
+ Config lama aman: model default yang sudah usang (`glm-5.3-flash` dst.)
85
+ otomatis termigrasi saat aplikasi pertama kali dijalankan setelah update.
86
+ Tidak ada langkah manual.
87
+
74
88
  ### From source
75
89
 
76
90
  ```bash
@@ -59,16 +59,16 @@ function freeTierEnabled() {
59
59
  /** Provider id used in config.provider / providers map. */
60
60
  exports.FREE_PROVIDER_ID = 'vectorhead-free';
61
61
  /**
62
- * Default free model (cheap, gateway allowlist-friendly). 2026-09-07: BACK
63
- * to `glm-5.3-flash` — the gateway allowlist flipped back from the 7-model
64
- * Claude window to the api.b.ai set: live GET /v1/models now serves hy3,
65
- * glm-5.3-flash, qwen3.8-max, mimo-v2.5, so `claude-sonnet-4-6` (the
66
- * 2026-09-06-malam default) is the id that would 404 on fresh installs.
67
- * glm-5.3-flash verified live end-to-end via the relay (chat 200). History
68
- * of the same trap: `deepseek-ai/DeepSeek-V4-Flash` (first default), then
69
- * claude-sonnet-4-6, now glm-5.3-flash again.
62
+ * Default free model (cheap, gateway allowlist-friendly). 2026-09-13:
63
+ * `kilo-auto/free` — gateway di-cutover ke Kilo Code (sesi "pindahkan
64
+ * provider di dashboard admin pilih kilo code"); id lama `glm-5.3-flash`
65
+ * TIDAK ada di katalog Kilo → onboarding user baru 404 "model not found"
66
+ * (ketemu via smoke test 0.3.22). Router Kilo terverifikasi live end-to-end
67
+ * (chat 200, dijawab dots-3-note-preview:free). Riwayat trap yang sama:
68
+ * `deepseek-ai/DeepSeek-V4-Flash` → `claude-sonnet-4-6` → `glm-5.3-flash`
69
+ * (dua flip 2026-09-06/07) → sekarang kilo-auto/free.
70
70
  */
71
- exports.FREE_DEFAULT_MODEL = 'glm-5.3-flash';
71
+ exports.FREE_DEFAULT_MODEL = 'kilo-auto/free';
72
72
  /** The providers-map entry for the free tier (idempotent — same shape always). */
73
73
  function freeProviderDef() {
74
74
  // When a catalog has been fetched in this process, the gateway URL and
@@ -94,18 +94,21 @@ function freeProviderDef() {
94
94
  },
95
95
  model: exports.FREE_DEFAULT_MODEL,
96
96
  models: Object.keys(models).length > 0 ? models : {
97
- [exports.FREE_DEFAULT_MODEL]: { name: 'GLM 5.3 Flash (free)' },
97
+ [exports.FREE_DEFAULT_MODEL]: { name: 'Kilo Auto (free)' },
98
98
  },
99
99
  };
100
100
  }
101
101
  /**
102
102
  * Stale first-run defaults — migrated to FREE_DEFAULT_MODEL once on
103
103
  * ensureFreeProvider (only while the free provider is active). glm-5.3-flash
104
- * itself was legacy during the 2026-09-06 Claude window; since 2026-09-07 it
105
- * is the default again, so the stale set is the original deepseek default
106
- * plus that window's claude default (no longer in the gateway allowlist).
104
+ * jadi legacy 2026-09-13 (cutover Kilo — id tidak ada di katalog Kilo);
105
+ * deepseek default pertama; claude-sonnet-4-6 window 2026-09-06.
107
106
  */
108
- exports.FREE_LEGACY_MODELS = ['deepseek-ai/DeepSeek-V4-Flash', 'claude-sonnet-4-6'];
107
+ exports.FREE_LEGACY_MODELS = [
108
+ 'deepseek-ai/DeepSeek-V4-Flash',
109
+ 'claude-sonnet-4-6',
110
+ 'glm-5.3-flash',
111
+ ];
109
112
  /**
110
113
  * Ensure the free provider entry exists in the config's providers map
111
114
  * (create or repair — never touches unrelated providers). Also migrates an
package/dist/tools/e2b.js CHANGED
@@ -140,5 +140,204 @@ function createE2bTools() {
140
140
  };
141
141
  },
142
142
  },
143
+ {
144
+ // ------------------------------------------------------------------
145
+ // CLOUD SESSION: sandbox persisten utk alur multi-langkah. Workflow
146
+ // browser testing (Chromium/Playwright headless DIJALANKAN DI SANDBOX):
147
+ // 1. e2b_cloud {action:'create'}
148
+ // 2. e2b_upload per file proyek (path absolut /home/user/...)
149
+ // 3. e2b_exec: npm install && npx playwright install --with-deps
150
+ // chromium (SEKALI per session — install tersimpan di sandbox)
151
+ // 4. e2b_exec: node test.mjs (script playwright; screenshot →
152
+ // /home/user/shots/...). ⚠️ chromium.launch WAJIB args:
153
+ // --no-sandbox --disable-dev-shm-usage --disable-gpu
154
+ // --js-flags=--jitless (tanpa itu → Target crashed: V8 CodeRange
155
+ // OOM di VM kecil; --single-process dilarang — exit 13)
156
+ // 5. poll e2b_exec (jobId) sampai status done → baca output/exit
157
+ // 6. e2b_download artefak (screenshot/log) bila perlu
158
+ // 7. e2b_cloud {action:'close'} — WAJIB di akhir (kredit shared)
159
+ // ------------------------------------------------------------------
160
+ definition: {
161
+ name: 'e2b_cloud',
162
+ description: 'Manage a persistent cloud sandbox session (E2B VM) for multi-step work: create a session, or close it when finished. Unlike e2b_run (one-shot), files and installed packages PERSIST across calls within the session — use for project upload + build/test + browser testing (Chromium/Playwright run headless inside the sandbox). ALWAYS close the session when done (shared quota). Browser testing REQUIRES launching Chromium with: --no-sandbox --disable-dev-shm-usage --disable-gpu --js-flags=--jitless (without --jitless the page crashes: V8 CodeRange OOM on the small E2B VM). Playwright is NOT preinstalled — run: npm i playwright && npx playwright install --with-deps chromium (1-3 min, persists for the session).',
163
+ parameters: {
164
+ type: 'object',
165
+ properties: {
166
+ action: { type: 'string', enum: ['create', 'close'], description: 'create = new persistent sandbox; close = kill it' },
167
+ id: { type: 'string', description: 'Session id (required for close)' },
168
+ ttl_s: { type: 'number', description: 'Session lifetime in seconds for create (default 1800, max 3600)' },
169
+ },
170
+ required: ['action'],
171
+ },
172
+ },
173
+ async execute(args, ctx) {
174
+ const action = String(args.action || '');
175
+ if (action === 'create') {
176
+ const ttl = Math.min(3600, Math.max(120, Number(args.ttl_s) || 1800));
177
+ ctx.onActivity?.('e2b', `cloud session create (${ttl}s)`);
178
+ const res = await fetch(GW_BASE + '/v1/e2b/session', {
179
+ method: 'POST',
180
+ headers: { 'Content-Type': 'application/json', 'User-Agent': 'VectorHead-AI-Agent/0.1' },
181
+ body: JSON.stringify({ ttl_s: ttl }),
182
+ signal: AbortSignal.timeout(60_000),
183
+ });
184
+ const data = (await res.json().catch(() => null));
185
+ if (!res.ok || !data?.id) {
186
+ const why = data?.error ? ` — ${(0, credentials_1.redact)(data.error)}` : '';
187
+ return { output: `ERROR: e2b session HTTP ${res.status}${why}`, summary: 'e2b session error' };
188
+ }
189
+ return {
190
+ output: `session created: ${data.id} (ttl ${data.ttl}s)\nNext: e2b_upload files → e2b_exec commands (poll jobId) → e2b_download artifacts → e2b_cloud close.\nSession id: ${data.id}`,
191
+ summary: `e2b session ${data.id}`,
192
+ data,
193
+ };
194
+ }
195
+ if (action === 'close') {
196
+ const id = String(args.id || '');
197
+ if (!/^[A-Za-z0-9-]{8,80}$/.test(id))
198
+ return { output: 'ERROR: provide the session "id"' };
199
+ ctx.onActivity?.('e2b', `cloud session close ${id}`);
200
+ const res = await fetch(GW_BASE + '/v1/e2b/close?id=' + encodeURIComponent(id), { method: 'POST', signal: AbortSignal.timeout(30_000) });
201
+ if (!res.ok)
202
+ return { output: `ERROR: e2b close HTTP ${res.status}`, summary: 'e2b close error' };
203
+ return { output: `session ${id} closed (sandbox killed).`, summary: `e2b session ${id} closed` };
204
+ }
205
+ return { output: 'ERROR: action harus create|close' };
206
+ },
207
+ },
208
+ {
209
+ definition: {
210
+ name: 'e2b_upload',
211
+ description: 'Upload one file into a persistent e2b cloud session (from e2b_cloud create). Content is sent as base64 — read local files first (size limit 20 MB). Target path must be absolute under /home/user/ or /tmp/.',
212
+ parameters: {
213
+ type: 'object',
214
+ properties: {
215
+ id: { type: 'string', description: 'Session id from e2b_cloud create' },
216
+ path: { type: 'string', description: 'Absolute path in the sandbox, e.g. /home/user/app/package.json' },
217
+ content_b64: { type: 'string', description: 'File content encoded as base64' },
218
+ },
219
+ required: ['id', 'path', 'content_b64'],
220
+ },
221
+ },
222
+ async execute(args) {
223
+ const id = String(args.id || '');
224
+ const path = String(args.path || '').trim();
225
+ const content = String(args.content_b64 || '');
226
+ if (!/^[A-Za-z0-9-]{8,80}$/.test(id))
227
+ return { output: 'ERROR: invalid session id' };
228
+ if (!/^\/(home\/user\/|tmp\/)/.test(path) || path.includes('..'))
229
+ return { output: 'ERROR: path harus absolut di bawah /home/user/ atau /tmp/' };
230
+ if (!content)
231
+ return { output: 'ERROR: content_b64 kosong' };
232
+ const res = await fetch(GW_BASE + '/v1/e2b/upload?id=' + encodeURIComponent(id), {
233
+ method: 'POST',
234
+ headers: { 'Content-Type': 'application/json', 'User-Agent': 'VectorHead-AI-Agent/0.1' },
235
+ body: JSON.stringify({ path, content_b64: content }),
236
+ signal: AbortSignal.timeout(120_000),
237
+ });
238
+ const data = (await res.json().catch(() => null));
239
+ if (!res.ok || !data?.ok) {
240
+ const why = data?.error ? ` — ${(0, credentials_1.redact)(data.error)}` : '';
241
+ return { output: `ERROR: e2b upload HTTP ${res.status}${why}`, summary: 'e2b upload error' };
242
+ }
243
+ return { output: `uploaded: ${path} (${data.size} bytes)`, summary: `e2b upload ${path}` };
244
+ },
245
+ },
246
+ {
247
+ definition: {
248
+ name: 'e2b_exec',
249
+ description: 'Run a command DETACHED inside a persistent e2b cloud session and WAIT for it to finish (polls automatically; long-running OK — e.g. npm install, npx playwright install --with-deps chromium, node test.mjs). Returns stdout/stderr + exit code. Use e2b_run for one-shot no-session commands instead. For Playwright scripts: launch Chromium with --no-sandbox --disable-dev-shm-usage --disable-gpu --js-flags=--jitless (else "Target crashed" — V8 CodeRange OOM on the small E2B VM) and NEVER --single-process (hard crash).',
250
+ parameters: {
251
+ type: 'object',
252
+ properties: {
253
+ id: { type: 'string', description: 'Session id from e2b_cloud create' },
254
+ command: { type: 'string', description: 'Shell command to run in the sandbox (bash -lc)' },
255
+ },
256
+ required: ['id', 'command'],
257
+ },
258
+ },
259
+ async execute(args, ctx) {
260
+ const id = String(args.id || '');
261
+ const command = String(args.command || '').trim();
262
+ if (!/^[A-Za-z0-9-]{8,80}$/.test(id))
263
+ return { output: 'ERROR: invalid session id' };
264
+ if (!command)
265
+ return { output: 'ERROR: provide a "command"' };
266
+ ctx.onActivity?.('e2b', `exec: ${command.slice(0, 80)}`);
267
+ const start = await fetch(GW_BASE + '/v1/e2b/exec?id=' + encodeURIComponent(id), {
268
+ method: 'POST',
269
+ headers: { 'Content-Type': 'application/json', 'User-Agent': 'VectorHead-AI-Agent/0.1' },
270
+ body: JSON.stringify({ command }),
271
+ signal: AbortSignal.timeout(30_000),
272
+ });
273
+ const startData = (await start.json().catch(() => null));
274
+ if (!start.ok || !startData?.jobId) {
275
+ const why = startData?.error ? ` — ${(0, credentials_1.redact)(startData.error)}` : '';
276
+ return { output: `ERROR: e2b exec HTTP ${start.status}${why}`, summary: 'e2b exec error' };
277
+ }
278
+ const jobId = startData.jobId;
279
+ const deadline = Date.now() + POLL_MAX_MS;
280
+ let last = '';
281
+ while (Date.now() < deadline) {
282
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
283
+ const pr = await fetch(GW_BASE + '/v1/e2b/job?id=' + encodeURIComponent(id) + '&job=' + encodeURIComponent(jobId), {
284
+ signal: AbortSignal.timeout(30_000),
285
+ headers: { 'User-Agent': 'VectorHead-AI-Agent/0.1' },
286
+ });
287
+ const pj = (await pr.json().catch(() => null));
288
+ if (!pj)
289
+ continue;
290
+ if (pj.status === 'done') {
291
+ const out = `exit ${pj.exitCode ?? '?'}\n${(pj.output || '').slice(0, OUT_CAP)}`;
292
+ return { output: out, summary: `e2b exec → exit ${pj.exitCode ?? '?'}`, data: { jobId, exitCode: pj.exitCode, output: pj.output } };
293
+ }
294
+ if (pj.status === 'starting' || pj.status === 'running') {
295
+ last = pj.output || last;
296
+ continue;
297
+ }
298
+ return { output: `ERROR: e2b job error: ${pj.error || pj.status}`, summary: 'e2b job error', data: { jobId } };
299
+ }
300
+ return { output: `TIMEOUT menunggu job ${jobId}. Log terakhir:\n${last.slice(0, 4000)}`, summary: 'e2b exec polling timeout', data: { jobId } };
301
+ },
302
+ },
303
+ {
304
+ definition: {
305
+ name: 'e2b_download',
306
+ description: 'Download a file (artifact) from a persistent e2b cloud session — e.g. Playwright screenshots (/home/user/shots/*.png), logs, or build outputs. Returns the file content as base64 (prefix data with file type when writing locally, e.g. data:image/png;base64,).',
307
+ parameters: {
308
+ type: 'object',
309
+ properties: {
310
+ id: { type: 'string', description: 'Session id from e2b_cloud create' },
311
+ path: { type: 'string', description: 'Absolute path of the file in the sandbox' },
312
+ },
313
+ required: ['id', 'path'],
314
+ },
315
+ },
316
+ async execute(args) {
317
+ const id = String(args.id || '');
318
+ const path = String(args.path || '').trim();
319
+ if (!/^[A-Za-z0-9-]{8,80}$/.test(id))
320
+ return { output: 'ERROR: invalid session id' };
321
+ if (!/^\/(home\/user\/|tmp\/)/.test(path) || path.includes('..'))
322
+ return { output: 'ERROR: path harus absolut di bawah /home/user/ atau /tmp/' };
323
+ const res = await fetch(GW_BASE + '/v1/e2b/file?id=' + encodeURIComponent(id) + '&path=' + encodeURIComponent(path), {
324
+ signal: AbortSignal.timeout(60_000),
325
+ headers: { 'User-Agent': 'VectorHead-AI-Agent/0.1' },
326
+ });
327
+ if (!res.ok) {
328
+ const t = await res.text().catch(() => '');
329
+ return { output: `ERROR: e2b download HTTP ${res.status}${t ? ' — ' + (0, credentials_1.redact)(t.slice(0, 200)) : ''}`, summary: 'e2b download error' };
330
+ }
331
+ const buf = new Uint8Array(await res.arrayBuffer());
332
+ let bin = '';
333
+ for (let i = 0; i < buf.length; i++)
334
+ bin += String.fromCharCode(buf[i]);
335
+ return {
336
+ output: `downloaded ${path} (${buf.length} bytes)\nbase64:\n${btoa(bin)}`,
337
+ summary: `e2b download ${path} (${buf.length}B)`,
338
+ data: { path, size: buf.length, base64: btoa(bin) },
339
+ };
340
+ },
341
+ },
143
342
  ];
144
343
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agen-vektor",
3
- "version": "0.3.22",
3
+ "version": "0.3.24",
4
4
  "description": "VectorHead (agen-vektor) — AI Coding Agent CLI/TUI for Linux & Termux. Multi-provider, tool calling, session, permission system.",
5
5
  "type": "commonjs",
6
6
  "bin": {