cuelo 0.6.4 → 0.6.5

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 (114) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +7 -7
  3. package/.next/build-manifest.json +2 -2
  4. package/.next/prerender-manifest.json +3 -3
  5. package/.next/required-server-files.js +2 -2
  6. package/.next/required-server-files.json +2 -2
  7. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  8. package/.next/server/app/_global-error.html +1 -1
  9. package/.next/server/app/_global-error.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  15. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  17. package/.next/server/app/_not-found.html +1 -1
  18. package/.next/server/app/_not-found.rsc +2 -2
  19. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  20. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  21. package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  22. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  23. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  24. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  25. package/.next/server/app/api/agent/[id]/account/route.js +1 -1
  26. package/.next/server/app/api/agent/[id]/account/route.js.nft.json +1 -1
  27. package/.next/server/app/api/agent/[id]/events/route.js +2 -2
  28. package/.next/server/app/api/agent/[id]/events/route.js.nft.json +1 -1
  29. package/.next/server/app/api/agent/[id]/route.js +1 -1
  30. package/.next/server/app/api/agent/[id]/route.js.nft.json +1 -1
  31. package/.next/server/app/api/agent/new/route.js +1 -1
  32. package/.next/server/app/api/agent/new/route.js.nft.json +1 -1
  33. package/.next/server/app/api/agent/running/route.js +1 -1
  34. package/.next/server/app/api/agent/running/route.js.nft.json +1 -1
  35. package/.next/server/app/api/gpt6/handles/route.js +1 -1
  36. package/.next/server/app/api/gpt6/handles/route.js.nft.json +1 -1
  37. package/.next/server/app/api/gpt6/reply/route.js +1 -1
  38. package/.next/server/app/api/gpt6/reply/route.js.nft.json +1 -1
  39. package/.next/server/app/api/gpt6/token/route.js +1 -1
  40. package/.next/server/app/api/gpt6/token/route.js.nft.json +1 -1
  41. package/.next/server/app/api/gpt6/web6-session/route.js +1 -1
  42. package/.next/server/app/api/gpt6/web6-session/route.js.nft.json +1 -1
  43. package/.next/server/app/api/live/call/[callId]/route.js +1 -1
  44. package/.next/server/app/api/live/call/[callId]/route.js.nft.json +1 -1
  45. package/.next/server/app/api/live/events/route.js +2 -2
  46. package/.next/server/app/api/live/events/route.js.nft.json +1 -1
  47. package/.next/server/app/api/live/offer/route.js +1 -1
  48. package/.next/server/app/api/live/offer/route.js.nft.json +1 -1
  49. package/.next/server/app/api/live/voices/route.js +1 -1
  50. package/.next/server/app/api/live/voices/route.js.nft.json +1 -1
  51. package/.next/server/app/api/lounge/events/route.js +2 -2
  52. package/.next/server/app/api/lounge/events/route.js.nft.json +1 -1
  53. package/.next/server/app/api/lounge/route.js +1 -1
  54. package/.next/server/app/api/lounge/route.js.nft.json +1 -1
  55. package/.next/server/app/api/project-trust/route.js +1 -1
  56. package/.next/server/app/api/project-trust/route.js.nft.json +1 -1
  57. package/.next/server/app/api/sessions/[id]/auto-name/route.js +1 -1
  58. package/.next/server/app/api/sessions/[id]/auto-name/route.js.nft.json +1 -1
  59. package/.next/server/app/api/sessions/[id]/context/route.js +1 -1
  60. package/.next/server/app/api/sessions/[id]/context/route.js.nft.json +1 -1
  61. package/.next/server/app/api/sessions/[id]/route.js +1 -1
  62. package/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
  63. package/.next/server/app/api/sessions/[id]/state/route.js +1 -1
  64. package/.next/server/app/api/sessions/[id]/state/route.js.nft.json +1 -1
  65. package/.next/server/app/api/sessions/route.js +1 -1
  66. package/.next/server/app/api/sessions/route.js.nft.json +1 -1
  67. package/.next/server/app/api/tool-image/route.js +1 -1
  68. package/.next/server/app/api/tool-image/route.js.nft.json +1 -1
  69. package/.next/server/app/api/update-maintenance/route.js +3 -3
  70. package/.next/server/app/api/update-maintenance/route.js.nft.json +1 -1
  71. package/.next/server/app/api/updates/route.js +1 -1
  72. package/.next/server/app/index.html +1 -1
  73. package/.next/server/app/index.rsc +4 -4
  74. package/.next/server/app/index.segments/__PAGE__.segment.rsc +3 -3
  75. package/.next/server/app/index.segments/_full.segment.rsc +4 -4
  76. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  77. package/.next/server/app/index.segments/_index.segment.rsc +2 -2
  78. package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  79. package/.next/server/app/page.js +3 -3
  80. package/.next/server/app/page_client-reference-manifest.js +1 -1
  81. package/.next/server/app/recover/page_client-reference-manifest.js +1 -1
  82. package/.next/server/app/recover.html +1 -1
  83. package/.next/server/app/recover.rsc +2 -2
  84. package/.next/server/app/recover.segments/_full.segment.rsc +2 -2
  85. package/.next/server/app/recover.segments/_head.segment.rsc +1 -1
  86. package/.next/server/app/recover.segments/_index.segment.rsc +2 -2
  87. package/.next/server/app/recover.segments/_tree.segment.rsc +1 -1
  88. package/.next/server/app/recover.segments/recover/__PAGE__.segment.rsc +1 -1
  89. package/.next/server/app/recover.segments/recover.segment.rsc +1 -1
  90. package/.next/server/app-paths-manifest.json +7 -7
  91. package/.next/server/chunks/3707.js +2 -2
  92. package/.next/server/chunks/6429.js +3 -3
  93. package/.next/server/middleware-build-manifest.js +1 -1
  94. package/.next/server/pages/404.html +1 -1
  95. package/.next/server/pages/500.html +1 -1
  96. package/.next/server/server-reference-manifest.json +1 -1
  97. package/.next/static/chunks/app/{layout-4e015d86866518f5.js → layout-1f400c6ff9faef10.js} +1 -1
  98. package/.next/static/chunks/app/page-73ab54909e09eb66.js +22 -0
  99. package/.next/trace +105 -105
  100. package/.next/trace-build +1 -1
  101. package/README.md +5 -2
  102. package/Tools/CUELO_Setup/files/native-runtime-patch.js +5 -5
  103. package/Tools/CUELO_Setup/files/usage-server.js +18 -1
  104. package/Tools/OMP_Global_Config/agent/RULES.md +1 -1
  105. package/Tools/OMP_Global_Config/agent/agents/maker.md +25 -42
  106. package/Tools/OMP_Global_Config/agent/extensions/lib/maker-routing.ts +7 -3
  107. package/Tools/OMP_Global_Config/agent/prompt-copies/RULES.en.md +2 -2
  108. package/Tools/OMP_Global_Config/agent/sop/_common.md +14 -23
  109. package/Tools/OMP_Global_Config/agent/sop/_writer.md +10 -18
  110. package/Tools/OMP_Global_Config/patches/apply-core-patch.mjs +248 -8
  111. package/package.json +7 -7
  112. package/.next/static/chunks/app/page-1933216030ad4353.js +0 -22
  113. /package/.next/static/{jnfhHGBsVK-Y8Lt6wq9XJ → p4VT4B7cMX4CRW9JBo9-M}/_buildManifest.js +0 -0
  114. /package/.next/static/{jnfhHGBsVK-Y8Lt6wq9XJ → p4VT4B7cMX4CRW9JBo9-M}/_ssgManifest.js +0 -0
