gitdone-agent 0.8.1 → 0.8.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/index.js +156 -44
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -29,7 +29,7 @@ import { randomUUID, createHash } from 'node:crypto'
|
|
|
29
29
|
// Reported to the server on every sync so the web UI can flag outdated agents.
|
|
30
30
|
// Keep in lockstep with packages/agent/package.json "version" AND
|
|
31
31
|
// src/lib/agentVersion.ts LATEST_AGENT_VERSION.
|
|
32
|
-
const AGENT_VERSION = '0.8.
|
|
32
|
+
const AGENT_VERSION = '0.8.4'
|
|
33
33
|
|
|
34
34
|
const AGENT_DIR = join(homedir(), '.gitdone-agent')
|
|
35
35
|
const CONFIG_PATH = join(AGENT_DIR, 'config.json')
|
|
@@ -999,7 +999,7 @@ function scanRepos(roots, maxDepth = 5) {
|
|
|
999
999
|
|
|
1000
1000
|
// ─── Server sync + commands ─────────────────────────────────────────────────────
|
|
1001
1001
|
|
|
1002
|
-
async function
|
|
1002
|
+
async function apiOnce(cfg, path, body) {
|
|
1003
1003
|
const res = await fetch(`${cfg.url}${path}`, {
|
|
1004
1004
|
method: 'POST',
|
|
1005
1005
|
headers: { Authorization: `Bearer ${cfg.key}`, 'Content-Type': 'application/json' },
|
|
@@ -1007,11 +1007,45 @@ async function api(cfg, path, body) {
|
|
|
1007
1007
|
})
|
|
1008
1008
|
if (!res.ok) {
|
|
1009
1009
|
const text = await res.text().catch(() => '')
|
|
1010
|
-
|
|
1010
|
+
const err = new Error(`HTTP ${res.status} ${path}: ${text}`)
|
|
1011
|
+
err.status = res.status
|
|
1012
|
+
throw err
|
|
1011
1013
|
}
|
|
1012
1014
|
return res.json().catch(() => ({}))
|
|
1013
1015
|
}
|
|
1014
1016
|
|
|
1017
|
+
// gd-514: statuses where the app server demonstrably never processed the body,
|
|
1018
|
+
// so re-sending it can't double-apply anything. 502/503/504 is Caddy answering
|
|
1019
|
+
// while `systemctl restart gitdone` is mid-flight — i.e. EVERY deploy. A thrown
|
|
1020
|
+
// fetch (no status) is a connection that never completed, same story. 4xx and
|
|
1021
|
+
// 500 are NOT retried: they won't get better, and a 500 may have half-applied.
|
|
1022
|
+
function isRetriableApiError(err) {
|
|
1023
|
+
return err?.status === undefined || [408, 429, 502, 503, 504].includes(err.status)
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* POST to the app, retrying the failures that are purely "the server wasn't
|
|
1028
|
+
* there". Console events used to be strictly best-effort, which meant a single
|
|
1029
|
+
* 502 during a deploy could swallow the terminal "turn finished" post and leave
|
|
1030
|
+
* the session RUNNING forever, showing a half-written reply that never moved
|
|
1031
|
+
* again (gd-514).
|
|
1032
|
+
*/
|
|
1033
|
+
async function api(cfg, path, body, attempts = 4) {
|
|
1034
|
+
let lastErr
|
|
1035
|
+
for (let i = 0; i < attempts; i++) {
|
|
1036
|
+
try {
|
|
1037
|
+
return await apiOnce(cfg, path, body)
|
|
1038
|
+
} catch (err) {
|
|
1039
|
+
lastErr = err
|
|
1040
|
+
if (i === attempts - 1 || !isRetriableApiError(err)) break
|
|
1041
|
+
// 1s, 2s, 4s — covers the restart window of a deploy without stalling the
|
|
1042
|
+
// console noticeably when the server is merely slow.
|
|
1043
|
+
await new Promise((r) => setTimeout(r, 1000 * 2 ** i))
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
throw lastErr
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1015
1049
|
async function reportCommandResult(cfg, id, status, result) {
|
|
1016
1050
|
await api(cfg, '/api/v1/agent/command-result', { id, status, result }).catch(() => {})
|
|
1017
1051
|
}
|
|
@@ -1021,20 +1055,28 @@ async function reportCommandResult(cfg, id, status, result) {
|
|
|
1021
1055
|
// how many tokens the run cost. `streamingText` / `activityText` (gd-419) carry
|
|
1022
1056
|
// the live "being typed" preview and the latest thinking snippet — explicit ''
|
|
1023
1057
|
// clears them; `undefined` leaves them untouched.
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1058
|
+
// Same contract as postSessionEvents: true when it landed, false when the
|
|
1059
|
+
// caller must keep the batch and retry it (gd-514).
|
|
1060
|
+
async function postRunEvents(cfg, runId, events, status, result, usage, streamingText, activityText, extra, attempts) {
|
|
1061
|
+
try {
|
|
1062
|
+
await api(cfg, '/api/v1/agent/ai-run/events', {
|
|
1063
|
+
runId,
|
|
1064
|
+
events,
|
|
1065
|
+
...(status ? { status } : {}),
|
|
1066
|
+
...(result !== undefined ? { result } : {}),
|
|
1067
|
+
...(usage ? { usage } : {}),
|
|
1068
|
+
...(streamingText !== undefined ? { streamingText } : {}),
|
|
1069
|
+
...(activityText !== undefined ? { activityText } : {}),
|
|
1070
|
+
// gd-466: claude's own session id (for --resume) + precise stop cause.
|
|
1071
|
+
...(extra?.claudeSessionId ? { claudeSessionId: extra.claudeSessionId } : {}),
|
|
1072
|
+
...(extra?.stopReason ? { stopReason: extra.stopReason } : {}),
|
|
1073
|
+
...(typeof extra?.resetAt === 'number' ? { resetAt: extra.resetAt } : {}),
|
|
1074
|
+
}, attempts)
|
|
1075
|
+
return true
|
|
1076
|
+
} catch (e) {
|
|
1077
|
+
log(`✗ ai-run events post failed: ${e.message}`)
|
|
1078
|
+
return false
|
|
1079
|
+
}
|
|
1038
1080
|
}
|
|
1039
1081
|
|
|
1040
1082
|
// gd-466: parse a hard usage-limit stop out of claude's headless output. On the
|
|
@@ -1058,17 +1100,26 @@ function detectUsageLimit(text) {
|
|
|
1058
1100
|
|
|
1059
1101
|
// Post transcript lines (and optional turn status / Claude session id) for an
|
|
1060
1102
|
// interactive AiSession chat turn.
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1103
|
+
// Returns true when the post landed. A false is the caller's cue to put the
|
|
1104
|
+
// batch back and re-send it next tick, instead of dropping it on the floor
|
|
1105
|
+
// (gd-514) — that is how whole chunks of a reply used to go missing.
|
|
1106
|
+
async function postSessionEvents(cfg, sessionId, events, status, claudeSessionId, streamingText, activityText, attempts) {
|
|
1107
|
+
try {
|
|
1108
|
+
await api(cfg, '/api/v1/agent/ai-session/events', {
|
|
1109
|
+
sessionId,
|
|
1110
|
+
events,
|
|
1111
|
+
...(status ? { status } : {}),
|
|
1112
|
+
...(claudeSessionId ? { claudeSessionId } : {}),
|
|
1113
|
+
// Explicit '' clears the live preview; `undefined` leaves it untouched.
|
|
1114
|
+
...(streamingText !== undefined ? { streamingText } : {}),
|
|
1115
|
+
// Same semantics for the live thinking snippet (gd-419).
|
|
1116
|
+
...(activityText !== undefined ? { activityText } : {}),
|
|
1117
|
+
}, attempts)
|
|
1118
|
+
return true
|
|
1119
|
+
} catch (e) {
|
|
1120
|
+
log(`✗ ai-session events post failed: ${e.message}`)
|
|
1121
|
+
return false
|
|
1122
|
+
}
|
|
1072
1123
|
}
|
|
1073
1124
|
|
|
1074
1125
|
// Turn one stream-json line from `claude -p` into console events. Phase 1 shows
|
|
@@ -1471,6 +1522,17 @@ function aiProviderArg(raw) {
|
|
|
1471
1522
|
return raw === 'codex' ? 'codex' : 'claude'
|
|
1472
1523
|
}
|
|
1473
1524
|
|
|
1525
|
+
// Reasoning effort — how hard the model thinks per turn (gd-507). Both CLIs
|
|
1526
|
+
// take the same five levels, by different spellings (gd-510): Claude Code as
|
|
1527
|
+
// `--effort <level>`, Codex as a `model_reasoning_effort` config override.
|
|
1528
|
+
// Whitelisted rather than pattern-matched, since an unknown level makes either
|
|
1529
|
+
// CLI refuse to start. null (incl. a gitDone older than gd-507) → don't pass
|
|
1530
|
+
// it, and the machine's own setting decides.
|
|
1531
|
+
const AI_EFFORT_LEVELS = ['low', 'medium', 'high', 'xhigh', 'max']
|
|
1532
|
+
function aiEffortArg(raw) {
|
|
1533
|
+
return typeof raw === 'string' && AI_EFFORT_LEVELS.includes(raw.trim()) ? raw.trim() : null
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1474
1536
|
function cliNotFoundMessage(provider, hostname) {
|
|
1475
1537
|
return provider === 'codex'
|
|
1476
1538
|
? `Codex CLI (ChatGPT) не е намерен на този компютър (${hostname}). Инсталирай го (npm i -g @openai/codex), влез с ChatGPT акаунт през „codex login", увери се, че „codex" е в PATH, после рестартирай агента.`
|
|
@@ -1495,7 +1557,7 @@ function cliNotFoundMessage(provider, hostname) {
|
|
|
1495
1557
|
// The commit policy is NOT here: Codex has no tool-level deny list to mirror
|
|
1496
1558
|
// Claude's --disallowedTools, so it rides along in the prompt (codexPrompt).
|
|
1497
1559
|
function codexExecArgs(cfg, opts) {
|
|
1498
|
-
const { model, resumeId } = opts
|
|
1560
|
+
const { model, effort, resumeId } = opts
|
|
1499
1561
|
const flags = [
|
|
1500
1562
|
'--json',
|
|
1501
1563
|
'--skip-git-repo-check',
|
|
@@ -1518,6 +1580,9 @@ function codexExecArgs(cfg, opts) {
|
|
|
1518
1580
|
// it, which killed every resumed turn with "unexpected argument".)
|
|
1519
1581
|
'--dangerously-bypass-approvals-and-sandbox',
|
|
1520
1582
|
...(model ? ['--model', model] : []),
|
|
1583
|
+
// Reasoning effort chosen in gitDone (gd-507). There is no flag for it, so
|
|
1584
|
+
// it goes in as a config override like the MCP settings below.
|
|
1585
|
+
...(effort ? ['-c', `model_reasoning_effort="${effort}"`] : []),
|
|
1521
1586
|
'-c', `mcp_servers.gitdone.url="${cfg.url}/api/mcp"`,
|
|
1522
1587
|
'-c', 'mcp_servers.gitdone.bearer_token_env_var="GITDONE_KEY"',
|
|
1523
1588
|
// Tags the AI agents this run registers with THIS computer (gd-308).
|
|
@@ -1551,6 +1616,7 @@ function runAiCommand(cfg, cmd, repoPath) {
|
|
|
1551
1616
|
const prompt = cmd.payload?.prompt ?? ''
|
|
1552
1617
|
const allowCommit = cmd.payload?.allowCommit === true
|
|
1553
1618
|
const model = aiModelArg(cmd.payload?.model)
|
|
1619
|
+
const effort = aiEffortArg(cmd.payload?.effort)
|
|
1554
1620
|
const provider = aiProviderArg(cmd.payload?.provider)
|
|
1555
1621
|
const isCodex = provider === 'codex'
|
|
1556
1622
|
// gd-466: a token-reset resume asks us to continue the CLI's own conversation.
|
|
@@ -1586,7 +1652,7 @@ function runAiCommand(cfg, cmd, repoPath) {
|
|
|
1586
1652
|
// Piping it to stdin is robust for both the native exe and the shim; `codex
|
|
1587
1653
|
// exec -` reads its prompt from stdin the same way.
|
|
1588
1654
|
const args = isCodex
|
|
1589
|
-
? codexExecArgs(cfg, { model, resumeId })
|
|
1655
|
+
? codexExecArgs(cfg, { model, effort, resumeId })
|
|
1590
1656
|
: [
|
|
1591
1657
|
'-p',
|
|
1592
1658
|
'--output-format', 'stream-json',
|
|
@@ -1600,6 +1666,8 @@ function runAiCommand(cfg, cmd, repoPath) {
|
|
|
1600
1666
|
...(resumeId ? ['--resume', resumeId] : []),
|
|
1601
1667
|
// Model chosen in gitDone (project default / task); omitted → machine default (gd-354).
|
|
1602
1668
|
...(model ? ['--model', model] : []),
|
|
1669
|
+
// Reasoning effort chosen in gitDone (gd-510); omitted → machine default.
|
|
1670
|
+
...(effort ? ['--effort', effort] : []),
|
|
1603
1671
|
'--permission-mode', 'acceptEdits',
|
|
1604
1672
|
'--allowedTools', 'Read,Edit,Write,Bash,mcp__gitdone__*',
|
|
1605
1673
|
// Block git commit/push unless this repo opted in (per-repo aiAutoCommit).
|
|
@@ -1628,7 +1696,9 @@ function runAiCommand(cfg, cmd, repoPath) {
|
|
|
1628
1696
|
const activityChanged = liveActivity !== sentActivity
|
|
1629
1697
|
if (pending.length === 0 && !liveChanged && !activityChanged) return
|
|
1630
1698
|
flushing = true
|
|
1631
|
-
const
|
|
1699
|
+
const taken = pending; pending = []
|
|
1700
|
+
const prevSentLive = sentLive
|
|
1701
|
+
const prevSentActivity = sentActivity
|
|
1632
1702
|
const streamingText = liveChanged ? liveText : undefined
|
|
1633
1703
|
const activityText = activityChanged ? liveActivity : undefined
|
|
1634
1704
|
sentLive = liveText
|
|
@@ -1637,7 +1707,16 @@ function runAiCommand(cfg, cmd, repoPath) {
|
|
|
1637
1707
|
// resume survives even if the agent dies before the clean close handler.
|
|
1638
1708
|
const extra = (!sentSession && capturedSession) ? { claudeSessionId: capturedSession } : undefined
|
|
1639
1709
|
if (extra) sentSession = true
|
|
1640
|
-
await postRunEvents(cfg, runId,
|
|
1710
|
+
const ok = await postRunEvents(cfg, runId, taken, undefined, undefined, undefined, streamingText, activityText, extra)
|
|
1711
|
+
if (!ok) {
|
|
1712
|
+
// Keep everything for the next tick rather than losing console lines to
|
|
1713
|
+
// one bad request (gd-514) — including the session id, so a resume still
|
|
1714
|
+
// gets it.
|
|
1715
|
+
pending = taken.concat(pending)
|
|
1716
|
+
sentLive = prevSentLive
|
|
1717
|
+
sentActivity = prevSentActivity
|
|
1718
|
+
if (extra) sentSession = false
|
|
1719
|
+
}
|
|
1641
1720
|
flushing = false
|
|
1642
1721
|
}
|
|
1643
1722
|
const push = (kind, text) => {
|
|
@@ -1740,7 +1819,9 @@ function runAiCommand(cfg, cmd, repoPath) {
|
|
|
1740
1819
|
: (ok ? '✓ Готово.' : `✗ Процесът приключи с код ${code}.`),
|
|
1741
1820
|
}]
|
|
1742
1821
|
if (usage) events.push({ kind: 'SYSTEM', text: `📊 Токени: ${fmtTokens(usage.inputTokens)} вход · ${fmtTokens(usage.outputTokens)} изход${usage.cacheReadTokens ? ` · ${fmtTokens(usage.cacheReadTokens)} кеш` : ''}${typeof usage.costUsd === 'number' ? ` · $${usage.costUsd.toFixed(4)}` : ''}` })
|
|
1743
|
-
|
|
1822
|
+
// Terminal post — retried hard (gd-514): losing it leaves the run stuck on
|
|
1823
|
+
// RUNNING, and with it the token-limit park/resume the server keys off.
|
|
1824
|
+
const landed = await postRunEvents(
|
|
1744
1825
|
cfg, runId,
|
|
1745
1826
|
events,
|
|
1746
1827
|
// A limit stop is not a clean finish — report it as 'error' so the run
|
|
@@ -1751,7 +1832,9 @@ function runAiCommand(cfg, cmd, repoPath) {
|
|
|
1751
1832
|
undefined,
|
|
1752
1833
|
undefined,
|
|
1753
1834
|
extra,
|
|
1835
|
+
7,
|
|
1754
1836
|
)
|
|
1837
|
+
if (!landed) log(`✗ ai_run ${runId}: терминалният пост не мина — сървърният watchdog поема`)
|
|
1755
1838
|
reportCommandResult(cfg, cmd.id, ok && !limit.limited ? 'done' : 'error', limit.limited ? 'usage_limit' : `exit ${code}`)
|
|
1756
1839
|
log(`■ ai_run ${runId} приключи (code ${code}${limit.limited ? ', usage_limit' : ''})`)
|
|
1757
1840
|
})
|
|
@@ -1846,12 +1929,26 @@ async function flushChatTurn(cfg, entry) {
|
|
|
1846
1929
|
const heartbeat = !hasEvents && !liveChanged && !activityChanged && Date.now() - t.lastPostAt > 2500
|
|
1847
1930
|
if (!hasEvents && !liveChanged && !activityChanged && !heartbeat) return
|
|
1848
1931
|
t.flushing = true
|
|
1849
|
-
const
|
|
1932
|
+
const taken = t.pending; t.pending = []
|
|
1933
|
+
const prevSentLive = t.sentLive
|
|
1934
|
+
const prevSentActivity = t.sentActivity
|
|
1850
1935
|
const streamingText = liveChanged ? t.liveText : undefined
|
|
1851
1936
|
const activityText = activityChanged ? t.liveActivity : undefined
|
|
1852
1937
|
t.sentLive = t.liveText
|
|
1853
1938
|
t.sentActivity = t.liveActivity
|
|
1854
|
-
await postSessionEvents(
|
|
1939
|
+
const ok = await postSessionEvents(
|
|
1940
|
+
cfg, entry.sessionId,
|
|
1941
|
+
taken.map((e) => ({ role: chatRoleFor(e.kind), text: e.text })),
|
|
1942
|
+
'running', entry.capturedSession || undefined, streamingText, activityText,
|
|
1943
|
+
)
|
|
1944
|
+
if (!ok) {
|
|
1945
|
+
// Put it all back so the next tick re-sends it (gd-514). Dropping the batch
|
|
1946
|
+
// is what used to tear holes in the transcript; rewinding sentLive makes the
|
|
1947
|
+
// next post carry the current live text again (it's a replace, not a delta).
|
|
1948
|
+
t.pending = taken.concat(t.pending)
|
|
1949
|
+
t.sentLive = prevSentLive
|
|
1950
|
+
t.sentActivity = prevSentActivity
|
|
1951
|
+
}
|
|
1855
1952
|
t.lastPostAt = Date.now()
|
|
1856
1953
|
t.flushing = false
|
|
1857
1954
|
}
|
|
@@ -1872,21 +1969,31 @@ async function finishChatTurn(cfg, entry, outcome) {
|
|
|
1872
1969
|
for (let i = 0; i < 20 && t.flushing; i++) await new Promise((r) => setTimeout(r, 100))
|
|
1873
1970
|
const batch = t.pending.map((e) => ({ role: chatRoleFor(e.kind), text: e.text }))
|
|
1874
1971
|
t.pending = []
|
|
1972
|
+
// The one post that MUST land: it carries the last of the reply and the
|
|
1973
|
+
// terminal status. Lose it and the console shows "АИ пише…" over a
|
|
1974
|
+
// half-written answer forever (gd-514) — so it gets ~1 minute of retries,
|
|
1975
|
+
// enough to outlast an app restart. The server's stall watchdog is the net if
|
|
1976
|
+
// even that fails.
|
|
1977
|
+
const terminal = async (status, extra) => {
|
|
1978
|
+
const ok = await postSessionEvents(
|
|
1979
|
+
cfg, entry.sessionId, extra ? batch.concat(extra) : batch, status,
|
|
1980
|
+
entry.capturedSession || undefined, '', '', 7,
|
|
1981
|
+
)
|
|
1982
|
+
if (!ok) log(`✗ ai_chat ${entry.sessionId}: терминалният пост не мина — сървърът ще помири сесията`)
|
|
1983
|
+
}
|
|
1875
1984
|
if (outcome.stopped) {
|
|
1876
|
-
|
|
1877
|
-
await postSessionEvents(cfg, entry.sessionId, batch, 'idle', entry.capturedSession || undefined, '', '')
|
|
1985
|
+
await terminal('idle', { role: 'SYSTEM', text: '⏹ Спряно. Напиши още нещо, за да продължим разговора.' })
|
|
1878
1986
|
reportCommandResult(cfg, t.cmdId, 'done', 'stopped by user')
|
|
1879
1987
|
log(`⏹ ai_chat ${entry.sessionId} спряно от потребителя`)
|
|
1880
1988
|
return
|
|
1881
1989
|
}
|
|
1882
1990
|
if (!outcome.ok) {
|
|
1883
|
-
|
|
1884
|
-
await postSessionEvents(cfg, entry.sessionId, batch, 'error', entry.capturedSession || undefined, '', '')
|
|
1991
|
+
await terminal('error', { role: 'SYSTEM', text: `✗ Ходът приключи с код ${outcome.code}.` })
|
|
1885
1992
|
reportCommandResult(cfg, t.cmdId, 'error', `exit ${outcome.code}`)
|
|
1886
1993
|
log(`✗ ai_chat ход в ${entry.sessionId} падна (code ${outcome.code})`)
|
|
1887
1994
|
return
|
|
1888
1995
|
}
|
|
1889
|
-
await
|
|
1996
|
+
await terminal('idle')
|
|
1890
1997
|
reportCommandResult(cfg, t.cmdId, 'done', 'ok')
|
|
1891
1998
|
log(`✓ ai_chat ход в ${entry.sessionId} приключи (процесът остава жив)`)
|
|
1892
1999
|
}
|
|
@@ -1894,7 +2001,7 @@ async function finishChatTurn(cfg, entry, outcome) {
|
|
|
1894
2001
|
// Spawn the persistent claude process for one session and wire its stream
|
|
1895
2002
|
// handlers once. Turns come and go via entry.turn; the process stays.
|
|
1896
2003
|
function spawnChatProc(cfg, opts) {
|
|
1897
|
-
const { sessionId, repoPath, model, allowCommit, resumeId, claudePath, shell, settingsPath, mcpConfigPath } = opts
|
|
2004
|
+
const { sessionId, repoPath, model, effort, allowCommit, resumeId, claudePath, shell, settingsPath, mcpConfigPath } = opts
|
|
1898
2005
|
|
|
1899
2006
|
// Room in the pool: evict the least-recently-used idle process first.
|
|
1900
2007
|
if (chatProcs.size >= CHAT_PROC_MAX) {
|
|
@@ -1914,6 +2021,9 @@ function spawnChatProc(cfg, opts) {
|
|
|
1914
2021
|
// Model resolved for this session (picker / project default); omitted →
|
|
1915
2022
|
// machine default (gd-354). Kept consistent across the session's turns.
|
|
1916
2023
|
...(model ? ['--model', model] : []),
|
|
2024
|
+
// Same for the session's reasoning effort (gd-510) — frozen at spawn, so a
|
|
2025
|
+
// change in gitDone reaches the pooled process on its next respawn.
|
|
2026
|
+
...(effort ? ['--effort', effort] : []),
|
|
1917
2027
|
'--permission-mode', 'acceptEdits',
|
|
1918
2028
|
'--allowedTools', 'Read,Edit,Write,Bash,mcp__gitdone__*',
|
|
1919
2029
|
// Block git commit/push unless this repo opted in (per-repo aiAutoCommit).
|
|
@@ -2005,7 +2115,7 @@ function killTree(child) {
|
|
|
2005
2115
|
// chatProcs under the same entry shape, so ai_chat_stop and the stuck-turn
|
|
2006
2116
|
// sweeper keep working untouched.
|
|
2007
2117
|
function runCodexChatTurn(cfg, cmd, repoPath, opts) {
|
|
2008
|
-
const { sessionId, prompt, resumeId, model, allowCommit } = opts
|
|
2118
|
+
const { sessionId, prompt, resumeId, model, effort, allowCommit } = opts
|
|
2009
2119
|
|
|
2010
2120
|
const { path: codexPath, shell, found } = findCodex()
|
|
2011
2121
|
if (!found) {
|
|
@@ -2016,7 +2126,7 @@ function runCodexChatTurn(cfg, cmd, repoPath, opts) {
|
|
|
2016
2126
|
return
|
|
2017
2127
|
}
|
|
2018
2128
|
|
|
2019
|
-
const args = codexExecArgs(cfg, { model, resumeId })
|
|
2129
|
+
const args = codexExecArgs(cfg, { model, effort, resumeId })
|
|
2020
2130
|
const childEnv = { ...process.env, GITDONE_URL: cfg.url, GITDONE_KEY: cfg.key, GITDONE_SESSION_ID: sessionId }
|
|
2021
2131
|
|
|
2022
2132
|
let child
|
|
@@ -2107,6 +2217,7 @@ async function runAiChat(cfg, cmd, repoPath) {
|
|
|
2107
2217
|
const claudeSessionId = cmd.payload?.claudeSessionId || null
|
|
2108
2218
|
const allowCommit = cmd.payload?.allowCommit === true
|
|
2109
2219
|
const model = aiModelArg(cmd.payload?.model)
|
|
2220
|
+
const effort = aiEffortArg(cmd.payload?.effort)
|
|
2110
2221
|
const provider = aiProviderArg(cmd.payload?.provider)
|
|
2111
2222
|
// A turn may be text-only, image-only, or both — but needs at least one.
|
|
2112
2223
|
if (!sessionId || (!prompt && images.length === 0)) {
|
|
@@ -2141,6 +2252,7 @@ async function runAiChat(cfg, cmd, repoPath) {
|
|
|
2141
2252
|
imgDir: dl?.dir ?? null,
|
|
2142
2253
|
resumeId: claudeSessionId,
|
|
2143
2254
|
model,
|
|
2255
|
+
effort,
|
|
2144
2256
|
allowCommit,
|
|
2145
2257
|
})
|
|
2146
2258
|
return
|
|
@@ -2160,7 +2272,7 @@ async function runAiChat(cfg, cmd, repoPath) {
|
|
|
2160
2272
|
let mcpConfigPath
|
|
2161
2273
|
try { mcpConfigPath = ensureAiMcpConfig(cfg) } catch (e) { log(`✗ ai mcp config setup failed: ${e.message}`) }
|
|
2162
2274
|
try {
|
|
2163
|
-
entry = spawnChatProc(cfg, { sessionId, repoPath, model, allowCommit, resumeId: claudeSessionId, claudePath, shell, settingsPath, mcpConfigPath })
|
|
2275
|
+
entry = spawnChatProc(cfg, { sessionId, repoPath, model, effort, allowCommit, resumeId: claudeSessionId, claudePath, shell, settingsPath, mcpConfigPath })
|
|
2164
2276
|
} catch (err) {
|
|
2165
2277
|
postSessionEvents(cfg, sessionId, [{ role: 'SYSTEM', text: `Грешка при стартиране: ${err.message}` }], 'error')
|
|
2166
2278
|
reportCommandResult(cfg, cmd.id, 'error', err.message)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gitdone-agent",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"description": "Local git agent for gitdone
|
|
3
|
+
"version": "0.8.4",
|
|
4
|
+
"description": "Local git agent for gitdone — watches a local repo and sends snapshots to gitdone.eu",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"gitdone-agent": "index.js"
|