gitdone-agent 0.6.3 → 0.6.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 +59 -1
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -27,7 +27,7 @@ import { randomUUID } from 'node:crypto'
|
|
|
27
27
|
// Reported to the server on every sync so the web UI can flag outdated agents.
|
|
28
28
|
// Keep in lockstep with packages/agent/package.json "version" AND
|
|
29
29
|
// src/lib/agentVersion.ts LATEST_AGENT_VERSION.
|
|
30
|
-
const AGENT_VERSION = '0.6.
|
|
30
|
+
const AGENT_VERSION = '0.6.4'
|
|
31
31
|
|
|
32
32
|
const AGENT_DIR = join(homedir(), '.gitdone-agent')
|
|
33
33
|
const CONFIG_PATH = join(AGENT_DIR, 'config.json')
|
|
@@ -806,6 +806,24 @@ async function downloadSessionImages(sessionId, urls) {
|
|
|
806
806
|
return { dir, paths }
|
|
807
807
|
}
|
|
808
808
|
|
|
809
|
+
// Live chat turns, keyed by sessionId → their spawned `claude` child. Lets an
|
|
810
|
+
// `ai_chat_stop` command find and kill the turn a user asked to interrupt
|
|
811
|
+
// (gd-303). A turn registers itself on spawn and removes itself on close.
|
|
812
|
+
const chatChildren = new Map()
|
|
813
|
+
|
|
814
|
+
// Kill a process AND its descendants. `claude` spawns its own children (bash,
|
|
815
|
+
// node hooks), so killing just the top pid would orphan them. Windows needs
|
|
816
|
+
// `taskkill /T`; POSIX kills the process group, falling back to a hard kill.
|
|
817
|
+
function killTree(child) {
|
|
818
|
+
const pid = child?.pid
|
|
819
|
+
if (!pid) return
|
|
820
|
+
if (process.platform === 'win32') {
|
|
821
|
+
try { spawn('taskkill', ['/PID', String(pid), '/T', '/F'], { windowsHide: true }) } catch { /* best-effort */ }
|
|
822
|
+
} else {
|
|
823
|
+
try { process.kill(-pid, 'SIGKILL') } catch { try { child.kill('SIGKILL') } catch { /* gone */ } }
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
809
827
|
// Run one turn of an interactive chat session: `claude -p` (resuming the prior
|
|
810
828
|
// Claude session when known) with the user's message on stdin, streaming the
|
|
811
829
|
// reply back to the session console. Long-running + fire-and-forget like ai_run.
|
|
@@ -912,6 +930,9 @@ async function runAiChat(cfg, cmd, repoPath) {
|
|
|
912
930
|
return
|
|
913
931
|
}
|
|
914
932
|
|
|
933
|
+
// Track this turn so an `ai_chat_stop` command can find and kill it (gd-303).
|
|
934
|
+
chatChildren.set(sessionId, child)
|
|
935
|
+
|
|
915
936
|
if (child.stdin) {
|
|
916
937
|
child.stdin.on('error', () => {})
|
|
917
938
|
try { child.stdin.write(fullPrompt); child.stdin.end() }
|
|
@@ -932,10 +953,23 @@ async function runAiChat(cfg, cmd, repoPath) {
|
|
|
932
953
|
child.on('error', (err) => push('SYSTEM', `Процесна грешка: ${err.message}`))
|
|
933
954
|
child.on('close', async (code) => {
|
|
934
955
|
clearInterval(timer)
|
|
956
|
+
chatChildren.delete(sessionId)
|
|
935
957
|
if (imgDir) { try { rmSync(imgDir, { recursive: true, force: true }) } catch { /* best-effort */ } }
|
|
936
958
|
if (buf.trim()) parseStreamLine(buf.trim(), push, onInit, onDelta)
|
|
937
959
|
liveText = '' // turn is over — drop any lingering live preview
|
|
938
960
|
await flush()
|
|
961
|
+
// User-initiated stop (gd-303): land on idle with a friendly note, not an
|
|
962
|
+
// "exited with code N" error — the kill's non-zero code is expected here.
|
|
963
|
+
if (child.__stopped) {
|
|
964
|
+
await postSessionEvents(
|
|
965
|
+
cfg, sessionId,
|
|
966
|
+
[{ role: 'SYSTEM', text: '⏹ Спряно. Напиши още нещо, за да продължим разговора.' }],
|
|
967
|
+
'idle', capturedSession || undefined, '',
|
|
968
|
+
)
|
|
969
|
+
reportCommandResult(cfg, cmd.id, 'done', 'stopped by user')
|
|
970
|
+
log(`⏹ ai_chat ${sessionId} спряно от потребителя`)
|
|
971
|
+
return
|
|
972
|
+
}
|
|
939
973
|
const ok = code === 0
|
|
940
974
|
await postSessionEvents(
|
|
941
975
|
cfg, sessionId,
|
|
@@ -949,6 +983,24 @@ async function runAiChat(cfg, cmd, repoPath) {
|
|
|
949
983
|
})
|
|
950
984
|
}
|
|
951
985
|
|
|
986
|
+
// ai_chat_stop — interrupt a running chat turn the user asked to stop (gd-303).
|
|
987
|
+
// Kills the tracked child (its `close` handler then posts the "⏹ Спряно" note and
|
|
988
|
+
// flips the session to idle). If nothing is running, just idles the session so
|
|
989
|
+
// the console unlocks.
|
|
990
|
+
function stopAiChat(cfg, cmd) {
|
|
991
|
+
const sessionId = cmd.payload?.sessionId
|
|
992
|
+
const child = sessionId ? chatChildren.get(sessionId) : null
|
|
993
|
+
if (!child) {
|
|
994
|
+
if (sessionId) postSessionEvents(cfg, sessionId, [], 'idle', undefined, '')
|
|
995
|
+
reportCommandResult(cfg, cmd.id, 'done', 'no active turn')
|
|
996
|
+
return
|
|
997
|
+
}
|
|
998
|
+
child.__stopped = true
|
|
999
|
+
killTree(child)
|
|
1000
|
+
log(`⏹ ai_chat_stop ${sessionId} — kill signalled`)
|
|
1001
|
+
reportCommandResult(cfg, cmd.id, 'done', 'stop signalled')
|
|
1002
|
+
}
|
|
1003
|
+
|
|
952
1004
|
// deploy — run the project's deploy script (scripts/deploy.sh) in the repo.
|
|
953
1005
|
// Long-running (git pull + npm build + service restart), so we spawn it detached
|
|
954
1006
|
// from the poll loop and report the result on exit. The agent is its OWN process
|
|
@@ -993,6 +1045,12 @@ async function executeCommand(cfg, cmd, repoPath) {
|
|
|
993
1045
|
runAiChat(cfg, cmd, repoPath)
|
|
994
1046
|
return
|
|
995
1047
|
}
|
|
1048
|
+
// ai_chat_stop — interrupt the running turn for a session (gd-303). Handled
|
|
1049
|
+
// inline (it just signals a kill) and reports its own result.
|
|
1050
|
+
if (cmd.type === 'ai_chat_stop') {
|
|
1051
|
+
stopAiChat(cfg, cmd)
|
|
1052
|
+
return
|
|
1053
|
+
}
|
|
996
1054
|
// deploy — run the repo's deploy script. Long-running (pull + build + service
|
|
997
1055
|
// restart), so it's launched in the background and reports its own result on
|
|
998
1056
|
// exit, same as ai_run.
|