{"_id":"@ask-a-human/sdk","name":"@ask-a-human/sdk","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@ask-a-human/sdk","version":"0.1.0","description":"TypeScript SDK for Ask-a-Human - get human input for your AI agents","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"build":"tsup","dev":"tsup --watch","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","lint":"eslint src tests examples","lint:fix":"eslint src tests examples --fix","format":"prettier --write .","format:check":"prettier --check .","typecheck":"tsc --noEmit","clean":"rm -rf dist node_modules .turbo coverage","prepublishOnly":"npm run build && npm run test"},"keywords":["ask-a-human","human-in-the-loop","ai","llm","agent","mcp","human-feedback"],"author":{"name":"Manuel Kießling","email":"manuel@kiessling.net"},"license":"MIT","publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/dx-tooling/ask-a-human.git"},"engines":{"node":">=20.0.0"},"devDependencies":{"@types/node":"^20.11.0","@typescript-eslint/eslint-plugin":"^7.0.0","@typescript-eslint/parser":"^7.0.0","@vitest/coverage-v8":"^1.2.0","eslint":"^8.56.0","eslint-config-prettier":"^9.1.0","prettier":"^3.2.0","tsup":"^8.0.0","typescript":"^5.3.0","vitest":"^1.2.0"},"gitHead":"da9eb4c11a26fe39fc3e6f145d7b90ad2af783c8","_id":"@ask-a-human/sdk@0.1.0","bugs":{"url":"https://github.com/dx-tooling/ask-a-human/issues"},"homepage":"https://github.com/dx-tooling/ask-a-human#readme","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-B+4Z3yZTCL4Q/yY4sFwR30g8eocHHwa4H9/3jUc/BDm8SZwOmtQvN761P6V+WHarM0k/Zql+/N50kHUgyqR7Jg==","shasum":"dcd0bfaacd4b94cf36e09193bb5706b9db9ac6d1","tarball":"https://registry.npmjs.org/@ask-a-human/sdk/-/sdk-0.1.0.tgz","fileCount":8,"unpackedSize":190521,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDcOWd0VtLZa7SviAXhhJ1yojI88IYe3Zca4CHQ/dO7XAIgUduyLdJzyf32Z0Y0dRM71+AdxWloZF8rryqCr2pZyIQ="}]},"_npmUser":{"name":"manuelkiessling","email":"manuel@kiessling.net"},"directories":{},"maintainers":[{"name":"manuelkiessling","email":"manuel@kiessling.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sdk_0.1.0_1770103679944_0.21518315767555718"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-03T07:27:59.854Z","0.1.0":"2026-02-03T07:28:00.085Z","modified":"2026-02-03T07:28:00.306Z"},"maintainers":[{"name":"manuelkiessling","email":"manuel@kiessling.net"}],"description":"TypeScript SDK for Ask-a-Human - get human input for your AI agents","homepage":"https://github.com/dx-tooling/ask-a-human#readme","keywords":["ask-a-human","human-in-the-loop","ai","llm","agent","mcp","human-feedback"],"repository":{"type":"git","url":"git+https://github.com/dx-tooling/ask-a-human.git"},"author":{"name":"Manuel Kießling","email":"manuel@kiessling.net"},"bugs":{"url":"https://github.com/dx-tooling/ask-a-human/issues"},"license":"MIT","readme":"# Ask-a-Human TypeScript SDK\n\nTypeScript SDK for integrating [Ask-a-Human](https://ask-a-human.com) into your AI agents. Get human input when your agent is uncertain or needs subjective judgment.\n\n## Installation\n\n```bash\nnpm install @ask-a-human/sdk\n```\n\nOr install from source:\n\n```bash\nnpm install /path/to/sdk-typescript\n```\n\n## Quick Start\n\n```typescript\nimport { AskHumanClient } from \"@ask-a-human/sdk\";\n\n// Create a client\nconst client = new AskHumanClient({ agentId: \"my-agent\" });\n\n// Submit a question\nconst result = await client.submitQuestion({\n  prompt: \"Should this error message apologize to the user or just state the facts?\",\n  type: \"text\",\n  audience: [\"product\", \"creative\"],\n  minResponses: 5,\n});\n\nconsole.log(`Question submitted: ${result.questionId}`);\n\n// Later, check for responses\nconst response = await client.getQuestion(result.questionId);\n\nif (response.status === \"CLOSED\" || response.status === \"PARTIAL\") {\n  for (const r of response.responses) {\n    console.log(`Human said: ${r.answer} (confidence: ${r.confidence})`);\n  }\n}\n```\n\n## Using the Orchestrator\n\nFor more complex workflows, use the `AskHumanOrchestrator` which handles polling and timeouts:\n\n```typescript\nimport { AskHumanClient, AskHumanOrchestrator } from \"@ask-a-human/sdk\";\n\nconst client = new AskHumanClient({ agentId: \"my-agent\" });\nconst orchestrator = new AskHumanOrchestrator(client, { pollInterval: 30000 });\n\n// Submit a question\nconst submission = await orchestrator.submit({\n  prompt: \"Which button label is clearer?\",\n  type: \"multiple_choice\",\n  options: [\"Submit\", \"Send\", \"Confirm\", \"Done\"],\n  minResponses: 10,\n});\n\n// Wait for responses (with timeout)\nconst responses = await orchestrator.awaitResponses([submission.questionId], {\n  minResponses: 5,\n  timeout: 300000, // 5 minutes\n});\n\n// Process responses\nconst question = responses[submission.questionId];\nconsole.log(`Status: ${question.status}`);\nconsole.log(`Got ${question.currentResponses} responses`);\n\nif (question.summary) {\n  console.log(`Summary: ${JSON.stringify(question.summary)}`);\n}\n```\n\n## Multiple Choice Questions\n\n```typescript\nconst result = await client.submitQuestion({\n  prompt: \"What tone should this notification use?\",\n  type: \"multiple_choice\",\n  options: [\n    \"Formal and professional\",\n    \"Friendly and casual\",\n    \"Urgent and direct\",\n    \"Neutral and informative\",\n  ],\n  audience: [\"product\"],\n  minResponses: 10,\n});\n\n// Check responses\nconst response = await client.getQuestion(result.questionId);\n\n// For multiple choice, responses have selectedOption instead of answer\nfor (const r of response.responses) {\n  if (r.selectedOption !== undefined && response.options) {\n    const optionText = response.options[r.selectedOption];\n    console.log(`Human chose: ${optionText} (confidence: ${r.confidence})`);\n  }\n}\n\n// summary field shows vote counts\nif (response.summary) {\n  console.log(`Vote distribution: ${JSON.stringify(response.summary)}`);\n}\n```\n\n## Handling Timeouts and Partial Results\n\nThe orchestrator can return partial results when a timeout is reached:\n\n```typescript\nconst responses = await orchestrator.awaitResponses([\"q_abc123\"], {\n  minResponses: 10,\n  timeout: 60000, // 1 minute\n});\n\nconst question = responses[\"q_abc123\"];\n\nif (question.status === \"PARTIAL\") {\n  console.log(`Got ${question.currentResponses} of ${question.requiredResponses} responses`);\n  // Decide whether to proceed with partial results or wait longer\n} else if (question.status === \"EXPIRED\") {\n  console.log(\"Question expired before getting enough responses\");\n}\n```\n\n## Cancellation with AbortController\n\nYou can cancel long-running operations using `AbortController`:\n\n```typescript\nconst controller = new AbortController();\n\n// Cancel after 30 seconds\nsetTimeout(() => controller.abort(), 30000);\n\ntry {\n  const responses = await orchestrator.awaitResponses([submission.questionId], {\n    timeout: 300000,\n    signal: controller.signal,\n  });\n} catch (error) {\n  if (error instanceof AbortError) {\n    console.log(\"Operation was cancelled\");\n  }\n}\n```\n\n## Configuration\n\n### Environment Variables\n\n- `ASK_A_HUMAN_BASE_URL` - Override the API base URL (default: `https://api.ask-a-human.com`)\n- `ASK_A_HUMAN_AGENT_ID` - Default agent ID if not specified in constructor\n\n### Client Options\n\n```typescript\nconst client = new AskHumanClient({\n  baseUrl: \"https://api.ask-a-human.com\", // API endpoint\n  agentId: \"my-agent\", // Your agent identifier\n  timeout: 30000, // HTTP request timeout (ms)\n});\n```\n\n### Orchestrator Options\n\n```typescript\nconst orchestrator = new AskHumanOrchestrator(client, {\n  pollInterval: 30000, // Base interval between polls (ms)\n  maxBackoff: 300000, // Maximum backoff interval (ms)\n  backoffMultiplier: 1.5, // Exponential backoff multiplier\n});\n```\n\n## Error Handling\n\n```typescript\nimport {\n  AskHumanClient,\n  AskHumanError,\n  ValidationError,\n  QuestionNotFoundError,\n  RateLimitError,\n  QuotaExceededError,\n  ServerError,\n  TimeoutError,\n  AbortError,\n} from \"@ask-a-human/sdk\";\n\nconst client = new AskHumanClient({ agentId: \"my-agent\" });\n\ntry {\n  const result = await client.submitQuestion({\n    prompt: \"...\",\n    type: \"text\",\n  });\n} catch (error) {\n  if (error instanceof ValidationError) {\n    console.log(`Invalid request: ${error.message}`);\n    console.log(`Field: ${error.field}, Constraint: ${error.constraint}`);\n  } else if (error instanceof QuotaExceededError) {\n    console.log(\"Too many concurrent questions, wait for some to close\");\n  } else if (error instanceof RateLimitError) {\n    console.log(`Rate limited. Retry after: ${error.retryAfter} seconds`);\n  } else if (error instanceof QuestionNotFoundError) {\n    console.log(`Question not found: ${error.questionId}`);\n  } else if (error instanceof ServerError) {\n    console.log(`Server error (HTTP ${error.statusCode}): ${error.message}`);\n  } else if (error instanceof AskHumanError) {\n    console.log(`API error: ${error.message}`);\n  }\n}\n```\n\n## API Reference\n\n### AskHumanClient\n\nLow-level API client for direct HTTP requests.\n\n#### Constructor\n\n```typescript\nnew AskHumanClient(options?: ClientOptions)\n```\n\n#### Methods\n\n- `submitQuestion(options: SubmitQuestionOptions): Promise<QuestionSubmission>` - Submit a question\n- `getQuestion(questionId: string): Promise<QuestionResponse>` - Get question status and responses\n\n### AskHumanOrchestrator\n\nHigh-level orchestration with polling and timeouts.\n\n#### Constructor\n\n```typescript\nnew AskHumanOrchestrator(client: AskHumanClient, options?: OrchestratorOptions)\n```\n\n#### Methods\n\n- `submit(options: SubmitQuestionOptions): Promise<QuestionSubmission>` - Submit a question\n- `awaitResponses(questionIds: string[], options?: AwaitResponsesOptions): Promise<Record<string, QuestionResponse>>` - Wait for responses\n- `pollOnce(questionIds: string[]): Promise<Record<string, QuestionResponse>>` - Non-blocking status check\n- `submitAndWait(options: SubmitQuestionOptions, awaitOptions?: AwaitResponsesOptions): Promise<QuestionResponse>` - Submit and wait in one call\n\n### Types\n\n#### Question Types\n\n- `QuestionType` - `\"text\"` or `\"multiple_choice\"`\n- `QuestionStatus` - `\"OPEN\"`, `\"PARTIAL\"`, `\"CLOSED\"`, or `\"EXPIRED\"`\n- `AudienceTag` - `\"technical\"`, `\"product\"`, `\"ethics\"`, `\"creative\"`, or `\"general\"`\n\n#### Request/Response Types\n\n- `SubmitQuestionOptions` - Options for submitting a question\n- `QuestionSubmission` - Response from submitting a question\n- `QuestionResponse` - Full question with status and responses\n- `HumanResponse` - Individual human response\n\n## Examples\n\nSee the `examples/` directory for more usage examples:\n\n- `basic-usage.ts` - Simple question submission and polling\n- `multi-question.ts` - Managing multiple concurrent questions\n\n## Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Run tests\nnpm test\n\n# Run tests with coverage\nnpm run test:coverage\n\n# Build\nnpm run build\n\n# Lint\nnpm run lint\n\n# Format\nnpm run format\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-89e13e5f81e5e9e6e7f4fead8bdc7635"}