@letsping/sdk 0.1.5 → 0.1.6

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.
package/README.md CHANGED
@@ -10,9 +10,9 @@ LetsPing is a behavioral firewall and Human-in-the-Loop (HITL) infrastructure la
10
10
  - **Smart-Accept Drift Adaptation:** Approval decisions mathematically alter the baseline. Old unused reasoning paths decay automatically via Exponential Moving Average (EMA).
11
11
 
12
12
  ## Requirements
13
-
14
13
  - Node.js 18+
15
14
  - TypeScript 5+ (recommended)
15
+ - (Optional) `@langchain/langgraph` and `@langchain/core` for state persistence
16
16
 
17
17
  ## Installation
18
18
 
@@ -96,7 +96,7 @@ console.log(`Approval request queued → ${id}`);
96
96
  LetsPing does **not** magically inject state back into your framework natively. You must handle the webhook and rehydrate your specific framework manually.
97
97
 
98
98
  ```typescript
99
- // Example Webhook Route Handler
99
+ // app/api/webhook/letsping/route.ts
100
100
  if (body.status === "APPROVED") {
101
101
  let hydratedState = null;
102
102
  if (body.state_download_url) {
@@ -107,6 +107,21 @@ if (body.status === "APPROVED") {
107
107
  }
108
108
  ```
109
109
 
110
+ ### LangGraph Integration (Persisted State)
111
+
112
+ LetsPing provides a `LetsPingCheckpointer` for LangGraph JS/TS that automatically encrypts and parks your agent's state in Cryo-Sleep storage.
113
+
114
+ ```typescript
115
+ import { StateGraph } from "@langchain/langgraph";
116
+ import { LetsPing } from "@letsping/sdk";
117
+
118
+ // Import the checkpointer from the specific integration path
119
+ import { LetsPingCheckpointer } from "@letsping/sdk/integrations/langgraph";
120
+
121
+ const lp = new LetsPing(process.env.LETSPING_API_KEY!);
122
+ const checkpointer = new LetsPingCheckpointer(lp);
123
+ ```
124
+
110
125
  ## API Reference
111
126
 
112
127
  ### `new LetsPing(apiKey, options?)`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letsping/sdk",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Behavioral Firewall and Cryo-Sleep State Parking for Autonomous Agents",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -10,6 +10,11 @@
10
10
  "types": "./dist/index.d.ts",
11
11
  "require": "./dist/index.js",
12
12
  "import": "./dist/index.mjs"
13
+ },
14
+ "./integrations/langgraph": {
15
+ "types": "./dist/integrations/langgraph.d.ts",
16
+ "require": "./dist/integrations/langgraph.js",
17
+ "import": "./dist/integrations/langgraph.mjs"
13
18
  }
14
19
  },
15
20
  "scripts": {
@@ -17,22 +22,31 @@
17
22
  "dev": "tsup --watch",
18
23
  "clean": "rm -rf dist .turbo"
19
24
  },
20
- "dependencies": {},
21
25
  "peerDependencies": {
26
+ "@langchain/core": ">=0.1.52",
27
+ "@langchain/langgraph": ">=0.0.1",
22
28
  "@opentelemetry/api": "^1.0.0"
23
29
  },
24
30
  "peerDependenciesMeta": {
25
31
  "@opentelemetry/api": {
26
32
  "optional": true
33
+ },
34
+ "@langchain/langgraph": {
35
+ "optional": true
36
+ },
37
+ "@langchain/core": {
38
+ "optional": true
27
39
  }
28
40
  },
29
41
  "devDependencies": {
30
- "tsup": "^8.0.0",
31
- "typescript": "^5.7.2",
42
+ "@langchain/core": "^1.1.28",
43
+ "@langchain/langgraph": "^1.1.5",
44
+ "@opentelemetry/api": "^1.9.0",
32
45
  "@types/node": "^22.0.0",
33
- "@opentelemetry/api": "^1.9.0"
46
+ "tsup": "^8.0.0",
47
+ "typescript": "^5.7.2"
34
48
  },
35
49
  "publishConfig": {
36
50
  "access": "public"
37
51
  }
38
- }
52
+ }
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createCipheriv, createDecipheriv, randomBytes, createHmac } from "node:crypto";
2
2
 
