aptly 0.1.0

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 (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +45 -0
  3. package/README.ja.md +45 -0
  4. package/README.md +45 -0
  5. package/README.zh-CN.md +45 -0
  6. package/dist/bin/aptly.d.ts +2 -0
  7. package/dist/bin/aptly.js +122 -0
  8. package/dist/src/automatic-hook.d.ts +42 -0
  9. package/dist/src/automatic-hook.js +232 -0
  10. package/dist/src/catalog.d.ts +19 -0
  11. package/dist/src/catalog.js +135 -0
  12. package/dist/src/config.d.ts +24 -0
  13. package/dist/src/config.js +75 -0
  14. package/dist/src/core.d.ts +50 -0
  15. package/dist/src/core.js +324 -0
  16. package/dist/src/diagnostics.d.ts +11 -0
  17. package/dist/src/diagnostics.js +68 -0
  18. package/dist/src/files.d.ts +10 -0
  19. package/dist/src/files.js +70 -0
  20. package/dist/src/hook.d.ts +20 -0
  21. package/dist/src/hook.js +102 -0
  22. package/dist/src/inspect.d.ts +31 -0
  23. package/dist/src/inspect.js +219 -0
  24. package/dist/src/model-info.d.ts +32 -0
  25. package/dist/src/model-info.js +223 -0
  26. package/dist/src/provenance.d.ts +62 -0
  27. package/dist/src/provenance.js +266 -0
  28. package/dist/src/provider.d.ts +9 -0
  29. package/dist/src/provider.js +80 -0
  30. package/dist/src/report.d.ts +51 -0
  31. package/dist/src/report.js +177 -0
  32. package/dist/src/runtime.d.ts +8 -0
  33. package/dist/src/runtime.js +22 -0
  34. package/dist/src/server.d.ts +7 -0
  35. package/dist/src/server.js +80 -0
  36. package/dist/src/setup.d.ts +22 -0
  37. package/dist/src/setup.js +408 -0
  38. package/dist/src/types.d.ts +76 -0
  39. package/dist/src/types.js +2 -0
  40. package/dist/src/version.d.ts +1 -0
  41. package/dist/src/version.js +1 -0
  42. package/docs/ADVISORY_USAGE.md +196 -0
  43. package/docs/DRAFT_0_1_0.md +55 -0
  44. package/docs/guide/en.md +190 -0
  45. package/docs/guide/ja.md +190 -0
  46. package/docs/guide/ko.md +190 -0
  47. package/docs/guide/zh-CN.md +190 -0
  48. package/examples/claude-mcp.json +15 -0
  49. package/examples/codex-mcp.toml +9 -0
  50. package/examples/recommend.claude.json +9 -0
  51. package/examples/recommend.codex.json +9 -0
  52. package/examples/router.shadow.json +5 -0
  53. package/examples/settings-inventory.json +5 -0
  54. package/package.json +75 -0
@@ -0,0 +1,196 @@
1
+ # 추천 사용 안내
2
+
3
+ Aptly는 작업 요약을 분류해 허용된 실행 프로필을 제안해요. `recommend_agent`와 CLI `recommend`는 같은 추천 전용 경로를 사용해요. `application: advisory_only`는 호스트 설정 확인·모델 사용 권한·자식 실행 승인이 아니에요. 프로필을 사용하기로 결정했다면 기존 호스트에서 모델·effort·역할·권한을 확인하고 명시적으로 선택해요.
4
+
5
+ 호스트 내부 패치는 보류 상태예요. 이 안내의 추천을 받기 위해 private host envelope나 `provenance_verified:true`가 필요하지 않아요. 기존 Hook의 엄격한 자동 적용 조건은 그대로이며 기본 shadow Hook은 추천 UI가 아니에요.
6
+
7
+ ## 키 없는 fixture 추천
8
+
9
+ 프로젝트 루트에서 Node 24 이상을 사용해요. fixture는 로컬 규칙으로 만든 합성 결과이며 Decisions API의 판단이나 품질 평가가 아니에요.
10
+
11
+ ```sh
12
+ ROUTER_PROVIDER=fixture node bin/aptly.ts recommend < examples/recommend.codex.json
13
+ ROUTER_PROVIDER=fixture node bin/aptly.ts recommend < examples/recommend.claude.json
14
+ ```
15
+
16
+ 첫 예제의 fixture 출력에는 아래 필드가 포함돼요. 버전·지연·그 밖의 metadata를 생략한 예예요.
17
+
18
+ ```json
19
+ {
20
+ "status": "recommended",
21
+ "reason": "allowed_profile",
22
+ "application": "advisory_only",
23
+ "profile_id": "codex-simple",
24
+ "profile": {
25
+ "id": "codex-simple",
26
+ "client": "codex",
27
+ "model": "gpt-6-luna",
28
+ "reasoning_effort": "max"
29
+ },
30
+ "provider": "fixture"
31
+ }
32
+ ```
33
+
34
+ Claude 예제의 fixture는 `claude-simple`을 반환해요. Claude 프로필에는 reasoning_effort를 추가하지 않아요. 결과의 model 문자열은 현재 호스트나 계정의 접근 가능성을 인증하지 않아요.
35
+
36
+ 빌드한 CLI에도 같은 JSON을 입력할 수 있어요.
37
+
38
+ ```sh
39
+ npm run build
40
+ ROUTER_PROVIDER=fixture node dist/bin/aptly.js recommend < examples/recommend.codex.json
41
+ node dist/bin/aptly.js recommend --help
42
+ ```
43
+
44
+ 검토한 로컬 npm archive를 별도 설치 디렉터리에 수동 설치한 경우에는 그 설치 위치의 `aptly recommend`에도 같은 요청을 전달해요. package registry에 게시된 패키지로 간주하지 않으며 전역 설치는 필요하지 않아요. archive의 정확한 이름·지원 범위는 해당 릴리스 기록을 확인해요. source checkout과 npm archive는 구조가 달라 설치된 node_modules에서 bin/aptly.ts를 직접 실행하지 않아요.
45
+
46
+ ### 검토한 로컬 archive를 수동 설치하기
47
+
48
+ 아래 경로는 설명용 절대 경로예요. 실제로 검토한 최신 archive와 새 설치 디렉터리로 바꿔요. 기존 브랜딩 시점 archive를 최신 검증 후보로 간주하지 않아요. source archive를 사용하는 경우에는 풀어 둔 프로젝트에서 앞의 source 명령을 사용해요.
49
+
50
+ ```sh
51
+ aptly_install=/absolute/private-aptly-install
52
+ aptly_archive=/absolute/reviewed/aptly-0.1.0.tgz
53
+ mkdir -m 700 "$aptly_install"
54
+ npm install --ignore-scripts --no-audit --no-fund --prefix "$aptly_install" "$aptly_archive"
55
+ ROUTER_PROVIDER=fixture "$aptly_install/node_modules/.bin/aptly" recommend < "$aptly_install/node_modules/aptly/examples/recommend.codex.json"
56
+ ```
57
+
58
+ 설치한 실행 파일에 `config claude` 또는 `config codex`를 실행하면 그 설치 위치의 compiled server를 가리키는 조각을 얻어요. 조각 출력 뒤 실제 등록은 사용자가 검토해 진행해요.
59
+
60
+ ## 요청 계약
61
+
62
+ | 필드 | 의미 |
63
+ |---|---|
64
+ | request_id | 같은 입력·정책·원장으로 재조회할 때 재사용할 ID. 서로 다른 작업에 재사용하지 않아요 |
65
+ | client | codex 또는 claude |
66
+ | task.summary | 분류에 필요한 최소 요약. 키·전체 대화·private 원문을 넣지 않아요 |
67
+ | task.operation | read_only, write, review, analysis 중 하나 |
68
+ | task.context_sufficient | 요약에 판단에 필요한 맥락을 제공했는지 호출자가 표현해요. false면 추가 맥락을 요청하며 호스트 검증을 뜻하지 않아요 |
69
+ | eligible_profile_ids | 선택 사항. 생략하면 해당 client의 현재 설정된 카탈로그를 사용해요. 지정하면 그 안에서 후보를 제한해요 |
70
+ | constraints | 선택 사항. 계획용 추천에서 호출자가 보존해야 한다고 선언한 model_pinned, effort_pinned, role_pinned, fork_inherits boolean이에요 |
71
+
72
+ constraints를 생략하면 **이 독립적인 계획 요청에 보존 조건을 선언하지 않은 것**으로 처리해요. 실제 호스트 설정에 pin이나 역할이 없다고 검사한 결과는 아니에요. 하나라도 true이면 기존 선택을 보존하고 분류를 진행하지 않아요. 잘못된 타입·지원하지 않는 후보는 안전한 결과로 거부해요. legacy `provenance_verified` 입력은 추천 경로의 권한 근거가 아니며 새 요청에서는 사용하지 않아요.
73
+
74
+ 기본 profile ID는 Codex의 `codex-simple`·`codex-routine`·`codex-deep`, Claude의 `claude-simple`·`claude-routine`·`claude-deep`예요. `eligible_profile_ids`에는 환경변수로 활성화한 후보 안의 ID만 지정해요. 환경에서 제외한 모델이나 다른 client의 프로필을 요청해 다시 활성화할 수 없어요. 현재 목록은 capabilities로 조회해요.
75
+
76
+ ```sh
77
+ node bin/aptly.ts capabilities
78
+ ```
79
+
80
+ ## 후보 환경변수
81
+
82
+ | 등급·프로필 | Claude 모델 | Codex 모델·effort |
83
+ |---|---|---|
84
+ | simple | `claude-simple`: haiku | `codex-simple`: gpt-6-luna / max |
85
+ | routine | `claude-routine`: sonnet | `codex-routine`: gpt-6.1-terra / high |
86
+ | deep | `claude-deep`: opus | `codex-deep`: gpt-6.1-sol / medium |
87
+
88
+ 프로세스 환경에서 후보를 좁힐 수 있어요. 모델명 배열이며 profile ID 배열은 아니에요.
89
+
90
+ ```sh
91
+ export APTLY_CLAUDE_MODELS='["haiku","sonnet","opus"]'
92
+ export APTLY_CODEX_MODELS='["gpt-6-luna","gpt-6.1-terra","gpt-6.1-sol"]'
93
+ ```
94
+
95
+ MCP 설정 JSON의 `env` 값에는 위 배열을 JSON 문자열로 넣어요. 실제 등록 형식은 [Claude 예제](../examples/claude-mcp.json)와 [Codex 예제](../examples/codex-mcp.toml)를 참고해요.
96
+
97
+ - 미지정: 위 세 모델을 기본 후보로 사용해요.
98
+ - `[]`: 해당 client에서 추천을 비활성화하고 provider를 호출하지 않아요. 둘 다 비활성화할 수도 있어요.
99
+ - 빈 환경변수 문자열, 배열 안 빈 문자열·중복·비문자열, 잘못된 JSON·지원하지 않는 ID: 명확한 설정 오류예요. 임의 모델을 자동 허용하지 않아요.
100
+ - `CLAUDE_MODELS`와 `CODEX_MODELS`는 각각 기본 이름의 별칭이에요. 같은 제공사에 두 이름을 동시에 설정하면 값이 같아도 오류예요. `CODEX_MODESL`은 오타이며 지원 이름이 아니에요.
101
+
102
+ 선택적으로 `gpt-6-astra`를 넣으면 `codex-frontier` / high, `fable`을 넣으면 `claude-frontier`가 후보에 들어가요. 이 둘은 기본 목록에는 없어요. 다른 모델을 문서에서 찾았다는 사실만으로 초안의 유한한 지원 목록이 확장되지는 않아요.
103
+
104
+ 이 설정은 추천 후보만 바꿔요. 기존 모델·effort·역할·fork·resume·조직 권한을 덮어쓰는 설정은 아니에요. Claude effort는 이 프리셋에서 지정하지 않아요. Codex effort는 이번에 정한 프로필 기본값이며 모델 배열에 effort를 함께 적는 형식은 지원하지 않아요. 높은 effort와 저렴한 모델의 조합이 항상 최소 토큰·최저 비용이라는 주장은 하지 않아요.
105
+
106
+ ## 공식 모델 정보와 캐시
107
+
108
+ Decisions에는 각 후보의 모델명·effort·동봉한 공식 특성·로컬 등급 기준을 짧은 설명으로 전달해요. 기본 정보만으로 실행할 수 있고 추천할 때마다 문서를 fetch하지 않아요.
109
+
110
+ 공식 Markdown을 명시적으로 갱신할 수 있어요. 출력 위치의 parent는 현재 사용자 소유의 private 디렉터리로 먼저 준비해요. 아래 절대 경로는 설명용 예예요.
111
+
112
+ ```sh
113
+ mkdir -m 700 /absolute/private-aptly-state
114
+ aptly catalog refresh --output /absolute/private-aptly-state/model-info.json
115
+ export APTLY_MODEL_INFO_PATH=/absolute/private-aptly-state/model-info.json
116
+ ```
117
+
118
+ source checkout에서 실행할 때는 `aptly` 대신 `node bin/aptly.ts`, compiled checkout에서는 `node dist/bin/aptly.js`를 사용해요. 갱신은 다음 고정 URL만 읽으며 API 키·사용자 작업 요약·인증 정보를 보내지 않아요.
119
+
120
+ - https://code.claude.com/docs/en/model-config.md
121
+ - https://learn.chatgpt.com/docs/models.md
122
+
123
+ 파싱한 모델 설명과 출처 hash만 정규화해 저장하고 문서 원문이나 문서의 명령을 실행하지 않아요. 갱신 실패 시 기존 파일을 유지해요. 프로세스 시작 시 `APTLY_MODEL_INFO_PATH`를 읽고, 7일을 넘긴 캐시·잘못된 파일·조회 불가는 동봉한 설명으로 대체해요. 갱신 파일을 바꾼 뒤 기존 MCP 프로세스가 자동으로 새 정보를 쓰지는 않으므로 재시작해요. capabilities/catalog 출력에서 현재 목록과 정보 source 상태를 확인할 수 있어요.
124
+
125
+ 문서·캐시의 특성 설명은 추천용 자료이고 호스트 설정이나 계정의 실제 모델 사용 권한을 인증하지 않아요. 모델 목록·실효 설명이 바뀌면 카탈로그 버전과 요청 digest가 달라져 이전 결과가 다른 후보에 잘못 재사용되지 않도록 해요. 기존 원장에 충돌이 나도 unknown 요청을 새 ID로 자동 재호출하지 않아요.
126
+
127
+ ## 결과별 다음 행동
128
+
129
+ | status | 예시 reason | 다음 행동 |
130
+ |---|---|---|
131
+ | recommended | allowed_profile | 제안된 profile을 검토해요. 실행은 기존 호스트에서 별도로 결정해요 |
132
+ | keep_current | preserved_constraints, provider_abstained | 명시 선택을 유지하거나 추천을 보류해요. 보존 flag를 지워 강제로 재분류하지 않아요 |
133
+ | needs_context | insufficient_context, provider_needs_context | 필요한 요약을 보완하고 변경된 요청에는 새 ID를 사용해요 |
134
+ | unavailable | invalid_input, unauthorized, rate_limited, timeout, ledger_locked, outcome_unknown | 고정 reason을 확인해 아래 재조회·복구 규칙을 따라요 |
135
+
136
+ 아래는 서로 다른 입력의 예예요. 원래 요청과 ID를 구분했어요.
137
+
138
+ ```json
139
+ {
140
+ "request_id": "demo-preserve-001",
141
+ "client": "codex",
142
+ "task": {
143
+ "summary": "Sort the supplied numbers [3, 1, 2].",
144
+ "operation": "analysis",
145
+ "context_sufficient": true
146
+ },
147
+ "constraints": { "model_pinned": true }
148
+ }
149
+ ```
150
+
151
+ 이 요청은 `keep_current`/`preserved_constraints`예요. 별도의 `request_id: demo-context-001` 요청에서 constraints를 생략하고 task.context_sufficient를 false로 두면 `needs_context`/`insufficient_context`가 돼요. MCP의 인자 schema 오류는 도구 validation 오류로 반환될 수 있어요. 형식 오류나 네트워크 오류의 실제 요청을 일부러 유료로 반복할 필요는 없어요. 관련 실패 동작은 mock 회귀로 확인해요.
152
+
153
+ ## MCP에서 수동 호출
154
+
155
+ 먼저 현재 설치 위치에 맞는 등록 조각을 출력하고 사용자 호스트 설정에서 검토해 등록해요. 이 명령은 설정을 수정하거나 Hook을 설치하지 않아요.
156
+
157
+ ```sh
158
+ node bin/aptly.ts config claude
159
+ node bin/aptly.ts config codex
160
+ ```
161
+
162
+ MCP client에서 `recommend_agent`를 선택하고 예제 JSON 전체를 도구 인자로 전달해요. request_id/client/task는 CLI와 같아요. 반환된 structuredContent의 status·reason·profile을 검토해요. 클라이언트가 표시하는 tool namespace는 등록한 MCP 서버 key `aptly`를 기준으로 해요.
163
+
164
+ 생성된 Claude 등록 조각은 fixture를 명시해요. 생성된 Codex 조각은 ROUTER_PROVIDER 등의 env_vars를 전달하며, 값이 없으면 runtime 기본값 fixture를 사용해요. 저장소의 Claude/Codex examples는 둘 다 fixture를 명시해요. 실제 API를 쓰려면 이 고정 fixture 설정을 확인하고 MCP 프로세스에 Decisions 환경을 전달해야 해요. 부모 셸의 환경을 바꿨다는 사실만으로 이미 실행 중인 MCP 서버가 바뀌었다고 가정하지 않아요.
165
+
166
+ `get_routing_precedence`와 `route_spawn_hook`는 현재 호출의 보존·적용 경계를 다루는 별도 경로예요. 작업 요약을 `route_spawn_hook`에 넣는 것만으로 host evidence gate가 풀리지 않으며, shadow 출력 `{}`를 추천 결과로 읽지 않아요.
167
+
168
+ ## Decisions 환경 준비
169
+
170
+ 키는 사용자 자격 저장소나 안전한 실행 환경에서 **서버 프로세스의 OPENAI_API_KEY**로 주입해요. 키를 요청 JSON, CLI 인자, MCP 설정 JSON, 소스·로그·임시 파일에 쓰지 않아요. 이 문서는 특정 자격 저장소를 요구하지 않아요.
171
+
172
+ 아래 예제에서 private 디렉터리는 먼저 직접 선택해요. ledger 경로는 절대 경로여야 하고 부모 디렉터리는 현재 사용자 소유·권한 700이어야 해요. `/absolute/private-aptly-state`는 실제 사용할 위치로 바꾸는 설명용 경로예요. 기존 디렉터리 권한이나 원장 파일을 임의로 바꾸지 않아요.
173
+
174
+ ```sh
175
+ mkdir -m 700 /absolute/private-aptly-state
176
+ export ROUTER_PROVIDER=decisions
177
+ export ROUTER_LEDGER_PATH=/absolute/private-aptly-state/advisory-ledger.json
178
+ # OPENAI_API_KEY는 이 프로세스의 안전한 환경 주입으로 준비해요.
179
+ node bin/aptly.ts recommend < examples/recommend.codex.json
180
+ ```
181
+
182
+ 컴파일한 CLI·MCP 서버도 같은 환경을 사용해요. `ROUTER_CONFIG_PATH`의 apply 설정이나 host context는 추천 호출에 필요하지 않아요. mode를 apply로 바꾸어 추천을 실행하는 절차도 없어요.
183
+
184
+ live 분류는 비용이 발생할 수 있어요. 원장은 요청 전에 예약하고 완료 캐시·unknown 재호출 방지에 사용해요. 로컬 예약액·호출 수 가드는 실제 청구 상한이 아니에요. 결과의 usage는 관측한 정수 토큰 수이고 latency_ms는 분류 경과 시간이에요. 누락된 사용량을 0이나 실제 청구액으로 해석하지 않아요. 분류 비용을 자식 모델의 실행 비용·구독 사용량·총비용 절감으로 표현하지 않아요.
185
+
186
+ ## 재조회와 오류 복구
187
+
188
+ - 같은 요청을 확인하려면 **같은 request_id, 입력, 정책, provider와 원장**을 유지해요. 완료 결과는 cached로 재사용돼요. 입력이나 정책을 바꾸고 같은 ID를 쓰면 request_conflict가 될 수 있어요.
189
+ - 이전 버전의 직접 추천과 새 advisory 경로는 원장 digest 용도가 달라요. 기존 ID가 request_conflict이면 원장을 보존하고 기존 완료/unknown 상태를 먼저 확인해요. 이를 해결하려고 unknown 요청에 새 ID를 발급하지 않아요.
190
+ - `outcome_unknown`은 이전 호출의 청구·결과가 불명확한 상태예요. 새 ID로 자동 재호출하거나 원장을 비워 우회하지 않아요. 기존 ID의 결과와 실행 상태부터 확인해요. 단순 재조회가 원격 API의 미확인 결과를 자동 복원해 주지는 않아요.
191
+ - `ledger_locked`이면 같은 원장을 쓰는 기존 서버·명령이 아직 실행 중인지 먼저 확인해요. 실행 중인 프로세스의 lock은 삭제하지 않아요. crash가 확인돼도 원장을 보존하고, 관련 프로세스가 모두 끝난 것을 확인한 뒤 그 원장의 stale lock만 운영자가 별도로 처리해요. 자동 lock 삭제·원장 초기화 기능은 없어요.
192
+ - 인증·429·timeout을 수정한 뒤에도 이전 요청이 unknown이면 새 ID로 무조건 재시도하지 않아요. 이 규칙은 실패 메시지가 안전한 코드로만 반환돼 원시 API 오류를 볼 수 없는 경우에도 같아요.
193
+
194
+ 진단 자료가 필요하면 `report --format both --output-dir reports`로 로컬 첨부 파일을 만들고 검토해요. 키가 존재한다는 진단은 API 인증 성공을 뜻하지 않아요. [진단 안내](REPORTING.md), [공개 준비](RELEASING.md)를 참고해요.
195
+
196
+ 설치용 npm archive에는 이 사용 안내를 동봉해요. 설계·호환성·과거 검증 등 다른 문서는 source archive에서 확인해요.
@@ -0,0 +1,55 @@
1
+ # Aptly 0.1.0 패키지 초안
2
+
3
+ 이 초안은 기존 MCP+Decisions 흐름에 사용자 요청한 후보·effort 기본값과 모델 정보 수집을 반영해요. 후속 요청으로 npm 설치 시 MCP·Hook 등록과 새 일반 서브에이전트의 기본 모델 자동 선택도 추가해요. 패키지 버전은 `0.1.0`이며 `private: true`를 유지해요. 로컬 패키지·호스트 재설치는 이번 요청 범위이고 원격 저장소 생성·push·registry 게시·전역 설치는 포함하지 않아요.
4
+
5
+ ## 반영 범위
6
+
7
+ | 등급 | Claude Code | Codex |
8
+ |---|---|---|
9
+ | simple | haiku | gpt-6-luna / max |
10
+ | routine | sonnet | gpt-6.1-terra / high |
11
+ | deep | opus | gpt-6.1-sol / medium |
12
+
13
+ 이 등급은 Aptly의 운영 프리셋이고 모델 간 동등 성능이나 최소 토큰·비용을 보장하지 않아요. 사용자 지정 후보 배열은 추천 범위를 제한하며 모델·effort·역할 pin을 덮어쓸 수 없어요. Claude effort는 이 카탈로그에서 지정하지 않아요.
14
+
15
+ - `APTLY_CLAUDE_MODELS`·`APTLY_CODEX_MODELS`: JSON 문자열 배열. 생략은 위 기본값, `[]`는 해당 client 비활성화예요. 둘 다 비활성화하면 분류 API를 호출하지 않아요.
16
+ - `CLAUDE_MODELS`·`CODEX_MODELS`: 별칭. 같은 제공사의 두 이름을 동시에 지정하면 오류예요. 오타 `CODEX_MODESL`, 잘못된 JSON·중복·비문자열·빈 문자열·지원하지 않는 ID도 안전한 설정 오류예요.
17
+ - 선택 후보 `fable`·`gpt-6-astra`: 각각 claude-frontier·codex-frontier(high)이며 기본 목록에는 없어요. 문서가 소개하는 모든 모델을 자동 추가하지 않아요.
18
+ - 실제 모델명·Codex effort·공식 특성·등급의 작업 기준을 Decisions의 각 선택지 설명에 함께 넣어요. 모델 목록과 실효 설명의 변경은 카탈로그 version/digest에 반영해요. 과거 4개 또는 현재 8개 tuple의 저장 기록은 읽을 수 있게 보존하지만, 캐시 재사용은 현재 후보의 전체 model/effort·version·digest가 맞아야 해요. 필터 변경 때문에 원장을 지우지 않으며 이전 unknown 요청은 재호출하지 않아요.
19
+ - `aptly catalog refresh --output ABS_PATH`: 고정된 공식 Markdown 두 개를 읽고 짧은 정보만 정규화해 private 파일에 저장해요. API 키나 작업 요약을 보내지 않아요.
20
+ - `APTLY_MODEL_INFO_PATH`: 시작 시 읽는 정규화 cache 경로예요. 7일 TTL을 넘거나 cache를 읽을 수 없으면 동봉 설명으로 돌아가고, explicit refresh 실패는 기존 파일을 보존해요. 추천마다 fetch하지 않아요.
21
+
22
+ 가져오는 공식 문서는 [Claude Code 모델 설정](https://code.claude.com/docs/en/model-config.md)과 [OpenAI 모델 안내](https://learn.chatgpt.com/docs/models.md)예요. 공식 설명은 호스트 계정의 실제 모델 가용성이나 실행 권한을 증명하지 않아요. [사용 안내](ADVISORY_USAGE.md)에 source·compiled·MCP 호출과 환경 설정을 정리했어요.
23
+
24
+ ## 후속 반영 — 설치와 자동 Hook
25
+
26
+ 사용자가 명시 승인한 정책은 **모델·effort를 명시하지 않은 새 일반 서브에이전트의 기본값만 Aptly가 선택**하는 `defaults`예요. 그 범위에서 호스트 기본 모델보다 Aptly의 선택을 우선해요. 호스트의 모든 설정 출처를 관측했다는 선언으로 취급하지 않아요.
27
+
28
+ - npm `postinstall`은 현재 설정이 있는 Claude Code·Codex에 MCP와 서브에이전트용 `PreToolUse` Hook을 등록해요. `CI`·`APTLY_SKIP_SETUP=1`·npm `--ignore-scripts`에서는 자동 등록을 건너뛰어요. `CODEX_HOME`·`CLAUDE_CONFIG_DIR`을 따라요.
29
+ - `aptly setup --client codex|claude|all --mode defaults|advisory`로 직접 등록해요. `advisory`는 추천만 전달하고 인자를 변경하지 않아요. `aptly remove --client codex|claude|all`은 소유 기록과 일치하는 항목만 제거하며 변경된 항목은 보존해요. mode를 바꿀 때는 기존 등록을 제거한 뒤 다시 등록해요.
30
+ - 기존 MCP·Hook·알 수 없는 설정, 기존 Aptly의 실행 명령·환경을 보존해요. 새 MCP는 decisions와 호스트별 private 원장을 지정해요. 키는 읽어 저장하지 않고 MCP 프로세스의 안전한 환경에서 전달해요. 키가 없거나 추천에 실패하면 원래 호출을 유지해요. 기존 fixture 설정은 유료 모드로 자동 전환하지 않으며 fixture 결과로 기본 모델을 자동 변경하지 않아요.
31
+ - Codex는 `/hooks`에서 추가된 Hook의 정확한 정의를 한 번 검토하고 신뢰 승인해야 해요. 설치 프로그램은 신뢰 설정을 조작하거나 이 절차를 우회하지 않아요. 호스트 새 세션·MCP 재시작으로 설정을 읽게 해요.
32
+ - 명시 모델·effort·고정 역할·resume·이력 상속 fork는 분류 전에 보존해요. Codex는 `fork_turns: "none"`, Claude Code는 `subagent_type: "general-purpose"`인 새 일반 호출이 대상이에요. 모델 외 권한·승인·샌드박스·MCP 권한은 변경하지 않아요.
33
+ - 작업 라벨과 제공된 message/prompt의 첫 문단에서 최대 4,000바이트의 요약만 분류해요. transcript·설정·업무 파일을 수집하지 않아요. 호출별 입력·정책·카탈로그를 결합하고 응답 후 재확인해요. 같은 native 호출의 변경된 입력이나 unknown 결과를 새 유료 요청 ID로 우회하지 않아요.
34
+
35
+ ## 유지하는 경계
36
+
37
+ 직접 추천은 advisory_only이고 실제 자식 실행은 기존 호스트가 담당해요. 새 operator 정책과 기존 host evidence 기반 경로를 분리해요. 자동 정책이 없는 기존 shadow/apply 경로의 미확인 출처 보존과 검증 조건은 유지해요. 완료 요청은 재사용하고 unknown 유료 요청을 새 ID로 자동 재호출하지 않아요. 호스트 내부 패치·broker/원자적 dispatch 연구는 계속 보류해요. 새 Hook의 실제 native 적용 여부는 별도 실행 메타데이터로 확인해야 해요.
38
+
39
+ IMP-001 중 이번에 명시한 추천 후보 목록 설정만 반영해요. 명시 선택 덮어쓰기·추천 제외 목록·동작 제외 경로 등 나머지 정책 확장은 이번 초안에 포함하지 않아요. 예전에 진행한 session-peer 번역 시험은 추천·spawn 시험이며 원본 README 수정은 범위가 아니었고 실제 저장소는 수정하지 않았어요.
40
+
41
+ ## 전달물과 검증 상태
42
+
43
+ 신뢰 승인 후 Codex native 후속 점검에서 Hook에 `message`가 암호문으로 전달되는 환경을 확인했어요. 원문 해독·호스트 패치 없이, 이런 호출은 충분히 구체적인 평문 `task_name`의 underscore를 공백으로 정규화한 요약만 사용하도록 보완해요. 최소 여섯 부분·24바이트·세 개 이상의 영문 단어를 요구하며 짧은 이름·잘못된 이름·누락된 본문은 계속 보존해요. 기존 평문 본문 경로와 pin·fork·resume 보호는 유지해요. 이 작업 이름만으로 실제 본문 전체를 확인했다고 선언하지 않아요. `get_routing_capabilities.automatic_hook.last_result`에 마지막 처리 사유를 제공하며 본문·경로·설정 값을 기록하지 않아요.
44
+
45
+ 이 후속 후보는 `.release/0.1.0-hooks-diagnostics/`에 별도 보관해요. 앞선 hooks-draft 검증은 당시 결과로 남기고 native 후속 결과를 소급해 덮어쓰지 않아요.
46
+
47
+ 후보·모델 정보 구현 시점의 전달물은 `.release/0.1.0-draft/`에 보관해요. Hook 후속 후보는 `.release/0.1.0-hooks-draft/`에 별도로 준비해요. npm/source archive·파일 목록·SHA-256 manifest·설치 검증 JSON을 해당 후보별로 확인해요. 기존 후보와 과거 `.release/advisory-preview-20261105/` archive는 덮어쓰지 않아요.
48
+
49
+ Hook 추가 전 후보·모델 정보 초안은 Node 무료 회귀 **226/226**, strict typecheck/build, fixture stdio MCP smoke, release 파일 검사(소스 후보130개·npm43개)를 통과했어요. 실제 공식 Markdown 두 곳의 refresh·8개 fact 정규화와 fresh cache 로딩도 확인했어요. 후보 필터·비활성화·오류, 메타데이터 크기/시간/TTL/파일 경계, 설명 변경의 cache identity와 과거 원장 보존을 포함해요. 이 수치는 새 설치·자동 Hook 구현의 검증 수치가 아니에요.
50
+
51
+ 최종 archive는 별도의 source/npm 설치에서 CLI·기본 6개·환경 필터·compiled config/report·MCP 네 도구와 최소 추천 예제를 확인하고 `verification.json`에 기록해요. npm에는 사용 안내와 이 초안 안내를 동봉하며 native 연구·test-support·private 자료는 포함하지 않아요. source archive에는 보류 표시한 연구 기록을 보존해요.
52
+
53
+ 후보·모델 정보 초안 작업에서는 새 후보의 유료 Decisions 호출·실제 child 실행·호스트 내부 실험을 하지 않았어요. 설치·자동 Hook 후속 작업의 실제 검증 상태는 해당 후보의 기록을 확인해요. 문서나 합성 시험만으로 native 적용 성공을 주장하지 않아요. 새 세 단계의 업무별 품질·토큰 절감·비용 최적성은 아직 평가하지 않았어요. 비용 관련 기본값은 사용자 요청한 프리셋이며 측정된 최저 비용 순위가 아니에요.
54
+
55
+ 과거 Node 193개 회귀·실제 Decisions 11회·초기 범주 8/8 기록은 **이전 네 프로필 카탈로그**의 검증이에요. 이를 이번 여섯 기본 프로필이나 새 환경변수·cache의 검증으로 바꾸어 표시하지 않아요. 그 기록은 [이전 실제 결과](ADVISORY_RESULTS.md)에 보존해요.
@@ -0,0 +1,190 @@
1
+ # Aptly user guide
2
+
3
+ [한국어](ko.md) · **English** · [简体中文](zh-CN.md) · [日本語](ja.md)
4
+
5
+ [Back to README](../../README.en.md)
6
+
7
+ Korean is the canonical documentation. This guide covers the local 0.1.0 package draft. It does not indicate publication to the npm registry or model availability for every account. Claude Code or Codex controls native subagent execution, approvals, sandboxing, and MCP permissions.
8
+
9
+ ## Installation
10
+
11
+ You need Node.js 24 or later and Claude Code or Codex. Specify a reviewed archive and installation location.
12
+
13
+ ```sh
14
+ aptly_install=/absolute/aptly-local
15
+ npm install --prefix "$aptly_install" /absolute/path/aptly-0.1.0.tgz
16
+ "$aptly_install/node_modules/.bin/aptly" setup --client all --mode defaults
17
+ ```
18
+
19
+ In later examples, `aptly` refers to the executable in this installation. Use its full path if it is not on PATH. Do not execute TypeScript source directly from an installed `node_modules` directory.
20
+
21
+ `postinstall` registers the MCP server and a `PreToolUse` Hook in the current user's existing host configuration directories. If you use `CODEX_HOME` or `CLAUDE_CONFIG_DIR`, pass the same environment to installation. Automatic registration skips host directories that do not exist; explicit `setup` can create them. To register only one host, use `--client codex` or `--client claude`.
22
+
23
+ New registrations set `ROUTER_PROVIDER=decisions`, the Hook's `defaults` policy, and a private ledger for each host. Other settings and Hooks, and existing Aptly commands, environments, providers, and ledger paths, are preserved. Existing fixture settings are not automatically switched to paid mode.
24
+
25
+ ### Keys and restarting
26
+
27
+ `OPENAI_API_KEY` is the classification key. It is separate from the existing login or subscription authentication used to run subagent models. **Inject it into the MCP process's environment** through your credential store or a secure execution environment. Do not put keys in command arguments, tool arguments, request JSON, MCP configuration JSON, source files, logs, or temporary files. No particular credential store is required.
28
+
29
+ Start a new host process or restart the MCP server to load registration and environment changes. Changing environment variables in another terminal does not update an already-running GUI or MCP process. **Codex requires you to review and trust the exact new Hook definition through `/hooks`.** The installer does not bypass trust review.
30
+
31
+ Missing keys and connection, authentication, or timeout errors leave the original call unchanged. Direct Decisions calls require the following environment.
32
+
33
+ | Environment variable | Purpose |
34
+ |---|---|
35
+ | `OPENAI_API_KEY` | Classification key injected through a secure execution environment |
36
+ | `ROUTER_PROVIDER=decisions` | Uses the actual classification API |
37
+ | `ROUTER_LEDGER_PATH` | Absolute ledger path inside a user-owned private directory with mode 700 |
38
+
39
+ ### Skipping registration and removing it
40
+
41
+ ```sh
42
+ APTLY_SKIP_SETUP=1 npm install --prefix "$aptly_install" /absolute/path/aptly-0.1.0.tgz
43
+ aptly setup --client codex --mode defaults
44
+ aptly remove --client all
45
+ ```
46
+
47
+ A `CI` environment or npm `--ignore-scripts` also skips automatic registration. You can register explicitly with `setup`. The `advisory` mode passes recommendations without changing arguments. To change policy, remove the existing registration, then register again with the desired `--mode`. For a `partial` result, check the error codes for each client. Removal only targets entries that match Aptly's ownership records; it preserves user changes and reports conflicts.
48
+
49
+ ## Automatic Hook
50
+
51
+ Connected, eligible calls automatically pass through `route_spawn_hook` → Decisions. The `defaults` policy lets an operator authorize Aptly's choice to take precedence over host defaults within this scope. It is not a claim that all host configuration sources have been observed.
52
+
53
+ | Host | Calls eligible for automatic selection |
54
+ |---|---|
55
+ | Codex | Fresh, general-purpose calls with no explicit model, effort, fixed role, or resume target, and `fork_turns: "none"` |
56
+ | Claude Code | Calls with `subagent_type: "general-purpose"` and no explicit model, effort, fixed role, resume target, or inherited history |
57
+
58
+ Explicit choices, roles, fork and resume settings, approvals, sandboxing, and MCP permissions are preserved. Fixture results never automatically change the default model. Insufficient context, unsupported responses, cancellation, or a changed call preserve the original arguments. Each call is bound to its input, policy, and catalog, and checked again after an asynchronous response.
59
+
60
+ The CLI without additional configuration, and existing manual configurations, default to fixture / shadow. The older host-evidence-based path without an automatic policy is distinct from the installed default-selection policy. Host-internal patches remain on hold and are not a prerequisite for direct recommendations.
61
+
62
+ | MCP tool | Purpose |
63
+ |---|---|
64
+ | `get_routing_capabilities` | Lists candidates, limits, provider, and Hook status without a paid API call |
65
+ | `recommend_agent` | Returns a direct recommendation for a task summary |
66
+ | `get_routing_precedence` | Explains preserved choices and unverified configuration sources for a call |
67
+ | `route_spawn_hook` | Selects defaults for eligible calls or passes advice only, according to the installed policy |
68
+
69
+ ## Direct recommendations
70
+
71
+ The `recommend_agent` MCP tool and CLI `recommend` accept a task summary and recommend a profile. They do not spawn subagents. Pass this JSON as MCP tool arguments, or save it to a file and provide it to the CLI through standard input.
72
+
73
+ ```json
74
+ {
75
+ "request_id": "demo-codex-sort-001",
76
+ "client": "codex",
77
+ "task": {
78
+ "summary": "Sort the supplied numbers [3, 1, 2] into ascending order. No files or network are needed.",
79
+ "operation": "analysis",
80
+ "context_sufficient": true
81
+ }
82
+ }
83
+ ```
84
+
85
+ ```sh
86
+ ROUTER_PROVIDER=fixture aptly recommend < request.json
87
+ aptly capabilities
88
+ aptly config codex
89
+ aptly config claude
90
+ ```
91
+
92
+ Fixture returns test results generated by local rules without a key, network access, or subagent execution. These are not actual Decisions judgments or quality-test results. `config` only prints a manual MCP registration fragment; `setup` changes configuration. For actual API use, prepare the key, provider, and private ledger described above.
93
+
94
+ `task.operation` is one of `read_only`, `write`, `review`, or `analysis`. `context_sufficient` means the caller has provided the summary needed for the task; it does not verify host settings. Omitting `eligible_profile_ids` uses the active catalog for that client; specifying it narrows candidates within that catalog.
95
+
96
+ If any of `model_pinned`, `effort_pinned`, `role_pinned`, or `fork_inherits` in `constraints` is true, the existing selection is preserved. Omitting these means no preservation constraints were declared for this planning request. It does not certify that the actual host has no pins. Do not use `provenance_verified` in new requests.
97
+
98
+ | status | Next action |
99
+ |---|---|
100
+ | `recommended` | Review `profile` and decide separately whether to execute it in the existing host |
101
+ | `keep_current` | Preserve the explicit selection or defer the recommendation |
102
+ | `needs_context` | Supply the necessary summary details |
103
+ | `unavailable` | Check the predefined `reason` code. Do not automatically retry a paid request with an unknown outcome under a new ID |
104
+
105
+ The direct recommendation's `application: advisory_only` does not verify host settings or model access, or authorize execution. To retrieve a completed result again, keep the same ID, input, policy, and ledger. `unknown` means the outcome is uncertain; do not blindly retry with a new ID. A new request whose input has changed because context was added uses a different ID.
106
+
107
+ ## Models and environment variables
108
+
109
+ There are six default profiles. Tiers are Aptly operational presets, not statements that the two providers' models have equivalent capabilities.
110
+
111
+ | Tier | Claude Code | Codex | Typical tasks |
112
+ |---|---|---|---|
113
+ | simple | `haiku` | `gpt-6-luna` / `max` | Extraction, format conversion, or small mechanical edits |
114
+ | routine | `sonnet` | `gpt-6.1-terra` / `high` | Context-aware writing or translation, or clearly scoped implementation |
115
+ | deep | `opus` | `gpt-6.1-sol` / `medium` | Cross-component debugging, architectural judgment, or in-depth review |
116
+
117
+ MCP `env` values are strings, so candidate arrays must also be passed as JSON strings.
118
+
119
+ ```json
120
+ {
121
+ "env": {
122
+ "APTLY_CLAUDE_MODELS": "[\"haiku\", \"sonnet\", \"opus\"]",
123
+ "APTLY_CODEX_MODELS": "[\"gpt-6-luna\", \"gpt-6.1-terra\", \"gpt-6.1-sol\"]"
124
+ }
125
+ }
126
+ ```
127
+
128
+ When unset, the defaults above apply; `[]` disables recommendations for that client. Empty strings, duplicates, unsupported models, and malformed JSON are errors. `CLAUDE_MODELS` and `CODEX_MODELS` are supported aliases, but setting both the primary name and its alias for the same provider is an error. Candidate configuration does not override existing choices or certify model access for an account.
129
+
130
+ ### Official descriptions and caching
131
+
132
+ Decisions choices include the model name, Codex effort, official characteristics, and task-tier descriptions. Bundled descriptions are available at startup; documentation is not fetched on every recommendation. Refresh explicitly to incorporate the latest official Markdown. First create a private directory you own for the output file.
133
+
134
+ ```sh
135
+ mkdir -m 700 /absolute/private-aptly-state
136
+ aptly catalog refresh --output /absolute/private-aptly-state/model-info.json
137
+ export APTLY_MODEL_INFO_PATH=/absolute/private-aptly-state/model-info.json
138
+ ```
139
+
140
+ Refresh only reads the fixed [Claude model documentation](https://code.claude.com/docs/en/model-config.md) and [OpenAI model documentation](https://learn.chatgpt.com/docs/models.md), and requires no API key. It stores short, normalized model information and preserves the existing file on failure. The cache is read at startup; an invalid cache or one older than 7 days falls back to bundled descriptions. Restart MCP after refreshing. New models appearing in documentation are not automatically added to candidates, and account access is not certified.
141
+
142
+ ## Providing context
143
+
144
+ When Codex passes a subagent `message` to the Hook as ciphertext, Aptly does not read or decrypt its body. It uses only a sufficiently descriptive plaintext `task_name` as a summary. Describe the task and its scope, for example `sort_supplied_numbers_3_1_2_ascending_without_tools`. Short or ambiguous names preserve the original model. Claude Code calls and calls that expose a plaintext body use a summary of the provided body.
145
+
146
+ Use `automatic_hook.last_result` in `get_routing_capabilities` to check the last application or preservation reason for that MCP process. `null` means it has not yet processed an automatic Hook call. Task bodies and personal configuration values are not returned.
147
+
148
+ For direct recommendations, you can include repository and file URLs alongside a summary of the scope, size, and constraints derived from reading the documents. Sending a URL string differs from providing document contents. Aptly's classification request code does not fetch repository URLs or retrieve their contents. The caller performs any required retrieval and summarization.
149
+
150
+ The current automatic Hook has no field for passing a URL separately as plaintext. Do not assume that adding a URL to an encrypted `message` forwards it for classification. Direct MCP recommendation followed by passing the profile as native arguments is distinct from automatic Hook application.
151
+
152
+ ## Cost benchmark
153
+
154
+ On 2026-11-05, the [English session-peer README](https://github.com/abruption/session-peer/blob/6e9efda7588e1df638748eeb7ad1b2707e0a3bf4/README.md) was used as the canonical source for reviewing its [Korean translation](https://github.com/abruption/session-peer/blob/6e9efda7588e1df638748eeb7ad1b2707e0a3bf4/README.ko.md). Each run received the same documents, totaling 17,964 bytes, and the same instructions to produce six suggested edits. The original files were not changed.
155
+
156
+ All three automatic Hook trials supplied with only the task name abstained with `needs_context`. Three `recommend_agent` requests with repository/file URLs, and three with URLs plus a document summary, all recommended Terra/high. The per-run averages below compare three actual Terra/high runs using the URL-plus-summary condition with the three immediately preceding Sol/high runs.
157
+
158
+ | Standard API cost conversion | Sol/high | Terra/high + classification | Reduction |
159
+ |---|---:|---:|---:|
160
+ | Observed cache usage included | $0.03229 | $0.01354 | 58.05% |
161
+ | All input priced as uncached | $0.11254 | $0.05363 | 52.35% |
162
+
163
+ These are USD conversions of measured native tokens at the [official Standard API rates](https://developers.openai.com/api/docs/pricing). They are not actual subscription invoices or measured quota deductions. The Terra figures include the [Decisions classification cost](https://developers.openai.com/api/docs/guides/decisions), $0.0000756 per run. The comparison covers the reviewing subagent and classification; document retrieval, context preparation, and main-agent research costs are excluded.
164
+
165
+ The measured workflow was **explicit MCP recommendation → native execution with the recommended profile passed as arguments**. It did not test automatic Hook URL retrieval or forwarding. The prior benchmark was reused as the control, with three samples per condition. Full-document reads, matching source quotes, and output structure were checked, but the results do not establish equal quality, statistical significance, or savings for other tasks.
166
+
167
+ ## Diagnostics and development
168
+
169
+ ```sh
170
+ aptly report --format both --output-dir reports
171
+ ```
172
+
173
+ `report` creates private Markdown and JSON files with the same ID. Review them before sharing manually; they are not uploaded automatically. Do not include keys, personal paths, environment values, task bodies, or raw errors in attachments. A report does not establish successful API authentication or verification of the host's full configuration.
174
+
175
+ Use the following commands in a source checkout. Distinguish their paths from the installed CLI examples above.
176
+
177
+ ```sh
178
+ npm ci --ignore-scripts --no-audit --no-fund
179
+ npm run typecheck
180
+ npm run build
181
+ ROUTER_PROVIDER=fixture node bin/aptly.ts recommend < examples/recommend.codex.json
182
+ ROUTER_PROVIDER=fixture node bin/aptly.ts recommend < examples/recommend.claude.json
183
+ npm test
184
+ npm run poc
185
+ npm run check:release
186
+ ```
187
+
188
+ Default regression tests and fixture smoke tests run without paid APIs or actual subagents. Release checks inspect public files, private-data patterns, and the npm file allowlist; they do not publish or push to Git. The ledger supports completed-result reuse, prevention of retries for unknown outcomes, and guards on calls, latency, and estimated cost. It is not an actual billing cap.
189
+
190
+ Design notes, historical validation, and development history remain in other source-checkout documents. This multilingual guide is the reference for installation and everyday use; it does not turn historical research claims or unimplemented plans into current features.