@galda/cli 0.10.75 → 0.10.77
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 +38 -9
- package/engine/lib.mjs +4 -59
- package/engine/server.mjs +7 -31
- package/package.json +1 -1
package/app/index.html
CHANGED
|
@@ -733,6 +733,12 @@
|
|
|
733
733
|
@keyframes saFinishDraw{to{stroke-dashoffset:0}}
|
|
734
734
|
@keyframes saFinishFade{0%,72%{opacity:1}100%{opacity:0}}
|
|
735
735
|
@media (prefers-reduced-motion:reduce){.sa-finish,.sa-finish-mark,.sa-finish-mark path{animation:none}.sa-finish{background:transparent}}
|
|
736
|
+
/* A PR-less approval is just as complete as a merged PR. Keep this transient
|
|
737
|
+
celebration fixed above the board so it never changes document height or
|
|
738
|
+
scroll position. */
|
|
739
|
+
.approve-finish{position:fixed;inset:0;z-index:10000;display:grid;place-items:center;pointer-events:none;
|
|
740
|
+
background:rgba(0,0,0,.28);backdrop-filter:blur(2px);animation:saFinishFade .9s ease both}
|
|
741
|
+
.approve-finish .sa-finish-mark{width:112px;height:112px}
|
|
736
742
|
|
|
737
743
|
/* connect modal */
|
|
738
744
|
/* A confirm dialog (deleteConfirmOverlay etc.) must always sit above other
|
|
@@ -3354,12 +3360,14 @@ const withKey = (u) => {
|
|
|
3354
3360
|
};
|
|
3355
3361
|
|
|
3356
3362
|
// Theme names v3 (§6.5): English registered names only. Migrate persisted values
|
|
3357
|
-
// (old internal codenames → v3 names; dark→midnight; archived glass→
|
|
3363
|
+
// (old internal codenames → v3 names; dark→midnight; archived glass→midnight default).
|
|
3358
3364
|
const THEMES = ['studio', 'gradient', 'banff', 'midnight', 'cinema'];
|
|
3359
3365
|
const THEME_MIGRATE = { w4: 'studio', arc: 'gradient', nature: 'banff', glow: 'midnight', comp: 'cinema', dark: 'midnight', glass: 'gradient' };
|
|
3360
3366
|
const savedTheme = (() => {
|
|
3361
3367
|
const s = localStorage.getItem('theme');
|
|
3362
|
-
|
|
3368
|
+
// New installations start in Midnight. Existing valid values are preserved;
|
|
3369
|
+
// only a missing/invalid persisted value takes this default.
|
|
3370
|
+
const v = THEME_MIGRATE[s] ?? (THEMES.includes(s) ? s : 'midnight');
|
|
3363
3371
|
if (v !== s) localStorage.setItem('theme', v);
|
|
3364
3372
|
return v;
|
|
3365
3373
|
})();
|
|
@@ -5592,6 +5600,20 @@ function finishOpenReview(goal){
|
|
|
5592
5600
|
const delay = reduce ? 350 : 950;
|
|
5593
5601
|
setTimeout(() => { layer.remove(); saClose(); render(); }, delay);
|
|
5594
5602
|
}
|
|
5603
|
+
// PR-less review approval: use the same high-signal green completion mark as
|
|
5604
|
+
// the merged-PR path, but do not close or reflow the review surface. The canvas
|
|
5605
|
+
// burst starts at the actual Approve control, making the reward feel causal.
|
|
5606
|
+
function celebrateDirectApprove(cx, cy){
|
|
5607
|
+
const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
5608
|
+
if (!reduce) confettiBurst(cx, cy);
|
|
5609
|
+
const layer = document.createElement('div');
|
|
5610
|
+
layer.className = 'approve-finish';
|
|
5611
|
+
layer.setAttribute('role', 'status');
|
|
5612
|
+
layer.setAttribute('aria-label', 'Approved');
|
|
5613
|
+
layer.innerHTML = '<div class="sa-finish-mark"><svg viewBox="0 0 64 64" aria-hidden="true"><path d="M17 33l10 10 21-23"/></svg></div>';
|
|
5614
|
+
document.body.appendChild(layer);
|
|
5615
|
+
setTimeout(() => layer.remove(), reduce ? 350 : 950);
|
|
5616
|
+
}
|
|
5595
5617
|
async function setRequirementStatus(goalId, taskId, status){
|
|
5596
5618
|
const approvals = (state.reviewApprovals[goalId] ??= {});
|
|
5597
5619
|
approvals[taskId] = approvals[taskId] === status ? undefined : status; // click again to undo
|
|
@@ -8702,6 +8724,11 @@ async function postOutboxItem(item){
|
|
|
8702
8724
|
render();
|
|
8703
8725
|
return; // finally still runs posting.delete(); skip land()/upsert()
|
|
8704
8726
|
}
|
|
8727
|
+
// This optimistic turn was only for the short interval before the server
|
|
8728
|
+
// classified the input. A real goal is represented by the To Do lane and
|
|
8729
|
+
// its live Implementing log, never as a second message below the composer.
|
|
8730
|
+
const turns = state.chatTurns[item.projectId];
|
|
8731
|
+
if (turns) state.chatTurns[item.projectId] = turns.filter((turn) => turn.oid !== item.oid);
|
|
8705
8732
|
goal.imageUrls = item.imageUrls;
|
|
8706
8733
|
outbox.land(item.oid, goal.id);
|
|
8707
8734
|
upsert(state.goals, goal);
|
|
@@ -8875,8 +8902,9 @@ async function send(){
|
|
|
8875
8902
|
const fullText = batt.length
|
|
8876
8903
|
? `${text}${text ? '\n\n' : ''}添付画像(workerはこのパスをReadで確認できます): ${batt.map((a) => a.path).join(', ')}`
|
|
8877
8904
|
: text;
|
|
8878
|
-
//
|
|
8879
|
-
|
|
8905
|
+
// A bound send belongs to this goal's persisted thread. Do not also add it
|
|
8906
|
+
// to the center conversation feed: that made a task reply appear below the
|
|
8907
|
+
// composer as an unrelated "You" turn.
|
|
8880
8908
|
state.attach = [];
|
|
8881
8909
|
state.msg = { mode: null, model: null, effort: null, skill: null, later: false, review: false };
|
|
8882
8910
|
renderMsgHint();
|
|
@@ -8925,9 +8953,10 @@ async function send(){
|
|
|
8925
8953
|
status: 'sending', ts: Date.now(),
|
|
8926
8954
|
};
|
|
8927
8955
|
outbox.add(item); // saved locally BEFORE any network call
|
|
8928
|
-
//
|
|
8929
|
-
//
|
|
8930
|
-
|
|
8956
|
+
// The server decides whether this is a conversation or a real task. Show an
|
|
8957
|
+
// optimistic conversation turn while that decision is pending, then remove it
|
|
8958
|
+
// if the request becomes a task (tasks belong in To Do / Implementing only).
|
|
8959
|
+
(state.chatTurns[state.active] ||= []).push({ role: 'you', text, oid: item.oid, ts: Date.now() });
|
|
8931
8960
|
state.attach = [];
|
|
8932
8961
|
// mode/skill/later/review are per-message → clear after send (model stays sticky).
|
|
8933
8962
|
state.msg = { mode: null, model: null, effort: null, skill: null, later: false, review: false };
|
|
@@ -10406,7 +10435,7 @@ $('seeall').addEventListener('click', async (e) => {
|
|
|
10406
10435
|
// instead of celebrating/advancing; saCall already upserted the still-'review' goal.
|
|
10407
10436
|
const g = state.goals.find((x) => x.id === it.goalId);
|
|
10408
10437
|
if (g && g.status === 'review' && g.pr) { confirmMerge(g); return; }
|
|
10409
|
-
|
|
10438
|
+
celebrateDirectApprove(bx.left + bx.width / 2, bx.top + bx.height / 2); saAdvanceFocus(it.goalId); }); }
|
|
10410
10439
|
if (k === 'dis') return saAct(it, 2, 'reject').then((ok) => { if (ok) saAdvanceFocus(it.goalId); }); // 07-20c: Reject → Rejected column
|
|
10411
10440
|
if (k === 'arch') return saAct(it, 4, 'archive').then((ok) => { if (ok) saAdvanceFocus(it.goalId); });
|
|
10412
10441
|
if (k === 'rev') return saAct(it, 5, 'revert').then((ok) => { if (ok) saAdvanceFocus(it.goalId); });
|
|
@@ -10469,7 +10498,7 @@ document.addEventListener('keydown', (e) => {
|
|
|
10469
10498
|
if (g && g.status === 'review' && g.pr) { confirmMerge(g); return; } // 07-20c: PR goal → merge dialog
|
|
10470
10499
|
const el = $('seeall').querySelector(`.sa-item[data-gid="${gid}"] .sa-app`);
|
|
10471
10500
|
const box = el?.getBoundingClientRect();
|
|
10472
|
-
|
|
10501
|
+
celebrateDirectApprove(box ? box.left + box.width / 2 : innerWidth / 2, box ? box.top + box.height / 2 : innerHeight / 3);
|
|
10473
10502
|
saAdvanceFocus(gid);
|
|
10474
10503
|
});
|
|
10475
10504
|
} else saAct(it, 2, 'reject').then((ok) => { if (ok) saAdvanceFocus(gid); }); // 07-20c: D = Reject
|
package/engine/lib.mjs
CHANGED
|
@@ -1306,6 +1306,10 @@ export function looksIncomplete(text) {
|
|
|
1306
1306
|
export function isSuspiciouslyIncompleteDone({ status, changedFiles = [], result = '', hasVerdict = false } = {}) {
|
|
1307
1307
|
if (status !== 'done' || hasVerdict) return false;
|
|
1308
1308
|
if ((changedFiles ?? []).length > 0) return false;
|
|
1309
|
+
// A worker that only asks for permission to start a local server did not
|
|
1310
|
+
// produce a locally checkable result. Treating its clean exit as success
|
|
1311
|
+
// re-opened the old PR/review summary and hid this real blocker (#788).
|
|
1312
|
+
if (/\b(permission|approve running|approval required)\b|許可.*(必要|ください)|実行してよろしい/i.test(String(result))) return true;
|
|
1309
1313
|
return looksIncomplete(result);
|
|
1310
1314
|
}
|
|
1311
1315
|
|
|
@@ -2382,65 +2386,6 @@ export function dismissGoal({ goalStatus, outcome = 'continue' } = {}) {
|
|
|
2382
2386
|
return { ok: true, outcome: 'continue', status: 'running', spawnsWorker: true };
|
|
2383
2387
|
}
|
|
2384
2388
|
|
|
2385
|
-
// 誰が決めるか=**AI本人** (docs/design/ONE-CONVERSATION.md §1.4)
|
|
2386
|
-
//
|
|
2387
|
-
// 返事が来た時、それが「このまま直して」なのか「考え直したい」なのか「別件」なのか
|
|
2388
|
-
// 「ただ聞いただけ」なのかを、**我々の層で当てない**。キーワードでも類似度でもなく、
|
|
2389
|
-
// 人の言葉を1回だけ AI に渡して、返ってきた答えのとおりに配線する。0.72 の自動合流を
|
|
2390
|
-
// 消したのと同じ理由——この層が人の意図を推測し始めると、外れた時に黙って別のことを
|
|
2391
|
-
// する(今日そこを1つずつ潰してきた)。
|
|
2392
|
-
//
|
|
2393
|
-
// 迷った時だけ人に聞く: AI が unsure と答えたら**ゴールに触らず**選択肢を返す。人は
|
|
2394
|
-
// モードを普段選ばない。迷った時だけ選ぶ。
|
|
2395
|
-
export const REPLY_INTENTS = [...REPLY_OUTCOMES, 'unsure'];
|
|
2396
|
-
export function buildReplyIntentPrompt({ goalText, reply } = {}) {
|
|
2397
|
-
return [
|
|
2398
|
-
'You route replies in Agent Manager: a person delegated a coding task, an AI did it, and it came back for review. The person has now replied.',
|
|
2399
|
-
'Decide what the reply ASKS FOR — exactly one of:',
|
|
2400
|
-
'- "continue": fix or change the work that was just done (the usual case).',
|
|
2401
|
-
'- "rescope": stop and rethink; the request itself should now say something different.',
|
|
2402
|
-
'- "split": a separate piece of work that should not touch this one.',
|
|
2403
|
-
'- "answer": a question about the work. Nothing should be built or changed — they want to know something.',
|
|
2404
|
-
'- "unsure": you genuinely cannot tell. Prefer this over guessing: the person will be shown the choices.',
|
|
2405
|
-
'Judge the reply on its own words. Do not treat a question mark as proof of "answer", or an imperative as proof of "continue" — "why is this red? make it blue" asks for a change.',
|
|
2406
|
-
'Output ONLY one line of raw JSON: {"outcome":"continue|rescope|split|answer|unsure"}. No markdown, no code fence, no other text.',
|
|
2407
|
-
'',
|
|
2408
|
-
'The request that was worked on:',
|
|
2409
|
-
String(goalText ?? '').slice(0, 2000),
|
|
2410
|
-
'',
|
|
2411
|
-
'Their reply:',
|
|
2412
|
-
String(reply ?? '').slice(0, 4000),
|
|
2413
|
-
].join('\n');
|
|
2414
|
-
}
|
|
2415
|
-
|
|
2416
|
-
// Tolerant parser — never throws. It returns TWO things, and the difference
|
|
2417
|
-
// between them is the whole point:
|
|
2418
|
-
//
|
|
2419
|
-
// read: true … the AI answered, and we understood it. 'unsure' here means the
|
|
2420
|
-
// AI genuinely could not tell → ask the person (§1.4).
|
|
2421
|
-
// read: false … we could not get an answer at all (no agent installed, offline,
|
|
2422
|
-
// rate-limited, garbled output). The AI never said anything, so
|
|
2423
|
-
// there is nobody to have been unsure.
|
|
2424
|
-
//
|
|
2425
|
-
// Treating those two the same is a real bug, found by an existing test on
|
|
2426
|
-
// 2026-07-22: on a machine with no agent, a reply stopped doing ANYTHING — it put
|
|
2427
|
-
// the four choices on screen and waited. But if the agent cannot be reached, no
|
|
2428
|
-
// answer can be written either; the only useful thing a reply can still do is
|
|
2429
|
-
// what it always did — go back as rework, and run when the agent returns.
|
|
2430
|
-
// So the caller falls back to 'continue' when read is false. That is not a guess
|
|
2431
|
-
// about what the person meant: it is the documented default this endpoint has
|
|
2432
|
-
// always had when no outcome is given.
|
|
2433
|
-
export function parseReplyIntent(raw) {
|
|
2434
|
-
const unread = { outcome: 'unsure', read: false };
|
|
2435
|
-
if (!raw || typeof raw !== 'string') return unread;
|
|
2436
|
-
const m = raw.match(/\{[\s\S]*\}/);
|
|
2437
|
-
if (!m) return unread;
|
|
2438
|
-
let obj;
|
|
2439
|
-
try { obj = JSON.parse(m[0]); } catch { return unread; }
|
|
2440
|
-
if (!REPLY_INTENTS.includes(obj?.outcome)) return unread; // answered, but not in our words
|
|
2441
|
-
return { outcome: obj.outcome, read: true };
|
|
2442
|
-
}
|
|
2443
|
-
|
|
2444
2389
|
// 宛先(docs/design/ONE-CONVERSATION.md §1.2)
|
|
2445
2390
|
//
|
|
2446
2391
|
// チャットで「#12 の件だけど」と書けたら、そのチケット宛て。**決定論で、我々の推測は
|
package/engine/server.mjs
CHANGED
|
@@ -21,7 +21,7 @@ import { homedir } from 'node:os';
|
|
|
21
21
|
import { fileURLToPath } from 'node:url';
|
|
22
22
|
import { pathToFileURL } from 'node:url';
|
|
23
23
|
import { needsProjectFolder, folderLabel, chooseFolderScript, parseChosenFolder, buildFolderQuestion, resolveFolderAnswer, classifyFolderTarget, buildFolderInitConfirm, isFolderInitConfirmed, needsReviewPreference, buildReviewPreferenceQuestion, resolveReviewPreferenceAnswer, routeQuestionAnswer, notUnderstoodNote, parseRelocalizedQuestion, classifyAuthProbe, authBannerText, makeSigninChallenge, parseClaimResponse, isCommandOnPath } from './lib.mjs';
|
|
24
|
-
import { parseStreamEvents, parseCodexEvents, buildCodexArgs, parsePlan, refinePlanTasks, canEditGoal, canDeleteGoal, shouldAskClarification, workerExitReason, classifyWorkerFailure, isForcedStop, taskStatusAfterVerify, workerResultText, taskCountsAsComplete, resolveWantsPR, resolveGoalSource, buildGoalSummary, latestGoalOutcomeTask, buildDirtyWorkspacePrBlock, buildGoalProofMd, buildGoalPrBody, buildReviewSummaryPrompt, parseReviewSummary, buildReviewRuleSection, nextGoalStatus, isPrMerged, isPrApproved, reviewToDoneStatus, advanceReviewGoal, revertReviewGoal, approveGoal, dismissGoal, rejectGoal, reopenGoal, permissionModeFor, isPlanReview, nextAfterPlanApprove, GOAL_MODES, parseSkillFrontmatter, collectSkills, retestGoal, cancelRetestGoal, computeRetestOutcome, revertGoal, planRevertActions, archiveGoal, unarchiveGoal, undismissGoal, parseNumstat, truncateDiffText, sumUsage, resolveEntryUrl, rebasePreviewUrl, shouldCreateGoalPR, normalizePullRequestUrl, parseGitLog, diffNewCommits, replayQueueLog, reconcileOrphanGoals, reorderQueue, sortQueueByPriority, TASK_PRIORITIES, validateWorkflowColumns, DEFAULT_WORKFLOW_COLUMNS, validateReviewDefinition, DEFAULT_REVIEW_DEFINITION, resolveTestGate, buildEphemeralSeedLog, buildEphemeralSeedProjects, trimTaskActivityForState, buildAskContext, WORKER_TOOLS, workerPrompt, verifyCfAccessJwt, resolveIdentity, goalVisibleTo, clampParallelLimit, canStartMore, nextRunnableTasks, goalsConflict, detectConflicts, pickFoldTarget, autoFoldReviewsEnabled, parseGitUnifiedDiffLocations, hasTestRelevantChanges, parseFailingTestNames, classifyTestGate, classifyPrSafety, isInconclusiveTestRun, countInfraFlakes, classifyRunFailures, classifyVerifyGate, buildVerificationInfraError, verificationInfraErrorFrom, buildExecutionPlan, buildContextHandoffSummary, buildFailurePostmortem, appendFailureMemory, latestFailurePolicy, classifyChangeRisk, checkRunBudget, usageBudgetTokens, isRateLimited, nextResumeDelay, checkFreeTierLimit, resolveEntitlement, resolveCachedEntitlement, FREE_TIER_LIMITS, QUEUED_GOAL_STATUSES, verifyLicenseToken, licenseTokenPayload, shouldRefreshLicense, shouldEmitSetupCompleted, pickAnalyticsUid, shouldEmitFreeExhausted, detectRequestLanguage, testFailureReason, manualTestRetryPrompt, isNothingVerifiable, isNothingVerifiableForPR, isSuspiciouslyIncompleteDone, classifyComposerIntentHeuristic, detectPauseIntent, buildIntentPrompt, parseIntentResponse, buildBoardSnapshot, validateBoardSnapshot, boardIsEmpty, decideBoardPull, resolveConnectedAgents, pickAvailableAgent, pickUtilityAgent, utilityModel, liveTakeoverDecision, shouldOpenBrowserOnBoot, goalTaskTitle, buildGoalTask, shouldUsePlannerForGoal, RUN_DECL_FILE, parseRunDeclaration, SHOT_DIR, collectShotNames, parseGoalAddress, REPLY_OUTCOMES,
|
|
24
|
+
import { parseStreamEvents, parseCodexEvents, buildCodexArgs, parsePlan, refinePlanTasks, canEditGoal, canDeleteGoal, shouldAskClarification, workerExitReason, classifyWorkerFailure, isForcedStop, taskStatusAfterVerify, workerResultText, taskCountsAsComplete, resolveWantsPR, resolveGoalSource, buildGoalSummary, latestGoalOutcomeTask, buildDirtyWorkspacePrBlock, buildGoalProofMd, buildGoalPrBody, buildReviewSummaryPrompt, parseReviewSummary, buildReviewRuleSection, nextGoalStatus, isPrMerged, isPrApproved, reviewToDoneStatus, advanceReviewGoal, revertReviewGoal, approveGoal, dismissGoal, rejectGoal, reopenGoal, permissionModeFor, isPlanReview, nextAfterPlanApprove, GOAL_MODES, parseSkillFrontmatter, collectSkills, retestGoal, cancelRetestGoal, computeRetestOutcome, revertGoal, planRevertActions, archiveGoal, unarchiveGoal, undismissGoal, parseNumstat, truncateDiffText, sumUsage, resolveEntryUrl, rebasePreviewUrl, shouldCreateGoalPR, normalizePullRequestUrl, parseGitLog, diffNewCommits, replayQueueLog, reconcileOrphanGoals, reorderQueue, sortQueueByPriority, TASK_PRIORITIES, validateWorkflowColumns, DEFAULT_WORKFLOW_COLUMNS, validateReviewDefinition, DEFAULT_REVIEW_DEFINITION, resolveTestGate, buildEphemeralSeedLog, buildEphemeralSeedProjects, trimTaskActivityForState, buildAskContext, WORKER_TOOLS, workerPrompt, verifyCfAccessJwt, resolveIdentity, goalVisibleTo, clampParallelLimit, canStartMore, nextRunnableTasks, goalsConflict, detectConflicts, pickFoldTarget, autoFoldReviewsEnabled, parseGitUnifiedDiffLocations, hasTestRelevantChanges, parseFailingTestNames, classifyTestGate, classifyPrSafety, isInconclusiveTestRun, countInfraFlakes, classifyRunFailures, classifyVerifyGate, buildVerificationInfraError, verificationInfraErrorFrom, buildExecutionPlan, buildContextHandoffSummary, buildFailurePostmortem, appendFailureMemory, latestFailurePolicy, classifyChangeRisk, checkRunBudget, usageBudgetTokens, isRateLimited, nextResumeDelay, checkFreeTierLimit, resolveEntitlement, resolveCachedEntitlement, FREE_TIER_LIMITS, QUEUED_GOAL_STATUSES, verifyLicenseToken, licenseTokenPayload, shouldRefreshLicense, shouldEmitSetupCompleted, pickAnalyticsUid, shouldEmitFreeExhausted, detectRequestLanguage, testFailureReason, manualTestRetryPrompt, isNothingVerifiable, isNothingVerifiableForPR, isSuspiciouslyIncompleteDone, classifyComposerIntentHeuristic, detectPauseIntent, buildIntentPrompt, parseIntentResponse, buildBoardSnapshot, validateBoardSnapshot, boardIsEmpty, decideBoardPull, resolveConnectedAgents, pickAvailableAgent, pickUtilityAgent, utilityModel, liveTakeoverDecision, shouldOpenBrowserOnBoot, goalTaskTitle, buildGoalTask, shouldUsePlannerForGoal, RUN_DECL_FILE, parseRunDeclaration, SHOT_DIR, collectShotNames, parseGoalAddress, REPLY_OUTCOMES, buildGoalMessage, serializeGoalMessage, parseGoalMessages, computeInternalQualityMetrics, classifyInternalQualityTaskError, resolveWorkspaceMode, goalSessionFor, setGoalAgentSession, switchGoalAgentSession } from './lib.mjs';
|
|
25
25
|
import { createSerialQueue } from './lib.mjs';
|
|
26
26
|
import { isPrClosed } from './lib.mjs';
|
|
27
27
|
import { collectProjectRules } from './lib.mjs';
|
|
@@ -5214,24 +5214,6 @@ const server = createServer(async (req, res) => {
|
|
|
5214
5214
|
return json(res, 200, payload);
|
|
5215
5215
|
}
|
|
5216
5216
|
|
|
5217
|
-
// 返事の行き先を AI 本人に決めてもらう (ONE-CONVERSATION.md §1.4)。
|
|
5218
|
-
//
|
|
5219
|
-
// 判定は1往復だけの軽い仕事なので、作業した本人ではなくユーティリティのモデルに
|
|
5220
|
-
// 聞く(答えを書く answer 本体は本人に聞く——あちらは中身を知っている必要がある)。
|
|
5221
|
-
// 呼べなかった時は 'unsure'。「たぶん continue」で走り出すより、人に1タップ選ばせる
|
|
5222
|
-
// ほうが安い。
|
|
5223
|
-
async function judgeReplyIntent(goal, reply) {
|
|
5224
|
-
let parsed;
|
|
5225
|
-
try {
|
|
5226
|
-
const r = await runUtility({ prompt: buildReplyIntentPrompt({ goalText: goal.text, reply }), cwd: ROOT, tools: 'Read' });
|
|
5227
|
-
parsed = parseReplyIntent(r.result);
|
|
5228
|
-
} catch { parsed = { outcome: 'unsure', read: false }; }
|
|
5229
|
-
// Could not ask at all → do what a reply has always done (continue). Blocking the
|
|
5230
|
-
// reply on four choices would leave a machine with no reachable agent unable to
|
|
5231
|
-
// send anything back, and an answer could not have been written there anyway.
|
|
5232
|
-
return parsed.read ? parsed.outcome : 'continue';
|
|
5233
|
-
}
|
|
5234
|
-
|
|
5235
5217
|
// 「聞いてるだけ」への返事 (ONE-CONVERSATION.md §1.3 answer)。
|
|
5236
5218
|
//
|
|
5237
5219
|
// 答えるのは**作業した AI 本人**=その agent の session を resume する。状況一覧しか
|
|
@@ -5285,26 +5267,20 @@ async function answerGoalQuestion(goal, question) {
|
|
|
5285
5267
|
let body = '';
|
|
5286
5268
|
req.on('data', (d) => { body += d; });
|
|
5287
5269
|
req.on('end', async () => {
|
|
5288
|
-
let text = '', outcome = 'continue',
|
|
5270
|
+
let text = '', outcome = 'continue', requested = {};
|
|
5289
5271
|
try {
|
|
5290
5272
|
requested = JSON.parse(body || '{}');
|
|
5291
5273
|
text = String(requested.text ?? '').trim();
|
|
5292
5274
|
if (REPLY_OUTCOMES.includes(requested.outcome)) outcome = requested.outcome;
|
|
5293
|
-
auto = requested.outcome === 'auto';
|
|
5294
5275
|
} catch {}
|
|
5295
5276
|
const worker = applyReplyWorker(goal, requested);
|
|
5296
5277
|
if (!worker.ok) return json(res, 409, { error: worker.error });
|
|
5297
5278
|
|
|
5298
|
-
//
|
|
5299
|
-
//
|
|
5300
|
-
//
|
|
5301
|
-
//
|
|
5302
|
-
if (auto) {
|
|
5303
|
-
if (!text) return json(res, 400, { error: 'text required' });
|
|
5304
|
-
const judged = await judgeReplyIntent(goal, text);
|
|
5305
|
-
if (judged === 'unsure') return json(res, 200, { goal, outcome: 'unsure', choices: REPLY_OUTCOMES });
|
|
5306
|
-
outcome = judged;
|
|
5307
|
-
}
|
|
5279
|
+
// A review reply is transport, not a classification request. Legacy clients
|
|
5280
|
+
// send outcome:'auto'; it deliberately means the deterministic default:
|
|
5281
|
+
// preserve the exact words and resume this goal's agent session. Specialized
|
|
5282
|
+
// outcomes work only when an explicit UI action sends one of REPLY_OUTCOMES.
|
|
5283
|
+
if (requested.outcome === 'auto' && !text) return json(res, 400, { error: 'text required' });
|
|
5308
5284
|
// 返事の行き先4種 (ONE-CONVERSATION.md §1.3). 既定は今までどおり continue なので、
|
|
5309
5285
|
// outcome を送ってこない今の UI の挙動は1ミリも変わらない。
|
|
5310
5286
|
const result = dismissGoal({ goalStatus: goal.status, outcome });
|
package/package.json
CHANGED