@galda/cli 0.10.65 → 0.10.66
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/app/index.html +11 -8
- package/engine/server.mjs +24 -6
- package/package.json +1 -1
package/app/index.html
CHANGED
|
@@ -4365,7 +4365,7 @@ function renderStream(){
|
|
|
4365
4365
|
if (e.key !== 'Enter' || e.isComposing || !inp.value.trim()) return;
|
|
4366
4366
|
const text = inp.value.trim(); inp.value = ''; inp.disabled = true;
|
|
4367
4367
|
const r = await fetch(withKey(`/api/goals/${inp.dataset.goal}/reply`), { method: 'POST',
|
|
4368
|
-
headers: { 'content-type': 'application/json' }, body: JSON.stringify({ text }) });
|
|
4368
|
+
headers: { 'content-type': 'application/json' }, body: JSON.stringify(replyWorkerPayload({ text })) });
|
|
4369
4369
|
if (!r.ok) showErr('Reply failed — the goal may not have started yet.');
|
|
4370
4370
|
inp.disabled = false;
|
|
4371
4371
|
});
|
|
@@ -4948,7 +4948,7 @@ function openGoalheadReply(id, btn){
|
|
|
4948
4948
|
const snd = document.createElement('button'); snd.className = 'qbtn ok'; snd.textContent = '↑';
|
|
4949
4949
|
wrap.appendChild(inp); wrap.appendChild(snd); head.insertAdjacentElement('afterend', wrap); inp.focus();
|
|
4950
4950
|
const send = async () => { const t = inp.value.trim(); if (!t) return;
|
|
4951
|
-
const r = await fetch(withKey(`/api/goals/${id}/reply`), { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ text: t }) });
|
|
4951
|
+
const r = await fetch(withKey(`/api/goals/${id}/reply`), { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(replyWorkerPayload({ text: t })) });
|
|
4952
4952
|
if (!r.ok) showErr('未開始のゴールは編集を使ってください'); await refresh(); };
|
|
4953
4953
|
inp.onkeydown = (e) => { if (e.key === 'Enter' && !e.isComposing) { e.preventDefault(); send(); } if (e.key === 'Escape') wrap.remove(); };
|
|
4954
4954
|
snd.onclick = send;
|
|
@@ -5405,7 +5405,7 @@ function renderReviewChecklist(){
|
|
|
5405
5405
|
b.disabled = true; input.disabled = true;
|
|
5406
5406
|
const r = await fetch(withKey(`/api/goals/${goalId}/reply`), { method: 'POST',
|
|
5407
5407
|
headers: { 'content-type': 'application/json' },
|
|
5408
|
-
body: JSON.stringify({ text: `[Fix request for requirement #${t?.num ?? ''} "${t?.title ?? ''}"] ${text}` }) });
|
|
5408
|
+
body: JSON.stringify(replyWorkerPayload({ text: `[Fix request for requirement #${t?.num ?? ''} "${t?.title ?? ''}"] ${text}` })) });
|
|
5409
5409
|
if (r.ok) { input.value = ''; input.placeholder = 'Sent — the worker will pick it up'; }
|
|
5410
5410
|
else showErr('Failed to send the fix request.');
|
|
5411
5411
|
b.disabled = false; input.disabled = false;
|
|
@@ -5649,9 +5649,12 @@ const REPLY_CHOICE_LABEL = { continue: 'Fix this', rescope: 'Rethink it', split:
|
|
|
5649
5649
|
// is the task/workflow vocabulary and this is neither (CDO §5).
|
|
5650
5650
|
const REPLY_WAIT_ROUTING = 'Thinking…';
|
|
5651
5651
|
const REPLY_WAIT_ANSWERING = 'Answering…';
|
|
5652
|
+
function replyWorkerPayload(extra = {}){
|
|
5653
|
+
return { ...extra, agent: state.connectedApp, model: state.stickyModel, effort: state.stickyEffort };
|
|
5654
|
+
}
|
|
5652
5655
|
async function postReply(goalId, text, outcome = 'auto'){
|
|
5653
5656
|
const r = await fetch(withKey(`/api/goals/${goalId}/dismiss`), { method: 'POST',
|
|
5654
|
-
headers: { 'content-type': 'application/json' }, body: JSON.stringify({ text, outcome }) });
|
|
5657
|
+
headers: { 'content-type': 'application/json' }, body: JSON.stringify(replyWorkerPayload({ text, outcome })) });
|
|
5655
5658
|
if (!r.ok) throw new Error();
|
|
5656
5659
|
return r.json();
|
|
5657
5660
|
}
|
|
@@ -6139,7 +6142,7 @@ async function sendGoalInstruction(goalId, text){
|
|
|
6139
6142
|
$('gdSend').disabled = true; $('gdDiscard').disabled = true;
|
|
6140
6143
|
try {
|
|
6141
6144
|
const r = await fetch(withKey(`/api/goals/${goalId}/reply`), { method: 'POST',
|
|
6142
|
-
headers: { 'content-type': 'application/json' }, body: JSON.stringify({ text }) });
|
|
6145
|
+
headers: { 'content-type': 'application/json' }, body: JSON.stringify(replyWorkerPayload({ text })) });
|
|
6143
6146
|
if (!r.ok) throw new Error();
|
|
6144
6147
|
upsert(state.tasks, await r.json());
|
|
6145
6148
|
const g = state.goals.find((x) => x.id === goalId);
|
|
@@ -7095,7 +7098,7 @@ function renderFsTodo(){
|
|
|
7095
7098
|
// hover-toolbar actions → same endpoints as the SL1 goalhead toolbar
|
|
7096
7099
|
for (const b of el.querySelectorAll('[data-fsreply]')) b.onclick = (e) => { e.stopPropagation();
|
|
7097
7100
|
fsInlineInput(b.closest('.trow'), '', 'Reply to this goal…', async (text) => {
|
|
7098
|
-
const r = await fetch(withKey(`/api/goals/${b.dataset.fsreply}/reply`), { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ text }) });
|
|
7101
|
+
const r = await fetch(withKey(`/api/goals/${b.dataset.fsreply}/reply`), { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(replyWorkerPayload({ text })) });
|
|
7099
7102
|
if (!r.ok) showErr('未開始のゴールは編集を使ってください'); await refresh();
|
|
7100
7103
|
}); };
|
|
7101
7104
|
for (const b of el.querySelectorAll('[data-fsedit]')) b.onclick = (e) => { e.stopPropagation();
|
|
@@ -8577,7 +8580,7 @@ async function send(){
|
|
|
8577
8580
|
} else {
|
|
8578
8581
|
// running/partial/failed/interrupted/blocked → a plain follow-up on the same goal.
|
|
8579
8582
|
const r = await fetch(withKey(`/api/goals/${bind.goal.id}/reply`), { method: 'POST',
|
|
8580
|
-
headers: { 'content-type': 'application/json' }, body: JSON.stringify({ text: fullText }) });
|
|
8583
|
+
headers: { 'content-type': 'application/json' }, body: JSON.stringify(replyWorkerPayload({ text: fullText })) });
|
|
8581
8584
|
if (!r.ok) throw new Error();
|
|
8582
8585
|
const g = await r.json(); const goal = g.goal ?? g;
|
|
8583
8586
|
if (goal?.id) upsert(state.goals, goal);
|
|
@@ -9781,7 +9784,7 @@ async function saSendReply(it, i, text){
|
|
|
9781
9784
|
it.st = 3; it.rstatus = 'working'; SA.cur = goalId;
|
|
9782
9785
|
saPaintCard(goalId);
|
|
9783
9786
|
try {
|
|
9784
|
-
const r = await fetch(withKey(`/api/goals/${goalId}/reply`), { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ text: workerText }) });
|
|
9787
|
+
const r = await fetch(withKey(`/api/goals/${goalId}/reply`), { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(replyWorkerPayload({ text: workerText })) });
|
|
9785
9788
|
if (!r.ok) throw new Error('reply failed');
|
|
9786
9789
|
if (isAttn) {
|
|
9787
9790
|
// A Needs-you reply picks the goal back up → it belongs in Doing, not lingering in
|
package/engine/server.mjs
CHANGED
|
@@ -1973,6 +1973,19 @@ function resumeProject(projectId) {
|
|
|
1973
1973
|
|
|
1974
1974
|
// Slack風スレッド返信タスクの生成(/api/goals/:id/reply と /api/goals/:id/dismiss で共有)。
|
|
1975
1975
|
// runTask()のtask.reply分岐がgoal.sessionIdをresumeに渡して同じ会話を続ける。
|
|
1976
|
+
function applyReplyWorker(goal, requested = {}) {
|
|
1977
|
+
if (requested.agent == null) return { ok: true };
|
|
1978
|
+
if (!WORKER_AGENTS.includes(requested.agent)) return { ok: false, error: 'unsupported agent' };
|
|
1979
|
+
const agent = workerAgent(requested.agent);
|
|
1980
|
+
if (!AVAILABLE_AGENTS.includes(agent)) return { ok: false, error: `${agent} is not connected on this device` };
|
|
1981
|
+
const switchedAgent = agent !== goal.agent;
|
|
1982
|
+
goal.agent = agent;
|
|
1983
|
+
goal.model = workerModel(agent, requested.model);
|
|
1984
|
+
goal.effort = workerEffort(agent, requested.effort);
|
|
1985
|
+
if (switchedAgent) goal.sessionId = null;
|
|
1986
|
+
return { ok: true };
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1976
1989
|
function queueReplyTask(goal, text, { from = 'manager', via = 'composer' } = {}) {
|
|
1977
1990
|
// Every follow-up on a goal is part of its conversation (§1.1). Recorded here
|
|
1978
1991
|
// rather than at each call site so an internally-generated nudge (auto-retest
|
|
@@ -4522,8 +4535,11 @@ const server = createServer(async (req, res) => {
|
|
|
4522
4535
|
req.on('data', (d) => { body += d; });
|
|
4523
4536
|
req.on('end', () => {
|
|
4524
4537
|
try {
|
|
4525
|
-
const
|
|
4538
|
+
const requested = JSON.parse(body);
|
|
4539
|
+
const { text } = requested;
|
|
4526
4540
|
if (!text?.trim()) return json(res, 400, { error: 'text required' });
|
|
4541
|
+
const worker = applyReplyWorker(goal, requested);
|
|
4542
|
+
if (!worker.ok) return json(res, 409, { error: worker.error });
|
|
4527
4543
|
// Reply picks a parked goal back up into the work queue. 'review' MUST be
|
|
4528
4544
|
// here: a completed goal awaiting your approve/reject is 'review', and a
|
|
4529
4545
|
// reply to it means "not done — keep working" (Masa 2026-07-24: replied to
|
|
@@ -4814,13 +4830,15 @@ async function answerGoalQuestion(goal, question) {
|
|
|
4814
4830
|
let body = '';
|
|
4815
4831
|
req.on('data', (d) => { body += d; });
|
|
4816
4832
|
req.on('end', async () => {
|
|
4817
|
-
let text = '', outcome = 'continue', auto = false;
|
|
4833
|
+
let text = '', outcome = 'continue', auto = false, requested = {};
|
|
4818
4834
|
try {
|
|
4819
|
-
|
|
4820
|
-
text = String(
|
|
4821
|
-
if (REPLY_OUTCOMES.includes(
|
|
4822
|
-
auto =
|
|
4835
|
+
requested = JSON.parse(body || '{}');
|
|
4836
|
+
text = String(requested.text ?? '').trim();
|
|
4837
|
+
if (REPLY_OUTCOMES.includes(requested.outcome)) outcome = requested.outcome;
|
|
4838
|
+
auto = requested.outcome === 'auto';
|
|
4823
4839
|
} catch {}
|
|
4840
|
+
const worker = applyReplyWorker(goal, requested);
|
|
4841
|
+
if (!worker.ok) return json(res, 409, { error: worker.error });
|
|
4824
4842
|
|
|
4825
4843
|
// outcome:'auto' = 決めるのは AI 本人 (§1.4)。人の言葉を1回だけ渡して、返って
|
|
4826
4844
|
// きた行き先のとおりに配線する。我々はキーワードも類似度も見ない。
|
package/package.json
CHANGED