{"_id":"@abto-app/sdk","_rev":"2-b81709f5672fe0153eb860a38af89e26","name":"@abto-app/sdk","dist-tags":{"latest":"0.0.1"},"versions":{"0.0.1":{"name":"@abto-app/sdk","version":"0.0.1","keywords":["abto","llm","analytics","autocapture","observability"],"license":"MIT","_id":"@abto-app/sdk@0.0.1","maintainers":[{"name":"turtlehwan","email":"turtlehwan@gmail.com"}],"homepage":"https://github.com/greedy-co/abto#readme","bugs":{"url":"https://github.com/greedy-co/abto/issues"},"dist":{"shasum":"d76e54451ddae0fa1ee1dc1cf7dd5441d0a6540a","tarball":"https://registry.npmjs.org/@abto-app/sdk/-/sdk-0.0.1.tgz","fileCount":70,"integrity":"sha512-4q4GFtzZVbqkD267F1J9e+LGi28qZ4SAbE0ijAE6r4/3MSWE83CYGJ02fgKN03Pr+YG9546+hXG5z2SkM/qo7A==","signatures":[{"sig":"MEYCIQDaxY/M9J0mzQ93IdN61UF1Fz70Ml/SS+pPJHxP0aBulwIhAMF9FuYSpTx1N16Viu2OFg5lfVamGfsbvBQVRXR/q6fr","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":227482},"main":"./dist/browser/index.js","type":"module","types":"./dist/browser/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/browser/index.d.ts","import":"./dist/browser/index.js"},"./server":{"types":"./dist/server/index.d.ts","import":"./dist/server/index.js"},"./browser":{"types":"./dist/browser/index.d.ts","import":"./dist/browser/index.js"}},"gitHead":"0cf8e59de9e590a02a282aab143f77c36a1d49f5","scripts":{"test":"vitest run","build":"pnpm run clean && tsc -p tsconfig.browser.json && tsc -p tsconfig.server.json","clean":"rm -rf dist","prepack":"pnpm run build","typecheck":"tsc -p tsconfig.browser.json --noEmit && tsc -p tsconfig.server.json --noEmit","test:watch":"vitest","prepublishOnly":"pnpm run typecheck && pnpm run build"},"_npmUser":{"name":"turtlehwan","email":"turtlehwan@gmail.com"},"repository":{"url":"git+https://github.com/greedy-co/abto.git","type":"git","directory":"packages/sdk"},"_npmVersion":"11.12.1","description":"ABTO SDK — broad autocapture of user behavior x LLM cost/latency/quality attribution. Browser (.) and Node server (./server) in one package.","directories":{},"sideEffects":false,"_nodeVersion":"24.15.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^25.0.1","vitest":"^4.1.10","typescript":"^5.4.0","@types/node":"^20.14.0"},"peerDependencies":{"openai":">=4"},"peerDependenciesMeta":{"openai":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.0.1_1784864501532_0.45966211045454486","host":"s3://npm-registry-packages-npm-production"}}},"time":{"created":"2026-07-24T03:41:41.361Z","modified":"2026-07-24T03:43:30.500Z","0.0.1":"2026-07-24T03:41:41.736Z"},"bugs":{"url":"https://github.com/greedy-co/abto/issues"},"license":"MIT","homepage":"https://github.com/greedy-co/abto#readme","keywords":["abto","llm","analytics","autocapture","observability"],"repository":{"url":"git+https://github.com/greedy-co/abto.git","type":"git","directory":"packages/sdk"},"description":"ABTO SDK — broad autocapture of user behavior x LLM cost/latency/quality attribution. Browser (.) and Node server (./server) in one package.","maintainers":[{"email":"turtlehwan@gmail.com","name":"turtlehwan"},{"email":"wejust.greedy@gmail.com","name":"wejust.greedy"}],"readme":"# @abto-app/sdk\n\nABTO SDK는 브라우저에서 관측 가능한 사용자 행동을 수집하고, Gateway가 반환한 `request_id`를 통해 서버의 LLM 비용·지연 데이터와 연결한다.\n\n- `@abto-app/sdk`: Browser SDK — autocapture, custom events, AI trace\n- `@abto-app/sdk/server`: Node Server SDK — Gateway 호출과 context 전달\n\n이 문서의 앞부분은 Browser SDK를 다룬다. 브라우저와 서버는 같은 npm 패키지로 배포되지만 API와 책임은 분리되어 있다.\n\nPostHog의 autocapture, ingestion, session, schema/discovery는 ABTO 이벤트 설계의 참고 모델이다. ABTO 이벤트를 PostHog로 보내는 연동이 아니라, 검증된 수집 철학을 ABTO 독립 수집 구조에 적용한다.\n\n## 설치\n\n```bash\npnpm add @abto-app/sdk\n```\n\n## 이벤트 경계\n\nBrowser SDK가 보내는 이벤트는 두 종류다.\n\n| 종류 | 이름 | 정의 주체 | 발생 방식 |\n|---|---|---|---|\n| 시스템 이벤트 | `$`로 시작 | ABTO | SDK 자동 수집 또는 전용 API |\n| 커스텀 이벤트 | `$` 없이 제품 도메인 이름 사용 | 고객 저장소 | `client.capture()` |\n\n사용자는 `$` 이벤트나 `$` 속성을 등록할 수 없다. ABTO 시스템 이벤트도 일반 `capture()`로 보낼 수 없으며 SDK 내부 경로와 AI trace 전용 메서드만 발생시킨다.\n\n### Browser SDK 시스템 이벤트\n\n| 이벤트 | 의미 | 발생 조건 |\n|---|---|---|\n| `$pageview` | 페이지/SPA route 진입 | 초기 로드, history 변경, bfcache 복원 |\n| `$pageleave` | 페이지/route 이탈 | SPA 이동, `pagehide` |\n| `$autocapture` | DOM 상호작용 원시 사실 | click, change, submit, copy |\n| `$rageclick` | 짧은 시간의 반복 클릭 | SDK 휴리스틱 |\n| `$dead_click` | 반응이 관측되지 않은 클릭 | SDK 휴리스틱 |\n| `$ai_prompt_submitted` | 프롬프트 제출을 앱이 확인 | `trace.submitPrompt()` |\n| `$ai_response_rendered` | 응답이 UI에 렌더됨을 앱이 확인 | `trace.markResponseRendered()` |\n| `$ai_response_interacted` | 응답에 대한 명시적 행동 | `trace.captureResponseInteraction()` |\n\n`$session_start`와 `$session_end`는 보내지 않는다. 모든 이벤트의 `$session_id`와 timestamp의 최솟값·최댓값을 분석 계층에서 사용해 세션 시작, 종료, duration을 파생한다. 브라우저 종료 신호는 유실될 수 있으므로 `$session_end`를 확정 사실로 기록하지 않는다.\n\n## 커스텀 이벤트 정본: `abto.events.ts`\n\n제품 이벤트는 고객 애플리케이션 저장소의 `abto.events.ts`에 사전 등록한다. 이 파일을 코드 리뷰와 향후 CLI/CI schema push의 정본으로 사용한다.\n\n```ts\n// abto.events.ts\nimport { defineEvents } from '@abto-app/sdk';\n\nexport const events = defineEvents({\n  checkout_completed: {\n    description: '결제가 완료됨',\n    properties: {\n      order_id: { type: 'string', required: true },\n      amount: { type: 'number', required: true },\n      currency: {\n        type: 'string',\n        enum: ['KRW', 'USD'],\n        required: true,\n      },\n    },\n  },\n});\n```\n\n```ts\nimport { initAbto } from '@abto-app/sdk';\nimport { events } from './abto.events';\n\nconst abto = initAbto({\n  projectKey: 'public_project_key',\n  environment: 'development',\n  events,\n});\n\nabto.capture('checkout_completed', {\n  order_id: 'order_123',\n  amount: 49_000,\n  currency: 'KRW',\n});\n```\n\n`defineEvents()`에서 타입을 추론하므로 잘못된 이벤트 이름, required 누락, enum 위반을 개발 시점에 확인할 수 있다. 런타임 정책은 환경별로 다르다.\n\n| 환경 | 미등록 이벤트 | 등록 schema drift |\n|---|---|---|\n| `development` | 전송하고 `Discovered` 경고 | 전송하고 drift 경고 |\n| `production` | drop | required/type/enum 위반 drop |\n\n알 수 없는 추가 속성은 막지 않는다. schema가 선언한 required/type/enum만 검사해 점진적 확장을 허용한다.\n\n## 초기화와 autocapture\n\n앱 루트에서 한 번 초기화하면 autocapture가 시작된다.\n\n```ts\nconst abto = initAbto({\n  projectKey: 'public_project_key',\n  apiHost: 'https://api.abto.app',\n  environment: 'production',\n  events,\n});\n```\n\n기본 endpoint는 `${apiHost}/v1/browser/events`다. 전송 envelope는 다음 모양이다.\n\n```json\n{\n  \"sent_at\": \"2026-07-15T04:10:03.000Z\",\n  \"batch\": [\n    {\n      \"uuid\": \"019b...\",\n      \"event\": \"$autocapture\",\n      \"timestamp\": \"2026-07-15T04:10:00.000Z\",\n      \"distinct_id\": \"user_123\",\n      \"properties\": {\n        \"$event_type\": \"click\",\n        \"$elements_chain\": \"button.cta:nth-child(1)\",\n        \"$session_id\": \"019b...\",\n        \"$window_id\": \"019b...\",\n        \"$pageview_id\": \"019b...\"\n      }\n    }\n  ]\n}\n```\n\n서버의 이벤트별 응답은 event UUID를 key로 사용한다.\n\n```json\n{\n  \"results\": {\n    \"019b5b74-11d0-7000-8000-000000000001\": {\n      \"result\": \"drop\",\n      \"code\": \"schema_type_mismatch\"\n    }\n  }\n}\n```\n\n일반 `fetch`는 public project key를 Bearer header에 싣고, `sendBeacon`은 custom header를 지원하지 않으므로 `?api_key=` query를 사용한다. 서버가 이 key에서 `project_id`와 `account_id`를 결정한다. `$tenant_id`를 포함한 client property는 분석 문맥이며 인증·project 귀속 값이 아니다.\n\n수신 계약의 상한은 요청당 100 events다. SDK 기본값은 20이며 keepalive/beacon payload는 약 60 KiB 아래에서 전송한다. malformed request와 인증 실패는 요청 단위 4xx, 개별 validation/storage 실패는 2xx 응답의 UUID별 `warning`, `drop`, `retry`로 처리한다.\n\n향후 PostgreSQL ingestion은 `uuid/event/timestamp/distinct_id/properties/set/set_once`를 각각 `event_id/event_name/occurred_at/distinct_id/properties/person_set/person_set_once`로 저장하고 서버 수신 시각을 `received_at`에 기록한다. `(project_id, event_id)`는 재전송 dedup key다.\n\nannotation은 원시 `$autocapture`를 다른 이벤트로 바꾸지 않는다. 원시 상호작용을 보존하면서 분석 차원만 보강한다.\n\n```html\n<button\n  data-abto-action=\"accept\"\n  data-abto-surface=\"generator\"\n  data-abto-node-key=\"resume.make\"\n  data-abto-response-id=\"resp_123\"\n  data-abto-request-id=\"req_123\">\n  적용\n</button>\n```\n\n위 클릭은 `$autocapture`로 수집되며 `$ai_action`, `$surface`, `$node_key`, `$response_id`, `$request_id`가 함께 실린다. 업무 의미가 확정된 행동은 앱 코드에서 커스텀 이벤트 또는 AI 전용 메서드로 별도 기록한다.\n\n## 개인정보 기본값\n\nprompt, response, DOM text/value는 기본적으로 원문을 수집하지 않는다.\n\n```ts\ninitAbto({\n  projectKey: 'public_project_key',\n  events,\n  capture: {\n    prompt: 'metadata_only',\n    response: 'metadata_only',\n    mask: 'all',\n  },\n});\n```\n\n위 값들이 생략됐을 때도 같은 안전한 기본값이 적용된다.\n\n| annotation | 동작 |\n|---|---|\n| `data-abto-no-capture` | 자신과 하위 트리를 수집하지 않음 |\n| `data-abto-sensitive` | 자신과 하위 text/value를 항상 전체 마스킹 |\n| `data-abto-include` | 해당 요소의 text/value 수집을 명시적으로 허용 |\n\n`password`, `hidden` input과 카드·비밀번호·SSN 계열 필드는 annotation과 무관하게 보호한다. `full` 원문 수집은 명시적 opt-in이며 고객의 동의·보존·삭제 정책과 함께 사용해야 한다.\n\n## 브라우저에서 관측 가능한 AI 이벤트\n\n브라우저가 확실히 아는 세 가지 사실만 전용 API로 제공한다.\n\n```ts\nconst trace = abto.startLlmTrace({\n  nodeId: 'resume.make',\n  taskType: 'draft_generation',\n  surface: 'editor',\n});\n\nawait trace.submitPrompt({\n  prompt: promptText,\n  language: 'ko',\n});\n\nconst response = await fetch('/api/generate', {\n  method: 'POST',\n  headers: { 'content-type': 'application/json', ...trace.getHeaders() },\n  body: JSON.stringify({ prompt: promptText }),\n});\ntrace.attachRequestId(response);\n\nawait trace.markResponseRendered({\n  responseId: 'resp_123',\n  timeToRenderMs: 1_380,\n});\n\nawait trace.captureResponseInteraction('copied', {\n  responseId: 'resp_123',\n  source: 'copy_button',\n});\n```\n\nprovider/model/token/cost/retry/fallback, 실제 첫 토큰 시점과 request 성공·실패는 Server SDK/Gateway가 소유한다. AI task 완료·이탈은 제품마다 의미가 다르므로 커스텀 이벤트 또는 분석 파생 지표로 둔다.\n\n## 식별자와 세션\n\n| 속성 | 수명과 역할 |\n|---|---|\n| `$device_id` | 프로젝트별 브라우저 설치, localStorage 유지 |\n| `$anonymous_id` | 로그인 전 distinct identity |\n| `$user_id` | `identify()`로 연결한 제품 사용자 |\n| `$session_id` | 탭 사이에서 공유하는 논리 세션, 30분 idle 또는 24시간 max age에 회전 |\n| `$window_id` | 탭/window별 ID, sessionStorage 유지 |\n| `$pageview_id` | 페이지/SPA route 구간, pageview마다 회전 |\n| `$trace_id` | 한 사용자 행동에서 서버 호출까지 연결 |\n| `$request_id` | Gateway의 실제 provider 호출 PK |\n\n```ts\nabto.identify('user_123', 'tenant_123');\nabto.reset();        // user/tenant 제거, device 유지\nabto.forgetDevice(); // outbox와 device identity 제거\n```\n\n## 전송과 재시도\n\n- 이벤트는 localStorage outbox에 먼저 저장한다.\n- 기본적으로 최대 20개씩 `POST /v1/browser/events`로 보낸다.\n- 일반 flush는 `fetch`, 페이지 이탈은 안전 크기에서 `sendBeacon`을 우선 사용한다.\n- keepalive/beacon payload는 약 60 KiB 이내로 제한한다.\n- 408, 429, 5xx와 이벤트별 `retry`만 지수 backoff로 재시도한다.\n- 영구 4xx와 이벤트별 `drop`은 outbox에서 제거한다.\n- 이벤트별 `ok`, `warning`, `drop`, `retry` 응답을 UUID 기준으로 처리한다.\n\n## Public API (browser)\n\n`initAbto` · `defineEvents` · `identify` · `getIdentity` · `reset` · `forgetDevice` · `startLlmTrace` · `setNode` · `getTraceHeaders` · `client.capture` · `trace.submitPrompt` · `trace.markResponseRendered` · `trace.captureResponseInteraction` · `flush`.\n\n## Node Server SDK\n\nNode 서버에서는 별도 subpath를 사용한다. Browser SDK와 섞어 import하지 않는다.\n\n```ts\nimport { createAbto } from '@abto-app/sdk/server';\n\nconst abto = createAbto({\n  abtoApiKey: process.env.ABTO_API_KEY,\n  providerKeys: {\n    openai: process.env.OPENAI_API_KEY,\n  },\n  gatewayBaseURL: 'https://gateway.abto.app/v1',\n  userId: process.env.ABTO_USER_ID,\n});\n```\n\nServer SDK의 Gateway/provider 계약은 서버 SDK 문서와 해당 PR에서 관리한다.\n\n## 개발 검증\n\n```bash\npnpm test\npnpm typecheck\npnpm build\nnode ../../examples/browser-smoke/collector.mjs\n```\n\n실브라우저 검증 절차는 [`examples/browser-smoke/README.md`](../../examples/browser-smoke/README.md)를 따른다.\n","readmeFilename":"README.md"}