@galda/cli 0.10.93 → 0.10.94

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/engine/lib.mjs CHANGED
@@ -2486,59 +2486,14 @@ export function dismissGoal({ goalStatus, outcome = 'continue' } = {}) {
2486
2486
  // remains unsure. Returning unsure is safe because the UI already presents
2487
2487
  // these exact choices without moving the goal.
2488
2488
  export const REPLY_INTENTS = [...REPLY_OUTCOMES, 'unsure'];
2489
- export function buildReplyIntentPrompt({ goalText, reply, currentProject = null, projects = [] } = {}) {
2490
- const projectLines = (projects ?? []).map((project) =>
2491
- `- ${project.id}: ${project.name || project.id} | root=${project.dir || '(unknown)'} | scope=${project.note || '(not specified)'}`);
2492
- return [
2493
- '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.',
2494
- 'Decide what the reply ASKS FOR exactly one of:',
2495
- '- "continue": fix or change the work that was just done.',
2496
- '- "rescope": stop and rethink; the request itself should now say something different.',
2497
- '- "split": a separate piece of work that should not touch this one.',
2498
- '- "answer": a question about the work. Nothing should be built or changed.',
2499
- '- "unsure": you genuinely cannot tell. Prefer this over guessing.',
2500
- 'Judge the full meaning. A question followed by a requested change is "continue".',
2501
- 'Also decide which project must receive the requested work.',
2502
- '- Keep targetProjectId equal to the current project for ordinary fixes to the reviewed work.',
2503
- '- If the current project is design/docs/prototype-only and the reply asks to implement or launch the chosen design in the real product, select the matching production/code project.',
2504
- '- Never infer a project that is not listed. If the target is ambiguous, use outcome "unsure" and targetProjectId null.',
2505
- 'Output ONLY one line of raw JSON: {"outcome":"continue|rescope|split|answer|unsure","targetProjectId":"listed-id-or-null"}.',
2506
- '',
2507
- `Current project: ${currentProject?.id || '(unknown)'}`,
2508
- 'Available projects:',
2509
- ...(projectLines.length ? projectLines : ['- (none)']),
2510
- '',
2511
- 'The request that was worked on:',
2512
- String(goalText ?? '').slice(0, 2000),
2513
- '',
2514
- 'Their reply:',
2515
- String(reply ?? '').slice(0, 4000),
2516
- ].join('\n');
2517
- }
2518
-
2519
- export function parseReplyIntent(raw) {
2520
- const unread = { outcome: 'unsure', read: false };
2521
- if (!raw || typeof raw !== 'string') return unread;
2522
- const m = raw.match(/\{[\s\S]*\}/);
2523
- if (!m) return unread;
2524
- let obj;
2525
- try { obj = JSON.parse(m[0]); } catch { return unread; }
2526
- if (!REPLY_INTENTS.includes(obj?.outcome)) return unread;
2527
- const targetProjectId = typeof obj.targetProjectId === 'string' && obj.targetProjectId.trim()
2528
- ? obj.targetProjectId.trim() : null;
2529
- return { outcome: obj.outcome, targetProjectId, read: true };
2530
- }
2531
-
2532
- // 宛先(docs/design/ONE-CONVERSATION.md §1.2)
2533
- //
2534
- // チャットで「#12 の件だけど」と書けたら、そのチケット宛て。**決定論で、我々の推測は
2535
- // ゼロ**。これが要るのは、今まで「同じ話かどうか」を我々の層が文字列の似かた(0.72)で
2536
- // 当てずっぽうに決めていたから——当たれば黙って合流し、外れれば別のカードが生えた。
2537
- // 人が番号を書けるなら、推測する理由が無い。
2538
- //
2539
- // 最初の `#N` だけを見る。番号が実在しなければ宛先なしとして扱う(=ふつうに新しい
2540
- // チケットになる)ので、`#` を含むだけの文章がどこかへ黙って吸い込まれることはない。
2541
- // 本文は削らない: 言われた言葉をそのまま作業AIへ渡す(P0 の「原文100%」と同じ約束)。
2489
+ // buildReplyIntentPrompt / parseReplyIntent lived here: the prompt and parser for a
2490
+ // utility LLM that decided what a Review reply MEANT before the goal's own agent saw
2491
+ // it. They are gone with their caller. Its "answer" bucket "a question about the
2492
+ // work. Nothing should be built or changed" — swallowed every politely-phrased
2493
+ // Japanese instruction, because there is no other way to ask politely in Japanese.
2494
+ // Claude Code and Codex already tell a question from an instruction; a second opinion
2495
+ // in front of them could only ever be worse-informed. See the `auto` branch of
2496
+ // /dismiss in engine/server.mjs and engine/test/reply-goes-to-the-agent.test.mjs.
2542
2497
  export function parseGoalAddress(text) {
2543
2498
  // `(?![\w-])` =「番号の直後に英数字やハイフンが続くなら、それは番号ではない」:
2544
2499
  // `#1f6f4a`(色) や `#12-3` を宛先と読み違えない。日本語は \w に入らないので
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, parseRequestedWorkspaceDir } from './lib.mjs';
24
- import { parseStreamEvents, parseCodexEvents, buildCodexArgs, parsePlan, refinePlanTasks, canEditGoal, canDeleteGoal, shouldAskClarification, workerExitReason, classifyWorkerFailure, isForcedStop, taskStatusAfterVerify, workerResultText, taskCountsAsComplete, resolveWantsPR, resolveGoalSource, buildGoalSummary, buildReviewAttemptLedger, 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, buildReplyIntentPrompt, parseReplyIntent, buildGoalMessage, serializeGoalMessage, parseGoalMessages, computeInternalQualityMetrics, classifyInternalQualityTaskError, resolveWorkspaceMode, goalSessionFor, setGoalAgentSession, switchGoalAgentSession, parseApprovalRequest } from './lib.mjs';
24
+ import { parseStreamEvents, parseCodexEvents, buildCodexArgs, parsePlan, refinePlanTasks, canEditGoal, canDeleteGoal, shouldAskClarification, workerExitReason, classifyWorkerFailure, isForcedStop, taskStatusAfterVerify, workerResultText, taskCountsAsComplete, resolveWantsPR, resolveGoalSource, buildGoalSummary, buildReviewAttemptLedger, 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, parseApprovalRequest } from './lib.mjs';
25
25
  import { createSerialQueue } from './lib.mjs';
26
26
  import { parseRequirementEvidence, unmappedChangedFiles } from './lib.mjs';
27
27
  import { isPrClosed } from './lib.mjs';
@@ -5522,29 +5522,13 @@ const server = createServer(async (req, res) => {
5522
5522
  // 「聞いてるだけ」への返事 (ONE-CONVERSATION.md §1.3 answer)。
5523
5523
  //
5524
5524
  // 答えるのは**作業した AI 本人**=その agent の session を resume する。状況一覧しか
5525
- // Resolve the app's `outcome:auto` contract before changing goal state. The
5526
- // utility call is classification-only; the goal's own agent/session still
5527
- // writes an answer or performs the requested change. If routing is unavailable,
5528
- // return unsure so a customer chooses explicitly instead of accidentally
5529
- // starting a ten-minute implementation worker for a question.
5530
- async function judgeReplyIntent(goal, reply) {
5531
- try {
5532
- const currentProject = projects.find((project) => project.id === goal.projectId) ?? null;
5533
- const r = await runUtility({
5534
- prompt: buildReplyIntentPrompt({ goalText: goal.text, reply, currentProject, projects }),
5535
- cwd: ROOT,
5536
- tools: 'Read',
5537
- });
5538
- const parsed = parseReplyIntent(r.result);
5539
- if (!parsed.read) return { outcome: 'unsure', targetProjectId: null };
5540
- if (parsed.targetProjectId && !projects.some((project) => project.id === parsed.targetProjectId)) {
5541
- return { outcome: 'unsure', targetProjectId: null };
5542
- }
5543
- return { outcome: parsed.outcome, targetProjectId: parsed.targetProjectId };
5544
- } catch {
5545
- return { outcome: 'unsure', targetProjectId: null };
5546
- }
5547
- }
5525
+ // judgeReplyIntent lived here: a utility-LLM pass that sorted a Review reply into
5526
+ // continue / rescope / split / answer BEFORE the goal's own agent saw it. It is
5527
+ // gone see the `auto` branch of /dismiss for the measured reason. Its stated
5528
+ // worry was "accidentally starting a ten-minute implementation worker for a
5529
+ // question", but the trade it actually made was the opposite one: it answered
5530
+ // real instructions, and the person paid several round trips to get the work
5531
+ // started at all. Deciding that is the agent's job.
5548
5532
 
5549
5533
  // 知らない別AIでは「なぜそうしたか」に答えられない(Masa確定 2026-07-22)。
5550
5534
  // コードは1文字も変わらない: ツールは Read だけ、permissionMode は 'plan'
@@ -5608,16 +5592,33 @@ async function answerGoalQuestion(goal, question) {
5608
5592
 
5609
5593
  if (auto) {
5610
5594
  if (!text) return json(res, 400, { error: 'text required' });
5611
- const routed = await judgeReplyIntent(goal, text);
5612
- outcome = routed.outcome;
5613
- requested.targetProjectId = routed.targetProjectId;
5614
- if (outcome === 'unsure') return json(res, 200, {
5615
- goal,
5616
- outcome,
5617
- choices: REPLY_OUTCOMES,
5618
- projectChoices: projects.map((project) => ({ id: project.id, name: project.name, current: project.id === goal.projectId })),
5619
- });
5620
- if (requested.targetProjectId && requested.targetProjectId !== goal.projectId) outcome = 'split';
5595
+ // The reply goes to the agent exactly as written. Galda does not decide
5596
+ // first what the person meant.
5597
+ //
5598
+ // This used to call a utility LLM (judgeReplyIntent) to sort the reply
5599
+ // into continue / rescope / split / answer before the agent ever saw it.
5600
+ // Its definition of "answer" was "a question about the work. Nothing
5601
+ // should be built or changed" — and Japanese has no other way to make a
5602
+ // polite request. Measured on the operator's own instance 2026-07-25..29:
5603
+ // 「案3で実装出来ますか?」「Aの方針で実装できますか!?」「あれ、prいるよね?」
5604
+ // 「okなんでprなげれますか?」「案がみえないのでローカルで確認できるように貰える?」
5605
+ // were all real instructions, and every one of them could be read as a
5606
+ // question. When it read one that way it created NO task at all, so the
5607
+ // request was answered instead of done and the person had to ask two or
5608
+ // three more times.
5609
+ //
5610
+ // The fix is not a better classifier. Claude Code and Codex already
5611
+ // decide correctly whether a message wants an answer or a change — that
5612
+ // is what they are. Galda's job is the interface around them, not a
5613
+ // second opinion in front of them. Note the `answer` route spawned an
5614
+ // agent too (Read-only, plan mode), so passing the reply through costs
5615
+ // no extra run — it removes one utility-LLM call per reply.
5616
+ //
5617
+ // Explicit routes are untouched: when the PERSON picks rescope / split /
5618
+ // answer from the chips, `requested.outcome` carries it and this branch
5619
+ // never runs. A human choosing where their words go is the interface
5620
+ // working; Galda guessing is not.
5621
+ outcome = 'continue';
5621
5622
  }
5622
5623
  // 返事の行き先4種 (ONE-CONVERSATION.md §1.3). 既定は今までどおり continue なので、
5623
5624
  // outcome を送ってこない今の UI の挙動は1ミリも変わらない。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@galda/cli",
3
- "version": "0.10.93",
3
+ "version": "0.10.94",
4
4
  "type": "module",
5
5
  "description": "Galda - hand off work to Claude Code or Codex, get proof back. Runs on your existing subscription, no extra API cost.",
6
6
  "scripts": {