@cartanova/qgrid-cli 2.10.2 → 2.10.4
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/bundle/dist/application/monit/log-buffer.js +7 -11
- package/bundle/dist/application/monit/monit.frame.js +6 -11
- package/bundle/dist/application/qgrid/oauth.js +2 -2
- package/bundle/dist/application/qgrid/qgrid-run-lifecycle.js +64 -141
- package/bundle/dist/application/qgrid/qgrid.dispatcher.js +12 -36
- package/bundle/dist/application/qgrid/qgrid.frame.js +3 -6
- package/bundle/dist/application/qgrid/qgrid.types.js +2 -1
- package/bundle/dist/application/qgrid/token-subscriber.js +4 -7
- package/bundle/dist/application/qgrid/token-window-keepalive.js +3 -2
- package/bundle/dist/application/qgrid/tool-emulation-schema.js +4 -4
- package/bundle/dist/application/qgrid/tool-emulation.js +14 -2
- package/bundle/dist/application/request-log/request-log.model.js +7 -5
- package/bundle/dist/application/setting/setting.model.js +2 -64
- package/bundle/dist/application/setting/setting.store.js +3 -4
- package/bundle/dist/application/setting/setting.types.js +2 -12
- package/bundle/dist/i18n/en.js +1 -3
- package/bundle/dist/i18n/ko.js +1 -3
- package/bundle/dist/sonamu.config.js +6 -6
- package/bundle/dist/utils/providers/anthropic/anthropic-constants.js +1 -1
- package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +53 -84
- package/bundle/dist/utils/providers/anthropic/anthropic-thinking.js +55 -0
- package/bundle/dist/utils/providers/anthropic/claude-session.js +11 -13
- package/bundle/dist/utils/providers/common/effort.js +1 -1
- package/bundle/dist/utils/providers/common/smooth-weighted-round-robin.js +3 -3
- package/bundle/dist/utils/providers/common/thinking.js +12 -0
- package/bundle/dist/utils/providers/openai/openai-backend-protocol.js +3 -2
- package/bundle/dist/utils/providers/openai/openai-direct-client.js +6 -16
- package/bundle/dist/utils/providers/openai/openai-dispatcher.js +89 -78
- package/bundle/src/application/monit/log-buffer.test.ts +3 -4
- package/bundle/src/application/monit/log-buffer.ts +13 -27
- package/bundle/src/application/monit/monit.frame.test.ts +7 -8
- package/bundle/src/application/monit/monit.frame.ts +9 -14
- package/bundle/src/application/qgrid/oauth.ts +1 -1
- package/bundle/src/application/qgrid/qgrid-run-lifecycle.test.ts +7 -111
- package/bundle/src/application/qgrid/qgrid-run-lifecycle.ts +83 -179
- package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +62 -1
- package/bundle/src/application/qgrid/qgrid.dispatcher.ts +25 -71
- package/bundle/src/application/qgrid/qgrid.frame.test.ts +0 -38
- package/bundle/src/application/qgrid/qgrid.frame.ts +4 -12
- package/bundle/src/application/qgrid/qgrid.types.ts +2 -0
- package/bundle/src/application/qgrid/token-subscriber.test.ts +8 -18
- package/bundle/src/application/qgrid/token-subscriber.ts +10 -20
- package/bundle/src/application/qgrid/token-window-keepalive.test.ts +2 -2
- package/bundle/src/application/qgrid/token-window-keepalive.ts +2 -1
- package/bundle/src/application/qgrid/tool-emulation-schema.test.ts +6 -3
- package/bundle/src/application/qgrid/tool-emulation-schema.ts +3 -3
- package/bundle/src/application/qgrid/tool-emulation.test.ts +8 -1
- package/bundle/src/application/qgrid/tool-emulation.ts +29 -0
- package/bundle/src/application/request-log/request-log.model.ts +5 -5
- package/bundle/src/application/setting/setting.model.test.ts +1 -101
- package/bundle/src/application/setting/setting.model.ts +1 -74
- package/bundle/src/application/setting/setting.store.ts +2 -3
- package/bundle/src/application/setting/setting.types.ts +0 -12
- package/bundle/src/application/sonamu.generated.http +2 -7
- package/bundle/src/i18n/en.ts +0 -3
- package/bundle/src/i18n/ko.ts +0 -3
- package/bundle/src/sonamu.config.ts +5 -5
- package/bundle/src/utils/providers/anthropic/anthropic-constants.ts +3 -6
- package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.test.ts +26 -0
- package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.ts +70 -148
- package/bundle/src/utils/providers/anthropic/anthropic-thinking.test.ts +36 -0
- package/bundle/src/utils/providers/anthropic/anthropic-thinking.ts +84 -0
- package/bundle/src/utils/providers/anthropic/claude-session.test.ts +74 -13
- package/bundle/src/utils/providers/anthropic/claude-session.ts +11 -20
- package/bundle/src/utils/providers/common/effort.ts +1 -1
- package/bundle/src/utils/providers/common/provider-dispatcher.ts +4 -0
- package/bundle/src/utils/providers/common/smooth-weighted-round-robin.ts +4 -4
- package/bundle/src/utils/providers/common/thinking.ts +10 -0
- package/bundle/src/utils/providers/openai/openai-backend-protocol.ts +7 -3
- package/bundle/src/utils/providers/openai/openai-direct-client.test.ts +3 -3
- package/bundle/src/utils/providers/openai/openai-direct-client.ts +20 -33
- package/bundle/src/utils/providers/openai/openai-dispatcher.test.ts +97 -6
- package/bundle/src/utils/providers/openai/openai-dispatcher.ts +113 -77
- package/bundle/web-dist/client/assets/index-CvG4UB9v.css +1 -0
- package/bundle/web-dist/client/assets/{index-DtRTSTes.js → index-reW8VvJF.js} +4 -4
- package/bundle/web-dist/client/assets/{logs-BhVYDEzC.js → logs-DDIt97D1.js} +1 -1
- package/bundle/web-dist/client/assets/{monit-BxN4JWqK.js → monit-DkABv7PG.js} +1 -1
- package/bundle/web-dist/client/assets/{routes-CBCqRp2o.js → routes-BRmQ4M3-.js} +1 -1
- package/bundle/web-dist/client/assets/services.generated-jnPHYMER.js +51 -0
- package/bundle/web-dist/client/assets/settings-BHvtycEZ.js +1 -0
- package/bundle/web-dist/client/assets/{show-BzYft5N4.js → show-qeTIzgDZ.js} +1 -1
- package/bundle/web-dist/client/assets/{tokens-PMybtI8X.js → tokens-5Bep_G-w.js} +1 -1
- package/bundle/web-dist/client/assets/{use-oauth-login-flow-C51_d6-6.js → use-oauth-login-flow-Cx-QO25M.js} +1 -1
- package/bundle/web-dist/client/assets/{use-update-token-mutation-D-Y8OySK.js → use-update-token-mutation-DQCwCYfR.js} +1 -1
- package/bundle/web-dist/client/index.html +3 -3
- package/bundle/web-dist/server/assets/{lazyRouteComponent-Bj1cPq6x.js → lazyRouteComponent-CUR-kWzI.js} +2 -2
- package/bundle/web-dist/server/assets/{logs-BSk9lOHe.js → logs-CagNGTBj.js} +3 -3
- package/bundle/web-dist/server/assets/{monit-D9W4ioUi.js → monit-DSxs4Jnl.js} +3 -3
- package/bundle/web-dist/server/assets/{plus-CmvbWuDf.js → plus-Bvdj8Pwx.js} +1 -1
- package/bundle/web-dist/server/assets/{react-dom-BjR1Iaz8.js → react-dom-CyuShs9K.js} +1 -1
- package/bundle/web-dist/server/assets/{refresh-cw-B8U9BL9-.js → refresh-cw-3QAiSWBd.js} +1 -1
- package/bundle/web-dist/server/assets/{routes-C7lB2Q85.js → routes-i-sTqXMX.js} +4 -4
- package/bundle/web-dist/server/assets/{services.generated-BM4X3Ecj.js → services.generated-FPwsG9p3.js} +116 -131
- package/bundle/web-dist/server/assets/{settings-BFVuldch.js → settings-DeHNwoMv.js} +40 -251
- package/bundle/web-dist/server/assets/{show-Cswh_tqs.js → show-C4jk1Hrp.js} +2 -2
- package/bundle/web-dist/server/assets/{tokens-DiTUwUwr.js → tokens-odgS2-9q.js} +4 -4
- package/bundle/web-dist/server/assets/{use-oauth-login-flow-DYKnG4A2.js → use-oauth-login-flow-DTQKdYBO.js} +1 -1
- package/bundle/web-dist/server/assets/{use-update-token-mutation-DqGMopII.js → use-update-token-mutation-xvXbPrCI.js} +1 -1
- package/bundle/web-dist/server/entry-server.generated.js +10 -10
- package/package.json +1 -1
- package/skills/qgrid/SKILL.md +1 -0
- package/skills/qgrid/references/ai-sdk-provider-contract.md +2 -1
- package/skills/qgrid/references/anthropic-claude-code-runtime.md +7 -4
- package/skills/qgrid/references/cli-env-and-server-boot.md +12 -9
- package/skills/qgrid/references/decision-rationale.md +9 -0
- package/skills/qgrid/references/openai-codex-runtime.md +6 -0
- package/skills/qgrid/references/token-auth-quota-lifecycle.md +2 -0
- package/skills/qgrid/references/tool-calling-and-multiturn.md +18 -1
- package/skills/qgrid/references/verification-and-debugging.md +1 -1
- package/bundle/dist/utils/process-supervisor.js +0 -22
- package/bundle/dist/utils/server-restart.js +0 -61
- package/bundle/src/utils/process-supervisor.test.ts +0 -24
- package/bundle/src/utils/process-supervisor.ts +0 -17
- package/bundle/src/utils/server-restart.test.ts +0 -76
- package/bundle/src/utils/server-restart.ts +0 -68
- package/bundle/web-dist/client/assets/index-D_sagzsR.css +0 -1
- package/bundle/web-dist/client/assets/services.generated-Cbvg2sGQ.js +0 -51
- package/bundle/web-dist/client/assets/settings-hBTEC7Ki.js +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as require_react, O as useQueryClient, X as __toESM, a as SettingService, k as require_jsx_runtime, o as TokenService } from "./services.generated-FPwsG9p3.js";
|
|
2
2
|
import { l as lucideRotateCcw } from "../entry-server.generated.js";
|
|
3
|
-
import { t as useUpdateTokenMutation } from "./use-update-token-mutation-
|
|
4
|
-
import { t as lucidePlus } from "./plus-
|
|
3
|
+
import { t as useUpdateTokenMutation } from "./use-update-token-mutation-xvXbPrCI.js";
|
|
4
|
+
import { t as lucidePlus } from "./plus-Bvdj8Pwx.js";
|
|
5
5
|
//#region ~icons/lucide/eye.jsx
|
|
6
6
|
var import_react = /* @__PURE__ */ __toESM(require_react());
|
|
7
7
|
var import_jsx_runtime = require_jsx_runtime();
|
|
@@ -62,7 +62,6 @@ var GROUP_LABELS = {
|
|
|
62
62
|
slack: "Slack 알림",
|
|
63
63
|
slackConnection: "Slack 연결"
|
|
64
64
|
};
|
|
65
|
-
var RESTART_READY_TIMEOUT_MS = 180 * 1e3;
|
|
66
65
|
function maskValue(value) {
|
|
67
66
|
if (!value) return "";
|
|
68
67
|
if (value.length <= 12) return "•".repeat(value.length);
|
|
@@ -357,212 +356,6 @@ function TriggerReminderButton() {
|
|
|
357
356
|
})]
|
|
358
357
|
});
|
|
359
358
|
}
|
|
360
|
-
/**
|
|
361
|
-
* 재시작은 스스로 종료하고 감독자(pm2)가 다시 띄우는 것에 맡긴다. 되돌릴 수 없고 1~2분간
|
|
362
|
-
* 요청이 실패하므로 확인 단계를 둔다 — 무엇이 일어나는지 모달이 먼저 설명한다.
|
|
363
|
-
*/
|
|
364
|
-
function RestartPanel({ supervisor }) {
|
|
365
|
-
const [confirming, setConfirming] = (0, import_react.useState)(false);
|
|
366
|
-
const [restartRequested, setRestartRequested] = (0, import_react.useState)(false);
|
|
367
|
-
const [restartStartedAt, setRestartStartedAt] = (0, import_react.useState)(null);
|
|
368
|
-
const [sawUnavailable, setSawUnavailable] = (0, import_react.useState)(false);
|
|
369
|
-
const [completing, setCompleting] = (0, import_react.useState)(false);
|
|
370
|
-
const [error, setError] = (0, import_react.useState)(null);
|
|
371
|
-
const completingRef = (0, import_react.useRef)(false);
|
|
372
|
-
const restartAttemptRef = (0, import_react.useRef)(0);
|
|
373
|
-
const queryClient = useQueryClient();
|
|
374
|
-
const mutation = SettingService.useRestartServerMutation();
|
|
375
|
-
const resetMutation = mutation.reset;
|
|
376
|
-
const healthQuery = useQuery({
|
|
377
|
-
...QgridService.healthQueryOptions(),
|
|
378
|
-
enabled: restartRequested && !completing,
|
|
379
|
-
refetchInterval: restartRequested && !completing ? 1e3 : false,
|
|
380
|
-
refetchIntervalInBackground: true,
|
|
381
|
-
retry: false
|
|
382
|
-
});
|
|
383
|
-
(0, import_react.useEffect)(() => {
|
|
384
|
-
if (!restartRequested || restartStartedAt === null || completing) return;
|
|
385
|
-
const remaining = Math.max(0, RESTART_READY_TIMEOUT_MS - (Date.now() - restartStartedAt));
|
|
386
|
-
const timeout = window.setTimeout(() => {
|
|
387
|
-
restartAttemptRef.current += 1;
|
|
388
|
-
resetMutation();
|
|
389
|
-
setConfirming(false);
|
|
390
|
-
setRestartRequested(false);
|
|
391
|
-
setRestartStartedAt(null);
|
|
392
|
-
setSawUnavailable(false);
|
|
393
|
-
setError("재시작 후 3분 안에 서버 준비를 확인하지 못했습니다. 상태를 확인한 뒤 다시 시도하세요.");
|
|
394
|
-
}, remaining);
|
|
395
|
-
return () => window.clearTimeout(timeout);
|
|
396
|
-
}, [
|
|
397
|
-
completing,
|
|
398
|
-
resetMutation,
|
|
399
|
-
restartRequested,
|
|
400
|
-
restartStartedAt
|
|
401
|
-
]);
|
|
402
|
-
(0, import_react.useEffect)(() => {
|
|
403
|
-
if (!restartRequested || restartStartedAt === null) return;
|
|
404
|
-
const currentAttemptError = healthQuery.isError && healthQuery.errorUpdatedAt >= restartStartedAt;
|
|
405
|
-
const currentAttemptData = healthQuery.isSuccess && healthQuery.dataUpdatedAt >= restartStartedAt;
|
|
406
|
-
if (currentAttemptError || currentAttemptData && !healthQuery.data.ready) {
|
|
407
|
-
setSawUnavailable(true);
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
if (!sawUnavailable || !currentAttemptData || !healthQuery.data.ready || completingRef.current) return;
|
|
411
|
-
completingRef.current = true;
|
|
412
|
-
setCompleting(true);
|
|
413
|
-
queryClient.invalidateQueries({ queryKey: SettingService.getSettingListQueryOptions().queryKey }).then(() => {
|
|
414
|
-
restartAttemptRef.current += 1;
|
|
415
|
-
resetMutation();
|
|
416
|
-
setConfirming(false);
|
|
417
|
-
setRestartRequested(false);
|
|
418
|
-
setRestartStartedAt(null);
|
|
419
|
-
setSawUnavailable(false);
|
|
420
|
-
}).catch((cause) => {
|
|
421
|
-
restartAttemptRef.current += 1;
|
|
422
|
-
resetMutation();
|
|
423
|
-
setConfirming(false);
|
|
424
|
-
setRestartRequested(false);
|
|
425
|
-
setRestartStartedAt(null);
|
|
426
|
-
setSawUnavailable(false);
|
|
427
|
-
setError(`서버는 준비됐지만 설정을 새로고침하지 못했습니다: ${cause.message}`);
|
|
428
|
-
}).finally(() => {
|
|
429
|
-
completingRef.current = false;
|
|
430
|
-
setCompleting(false);
|
|
431
|
-
});
|
|
432
|
-
}, [
|
|
433
|
-
healthQuery.dataUpdatedAt,
|
|
434
|
-
healthQuery.data?.ready,
|
|
435
|
-
healthQuery.errorUpdatedAt,
|
|
436
|
-
healthQuery.isError,
|
|
437
|
-
healthQuery.isSuccess,
|
|
438
|
-
queryClient,
|
|
439
|
-
resetMutation,
|
|
440
|
-
restartStartedAt,
|
|
441
|
-
restartRequested,
|
|
442
|
-
sawUnavailable
|
|
443
|
-
]);
|
|
444
|
-
const restart = async () => {
|
|
445
|
-
const attempt = restartAttemptRef.current + 1;
|
|
446
|
-
restartAttemptRef.current = attempt;
|
|
447
|
-
setError(null);
|
|
448
|
-
setSawUnavailable(false);
|
|
449
|
-
setCompleting(false);
|
|
450
|
-
completingRef.current = false;
|
|
451
|
-
setRestartRequested(true);
|
|
452
|
-
setRestartStartedAt(Date.now());
|
|
453
|
-
try {
|
|
454
|
-
await mutation.mutateAsync();
|
|
455
|
-
} catch (e) {
|
|
456
|
-
if (restartAttemptRef.current !== attempt) return;
|
|
457
|
-
if (isSonamuError(e) ? e.code >= 400 && e.code < 500 : isAxiosError(e) && e.response !== void 0 && e.response.status >= 400 && e.response.status < 500) {
|
|
458
|
-
restartAttemptRef.current += 1;
|
|
459
|
-
resetMutation();
|
|
460
|
-
setConfirming(false);
|
|
461
|
-
setRestartRequested(false);
|
|
462
|
-
setRestartStartedAt(null);
|
|
463
|
-
setError(e.message || "재시작 요청이 거부되었습니다.");
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
};
|
|
467
|
-
const closeConfirmation = () => {
|
|
468
|
-
if (restartRequested) return;
|
|
469
|
-
setConfirming(false);
|
|
470
|
-
setError(null);
|
|
471
|
-
};
|
|
472
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
473
|
-
className: "panel overflow-hidden",
|
|
474
|
-
children: [
|
|
475
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
476
|
-
className: "panel-header px-4 py-2.5 sm:px-5",
|
|
477
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
478
|
-
className: "text-[13px] font-medium text-sand-700",
|
|
479
|
-
children: "서버 제어"
|
|
480
|
-
})
|
|
481
|
-
}),
|
|
482
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
483
|
-
className: "px-4 py-3 sm:px-5 flex items-start justify-between gap-4",
|
|
484
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
485
|
-
className: "min-w-0",
|
|
486
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
487
|
-
className: "text-[13px] text-sand-800",
|
|
488
|
-
children: "qgrid 재시작"
|
|
489
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
490
|
-
className: "mt-0.5 text-[11px] text-sand-500",
|
|
491
|
-
children: supervisor ? "재시작 필요 설정을 반영하고 qgrid·Codex·Claude CLI를 확인합니다" : "로컬 실행 환경에서는 재시작할 수 없습니다"
|
|
492
|
-
})]
|
|
493
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
494
|
-
type: "button",
|
|
495
|
-
disabled: !supervisor || restartRequested || mutation.isPending,
|
|
496
|
-
onClick: () => setConfirming(true),
|
|
497
|
-
className: "shrink-0 px-2.5 py-1.5 text-[11px] font-medium rounded-lg border border-red-200 text-red-600 hover:bg-red-50 disabled:opacity-40 disabled:hover:bg-transparent transition-colors duration-150",
|
|
498
|
-
children: "재시작"
|
|
499
|
-
})]
|
|
500
|
-
}),
|
|
501
|
-
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
502
|
-
className: "px-4 pb-3 sm:px-5 text-[11px] text-red-500",
|
|
503
|
-
children: error
|
|
504
|
-
}),
|
|
505
|
-
confirming && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
506
|
-
className: "fixed inset-0 z-50 flex items-start justify-center pt-[15vh]",
|
|
507
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
508
|
-
className: "absolute inset-0 bg-sand-900/8 backdrop-blur-sm",
|
|
509
|
-
onClick: closeConfirmation,
|
|
510
|
-
onKeyDown: () => {}
|
|
511
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
512
|
-
className: "relative panel shadow-xl w-full max-w-sm mx-4",
|
|
513
|
-
children: [
|
|
514
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
515
|
-
className: "px-5 py-4 border-b border-sand-100/60",
|
|
516
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", {
|
|
517
|
-
className: "text-base font-medium text-sand-900",
|
|
518
|
-
children: restartRequested ? mutation.isPending ? "재시작 요청 중" : "서버 재연결 중" : "qgrid 재시작"
|
|
519
|
-
})
|
|
520
|
-
}),
|
|
521
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
522
|
-
className: "px-5 py-4 space-y-2",
|
|
523
|
-
children: [
|
|
524
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
|
|
525
|
-
className: "text-[13px] text-sand-700",
|
|
526
|
-
children: [
|
|
527
|
-
"워커가 다시 뜰 때까지 ",
|
|
528
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", {
|
|
529
|
-
className: "text-sand-900",
|
|
530
|
-
children: "1~2분간 요청이 실패"
|
|
531
|
-
}),
|
|
532
|
-
"합니다."
|
|
533
|
-
]
|
|
534
|
-
}),
|
|
535
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
536
|
-
className: "text-[12px] text-sand-500",
|
|
537
|
-
children: "서버를 종료하면 모니터링 도구가 다시 띄웁니다. 이때 재시작 필요 설정을 반영하고, qgrid와 Codex CLI·Claude Code의 최신 버전을 확인합니다."
|
|
538
|
-
}),
|
|
539
|
-
restartRequested && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
540
|
-
className: "text-[12px] font-medium text-sienna-600",
|
|
541
|
-
children: mutation.isPending ? "종료 요청을 처리하고 있습니다…" : sawUnavailable ? "새 서버가 준비될 때까지 연결을 확인하고 있습니다…" : "서버 종료를 기다리고 있습니다…"
|
|
542
|
-
})
|
|
543
|
-
]
|
|
544
|
-
}),
|
|
545
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
546
|
-
className: "px-5 py-3 border-t border-sand-100/60 flex justify-end gap-2",
|
|
547
|
-
children: [!restartRequested && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
548
|
-
type: "button",
|
|
549
|
-
onClick: closeConfirmation,
|
|
550
|
-
className: "px-3 py-1.5 text-[12px] rounded-lg text-sand-600 hover:bg-sand-100 transition-colors duration-150",
|
|
551
|
-
children: "취소"
|
|
552
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
553
|
-
type: "button",
|
|
554
|
-
disabled: restartRequested || mutation.isPending,
|
|
555
|
-
onClick: () => void restart(),
|
|
556
|
-
className: "px-3 py-1.5 text-[12px] font-medium rounded-lg bg-red-500 text-white hover:bg-red-600 disabled:opacity-50 transition-colors duration-150",
|
|
557
|
-
children: restartRequested ? mutation.isPending ? "요청 중" : "재연결 중" : "재시작"
|
|
558
|
-
})]
|
|
559
|
-
})
|
|
560
|
-
]
|
|
561
|
-
})]
|
|
562
|
-
})
|
|
563
|
-
]
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
359
|
function SettingsPanel() {
|
|
567
360
|
const settingsQuery = SettingService.useSettingList();
|
|
568
361
|
const { data, isLoading } = settingsQuery;
|
|
@@ -611,53 +404,49 @@ function SettingsPanel() {
|
|
|
611
404
|
const groups = [...new Set(settings.map((s) => s.group))];
|
|
612
405
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
613
406
|
className: "space-y-4",
|
|
614
|
-
children: [
|
|
615
|
-
|
|
407
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
408
|
+
className: "panel overflow-hidden",
|
|
409
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
410
|
+
className: "panel-header flex items-center justify-between px-4 py-2.5 sm:px-5",
|
|
411
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
412
|
+
className: "text-[13px] font-medium text-sand-700",
|
|
413
|
+
children: "환경"
|
|
414
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
415
|
+
className: "text-[11px] text-sand-400",
|
|
416
|
+
children: "기동 시점 값"
|
|
417
|
+
})]
|
|
418
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dl", {
|
|
419
|
+
className: "px-4 py-3 sm:px-5 grid grid-cols-1 sm:grid-cols-2 gap-x-8 gap-y-1.5",
|
|
420
|
+
children: runtime.map((info) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
421
|
+
className: "flex items-baseline justify-between gap-3 min-w-0",
|
|
422
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
|
|
423
|
+
className: "text-[11px] text-sand-500 shrink-0",
|
|
424
|
+
children: info.label
|
|
425
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", {
|
|
426
|
+
className: "text-[12px] font-mono text-sand-700 truncate",
|
|
427
|
+
children: info.value
|
|
428
|
+
})]
|
|
429
|
+
}, info.label))
|
|
430
|
+
})]
|
|
431
|
+
}), groups.map((group) => {
|
|
432
|
+
const items = settings.filter((s) => s.group === group);
|
|
433
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
616
434
|
className: "panel overflow-hidden",
|
|
617
435
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
618
436
|
className: "panel-header flex items-center justify-between px-4 py-2.5 sm:px-5",
|
|
619
437
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
620
438
|
className: "text-[13px] font-medium text-sand-700",
|
|
621
|
-
children:
|
|
622
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
className: "flex items-baseline justify-between gap-3 min-w-0",
|
|
630
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
|
|
631
|
-
className: "text-[11px] text-sand-500 shrink-0",
|
|
632
|
-
children: info.label
|
|
633
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", {
|
|
634
|
-
className: "text-[12px] font-mono text-sand-700 truncate",
|
|
635
|
-
children: info.value
|
|
636
|
-
})]
|
|
637
|
-
}, info.label))
|
|
439
|
+
children: GROUP_LABELS[group] ?? group
|
|
440
|
+
}), group === "slack" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TriggerReminderButton, {})]
|
|
441
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
442
|
+
className: "divide-y divide-sand-100/80",
|
|
443
|
+
children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingRow, {
|
|
444
|
+
item,
|
|
445
|
+
disabled: item.key === "qgrid.tokenWindowKeepaliveEnabled" && !data?.keepaliveRunnerEnabled
|
|
446
|
+
}), item.key === "qgrid.tokenWindowKeepaliveEnabled" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(KeepaliveTokenSettings, { runnerEnabled: data?.keepaliveRunnerEnabled ?? false })] }, item.key))
|
|
638
447
|
})]
|
|
639
|
-
})
|
|
640
|
-
|
|
641
|
-
const items = settings.filter((s) => s.group === group);
|
|
642
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
643
|
-
className: "panel overflow-hidden",
|
|
644
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
645
|
-
className: "panel-header flex items-center justify-between px-4 py-2.5 sm:px-5",
|
|
646
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
647
|
-
className: "text-[13px] font-medium text-sand-700",
|
|
648
|
-
children: GROUP_LABELS[group] ?? group
|
|
649
|
-
}), group === "slack" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TriggerReminderButton, {})]
|
|
650
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
651
|
-
className: "divide-y divide-sand-100/80",
|
|
652
|
-
children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingRow, {
|
|
653
|
-
item,
|
|
654
|
-
disabled: item.key === "qgrid.tokenWindowKeepaliveEnabled" && !data?.keepaliveRunnerEnabled
|
|
655
|
-
}), item.key === "qgrid.tokenWindowKeepaliveEnabled" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(KeepaliveTokenSettings, { runnerEnabled: data?.keepaliveRunnerEnabled ?? false })] }, item.key))
|
|
656
|
-
})]
|
|
657
|
-
}, group);
|
|
658
|
-
}),
|
|
659
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RestartPanel, { supervisor: data?.supervisor ?? null })
|
|
660
|
-
]
|
|
448
|
+
}, group);
|
|
449
|
+
})]
|
|
661
450
|
});
|
|
662
451
|
}
|
|
663
452
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import { a as Link } from "./lazyRouteComponent-
|
|
1
|
+
import { A as require_react, E as useQuery, X as __toESM, i as RequestLogStepService, k as require_jsx_runtime, l as isSonamuError, q as __commonJSMin, r as RequestLogService } from "./services.generated-FPwsG9p3.js";
|
|
2
|
+
import { a as Link } from "./lazyRouteComponent-CUR-kWzI.js";
|
|
3
3
|
import { c as lucideX, d as Markdown, t as Route, u as remarkGfm } from "../entry-server.generated.js";
|
|
4
4
|
import { n as formatMicroUsd, t as cacheHitRate } from "./cost-CSH_t17I.js";
|
|
5
5
|
import { t as requestModelDisplay } from "./request-model-CnCeaNLE.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as require_react, O as useQueryClient, X as __toESM, k as require_jsx_runtime, n as QgridService, o as TokenService } from "./services.generated-FPwsG9p3.js";
|
|
2
2
|
import { f as lucideKeyRound, o as Q } from "../entry-server.generated.js";
|
|
3
|
-
import { t as useUpdateTokenMutation } from "./use-update-token-mutation-
|
|
4
|
-
import { n as OAuthCodeEntry, t as useOAuthLoginFlow } from "./use-oauth-login-flow-
|
|
5
|
-
import { t as lucidePlus } from "./plus-
|
|
3
|
+
import { t as useUpdateTokenMutation } from "./use-update-token-mutation-xvXbPrCI.js";
|
|
4
|
+
import { n as OAuthCodeEntry, t as useOAuthLoginFlow } from "./use-oauth-login-flow-DTQKdYBO.js";
|
|
5
|
+
import { t as lucidePlus } from "./plus-Bvdj8Pwx.js";
|
|
6
6
|
//#region src/components/qgrid/AddTokenModal.tsx
|
|
7
7
|
var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
|
|
8
8
|
var import_jsx_runtime = require_jsx_runtime();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as require_react, O as useQueryClient, X as __toESM, k as require_jsx_runtime, n as QgridService, o as TokenService } from "./services.generated-FPwsG9p3.js";
|
|
2
2
|
import { o as Q } from "../entry-server.generated.js";
|
|
3
3
|
//#region src/components/qgrid/OAuthCodeEntry.tsx
|
|
4
4
|
var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { T as useMutation, n as QgridService } from "./services.generated-FPwsG9p3.js";
|
|
2
2
|
//#region src/services/token/use-update-token-mutation.ts
|
|
3
3
|
function useUpdateTokenMutation() {
|
|
4
4
|
return useMutation({ mutationFn: ({ id, name, quotaThreshold, weight, keepaliveEnabled }) => QgridService.updateToken(id, name, quotaThreshold, weight, keepaliveEnabled) });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import { A as decodePath, C as findRouteMatch, D as createLRUCache, E as processRouteTree, F as last, I as replaceEqualDeep, M as findLast, N as functionalUpdate$1, O as invariant, P as isPromise, S as findFlatMatch, T as processRouteMasks, _ as interpolatePath, a as Link$1, b as trimPath, c as usePrevious$1, d as useRouter, f as getRouterContext, g as cleanPath, h as isNotFound, i as createRootRouteWithContext, j as deepEqual$2, k as createControlledPromise, l as matchContext, m as rootRouteId, n as createFileRoute, p as warning, r as createLazyFileRoute, s as useLayoutEffect$3, t as lazyRouteComponent, u as useRouterState, v as joinPaths, w as findSingleMatch, x as trimPathRight, y as resolvePath } from "./assets/lazyRouteComponent-
|
|
3
|
-
import { n as require_shim, t as require_react_dom } from "./assets/react-dom-
|
|
1
|
+
import { A as require_react, B as matchQuery, C as union, D as QueryClientProvider, F as notifyManager, G as focusManager, H as partialMatchKey, I as functionalUpdate, J as __exportAll, K as Subscribable, L as hashKey, M as infiniteQueryBehavior, N as Query, P as onlineManager, R as hashQueryKeyByOptions, S as string$2, U as resolveStaleTime, V as noop$1, W as skipToken, X as __toESM, Y as __require, _ as custom, b as number$1, c as fetch$1, d as SD, f as SUPPORTED_LOCALES, g as boolean$1, h as array, j as Mutation, k as require_jsx_runtime, m as _enum, n as QgridService, o as TokenService, p as setLocale, q as __commonJSMin, s as SonamuQueryMode, u as zArrayable, v as date, w as unknown, x as object, y as int, z as matchMutation } from "./assets/services.generated-FPwsG9p3.js";
|
|
2
|
+
import { A as decodePath, C as findRouteMatch, D as createLRUCache, E as processRouteTree, F as last, I as replaceEqualDeep, M as findLast, N as functionalUpdate$1, O as invariant, P as isPromise, S as findFlatMatch, T as processRouteMasks, _ as interpolatePath, a as Link$1, b as trimPath, c as usePrevious$1, d as useRouter, f as getRouterContext, g as cleanPath, h as isNotFound, i as createRootRouteWithContext, j as deepEqual$2, k as createControlledPromise, l as matchContext, m as rootRouteId, n as createFileRoute, p as warning, r as createLazyFileRoute, s as useLayoutEffect$3, t as lazyRouteComponent, u as useRouterState, v as joinPaths, w as findSingleMatch, x as trimPathRight, y as resolvePath } from "./assets/lazyRouteComponent-CUR-kWzI.js";
|
|
3
|
+
import { n as require_shim, t as require_react_dom } from "./assets/react-dom-CyuShs9K.js";
|
|
4
4
|
import minpath from "node:path";
|
|
5
5
|
import minproc from "node:process";
|
|
6
6
|
import { fileURLToPath as urlToPath } from "node:url";
|
|
@@ -46874,7 +46874,7 @@ function ChatWidget() {
|
|
|
46874
46874
|
}
|
|
46875
46875
|
//#endregion
|
|
46876
46876
|
//#region src/components/Sidebar.tsx
|
|
46877
|
-
var qgridVersion = "2.10.
|
|
46877
|
+
var qgridVersion = "2.10.4";
|
|
46878
46878
|
function Sidebar({ className }) {
|
|
46879
46879
|
const pathname = useRouterState({ select: (s) => s.location.pathname });
|
|
46880
46880
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("aside", {
|
|
@@ -68752,15 +68752,15 @@ function RootComponent() {
|
|
|
68752
68752
|
}
|
|
68753
68753
|
//#endregion
|
|
68754
68754
|
//#region src/routes/tokens.tsx
|
|
68755
|
-
var $$splitComponentImporter$5 = () => import("./assets/tokens-
|
|
68755
|
+
var $$splitComponentImporter$5 = () => import("./assets/tokens-odgS2-9q.js");
|
|
68756
68756
|
var Route$5 = createFileRoute("/tokens")({ component: lazyRouteComponent($$splitComponentImporter$5, "component") });
|
|
68757
68757
|
//#endregion
|
|
68758
68758
|
//#region src/routes/settings.tsx
|
|
68759
|
-
var $$splitComponentImporter$4 = () => import("./assets/settings-
|
|
68759
|
+
var $$splitComponentImporter$4 = () => import("./assets/settings-DeHNwoMv.js");
|
|
68760
68760
|
var Route$4 = createFileRoute("/settings")({ component: lazyRouteComponent($$splitComponentImporter$4, "component") });
|
|
68761
68761
|
//#endregion
|
|
68762
68762
|
//#region src/routes/monit.tsx
|
|
68763
|
-
var $$splitComponentImporter$3 = () => import("./assets/monit-
|
|
68763
|
+
var $$splitComponentImporter$3 = () => import("./assets/monit-DSxs4Jnl.js");
|
|
68764
68764
|
var monitSearchSchema = object({ level: _enum([
|
|
68765
68765
|
"all",
|
|
68766
68766
|
"warn",
|
|
@@ -69137,7 +69137,7 @@ object({
|
|
|
69137
69137
|
_enum(["A"]);
|
|
69138
69138
|
//#endregion
|
|
69139
69139
|
//#region src/routes/logs.tsx
|
|
69140
|
-
var $$splitComponentImporter$2 = () => import("./assets/logs-
|
|
69140
|
+
var $$splitComponentImporter$2 = () => import("./assets/logs-CagNGTBj.js");
|
|
69141
69141
|
var logsSearchSchema = object({
|
|
69142
69142
|
token: string$2().optional(),
|
|
69143
69143
|
project: string$2().optional(),
|
|
@@ -69151,11 +69151,11 @@ var Route$2 = createFileRoute("/logs")({
|
|
|
69151
69151
|
});
|
|
69152
69152
|
//#endregion
|
|
69153
69153
|
//#region src/routes/index.tsx
|
|
69154
|
-
var $$splitComponentImporter$1 = () => import("./assets/routes-
|
|
69154
|
+
var $$splitComponentImporter$1 = () => import("./assets/routes-i-sTqXMX.js");
|
|
69155
69155
|
var Route$1 = createFileRoute("/")({ component: lazyRouteComponent($$splitComponentImporter$1, "component") });
|
|
69156
69156
|
//#endregion
|
|
69157
69157
|
//#region src/routes/requests/show.tsx
|
|
69158
|
-
var $$splitComponentImporter = () => import("./assets/show-
|
|
69158
|
+
var $$splitComponentImporter = () => import("./assets/show-C4jk1Hrp.js");
|
|
69159
69159
|
var showSearchSchema = object({ id: number$1() });
|
|
69160
69160
|
var Route = createFileRoute("/requests/show")({
|
|
69161
69161
|
validateSearch: showSearchSchema,
|
package/package.json
CHANGED
package/skills/qgrid/SKILL.md
CHANGED
|
@@ -45,6 +45,7 @@ Request logging is enabled by default. Use `providerOptions.qgrid.logger: false`
|
|
|
45
45
|
## Non-Negotiable Boundaries
|
|
46
46
|
|
|
47
47
|
- Use `packages/ai-sdk` as the active public SDK surface.
|
|
48
|
+
- Expose thinking only as `providerOptions.qgrid.thinking?: boolean`: omission preserves the model policy, false overrides effort, and unsupported/unverified off requests fail explicitly. See the SDK and provider runtime references for the dated support snapshot. Keep Anthropic thinking controls separate from mandatory configuration and memory isolation. Haiku/Sonnet 5.5 off uses a computed thinking-only native body override with Claude Code >=2.1.295; never inherit or expose arbitrary extra body.
|
|
48
49
|
- The old v1 SDK package (`packages/sdk`, `@cartanova/qgrid-sdk`) has been removed from the repository. It survives only as a deprecated npm artifact (1.9.0); do not resurrect it or write new code against it.
|
|
49
50
|
- Let the server infer single-turn versus tool-run request-log lifecycle. Do not recreate caller-selected logging modes in the SDK or API examples.
|
|
50
51
|
- Route provider models by prefix: `openai/*` goes to the OpenAI Codex runtime, `anthropic/*` goes to the Anthropic Claude Code runtime. Prefix-less model fallback is not implemented.
|
|
@@ -63,7 +63,8 @@ Do not write `providerOptions: { ... } satisfies QgridProviderOptions`: the expo
|
|
|
63
63
|
rejected by the SDK before transport; omission keeps weighted round-robin routing.
|
|
64
64
|
- `logger`: request-log switch. Omitted or `true` is the default and enables qgrid logging; `false` guarantees that generation creates zero qgrid request-log rows.
|
|
65
65
|
- `sessionKey`: source for model-scoped opaque OpenAI prompt-cache affinity. Disabled for Anthropic models.
|
|
66
|
-
- `
|
|
66
|
+
- `thinking`: optional boolean for both providers. Omission preserves existing model behavior. True enables thinking with the normal request effort → config `defaultEffort` → default resolution; it does not guarantee thinking tokens on every response. False ignores both efforts, including in request logging, and rejects unsupported or unverified model/runtime combinations. Generate, stream, tool continuation, and structured output carry the same boolean. Update SDK and server together: older servers can ignore the field. No `defaultThinking` or public `effort: "none"` is exposed. Haiku/Sonnet 5.5 off requires Claude Code >=2.1.295; Sonnet uses `between_tools` and may retain thinking blocks between tools.
|
|
67
|
+
- `effort`: provider-specific vocabulary. OpenAI (ChatGPT-subscription Codex route): `low | medium | high | xhigh | max | ultra`, where GPT-6 Astra, GPT-6 Sol, GPT-5.6 Sol, and Terra support `ultra`, and GPT-6 Luna and GPT-5.6 Luna support through `max`; `none`/`minimal` are not public effort values; use `thinking: false` for verified off-capable models. Anthropic (Claude Code `--effort`): `low | medium | high | xhigh | max`. The server resolves the value per provider and model (`providers/common/effort.ts`): anything outside the provider set, or above the Codex model's supported maximum, is silently dropped and treated as unspecified. With thinking omitted, Anthropic falls back to qgrid's default `low`; OpenAI omits `reasoning.effort` so the backend default applies. With `thinking: true`, OpenAI explicitly falls back to `low` for absent or unsupported effort, including raw API requests. With `thinking: false`, both providers ignore caller/default effort and apply their off policy instead. Invalid effort itself produces no error or metadata, only a debug log; unsupported thinking-off requests still fail explicitly. Use `QgridOpenAIProviderOptions` / `QgridAnthropicProviderOptions` for provider-typed `satisfies`; `QgridProviderOptions` is their union.
|
|
67
68
|
- `verbosity`: OpenAI/Codex route only.
|
|
68
69
|
- `reasoningSummary`: OpenAI/Codex route only.
|
|
69
70
|
- `serviceTier`: OpenAI/Codex route only.
|
|
@@ -77,7 +77,7 @@ claude -p
|
|
|
77
77
|
--model <canonical-model-or-1m-suffix>
|
|
78
78
|
--system-prompt <text> # small system prompt
|
|
79
79
|
--system-prompt-file <path> # large system prompt
|
|
80
|
-
--thinking disabled
|
|
80
|
+
--thinking <disabled|adaptive> # resolved from thinking and the existing model default
|
|
81
81
|
--effort <effort-or-low>
|
|
82
82
|
--disable-slash-commands
|
|
83
83
|
--session-id <uuid>
|
|
@@ -97,7 +97,11 @@ Important details:
|
|
|
97
97
|
- Large system prompts over 64 KiB are written to a temporary file to avoid argv `E2BIG` — this
|
|
98
98
|
same branch absorbs large injected schema contracts, so the old 64 KiB schema argv limit no
|
|
99
99
|
longer applies (only the global 512 KiB caller-schema complexity limit remains).
|
|
100
|
-
-
|
|
100
|
+
- One policy resolves the CLI thinking flag and effort before spawning. Omission preserves the existing model policy: Fable 5/5.1, Opus 5/5.5, Sonnet 5.5 and Haiku 5.5 keep adaptive defaults; other models use `--thinking disabled`. Explicit `true` uses `--thinking adaptive` and the normal effort resolution. Explicit `false` uses `--thinking disabled --effort low` only on supported models, ignoring caller/default effort. `CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING` and `MAX_THINKING_TOKENS` are no longer sent. Do not reintroduce overlapping suppressors or inherit `CLAUDE_CODE_EFFORT_LEVEL`.
|
|
101
|
+
- The 2026-10-10 off policy includes Haiku 4.5/5.5, Sonnet 4.5/4.6/5/5.5, and Opus 4.5/4.6/4.7/4.8/5. Fable 5/5.1 and Opus 5.5 reject disabled thinking at the backend. Off requests for unverified models fail before spawning instead of changing models or silently enabling thinking.
|
|
102
|
+
- Haiku 5.5 and Sonnet 5.5 require Claude Code 2.1.295 or newer for explicit false. Cache the result of `claude --version`; unknown or older versions return an input error. The policy computes `CLAUDE_CODE_EXTRA_BODY` containing only `thinking: {type: "disabled"}` for Haiku or `thinking: {type: "between_tools"}` for Sonnet, alongside `--thinking disabled --effort low`. Native delivery was verified on 2.1.295; invalid-type and Sonnet-disabled negative controls confirmed that the CLI applied the body field. `between_tools` disables pre-answer thinking but may emit thinking blocks between tools. Never inherit this env variable or expose arbitrary body overrides to callers.
|
|
103
|
+
- Legacy environment suppression versus CLI-only disabled was compared on Sonnet 4.6 and Opus 4.8: both completed with zero thinking tokens in the short-text baseline. The comparison supports removing the duplicate suppressors without changing the omitted-option policy.
|
|
104
|
+
|
|
101
105
|
|
|
102
106
|
## Spawn env
|
|
103
107
|
|
|
@@ -109,14 +113,13 @@ Included env:
|
|
|
109
113
|
- `TMPDIR`
|
|
110
114
|
- `CLAUDE_CODE_OAUTH_TOKEN`
|
|
111
115
|
- `CLAUDE_CONFIG_DIR`
|
|
116
|
+
- `CLAUDE_CODE_EXTRA_BODY` only when computed by the thinking policy for explicit Haiku/Sonnet 5.5 off; never inherited
|
|
112
117
|
- `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1`
|
|
113
|
-
- `CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING=1` except for Fable 5/5.1 and Opus 5
|
|
114
118
|
- `CLAUDE_CODE_DISABLE_CLAUDE_MDS=1`
|
|
115
119
|
- `CLAUDE_CODE_DISABLE_TERMINAL_TITLE=1`
|
|
116
120
|
- `CLAUDE_CODE_DISABLE_BUNDLED_SKILLS=1`
|
|
117
121
|
- `CLAUDE_CODE_DISABLE_WORKFLOWS=1`
|
|
118
122
|
- `CLAUDE_CODE_ATTRIBUTION_HEADER=0`
|
|
119
|
-
- `MAX_THINKING_TOKENS=0` except for Fable 5/5.1 and Opus 5
|
|
120
123
|
- `CLAUDE_CODE_DISABLE_1M_CONTEXT=1` when model does not support qgrid's 1M path
|
|
121
124
|
- `MAX_STRUCTURED_OUTPUT_RETRIES` for streaming structured output only (not non-streaming `generate`)
|
|
122
125
|
|
|
@@ -92,17 +92,20 @@ Some env values are editable at runtime through the dashboard's Settings page, b
|
|
|
92
92
|
- Editable: Slack delivery control (master switch, reminder on/off, reminder interval, quiet-hours window, weekend behaviour), and the Slack channel/user-map/bot-token. `SETTING_DEFS` in `setting.constant.ts` is the single definition — key, env name, type, bounds, and whether the change applies immediately or needs a restart. The `settings` table itself (`setting.entity.json`) only stores `(key, value)` strings; everything the UI needs to render and validate a key lives in that constant, not in the schema.
|
|
93
93
|
- Not editable: anything needed before the server can read its own database (`QGRID_DB_*`, `HOST`, `PORT`, `NODE_ENV`). These are exposed read-only on the same page so an operator can confirm which environment they are looking at; the DB password is masked.
|
|
94
94
|
|
|
95
|
-
###
|
|
95
|
+
### Process lifecycle
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
qgrid does not detect a process supervisor or expose a restart API or dashboard
|
|
98
|
+
control. Restarting, replacing, and recovering the process belong to the
|
|
99
|
+
operator's deployment tooling. Settings marked `restart` still take effect on
|
|
100
|
+
the next process start; the label does not imply an in-app restart action.
|
|
101
|
+
|
|
102
|
+
There is no SIGTERM drain handler. Replacing the process during traffic can
|
|
103
|
+
interrupt active responses, so deployment tooling must account for startup
|
|
104
|
+
readiness and interruption. Ordinary request success/error/abort accounting and
|
|
105
|
+
stale tool-wait cleanup remain independent of the deployment mechanism.
|
|
106
|
+
|
|
107
|
+
### Live notification settings
|
|
98
108
|
|
|
99
|
-
- `detectSupervisor` (`utils/process-supervisor.ts`) accepts only `pm_id` (pm2). `INVOCATION_ID` proves systemd launched a process but does not prove the unit has a restart policy, so it is not sufficient. Without pm2, `restartServer` returns 400 and the button is disabled: exiting with nothing to relaunch the process is a shutdown, not a restart. Presence is the signal, not the value — pm2 sets `pm_id=0` for the first process.
|
|
100
|
-
- Browser requests must be same-host: when `Origin` is present and its host differs from `X-Forwarded-Host`/`Host`, `restartServer` returns 403. Origin-less operational clients such as `curl` remain supported. This is CSRF protection, not a substitute for authenticating the management API at Caddy.
|
|
101
|
-
- The first restart request latches `restartPending`, blocks new native query/stream dispatch with 503, and marks only process-local active native request-log IDs as `error: server restarted`. Tool-result-waiting runs and externally owned logger runs are untouched. This is state reconciliation, not request draining.
|
|
102
|
-
- Exit is process-wide and one-shot. It runs after the restart response emits `finish`; a response `close` or a five-second fallback covers disconnects. Concurrent restart calls do not schedule multiple exits.
|
|
103
|
-
- The supervisor relaunches from `ecosystem.config.cjs`, not from the dying process's argv. That file carries no `--skip-update`, so each restart also runs the CLI self-update — a restart is a deploy. The confirmation dialog says so; dropping the update means adding `--skip-update` there.
|
|
104
|
-
- The relaunch reuses the environment pm2 captured when the app was first started. Neither the dashboard restart nor `pm2 restart qgrid` re-reads `ecosystem.config.cjs`, so an env edit in that file (for example `QGRID_TOKEN_WINDOW_KEEPALIVE_ENABLED`) stays invisible until `pm2 restart ecosystem.config.cjs --update-env` (or `pm2 delete` + `pm2 start`) followed by `pm2 save`. Verify with `GET /api/setting/listSettings`, which reports the env the process actually sees. dev0 ran for a week with the keepalive env in the file but not in the process because of this (2026-09-02).
|
|
105
|
-
- In-flight provider responses are not drained. There is no SIGTERM handler, so a restart during traffic cuts active responses, and the OpenAI pool needs 1–2 minutes before requests succeed again (`QgridDispatcher.startupState` answers 503 in that window). The dashboard stays locked after confirmation, observes an unavailable/not-ready health state, then waits for a later `ready: true` before refreshing settings.
|
|
106
109
|
- Slack channel, bot-token, and user-map changes are read on the next notification. Changing the expiry-reminder interval or toggling the reminder replaces the current timer immediately without sending a notification merely because the setting changed; process boot still performs the intended immediate reminder run.
|
|
107
110
|
- `slack.enabled` is the holiday switch: an operator turns it off for periods no rule can express. It suppresses ordinary notifications but not `urgent` ones, so a provider losing its last token still reports during a long break. `slack.remindersEnabled` is narrower — it stops only the repeat timer and preserves the chosen interval so re-enabling restores it.
|
|
108
111
|
- Quiet hours are `slack.quietFromHour`/`slack.quietUntilHour` (0–23, Asia/Seoul) plus `slack.notifyOnWeekends`. From > until reads as an overnight window (20→8); from < until reads as a same-day window, which night-shift teams may want. Equal values mean no quiet window at all. Out-of-range values fall back to 20/8 at runtime, since env can bypass the stored-value validation.
|
|
@@ -37,6 +37,15 @@ Key decisions:
|
|
|
37
37
|
- The SDK should stay a thin adapter. Server APIs own request-log lifecycle, provider dispatch, structured-output emulation, and provider runtime details.
|
|
38
38
|
- Request logging defaults to enabled. `providerOptions.qgrid.logger: false` is the single per-generation opt-out for both native qgrid logs and `createQgridLogger` telemetry logs; it must not alter generation or tool behavior.
|
|
39
39
|
|
|
40
|
+
## Thinking Control
|
|
41
|
+
|
|
42
|
+
Source: `docs/plans/2026-10-10-001-feat-thinking-control-plan.md`.
|
|
43
|
+
|
|
44
|
+
- Use the independent `providerOptions.qgrid.thinking?: boolean` switch. False makes effort irrelevant; do not reject the combination or expose a cross-provider `effort: "none"` value.
|
|
45
|
+
- Preserve omitted-option behavior. Reject unsupported and runtime-unverified off requests before token acquisition/spawn, without fallback. Backend capability and Claude Code delivery are separate evidence.
|
|
46
|
+
- Use one Anthropic CLI thinking policy instead of overlapping environment suppressors. Preserve the OAuth env allowlist, token-specific config, empty settings, isolated cwd, memory/CLAUDE.md/skills/workflow disabling, tools, attribution, and 1M policy. These isolation controls prevent previously observed cross-request context injection and account metadata leakage.
|
|
47
|
+
- Haiku/Sonnet 5.5 use a narrowly computed native `CLAUDE_CODE_EXTRA_BODY` thinking field, guarded by Claude Code >=2.1.295. Do not inherit that variable, expose arbitrary body overrides, add a transport, or add a relay. Sonnet's `between_tools` exception remains visible: it turns off pre-answer thinking but can retain thinking blocks between tools.
|
|
48
|
+
|
|
40
49
|
## Tool Calling And Request-Log Lifecycle
|
|
41
50
|
|
|
42
51
|
Sources:
|
|
@@ -12,6 +12,12 @@ Use this reference before changing OpenAI transport, concurrency, routing, promp
|
|
|
12
12
|
- Direct quota lookup: `openai-quota.ts`.
|
|
13
13
|
- Provider integration and history: `packages/api/src/application/qgrid/qgrid.dispatcher.ts`, `conv-routing.ts`.
|
|
14
14
|
|
|
15
|
+
## Thinking control
|
|
16
|
+
|
|
17
|
+
`providerOptions.qgrid.thinking` reaches the Responses builder as a boolean. False overrides request/default effort and sends `reasoning: { effort: "none" }` without a reasoning summary. True uses a valid resolved effort, or explicitly sends `low` when effort is absent or unsupported, so a raw API request cannot fall back to a non-thinking backend default. Omission preserves the existing behavior: valid effort is forwarded; absent or unsupported effort leaves the backend default in effect. True and omission retain the requested reasoning summary policy. `none` belongs only to the internal wire type, not the public effort enum.
|
|
18
|
+
|
|
19
|
+
The 2026-10-10 subscription probe accepted off for `gpt-6-sol`, `gpt-6-luna`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, and `gpt-5.5`. `gpt-6-astra` and `gpt-6.1-sol` explicitly rejected it. Other model IDs are unverified for off and are rejected locally only when false is requested. Model-access failures are not evidence of mandatory thinking. Validate terminal success and response settings, not HTTP 200 alone: errors can arrive within SSE. The same policy applies to text-model requests driving image generation, not to the image model itself.
|
|
20
|
+
|
|
15
21
|
## Adding an OpenAI model
|
|
16
22
|
|
|
17
23
|
Use this checklist whenever a model is added. A public API release does not by itself prove
|
|
@@ -253,6 +253,8 @@ Anthropic's 5-hour usage window anchors on the first request, so an unused token
|
|
|
253
253
|
|
|
254
254
|
Keepalive breaks that coupling by burning the window clock during idle time. Every keepalive-enabled active Anthropic token gets one cheap request as soon as its window expires, so windows always run and a workday lands mid-window rather than on a boundary. Across an eight-hour day this raises the number of windows the day can touch from a fixed 2 to an expected 2.6.
|
|
255
255
|
|
|
256
|
+
The keepalive request uses `anthropic/claude-haiku-5-5`, `thinking: false`, `effort: "low"`, and the prompt `Reply OK.`. Thinking is disabled because this request only opens the usage window; the false policy overrides the supplied effort and uses the provider's low-effort off path. Haiku 5.5 off requires Claude Code >=2.1.295. The model constant lives in `token-window-keepalive.ts`; changing it does not change the scheduler or opt-in gates below.
|
|
257
|
+
|
|
256
258
|
- Since 2.8.0 (`ea7feb3`) keepalive is opt-in at two levels. The runner instance must have env `QGRID_TOKEN_WINDOW_KEEPALIVE_ENABLED=true` (default off; set it on one instance per shared DB), and a token is a target only when `tokens.keepalive_enabled` is `true` (default `false`, toggled in the dashboard settings panel). The global setting `qgrid.tokenWindowKeepaliveEnabled` is a kill switch (`false` disables everything), not a substitute for either gate. In 2.7.6 every instance fired for every active token.
|
|
257
259
|
- Operational trap: the 2.8.0 migration adds `keepalive_enabled` as default `false` without backfilling existing tokens. After upgrading, both the runner env and the per-token toggles must be set, or keepalive silently schedules zero tokens. This happened on dev0 after the 2026-08-31 self-update; a lingering 2.7.6 instance masked it until 2026-09-01. Diagnose with `GET /api/setting/listSettings` (`keepaliveRunnerEnabled`), `GET /api/token/findMany?subset=A` (`keepalive_enabled`), and the latest request log under project `qgrid-token-window-keepalive`.
|
|
258
260
|
- The next fire is scheduled from the token's real `resetsAt` rather than a fixed five-hour timer, so a provider-side change to the window length is followed automatically.
|
|
@@ -24,6 +24,16 @@ qgrid supports the AI SDK `tools` interface, but it does not use native provider
|
|
|
24
24
|
- qgrid maps that structured object back into AI SDK `tool-call` content.
|
|
25
25
|
- The AI SDK executes the actual tool functions and calls qgrid again with tool results.
|
|
26
26
|
|
|
27
|
+
OpenAI can emit separate commentary and final-answer message items in one response.
|
|
28
|
+
Plain commentary is an intermediate update, not an emulation payload: do not join
|
|
29
|
+
it to the final JSON. Known final-answer message deltas still stream incrementally.
|
|
30
|
+
For tool emulation, a complete message that passes the strict envelope and tool-name
|
|
31
|
+
validation with `action: tool_call` is the client handoff boundary. Stop aggregating
|
|
32
|
+
and streaming later text from that response: it cannot incorporate a tool result
|
|
33
|
+
that the client has not produced yet. Drain the upstream response to preserve usage,
|
|
34
|
+
errors, and cancellation. Never split concatenated JSON inside a single message or
|
|
35
|
+
recover malformed envelopes. Ordinary text requests keep their existing aggregation.
|
|
36
|
+
|
|
27
37
|
`generateText` and `streamText` default to `stepCountIs(1)`. When an executable
|
|
28
38
|
tool can run before the final answer, callers must set a bounded `stopWhen`
|
|
29
39
|
(for example `stepCountIs(3)`) or the AI SDK stops after the first tool-call
|
|
@@ -99,9 +109,16 @@ The schema explicitly tells the model:
|
|
|
99
109
|
|
|
100
110
|
- use `tool_call` when client-side tool execution is needed;
|
|
101
111
|
- use `answer` only for a final answer;
|
|
102
|
-
-
|
|
112
|
+
- return the tool request as the JSON response body, then end the turn and wait for client results;
|
|
113
|
+
- do not invent tool results or emit an answer before the required results arrive;
|
|
103
114
|
- put tool arguments in `args` as a JSON string.
|
|
104
115
|
|
|
116
|
+
Do not instruct OpenAI to use a `StructuredOutput` or Claude Code tool: neither is
|
|
117
|
+
provided on this route. The legacy schema descriptions contained that stale
|
|
118
|
+
instruction; schema descriptions now explain the actual client handoff protocol.
|
|
119
|
+
The discriminated union, required fields, argument encoding and user-schema refs
|
|
120
|
+
are unchanged. Prompt wording improves guidance but does not guarantee model compliance.
|
|
121
|
+
|
|
105
122
|
Delivery differs by provider (SON-532). OpenAI: `qgrid.dispatcher.ts` strictifies the composed
|
|
106
123
|
envelope through `buildStrictOutputSchema` and passes it as the response format — constrained
|
|
107
124
|
decoding enforces it. Anthropic: no schema is passed to CC at all; `schema-prompt.ts` renders the
|