@farukada/langchain-ts-gms
v0.2.0
Published
Vector-centric Goal Management System built with LangChain TypeScript and LangGraph
Maintainers
Readme
Vector-centric Goal Management System built with LangChain TypeScript and LangGraph (GMS)
GMS is a planning library for autonomous agents. It turns a goal into a hierarchical task graph (tasks + sub-tasks + dependencies), while your external agent remains responsible for execution.
Table of Contents
- What This Package Is
- Responsibility Boundary
- System Overview
- Quick Start
- Use as Library
- Public API Reference
- Package Exports
- Tool Catalog
- Configuration Reference
- Production Considerations
- Known Non-Goals
- Testing and CI
- Documentation
- Project Structure
- License
What This Package Is
- Planning-only engine: builds plans and validates goal trees.
- Hierarchical model: goal -> tasks -> sub-tasks with parent and dependency links.
- LangChain-native: exports tools via the
tool()factory from@langchain/core/tools. - Vector-centric: uses embeddings + Qdrant for semantic decomposition and retrieval.
Responsibility Boundary
| Concern | Owned by GMS | Owned by your Agent | | --------------------------------------------- | ------------ | ------------------- | | Goal decomposition into tasks/sub-tasks | Yes | No | | Task dependency-safe ordering | Yes | No | | Goal/task retrieval and mutation tooling | Yes | No | | Task execution (calling APIs, tools, scripts) | No | Yes | | Runtime retries, side effects, actuation | No | Yes |
System Overview
flowchart LR
subgraph agentRuntime [AgentRuntime]
agent[LangChainAgent]
gmsTools[GmsTools]
executor[TaskExecutor]
end
subgraph gmsLibrary [GmsLibraryInProcess]
workflow[createGmsWorkflow]
planner[gms_plan_goal]
lifecycle[LifecycleTools]
end
subgraph backingServices [BackingServices]
qdrant[(Qdrant)]
embeddings[EmbeddingsProvider]
end
agent --> gmsTools
gmsTools --> planner
gmsTools --> lifecycle
planner --> workflow
workflow --> qdrant
workflow --> embeddings
planner -->|"returns plan JSON"| executorQuick Start
Prerequisites
- Node.js >= 22
- npm
- Docker (for Qdrant)
Install
npm add @farukada/langchain-ts-gmsDevelopment infrastructure
# Start Qdrant (vector storage) and Ollama (LLM + embeddings)
docker compose up -d qdrant
docker compose --profile ollama up -d ollamaFull operational steps and troubleshooting are in docs/operations.md.
Use as Library
Install:
npm add @farukada/langchain-ts-gmsMinimal registration
import { createGmsToolFromEnv } from "@farukada/langchain-ts-gms/tool";
const planTool = await createGmsToolFromEnv();
agent.tools.push(planTool);Full lifecycle registration
import {
createGmsToolFromEnv,
createGmsLifecycleToolsFromEnv,
} from "@farukada/langchain-ts-gms/tool";
const planTool = await createGmsToolFromEnv({
decomposeOptions: { topK: 5, maxDepth: 4 },
});
const lifecycleTools = await createGmsLifecycleToolsFromEnv();
agent.tools.push(planTool, ...lifecycleTools);decomposeOptions
| Option | Default | Purpose |
| ---------- | ------- | --------------------------------------------------------------------------------------- |
| topK | 5 | Number of capability vectors retrieved from Qdrant as context for the LLM decomposition |
| maxDepth | 4 | Maximum nesting depth for the hierarchical task tree |
Higher topK gives the planner more context but increases token cost. Lower maxDepth produces flatter plans.
Multi-tenancy
All tools support an optional tenantId field for data isolation. When set, capability search and goal listing are scoped to the given tenant:
const planTool = await createGmsToolFromEnv();
// The agent passes tenantId when invoking the tool:
// { goalDescription: "...", tenantId: "org-123" }Qdrant payload indexes on metadata.tenant_id ensure filtered queries remain fast.
Public API Reference
Main exports
createGmsPlanTool(deps): create planning tool (gms_plan_goal).createGmsToolFromEnv(options): env-based planning tool factory.createGmsLifecycleTools(deps): create retrieval/mutation/validation toolset.createGmsLifecycleToolsFromEnv(options): env-based lifecycle factory.createAllGmsToolsFromEnv(options): convenience factory for both planning + lifecycle tools with shared repository instances (recommended).createPlan(input, deps): execute planning directly without tool wrapper.streamPlan(input, deps): async generator that streamsGmsEventobjects as the workflow progresses (planner → guardrail → summarizer).decomposeGoal(goal, repo, embeddings, chatModel, options?): lower-level LLM-powered task decomposition (used internally by the planner).patchPlanSubtree(repository, options): surgically replace a sub-tree of the task DAG without touching the rest of the plan.createGmsWorkflow(deps): lower-level LangGraph workflow factory.
Rate limiting
TokenBucketLimiter: in-process token-bucket rate limiter. Pass asGmsToolDeps.rateLimiterto throttle tool invocations.RateLimitError: thrown whenTokenBucketLimiter.acquire()times out.
Key result shape (gms_plan_goal)
{
version: "1.0",
goalId: string,
status: "planned" | "failed" | "human_approval_required",
tasks: Task[],
executionOrder: string[],
traceId?: string,
interrupt?: unknown,
error?: string
}Advanced dependency wiring (explicit repositories)
import {
createGmsPlanTool,
QdrantGoalRepository,
createEmbeddingProvider,
CAPABILITIES_COLLECTION,
} from "@farukada/langchain-ts-gms";
const embeddings = createEmbeddingProvider();
const goalRepository = new QdrantGoalRepository({ embeddings });
const capabilityRepository = new QdrantGoalRepository({
embeddings,
collectionName: CAPABILITIES_COLLECTION,
});
await goalRepository.bootstrap();
const planTool = createGmsPlanTool({ goalRepository, capabilityRepository });
agent.tools.push(planTool);Production checkpointer
By default, the GMS workflow uses an in-memory checkpointer (MemorySaver) — state is lost on process restart. For production, inject a persistent checkpointer via createGmsWorkflow:
import { SqliteSaver } from "@langchain/langgraph-checkpoint-sqlite";
import {
createGmsWorkflow,
QdrantGoalRepository,
createEmbeddingProvider,
} from "@farukada/langchain-ts-gms";
const checkpointer = SqliteSaver.fromConnString("./gms.db");
const embeddings = createEmbeddingProvider();
const goalRepository = new QdrantGoalRepository({ embeddings });
const workflow = createGmsWorkflow({
goalRepository,
checkpointer, // persists workflow state across restarts
});The checkpointer option is accepted by createGmsWorkflow (via the WorkflowDeps interface). The higher-level createGmsPlanTool and *FromEnv factories use MemorySaver internally; to override it, use createGmsWorkflow directly.
Human-in-the-Loop (HITL)
When a plan exceeds the guardrail task-count threshold (default: 50 tasks) or contains a task with riskLevel: "critical", the workflow triggers a LangGraph interrupt and returns status: "human_approval_required":
const result = JSON.parse(await planTool.invoke({ goalDescription: "..." }));
if (result.status === "human_approval_required") {
// result.interrupt contains: { action, goalId, taskCount, message }
// Present the plan to a human for review, then resume the workflow
// using the thread_id (result.goalId) via the compiled workflow.
}The interrupt payload shape:
{ action: "approve_plan", goalId: string, taskCount: number, message: string }Capability seeding
The planner searches the gms_capabilities Qdrant collection for existing capabilities to inform task decomposition. Without seeded capabilities, the LLM decomposes blindly — plans still work but are lower quality.
Seed capabilities using the same GoalMemoryRepository:
import {
QdrantGoalRepository,
createEmbeddingProvider,
CAPABILITIES_COLLECTION,
} from "@farukada/langchain-ts-gms";
const embeddings = createEmbeddingProvider();
const capRepo = new QdrantGoalRepository({
embeddings,
collectionName: CAPABILITIES_COLLECTION,
});
await capRepo.bootstrap();
await capRepo.upsert({
id: crypto.randomUUID(),
description: "Deploy Node.js services to Kubernetes",
status: "planned",
priority: "medium",
tasks: [],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
});The more relevant capabilities you seed, the better the planner's task decomposition quality.
Package Exports
The package exposes granular sub-path exports for tree-shaking and targeted imports:
| Sub-path | Provides |
| ---------------------------------------------- | ------------------------------------------------------------- |
| @farukada/langchain-ts-gms | All public APIs (tools, repos, domain utils, schemas) |
| @farukada/langchain-ts-gms/tool | createGmsPlanTool, createGmsLifecycleTools, env factories |
| @farukada/langchain-ts-gms/tools/* | Individual tool factories (planGoal, getGoal, etc.) |
| @farukada/langchain-ts-gms/helpers | Input normalization, pagination, tree filtering |
| @farukada/langchain-ts-gms/types | GmsToolDeps, GmsPlanResult, AllGmsTools |
| @farukada/langchain-ts-gms/schemas/planning | GmsToolInputSchema |
| @farukada/langchain-ts-gms/schemas/lifecycle | All lifecycle tool input schemas |
| @farukada/langchain-ts-gms/mcp | createGmsMcpServer, startMcpServer, GmsMcpServerOptions |
Example: import individual tools
import { createGetGoalTool } from "@farukada/langchain-ts-gms/tools/getGoal";
import { createUpdateTaskTool } from "@farukada/langchain-ts-gms/tools/updateTask";Tool Catalog
Planning tool
gms_plan_goal: create a hierarchical plan and dependency-safeexecutionOrder.
Lifecycle tools
gms_get_goal: fetch goal with full hierarchical tasks.gms_get_task: fetch a single task bygoalId+taskId.gms_list_tasks: list tasks with filters and pagination.gms_search_tasks: search tasks by text, status, priority, dependency presence.gms_list_goals: list goals with filters and pagination.gms_update_goal: update goal fields.gms_update_task: update task status/result/error.gms_validate_goal_tree: validate hierarchy and dependency invariants.gms_get_progress: get progress counters and completion rate.gms_replan_goal: replan usingappend,replace_failed, orreplace_all.gms_expand_task: dynamically expand a parent task into sub-tasks at runtime (map-reduce / fan-out).
Tool input/output reference
| Tool | Required input | Output focus |
| ------------------------ | ---------------------------- | ---------------------------------------------------------- |
| gms_plan_goal | goalDescription | goalId, status, hierarchical tasks, executionOrder |
| gms_get_goal | goalId | Full goal object with nested tasks |
| gms_get_task | goalId, taskId | Single task + parentId + dependency context |
| gms_list_tasks | goalId | Filtered/paginated task list + total |
| gms_search_tasks | goalId | Query/filter-based paginated task results |
| gms_list_goals | none | Filtered/paginated goals + total |
| gms_update_goal | goalId | Updated goal status/metadata fields |
| gms_update_task | goalId, taskId | Updated task snapshot |
| gms_validate_goal_tree | goalId | Invariant validation result (valid, issues) |
| gms_get_progress | goalId | Counters + completion rate |
| gms_replan_goal | goalId | Replan diff (replacedTaskIds, newTaskIds) |
| gms_expand_task | goalId, parentTaskId, subTasks | Expanded task with new sub-tasks attached |
Configuration Reference
Runtime environment variables:
| Variable | Required | Default | Purpose |
| ---------------------------- | -------- | -------------------------------------- | ---------------------------------------------------- |
| NODE_ENV | No | development | Runtime mode |
| LOG_LEVEL | No | info | Minimum log level (debug, info, warn, error) |
| QDRANT_URL | No | http://localhost:6333 | Qdrant endpoint |
| QDRANT_API_KEY | No | - | Qdrant authentication key |
| OLLAMA_HOST | No | http://localhost:11434 | Ollama server URL |
| OLLAMA_EMBEDDING_MODEL | No | nomic-embed-text | Default embedding model |
| OLLAMA_CHAT_MODEL | No | qwen3:8b | Default chat/planning model |
| GMS_OLLAMA_EMBEDDING_MODEL | No | falls back to OLLAMA_EMBEDDING_MODEL | GMS-specific embedding model override |
| GMS_OLLAMA_CHAT_MODEL | No | falls back to OLLAMA_CHAT_MODEL | GMS-specific chat model override |
| LANGCHAIN_TRACING_V2 | No | false | LangChain tracing toggle |
| LANGCHAIN_API_KEY | No | - | LangSmith API key when tracing is enabled |
Production Considerations
- Checkpointer: Replace the default in-memory
MemorySaverwith a persistent store. See Production checkpointer. - Capabilities: Seed the
gms_capabilitiescollection for higher-quality plans. See Capability seeding. - HITL: Handle the
human_approval_requiredstatus if plans exceed guardrail thresholds. See Human-in-the-Loop. - Multi-tenancy: Pass
tenantIdto isolate data per organization. See Multi-tenancy.
Known Non-Goals
- GMS does not execute tasks; it only plans and manages goal/task state.
- GMS does not lock/claim tasks for concurrent workers in this package version.
- GMS does not include audit history/event sourcing for every lifecycle mutation.
- GMS does not enforce organization-specific policy packs out of the box; guardrails are extensible.
Testing and CI
Local checks
| Command | Purpose |
| ----------------------- | -------------------------------------- |
| npm test | Unit tests (CI-safe, no external deps) |
| npm run test:ci | CI-safe unit test suite |
| npm run test:watch | Watch mode for development |
| npm run test:coverage | Unit tests with V8 coverage |
CI pipeline
flowchart LR
sourceChange[PushOrPullRequest]
validate[Validate]
integrationAgent[IntegrationAgent]
publish[PublishToNpm]
sourceChange --> validate
validate -->|"push to main"| integrationAgent --> publishDocumentation
Project Structure
src/
├── app/ # Workflow, planning, guardrails
├── lib/ # Public library API + tools
├── config/ # Environment configuration
├── domain/ # Core contracts and task utilities
└── infra/ # Qdrant, embeddings, observabilityLicense
MIT