3
- let SDK_VERSION = "0.1.5";
3
+ let SDK_VERSION = "0.1.6";
4
4
  try {
5
5
 
6
6
  SDK_VERSION = require("../package.json").version;
@@ -0,0 +1,65 @@
1
+ import { BaseCheckpointSaver, Checkpoint, CheckpointMetadata, CheckpointTuple } from "@langchain/langgraph";
2
+ import { RunnableConfig } from "@langchain/core/runnables";
3
+ import { LetsPing } from "../index";
4
+
5
+ export class LetsPingCheckpointer extends BaseCheckpointSaver {
6
+ private checkpoints: Record<string, [Checkpoint, CheckpointMetadata]> = {};
7
+
8
+ constructor(public client: LetsPing) {
9
+ super();
10
+ }
11
+
12
+ async put(
13
+ config: RunnableConfig,
14
+ checkpoint: Checkpoint,
15
+ metadata: CheckpointMetadata,
16
+ newVersions?: Record<string, string | number>
17
+ ): Promise<RunnableConfig> {
18
+ const threadId = config.configurable?.thread_id;
19
+ const checkpointId = checkpoint.id;
20
+
21
+ this.checkpoints[`${threadId}:${checkpointId}`] = [checkpoint, metadata];
22
+
23
+ return {
24
+ configurable: {
25
+ thread_id: threadId,
26
+ checkpoint_id: checkpointId,
27
+ }
28
+ };
29
+ }
30
+
31
+ // --- NEW METHODS REQUIRED BY LANGGRAPH V0.1+ ---
32
+ async putWrites(config: RunnableConfig, writes: any, taskId: string): Promise<void> {
33
+ // No-op for V1: LetsPing focuses on primary state parking, not granular sub-task writes.
34
+ }
35
+
36
+ async deleteThread(threadId: string): Promise<void> {
37
+ for (const key of Object.keys(this.checkpoints)) {
38
+ if (key.startsWith(`${threadId}:`)) {
39
+ delete this.checkpoints[key];
40
+ }
41
+ }
42
+ }
43
+
44
+ async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {
45
+ const threadId = config.configurable?.thread_id;
46
+ const checkpointId = config.configurable?.checkpoint_id;
47
+
48
+ if (checkpointId) {
49
+ const match = this.checkpoints[`${threadId}:${checkpointId}`];
50
+ if (match) return { config, checkpoint: match[0], metadata: match[1] };
51
+ }
52
+
53
+ let latest: CheckpointTuple | undefined;
54
+ for (const [key, val] of Object.entries(this.checkpoints)) {
55
+ if (key.startsWith(`${threadId}:`)) {
56
+ latest = { config, checkpoint: val[0], metadata: val[1] };
57
+ }
58
+ }
59
+ return latest;
60
+ }
61
+
62
+ async *list(config: RunnableConfig, options?: any): AsyncGenerator<CheckpointTuple> {
63
+ yield* [];
64
+ }
65
+ }
package/tsup.config.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { defineConfig } from "tsup";
2
2
 
3
3
  export default defineConfig({
4
- entry: ["src/index.ts"],
4
+ entry: ["src/index.ts", "src/integrations/langgraph.ts"],
5
5
  format: ["cjs", "esm"],
6
6
  dts: true,
7
7
  clean: true,
package/dist/index.d.mts DELETED
@@ -1,37 +0,0 @@
1
- type Priority = "low" | "medium" | "high" | "critical";
2
- interface RequestOptions {
3
- service: string;
4
- action: string;
5
- payload: Record<string, any>;
6
- priority?: Priority;
7
- schema?: Record<string, any>;
8
- timeoutMs?: number;
9
- }
10
- interface Decision {
11
- status: "APPROVED" | "REJECTED";
12
- payload: any;
13
- patched_payload?: any;
14
- metadata?: {
15
- resolved_at: string;
16
- actor_id: string;
17
- method?: string;
18
- };
19
- }
20
- declare class LetsPingError extends Error {
21
- status?: number | undefined;
22
- constructor(message: string, status?: number | undefined);
23
- }
24
- declare class LetsPing {
25
- private readonly apiKey;
26
- private readonly baseUrl;
27
- constructor(apiKey?: string, options?: {
28
- baseUrl?: string;
29
- });
30
- ask(options: RequestOptions): Promise<Decision>;
31
- defer(options: RequestOptions): Promise<{
32
- id: string;
33
- }>;
34
- private request;
35
- }
36
-
37
- export { type Decision, LetsPing, LetsPingError, type Priority, type RequestOptions };
package/dist/index.d.ts DELETED
@@ -1,52 +0,0 @@
1
- export type Priority = "low" | "medium" | "high" | "critical";
2
- export interface RequestOptions {
3
- service: string;
4
- action: string;
5
- payload: Record<string, any>;
6
- priority?: Priority;
7
- schema?: Record<string, any>;
8
- state_snapshot?: Record<string, any>;
9
- timeoutMs?: number;
10
- role?: string;
11
- }
12
- export interface Decision {
13
- status: "APPROVED" | "REJECTED" | "APPROVED_WITH_MODIFICATIONS";
14
- payload: any;
15
- patched_payload?: any;
16
- diff_summary?: any;
17
- metadata?: {
18
- resolved_at: string;
19
- actor_id: string;
20
- method?: string;
21
- };
22
- }
23
- export declare class LetsPingError extends Error {
24
- status?: number | undefined;
25
- constructor(message: string, status?: number | undefined);
26
- }
27
- declare function computeDiff(original: any, patched: any): any;
28
- export declare class LetsPing {
29
- private readonly apiKey;
30
- private readonly baseUrl;
31
- private readonly encryptionKey;
32
- constructor(apiKey?: string, options?: {
33
- baseUrl?: string;
34
- encryptionKey?: string;
35
- });
36
- private _encrypt;
37
- private _decrypt;
38
- private _prepareStateUpload;
39
- ask(options: RequestOptions): Promise<Decision>;
40
- defer(options: RequestOptions): Promise<{
41
- id: string;
42
- }>;
43
- private request;
44
- tool(service: string, action: string, priority?: Priority): (context: string | Record<string, any>) => Promise<string>;
45
- webhookHandler(payloadStr: string, signatureHeader: string, webhookSecret: string): Promise<{
46
- id: string;
47
- event: string;
48
- data: Decision;
49
- state_snapshot?: Record<string, any>;
50
- }>;
51
- }
52
- export { computeDiff };