@cartanova/qgrid-cli 2.5.2 → 2.5.3

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.
Files changed (27) hide show
  1. package/bundle/dist/application/qgrid/qgrid.dispatcher.js +6 -2
  2. package/bundle/dist/application/qgrid/qgrid.frame.js +56 -24
  3. package/bundle/dist/application/qgrid/qgrid.types.js +5 -3
  4. package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +2 -2
  5. package/bundle/dist/utils/providers/anthropic/stream-json-adapter.js +6 -4
  6. package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +21 -1
  7. package/bundle/src/application/qgrid/qgrid.dispatcher.ts +5 -3
  8. package/bundle/src/application/qgrid/qgrid.frame.test.ts +49 -0
  9. package/bundle/src/application/qgrid/qgrid.frame.ts +79 -26
  10. package/bundle/src/application/qgrid/qgrid.types.ts +4 -1
  11. package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.test.ts +12 -0
  12. package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.ts +1 -1
  13. package/bundle/src/utils/providers/anthropic/stream-json-adapter.test.ts +66 -0
  14. package/bundle/src/utils/providers/anthropic/stream-json-adapter.ts +12 -3
  15. package/bundle/src/utils/providers/common/provider-dispatcher.ts +2 -0
  16. package/bundle/web-dist/client/assets/{index-BQuKeBC5.js → index-DXdGBfB8.js} +3 -3
  17. package/bundle/web-dist/client/assets/{logs-BSsHMIKT.js → logs-D_j1yxC4.js} +1 -1
  18. package/bundle/web-dist/client/assets/{monit-D5ypGmGi.js → monit-CIToyoxW.js} +1 -1
  19. package/bundle/web-dist/client/assets/{routes-BsCbz225.js → routes-Dd21A-wi.js} +1 -1
  20. package/bundle/web-dist/client/assets/{show-CXPhLZCl.js → show-DfePWRbc.js} +1 -1
  21. package/bundle/web-dist/client/assets/{tokens-gEGH52O8.js → tokens-B4MV2fdk.js} +1 -1
  22. package/bundle/web-dist/client/index.html +1 -1
  23. package/bundle/web-dist/server/entry-server.generated.js +1 -1
  24. package/package.json +1 -1
  25. package/skills/qgrid/references/ai-sdk-provider-contract.md +8 -0
  26. package/skills/qgrid/references/anthropic-claude-code-runtime.md +28 -1
  27. package/skills/qgrid/references/decision-rationale.md +1 -0
@@ -175,6 +175,18 @@ describe("AnthropicDispatcher", () => {
175
175
  expect(call.jsonSchema).toBe(JSON.stringify({ type: "object", properties: {} }));
176
176
  });
177
177
 
