@galda/cli 0.10.88 → 0.10.89

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/CLAUDE.md CHANGED
@@ -105,6 +105,8 @@
105
105
  - Worker/API へは**必ず所有ドメイン `https://galda.app`(custom domain)経由**で参照する。`*.workers.dev` サブドメインを製品コードに焼き込まない(`MANAGER_BILLING_API_URL` 等の既定値は `https://galda.app`)。
106
106
  - **Cloudflare/wrangler にデプロイする前に `wrangler whoami` でデプロイ先アカウントを確認**し、Galda/Kodo 所有であること・workers.dev サブドメイン名に客先名や別プロジェクト名が出ていないことを確かめる。怪しければ止めて Masa に確認(本番デプロイは Masa 明示許可制)。
107
107
  - **リリースは4面**(① npm ② Fly ③ Worker ④ Masaの常駐インスタンス `~/manager-for-ai`:4400)。①だけ出して④を忘れると「直したのに直ってない」+relay相互kickループ再発の実例あり(2026-07-16/17)。全文=`docs/RELEASE-WORKFLOW.md`の「本番は1つじゃない」表。
108
+ - **常駐Managerを直接 `kill`/`launchctl kickstart -k` しない**(2026-07-28、実行中3 workerを同時中断した実例)。再起動は対象checkoutで
109
+ `node tools/safe-restart-manager.mjs` を使う。このコマンドはDoing/Finalizingが1件でもあれば拒否する。拒否時は完了を待って再実行し、Agent切替で回避しない。
108
110
 
109
111
  ## relay接続(`app.galda.app`)を伴う作業の掟(Masa明言 2026-07-17・全レーン必読)
110
112
 
package/app/index.html CHANGED
@@ -1558,6 +1558,14 @@
1558
1558
  line-height:1.15;padding:2.5px 10px;border-radius:7px;box-shadow:inset 0 0 0 1px var(--hair2)}
1559
1559
  #fsRoot .acts2 .errbtn:first-child{color:var(--ink2)}
1560
1560
  #fsRoot .errbtn:hover{background:var(--hover);color:var(--ink)}
