@inetafrica/open-claudia 2.6.6 → 2.6.8
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/CHANGELOG.md +3 -0
- package/bin/task.js +7 -0
- package/core/loopback.js +3 -1
- package/core/runner.js +13 -7
- package/core/tasks.js +13 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v2.6.8
|
|
4
|
+
- **Tasks: a plan can't be completed while subtasks are still open.** `task done` (and the loopback task-update path) now refuses to flip a parent to completed if any child is not yet completed — it returns a blocked result listing the open children, and the CLI prints a clean "Can't complete <id>: N subtask(s) still open" message instead of silently corrupting the tree. Standalone tasks and completing the last open child (which removes the whole plan) are unchanged. Prevents the inflated-count drift where parents showed done over still-open children.
|
|
5
|
+
|
|
3
6
|
## v2.6.6
|
|
4
7
|
- **Recall: recent-turns window as judge-gated context.** v2.6.5 fixed quotes, but a bare follow-up like "Push" with no quote still recalled nothing — recall only ever saw the current message. The last ~6 user/assistant turns on this channel (read from the project transcript, current turn excluded) now feed recall the same way the quote does: keyword-matched as origin "context", kept only when the haiku judge confirms relevance, dropped on fail-open. A stale thread can never force-inject packs on keywords alone, but terse follow-ups finally inherit the topic of the conversation.
|
|
5
8
|
|
package/bin/task.js
CHANGED
|
@@ -13,6 +13,7 @@ Per-channel todo list with optional plan/subtask hierarchy.
|
|
|
13
13
|
open-claudia task list [--status pending|in_progress|completed]
|
|
14
14
|
open-claudia task start <id>
|
|
15
15
|
open-claudia task done <id> # completes AND removes; last subtask done removes the plan
|
|
16
|
+
# a plan won't complete while any subtask is still open
|
|
16
17
|
open-claudia task remove <id> # removing a plan removes its subtasks
|
|
17
18
|
open-claudia task clear-completed # prune any leftover completed entries
|
|
18
19
|
|
|
@@ -106,6 +107,12 @@ async function runUpdate(args, status) {
|
|
|
106
107
|
if (!id) { console.error(`Usage: task ${status === "in_progress" ? "start" : "done"} <id>`); process.exit(2); }
|
|
107
108
|
try {
|
|
108
109
|
const res = await postJson("task-update", { id, status });
|
|
110
|
+
if (res.blocked) {
|
|
111
|
+
const kids = res.openChildren || [];
|
|
112
|
+
const names = kids.map((c) => ` - ${c.content} (${c.id})`).join("\n");
|
|
113
|
+
console.error(`Can't complete ${id}: ${kids.length} subtask${kids.length === 1 ? "" : "s"} still open. Finish or remove ${kids.length === 1 ? "it" : "them"} first:\n${names}`);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
109
116
|
if (!res.ok) { console.error(`Not found: ${id}`); process.exit(1); }
|
|
110
117
|
if (status === "completed") {
|
|
111
118
|
const n = res.removedCount || 0;
|
package/core/loopback.js
CHANGED
|
@@ -226,7 +226,9 @@ async function handleJson(req, res, url, kind) {
|
|
|
226
226
|
if (!payload.id) return reply(res, 400, { error: "missing id" });
|
|
227
227
|
if (payload.status === "completed") {
|
|
228
228
|
const result = tasksStore.complete(adapterId, channelId, payload.id);
|
|
229
|
-
return reply(res,
|
|
229
|
+
if (!result) return reply(res, 404, { ok: false, task: null, removedCount: 0 });
|
|
230
|
+
if (result.blocked) return reply(res, 200, { ok: false, blocked: true, task: result.task, openChildren: result.openChildren });
|
|
231
|
+
return reply(res, 200, { ok: true, task: result.task, removedCount: result.removedCount });
|
|
230
232
|
}
|
|
231
233
|
const patch = {};
|
|
232
234
|
if (payload.status) patch.status = payload.status;
|
package/core/runner.js
CHANGED
|
@@ -132,6 +132,7 @@ async function buildClaudeArgs(prompt, opts = {}) {
|
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
args.push("--model", settings.model || DEFAULT_CLAUDE_MODEL);
|
|
135
|
+
if (opts.maxTurns) args.push("--max-turns", String(opts.maxTurns));
|
|
135
136
|
if (settings.effort) args.push("--effort", settings.effort);
|
|
136
137
|
if (settings.budget) args.push("--max-budget-usd", String(settings.budget));
|
|
137
138
|
if (settings.permissionMode) args.push("--permission-mode", settings.permissionMode);
|
|
@@ -276,6 +277,11 @@ function compactSeedPrompt(summary, extras = {}) {
|
|
|
276
277
|
"Treat the following summary as the prior conversation context. Do not repeat it back unless asked.",
|
|
277
278
|
"Continue from this state in future turns.",
|
|
278
279
|
"",
|
|
280
|
+
// A seed turn that starts working (e.g. resuming a CI watch from the
|
|
281
|
+
// brief) can background a long-running command, and `claude -p` won't
|
|
282
|
+
// exit while a background task runs — that wedges the message queue.
|
|
283
|
+
"RIGHT NOW: reply with ONE short acknowledgement line only. Do NOT use any tools, run any commands, or start/resume any work in this turn — even if the summary, pending tasks, or recent turns suggest obvious next steps. Work resumes with the next user message.",
|
|
284
|
+
"",
|
|
279
285
|
"Before telling the user you lack context on something they reference:",
|
|
280
286
|
"1. Check the summary below.",
|
|
281
287
|
"2. Read the full archived brief on disk (path given below, when present) — it is the detailed version of this summary.",
|
|
@@ -545,10 +551,6 @@ async function compactActiveSession(cwd, opts = {}) {
|
|
|
545
551
|
if (opts.notify) await send(opts.message || "Context is getting large, compacting first so this stays fast…");
|
|
546
552
|
const summaryRun = await runClaudeCapture(compactSummaryPrompt(), cwd, { resumeSessionId: oldSessionId, skipAutoCompact: true, label: "compact-summary", timeoutMs: COMPACT_SUMMARY_TIMEOUT });
|
|
547
553
|
summary = summaryRun.text || "No prior context was returned by the summarizer.";
|
|
548
|
-
state[sessionKey] = null;
|
|
549
|
-
state.sessionUsage = { turns: 0, inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheCreationTokens: 0, costUsd: 0, lastInputTokens: 0 };
|
|
550
|
-
state.isFirstMessage = true;
|
|
551
|
-
saveState();
|
|
552
554
|
} finally {
|
|
553
555
|
state.isCompacting = false;
|
|
554
556
|
saveState();
|
|
@@ -559,9 +561,13 @@ async function compactActiveSession(cwd, opts = {}) {
|
|
|
559
561
|
// Only seed with the condensed version when the full text actually made it to disk.
|
|
560
562
|
const seedSummary = (condensed && briefPath) ? condensed : (condensed ? `${fullBrief}\n\n${condensed}` : fullBrief);
|
|
561
563
|
const repoFacts = collectRepoStateFacts(cwd);
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
564
|
+
// The old session id stays in place until the seed succeeds: if the seed
|
|
565
|
+
// dies (timeout, /stop, crash) the conversation keeps resuming the
|
|
566
|
+
// pre-compaction session instead of being orphaned. maxTurns: 1 hard-stops
|
|
567
|
+
// a seed that ignores the no-work instruction and reaches for tools.
|
|
568
|
+
const seedRun = await runClaudeCapture(compactSeedPrompt(seedSummary, { briefPath, condensed: !!(condensed && briefPath), repoFacts, recentTurns }), cwd, { fresh: true, skipAutoCompact: true, label: "compact-seed", timeoutMs: COMPACT_SUMMARY_TIMEOUT, maxTurns: 1 });
|
|
569
|
+
const newSessionId = seedRun.sessionId || null;
|
|
570
|
+
state[sessionKey] = newSessionId;
|
|
565
571
|
state.isFirstMessage = false;
|
|
566
572
|
state.lastCompactedAt = Date.now();
|
|
567
573
|
state.sessionUsage = { turns: 0, inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheCreationTokens: 0, costUsd: 0, lastInputTokens: 0 };
|
package/core/tasks.js
CHANGED
|
@@ -120,7 +120,20 @@ function prune(adapter, channelId) {
|
|
|
120
120
|
|
|
121
121
|
// Mark a task completed, then prune. Reports how many entries left the
|
|
122
122
|
// list so callers can tell the agent what disappeared.
|
|
123
|
+
//
|
|
124
|
+
// A plan can't be completed while any of its subtasks are still open:
|
|
125
|
+
// flipping the parent to [x] over [ ] children leaves a contradictory
|
|
126
|
+
// row that lingers (prune only drops a parent once all kids are done)
|
|
127
|
+
// and inflates the visible list. Refuse instead, naming the open kids
|
|
128
|
+
// so the caller finishes or removes them first.
|
|
123
129
|
function complete(adapter, channelId, id) {
|
|
130
|
+
const list = load(adapter, channelId);
|
|
131
|
+
const target = list.find((t) => t.id === id);
|
|
132
|
+
if (!target) return null;
|
|
133
|
+
if (!target.parentId) {
|
|
134
|
+
const openChildren = list.filter((c) => c.parentId === id && c.status !== "completed");
|
|
135
|
+
if (openChildren.length > 0) return { blocked: true, task: target, openChildren };
|
|
136
|
+
}
|
|
124
137
|
const t = update(adapter, channelId, id, { status: "completed" });
|
|
125
138
|
if (!t) return null;
|
|
126
139
|
const before = load(adapter, channelId).length;
|
package/package.json
CHANGED