@@ -341,7 +341,7 @@ const EDITS = [
341
341
  },
342
342
  {
343
343
  file: "../pi-agent-core/src/agent.ts",
344
- marker: "for (const listener of this.#userSteeringListeners) listener();",
344
+ marker: "for (const listener of this.#userSteeringListeners) listener();\n\t\t}\n\t\tthis.#notifySteeringWaiters();\n\t}",
345
345
  anchor: ` steer(m: AgentMessage) {
346
346
  this.#steeringQueue.push(m);
347
347
  this.#notifySteeringWaiters();
@@ -353,6 +353,25 @@ const EDITS = [
353
353
  }
354
354
  this.#notifySteeringWaiters();
355
355
  }`,
356
+ // 18.4.4(#11872): steer() 가 큐 mutator 마다 `#emitQueueChanged()` 를 정확히 한 번 부른다. 사용자 steer 리스너는
357
+ // 큐 push 직후·notifyWaiters 이전이라는 위치만 그대로 두고 upstream 의 emit 은 건드리지 않는다.
358
+ alternates: [{
359
+ file: "../pi-agent-core/src/agent.ts",
360
+ marker: "for (const listener of this.#userSteeringListeners) listener();\n\t\t}\n\t\tthis.#notifySteeringWaiters();\n\t\tthis.#emitQueueChanged();",
361
+ anchor: ` steer(m: AgentMessage) {
362
+ this.#steeringQueue.push(m);
363
+ this.#notifySteeringWaiters();
364
+ this.#emitQueueChanged();
365
+ }`,
366
+ patched: ` steer(m: AgentMessage) {
367
+ this.#steeringQueue.push(m);
368
+ if (m.role === "user" && ("attribution" in m ? m.attribution !== "agent" : true) && !("synthetic" in m && m.synthetic === true)) {
369
+ for (const listener of this.#userSteeringListeners) listener();
370
+ }
371
+ this.#notifySteeringWaiters();
372
+ this.#emitQueueChanged();
373
+ }`,
374
+ }],
356
375
  },