1561
+ /* The review/attention dialog is rendered in #seeall, outside #fsRoot.
1562
+ Keep its recovery actions on the same designed component instead of
1563
+ falling back to the browser's unstyled native button. */
1564
+ #seeall .acts2{display:flex;gap:6px;margin-top:3px;align-items:center}
1565
+ #seeall .errbtn{border:0;background:transparent;color:var(--ink3);font-family:var(--mono);font-size:10px;letter-spacing:.02em;cursor:pointer;
1566
+ line-height:1.15;padding:2.5px 10px;border-radius:7px;box-shadow:inset 0 0 0 1px var(--hair2)}
1567
+ #seeall .acts2 .errbtn:first-child{color:var(--ink2)}
1568
+ #seeall .errbtn:hover{background:var(--hover);color:var(--ink)}
1561
1569
  /* AI question in the feed (q2 — §6.5: while current it lives in the chat feed).
1562
1570
  H18 / CDO HANDOFF-question-ui-2026-07-13: QUESTION card = hairline frame +
1563
1571
  2px --blue left rule + mono label + option chips + an always-on free-text
@@ -7189,6 +7197,7 @@ const FAILURE_COPY = {
7189
7197
  infra: {
7190
7198
  title: 'Galda基盤エラー',
7191
7199
  timeout: 'Galda側の実行上限や待ち時間で止まりました。変更内容の失敗とは限りません。',
7200
+ interrupted: 'ローカルの実行が中断されました。変更内容やAgentの失敗とは限らないため、同じ作業を再開できます。',
7192
7201
  auth: '接続や認証確認で止まりました。接続を直してから再開できます。',
7193
7202
  relay: 'relay接続で止まりました。接続状態を確認してから再試行できます。',
7194
7203
  chrome: 'Chrome/ブラウザ確認で止まりました。画面確認環境の問題で、実装失敗とは限りません。',
@@ -7198,8 +7207,8 @@ const FAILURE_COPY = {
7198
7207
  unknown: 'Galdaの実行基盤で止まりました。ログを確認し、原因に応じて再試行できます。',
7199
7208
  },
7200
7209
  actions: {
7201
- retry: '再試行', resume: '再開', connect: 'Connect', check: '接続確認',
7202
- other: 'Agentで実行', log: 'ログ確認', tests: 'テスト再実行', pr: 'PR再試行',
7210
+ retry: 'Retry', resume: 'Resume', connect: 'Connect', check: 'Check connection',
7211
+ other: 'Try another Agent', log: 'View log', tests: 'Re-run tests', pr: 'Retry PR',
7203
7212
  },
7204
7213
  },
7205
7214
  en: {
@@ -7207,6 +7216,7 @@ const FAILURE_COPY = {
7207
7216
  infra: {
7208
7217
  title: 'Galda system issue',
7209
7218
  timeout: 'Galda stopped this at the run limit or a wait timed out. This is not necessarily a problem with the change.',
7219
+ interrupted: 'The local run was interrupted. This does not necessarily reflect a problem with the change or agent; resume the same work.',
7210
7220
  auth: 'Connection or authentication failed. Reconnect, then continue.',
7211
7221
  relay: 'The relay connection failed. Check the connection, then retry.',
7212
7222
  chrome: 'Chrome/browser verification failed. The UI check environment stopped, not necessarily the implementation.',
@@ -7227,17 +7237,23 @@ function failureLang(){
7227
7237
  function inferErrorClass(text, agent){
7228
7238
  const low = String(text || '').toLowerCase();
7229
7239
  const infra = (reason, actionIds) => ({ owner: 'galda-infra', reason, actionIds });
7230
- if (/run limit|実行上限|timed out|timeout|タイムアウト/.test(low)) return infra('timeout', ['retry', 'run-with-codex', 'view-log']);
7231
- if (/spawn|enoent|eacces|command not found|worker tool exited early|作業ツールが途中で終了/.test(low)) return infra('worker-start', ['retry', 'run-with-codex', 'view-log']);
7232
- if (/auth probe|preflight|not logged in|logged out|not authenticated|authentication|unauthorized|invalid api key|bad credentials|401\b|403\b|接続が必要|認証/.test(low)) return infra('auth', ['connect', 'check-connection', 'run-with-codex', 'view-log']);
7240
+ if (/run limit|実行上限|timed out|timeout|タイムアウト/.test(low)) return infra('timeout', ['retry', 'view-log']);
7241
+ if (/worker was interrupted|作業が途中で中断|exit 143|sigterm|sigint/.test(low)) return infra('interrupted', ['retry', 'view-log']);
7242
+ if (/spawn|enoent|eacces|command not found|worker tool exited early|作業ツールが途中で終了/.test(low)) return infra('worker-start', ['retry', 'view-log']);
7243
+ if (/auth probe|preflight|not logged in|logged out|not authenticated|authentication|unauthorized|invalid api key|bad credentials|401\b|403\b|接続が必要|認証/.test(low)) return infra('auth', ['connect', 'check-connection', 'view-log']);
7233
7244
  if (/relay|websocket|socket|econnreset|econnrefused|billing-api unreachable|cf access|cloudflare access/.test(low)) return infra('relay', ['retry', 'connect', 'view-log']);
7234
7245
  if (/chrome|chromium|puppeteer|target closed|browser|navigation timeout|waiting failed|screenshot|headless/.test(low)) return infra('chrome', ['retry', 'view-log']);
7235
7246
  if (/test runner|node --test|npm test|project tests|server exited \(code \d+\) before it was ready|did not report ready in time|test timed out after|tap|not ok \d+/.test(low)) return infra('test-runner', ['retry-tests', 'view-log']);
7236
7247
  if (/pr step failed|openpr|pull request|github|gh:|gh auth|git push|git fetch|git .*failed|pr作成|pr failed/.test(low)) return infra('pr', ['retry-pr', 'view-log']);
7237
- return { owner: 'agent', reason: agent === 'codex' ? 'codex' : 'claude-code', actionIds: ['retry', 'run-with-codex', 'view-log'] };
7248
+ return { owner: 'agent', reason: agent === 'codex' ? 'codex' : 'claude-code', actionIds: ['retry', 'view-log'] };
7238
7249
  }
7239
7250
  function taskErrorMeta(t, goal){
7240
- const meta = t.errorClass || inferErrorClass(t.result, t.agent || goal?.agent);
7251
+ // Reclassify interrupted tasks from their concrete exit text instead of
7252
+ // trusting stale persisted metadata. Older records labelled an external
7253
+ // SIGTERM/restart as an Agent failure and permanently carried a Codex switch.
7254
+ const inferred = inferErrorClass(t.result, t.agent || goal?.agent);
7255
+ const raw = t.status === 'interrupted' ? inferred : (t.errorClass || inferred);
7256
+ const meta = { ...raw, actionIds: (raw.actionIds || []).filter((id) => !['run-with-codex', 'run-with-claude'].includes(id)) };
7241
7257
  const lang = failureLang(), C = FAILURE_COPY[lang];
7242
7258
  const isInfra = meta.owner === 'galda-infra';
7243
7259
  return {
@@ -7251,20 +7267,16 @@ function goalBlockedErrorMeta(g){
7251
7267
  const raw = g.blocked?.errorClass || (g.blocked?.kind === 'test'
7252
7268
  ? { owner: 'galda-infra', reason: 'test-runner', actionIds: ['retry-tests', 'view-log'] }
7253
7269
  : inferErrorClass(g.blocked?.reason, g.agent));
7270
+ const meta = { ...raw, actionIds: (raw.actionIds || []).filter((id) => !['run-with-codex', 'run-with-claude'].includes(id)) };
7254
7271
  const lang = failureLang(), C = FAILURE_COPY[lang];
7255
- const isInfra = raw.owner === 'galda-infra';
7256
- return { ...raw, title: isInfra ? C.infra.title : C.agent.title, body: isInfra ? (C.infra[raw.reason] || C.infra.unknown) : C.agent.body, copy: C };
7272
+ const isInfra = meta.owner === 'galda-infra';
7273
+ return { ...meta, title: isInfra ? C.infra.title : C.agent.title, body: isInfra ? (C.infra[meta.reason] || C.infra.unknown) : C.agent.body, copy: C };
7257
7274
  }
7258
7275
  function errorButtonsHtml(meta, { taskId = null, goalId = null, interrupted = false, agent = null, pr = false } = {}){
7259
7276
  const C = meta.copy.actions;
7260
- const canCodex = goalId != null && agent !== 'codex' && (state.agents || []).includes('codex');
7261
7277
  const out = [];
7262
7278
  for (const id of meta.actionIds || []) {
7263
7279
  if (id === 'retry' && taskId != null) out.push(`<button class="errbtn" data-fsretry="${taskId}">${interrupted ? C.resume : C.retry}</button>`);
7264
- // This keeps the existing agent-switch endpoint, but says what it really does
7265
- // for an interrupted run: continue the saved goal with another agent rather
7266
- // than imply a brand-new, contextless task will be created.
7267
- else if (id === 'run-with-codex' && canCodex) out.push(`<button class="errbtn" data-fscodex="${goalId}">${interrupted ? (failureLang() === 'ja' ? 'Codexで再開' : 'Resume with Codex') : C.other}</button>`);
7268
7280
  else if (id === 'connect') out.push(`<button class="errbtn" data-fsconnectworker="1">${C.connect}</button>`);
7269
7281
  else if (id === 'check-connection') out.push(`<button class="errbtn" data-fscheckworker="1">${C.check}</button>`);
7270
7282
  else if (id === 'retry-tests' && goalId != null) out.push(`<button class="errbtn" data-fsreverify="${goalId}">${C.tests}</button>`);
package/engine/lib.mjs CHANGED
@@ -1258,13 +1258,20 @@ export function classifyWorkerFailure({ agent = 'claude-code', result = '', err
1258
1258
  actionIds,
1259
1259
  });
1260
1260
  if (timedOut || /run limit|実行上限|timed out|timeout|タイムアウト/.test(low)) {
1261
- return infra('timeout', ['retry', 'run-with-codex', 'view-log']);
1261
+ return infra('timeout', ['retry', 'view-log']);
1262
+ }
1263
+ // SIGTERM/SIGINT means the local worker was stopped from outside the task
1264
+ // (for example while the local Manager was restarting). It says nothing
1265
+ // about either the requested change or the chosen agent, so never turn this
1266
+ // recovery path into a suggestion to switch agents.
1267
+ if (code === 143 || code === 130 || /worker was interrupted|作業が途中で中断|exit 143|sigterm|sigint/.test(low)) {
1268
+ return infra('interrupted', ['retry', 'view-log']);
1262
1269
  }
1263
1270
  if (/spawn|enoent|eacces|command not found|executable|worker tool exited early|作業ツールが途中で終了/.test(low)) {
1264
- return infra('worker-start', ['retry', 'run-with-codex', 'view-log']);
1271
+ return infra('worker-start', ['retry', 'view-log']);
1265
1272
  }
1266
1273
  if (/auth probe|preflight|not logged in|logged out|not authenticated|authentication|unauthorized|invalid api key|bad credentials|401\b|403\b|接続が必要|認証/.test(low)) {
1267
- return infra('auth', ['connect', 'check-connection', 'run-with-codex', 'view-log']);
1274
+ return infra('auth', ['connect', 'check-connection', 'view-log']);
1268
1275
  }
1269
1276
  if (/relay|websocket|socket|econnreset|econnrefused|billing-api unreachable|cf access|cloudflare access/.test(low)) {
1270
1277
  return infra('relay', ['retry', 'connect', 'view-log']);
@@ -1281,7 +1288,7 @@ export function classifyWorkerFailure({ agent = 'claude-code', result = '', err
1281
1288
  return {
1282
1289
  owner: 'agent',
1283
1290
  reason: agent === 'codex' ? 'codex' : 'claude-code',
1284
- actionIds: ['retry', agent === 'claude-code' ? 'run-with-codex' : 'run-with-claude', 'view-log'],
1291
+ actionIds: ['retry', 'view-log'],
1285
1292
  };
1286
1293
  }
1287
1294
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@galda/cli",
3
- "version": "0.10.88",
3
+ "version": "0.10.89",
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": {