@galda/cli 0.10.9 → 0.10.11
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 +53 -8
- package/engine/lib.mjs +50 -4
- package/engine/relay-client.mjs +7 -0
- package/engine/server.mjs +31 -13
- package/package.json +1 -1
package/app/index.html
CHANGED
|
@@ -1922,6 +1922,10 @@
|
|
|
1922
1922
|
Two pixel frames (neutral black-eyes / laughing), no arm-raise, no official confetti. reduce-motion=static. */
|
|
1923
1923
|
#fsClawdHero .cstage{position:relative;width:170px;height:132px;display:flex;align-items:flex-end;justify-content:center;margin:0 auto 2px}#fsClawdHero .cw{position:relative;width:150px;transform-origin:50% 100%;will-change:transform;animation:chHop 2.4s linear infinite}#fsClawdHero .cw img{position:absolute;left:0;bottom:0;width:100%;display:block;image-rendering:pixelated}#fsClawdHero .cw .chNeu{animation:chNeu 2.4s linear infinite}#fsClawdHero .cw .chLau{opacity:0;animation:chLau 2.4s linear infinite}#fsClawdHero .chConf{position:absolute;left:50%;top:34px;width:0;height:0;pointer-events:none;z-index:3}#fsClawdHero .chConf i{position:absolute;width:4px;height:7px;border-radius:1.5px;opacity:0;transform:translate(-50%,-50%);animation:chPop 2.4s cubic-bezier(.2,.7,.3,1) infinite}@keyframes chHop{0%,52%{transform:translateY(0) scale(1,1)}55%{transform:translateY(0) scale(1.08,.85)}58%{transform:translateY(-16px) scale(.95,1.14)}61%{transform:translateY(-27px) scale(.98,1.05)}64%{transform:translateY(-31px) scale(1,1)}67%{transform:translateY(-26px) scale(.98,1.05)}70%{transform:translateY(-14px) scale(.95,1.12)}73%{transform:translateY(0) scale(1.1,.82)}77%{transform:translateY(-4px) scale(.99,1.02)}80%,100%{transform:translateY(0) scale(1,1)}}@keyframes chLau{0%,53.8%{opacity:0}54%,85.8%{opacity:1}86%,100%{opacity:0}}@keyframes chNeu{0%,53.8%{opacity:1}54%,85.8%{opacity:0}86%,100%{opacity:1}}@keyframes chPop{0%,54%{opacity:0;transform:translate(0,6px) rotate(0) scale(.5)}60%{opacity:1;transform:translate(calc(var(--dx)*.5),calc(var(--dy)*.5)) rotate(calc(var(--rot)*.5)) scale(1)}72%{opacity:1;transform:translate(var(--dx),var(--dy)) rotate(var(--rot))}100%{opacity:0;transform:translate(calc(var(--dx)*1.1),calc(var(--dy) + 44px)) rotate(calc(var(--rot)*1.4)) scale(.9)}}@media (prefers-reduced-motion:reduce){#fsClawdHero .cw,#fsClawdHero .cw .chNeu,#fsClawdHero .cw .chLau{animation:none}#fsClawdHero .cw .chLau{opacity:0}#fsClawdHero .chConf{display:none}}
|
|
1924
1924
|
#fsRoot.clawdhero #fsClawdHero{display:block}
|
|
1925
|
+
/* Non-Claude agent (e.g. Codex) hero: no Clawd mascot (that's a Claude Code brand mark,
|
|
1926
|
+
DESIGN-RULES v3 §6.5) — a plain glyph badge stands in so the hero still names the
|
|
1927
|
+
agent actually connected, instead of always reading "Claude Code" (Masa 2026-07-16). */
|
|
1928
|
+
#fsClawdHero .chBadge{display:grid;place-items:center;width:64px;height:64px;border-radius:16px;background:var(--ink);color:var(--panel);font:700 22px/1 var(--mono);letter-spacing:0}
|
|
1925
1929
|
#fsRoot .cc-who{display:flex;align-items:center;gap:9px;padding:2px 0 6px}
|
|
1926
1930
|
#fsRoot .cc-who .cs-n{font-size:12px;font-weight:600;color:var(--ink)}
|
|
1927
1931
|
#fsRoot .cc-who .cs-d{font-family:var(--mono);font-size:9.5px;color:var(--ink3);letter-spacing:.05em}
|
|
@@ -2387,7 +2391,7 @@
|
|
|
2387
2391
|
<div class="perahead" style="border-bottom:1px solid var(--hair)"><div class="peratitle">Settings</div><span style="flex:1"></span><button class="mbtn ghost" id="setClose">Close</button></div>
|
|
2388
2392
|
<div style="padding:16px">
|
|
2389
2393
|
<div class="setrow"><div><div class="setlabel">Content language</div><div class="setsub">UI is English; this sets the language of AI summaries and PR bodies.</div></div>
|
|
2390
|
-
<div class="segbtn" id="langSeg"><button data-lang="en">English</button><button data-lang="ja">日本語</button></div></div>
|
|
2394
|
+
<div class="segbtn" id="langSeg"><button data-lang="auto">Auto</button><button data-lang="en">English</button><button data-lang="ja">日本語</button></div></div>
|
|
2391
2395
|
</div>
|
|
2392
2396
|
</div></div>
|
|
2393
2397
|
<!-- Slack-style centered delete confirmation, replacing native confirm() for every goal
|
|
@@ -2586,7 +2590,7 @@ function prioBadge(t){
|
|
|
2586
2590
|
// Mirrors engine/lib.mjs DEFAULT_REVIEW_DEFINITION — kept in sync manually,
|
|
2587
2591
|
// see the comment on DEFAULT_WORKFLOW_COLUMNS above.
|
|
2588
2592
|
const DEFAULT_REVIEW_DEFINITION = {
|
|
2589
|
-
requirePR: true, requireVerifyPass: false, description: '', defaultWantsPR: false, language: '
|
|
2593
|
+
requirePR: true, requireVerifyPass: false, description: '', defaultWantsPR: false, language: 'auto',
|
|
2590
2594
|
prSections: { summary: true, whatChanged: true, howToReview: true, tests: true, screenshots: true, risk: true },
|
|
2591
2595
|
reviewCard: { screenshots: true, beforeAfter: false },
|
|
2592
2596
|
};
|
|
@@ -2598,7 +2602,10 @@ function reviewDefinitionFor(projectId){ return state.reviewDefinitions[projectI
|
|
|
2598
2602
|
// review-definitions.json entries are never re-validated on server load), so
|
|
2599
2603
|
// merge with the defaults key-by-key here rather than assuming they exist.
|
|
2600
2604
|
function reviewCardConfigFor(projectId){ return { ...DEFAULT_REVIEW_DEFINITION.reviewCard, ...(reviewDefinitionFor(projectId).reviewCard ?? {}) }; }
|
|
2601
|
-
|
|
2605
|
+
// 'auto'|'ja'|'en' verbatim — 'auto' (default) means the Manager matches each
|
|
2606
|
+
// goal's own request language, same as the planner/worker; ja/en pin the
|
|
2607
|
+
// project to one language regardless of what a given goal was typed in.
|
|
2608
|
+
function reviewLangFor(projectId){ const l = reviewDefinitionFor(projectId).language; return (l === 'en' || l === 'ja') ? l : 'auto'; }
|
|
2602
2609
|
|
|
2603
2610
|
function esc(s){ const d = document.createElement('div'); d.textContent = s ?? ''; return d.innerHTML; }
|
|
2604
2611
|
|
|
@@ -3911,6 +3918,7 @@ function renderRdEditor(){
|
|
|
3911
3918
|
|
|
3912
3919
|
<div class="rdsub">出力言語 / Output language</div>
|
|
3913
3920
|
<label class="rdrow"><select id="rdLanguage">
|
|
3921
|
+
<option value="auto"${state.rdDraft.language === 'auto' ? ' selected' : ''}>Auto (match request)</option>
|
|
3914
3922
|
<option value="ja"${state.rdDraft.language === 'ja' ? ' selected' : ''}>日本語</option>
|
|
3915
3923
|
<option value="en"${state.rdDraft.language === 'en' ? ' selected' : ''}>English</option>
|
|
3916
3924
|
</select>
|
|
@@ -5329,14 +5337,49 @@ function applyFsBand(){
|
|
|
5329
5337
|
band.style.backgroundImage = FS_BANDS[i];
|
|
5330
5338
|
[...document.querySelectorAll('#fsBandPop [data-fsband]')].forEach((t) => t.classList.toggle('on', Number(t.dataset.fsband) === i));
|
|
5331
5339
|
}
|
|
5340
|
+
// Clawd HERO (§4.5 初期状態): big Clawd + "Claude Code / CONNECTED" in the
|
|
5341
|
+
// implementing column. Visibility is state-driven (renderFsFeed toggles
|
|
5342
|
+
// #fsRoot.clawdhero when the column has zero activity — no timers).
|
|
5343
|
+
// Non-Claude agents (Codex) get a plain glyph badge instead of Clawd — Clawd
|
|
5344
|
+
// is a Claude Code brand mark (DESIGN-RULES v3 §6.5), not a generic "an agent
|
|
5345
|
+
// is connected" icon, so it must not keep showing "Claude Code" once the
|
|
5346
|
+
// composer's agent picker is switched to Codex (Masa 2026-07-16).
|
|
5347
|
+
const CLAWD_HERO_IMGS = '<img class="chNeu" src="data:image/png;base64,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" alt=""><img class="chLau" src="data:image/png;base64,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" alt=""></div></div>';
|
|
5348
|
+
function clawdHeroHTML(agent){
|
|
5349
|
+
const app = connectedAppMeta(agent);
|
|
5350
|
+
const stage = agent === 'codex'
|
|
5351
|
+
? `<div class="cstage"><div class="chBadge">${app.icon}</div></div>`
|
|
5352
|
+
: `<div class="cstage"><div class="chConf"></div><div class="cw">${CLAWD_HERO_IMGS}`;
|
|
5353
|
+
return stage + `<b>${esc(app.label)}</b><span>CONNECTED · WAITING FOR GOALS</span>`;
|
|
5354
|
+
}
|
|
5355
|
+
function spawnHeroConfetti(){
|
|
5356
|
+
const conf = document.querySelector('#fsClawdHero .chConf'); if (!conf) return;
|
|
5357
|
+
if (window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
|
5358
|
+
const C = ["#FF6B6B","#F86A5C","#FFD166","#3DDC97","#8FB0FF","#C77DFF","#ffffff"], N = 16;
|
|
5359
|
+
for (let k = 0; k < N; k++) {
|
|
5360
|
+
const i = document.createElement('i');
|
|
5361
|
+
const ang = (-172 + Math.random() * 164) * Math.PI / 180;
|
|
5362
|
+
const dist = 50 + Math.random() * 94;
|
|
5363
|
+
const dx = Math.cos(ang) * dist, dy = Math.sin(ang) * dist - 12;
|
|
5364
|
+
i.style.setProperty('--dx', dx.toFixed(1) + 'px');
|
|
5365
|
+
i.style.setProperty('--dy', dy.toFixed(1) + 'px');
|
|
5366
|
+
i.style.setProperty('--rot', (Math.random() * 360 - 180 | 0) + 'deg');
|
|
5367
|
+
i.style.animationDelay = (-(Math.random() * 0.04)).toFixed(3) + 's';
|
|
5368
|
+
i.style.background = C[k % C.length];
|
|
5369
|
+
conf.appendChild(i);
|
|
5370
|
+
}
|
|
5371
|
+
}
|
|
5372
|
+
function renderClawdHero(){
|
|
5373
|
+
const el = $('fsClawdHero'); if (!el) return;
|
|
5374
|
+
const prevStatus = el.querySelector('span')?.textContent;
|
|
5375
|
+
el.innerHTML = clawdHeroHTML(state.connectedApp);
|
|
5376
|
+
if (prevStatus) { const s = el.querySelector('span'); if (s) s.textContent = prevStatus; }
|
|
5377
|
+
if (state.connectedApp !== 'codex') spawnHeroConfetti(); // Welcome P1 confetti (H17 item 4)
|
|
5378
|
+
}
|
|
5332
5379
|
// one-time static wiring for the flagship shell (folds, lane resize, rail menu, band picker)
|
|
5333
5380
|
(() => {
|
|
5334
5381
|
const root = $('fsRoot'); if (!root) return;
|
|
5335
|
-
|
|
5336
|
-
// implementing column. Visibility is state-driven (renderFsFeed toggles
|
|
5337
|
-
// #fsRoot.clawdhero when the column has zero activity — no timers).
|
|
5338
|
-
$('fsClawdHero').innerHTML = '<div class="cstage"><div class="chConf"></div><div class="cw"><img class="chNeu" src="data:image/png;base64,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" alt=""><img class="chLau" src="data:image/png;base64,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" alt=""></div></div>' + '<b>Claude Code</b><span>CONNECTED · WAITING FOR GOALS</span>';
|
|
5339
|
-
(function(){var conf=document.querySelector('#fsClawdHero .chConf');if(!conf)return;if(window.matchMedia&&matchMedia('(prefers-reduced-motion: reduce)').matches)return;var C=["#FF6B6B","#F86A5C","#FFD166","#3DDC97","#8FB0FF","#C77DFF","#ffffff"],N=16;for(var k=0;k<N;k++){var i=document.createElement('i');var ang=(-172+Math.random()*164)*Math.PI/180;var dist=50+Math.random()*94;var dx=Math.cos(ang)*dist,dy=Math.sin(ang)*dist-12;i.style.setProperty('--dx',dx.toFixed(1)+'px');i.style.setProperty('--dy',dy.toFixed(1)+'px');i.style.setProperty('--rot',(Math.random()*360-180|0)+'deg');i.style.animationDelay=(-(Math.random()*0.04)).toFixed(3)+'s';i.style.background=C[k%C.length];conf.appendChild(i);}})(); // Welcome P1 confetti (H17 item 4)
|
|
5382
|
+
renderClawdHero();
|
|
5340
5383
|
// fold toggles = パネルアイコン Fill v2 (HANDOFF-v45 §4) — faithful port of flagship
|
|
5341
5384
|
// tgSvg/tgUpdate at the adopted value (__tgv v=1). OPEN = pane side filled 14% +
|
|
5342
5385
|
// divider; CLOSED = outward arrow pointing in the opening direction. No rotation.
|
|
@@ -6443,6 +6486,7 @@ $('appmenu').addEventListener('click', (e) => {
|
|
|
6443
6486
|
state.msg.model = null;
|
|
6444
6487
|
state.msg.effort = null;
|
|
6445
6488
|
renderAppSelect();
|
|
6489
|
+
renderClawdHero();
|
|
6446
6490
|
renderModelSelect();
|
|
6447
6491
|
renderEffortSelect();
|
|
6448
6492
|
renderMsgHint();
|
|
@@ -7419,6 +7463,7 @@ function gearSetHTML(){
|
|
|
7419
7463
|
}
|
|
7420
7464
|
return `
|
|
7421
7465
|
<div class="srow"><div class="sl">Language<span class="sd">Review checklist language for this project</span></div>
|
|
7466
|
+
<button class="optchip${lang === 'auto' ? ' on' : ''}" data-gslang="auto">Auto</button>
|
|
7422
7467
|
<button class="optchip${lang === 'en' ? ' on' : ''}" data-gslang="en">EN</button>
|
|
7423
7468
|
<button class="optchip${lang === 'ja' ? ' on' : ''}" data-gslang="ja">JA</button></div>
|
|
7424
7469
|
${billingRow}
|
package/engine/lib.mjs
CHANGED
|
@@ -260,7 +260,7 @@ function primaryFailureCause({ usage = null, blockedKind = null, changedFiles =
|
|
|
260
260
|
|
|
261
261
|
function failurePhase(kind) {
|
|
262
262
|
if (kind === 'budget' || kind === 'rate-limit') return 'worker-run';
|
|
263
|
-
if (kind === 'test' || kind === 'proof' || kind === 'verify') return 'manager-verification';
|
|
263
|
+
if (kind === 'test' || kind === 'proof' || kind === 'verify' || kind === 'noop') return 'manager-verification';
|
|
264
264
|
return 'execution';
|
|
265
265
|
}
|
|
266
266
|
|
|
@@ -297,6 +297,12 @@ function failureSummaryFor({ kind, goalText, testResult, budgetReason, proofMiss
|
|
|
297
297
|
yourCall: '不足している proof / 検証結果をそろえ、判断できる状態にしてから再提出します。',
|
|
298
298
|
};
|
|
299
299
|
}
|
|
300
|
+
if (kind === 'noop') {
|
|
301
|
+
return {
|
|
302
|
+
whatHappened: `必要だった作業「${needed}」は検証条件つきのタスクを含みますが、worker はファイル変更もproofも無いまま完了報告しました。`,
|
|
303
|
+
yourCall: '約束した検証(passCondition)が実行されなかった理由を確認し、必要なら worker にやり直しを指示します。',
|
|
304
|
+
};
|
|
305
|
+
}
|
|
300
306
|
return {
|
|
301
307
|
whatHappened: budgetReason ? String(budgetReason).slice(0, 180) : `必要だった作業「${needed}」が途中で止まりました。`,
|
|
302
308
|
yourCall: '停止地点のログを読んで、同じ失敗を避ける最小ステップで再開します。',
|
|
@@ -1099,7 +1105,11 @@ export function resolveGoalSource(input) {
|
|
|
1099
1105
|
// no explicit signal — it decides whether *this project* opens PRs by
|
|
1100
1106
|
// default, separately from whether a PR-wanting goal must go through review.
|
|
1101
1107
|
// `language` is the output language for the Manager's review-facing text
|
|
1102
|
-
// (e.g. summarizeForReview's plain-language headline) — '
|
|
1108
|
+
// (e.g. summarizeForReview's plain-language headline) — 'auto' (match the
|
|
1109
|
+
// language the goal's own request was written in, same as the planner/worker —
|
|
1110
|
+
// see detectRequestLanguage) or an explicit 'ja'/'en' override for a project
|
|
1111
|
+
// that always wants one fixed language regardless of what any given goal was
|
|
1112
|
+
// typed in.
|
|
1103
1113
|
// `prSections` toggles which sections createGoalPR includes in the PR body;
|
|
1104
1114
|
// `reviewCard` toggles what the review checklist card shows for each task.
|
|
1105
1115
|
export const DEFAULT_REVIEW_DEFINITION = {
|
|
@@ -1107,11 +1117,19 @@ export const DEFAULT_REVIEW_DEFINITION = {
|
|
|
1107
1117
|
requireVerifyPass: false,
|
|
1108
1118
|
description: '',
|
|
1109
1119
|
defaultWantsPR: false,
|
|
1110
|
-
language: '
|
|
1120
|
+
language: 'auto',
|
|
1111
1121
|
prSections: { summary: true, whatChanged: true, howToReview: true, tests: true, screenshots: true, risk: true },
|
|
1112
1122
|
reviewCard: { screenshots: true, beforeAfter: false },
|
|
1113
1123
|
};
|
|
1114
1124
|
|
|
1125
|
+
// Detects whether text was written in Japanese (kana/kanji present) or not, so
|
|
1126
|
+
// generated replies can match the language the request was written in
|
|
1127
|
+
// ("test" → English, "テスト" → 日本語) — worker close-out summaries, and (via
|
|
1128
|
+
// reviewDefinition.language === 'auto') review headlines/PR bodies.
|
|
1129
|
+
export function detectRequestLanguage(text) {
|
|
1130
|
+
return /[-ヿ㐀-鿿]/.test(text || '') ? 'ja' : 'en';
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1115
1133
|
// Normalize a nested boolean-flag object (prSections/reviewCard) key by key,
|
|
1116
1134
|
// so a partial payload — or one missing the whole sub-object — can't
|
|
1117
1135
|
// silently disable flags it never mentioned; each key falls back to its own
|
|
@@ -1138,7 +1156,7 @@ export function validateReviewDefinition(input) {
|
|
|
1138
1156
|
requireVerifyPass: input.requireVerifyPass !== undefined ? !!input.requireVerifyPass : DEFAULT_REVIEW_DEFINITION.requireVerifyPass,
|
|
1139
1157
|
description: input.description !== undefined ? String(input.description).slice(0, 500) : DEFAULT_REVIEW_DEFINITION.description,
|
|
1140
1158
|
defaultWantsPR: input.defaultWantsPR !== undefined ? !!input.defaultWantsPR : DEFAULT_REVIEW_DEFINITION.defaultWantsPR,
|
|
1141
|
-
language: (input.language === 'ja' || input.language === 'en') ? input.language : DEFAULT_REVIEW_DEFINITION.language,
|
|
1159
|
+
language: (input.language === 'ja' || input.language === 'en' || input.language === 'auto') ? input.language : DEFAULT_REVIEW_DEFINITION.language,
|
|
1142
1160
|
prSections: normalizeBoolMap(input.prSections, DEFAULT_REVIEW_DEFINITION.prSections),
|
|
1143
1161
|
reviewCard: normalizeBoolMap(input.reviewCard, DEFAULT_REVIEW_DEFINITION.reviewCard),
|
|
1144
1162
|
},
|
|
@@ -1160,6 +1178,20 @@ export function nextGoalStatus({ allDone, wantsPR, allVerified = true, reviewDef
|
|
|
1160
1178
|
return allDone ? 'review' : 'partial';
|
|
1161
1179
|
}
|
|
1162
1180
|
|
|
1181
|
+
// 段5 検証ゲート (Masa 2026-07-15: "proof無しは完了と言わない"): a task that carries
|
|
1182
|
+
// an EXPLICIT passCondition is a case where the user asked for proof — but if the
|
|
1183
|
+
// worker changed zero files, isUiChange() never matches (empty changedFiles), so
|
|
1184
|
+
// uiTasks stays empty and the existing proofMissing check (scoped to UI tasks)
|
|
1185
|
+
// never fires: the promised verification silently never happened. This is
|
|
1186
|
+
// deliberately narrower than "any 0-file goal" — a goal with NO passCondition
|
|
1187
|
+
// (an investigation/report-only ask) legitimately has nothing to prove and must
|
|
1188
|
+
// still reach Review for a human to read (see goal-guardrails-integration.test.mjs
|
|
1189
|
+
// (a2)/(a3): budget/cache-guardrail continuations correctly land in Review with
|
|
1190
|
+
// zero file changes because nothing was ever promised to be verified).
|
|
1191
|
+
export function isNothingVerifiable({ changedFiles = [], hasPassCondition = false, hasAnyProof = false } = {}) {
|
|
1192
|
+
return hasPassCondition && changedFiles.length === 0 && !hasAnyProof;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1163
1195
|
// チェックリストUIで要件が全件Approveされたら goal を review→done へ進める。
|
|
1164
1196
|
// 以前は「PRありgoalはマージが唯一の完了トリガー」として弾いていたが、それだと
|
|
1165
1197
|
// 承認しても done が永続化されず次のrefreshで review に戻る不具合になっていた
|
|
@@ -2452,3 +2484,17 @@ export function resolveFolderAnswer(answer, folders = []) {
|
|
|
2452
2484
|
if (a.startsWith('/') || a.startsWith('~')) return { dir: a };
|
|
2453
2485
|
return null;
|
|
2454
2486
|
}
|
|
2487
|
+
|
|
2488
|
+
// Analytics identity (A+): a signed-in install scopes usage events to the
|
|
2489
|
+
// ACCOUNT (its email, which the billing Worker hashes into the same account_id
|
|
2490
|
+
// the accounts table carries) so per-user app activity joins WITHOUT a cookie;
|
|
2491
|
+
// otherwise the anonymous per-install id. Pure for unit-testing.
|
|
2492
|
+
export function pickAnalyticsUid(accountEmail, installId) {
|
|
2493
|
+
return accountEmail || installId;
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
// Fire the free_exhausted funnel event only the first time a non-paying install
|
|
2497
|
+
// is actually blocked by the free-tier cap.
|
|
2498
|
+
export function shouldEmitFreeExhausted(alreadyEmitted, blocked) {
|
|
2499
|
+
return !alreadyEmitted && Boolean(blocked);
|
|
2500
|
+
}
|
package/engine/relay-client.mjs
CHANGED
|
@@ -45,6 +45,13 @@ function scheduleReconnect(retryMs) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
function connect(retryMs = 1000) {
|
|
48
|
+
// Not signed in yet → do NOT dial the relay. A tokenless agent socket is
|
|
49
|
+
// rejected by the relay (server.mjs closes 4001 "bad or missing license
|
|
50
|
+
// token"), and onclose would reconnect → an INFINITE pre-sign-in flap that
|
|
51
|
+
// every new user hit (connect → 4001 → retry → …). Stay idle until the
|
|
52
|
+
// license.token watcher below calls connect() the instant sign-in writes the
|
|
53
|
+
// token. A signed-in agent that merely drops still reconnects (token present).
|
|
54
|
+
if (!DEV_EMAIL && !readToken()) return;
|
|
48
55
|
// Single-socket invariant: never open a second socket while one is CONNECTING
|
|
49
56
|
// (0) or OPEN (1). Two live sockets with the same identity make the relay evict
|
|
50
57
|
// each other (server.mjs "replaced by a newer connection") → an infinite flap.
|
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, buildFolderQuestion, resolveFolderAnswer } from './lib.mjs';
|
|
24
|
-
import { parseStreamEvents, parsePlan, refinePlanTasks, findOverlappingGoal, canEditGoal, canDeleteGoal, shouldAskClarification, workerExitReason, isForcedStop, workerResultText, taskCountsAsComplete, resolveWantsPR, resolveGoalSource, buildGoalSummary, buildGoalProofMd, buildReviewRuleSection, nextGoalStatus, isPrMerged, isPrApproved, reviewToDoneStatus, advanceReviewGoal, revertReviewGoal, approveGoal, dismissGoal, permissionModeFor, isPlanReview, nextAfterPlanApprove, GOAL_MODES, parseSkillFrontmatter, collectSkills, retestGoal, cancelRetestGoal, computeRetestOutcome, revertGoal, planRevertActions, archiveGoal, unarchiveGoal, undismissGoal, parseNumstat, truncateDiffText, sumUsage, resolveEntryUrl, parseGitLog, diffNewCommits, replayQueueLog, reconcileOrphanGoals, reorderQueue, sortQueueByPriority, TASK_PRIORITIES, validateWorkflowColumns, DEFAULT_WORKFLOW_COLUMNS, validateReviewDefinition, DEFAULT_REVIEW_DEFINITION, buildEphemeralSeedLog, buildEphemeralSeedProjects, trimTaskActivityForState, buildAskContext, WORKER_TOOLS, verifyCfAccessJwt, resolveIdentity, goalVisibleTo, clampParallelLimit, canStartMore, nextRunnableTasks, goalsConflict, detectConflicts, isUiChange, shouldCaptureProof, shouldCaptureBaseline, hasTestRelevantChanges, buildExecutionPlan, buildContextHandoffSummary, buildFailurePostmortem, appendFailureMemory, latestFailurePolicy, classifyChangeRisk, checkRunBudget, usageBudgetTokens, isRateLimited, nextResumeDelay, checkFreeTierLimit, resolveEntitlement, resolveCachedEntitlement, FREE_TIER_LIMITS, verifyLicenseToken, shouldEmitSetupCompleted } from './lib.mjs';
|
|
24
|
+
import { parseStreamEvents, parsePlan, refinePlanTasks, findOverlappingGoal, canEditGoal, canDeleteGoal, shouldAskClarification, workerExitReason, isForcedStop, workerResultText, taskCountsAsComplete, resolveWantsPR, resolveGoalSource, buildGoalSummary, buildGoalProofMd, buildReviewRuleSection, nextGoalStatus, isPrMerged, isPrApproved, reviewToDoneStatus, advanceReviewGoal, revertReviewGoal, approveGoal, dismissGoal, permissionModeFor, isPlanReview, nextAfterPlanApprove, GOAL_MODES, parseSkillFrontmatter, collectSkills, retestGoal, cancelRetestGoal, computeRetestOutcome, revertGoal, planRevertActions, archiveGoal, unarchiveGoal, undismissGoal, parseNumstat, truncateDiffText, sumUsage, resolveEntryUrl, parseGitLog, diffNewCommits, replayQueueLog, reconcileOrphanGoals, reorderQueue, sortQueueByPriority, TASK_PRIORITIES, validateWorkflowColumns, DEFAULT_WORKFLOW_COLUMNS, validateReviewDefinition, DEFAULT_REVIEW_DEFINITION, buildEphemeralSeedLog, buildEphemeralSeedProjects, trimTaskActivityForState, buildAskContext, WORKER_TOOLS, verifyCfAccessJwt, resolveIdentity, goalVisibleTo, clampParallelLimit, canStartMore, nextRunnableTasks, goalsConflict, detectConflicts, isUiChange, shouldCaptureProof, shouldCaptureBaseline, hasTestRelevantChanges, buildExecutionPlan, buildContextHandoffSummary, buildFailurePostmortem, appendFailureMemory, latestFailurePolicy, classifyChangeRisk, checkRunBudget, usageBudgetTokens, isRateLimited, nextResumeDelay, checkFreeTierLimit, resolveEntitlement, resolveCachedEntitlement, FREE_TIER_LIMITS, verifyLicenseToken, shouldEmitSetupCompleted, pickAnalyticsUid, shouldEmitFreeExhausted, detectRequestLanguage, isNothingVerifiable } from './lib.mjs';
|
|
25
25
|
import { openPR } from './pr.mjs';
|
|
26
26
|
import { runVerification, exerciseUi } from './verify.mjs';
|
|
27
27
|
|
|
@@ -180,7 +180,7 @@ let licenseState = { email: null, isPaying: false, verifiedAt: 0 }; // in-memory
|
|
|
180
180
|
// carries, so per-user app activity joins WITHOUT a cookie). Not signed in →
|
|
181
181
|
// the anonymous per-install id. Raw email never lands in usage_events (the
|
|
182
182
|
// Worker pseudonymizes it server-side).
|
|
183
|
-
function analyticsUid() { return licenseState.email
|
|
183
|
+
function analyticsUid() { return pickAnalyticsUid(licenseState.email, ANALYTICS_UID); }
|
|
184
184
|
let _freeExhaustedEmitted = false;
|
|
185
185
|
|
|
186
186
|
// One-time nonces for the "Sign in with Google" loopback (GET /api/signin-url →
|
|
@@ -388,7 +388,7 @@ async function requireEntitlementGate(identity) {
|
|
|
388
388
|
// blocked by the free-tier cap (the funnel dedupes by distinct account/install,
|
|
389
389
|
// so once-per-session is enough).
|
|
390
390
|
function noteFreeExhausted(result) {
|
|
391
|
-
if (_freeExhaustedEmitted
|
|
391
|
+
if (!shouldEmitFreeExhausted(_freeExhaustedEmitted, result?.blocked)) return;
|
|
392
392
|
_freeExhaustedEmitted = true;
|
|
393
393
|
emitEvent('free_exhausted', analyticsUid());
|
|
394
394
|
}
|
|
@@ -1374,15 +1374,15 @@ function testOutputExcerpt(text) {
|
|
|
1374
1374
|
}
|
|
1375
1375
|
|
|
1376
1376
|
function workerPrompt(task, goal, lastFailure) {
|
|
1377
|
-
// Report back to the human in the SAME language they wrote the goal in (Masa
|
|
1378
|
-
// 2026-07-15: "撃った言語で返す"). JP characters → Japanese, otherwise English.
|
|
1379
|
-
const replyLang = /[-ヿ㐀-鿿]/.test(goal?.text || task?.title || '') ? '日本語' : 'English';
|
|
1380
1377
|
// Optional skill the user attached to this goal. Empirically (scratch worker,
|
|
1381
1378
|
// haiku, custom fixture skill) the reliably-firing form is an explicit Skill-tool
|
|
1382
1379
|
// instruction as the FIRST line; a bare "/name" first line did not consistently
|
|
1383
1380
|
// trigger the skill in `claude -p`. See commit body for the run evidence.
|
|
1384
1381
|
const skill = task?.skill ?? goal?.skill;
|
|
1385
1382
|
const agentName = workerAgent(task?.agent ?? goal?.agent) === 'codex' ? 'Codex' : 'Claude Code';
|
|
1383
|
+
// Report back to the human in the SAME language they wrote the goal in (Masa
|
|
1384
|
+
// 2026-07-15: "撃った言語で返す"). JP characters → Japanese, otherwise English.
|
|
1385
|
+
const replyLang = detectRequestLanguage(goal?.text || task?.title || '') === 'ja' ? '日本語' : 'English';
|
|
1386
1386
|
return [
|
|
1387
1387
|
skill ? `FIRST, use the /${skill} skill via the Skill tool (invoke Skill with the "${skill}" skill) before doing anything else. Then carry out the task below.` : '',
|
|
1388
1388
|
`You are a ${agentName} worker managed by "Manager for AI". Do ONLY the task below inside this project directory.`,
|
|
@@ -2044,7 +2044,11 @@ function computeGoalDiff(goal, project) {
|
|
|
2044
2044
|
// plain-language review headline in the user's language: what to CHECK and what
|
|
2045
2045
|
// CHANGED. Never trusts the worker to self-format.
|
|
2046
2046
|
async function summarizeForReview(goal, siblings, dir, reviewDefinition) {
|
|
2047
|
-
|
|
2047
|
+
// 'auto' (default) matches the language the goal's own request was written
|
|
2048
|
+
// in, same as the planner/worker (Masa 2026-07-15 follow-up to a8d69fe: "the
|
|
2049
|
+
// review-column headline still defaults to ja — align it the same way").
|
|
2050
|
+
// An explicit ja/en on the project's review definition always overrides.
|
|
2051
|
+
const lang = reviewDefinition.language === 'auto' ? detectRequestLanguage(goal.text) : (reviewDefinition.language || 'ja');
|
|
2048
2052
|
const changed = [...new Set(siblings.flatMap((t) => t.changedFiles ?? []))].slice(0, 20);
|
|
2049
2053
|
const reports = siblings.filter((t) => !t.reply).map((t) => `- ${t.title}: ${(t.result ?? '').replace(/\s+/g, ' ').slice(0, 300)}`).join('\n');
|
|
2050
2054
|
const fallback = { check: '', changed: (goal.plan?.[0] ?? goal.text ?? '').replace(/\s+/g, ' ').slice(0, 80) };
|
|
@@ -2253,6 +2257,16 @@ async function verifyGate(goal, prProject, siblings, baseProject, activityTask =
|
|
|
2253
2257
|
// EVERY task must carry a passing proof, not just UI tasks. Only ever tightens;
|
|
2254
2258
|
// never conflicts with §6 (still can't reach Done without a human).
|
|
2255
2259
|
const verifyFail = reviewDefinition.requireVerifyPass && !allVerified;
|
|
2260
|
+
// 段5 検証ゲート厳格化 (Masa 2026-07-15: "proof無しは完了と言わない"): passCondition
|
|
2261
|
+
// を約束したタスクなのに 0 ファイル変更で proof も無い=約束した検証が実行されずに
|
|
2262
|
+
// すり抜けたサイン(isUiChange は changedFiles が空だと絶対に一致しないため、既存の
|
|
2263
|
+
// proofMissing(UIタスク限定)はこのケースを検知できない)。passCondition が無い調査
|
|
2264
|
+
// 系ゴール(0ファイルが正当な完了)は対象外 — 人間が読んで判断すれば足りる。
|
|
2265
|
+
const nothingVerifiable = isNothingVerifiable({
|
|
2266
|
+
changedFiles,
|
|
2267
|
+
hasPassCondition: siblings.some((t) => t.passCondition),
|
|
2268
|
+
hasAnyProof: siblings.some((t) => t.proof),
|
|
2269
|
+
});
|
|
2256
2270
|
if (testsFailing) {
|
|
2257
2271
|
const failure = recordGoalFailure(goal, {
|
|
2258
2272
|
kind: 'test',
|
|
@@ -2285,22 +2299,24 @@ async function verifyGate(goal, prProject, siblings, baseProject, activityTask =
|
|
|
2285
2299
|
return;
|
|
2286
2300
|
}
|
|
2287
2301
|
}
|
|
2288
|
-
if (testsFailing || proofMissing || proofFailing || verifyFail) {
|
|
2302
|
+
if (testsFailing || proofMissing || proofFailing || verifyFail || nothingVerifiable) {
|
|
2289
2303
|
if (!testsFailing) {
|
|
2290
2304
|
recordGoalFailure(goal, {
|
|
2291
|
-
kind: (proofMissing || proofFailing) ? 'proof' : 'verify',
|
|
2305
|
+
kind: (proofMissing || proofFailing) ? 'proof' : nothingVerifiable ? 'noop' : 'verify',
|
|
2292
2306
|
reason: proofMissing ? 'UI変更なのに proof(スクショ/動画) が取れていない'
|
|
2293
2307
|
: proofFailing ? 'UI proof は撮れたが、依頼対象のUIを確認できていない'
|
|
2308
|
+
: nothingVerifiable ? '検証条件つきのタスクなのに、ファイル変更も proof も無い(約束した検証が行われていない)'
|
|
2294
2309
|
: '検証(proof)が全タスク分そろっていない(requireVerifyPass)',
|
|
2295
2310
|
changedFiles,
|
|
2296
2311
|
testResult: goal.testResult,
|
|
2297
2312
|
});
|
|
2298
2313
|
}
|
|
2299
2314
|
goal.blocked = {
|
|
2300
|
-
kind: testsFailing ? 'test' : (proofMissing || proofFailing) ? 'proof' : 'verify',
|
|
2315
|
+
kind: testsFailing ? 'test' : (proofMissing || proofFailing) ? 'proof' : nothingVerifiable ? 'noop' : 'verify',
|
|
2301
2316
|
reason: testsFailing ? `テスト失敗 ${goal.testResult.failed} 件(自動修正 ${Number(goal.autoRework?.testFailures ?? 0)} 回後も失敗)`
|
|
2302
2317
|
: proofMissing ? 'UI変更なのに proof(スクショ/動画) が取れていない'
|
|
2303
2318
|
: proofFailing ? 'UI proof は撮れたが、依頼対象のUIを確認できていない'
|
|
2319
|
+
: nothingVerifiable ? '検証条件つきのタスクなのに、ファイル変更も proof も無い(約束した検証が行われていない)'
|
|
2304
2320
|
: '検証(proof)が全タスク分そろっていない(requireVerifyPass)',
|
|
2305
2321
|
};
|
|
2306
2322
|
goal.status = 'blocked';
|
|
@@ -2349,9 +2365,11 @@ async function createGoalPR(goal, project, goalTasks, reviewDefinition = DEFAULT
|
|
|
2349
2365
|
}
|
|
2350
2366
|
const ownerRes = spawnSync('gh', ['repo', 'view', '--json', 'nameWithOwner', '-q', '.nameWithOwner'], { cwd: project.dir, encoding: 'utf8' });
|
|
2351
2367
|
const owner = ownerRes.status === 0 ? ownerRes.stdout.trim() : null;
|
|
2352
|
-
// PR body in the USER's language
|
|
2353
|
-
//
|
|
2354
|
-
|
|
2368
|
+
// PR body in the USER's language — default 'auto' matches the goal's own
|
|
2369
|
+
// request language (same as planner/worker); an explicit ja/en override on
|
|
2370
|
+
// the project's review definition always wins. Not forced English/bilingual
|
|
2371
|
+
// (Masa: 「ユーザーの言語で」).
|
|
2372
|
+
const lang = reviewDefinition.language === 'auto' ? detectRequestLanguage(goal.text) : (reviewDefinition.language || 'ja');
|
|
2355
2373
|
const L = (en, ja) => (lang === 'en' ? en : ja);
|
|
2356
2374
|
const bodyFor = (branch) => [
|
|
2357
2375
|
...(proofItems.length && owner ? [
|
package/package.json
CHANGED