agen-vektor 0.3.21 → 0.3.23

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
@@ -359,6 +359,7 @@ Every shell command is classified:
359
359
  | `vector-tui.methatech.eu.org` (VectorHead Free gateway) | Only if you pick the free tier; the shared provider key stays server-side, users never see it |
360
360
  | Brave / Exa / Serper | Only when you use the web-search tool with your own key |
361
361
  | `vector-tui.methatech.eu.org/v1/web-search` | Web search when you have NO search key of your own — the query is relayed by the gateway; your own key, when set, always takes precedence and bypasses the relay |
362
+ | `vector-tui.methatech.eu.org/v1/e2b/run` | `e2b_run` tool — run a command in a disposable cloud sandbox (E2B VM) when the local machine is too weak/restricted; relayed by the gateway, no E2B key needed client-side |
362
363
  | Raw model catalog (public JSON) | To populate the free-tier model list |
363
364
 
364
365
  **Stored locally** (all in `~/.vector/`): `config.json` (preferences, no secrets), `credentials.json` (only keys *you* enter, file mode 0600), sessions, memory, logs. Nothing else is written anywhere.
@@ -8,6 +8,7 @@ const search_1 = require("../tools/search");
8
8
  const shell_1 = require("../tools/shell");
9
9
  const git_1 = require("../tools/git");
10
10
  const web_1 = require("../tools/web");
11
+ const e2b_1 = require("../tools/e2b");
11
12
  const extras_1 = require("../tools/extras");
12
13
  const apply_patch_1 = require("../tools/apply-patch");
13
14
  const permissions_1 = require("../security/permissions");