357
376
  {
358
377
  file: "../pi-agent-core/src/agent-loop.ts",
@@ -5308,6 +5327,12 @@ export class LearnTool implements AgentTool<LearnSchema> {`,
5308
5327
  marker: "// HANSE: env without a service name exports into the command\n\t\t\tpendingNotices.push(",
5309
5328
  anchor: "\t\t} else if (ready !== undefined || env !== undefined) {\n\t\t\t// Nothing can honour ready/env without a service to attach them to;\n\t\t\t// running the command the caller did ask for beats failing the call.\n\t\t\tconst ignored = [ready && \"ready\", env && \"env\"].filter(Boolean).join(\" and \");\n\t\t\tpendingNotices.push(`Ignored ${ignored}: service-only, and no service name was given.`);\n",
5310
5329
  patched: "\t\t} else if (ready !== undefined) {\n\t\t\t// HANSE: env without a service name exports into the command\n\t\t\tpendingNotices.push(\"Ignored ready: service-only, and no service name was given.\");\n",
5330
+ }, {
5331
+ // 18.4.4: upstream 이 env 파라미터를 지우면서 알림은 ready 만 남았다. 이미 우리가 바라는 형태라 적용할 것이 없다.
5332
+ file: "src/tools/bash.ts",
5333
+ marker: "\t\t} else if (ready !== undefined) {\n\t\t\t// Nothing can honour ready without a service to attach it to;\n",
5334
+ anchor: "\t\t} else if (ready !== undefined) {\n\t\t\t// Nothing can honour ready without a service to attach it to;\n",
5335
+ patched: "\t\t} else if (ready !== undefined) {\n\t\t\t// Nothing can honour ready without a service to attach it to;\n",
5311
5336
  }],
5312
5337
  },
5313
5338
  {
@@ -5316,8 +5341,8 @@ export class LearnTool implements AgentTool<LearnSchema> {`,
5316
5341
  marker: "// HANSE: non-service env becomes a leading export",
5317
5342
  anchor: "\t\tinvalidateGithubCacheForBashCommand(command);\n",
5318
5343
  patched: ` invalidateGithubCacheForBashCommand(command);
5319
- // HANSE: non-service env becomes a leading export
5320
- if (name === undefined && env) {
5344
+ // HANSE: non-service env becomes a leading export (18.4.4: service env too — upstream dropped launch env)
5345
+ if (env) {
5321
5346
  const exports: string[] = [];
5322
5347
  for (const [key, value] of Object.entries(env)) {
5323
5348
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) throw new ToolError("Invalid env name: " + key);
@@ -5327,11 +5352,94 @@ export class LearnTool implements AgentTool<LearnSchema> {`,
5327
5352
  }
5328
5353
  `,
5329
5354
  },
5355
+ // 18.4.4 는 bash 도구의 `env` 파라미터를 통째로 지웠다(pi-coding-agent CHANGELOG "Removed"). env 는 사용자의 기존 도구
5356
+ // 계약이라 upstream 삭제 hunk 를 되돌린다(아래 여섯 항목). 각 항목의 본 후보는 18.4.4 원문에 `// HANSE: env restored` 를
5357
+ // 붙여 복원하고, alternate 는 env 가 원래 있는 18.4.3 원문의 no-op 이다. 18.4.3 에서 --revert 가 upstream 의
5358
+ // env 코드를 지우지 않도록 두 후보의 marker 가 서로의 결과에 들어 있지 않게 나눴다.
5359
+ // service 는 launch 가 env 를 받지 않으므로 위 export 항목이 service·일반 명령 모두 명령 앞 export 로 넣는다.
5360
+ {
5361
+ file: "src/tools/bash.ts",
5362
+ marker: "\t\"env?\": type.record(\"string\", \"string\"), // HANSE: env restored\n});\n\nconst bashSchemaWithAsyncAndService = type({",
5363
+ anchor: "\t}),\n});\n\nconst bashSchemaWithAsyncAndService = type({",
5364
+ patched: "\t}),\n\t\"env?\": type.record(\"string\", \"string\"), // HANSE: env restored\n});\n\nconst bashSchemaWithAsyncAndService = type({",
5365
+ alternates: [{
5366
+ file: "src/tools/bash.ts",
5367
+ marker: "\t}),\n\t\"env?\": type.record(\"string\", \"string\"),\n});\n\nconst bashSchemaWithAsyncAndService = type({",
5368
+ anchor: "\t}),\n\t\"env?\": type.record(\"string\", \"string\"),\n});\n\nconst bashSchemaWithAsyncAndService = type({",
5369
+ patched: "\t}),\n\t\"env?\": type.record(\"string\", \"string\"),\n});\n\nconst bashSchemaWithAsyncAndService = type({",
5370
+ }],
5371
+ },
5372
+ {
5373
+ file: "src/tools/bash.ts",
5374
+ marker: "\t\"env?\": type.record(\"string\", \"string\"), // HANSE: env restored\n});\n\ntype BashToolSchema =",
5375
+ anchor: "\t}),\n});\n\ntype BashToolSchema =",
5376
+ patched: "\t}),\n\t\"env?\": type.record(\"string\", \"string\"), // HANSE: env restored\n});\n\ntype BashToolSchema =",
5377
+ alternates: [{
5378
+ file: "src/tools/bash.ts",
5379
+ marker: "\t}),\n\t\"env?\": type.record(\"string\", \"string\"),\n});\n\ntype BashToolSchema =",
5380
+ anchor: "\t}),\n\t\"env?\": type.record(\"string\", \"string\"),\n});\n\ntype BashToolSchema =",
5381
+ patched: "\t}),\n\t\"env?\": type.record(\"string\", \"string\"),\n});\n\ntype BashToolSchema =",
5382
+ }],
5383
+ },
5384
+ {
5385
+ file: "src/tools/bash.ts",
5386
+ marker: "\tenv?: Record<string, string>; // HANSE: env restored\n",
5387
+ anchor: "\tready?: ServiceReady;\n\tasync?: boolean;",
5388
+ patched: "\tready?: ServiceReady;\n\tenv?: Record<string, string>; // HANSE: env restored\n\tasync?: boolean;",
5389
+ alternates: [{
5390
+ file: "src/tools/bash.ts",
5391
+ marker: "\tready?: ServiceReady;\n\tenv?: Record<string, string>;\n\tasync?: boolean;",
5392
+ anchor: "\tready?: ServiceReady;\n\tenv?: Record<string, string>;\n\tasync?: boolean;",
5393
+ patched: "\tready?: ServiceReady;\n\tenv?: Record<string, string>;\n\tasync?: boolean;",
5394
+ }],
5395
+ },
5396
+ {
5397
+ file: "src/tools/bash.ts",
5398
+ marker: "/** HANSE: env restored. Drops a record with no keys",
5399
+ anchor: "\treturn { log, host, port, timeout };\n}\n\nexport interface BashToolOptions {}",
5400
+ patched: "\treturn { log, host, port, timeout };\n}\n\n/** HANSE: env restored. Drops a record with no keys: `env: {}` sets nothing, so it requests nothing. */\nfunction nonEmptyRecord(record: Record<string, string> | undefined): Record<string, string> | undefined {\n\tif (!record) return undefined;\n\tfor (const _key in record) return record;\n\treturn undefined;\n}\n\nexport interface BashToolOptions {}",
5401
+ alternates: [{
5402
+ file: "src/tools/bash.ts",
5403
+ marker: "/** Drops a record with no keys: `env: {}` sets nothing, so it requests nothing. */\nfunction nonEmptyRecord(record: Record<string, string> | undefined)",
5404
+ anchor: "/** Drops a record with no keys: `env: {}` sets nothing, so it requests nothing. */\nfunction nonEmptyRecord(record: Record<string, string> | undefined)",
5405
+ patched: "/** Drops a record with no keys: `env: {}` sets nothing, so it requests nothing. */\nfunction nonEmptyRecord(record: Record<string, string> | undefined)",
5406
+ }],
5407
+ },
5408
+ {
5409
+ file: "src/tools/bash.ts",
5410
+ marker: "\t\t\tenv: rawEnv, // HANSE: env restored\n",
5411
+ anchor: "\t\t\tready: rawReady,\n\t\t\tasync: rawAsync,",
5412
+ patched: "\t\t\tready: rawReady,\n\t\t\tenv: rawEnv, // HANSE: env restored\n\t\t\tasync: rawAsync,",
5413
+ alternates: [{
5414
+ file: "src/tools/bash.ts",
5415
+ marker: "\t\t\tready: rawReady,\n\t\t\tenv: rawEnv,\n\t\t\tasync: rawAsync,",
5416
+ anchor: "\t\t\tready: rawReady,\n\t\t\tenv: rawEnv,\n\t\t\tasync: rawAsync,",
5417
+ patched: "\t\t\tready: rawReady,\n\t\t\tenv: rawEnv,\n\t\t\tasync: rawAsync,",
5418
+ }],
5419
+ },
5420
+ {
5421
+ file: "src/tools/bash.ts",
5422
+ marker: "\t\tconst env = nonEmptyRecord(rawEnv); // HANSE: env restored\n",
5423
+ anchor: "\t\tconst ready = normalizeReady(rawReady);\n\t\tconst asyncRequested = rawAsync === true;\n",
5424
+ patched: "\t\tconst ready = normalizeReady(rawReady);\n\t\tconst env = nonEmptyRecord(rawEnv); // HANSE: env restored\n\t\tconst asyncRequested = rawAsync === true;\n",
5425
+ alternates: [{
5426
+ file: "src/tools/bash.ts",
5427
+ marker: "\t\tconst ready = normalizeReady(rawReady);\n\t\tconst env = nonEmptyRecord(rawEnv);\n",
5428
+ anchor: "\t\tconst ready = normalizeReady(rawReady);\n\t\tconst env = nonEmptyRecord(rawEnv);\n",
5429
+ patched: "\t\tconst ready = normalizeReady(rawReady);\n\t\tconst env = nonEmptyRecord(rawEnv);\n",
5430
+ }],
5431
+ },
5330
5432
  {
5331
5433
  file: "src/prompts/tools/bash.md",
5332
5434
  marker: "ready requires name; no async/timeout. env adds variables (without name: exported before the command)",
5333
5435
  anchor: "unique name; ready/env require name; no async/timeout. env adds variables;",
5334
5436
  patched: "unique name; ready requires name; no async/timeout. env adds variables (without name: exported before the command);",
5437
+ alternates: [{
5438
+ file: "src/prompts/tools/bash.md",
5439
+ marker: "env adds variables (exported before the command)",
5440
+ anchor: "unique name; ready requires name; no async/timeout; pty defaults true.",
5441
+ patched: "unique name; ready requires name; no async/timeout. env adds variables (exported before the command); pty defaults true.",
5442
+ }],
5335
5443
  },
5336
5444
  {
5337
5445
  // 같은 집계: `todo` append 에 phase 를 빠뜨리면 거절됐다(사흘간 9건). phase 가 없으면 아직 안 끝난
@@ -5589,9 +5697,15 @@ function raiseToAutoThinkingFloor(model: Model, level: Effort | undefined, setti
5589
5697
  // 조건이 하나도 걸리지 않아 모든 창이 일치했고("multiple windows match 65822"), `title: /정규식/`은
5590
5698
  // 안내 없이 TypeError로 죽었다. 필터 값은 문자열만 받고, 아니면 무엇이 틀렸는지 말하는 ToolError를 낸다.
5591
5699
  file: "src/tools/computer/worker.ts",
5592
- marker: "function assertWindowFilter(filter: unknown)",
5700
+ // 18.4.3: matchesFilter 가 `window.id === filter.id` 라 숫자 id 필터는 어떤 창과도 일치하지 않는다.
5701
+ // 그래서 이 버전에서는 숫자 id 필터도 거절하는 원래 검사를 유지한다(아래 alternate 는 18.4.4 전용).
5702
+ marker: "\"exact window id\" : \"case-insensitive substring\"",
5593
5703
  anchor: `function matchesFilter(window: DesktopWindow, filter?: WindowFilter): boolean {
5594
- if (!filter) return true;`,
5704
+ if (!filter) return true;
5705
+ const app = filter.app?.toLocaleLowerCase();
5706
+ const title = filter.title?.toLocaleLowerCase();
5707
+ return (
5708
+ (filter.id === undefined || window.id === filter.id) &&`,
5595
5709
  patched: `/** A filter is a plain \`{ id?, app?, title? }\` of strings; anything else used to match every window. */
5596
5710
  function assertWindowFilter(filter: unknown): asserts filter is WindowFilter | undefined {
5597
5711
  if (filter === undefined) return;
@@ -5608,7 +5722,45 @@ function assertWindowFilter(filter: unknown): asserts filter is WindowFilter | u
5608
5722
  }
5609
5723
 
5610
5724
  function matchesFilter(window: DesktopWindow, filter?: WindowFilter): boolean {
5611
- if (!filter) return true;`,
5725
+ if (!filter) return true;
5726
+ const app = filter.app?.toLocaleLowerCase();
5727
+ const title = filter.title?.toLocaleLowerCase();
5728
+ return (
5729
+ (filter.id === undefined || window.id === filter.id) &&`,
5730
+ // 18.4.4(#13649): matchesFilter 가 `String(filter.id)` 로 정규화한다. 안전한 정수 id 는 문자열 id 와 같은 창이라
5731
+ // 허용하고, 그 밖의 값(정규식·객체·소수 등)만 거절한다.
5732
+ alternates: [{
5733
+ file: "src/tools/computer/worker.ts",
5734
+ marker: "Number.isSafeInteger(value)) continue;",
5735
+ anchor: `function matchesFilter(window: DesktopWindow, filter?: WindowFilter): boolean {
5736
+ if (!filter) return true;
5737
+ const app = filter.app?.toLocaleLowerCase();
5738
+ const title = filter.title?.toLocaleLowerCase();
5739
+ return (
5740
+ (filter.id === undefined || window.id === String(filter.id)) &&`,
5741
+ patched: `/** A filter is a plain \`{ id?, app?, title? }\`; string app/title and a string or integer id. Anything else used to match every window. */
5742
+ function assertWindowFilter(filter: unknown): asserts filter is WindowFilter | undefined {
5743
+ if (filter === undefined) return;
5744
+ if (filter === null || typeof filter !== "object" || Array.isArray(filter)) {
5745
+ throw new ToolError(\`window filter must be an object like { app?, title?, id? }; got \${filter === null ? "null" : typeof filter}\`);
5746
+ }
5747
+ for (const key of ["id", "app", "title"] as const) {
5748
+ const value = (filter as Record<string, unknown>)[key];
5749
+ if (key === "id" && typeof value === "number" && Number.isSafeInteger(value)) continue;
5750
+ if (value !== undefined && typeof value !== "string") {
5751
+ const got = value instanceof RegExp ? \`RegExp \${value}\` : typeof value;
5752
+ throw new ToolError(\`window filter \${key} must be a string (\${key === "id" ? "exact window id, or an integer" : "case-insensitive substring"}); got \${got}\`);
5753
+ }
5754
+ }
5755
+ }
5756
+
5757
+ function matchesFilter(window: DesktopWindow, filter?: WindowFilter): boolean {
5758
+ if (!filter) return true;
5759
+ const app = filter.app?.toLocaleLowerCase();
5760
+ const title = filter.title?.toLocaleLowerCase();
5761
+ return (
5762
+ (filter.id === undefined || window.id === String(filter.id)) &&`,
5763
+ }],
5612
5764
  },
5613
5765
  {
5614
5766
  // 숫자 id는 창 id(숫자 문자열)와 뜻이 하나뿐이라 문자열로 바꿔 id로 찾는다. 그 밖의 값은 위에서 거절한다.
@@ -5626,6 +5778,23 @@ function matchesFilter(window: DesktopWindow, filter?: WindowFilter): boolean {
5626
5778
  typeof byId === "string"
5627
5779
  ? windows.filter(window => window.id === byId)
5628
5780
  : windows.filter(window => matchesFilter(window, byId));`,
5781
+ // 18.4.4: upstream(#13649)이 숫자 id 를 `String(selector)` 로 직접 푼다. 숫자 변환은 upstream 것을 쓰고, 우리는
5782
+ // 객체 필터(null·배열·정규식 값)를 거절하는 검사만 남긴다.
5783
+ alternates: [{
5784
+ file: "src/tools/computer/worker.ts",
5785
+ marker: "if (typeof selector !== \"string\" && typeof selector !== \"number\") assertWindowFilter(selector);",
5786
+ anchor: ` const windows = await nativeCall(signal, () => session.listWindows());
5787
+ const matches =
5788
+ typeof selector === "string" || typeof selector === "number"
5789
+ ? windows.filter(window => window.id === String(selector))
5790
+ : windows.filter(window => matchesFilter(window, selector));`,
5791
+ patched: ` if (typeof selector !== "string" && typeof selector !== "number") assertWindowFilter(selector);
5792
+ const windows = await nativeCall(signal, () => session.listWindows());
5793
+ const matches =
5794
+ typeof selector === "string" || typeof selector === "number"
5795
+ ? windows.filter(window => window.id === String(selector))
5796
+ : windows.filter(window => matchesFilter(window, selector));`,
5797
+ }],
5629
5798
  },
5630
5799
  {
5631
5800
  file: "src/tools/computer/worker.ts",
@@ -7406,8 +7575,10 @@ export interface McpSelectEventResult {
7406
7575
  {
7407
7576
  // 교훈 자동 저장 표시(2026-09-29 사용자 지적 "교훈 저장이 안 뜬다"): autolearn capture Agent는 본 대화와
7408
7577
  // 분리돼 저장해도 화면·다음 턴 문맥에 아무것도 남지 않았다. 성공한 learn·manage_skill만 요약해 돌려준다.
7578
+ // r3(2026-09-30 사용자 지적 "... 로 잘리는 것보다 몇 줄 요약이 낫다"): 160자 절단 대신 교훈의 첫 문장(=capture가
7579
+ // 맨 앞에 쓰는 제목, 아래 nudge 항목)만 보여 주고, 그마저 120자를 넘으면 단어 경계에서 줄인다. legacyPatched는 r1 적용본.
7409
7580
  file: "src/sdk.ts",
7410
- marker: `export function summarizeAutoLearnSaved(`,
7581
+ marker: ` if (first.length <= 120) return first;`,
7411
7582
  anchor: ` createSessionId?: () => string;
7412
7583
  }
7413
7584
 
@@ -7417,6 +7588,53 @@ export interface McpSelectEventResult {
7417
7588
  onCaptured?: (saved: string[]) => void;
7418
7589
  }
7419
7590
 
7591
+ /**
7592
+ * Summarize a capture run's successful \`learn\`/\`manage_skill\` calls. Each call is paired with its
7593
+ * result, so a rejected or failed save is never reported as stored.
7594
+ */
7595
+ export function summarizeAutoLearnSaved(messages: readonly AgentMessage[], from: number): string[] {
7596
+ const text = (value: unknown): string => (typeof value === "string" ? value.replace(/\\s+/g, " ").trim() : "");
7597
+ const calls = new Map<string, { name: string; args: Record<string, unknown> }>();
7598
+ const saved: string[] = [];
7599
+ for (const message of messages.slice(from)) {
7600
+ if (message.role === "assistant") {
7601
+ for (const block of message.content) {
7602
+ if (block.type === "toolCall") {
7603
+ calls.set(block.id, { name: block.name, args: (block.arguments ?? {}) as Record<string, unknown> });
7604
+ }
7605
+ }
7606
+ continue;
7607
+ }
7608
+ if (message.role !== "toolResult" || message.isError) continue;
7609
+ const call = calls.get(message.toolCallId);
7610
+ if (!call) continue;
7611
+ if (call.name === "learn") {
7612
+ const memory = text(call.args.memory);
7613
+ const skill = call.args.skill as { action?: unknown; name?: unknown } | undefined;
7614
+ const skillNote = skill ? " (스킬 " + text(skill.action) + " " + text(skill.name) + ")" : "";
7615
+ saved.push("교훈: " + autoLearnHeadline(memory) + skillNote);
7616
+ } else if (call.name === "manage_skill") {
7617
+ saved.push("스킬 " + text(call.args.action) + ": " + text(call.args.name));
7618
+ }
7619
+ }
7620
+ return saved;
7621
+ }
7622
+
7623
+ /** The notice headline: the lesson's first sentence, shortened at a word boundary past 120 chars. */
7624
+ function autoLearnHeadline(memory: string): string {
7625
+ const first = /^.+?[.!?](?=\\s|$)/.exec(memory)?.[0] ?? memory;
7626
+ if (first.length <= 120) return first;
7627
+ const cut = first.slice(0, 120);
7628
+ const space = cut.lastIndexOf(" ");
7629
+ return (space > 60 ? cut.slice(0, space) : cut).replace(/[\\s,;:(]+$/, "") + "…";
7630
+ }
7631
+
7632
+ /** Build a private capture runner over a detached message snapshot and provider session. */`,
7633
+ legacyPatched: ` createSessionId?: () => string;
7634
+ /** Receives the capture run's successful \`learn\`/\`manage_skill\` saves so the session can show them. */
7635
+ onCaptured?: (saved: string[]) => void;
7636
+ }
7637
+
7420
7638
  /**
7421
7639
  * Summarize a capture run's successful \`learn\`/\`manage_skill\` calls. Each call is paired with its
7422
7640
  * result, so a rejected or failed save is never reported as stored.
@@ -7464,11 +7682,25 @@ export function summarizeAutoLearnSaved(messages: readonly AgentMessage[], from:
7464
7682
  },
7465
7683
  {
7466
7684
  // 저장 결과를 보이는 custom 메시지로 남긴다. idle이면 턴을 시작하지 않고 붙이고, 진행 중이면 aside로 끼운다.
7685
+ // r2: 머리글을 "[교훈 자동 저장] N건"으로 줄이고 3건까지만 보인다. legacyPatched는 r1 적용본.
7467
7686
  file: "src/sdk.ts",
7468
- marker: ` { customType: "autolearn-saved", content, display: true, attribution: "agent" },`,
7687
+ marker: ` const content = ["[교훈 자동 저장] " + saved.length + "건", ...shown].join("\\n");`,
7469
7688
  anchor: ` const runAutoLearnCapture = createAutoLearnCaptureRunner({
7470
7689
  sourceAgent: agent,`,
7471
7690
  patched: ` const runAutoLearnCapture = createAutoLearnCaptureRunner({
7691
+ sourceAgent: agent,
7692
+ onCaptured: saved => {
7693
+ const shown = saved.slice(0, 3).map(line => "- " + line);
7694
+ if (saved.length > 3) shown.push("- 외 " + (saved.length - 3) + "건");
7695
+ const content = ["[교훈 자동 저장] " + saved.length + "건", ...shown].join("\\n");
7696
+ void session
7697
+ .sendCustomMessage(
7698
+ { customType: "autolearn-saved", content, display: true, attribution: "agent" },
7699
+ session.isStreaming ? { deliverAs: "aside" } : undefined,
7700
+ )
7701
+ .catch(error => logger.warn("Failed to show auto-learn capture result", { error: String(error) }));
7702
+ },`,
7703
+ legacyPatched: ` const runAutoLearnCapture = createAutoLearnCaptureRunner({
7472
7704
  sourceAgent: agent,
7473
7705
  onCaptured: saved => {
7474
7706
  const content = ["[교훈 자동 저장] 이번 턴이 끝난 뒤 별도 capture가 저장했다.", ...saved.map(line => "- " + line)].join("\\n");
@@ -7480,6 +7712,14 @@ export function summarizeAutoLearnSaved(messages: readonly AgentMessage[], from:
7480
7712
  .catch(error => logger.warn("Failed to show auto-learn capture result", { error: String(error) }));
7481
7713
  },`,
7482
7714
  },
7715
+ {
7716
+ // capture가 교훈 맨 앞에 짧은 제목 한 문장을 쓰게 한다(2026-09-30). 알림은 그 첫 문장을 보여 주므로,
7717
+ // 긴 설명형 첫 문장이 잘려 "…"로 끝나지 않는다. 기억 검색에도 제목이 앞에 오는 편이 낫다.
7718
+ file: "src/prompts/system/autolearn-nudge-autocontinue.md",
7719
+ marker: "Begin each `learn` memory with one short headline sentence",
7720
+ anchor: "remember with `learn` when memory enabled. If nothing worth keeping, do nothing.",
7721
+ patched: "remember with `learn` when memory enabled. Begin each `learn` memory with one short headline sentence (under 80 characters) that states the lesson, then give the details. If nothing worth keeping, do nothing.",
7722
+ },
7483
7723
  ];
7484
7724
  // EDITS 문자열의 줄 끝을 LF로 통일한다. 이 파일의 작업 사본이 CRLF여도 core 파일(LF)과
7485
7725
  // 비교·치환이 어긋나지 않는다. core 파일 자체의 줄 끝은 건드리지 않는다.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cuelo",
3
- "version": "0.6.4",
3
+ "version": "0.6.5",
4
4
  "description": "CUELO — open-source AI coding agent workspace and harness for omp (oh-my-pi)",
5
5
  "homepage": "https://github.com/gim47656-ship-it/CUELO#readme",
6
6
  "repository": {
@@ -30,7 +30,7 @@
30
30
  "license": "MIT",
31
31
  "cueloBuild": {
32
32
  "distribution": "cuelo",
33
- "coreVersion": "18.4.3"
33
+ "coreVersion": "18.4.4"
34
34
  },
35
35
  "engines": {
36
36
  "node": ">=22.19.0",
@@ -87,11 +87,11 @@
87
87
  "test:coverage": "bun test --coverage ./app ./components ./hooks ./lib ./public ./bin ./install.test.mjs"
88
88
  },
89
89
  "dependencies": {
90
- "@oh-my-pi/pi-agent-core": "18.4.3",
91
- "@oh-my-pi/pi-ai": "18.4.3",
92
- "@oh-my-pi/pi-coding-agent": "18.4.3",
93
- "@oh-my-pi/pi-tui": "18.4.3",
94
- "@oh-my-pi/pi-utils": "18.4.3",
90
+ "@oh-my-pi/pi-agent-core": "18.4.4",
91
+ "@oh-my-pi/pi-ai": "18.4.4",
92
+ "@oh-my-pi/pi-coding-agent": "18.4.4",
93
+ "@oh-my-pi/pi-tui": "18.4.4",
94
+ "@oh-my-pi/pi-utils": "18.4.4",
95
95
  "@seed-design/css": "2.8.0",
96
96
  "@seed-design/react": "2.4.1",
97
97
  "@seed-design/tailwind4-theme": "2.4.0",