178
+ it("요청 timeoutMs를 Claude session에 전달하고 미지정 시 240초 기본값을 쓴다", async () => {
179
+ const d = new AnthropicDispatcher();
180
+ d.onTokenAdded(1, "tok-A", creds(), null, 1);
181
+
182
+ await d.generate(baseReq({ timeoutMs: 360_000 }));
183
+ expect(firstRunRequest().timeoutMs).toBe(360_000);
184
+
185
+ runClaudeSessionMock.mockClear();
186
+ await d.generate(baseReq());
187
+ expect(firstRunRequest().timeoutMs).toBe(240_000);
188
+ });
189
+
178
190
  it("quota 소진 → 에러", async () => {
179
191
  const d = new AnthropicDispatcher();
180
192
  d.onTokenAdded(1, "tok-A", creds(), null, 1);
@@ -326,7 +326,7 @@ export class AnthropicDispatcher implements ProviderDispatcher {
326
326
  system: req.systemPrompt,
327
327
  jsonSchema,
328
328
  effort: req.effort,
329
- timeoutMs: DEFAULT_TIMEOUT_MS,
329
+ timeoutMs: req.timeoutMs ?? DEFAULT_TIMEOUT_MS,
330
330
  coldHistory: req.coldHistory,
331
331
  input: req.coldInput,
332
332
  abortSignal: req.abortSignal,
@@ -265,6 +265,72 @@ describe("handleStreamJsonLine (출력 어댑터)", () => {
265
265
  expect(result!.costUsd).toBe(0.0042);
266
266
  });
267
267
 
268
+ it("structured retry 성공: 첫 거절 입력이 아니라 최종 structured_output을 반환", () => {
269
+ const rejected = {
270
+ l2: "en",
271
+ line_id: "s1_l1",
272
+ speaker: "Minjae",
273
+ title: "첫 시도",
274
+ scenes: [],
275
+ };
276
+ const accepted = {
277
+ title: "두 번째 시도",
278
+ opening_line: "스키마를 만족한 최종 응답",
279
+ scenes: [],
280
+ };
281
+ const { result } = runLines(
282
+ [
283
+ JSON.stringify({
284
+ type: "assistant",
285
+ message: {
286
+ content: [{ type: "tool_use", name: "StructuredOutput", input: rejected }],
287
+ },
288
+ }),
289
+ JSON.stringify({
290
+ type: "user",
291
+ message: {
292
+ content: [
293
+ {
294
+ type: "tool_result",
295
+ is_error: true,
296
+ content: "Output does not match required schema",
297
+ },
298
+ ],
299
+ },
300
+ }),
301
+ JSON.stringify({
302
+ type: "assistant",
303
+ message: {
304
+ content: [{ type: "tool_use", name: "StructuredOutput", input: accepted }],
305
+ },
306
+ }),
307
+ JSON.stringify({
308
+ type: "user",
309
+ message: {
310
+ content: [
311
+ {
312
+ type: "tool_result",
313
+ content: "Structured output provided successfully",
314
+ },
315
+ ],
316
+ },
317
+ }),
318
+ JSON.stringify({
319
+ type: "result",
320
+ subtype: "success",
321
+ is_error: false,
322
+ structured_output: accepted,
323
+ usage: { input_tokens: 10, output_tokens: 20 },
324
+ }),
325
+ ],
326
+ { structuredOutput: true },
327
+ );
328
+
329
+ expect(JSON.parse(result!.text)).toEqual(accepted);
330
+ expect(result!.isError).toBe(false);
331
+ expect(result!.subtype).toBe("success");
332
+ });
333
+
268
334
  // SON-495: error_max_structured_output_retries 는 모델이 schema 를 못 맞춘 비정상 종료다.
269
335
  // CC structured output 은 constrained decoding 이 아니라 사후 AJV 검증이라, 모델이 가끔
270
336
  // placeholder($PARAMETER_NAME)·거부("just kidding")·필드 누락 같은 발작/불완전 출력을 낸다.
@@ -406,9 +406,20 @@ export function handleStreamJsonLine(
406
406
  }
407
407
 
408
408
  if (j.type === "result") {
409
+ const subtype = typeof j.subtype === "string" ? j.subtype : undefined;
410
+ const terminalReason = typeof j.terminal_reason === "string" ? j.terminal_reason : undefined;
411
+ const resultSucceeded =
412
+ j.is_error !== true &&
413
+ terminalReason !== "model_error" &&
414
+ (subtype === undefined || subtype === "success");
409
415
  let text: string;
410
416
  const preservedStructuredOutput = structuredOutput ? state?.structuredOutputText : undefined;
411
- if (preservedStructuredOutput !== undefined) {
417
+ // Claude Code는 StructuredOutput 스키마 검증 실패 후 내부 재시도를 할 수 있다. assistant
418
+ // 이벤트의 첫 tool input은 거절된 시도일 수 있으므로, 성공 result가 제공한 최종 검증값을
419
+ // 우선한다. 첫 입력 보존값은 max_turns/retry 초과처럼 성공 result가 없는 진단 경로용이다.
420
+ if (structuredOutput && resultSucceeded && j.structured_output !== undefined) {
421
+ text = JSON.stringify(j.structured_output);
422
+ } else if (preservedStructuredOutput !== undefined) {
412
423
  text = preservedStructuredOutput;
413
424
  } else if (j.structured_output !== undefined) {
414
425
  text = JSON.stringify(j.structured_output);
@@ -429,8 +440,6 @@ export function handleStreamJsonLine(
429
440
  }
430
441
  const usage = toUsageBreakdown(rawUsage as ClaudeUsage);
431
442
  const quotaExhausted = text.startsWith("You've hit");
432
- const subtype = typeof j.subtype === "string" ? j.subtype : undefined;
433
- const terminalReason = typeof j.terminal_reason === "string" ? j.terminal_reason : undefined;
434
443
 
435
444
  // SON-495: 비정상 종료(error_max_structured_output_retries 등)는 정직하게 에러로 처리한다.
436
445
  // CC structured output 은 constrained decoding 이 아니라 "tool input 생성 후 사후 AJV 검증"이라
@@ -55,6 +55,8 @@ export interface GenerateRequest {
55
55
  verbosity?: string;
56
56
  reasoningSummary?: string;
57
57
  serviceTier?: string;
58
+ // provider 실행 제한(ms). 현재 Anthropic Claude session 타이머가 사용한다.
59
+ timeoutMs?: number;
58
60
  abortSignal?: AbortSignal;
59
61
  // 첫 turn / 재사용 폴백 시 보낼 input — 전체 prompt. 항상 설정.
60
62
  coldInput: Array<UserInput>;