elanous 0.2.25 → 0.2.27
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/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/{1454-37fd16f479f5554a.js → 1454-a9c338f5bb556f42.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/{page-9360b1285b2a346e.js → page-a1ce306364f08da9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-07110da72c45e68f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-d2d76d4a7eae91bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-30007380f2e2a3c1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-e5a9302c105725a3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-b93c04b5311c97ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-718a49e86bf25f9c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-44eaad486bc98a45.js → page-20662900d6b4b455.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/{page-97fa6cf3d34c7eb9.js → page-f707a509515ac50c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/{page-305cd294e754906e.js → page-6a16a48d1aa6e91f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-fcb06e305f515527.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-6acf5ec4e6c6d1af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-45889e7070d11126.js → page-39f82acf619cca23.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-16a2e893f83ae4d0.js → page-7c2c8f81b185f9c0.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ae7aff07e32676bb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-56888bc744e08dac.js → page-482c60c1bc540d0a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-7cb996c1aa08b6f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/{page-952c6ceec6d9de38.js → page-623e08fe4477d606.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/{page-6d0202af568f4c63.js → page-db460cd8aecb8490.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-ff27902d0b11ef4f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/{page-74366ae2693a7d95.js → page-8397a5c6ca569bf4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-4fb6fc34e20c07b7.js → page-1917ea87ea71ab6b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-e9502921f4f01b1f.js → page-25a302273b054fd7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-b6169ea2f09cf287.js → not-found-670510eda8b9443f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-d73c3f1e00d93986.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-65caacf54389081e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4bb7556e2e97a750.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-77e04b4f8762c666.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/{page-b9cc8a10bc6078b4.js → page-15c0a4095baddfba.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{page-3d3fc73dd187ed52.js → page-4eb96fc7241c2456.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-63e272a4cd3b6634.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-9c04dac0831c2bbb.js → page-0d64ff212dc55d03.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-7f096248993fc2d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-04de9ca6d52a3893.js → page-e47fc72049149a38.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-62527677bbc2fdc4.js → page-f7d808e31e005895.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-9a2e09c4a311764c.js → page-5c2b24b5496dad73.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-bfeb35d69e8b24bb.js → page-76cdb531668b5191.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-e2b63345bba1a11f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/{page-b008aff96be56654.js → page-d376e0c83edc041a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-d6db55fe2c04d128.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-87b88f551f421d8b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/{page-6445ac4cbb8dd869.js → page-dab163b338e3b966.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/{page-852170303c6cd755.js → page-08019efef111ed2e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-0260ffc7eda50db1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-97c7361cc2a19319.js → page-980ce6fe36ad95eb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-20162174b77f96cf.js → page-99c4e6c7345a9156.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-8c6901833f897496.js → page-0ab42e0f05823013.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ad9220422d20ba33.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +13 -13
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +13 -13
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +13 -13
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +13 -13
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +13 -13
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +13 -13
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +13 -13
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +13 -13
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +13 -13
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +13 -13
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +13 -13
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +13 -13
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +13 -13
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +13 -13
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +13 -13
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +13 -13
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +13 -13
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +13 -13
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +13 -13
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +13 -13
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +13 -13
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +13 -13
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +13 -13
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +13 -13
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +13 -13
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +13 -13
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +13 -13
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +13 -13
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +14 -14
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +13 -13
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +13 -13
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +13 -13
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +13 -13
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +13 -13
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +13 -13
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +13 -13
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +13 -13
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +13 -13
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +13 -13
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +13 -13
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +13 -13
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +13 -13
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +13 -13
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +13 -13
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +13 -13
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +13 -13
- package/package.json +1 -1
- package/scripts/ci-typecheck-changed.ts +81 -2
- package/scripts/coord-msg-bridge.ts +209 -0
- package/scripts/coord-post.sh +40 -7
- package/scripts/coord-requests-bridge.ts +10 -5
- package/scripts/expose-rubric-suggest.ts +1 -1
- package/scripts/measure-tsc-affected-roots.ts +10 -1
- package/scripts/release-loop/export-check-node.ts +8 -5
- package/scripts/release-loop/node-verdict.ts +7 -2
- package/src/agent/task-notification.ts +30 -1
- package/src/agent-substrate/review-observation.ts +9 -2
- package/src/agent-substrate/self-review-cli.ts +28 -6
- package/src/boot/daemon-tools/self-implement.ts +72 -4
- package/src/budget/llm-cost.ts +5 -1
- package/src/cli/control-cli.ts +29 -0
- package/src/cli/doctor-cli.ts +90 -8
- package/src/cli/msg-cli.ts +15 -5
- package/src/cli/pr-cli.ts +53 -0
- package/src/cli/pr-land-overlap-evidence.ts +18 -10
- package/src/cli/release-cli.ts +32 -5
- package/src/cli/seat-cli.ts +121 -0
- package/src/cli/self-utterances-cli.ts +140 -0
- package/src/cli/tasks-cli.ts +139 -9
- package/src/context-card/cell.ts +40 -0
- package/src/dashboard/boot-timing.ts +35 -0
- package/src/dashboard/index.ts +86 -24
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +26 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +6 -0
- package/src/debug/log.ts +1 -1
- package/src/domains/release-tool.ts +29 -12
- package/src/domains/surface-events.ts +16 -0
- package/src/elanous-config-dir.ts +2 -2
- package/src/fleet/host-doctor.ts +73 -2
- package/src/graph-runner/runner.ts +3 -1
- package/src/harness/harness-cli-command.ts +46 -4
- package/src/harness/harness-stop.ts +11 -7
- package/src/index.ts +35 -4
- package/src/instance/current.ts +2 -6
- package/src/instance/resolve.ts +5 -6
- package/src/llm/openai-sampling-fields.ts +89 -0
- package/src/llm/runtime-model-family.ts +61 -0
- package/src/llm/zai-coding-plan.ts +27 -1
- package/src/llm.ts +13 -89
- package/src/machines/machine-ledger.ts +31 -3
- package/src/maturity/cli-maturity.ts +13 -3
- package/src/msg/msg-store.ts +69 -7
- package/src/ops/overview-collect.ts +3 -2
- package/src/ops/overview-owner.ts +29 -0
- package/src/plugin-hooks/shell-hook.ts +17 -2
- package/src/plugin-missions/evaluator-runner.ts +18 -2
- package/src/plugins/install/plugin-install.ts +11 -1
- package/src/prompt-enhance/enhance.ts +11 -0
- package/src/release-loop/checklist.ts +41 -4
- package/src/release-loop/feature-store.ts +10 -9
- package/src/seat-loop/seat-loop.ts +5 -1
- package/src/self-dev/draft-sweep.ts +23 -14
- package/src/self-dev/orchestrate.ts +6 -2
- package/src/self-dev/run-supervisor.ts +35 -1
- package/src/self-implement/goal-author.ts +3 -27
- package/src/self-implement/goal-command-execution.ts +26 -0
- package/src/self-implement/orchestrator.ts +2 -1
- package/src/self-implement/pwa-import-graph.ts +194 -7
- package/src/self-implement/self-implement-runtime.ts +126 -21
- package/src/session/index.ts +14 -4
- package/src/session-runtime/index.ts +55 -11
- package/src/task-agent/cover.ts +28 -9
- package/src/task-agent/live-moves.ts +14 -6
- package/src/task-agent/rejudge-on-head.ts +220 -23
- package/src/task-agent/review-result-capture.ts +64 -1
- package/src/task-agent/shadow.ts +137 -22
- package/src/task-agent/task-hand.ts +18 -2
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +1 -0
- package/src/tool-runtime/tool-search-runtime.ts +25 -0
- package/src/tui.ts +20 -1
- package/src/user-config.ts +3 -59
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f08f6529d09ffae1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-f000d54d258f9ef6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-b12149c51f7d04b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-22f674e7e4c2c9d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-8888733e40cea6d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9717ec6964b92276.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-dea4b795741126fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-35e05d7450461a67.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-b2638013c0bf7dfd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-c44f93a41167536f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-1b810474507f3702.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-6a14333d1b1b3967.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-8c91a9a66c9ec90a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4e634c5b98fd4e93.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ed3070d637850548.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-128458c8d86fbb5f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-cf0c2b0183991d09.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-89cb33bced824e4a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-814394dcbae789b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-6a87325c87f09dd2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-087ceeb975ef272b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-05a4b2c9b91034aa.js +0 -1
- /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → Ev2jivDMTSoEc-5rIXdcy}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → Ev2jivDMTSoEc-5rIXdcy}/_ssgManifest.js +0 -0
|
@@ -25,6 +25,8 @@ import { classifyPodExitStop, recordRunStop, runStopRecorded, RUN_STOP_EVENT } f
|
|
|
25
25
|
import { ensureOpenRouterChildModelLive, resolveChildLlmEffort, resolveImplementationChildModel } from '../self-dev/dev-cli.js';
|
|
26
26
|
import { resolveHarnessTarget } from '../self-implement/harness-target-options.js';
|
|
27
27
|
import { buildRunProgressRows, isPartialRunningRuns, queryRunningRuns, renderRunProgressTable } from '../self-implement/running-runs.js';
|
|
28
|
+
import { launchBindingRunIds, readLaunchBinding } from './launch-registry.js';
|
|
29
|
+
import { psProcessStartMs } from './harness-stop.js';
|
|
28
30
|
import { DevPipelineError } from '../self-dev/dev-pipeline.js';
|
|
29
31
|
import { collectDraftMetrics, collectOverlapMetrics, DRAFT_SWEEP_CONCURRENCY, mapBounded, resolveDraftSweepCloseCap, runDraftSweep, sweepFailureReason, type DraftSweepAdapters, type DraftSweepResult, type OverlapMetricAdapters, type SweepDraft, type SweepMergedPr, type SweepReviewGate } from '../self-dev/draft-sweep.js';
|
|
30
32
|
import { readAskPreflightRowsSince, readSalvagedRowsSince } from '../self-dev/ask-launch-io.js';
|
|
@@ -2391,6 +2393,7 @@ const defaultDraftSweepStopRun: DraftSweepStopRun = async (runId) => {
|
|
|
2391
2393
|
export function githubDraftSweepAdapters(execute: GhExecute = executeGh, git: DraftSweepGitExecute = runGitCommand, cwd = process.cwd(),
|
|
2392
2394
|
executeAsync: (args: string[]) => Promise<string> = execute === executeGh ? executeGhAsync : async (args) => execute(args),
|
|
2393
2395
|
stopRun: DraftSweepStopRun = defaultDraftSweepStopRun): GithubDraftSweepAdapters {
|
|
2396
|
+
const ensuredSweepLabels = new Set<string>();
|
|
2394
2397
|
const inventory = async (repository: string, state: 'open' | 'closed'): Promise<GithubPull[]> => {
|
|
2395
2398
|
// ⛔ A large repository has tens of thousands of closed PRs (2026-09-28: 21k → «exceeded pagination limit», no sweep at all).
|
|
2396
2399
|
// Merged twins only matter after the oldest open draft was opened, so the closed listing stops at that time.
|
|
@@ -2642,11 +2645,19 @@ export function githubDraftSweepAdapters(execute: GhExecute = executeGh, git: Dr
|
|
|
2642
2645
|
.map((line) => line.slice('branch refs/heads/'.length).trim()));
|
|
2643
2646
|
},
|
|
2644
2647
|
setLabels: async (repository, number, change) => {
|
|
2648
|
+
if (change.add.startsWith('elanous:swept:')) {
|
|
2649
|
+
const key = `${repository}:${change.add}`;
|
|
2650
|
+
if (!ensuredSweepLabels.has(key)) {
|
|
2651
|
+
execute(['label', 'create', change.add, '--repo', repository, '--color', '6A737D',
|
|
2652
|
+
'--description', 'Draft sweep close reason; reopen this PR to restore it', '--force']);
|
|
2653
|
+
ensuredSweepLabels.add(key);
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2645
2656
|
execute(['pr', 'edit', String(number), '--repo', repository, '--add-label', change.add,
|
|
2646
2657
|
...change.remove.flatMap((name) => ['--remove-label', name])]);
|
|
2647
2658
|
},
|
|
2648
|
-
closeDraft: async (repository, number
|
|
2649
|
-
execute(['pr', 'close', String(number), '--repo', repository
|
|
2659
|
+
closeDraft: async (repository, number) => {
|
|
2660
|
+
execute(['pr', 'close', String(number), '--repo', repository]);
|
|
2650
2661
|
},
|
|
2651
2662
|
getClaimOwner: async (repository, number) => claimCommentOwner(repository, number, execute),
|
|
2652
2663
|
getActiveClaimOwner: async (repository, number) => activeClaimOwnerFromBodies(await claimCommentsFor(repository, number)),
|
|
@@ -3137,7 +3148,13 @@ export interface HarnessCliCommandDeps {
|
|
|
3137
3148
|
mission?: HarnessMissionHandler;
|
|
3138
3149
|
missionLoop?: HarnessMissionLoopHandler;
|
|
3139
3150
|
/** `harness progress` 가 도는 런 관측을 읽는 자리(시험 주입). 기본은 queryRunningRuns. */
|
|
3140
|
-
progress?: {
|
|
3151
|
+
progress?: {
|
|
3152
|
+
query?: typeof queryRunningRuns;
|
|
3153
|
+
nowMs?: () => number;
|
|
3154
|
+
bindingRunIds?: typeof launchBindingRunIds;
|
|
3155
|
+
readBinding?: typeof readLaunchBinding;
|
|
3156
|
+
processStartMs?: typeof psProcessStartMs;
|
|
3157
|
+
};
|
|
3141
3158
|
}
|
|
3142
3159
|
|
|
3143
3160
|
export function installHarnessCliCommand(program: Command, deps: HarnessCliCommandDeps): Command {
|
|
@@ -3198,8 +3215,33 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
|
|
|
3198
3215
|
});
|
|
3199
3216
|
harnessCmd.command('progress').description('도는 골마다 진도를 한 표로 읽는다 — 단계 · 진입 시각 · 마지막 진전 · pty (읽기 전용)')
|
|
3200
3217
|
.option('--json', '같은 행 배열을 JSON 으로 출력')
|
|
3201
|
-
.
|
|
3218
|
+
.option('--pid <number>', '기록된 로컬 PID 에 연결된 런 한 건만 조회')
|
|
3219
|
+
.action((opts: { json?: boolean; pid?: string }) => {
|
|
3202
3220
|
try {
|
|
3221
|
+
if (opts.pid !== undefined) {
|
|
3222
|
+
const pid = Number(opts.pid);
|
|
3223
|
+
if (!Number.isSafeInteger(pid) || pid <= 1) {
|
|
3224
|
+
console.error('❌ --pid 는 2 이상의 정수여야 합니다');
|
|
3225
|
+
process.exitCode = 2;
|
|
3226
|
+
return;
|
|
3227
|
+
}
|
|
3228
|
+
const bindings = (deps.progress?.bindingRunIds ?? launchBindingRunIds)()
|
|
3229
|
+
.map((runId) => ({ runId, binding: (deps.progress?.readBinding ?? readLaunchBinding)(runId) }))
|
|
3230
|
+
.filter(({ binding }) => binding?.pid === pid);
|
|
3231
|
+
const binding = bindings.length === 1 && !bindings[0]!.binding?.podName ? bindings[0] : undefined;
|
|
3232
|
+
const startedAt = binding ? (deps.progress?.processStartMs ?? psProcessStartMs)(pid) : null;
|
|
3233
|
+
// ps lstart has second precision. A wider stop-oriented tolerance would attribute a reused PID to this run.
|
|
3234
|
+
const observed = binding && startedAt !== null
|
|
3235
|
+
&& startedAt === Math.floor(binding.binding!.startedAt / 1_000) * 1_000;
|
|
3236
|
+
const result = observed
|
|
3237
|
+
? (deps.progress?.query ?? queryRunningRuns)({ runIds: [binding.runId], caller: 'harness.progress.pid' })
|
|
3238
|
+
: null;
|
|
3239
|
+
const row = result ? buildRunProgressRows(result, (deps.progress?.nowMs ?? Date.now)())
|
|
3240
|
+
.find((item) => item.runId === binding!.runId && item.status !== 'ended-unclosed' && item.status !== 'unknown') : undefined;
|
|
3241
|
+
if (opts.json) console.log(JSON.stringify({ pid, observation: row ? 'observed' : 'unobserved', runId: row?.runId ?? null, progress: row ?? null }));
|
|
3242
|
+
else console.log(row ? `pid ${pid} · ${renderRunProgressTable({ ...result!, entries: result!.entries.filter((item) => item.runId === row.runId) }, (deps.progress?.nowMs ?? Date.now)()).split('\n').slice(1).join('\n')}` : `pid ${pid} · 미관측`);
|
|
3243
|
+
return;
|
|
3244
|
+
}
|
|
3203
3245
|
const result = (deps.progress?.query ?? queryRunningRuns)({ caller: 'harness.progress' });
|
|
3204
3246
|
const nowMs = (deps.progress?.nowMs ?? Date.now)();
|
|
3205
3247
|
if (opts.json) {
|
|
@@ -81,7 +81,7 @@ export function discoverStopProcesses(runId: string, goalPath: string | null, ta
|
|
|
81
81
|
const match = /^\s*(\d+)\s+((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s+\w+\s+\d+\s+\d\d:\d\d:\d\d\s+\d{4})\s+(.+)$/.exec(line);
|
|
82
82
|
if (!match) return { status: 'failed', reason: 'unparseable process table row' };
|
|
83
83
|
const pid = Number(match[1]);
|
|
84
|
-
const startedAt = Date.parse(match[2]!);
|
|
84
|
+
const startedAt = Date.parse(`${match[2]!} GMT`);
|
|
85
85
|
if (!Number.isSafeInteger(pid) || !Number.isFinite(startedAt)) return { status: 'failed', reason: 'invalid process table row' };
|
|
86
86
|
if (pid <= 1) continue;
|
|
87
87
|
const args = match[3]!.match(/"[^"]*"|'[^']*'|\S+/g)?.map((part) => part.replace(/^(['"])(.*)\1$/, '$2')) ?? [];
|
|
@@ -132,7 +132,9 @@ function stopProcessCwd(pid: number): string | null {
|
|
|
132
132
|
|
|
133
133
|
function scanStopProcessTable(runId: string, goalPath: string | null): StopProcessScan {
|
|
134
134
|
let r: ReturnType<typeof spawnSync>;
|
|
135
|
-
try { r = spawnSync('ps', ['-eo', 'pid=,lstart=,args='], {
|
|
135
|
+
try { r = spawnSync('ps', ['-eo', 'pid=,lstart=,args='], {
|
|
136
|
+
encoding: 'utf8', timeout: 5_000, maxBuffer: 16 * 1024 * 1024, env: { ...process.env, TZ: 'UTC' },
|
|
137
|
+
}); }
|
|
136
138
|
catch (error) { return { status: 'failed', reason: String(error) }; }
|
|
137
139
|
if (r.error || r.status !== 0 || typeof r.stdout !== 'string') return { status: 'failed', reason: String(r.error ?? r.stderr ?? `ps status ${r.status}`) };
|
|
138
140
|
return discoverStopProcesses(runId, goalPath, r.stdout, stopProcessCwd);
|
|
@@ -201,13 +203,15 @@ export function pidRecordFromLaunchBinding(runId: string, registryDir = launchRe
|
|
|
201
203
|
return { pid: binding.pid, startedAt: binding.startedAt, ...(binding.argv0 ? { argv0: binding.argv0 } : {}) };
|
|
202
204
|
}
|
|
203
205
|
|
|
204
|
-
/** macOS·Linux 공통 —
|
|
206
|
+
/** macOS·Linux 공통 — UTC 시작 시각과 상태를 같은 ps 관측에서 읽는다. */
|
|
205
207
|
export function psProcessStartMs(pid: number): number | null {
|
|
206
|
-
const r = spawnSync('ps', ['-o', 'lstart=', '-p', String(pid)], {
|
|
208
|
+
const r = spawnSync('ps', ['-o', 'lstart=,stat=', '-p', String(pid)], {
|
|
209
|
+
encoding: 'utf8', timeout: 5_000, env: { ...process.env, TZ: 'UTC' },
|
|
210
|
+
});
|
|
207
211
|
if (r.status !== 0) return null;
|
|
208
|
-
const
|
|
209
|
-
if (!
|
|
210
|
-
const ms = Date.parse(
|
|
212
|
+
const match = /^\s*((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s+\w+\s+\d+\s+\d\d:\d\d:\d\d\s+\d{4})\s+(\S+)\s*$/.exec(r.stdout);
|
|
213
|
+
if (!match || match[2]!.startsWith('Z')) return null;
|
|
214
|
+
const ms = Date.parse(`${match[1]!} GMT`);
|
|
211
215
|
return Number.isFinite(ms) ? ms : null;
|
|
212
216
|
}
|
|
213
217
|
|
package/src/index.ts
CHANGED
|
@@ -38,6 +38,7 @@ import { DEV_PIPELINE_SINK_SURFACE } from './self-implement/self-cli-sink-surfac
|
|
|
38
38
|
import { registerPrivateLedgerCommands } from './cli/private-ledger-commands.js';
|
|
39
39
|
import { registerKnowCommand } from './cli/know-cli.js';
|
|
40
40
|
import { registerSeatRequestsCommands } from './cli/seat-requests-cli.js';
|
|
41
|
+
import { registerSeatBootCommands } from './cli/seat-cli.js';
|
|
41
42
|
import { applyTestStateDirFlagFromArgv } from './cli/test-state-dir-flag.js';
|
|
42
43
|
import { applyTestFlagFromArgv, observeTestFlagOwnership, uncoveredTestFlagPaths, staleTestFlagPaths } from './cli/test-flag.js';
|
|
43
44
|
import { registerPtyTakeoverCommands } from './cli/pty-takeover-cli.js';
|
|
@@ -1118,6 +1119,7 @@ hqCmd.command('fence').description('이 호스트가 임대를 쥐었을 때만
|
|
|
1118
1119
|
process.exitCode = hqFenceRun(opts.role as 'cron', command);
|
|
1119
1120
|
});
|
|
1120
1121
|
const seatCmd = program.command('seat').description('분배된 자리의 하루 루프와 보고');
|
|
1122
|
+
registerSeatBootCommands(seatCmd);
|
|
1121
1123
|
registerSeatRequestsCommands(seatCmd);
|
|
1122
1124
|
seatCmd.command('asks').description('최근 CTO 되묻기 왕복과 10분 판정')
|
|
1123
1125
|
.option('--since <duration>', '조회 창 (예: 24h)', '24h')
|
|
@@ -3352,6 +3354,7 @@ coordCmd.command('events')
|
|
|
3352
3354
|
});
|
|
3353
3355
|
|
|
3354
3356
|
const selfCmd = program.command('self').description('Self-awareness memory — 외부 도구(Claude Code/Codex)가 구현/변경 이력을 elanous 기억에 주입·회상');
|
|
3357
|
+
{ const { registerSelfUtterancesCommand } = await import('./cli/self-utterances-cli.js'); registerSelfUtterancesCommand(selfCmd); }
|
|
3355
3358
|
|
|
3356
3359
|
// M12 Claude Code 의존도 계기판 — `elanous self dependence [--since] [--until] [--json]`(읽기만).
|
|
3357
3360
|
{ const { registerDependenceCommand } = await import('./cli/dependence-gauge.js'); registerDependenceCommand(selfCmd); }
|
|
@@ -5744,6 +5747,19 @@ selfCmd
|
|
|
5744
5747
|
envModel: () => process.env.ELANOUS_PR_REVIEW_MODEL,
|
|
5745
5748
|
});
|
|
5746
5749
|
if (opts.json) await writeStdoutJson(selfReviewJsonLine(results));
|
|
5750
|
+
// Detached TA review: stdout is now complete in its result file. Resume only the bound card.
|
|
5751
|
+
try {
|
|
5752
|
+
const { rejudgeCompletedTaskReview, TA_REVIEW_COMPLETION_CARD_ENV, TA_REVIEW_COMPLETION_STATE_ENV, TA_REVIEW_COMPLETION_RESULT_ENV } = await import('./task-agent/review-result-capture.js');
|
|
5753
|
+
const completionEnv: Record<string, string | undefined> = {};
|
|
5754
|
+
for (const key of [TA_REVIEW_COMPLETION_CARD_ENV, TA_REVIEW_COMPLETION_STATE_ENV, TA_REVIEW_COMPLETION_RESULT_ENV]) {
|
|
5755
|
+
const value = process.env[key];
|
|
5756
|
+
if (value) completionEnv[key] = value;
|
|
5757
|
+
delete process.env[key];
|
|
5758
|
+
}
|
|
5759
|
+
await rejudgeCompletedTaskReview(completionEnv);
|
|
5760
|
+
} catch (error) {
|
|
5761
|
+
try { debug.log('task-agent', 'review-completion-rejudge-failed', { reason: String(error).slice(-300) }, { level: 'warn' }); } catch { /* fail-soft */ }
|
|
5762
|
+
}
|
|
5747
5763
|
if (failed >= 1) process.exitCode = 1;
|
|
5748
5764
|
});
|
|
5749
5765
|
|
|
@@ -5822,22 +5838,25 @@ selfCmd
|
|
|
5822
5838
|
.description('외부 도구 발화를 elanous 기억에 주입 — origin/git/branch/cwd/시간 태그(회상이 "누가 언제 어디서" 구분). text=--text 또는 stdin')
|
|
5823
5839
|
.option('-s, --text <text>', '발화 원문(생략 시 stdin)')
|
|
5824
5840
|
.option('--source <origin>', 'claude-code|codex|gemini|…', 'claude-code')
|
|
5841
|
+
.option('--origin <origin>', '발화 주체: human|agent (기본 agent)')
|
|
5825
5842
|
.option('--session <id>', '세션 id')
|
|
5826
5843
|
.option('--git-hash <hash>', '발화 시점 git HEAD(short)')
|
|
5827
5844
|
.option('--branch <name>', 'git 브랜치')
|
|
5828
5845
|
.option('--cwd <dir>', '작업 디렉토리')
|
|
5829
5846
|
.option('--ts <iso>', 'ISO 시간(기본 now)')
|
|
5830
5847
|
.option('-i, --importance <n>', '0-10 현저성', '5')
|
|
5831
|
-
.action(async (opts: { text?: string; source: string; session?: string; gitHash?: string; branch?: string; cwd?: string; ts?: string; importance: string }) => {
|
|
5848
|
+
.action(async (opts: { text?: string; source: string; origin?: string; session?: string; gitHash?: string; branch?: string; cwd?: string; ts?: string; importance: string }) => {
|
|
5849
|
+
if (opts.origin !== undefined && opts.origin !== 'human' && opts.origin !== 'agent') { ui.error('utterance: --origin must be human or agent'); process.exitCode = 1; return; }
|
|
5832
5850
|
let text = opts.text;
|
|
5833
5851
|
if (!text) text = await readPipedStdin(); // ★ OBS-T3
|
|
5834
5852
|
if (!text || !text.trim()) { ui.error('utterance: --text 또는 stdin 필요'); process.exit(1); }
|
|
5835
5853
|
// 독립 CLI 프로세스 — 데몬 StoreSink 를 상속 안 하므로 logs.db 싱크를 붙여야
|
|
5836
5854
|
// injectUtterance 의 `debug.log('memory.utterance','ingress')` 가 logs.db 에 닿음(제1원칙 관측).
|
|
5837
|
-
const {
|
|
5838
|
-
const r =
|
|
5855
|
+
const { recordSelfUtterance } = await import('./cli/self-utterances-cli.js');
|
|
5856
|
+
const r = recordSelfUtterance({
|
|
5839
5857
|
text: text.trim(),
|
|
5840
5858
|
origin: opts.source,
|
|
5859
|
+
utteranceOrigin: opts.origin === 'human' ? 'human' : 'agent',
|
|
5841
5860
|
...(opts.session ? { sessionId: opts.session } : {}),
|
|
5842
5861
|
...(opts.gitHash ? { gitHash: opts.gitHash } : {}),
|
|
5843
5862
|
...(opts.branch ? { branch: opts.branch } : {}),
|
|
@@ -11722,7 +11741,10 @@ async function main(): Promise<void> {
|
|
|
11722
11741
|
// 한다. 인터랙티브 TUI 는 데몬 sink 를 상속 안 하므로 안 붙이면 debug.log 가 no-op
|
|
11723
11742
|
// (제1원칙 관측 갭 · tui-sim 드라이브에서 agent.source 0건으로 실측 2026-07-19).
|
|
11724
11743
|
// PR#4622(self utterance CLI)·session-cli 미러. fail-open·비블로킹.
|
|
11744
|
+
const bootTimer = (await import('./dashboard/boot-timing.js')).createBootTimer();
|
|
11745
|
+
bootTimer.begin();
|
|
11725
11746
|
try { await (await import('./domains/standalone-log-sink.js')).registerStandaloneLogSink('tui'); } catch { /* fail-open */ }
|
|
11747
|
+
bootTimer.phase('log-sink');
|
|
11726
11748
|
// P3(대표 2026-07-26) — footgun 관측을 logs.db 에 도달시킨다. 초반(arg 파싱 前) warnProdSpawnFootgun 은
|
|
11727
11749
|
// StoreSink 등록 前이라 debug.log 가 logs.db 미도달이었다(순서 갭). sink 등록 직후 관측만 재발행(stderr 중복 X).
|
|
11728
11750
|
try { (await import('./instance-root-coherence.js')).warnProdSpawnFootgun({ emit: 'log' }); } catch { /* fail-open */ }
|
|
@@ -11732,7 +11754,9 @@ async function main(): Promise<void> {
|
|
|
11732
11754
|
// so `elanous self screen` / `/v1/terminals` / (P2) PWA can see the
|
|
11733
11755
|
// dashboard the user is actually looking at. Off unless this boots
|
|
11734
11756
|
// (no producer in headless/daemon paths). fail-soft·비블로킹.
|
|
11757
|
+
bootTimer.begin();
|
|
11735
11758
|
try { (await import('./capture/tui-self-report.js')).startTuiSelfReport(); } catch { /* fail-soft */ }
|
|
11759
|
+
bootTimer.phase('self-report');
|
|
11736
11760
|
|
|
11737
11761
|
// Dashboard handles sync inline — only returns on quit
|
|
11738
11762
|
// ⛔ stdin 이 TTY 가 아니면 «뜨지 않는다» — 뜨면 readKey 가 엔터를 지어내 초당 수백 프레임을
|
|
@@ -11745,7 +11769,14 @@ async function main(): Promise<void> {
|
|
|
11745
11769
|
return;
|
|
11746
11770
|
}
|
|
11747
11771
|
}
|
|
11748
|
-
|
|
11772
|
+
bootTimer.begin();
|
|
11773
|
+
await showDashboard({
|
|
11774
|
+
...dashboardOpts,
|
|
11775
|
+
onFirstFrameRendered: () => {
|
|
11776
|
+
bootTimer.phase('show-dashboard');
|
|
11777
|
+
bootTimer.done();
|
|
11778
|
+
},
|
|
11779
|
+
});
|
|
11749
11780
|
closeTui();
|
|
11750
11781
|
// Force exit: the dashboard starts background timers (clipboard
|
|
11751
11782
|
// poller, shimmer/blink animations) that keep the Bun event loop
|
package/src/instance/current.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
2
1
|
import { debug } from '../debug/log.js';
|
|
3
2
|
import { getElanousConfigDirOverride } from '../elanous-config-dir.js';
|
|
4
3
|
import { getTestStateRoot } from '../nexus/paths.js';
|
|
5
|
-
import {
|
|
6
|
-
import { isInstalledCopyScript
|
|
4
|
+
import { getAppliedGlobalTestRoot } from '../cli/test-flag.js';
|
|
5
|
+
import { isInstalledCopyScript } from './leader.js';
|
|
7
6
|
import { prodInstanceRoot, resolveInstance, type InstanceResolution } from './resolve.js';
|
|
8
7
|
|
|
9
8
|
interface ResolveCurrentInstanceDeps {
|
|
@@ -16,15 +15,12 @@ interface ResolveCurrentInstanceDeps {
|
|
|
16
15
|
|
|
17
16
|
/** 현재 프로세스의 4층 판정 입력을 모아 순수 리졸버에 전달한다. */
|
|
18
17
|
export function resolveCurrentInstance(deps: ResolveCurrentInstanceDeps = {}): InstanceResolution {
|
|
19
|
-
const cwd = (deps.cwd ?? (() => process.cwd()))();
|
|
20
18
|
const explicitFlagRoot = (deps.explicitFlagRoot ?? (() =>
|
|
21
19
|
getAppliedGlobalTestRoot() ?? getTestStateRoot() ?? getElanousConfigDirOverride()))();
|
|
22
|
-
const treeRoot = (process.argv[1] ? treeFromScriptPath(process.argv[1]) : null) ?? findTreeRoot(cwd);
|
|
23
20
|
const resolution = resolveInstance({
|
|
24
21
|
explicitFlagRoot,
|
|
25
22
|
stampedStateDir: (deps.stampedStateDir ?? (() => process.env.ELANOUS_STATE_DIR))(),
|
|
26
23
|
installedCopy: isInstalledCopyScript(),
|
|
27
|
-
treeTestRoot: treeRoot ? join(treeRoot, '.elanous-test') : null,
|
|
28
24
|
prodRoot: prodInstanceRoot(),
|
|
29
25
|
});
|
|
30
26
|
debug.log('instance.current', 'resolved', { layer: resolution.layer, kind: resolution.kind });
|
package/src/instance/resolve.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// 인스턴스 우주: 명시 루트 → 부모 스탬프 →
|
|
1
|
+
// 인스턴스 우주: 명시 루트 → 부모 스탬프 → 운영 집. 트리 위치는 우주를 고르지 않는다.
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { join, resolve } from 'node:path';
|
|
4
4
|
|
|
5
5
|
export type InstanceKind = 'prod' | 'test';
|
|
6
|
-
export type ResolutionLayer = 'explicit-flag' | 'parent-stamp' | 'installed' | '
|
|
6
|
+
export type ResolutionLayer = 'explicit-flag' | 'parent-stamp' | 'installed' | 'default';
|
|
7
7
|
|
|
8
8
|
export interface InstanceResolution {
|
|
9
9
|
kind: InstanceKind;
|
|
@@ -50,10 +50,9 @@ export function resolveInstance(deps: ResolveDeps = {}): InstanceResolution {
|
|
|
50
50
|
}
|
|
51
51
|
const installed = deps.installedCopy ?? isInstalledExecution();
|
|
52
52
|
if (installed) return { kind: 'prod', root: prodRoot, layer: 'installed', why: '설치본으로 실행' };
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return { kind: 'test', root: normRoot(join(process.cwd(), '.elanous-test')), layer: 'default', why: '소스 트리 밖 실행(격리)' };
|
|
53
|
+
// Explicit --test selects a test root before this resolver runs. A checkout,
|
|
54
|
+
// worktree, or arbitrary cwd alone never changes the operating house.
|
|
55
|
+
return { kind: 'prod', root: prodRoot, layer: 'default', why: '명시 시험 우주가 없는 운영 집' };
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
/** 부모가 도장으로 물려준 우주와 자식이 실제로 쓰는 우주가 다른가. 비어 있는 쪽은 비교하지 않는다. */
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ⛔⭐ OpenAI Chat Completions 의 «출력 상한» 칸 이름은 모델 세대로 갈린다.
|
|
3
|
+
*
|
|
4
|
+
* `gpt-5` 이후 계열은 `max_tokens` 를 ***거부한다*** — 400 `unsupported_parameter` 를 내고
|
|
5
|
+
* *"Use 'max_completion_tokens' instead"* 라고 말한다. `gpt-4o` 계열은 반대로 옛 이름만 받는다.
|
|
6
|
+
*
|
|
7
|
+
* 📏 2026-08-22 실측: `provider auto` 의 티어 사다리가 `gpt-5.6-*` 로 바뀐 뒤(`#11161`)
|
|
8
|
+
* 3시간 만에 이 400 이 **93건** 났고 «가속»했다(20h:2 → 21h:16 → 22h:30 → 23h:45).
|
|
9
|
+
* 7일 창에서 그 이전 발생은 ***0***이다. ⇒ 칸 이름을 모델로 갈라야 한다.
|
|
10
|
+
*
|
|
11
|
+
* ⚠️ 값을 «안 보내는» 선택은 하지 않는다 — 상한이 사라지면 비용이 조용히 는다.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* ⛔⭐⭐ **브랜드마다 «칸 이름」이 다르다**(2026-09-12). `makeOpenAICompatProvider` 는 `grok` 과
|
|
15
|
+
* `openai` 를 «한 경로»로 태우는데, 종전엔 둘 다 옛 칸(`max_tokens`·`temperature`)을 «생으로» 박았다.
|
|
16
|
+
* ⇒ `openai` 브랜드의 새 세대(`gpt-5`+)는 그 둘을 «거부»한다 — 400 `unsupported_parameter`.
|
|
17
|
+
*
|
|
18
|
+
* 📏 실측 2026-09-12: `gpt-6-astra · maxTokens=2048 · api.openai.com/v1/chat/completions` 가 400 을 받았고,
|
|
19
|
+
* 그 400 이 `review.done` 미실행 57건 중 ***30건***의 사유였다(무인 리뷰가 그만큼 «안 돌았다»).
|
|
20
|
+
*
|
|
21
|
+
* ⭐ 자는 «이미» 있었다 — `openAiOutputTokenField`·`openAiTemperatureField`. ***이 호출부가 안 물었을 뿐이다.***
|
|
22
|
+
* 🪞 같은 파일의 `stripLocalLlmSpec` 이 「두 builder 중 하나만 정규화했다」로 난 사고의 처방이었다.
|
|
23
|
+
* ***같은 모양이 다시 났다*** — 그래서 이번에도 «순수 함수로 뽑아» 양쪽이 같은 자를 쓰게 한다.
|
|
24
|
+
*
|
|
25
|
+
* ⛔ grok 은 옛 칸을 그대로 받는다 — 바꾸지 않는다.
|
|
26
|
+
*/
|
|
27
|
+
export function openAiCompatSamplingFields(
|
|
28
|
+
brand: string,
|
|
29
|
+
model: string,
|
|
30
|
+
temperature: number,
|
|
31
|
+
maxTokens: number,
|
|
32
|
+
): Record<string, number> {
|
|
33
|
+
return brand === 'openai'
|
|
34
|
+
? { ...openAiTemperatureField(model, temperature), ...openAiOutputTokenField(model, maxTokens) }
|
|
35
|
+
: { temperature, max_tokens: maxTokens };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function openAiOutputTokenField(model: string, maxTokens: number): Record<string, number> {
|
|
39
|
+
return isNewGenerationOpenAiModel(model)
|
|
40
|
+
? { max_completion_tokens: maxTokens }
|
|
41
|
+
: { max_tokens: maxTokens };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** `gpt-5` 이후 계열 판정 — 옛 Chat Completions 파라미터를 거부하는 세대.
|
|
45
|
+
*
|
|
46
|
+
* 🩸⭐⭐ **2026-09-12 — 이 자가 «늙어서» 400 이 돌아왔다.** 종전 문면은 `/^(?:gpt-5|o[1-9])/` 라
|
|
47
|
+
* ***`gpt-6` 계열을 «못 봤다»***. 그래서 `gpt-6-astra` 가 `max_tokens` 로 나갔고 400 을 받았다:
|
|
48
|
+
* `Unsupported parameter: 'max_tokens' is not supported with this model. Use 'max_completion_tokens'`
|
|
49
|
+
* 📏 실측(2026-09-12 · 12시간 창): `review.done` 200행 중 `reviewed=false` 57, 그중 ***30***이 이 400 이다.
|
|
50
|
+
* ⊕ 요청 로그가 그것을 한 줄로 말한다 — `gpt-6-astra · maxTokens=2048 · api.openai.com/v1/chat/completions`.
|
|
51
|
+
*
|
|
52
|
+
* ⇒ ⭐ **세대를 「이름 목록」으로 세지 않는다.** 번호를 «범위»로 읽어 다음 세대가 와도 안 늙게 한다.
|
|
53
|
+
* `gpt-5` 이상(두 자리 포함) ⊕ `o1`~`o9`. ⛔ `gpt-4` 이하와 로컬 모델은 종전대로 옛 칸을 쓴다.
|
|
54
|
+
*
|
|
55
|
+
* 🌐 **외부 그라운딩**(2026-09-12 · omni-crawl `fc-dev`) — 같은 사고가 «업스트림에도» 있었다:
|
|
56
|
+
* · `quantumnous/new-api#7211` — *"GPT-6 Astra 未命中原来的 GPT-5 前缀判断"* (똑같은 접두 판정 누락)
|
|
57
|
+
* · `genkit-ai/genkit#6320` — *"max_tokens is sent verbatim and rejected by reasoning models"*
|
|
58
|
+
* · Azure AI docs — `gpt-6-astra`(2026-09-03) Chat Completions ⊕ Reasoning
|
|
59
|
+
* · overchat.ai — *"Astra does not support `temperature`, `top_p`, `top_logprobs`"*
|
|
60
|
+
* ⇒ ✅ 우리가 `temperature` 를 «생략»하는 것이 외부 사실과 «맞는다».
|
|
61
|
+
* ⚠️ `top_p`·`logprobs` 는 이 경로가 «안 보낸다»(local 프리셋 경로만 보낸다) — 그래서 조치 없음.
|
|
62
|
+
*
|
|
63
|
+
* ⚖️ ⛔ **알려진 트레이드오프 — new-api 는 «반대» 결론을 냈다.** 그들은
|
|
64
|
+
* *"모든 미래 GPT 메이저를 GPT-5 로 묶으면 서로 다른 모델의 파라미터 규칙이 엮인다"* 며
|
|
65
|
+
* ***미지의 미래 메이저는 추론하지 않는다***로 갔다(명시 목록 + 날짜 스냅샷).
|
|
66
|
+
* ⇒ 우리는 «반대»를 택했다. 이유: ***목록이 늙어서 이 사고가 났기 때문***이다
|
|
67
|
+
* (이 저장소에서 실제로 리뷰 미실행 30건을 냈다). 새 규칙은 «새 관례 쪽으로» 실패한다.
|
|
68
|
+
* 📌 그러므로 ***`gpt-N` 중 옛 칸을 «되찾는» 모델이 나오면 이 자가 틀린다.*** 그때는
|
|
69
|
+
* 여기서 갈지 말고 «모델별 능력 선언»으로 옮긴다(new-api 가 간 길). 지금은 그 표본이 0이다. */
|
|
70
|
+
export function isNewGenerationOpenAiModel(model: string): boolean {
|
|
71
|
+
const match = /^gpt-(\d+)/i.exec(model);
|
|
72
|
+
if (match) return Number.parseInt(match[1]!, 10) >= 5;
|
|
73
|
+
return /^o[1-9]/i.test(model);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* ⛔⭐ 같은 세대가 `temperature` 도 거부한다 — 400 `unsupported_value`,
|
|
78
|
+
* *"Only the default (1) value is supported"*.
|
|
79
|
+
*
|
|
80
|
+
* 📏 2026-08-22 실측: `max_tokens` 를 고친 «2분 뒤»(00:13:03Z)부터 이 400 이 났고
|
|
81
|
+
* 90분에 **41건**이었다. ⇒ ***한 파라미터만 고치면 다음 파라미터에서 막힌다.***
|
|
82
|
+
* ⚠️ 그때 나는 「수리 뒤 400 이 0건」이라 보고했는데, 그 자가 `grep max_completion_tokens`
|
|
83
|
+
* 라서 «새 400»을 못 봤다 — ***좁은 자로 「나았다」를 읽었다.***
|
|
84
|
+
*
|
|
85
|
+
* ⇒ 값을 «1로 보내지» 않고 ***칸을 생략***한다. 서버 기본이 1이고, 보내면 또 다른 거부를 부른다.
|
|
86
|
+
*/
|
|
87
|
+
export function openAiTemperatureField(model: string, temperature: number): Record<string, number> {
|
|
88
|
+
return isNewGenerationOpenAiModel(model) ? {} : { temperature };
|
|
89
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { LLMProviderName } from '../user-config.js';
|
|
2
|
+
|
|
3
|
+
/** Lightweight model-id family — prefix/substring only. No catalog, no
|
|
4
|
+
* `src/llm/model-defaults.ts` (that module imports this file). */
|
|
5
|
+
export type RuntimeLlmModelFamily =
|
|
6
|
+
| 'grok' | 'anthropic' | 'openai' | 'openai-codex' | 'gemini' | 'local'
|
|
7
|
+
| 'kimi' | 'qwen' | 'glm' | 'openrouter' | 'bedrock' | 'unknown';
|
|
8
|
+
|
|
9
|
+
export function inferRuntimeLlmModelFamily(model: string | undefined): RuntimeLlmModelFamily {
|
|
10
|
+
if (!model) return 'unknown';
|
|
11
|
+
const m = model.trim().toLowerCase();
|
|
12
|
+
if (!m) return 'unknown';
|
|
13
|
+
// ⛔ 2026-09-23 — 게이트웨이 접두가 «먼저»다. 안 그러면 `openrouter/anthropic/claude-…` 가 `anthropic` 으로
|
|
14
|
+
// 읽혀 openrouter 와 «비호환»이 되고, `openrouter/z-ai/…` 는 `unknown` 이라 아무 provider 와도 «호환»이 됐다.
|
|
15
|
+
if (m.startsWith('openrouter/')) return 'openrouter';
|
|
16
|
+
// ⭐ BEDROCK-PROVIDER — Bedrock 전용 꼴(`anthropic.claude-…`·지역 접두·`bedrock/…`)이 `includes('claude')` 보다 «먼저».
|
|
17
|
+
// 1st-party id(`claude-…`)는 그대로 anthropic 이다(불변).
|
|
18
|
+
if (m.startsWith('bedrock/') || /^(?:[a-z]{2,6}\.)?anthropic\.claude-/.test(m)) return 'bedrock';
|
|
19
|
+
if (m.includes('grok')) return 'grok';
|
|
20
|
+
if (m.includes('claude')) return 'anthropic';
|
|
21
|
+
if (m.startsWith('gemini-') || m.includes('gemini')) return 'gemini';
|
|
22
|
+
if (m.startsWith('local:')) return 'local';
|
|
23
|
+
if (m.startsWith('kimi')) return 'kimi';
|
|
24
|
+
if (m.startsWith('qwen')) return 'qwen';
|
|
25
|
+
if (m.startsWith('glm')) return 'glm';
|
|
26
|
+
// openai-codex 구독 모델만 여기 — gpt-5.5 등 Chat Completions 모델은 openai.
|
|
27
|
+
// ⛔ openai 와 openai-codex 를 한 계열로 접지 마라. 서로 다른 엔드포인트다.
|
|
28
|
+
// ✅ 2026-09-23 (대표 ⒜ 결정) — ***`gpt-6` 를 여기 «더했다».***
|
|
29
|
+
// 종전엔 `gpt-6-*` 가 아래 `startsWith('gpt-')` 로 떨어져 `openai` 가 됐고, 그래서
|
|
30
|
+
// ***사다리가 «자기 가드가 거부하는 모델»을 가리켰다*** (`CODEX.loaded = gpt-6-astra`).
|
|
31
|
+
// 📏 구독 경로 실호출로 확인(codex-cli 0.155.1): gpt-6-sol · luna · astra 전부 응답.
|
|
32
|
+
// ⚠️ 이 셋은 API 로도 불린다(`/v1/models` 에 있다). 「계열」이 재는 것은 «가용성»이 아니라
|
|
33
|
+
// ***「우리가 어느 문으로 보내나」***다 — 5.6 계열도 API 에 있지만 여기로 온다.
|
|
34
|
+
if (m.includes('codex') || m.startsWith('gpt-5.6') || m.startsWith('gpt-6')) return 'openai-codex';
|
|
35
|
+
// Exact o1/o3/o4 ids are standard OpenAI models; prefix-only (`o1-`) misses them.
|
|
36
|
+
if (
|
|
37
|
+
m.startsWith('gpt-')
|
|
38
|
+
|| m === 'o1' || m.startsWith('o1-')
|
|
39
|
+
|| m === 'o3' || m.startsWith('o3-')
|
|
40
|
+
|| m === 'o4' || m.startsWith('o4-')
|
|
41
|
+
) return 'openai';
|
|
42
|
+
return 'unknown';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Cycle-safe compatibility: same-provider model overrides pass;
|
|
46
|
+
* cross-family ids fail. Unknown prefixes and `auto` stay silent.
|
|
47
|
+
* ⛔ `openai` 와 `openai-codex` 는 별개 provider — 양방향 호환으로 접지 않는다. */
|
|
48
|
+
export function isRuntimeLlmModelCompatibleWithProvider(
|
|
49
|
+
provider: LLMProviderName,
|
|
50
|
+
model: string | undefined,
|
|
51
|
+
): boolean {
|
|
52
|
+
if (!model) return true;
|
|
53
|
+
if (provider === 'auto') return true;
|
|
54
|
+
const family = inferRuntimeLlmModelFamily(model);
|
|
55
|
+
if (family === 'unknown') return true;
|
|
56
|
+
if (provider === 'local') return family === 'local';
|
|
57
|
+
// bedrock 은 1st-party claude id 도 받는다(공급자가 `anthropic.` 접두로 바꿔 보낸다).
|
|
58
|
+
if (provider === 'bedrock') return family === 'bedrock' || family === 'anthropic';
|
|
59
|
+
return provider === family;
|
|
60
|
+
}
|
|
61
|
+
|
|
@@ -122,6 +122,26 @@ export interface ZaiErrorClass {
|
|
|
122
122
|
resetRaw: string | null;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
/** Provider-neutral signal shape for supervisors coordinating plan-limit recovery. */
|
|
126
|
+
export interface ProviderPlanLimitSignal {
|
|
127
|
+
provider: string;
|
|
128
|
+
kind: 'plan-limit';
|
|
129
|
+
resetAt: number | null;
|
|
130
|
+
overage: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Extract the structured recovery signal from a Z.ai 1308 response; unrelated errors return null. */
|
|
134
|
+
export function extractZaiPlanLimitSignal(
|
|
135
|
+
status: number,
|
|
136
|
+
bodyText: string,
|
|
137
|
+
overage: string,
|
|
138
|
+
): ProviderPlanLimitSignal | null {
|
|
139
|
+
const classified = classifyZaiError(status, bodyText);
|
|
140
|
+
if (!classified || classified.code !== 1308 || classified.kind !== 'plan-limit') return null;
|
|
141
|
+
return { provider: 'glm', kind: 'plan-limit', resetAt: classified.resetAt, overage };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
125
145
|
const PLAN_LIMIT = new Set([1308, 1310]);
|
|
126
146
|
const BALANCE_LIMIT = new Set([1113, 1316, 1317, 1318, 1319, 1320, 1321]);
|
|
127
147
|
const PLAN_UNAVAILABLE = new Set([1309, 1311, 1313, 1314, 1315]);
|
|
@@ -433,13 +453,15 @@ export class ZaiCodingPlanLimitError extends Error {
|
|
|
433
453
|
readonly code: number;
|
|
434
454
|
readonly limitKind: ZaiLimitKind;
|
|
435
455
|
readonly resetAt: number | null;
|
|
436
|
-
|
|
456
|
+
readonly planLimitSignal?: ProviderPlanLimitSignal;
|
|
457
|
+
constructor(opts: { code: number; limitKind: ZaiLimitKind; resetAt: number | null; policy: ZaiOveragePolicy; reason: string; planLimitSignal?: ProviderPlanLimitSignal }) {
|
|
437
458
|
const when = opts.resetAt ? ` · 리셋 ${new Date(opts.resetAt).toISOString()}` : '';
|
|
438
459
|
super(`GLM Coding Plan 한도(code ${opts.code} · ${opts.limitKind}${when}) — 정책 llm.zaiCodingPlan.overage=${opts.policy}: ${opts.reason}`);
|
|
439
460
|
this.name = 'ZaiCodingPlanLimitError';
|
|
440
461
|
this.code = opts.code;
|
|
441
462
|
this.limitKind = opts.limitKind;
|
|
442
463
|
this.resetAt = opts.resetAt;
|
|
464
|
+
if (opts.planLimitSignal) this.planLimitSignal = opts.planLimitSignal;
|
|
443
465
|
}
|
|
444
466
|
}
|
|
445
467
|
|
|
@@ -575,6 +597,9 @@ export function makeZaiCodingPlanProvider(cfg: UCLLMConfig, deps: ZaiProviderDep
|
|
|
575
597
|
const status = (error as { status?: unknown })?.status;
|
|
576
598
|
const bodyText = (error as { bodyText?: unknown })?.bodyText;
|
|
577
599
|
const cls = typeof status === 'number' && typeof bodyText === 'string' ? classifyZaiError(status, bodyText) : null;
|
|
600
|
+
const planLimitSignal = typeof status === 'number' && typeof bodyText === 'string'
|
|
601
|
+
? extractZaiPlanLimitSignal(status, bodyText, settings.overage)
|
|
602
|
+
: null;
|
|
578
603
|
if (!cls || cls.kind === 'rate' || cls.kind === 'other') {
|
|
579
604
|
if (cls) debug.log('zai.coding-plan', 'error', { code: cls.code, kind: cls.kind, model: wireModel }, { level: 'warn' });
|
|
580
605
|
throw error;
|
|
@@ -588,6 +613,7 @@ export function makeZaiCodingPlanProvider(cfg: UCLLMConfig, deps: ZaiProviderDep
|
|
|
588
613
|
if (action === 'stop' || emitted) {
|
|
589
614
|
throw new ZaiCodingPlanLimitError({
|
|
590
615
|
code: cls.code, limitKind: cls.kind, resetAt: cls.resetAt, policy: settings.overage,
|
|
616
|
+
...(planLimitSignal ? { planLimitSignal } : {}),
|
|
591
617
|
reason: emitted ? '출력이 이미 나간 뒤라 폴백하지 않는다' : '리셋까지 멈춘다',
|
|
592
618
|
});
|
|
593
619
|
}
|
package/src/llm.ts
CHANGED
|
@@ -11699,92 +11699,16 @@ export function getProviderForConfig(
|
|
|
11699
11699
|
import { getModelFamily } from './models/prompts.js';
|
|
11700
11700
|
import type { ModelFamily } from './models/prompts.js';
|
|
11701
11701
|
|
|
11702
|
-
|
|
11703
|
-
|
|
11704
|
-
|
|
11705
|
-
|
|
11706
|
-
|
|
11707
|
-
|
|
11708
|
-
|
|
11709
|
-
|
|
11710
|
-
|
|
11711
|
-
|
|
11712
|
-
|
|
11713
|
-
|
|
11714
|
-
|
|
11715
|
-
* ⛔⭐⭐ **브랜드마다 «칸 이름」이 다르다**(2026-09-12). `makeOpenAICompatProvider` 는 `grok` 과
|
|
11716
|
-
* `openai` 를 «한 경로»로 태우는데, 종전엔 둘 다 옛 칸(`max_tokens`·`temperature`)을 «생으로» 박았다.
|
|
11717
|
-
* ⇒ `openai` 브랜드의 새 세대(`gpt-5`+)는 그 둘을 «거부»한다 — 400 `unsupported_parameter`.
|
|
11718
|
-
*
|
|
11719
|
-
* 📏 실측 2026-09-12: `gpt-6-astra · maxTokens=2048 · api.openai.com/v1/chat/completions` 가 400 을 받았고,
|
|
11720
|
-
* 그 400 이 `review.done` 미실행 57건 중 ***30건***의 사유였다(무인 리뷰가 그만큼 «안 돌았다»).
|
|
11721
|
-
*
|
|
11722
|
-
* ⭐ 자는 «이미» 있었다 — `openAiOutputTokenField`·`openAiTemperatureField`. ***이 호출부가 안 물었을 뿐이다.***
|
|
11723
|
-
* 🪞 같은 파일의 `stripLocalLlmSpec` 이 「두 builder 중 하나만 정규화했다」로 난 사고의 처방이었다.
|
|
11724
|
-
* ***같은 모양이 다시 났다*** — 그래서 이번에도 «순수 함수로 뽑아» 양쪽이 같은 자를 쓰게 한다.
|
|
11725
|
-
*
|
|
11726
|
-
* ⛔ grok 은 옛 칸을 그대로 받는다 — 바꾸지 않는다.
|
|
11727
|
-
*/
|
|
11728
|
-
export function openAiCompatSamplingFields(
|
|
11729
|
-
brand: string,
|
|
11730
|
-
model: string,
|
|
11731
|
-
temperature: number,
|
|
11732
|
-
maxTokens: number,
|
|
11733
|
-
): Record<string, number> {
|
|
11734
|
-
return brand === 'openai'
|
|
11735
|
-
? { ...openAiTemperatureField(model, temperature), ...openAiOutputTokenField(model, maxTokens) }
|
|
11736
|
-
: { temperature, max_tokens: maxTokens };
|
|
11737
|
-
}
|
|
11738
|
-
|
|
11739
|
-
export function openAiOutputTokenField(model: string, maxTokens: number): Record<string, number> {
|
|
11740
|
-
return isNewGenerationOpenAiModel(model)
|
|
11741
|
-
? { max_completion_tokens: maxTokens }
|
|
11742
|
-
: { max_tokens: maxTokens };
|
|
11743
|
-
}
|
|
11744
|
-
|
|
11745
|
-
/** `gpt-5` 이후 계열 판정 — 옛 Chat Completions 파라미터를 거부하는 세대.
|
|
11746
|
-
*
|
|
11747
|
-
* 🩸⭐⭐ **2026-09-12 — 이 자가 «늙어서» 400 이 돌아왔다.** 종전 문면은 `/^(?:gpt-5|o[1-9])/` 라
|
|
11748
|
-
* ***`gpt-6` 계열을 «못 봤다»***. 그래서 `gpt-6-astra` 가 `max_tokens` 로 나갔고 400 을 받았다:
|
|
11749
|
-
* `Unsupported parameter: 'max_tokens' is not supported with this model. Use 'max_completion_tokens'`
|
|
11750
|
-
* 📏 실측(2026-09-12 · 12시간 창): `review.done` 200행 중 `reviewed=false` 57, 그중 ***30***이 이 400 이다.
|
|
11751
|
-
* ⊕ 요청 로그가 그것을 한 줄로 말한다 — `gpt-6-astra · maxTokens=2048 · api.openai.com/v1/chat/completions`.
|
|
11752
|
-
*
|
|
11753
|
-
* ⇒ ⭐ **세대를 「이름 목록」으로 세지 않는다.** 번호를 «범위»로 읽어 다음 세대가 와도 안 늙게 한다.
|
|
11754
|
-
* `gpt-5` 이상(두 자리 포함) ⊕ `o1`~`o9`. ⛔ `gpt-4` 이하와 로컬 모델은 종전대로 옛 칸을 쓴다.
|
|
11755
|
-
*
|
|
11756
|
-
* 🌐 **외부 그라운딩**(2026-09-12 · omni-crawl `fc-dev`) — 같은 사고가 «업스트림에도» 있었다:
|
|
11757
|
-
* · `quantumnous/new-api#7211` — *"GPT-6 Astra 未命中原来的 GPT-5 前缀判断"* (똑같은 접두 판정 누락)
|
|
11758
|
-
* · `genkit-ai/genkit#6320` — *"max_tokens is sent verbatim and rejected by reasoning models"*
|
|
11759
|
-
* · Azure AI docs — `gpt-6-astra`(2026-09-03) Chat Completions ⊕ Reasoning
|
|
11760
|
-
* · overchat.ai — *"Astra does not support `temperature`, `top_p`, `top_logprobs`"*
|
|
11761
|
-
* ⇒ ✅ 우리가 `temperature` 를 «생략»하는 것이 외부 사실과 «맞는다».
|
|
11762
|
-
* ⚠️ `top_p`·`logprobs` 는 이 경로가 «안 보낸다»(local 프리셋 경로만 보낸다) — 그래서 조치 없음.
|
|
11763
|
-
*
|
|
11764
|
-
* ⚖️ ⛔ **알려진 트레이드오프 — new-api 는 «반대» 결론을 냈다.** 그들은
|
|
11765
|
-
* *"모든 미래 GPT 메이저를 GPT-5 로 묶으면 서로 다른 모델의 파라미터 규칙이 엮인다"* 며
|
|
11766
|
-
* ***미지의 미래 메이저는 추론하지 않는다***로 갔다(명시 목록 + 날짜 스냅샷).
|
|
11767
|
-
* ⇒ 우리는 «반대»를 택했다. 이유: ***목록이 늙어서 이 사고가 났기 때문***이다
|
|
11768
|
-
* (이 저장소에서 실제로 리뷰 미실행 30건을 냈다). 새 규칙은 «새 관례 쪽으로» 실패한다.
|
|
11769
|
-
* 📌 그러므로 ***`gpt-N` 중 옛 칸을 «되찾는» 모델이 나오면 이 자가 틀린다.*** 그때는
|
|
11770
|
-
* 여기서 갈지 말고 «모델별 능력 선언»으로 옮긴다(new-api 가 간 길). 지금은 그 표본이 0이다. */
|
|
11771
|
-
export function isNewGenerationOpenAiModel(model: string): boolean {
|
|
11772
|
-
const match = /^gpt-(\d+)/i.exec(model);
|
|
11773
|
-
if (match) return Number.parseInt(match[1]!, 10) >= 5;
|
|
11774
|
-
return /^o[1-9]/i.test(model);
|
|
11775
|
-
}
|
|
11776
|
-
|
|
11777
|
-
/**
|
|
11778
|
-
* ⛔⭐ 같은 세대가 `temperature` 도 거부한다 — 400 `unsupported_value`,
|
|
11779
|
-
* *"Only the default (1) value is supported"*.
|
|
11780
|
-
*
|
|
11781
|
-
* 📏 2026-08-22 실측: `max_tokens` 를 고친 «2분 뒤»(00:13:03Z)부터 이 400 이 났고
|
|
11782
|
-
* 90분에 **41건**이었다. ⇒ ***한 파라미터만 고치면 다음 파라미터에서 막힌다.***
|
|
11783
|
-
* ⚠️ 그때 나는 「수리 뒤 400 이 0건」이라 보고했는데, 그 자가 `grep max_completion_tokens`
|
|
11784
|
-
* 라서 «새 400»을 못 봤다 — ***좁은 자로 「나았다」를 읽었다.***
|
|
11785
|
-
*
|
|
11786
|
-
* ⇒ 값을 «1로 보내지» 않고 ***칸을 생략***한다. 서버 기본이 1이고, 보내면 또 다른 거부를 부른다.
|
|
11787
|
-
*/
|
|
11788
|
-
export function openAiTemperatureField(model: string, temperature: number): Record<string, number> {
|
|
11789
|
-
return isNewGenerationOpenAiModel(model) ? {} : { temperature };
|
|
11790
|
-
}
|
|
11702
|
+
// COMPAT(llm-facade-openai-sampling-fields) · 기존 llm.ts 공개 API 유지.
|
|
11703
|
+
import {
|
|
11704
|
+
openAiCompatSamplingFields,
|
|
11705
|
+
openAiOutputTokenField,
|
|
11706
|
+
isNewGenerationOpenAiModel,
|
|
11707
|
+
openAiTemperatureField,
|
|
11708
|
+
} from './llm/openai-sampling-fields.js';
|
|
11709
|
+
export {
|
|
11710
|
+
openAiCompatSamplingFields,
|
|
11711
|
+
openAiOutputTokenField,
|
|
11712
|
+
isNewGenerationOpenAiModel,
|
|
11713
|
+
openAiTemperatureField,
|
|
11714
|
+
};
|