@mcp-graph-workflow/agent-graph-flow 0.2.0 → 0.3.0
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/cli/index.js +205 -9
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { randomUUID, randomBytes, createHash } from 'crypto';
|
|
|
11
11
|
import os, { homedir } from 'os';
|
|
12
12
|
import { stat, readFile, access, constants } from 'fs/promises';
|
|
13
13
|
import { execFile, execSync, spawn } from 'child_process';
|
|
14
|
-
import { render, useApp, Box, Text } from 'ink';
|
|
14
|
+
import { render, useApp, useInput, Box, Text } from 'ink';
|
|
15
15
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
16
16
|
import { createElement, useState, useCallback, useEffect } from 'react';
|
|
17
17
|
import TextInput from 'ink-text-input';
|
|
@@ -6839,9 +6839,15 @@ var init_copilot_sdk_adapter = __esm({
|
|
|
6839
6839
|
init_logger();
|
|
6840
6840
|
log22 = createLogger({ layer: "core", source: "copilot-sdk-adapter.ts" });
|
|
6841
6841
|
ModelAdapterError = class extends McpGraphError {
|
|
6842
|
-
|
|
6842
|
+
/** HTTP status quando a falha veio de uma resposta do provider (p/ classifyLlmError). */
|
|
6843
|
+
status;
|
|
6844
|
+
/** Espera sugerida (ms), derivada de `retry-after` em respostas 429. */
|
|
6845
|
+
retryAfterMs;
|
|
6846
|
+
constructor(message, opts = {}) {
|
|
6843
6847
|
super(`Model adapter error: ${message}`);
|
|
6844
6848
|
this.name = "ModelAdapterError";
|
|
6849
|
+
this.status = opts.status;
|
|
6850
|
+
this.retryAfterMs = opts.retryAfterMs;
|
|
6845
6851
|
}
|
|
6846
6852
|
};
|
|
6847
6853
|
CopilotSdkAdapter = class {
|
|
@@ -6932,9 +6938,16 @@ var init_copilot_api_adapter = __esm({
|
|
|
6932
6938
|
}
|
|
6933
6939
|
if (!res.ok) {
|
|
6934
6940
|
if (res.status === 401 || res.status === 403) {
|
|
6935
|
-
throw new ModelAdapterError("token Copilot expirado/inv\xE1lido (401/403) \u2014 rode `agf login` novamente."
|
|
6941
|
+
throw new ModelAdapterError("token Copilot expirado/inv\xE1lido (401/403) \u2014 rode `agf login` novamente.", {
|
|
6942
|
+
status: res.status
|
|
6943
|
+
});
|
|
6936
6944
|
}
|
|
6937
|
-
|
|
6945
|
+
const retryAfterRaw = res.headers?.get?.("retry-after");
|
|
6946
|
+
const retryAfterMs = retryAfterRaw && Number.isFinite(Number(retryAfterRaw)) ? Math.round(Number(retryAfterRaw) * 1e3) : void 0;
|
|
6947
|
+
throw new ModelAdapterError(`API do Copilot retornou ${res.status}: ${(await res.text()).slice(0, 200)}`, {
|
|
6948
|
+
status: res.status,
|
|
6949
|
+
retryAfterMs
|
|
6950
|
+
});
|
|
6938
6951
|
}
|
|
6939
6952
|
const body = await res.json();
|
|
6940
6953
|
const text = body.choices?.[0]?.message?.content;
|
|
@@ -7159,7 +7172,83 @@ var init_plan_parser = __esm({
|
|
|
7159
7172
|
}
|
|
7160
7173
|
});
|
|
7161
7174
|
|
|
7175
|
+
// src/core/model-hub/llm-error.ts
|
|
7176
|
+
function readHeader(headers, name) {
|
|
7177
|
+
if (!headers || typeof headers !== "object") return null;
|
|
7178
|
+
const getter = headers.get;
|
|
7179
|
+
if (typeof getter === "function") {
|
|
7180
|
+
const v = getter.call(headers, name);
|
|
7181
|
+
return typeof v === "string" ? v : null;
|
|
7182
|
+
}
|
|
7183
|
+
const lower = name.toLowerCase();
|
|
7184
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
7185
|
+
if (k.toLowerCase() === lower && typeof v === "string") return v;
|
|
7186
|
+
}
|
|
7187
|
+
return null;
|
|
7188
|
+
}
|
|
7189
|
+
function extractStatus(err) {
|
|
7190
|
+
const candidates = [err.status, err.statusCode, err.response?.status];
|
|
7191
|
+
for (const c of candidates) {
|
|
7192
|
+
const n = typeof c === "number" ? c : typeof c === "string" ? Number(c) : NaN;
|
|
7193
|
+
if (Number.isFinite(n) && n >= 100 && n < 600) return n;
|
|
7194
|
+
}
|
|
7195
|
+
if (typeof err.message === "string") {
|
|
7196
|
+
const m = err.message.match(/\b(4\d\d|5\d\d)\b/);
|
|
7197
|
+
if (m) return Number(m[1]);
|
|
7198
|
+
}
|
|
7199
|
+
return void 0;
|
|
7200
|
+
}
|
|
7201
|
+
function resolveRetryAfterMs(err) {
|
|
7202
|
+
if (typeof err.retryAfterMs === "number" && err.retryAfterMs >= 0) return err.retryAfterMs;
|
|
7203
|
+
const headers = err.headers ?? err.response?.headers;
|
|
7204
|
+
const raw = readHeader(headers, "retry-after");
|
|
7205
|
+
if (raw !== null) {
|
|
7206
|
+
const seconds = Number(raw);
|
|
7207
|
+
if (Number.isFinite(seconds) && seconds >= 0) return Math.round(seconds * 1e3);
|
|
7208
|
+
}
|
|
7209
|
+
return DEFAULT_RATE_LIMIT_MS;
|
|
7210
|
+
}
|
|
7211
|
+
function classifyLlmError(err) {
|
|
7212
|
+
const e = err && typeof err === "object" ? err : {};
|
|
7213
|
+
const message = typeof e.message === "string" ? e.message : "";
|
|
7214
|
+
const status = extractStatus(e);
|
|
7215
|
+
if (status === 429) {
|
|
7216
|
+
return { kind: "rate_limit", retryable: true, retryAfterMs: resolveRetryAfterMs(e) };
|
|
7217
|
+
}
|
|
7218
|
+
if (status === 401 || status === 403) {
|
|
7219
|
+
return { kind: "auth", retryable: false };
|
|
7220
|
+
}
|
|
7221
|
+
if (status !== void 0 && status >= 400 && status < 500 && CONTENT_POLICY_RE.test(message)) {
|
|
7222
|
+
return { kind: "content_policy", retryable: false };
|
|
7223
|
+
}
|
|
7224
|
+
if (status !== void 0 && status >= 400 && status < 500) {
|
|
7225
|
+
return { kind: "invalid_request", retryable: false };
|
|
7226
|
+
}
|
|
7227
|
+
if (status !== void 0 && RETRYABLE_5XX.has(status)) {
|
|
7228
|
+
return { kind: "server", retryable: true };
|
|
7229
|
+
}
|
|
7230
|
+
const code = typeof e.code === "string" ? e.code : "";
|
|
7231
|
+
if (NETWORK_CODE.has(code) || status === void 0 && NETWORK_MSG_RE.test(message)) {
|
|
7232
|
+
return { kind: "network", retryable: true };
|
|
7233
|
+
}
|
|
7234
|
+
return { kind: "unknown", retryable: false };
|
|
7235
|
+
}
|
|
7236
|
+
var DEFAULT_RATE_LIMIT_MS, CONTENT_POLICY_RE, NETWORK_CODE, NETWORK_MSG_RE, RETRYABLE_5XX;
|
|
7237
|
+
var init_llm_error = __esm({
|
|
7238
|
+
"src/core/model-hub/llm-error.ts"() {
|
|
7239
|
+
init_esm_shims();
|
|
7240
|
+
DEFAULT_RATE_LIMIT_MS = 1e3;
|
|
7241
|
+
CONTENT_POLICY_RE = /content[_\s-]?filter|content[_\s-]?policy|policy violation|moderat/i;
|
|
7242
|
+
NETWORK_CODE = /* @__PURE__ */ new Set(["ECONNRESET", "ETIMEDOUT", "ECONNREFUSED", "EAI_AGAIN", "EPIPE", "ENOTFOUND"]);
|
|
7243
|
+
NETWORK_MSG_RE = /fetch failed|network|socket hang up|timed? out|timeout|aborted|abort/i;
|
|
7244
|
+
RETRYABLE_5XX = /* @__PURE__ */ new Set([500, 502, 503, 504, 529]);
|
|
7245
|
+
}
|
|
7246
|
+
});
|
|
7247
|
+
|
|
7162
7248
|
// src/core/autonomy/implement-attempt.ts
|
|
7249
|
+
function defaultSleep(ms) {
|
|
7250
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
7251
|
+
}
|
|
7163
7252
|
function buildInitialPrompt(node, opts = {}) {
|
|
7164
7253
|
const repoMapBlock = opts.repoMap && opts.repoMap.length > 0 ? [`Contexto do reposit\xF3rio (refer\xEAncia, n\xE3o reescreva o que j\xE1 existe):`, opts.repoMap, ""] : [];
|
|
7165
7254
|
const flowBlock = opts.flowContext && opts.flowContext.length > 0 ? [opts.flowContext, ""] : [];
|
|
@@ -7191,16 +7280,40 @@ function buildRetryPrompt(node, failure, maxFeedbackChars) {
|
|
|
7191
7280
|
async function attemptImplementation(deps, options) {
|
|
7192
7281
|
const maxAttempts = Math.max(1, options.maxAttempts);
|
|
7193
7282
|
const maxFeedbackChars = options.maxFeedbackChars ?? DEFAULT_MAX_FEEDBACK_CHARS;
|
|
7283
|
+
const sleep2 = deps.sleep ?? defaultSleep;
|
|
7194
7284
|
let lastResult;
|
|
7195
7285
|
let lastError;
|
|
7196
7286
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
7197
7287
|
const prompt = attempt === 1 || !lastResult ? buildInitialPrompt(options.node, { repoMap: options.repoMap, flowContext: options.flowContext }) : buildRetryPrompt(options.node, lastResult, maxFeedbackChars);
|
|
7288
|
+
let text;
|
|
7289
|
+
try {
|
|
7290
|
+
text = await deps.generate(prompt);
|
|
7291
|
+
} catch (err) {
|
|
7292
|
+
lastError = getErrorMessage(err);
|
|
7293
|
+
const cls = classifyLlmError(err);
|
|
7294
|
+
if (!cls.retryable) {
|
|
7295
|
+
log25.warn("Erro permanente do provider \u2014 escalando sem re-tentar", {
|
|
7296
|
+
attempt,
|
|
7297
|
+
node: options.node.id,
|
|
7298
|
+
kind: cls.kind,
|
|
7299
|
+
error: lastError
|
|
7300
|
+
});
|
|
7301
|
+
return { success: false, attempts: attempt, lastResult, error: lastError };
|
|
7302
|
+
}
|
|
7303
|
+
if (cls.retryAfterMs && cls.retryAfterMs > 0) await sleep2(cls.retryAfterMs);
|
|
7304
|
+
log25.warn("Erro transit\xF3rio do provider \u2014 re-tentando", {
|
|
7305
|
+
attempt,
|
|
7306
|
+
node: options.node.id,
|
|
7307
|
+
kind: cls.kind,
|
|
7308
|
+
retryAfterMs: cls.retryAfterMs
|
|
7309
|
+
});
|
|
7310
|
+
continue;
|
|
7311
|
+
}
|
|
7198
7312
|
let plan;
|
|
7199
7313
|
try {
|
|
7200
|
-
const text = await deps.generate(prompt);
|
|
7201
7314
|
plan = parseImplementationPlan(text);
|
|
7202
7315
|
} catch (err) {
|
|
7203
|
-
lastError =
|
|
7316
|
+
lastError = getErrorMessage(err);
|
|
7204
7317
|
log25.warn("Tentativa falhou no parse", { attempt, node: options.node.id, error: lastError });
|
|
7205
7318
|
continue;
|
|
7206
7319
|
}
|
|
@@ -7219,8 +7332,10 @@ var init_implement_attempt = __esm({
|
|
|
7219
7332
|
"src/core/autonomy/implement-attempt.ts"() {
|
|
7220
7333
|
init_esm_shims();
|
|
7221
7334
|
init_logger();
|
|
7335
|
+
init_errors();
|
|
7222
7336
|
init_truncate();
|
|
7223
7337
|
init_plan_parser();
|
|
7338
|
+
init_llm_error();
|
|
7224
7339
|
log25 = createLogger({ layer: "core", source: "implement-attempt.ts" });
|
|
7225
7340
|
DEFAULT_MAX_FEEDBACK_CHARS = 1200;
|
|
7226
7341
|
}
|
|
@@ -8568,6 +8683,18 @@ var init_store_port = __esm({
|
|
|
8568
8683
|
init_definition_of_done();
|
|
8569
8684
|
}
|
|
8570
8685
|
});
|
|
8686
|
+
|
|
8687
|
+
// src/tui/status-line.ts
|
|
8688
|
+
function formatStatusLine(input) {
|
|
8689
|
+
const tokens = `${Math.max(0, Math.round(input.totalTokens))} tok`;
|
|
8690
|
+
const cost = `$${input.costUsd.toFixed(4)}`;
|
|
8691
|
+
return `\u26C1 ${tokens} \xB7 ${cost} \xB7 ${input.model}`;
|
|
8692
|
+
}
|
|
8693
|
+
var init_status_line = __esm({
|
|
8694
|
+
"src/tui/status-line.ts"() {
|
|
8695
|
+
init_esm_shims();
|
|
8696
|
+
}
|
|
8697
|
+
});
|
|
8571
8698
|
function TaskRow({ task }) {
|
|
8572
8699
|
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
8573
8700
|
" ",
|
|
@@ -8594,6 +8721,7 @@ function App({ model }) {
|
|
|
8594
8721
|
" ",
|
|
8595
8722
|
model.wip
|
|
8596
8723
|
] }) }),
|
|
8724
|
+
/* @__PURE__ */ jsx(Box, { paddingX: 1, children: /* @__PURE__ */ jsx(Text, { color: "green", children: formatStatusLine({ totalTokens: model.tokens.total, costUsd: model.tokens.costUsd, model: model.modelLabel }) }) }),
|
|
8597
8725
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
8598
8726
|
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
8599
8727
|
"Tasks ativas (",
|
|
@@ -8624,6 +8752,7 @@ var STATUS_ICON;
|
|
|
8624
8752
|
var init_app = __esm({
|
|
8625
8753
|
"src/tui/app.tsx"() {
|
|
8626
8754
|
init_esm_shims();
|
|
8755
|
+
init_status_line();
|
|
8627
8756
|
STATUS_ICON = {
|
|
8628
8757
|
in_progress: "\u25CF",
|
|
8629
8758
|
ready: "\u25CB",
|
|
@@ -8674,6 +8803,7 @@ var init_banner_screen = __esm({
|
|
|
8674
8803
|
function CommandBar({ value, onChange, onSubmit, suggestions }) {
|
|
8675
8804
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
8676
8805
|
suggestions.length > 0 && /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginBottom: 1, children: suggestions.map((c) => /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
8806
|
+
c.source === "skill" ? /* @__PURE__ */ jsx(Text, { color: "magenta", children: "[skill] " }) : null,
|
|
8677
8807
|
c.usage,
|
|
8678
8808
|
" \u2014 ",
|
|
8679
8809
|
c.desc
|
|
@@ -8690,6 +8820,28 @@ var init_command_bar = __esm({
|
|
|
8690
8820
|
}
|
|
8691
8821
|
});
|
|
8692
8822
|
|
|
8823
|
+
// src/tui/history.ts
|
|
8824
|
+
function valueAt(state, cursor) {
|
|
8825
|
+
if (cursor < 0) return state.draft;
|
|
8826
|
+
return state.history[state.history.length - 1 - cursor];
|
|
8827
|
+
}
|
|
8828
|
+
function navigateHistory(state, dir) {
|
|
8829
|
+
const last = state.history.length - 1;
|
|
8830
|
+
if (last < 0) return { value: state.draft, cursor: -1 };
|
|
8831
|
+
let cursor = state.cursor;
|
|
8832
|
+
if (dir === "up") {
|
|
8833
|
+
cursor = Math.min(cursor + 1, last);
|
|
8834
|
+
} else {
|
|
8835
|
+
cursor = Math.max(cursor - 1, -1);
|
|
8836
|
+
}
|
|
8837
|
+
return { value: valueAt(state, cursor), cursor };
|
|
8838
|
+
}
|
|
8839
|
+
var init_history = __esm({
|
|
8840
|
+
"src/tui/history.ts"() {
|
|
8841
|
+
init_esm_shims();
|
|
8842
|
+
}
|
|
8843
|
+
});
|
|
8844
|
+
|
|
8693
8845
|
// src/tui/dispatch.ts
|
|
8694
8846
|
function parseCommand(input) {
|
|
8695
8847
|
const trimmed = input.trim();
|
|
@@ -8699,11 +8851,38 @@ function parseCommand(input) {
|
|
|
8699
8851
|
if (sp === -1) return { cmd: body.toLowerCase(), args: "" };
|
|
8700
8852
|
return { cmd: body.slice(0, sp).toLowerCase(), args: body.slice(sp + 1).trim() };
|
|
8701
8853
|
}
|
|
8854
|
+
function fuzzyScore(query, text) {
|
|
8855
|
+
const q = query.toLowerCase();
|
|
8856
|
+
const t = text.toLowerCase();
|
|
8857
|
+
if (q === "") return 0;
|
|
8858
|
+
let qi = 0;
|
|
8859
|
+
let score = 0;
|
|
8860
|
+
let lastMatch = -1;
|
|
8861
|
+
for (let ti = 0; ti < t.length && qi < q.length; ti++) {
|
|
8862
|
+
if (t[ti] === q[qi]) {
|
|
8863
|
+
if (lastMatch === -1) score += ti;
|
|
8864
|
+
else score += ti - lastMatch - 1;
|
|
8865
|
+
lastMatch = ti;
|
|
8866
|
+
qi++;
|
|
8867
|
+
}
|
|
8868
|
+
}
|
|
8869
|
+
return qi === q.length ? score : null;
|
|
8870
|
+
}
|
|
8871
|
+
function fuzzyFilter(query, commands) {
|
|
8872
|
+
if (query.trim() === "") return [...commands];
|
|
8873
|
+
const scored = [];
|
|
8874
|
+
commands.forEach((cmd, idx) => {
|
|
8875
|
+
const score = fuzzyScore(query, cmd.name);
|
|
8876
|
+
if (score !== null) scored.push({ cmd, score, idx });
|
|
8877
|
+
});
|
|
8878
|
+
scored.sort((a, b) => a.score !== b.score ? a.score - b.score : a.idx - b.idx);
|
|
8879
|
+
return scored.map((s) => s.cmd);
|
|
8880
|
+
}
|
|
8702
8881
|
function filterCommands(input, extra = []) {
|
|
8703
8882
|
const trimmed = input.trim();
|
|
8704
8883
|
if (!trimmed.startsWith("/")) return [];
|
|
8705
|
-
const
|
|
8706
|
-
return [...COMMANDS, ...extra]
|
|
8884
|
+
const query = trimmed.slice(1).split(" ")[0];
|
|
8885
|
+
return fuzzyFilter(query, [...COMMANDS, ...extra]);
|
|
8707
8886
|
}
|
|
8708
8887
|
async function runAsyncCommand(port, parsed, _onLine) {
|
|
8709
8888
|
switch (parsed.cmd) {
|
|
@@ -8803,12 +8982,27 @@ function InteractiveApp({ dashboard, port, asyncPort, liveRunner, skillCommands
|
|
|
8803
8982
|
const [log48, setLog] = useState([]);
|
|
8804
8983
|
const [running, setRunning] = useState(false);
|
|
8805
8984
|
const [showHelp, setShowHelp] = useState(false);
|
|
8985
|
+
const [history, setHistory] = useState([]);
|
|
8986
|
+
const [histCursor, setHistCursor] = useState(-1);
|
|
8987
|
+
const [draft, setDraft] = useState("");
|
|
8806
8988
|
const append = (line) => setLog((prev) => [...prev, line].slice(-MAX_LOG_LINES));
|
|
8989
|
+
useInput((_input, key) => {
|
|
8990
|
+
if (phase !== "dashboard" || running) return;
|
|
8991
|
+
if (!key.upArrow && !key.downArrow) return;
|
|
8992
|
+
const effectiveDraft = histCursor === -1 ? input : draft;
|
|
8993
|
+
if (histCursor === -1) setDraft(input);
|
|
8994
|
+
const result = navigateHistory({ history, cursor: histCursor, draft: effectiveDraft }, key.upArrow ? "up" : "down");
|
|
8995
|
+
setHistCursor(result.cursor);
|
|
8996
|
+
setInput(result.value);
|
|
8997
|
+
});
|
|
8807
8998
|
const submit = (raw) => {
|
|
8808
8999
|
const parsed = parseCommand(raw);
|
|
8809
9000
|
setInput("");
|
|
8810
9001
|
const text = raw.trim();
|
|
8811
9002
|
if (text === "" || running) return;
|
|
9003
|
+
setHistory((prev) => [...prev, text]);
|
|
9004
|
+
setHistCursor(-1);
|
|
9005
|
+
setDraft("");
|
|
8812
9006
|
if (parsed.cmd === "quit") {
|
|
8813
9007
|
exit();
|
|
8814
9008
|
return;
|
|
@@ -8872,6 +9066,7 @@ var init_interactive_app = __esm({
|
|
|
8872
9066
|
init_app();
|
|
8873
9067
|
init_banner_screen();
|
|
8874
9068
|
init_command_bar();
|
|
9069
|
+
init_history();
|
|
8875
9070
|
init_dispatch();
|
|
8876
9071
|
MAX_LOG_LINES = 12;
|
|
8877
9072
|
}
|
|
@@ -10403,7 +10598,8 @@ async function launchTui(store2) {
|
|
|
10403
10598
|
return skills.map((s) => ({
|
|
10404
10599
|
name: s.name,
|
|
10405
10600
|
usage: `/${s.name}`,
|
|
10406
|
-
desc: `[${s.category}] ${s.description}
|
|
10601
|
+
desc: `[${s.category}] ${s.description}`,
|
|
10602
|
+
source: "skill"
|
|
10407
10603
|
}));
|
|
10408
10604
|
});
|
|
10409
10605
|
const instance = render(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-graph-workflow/agent-graph-flow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Agente SWE autônomo, local-first e token-frugal: PRD → grafo de execução persistente, TDD obrigatório, custo de token brutalmente baixo. AGPL v3.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|