@@ -59,6 +60,9 @@ class Agent {
59
60
  (0, shell_1.createShellTool)(),
60
61
  (0, git_1.createGitTool)(),
61
62
  ...(0, web_1.createWebTools)(),
63
+ // E2B cloud sandbox (via gateway relay): e2b_run / e2b_status —
64
+ // eksekusi di VM cloud utk perangkat lemah, zero-config (key di gateway).
65
+ ...(0, e2b_1.createE2bTools)(),
62
66
  // Freebuff-style extras: glob, read_subtree, task_completed,
63
67
  // suggest_followups, render_ui.
64
68
  ...(0, extras_1.createExtrasTools)(),
@@ -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
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createE2bTools = createE2bTools;
4
+ const credentials_1 = require("../config/credentials");
5
+ const free_tier_1 = require("../config/free-tier");
6
+ const GW_BASE = free_tier_1.FREE_GATEWAY_URL.replace(/\/v1$/, '');
7
+ // Poll interval env-overridable (test memakai VECTOR_E2B_POLL_MS=10).
8
+ const POLL_INTERVAL_MS = Math.max(10, Number(process.env.VECTOR_E2B_POLL_MS) || 3_000);
9
+ const POLL_MAX_MS = 320_000; // > E2B_MAX_TIMEOUT_MS worker (300s) + margin
10
+ const OUT_CAP = 20_000;
11
+ async function postRun(command, timeoutMs) {
12
+ const controller = new AbortController();
13
+ const timer = setTimeout(() => controller.abort(), 30_000);
14
+ try {
15
+ const res = await fetch(GW_BASE + '/v1/e2b/run', {
16
+ method: 'POST',
17
+ signal: controller.signal,
18
+ headers: { 'Content-Type': 'application/json', 'User-Agent': 'VectorHead-AI-Agent/0.1' },
19
+ body: JSON.stringify({ command, timeout_ms: timeoutMs }),
20
+ });
21
+ const data = (await res.json().catch(() => null));
22
+ return { res, data };
23
+ }
24
+ finally {
25
+ clearTimeout(timer);
26
+ }
27
+ }
28
+ async function fetchStatus(id) {
29
+ const controller = new AbortController();
30
+ const timer = setTimeout(() => controller.abort(), 20_000);
31
+ try {
32
+ const res = await fetch(GW_BASE + '/v1/e2b/status?id=' + encodeURIComponent(id), {
33
+ signal: controller.signal,
34
+ headers: { 'User-Agent': 'VectorHead-AI-Agent/0.1' },
35
+ });
36
+ const data = (await res.json().catch(() => null));
37
+ return { res, data };
38
+ }
39
+ finally {
40
+ clearTimeout(timer);
41
+ }
42
+ }
43
+ function formatResult(s) {
44
+ const exit = s.exitCode === null || s.exitCode === undefined ? '?' : s.exitCode;
45
+ let out = `exit ${exit}`;
46
+ if (s.stdout)
47
+ out += '\n' + s.stdout.slice(0, OUT_CAP);
48
+ if (s.stderr)
49
+ out += (s.stdout ? '\n[stderr]\n' : '') + s.stderr.slice(0, Math.min(OUT_CAP, 10_000));
50
+ return out;
51
+ }
52
+ async function pollUntilDone(id) {
53
+ const deadline = Date.now() + POLL_MAX_MS;
54
+ let last = null;
55
+ while (Date.now() < deadline) {
56
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
57
+ const { res, data } = await fetchStatus(id);
58
+ if (!res.ok || !data)
59
+ continue; // gesit — coba lagi sampai deadline
60
+ last = data;
61
+ if (data.status === 'done' || data.status === 'error')
62
+ return data;
63
+ }
64
+ return last;
65
+ }
66
+ function createE2bTools() {
67
+ return [
68
+ {
69
+ definition: {
70
+ name: 'e2b_run',
71
+ description: 'Run a shell command inside a disposable cloud sandbox (E2B VM via the VectorHead gateway relay) and wait for the result. Use when the local machine is too weak/restricted for a task — builds, installs, or tests that do not fit locally. Network is available in the sandbox. Returns exit code + stdout/stderr (this tool blocks until the command finishes; timeout default 120s, max 300s). Destructive commands (mkfs, fork bombs, pipe-to-shell) are rejected by the relay policy.',
72
+ parameters: {
73
+ type: 'object',
74
+ properties: {
75
+ command: { type: 'string', description: 'The shell command to run in the sandbox (bash -lc; use && to chain)' },
76
+ timeout_ms: { type: 'number', description: 'Timeout in ms (default 120000, max 300000)' },
77
+ },
78
+ required: ['command'],
79
+ },
80
+ },
81
+ async execute(args, ctx) {
82
+ const command = String(args.command || '').trim();
83
+ if (!command)
84
+ return { output: 'ERROR: provide a "command"' };
85
+ const timeoutMs = Math.min(300_000, Math.max(5_000, Number(args.timeout_ms) || 120_000));
86
+ ctx.onActivity?.('e2b', `sandbox: ${command.slice(0, 80)}`);
87
+ const { res, data } = await postRun(command, timeoutMs);
88
+ if (!res.ok || !data || !data.id) {
89
+ const why = data?.error ? ` — ${(0, credentials_1.redact)(data.error)}` : '';
90
+ return { output: `ERROR: e2b relay HTTP ${res.status}${why}`, summary: 'e2b relay error' };
91
+ }
92
+ const done = await pollUntilDone(data.id);
93
+ if (!done) {
94
+ return {
95
+ output: `ERROR: e2b job ${data.id} belum selesai dan polling kehabisan waktu — cek manual: curl '${GW_BASE}/v1/e2b/status?id=${data.id}'`,
96
+ summary: 'e2b polling timeout',
97
+ data: { id: data.id },
98
+ };
99
+ }
100
+ if (done.status === 'error') {
101
+ return {
102
+ output: `ERROR: e2b job failed${done.error ? ': ' + (0, credentials_1.redact)(done.error) : ''}`,
103
+ summary: 'e2b job error',
104
+ data: { id: data.id },
105
+ };
106
+ }
107
+ const out = formatResult(done);
108
+ return {
109
+ output: out,
110
+ summary: `e2b sandbox → exit ${done.exitCode ?? '?'}`,
111
+ data: { id: data.id, exitCode: done.exitCode, stdout: done.stdout, stderr: done.stderr },
112
+ };
113
+ },
114
+ },
115
+ {
116
+ definition: {
117
+ name: 'e2b_status',
118
+ description: 'Check the result of an E2B sandbox job by id (from e2b_run): status (running|done|error), exit code, stdout/stderr. Only needed when you kept an id and want the latest state without polling yourself.',
119
+ parameters: {
120
+ type: 'object',
121
+ properties: {
122
+ id: { type: 'string', description: 'Job id returned by e2b_run' },
123
+ },
124
+ required: ['id'],
125
+ },
126
+ },
127
+ async execute(args) {
128
+ const id = String(args.id || '').trim();
129
+ if (!/^[A-Za-z0-9-]{8,80}$/.test(id))
130
+ return { output: 'ERROR: invalid job id' };
131
+ const { res, data } = await fetchStatus(id);
132
+ if (!res.ok || !data) {
133
+ const why = data?.error ? ` — ${(0, credentials_1.redact)(data.error)}` : '';
134
+ return { output: `ERROR: e2b status HTTP ${res.status}${why}`, summary: 'e2b status error' };
135
+ }
136
+ return {
137
+ output: data.status === 'done' || data.status === 'error' ? formatResult(data) : `status: ${data.status}`,
138
+ summary: `e2b job ${id}: ${data.status}`,
139
+ data,
140
+ };
141
+ },
142
+ },
143
+ ];
144
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agen-vektor",
3
- "version": "0.3.21",
3
+ "version": "0.3.23",
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": {