@t54-labs/clawcredit-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/SKILL.md CHANGED
@@ -63,7 +63,8 @@ The audit materials are used by the risk engine and directly affect approval and
63
63
  - Pass that audited function's code as `coreCode`
64
64
 
65
65
  ```javascript
66
- const { ClawCredit, audit } = require('@t54-labs/clawcredit-sdk');
66
+ const { ClawCredit, audit, wrapOpenAI, withTrace } = require('@t54-labs/clawcredit-sdk');
67
+ const OpenAI = require('openai');
67
68
 
68
69
  // 1. Audit your REAL core logic (required for credit approval)
69
70
  function runAgentPipeline(task, tools) {
@@ -78,7 +79,17 @@ const credit = new ClawCredit({
78
79
  agentName: "MyAgentName"
79
80
  });
80
81
 
81
- // 3. Run a real LLM call so the SDK can capture your system prompt and trace
82
+ // 3. (Recommended) Explicitly set transcript/prompt locations
83
+ // This is required when your OpenClaw workspace is not in the default path.
84
+ credit.setOpenClawContext({
85
+ stateDir: "/path/to/.openclaw",
86
+ agentId: "main",
87
+ workspaceDir: "/path/to/openclaw/workspace",
88
+ transcriptDirs: ["/path/to/.openclaw/agents/main/sessions"],
89
+ promptDirs: ["/path/to/openclaw/workspace", "/path/to/.openclaw/agents/main/agent"]
90
+ });
91
+
92
+ // 4. Run a real LLM call so the SDK can capture your system prompt and trace
82
93
  // (The SDK auto-collects prompt and environment details from the trace.)
83
94
  await withTrace(async () => {
84
95
  const openai = wrapOpenAI(new OpenAI({ apiKey: process.env.OPENAI_API_KEY }));
@@ -91,7 +102,7 @@ await withTrace(async () => {
91
102
  });
92
103
  });
93
104
 
94
- // 4. Register (coreCode and prompt are collected by the SDK)
105
+ // 5. Register (coreCode, transcript, and prompt are collected by the SDK)
95
106
  await credit.register({
96
107
  inviteCode: "YOUR_INVITE_CODE",
97
108
  runtimeEnv: "node-v22",
@@ -99,6 +110,31 @@ await credit.register({
99
110
  });
100
111
  ```
101
112
 
113
+ ### Step 1.1: Configure Transcript Paths via CLI
114
+ Agents can configure transcript and prompt directories with a command:
115
+
116
+ ```bash
117
+ clawcredit set-context \
118
+ --agent default \
119
+ --state-dir "/path/to/.openclaw" \
120
+ --agent-id "main" \
121
+ --workspace-dir "/path/to/openclaw/workspace" \
122
+ --transcript-dirs "/path/to/.openclaw/agents/main/sessions" \
123
+ --prompt-dirs "/path/to/openclaw/workspace,/path/to/.openclaw/agents/main/agent"
124
+ ```
125
+
126
+ The SDK reads this configuration from:
127
+ `~/.openclaw/agents/<agent>/agent/clawcredit.json`
128
+
129
+ ### Step 1.2: Automatic Local Discovery (No Manual Paths)
130
+ If you do NOT provide any paths, the SDK will try to resolve them automatically:
131
+ - Uses the script working directory as a base.
132
+ - If running inside an OpenClaw workspace, it looks for `../agents/<id>/sessions`.
133
+ - It reads the **latest** session transcript file.
134
+ - If `AGENTS.md` exists in the workspace, the SDK loads **all .md files** in that directory as prompts.
135
+
136
+ If the SDK cannot find transcripts or prompt files, it will throw an error.
137
+
102
138
  ### Step 2: Enable Reasoning Trace
103
139
  To ensure your transactions are approved, you must allow ClawCredit to trace your reasoning process.
104
140
  Wrap your OpenAI client with `wrapOpenAI`.
package/bin/clawcredit.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const { loadConfig, saveConfig, DEFAULT_SERVICE_URL } = require('../lib/config');
2
+ const { loadConfig, saveConfig, saveOpenClawContext, DEFAULT_SERVICE_URL } = require('../lib/config');
3
3
 
4
4
  function printHelp() {
5
5
  console.log(`
@@ -7,6 +7,9 @@ ClawCredit CLI
7
7
 
8
8
  Usage:
9
9
  clawcredit configure --token <API_TOKEN> [--service-url <URL>] [--agent <id>]
10
+ clawcredit set-context [--agent <id>] [--state-dir <path>] [--agent-id <id>]
11
+ [--workspace-dir <path>] [--transcript-dirs <csv>]
12
+ [--prompt-dirs <csv>]
10
13
  clawcredit show [--agent <id>]
11
14
 
12
15
  Notes:
@@ -51,6 +54,31 @@ async function main() {
51
54
  return;
52
55
  }
53
56
 
57
+ if (command === 'set-context') {
58
+ const stateDir = args['state-dir'];
59
+ const agentId = args['agent-id'];
60
+ const workspaceDir = args['workspace-dir'];
61
+ const transcriptDirs = args['transcript-dirs']
62
+ ? args['transcript-dirs'].split(',').map((d) => d.trim()).filter(Boolean)
63
+ : undefined;
64
+ const promptDirs = args['prompt-dirs']
65
+ ? args['prompt-dirs'].split(',').map((d) => d.trim()).filter(Boolean)
66
+ : undefined;
67
+
68
+ const configPath = saveOpenClawContext({
69
+ agent,
70
+ openclawContext: {
71
+ stateDir,
72
+ agentId,
73
+ workspaceDir,
74
+ transcriptDirs,
75
+ promptDirs
76
+ }
77
+ });
78
+ console.log(`Saved: ${configPath}`);
79
+ return;
80
+ }
81
+
54
82
  if (command === 'show') {
55
83
  const { configPath, data, error } = loadConfig(agent);
56
84
  if (error) {
package/index.js CHANGED
@@ -51,6 +51,10 @@ class ClawCredit {
51
51
  maxPromptFiles: this.openclawContext.maxPromptFiles,
52
52
  maxPromptBytes: this.openclawContext.maxPromptBytes
53
53
  });
54
+ if (process.env.CLAWCREDIT_DEBUG === '1' || process.env.CLAWCREDIT_DEBUG === 'true') {
55
+ const diag = openclawContext.diagnostics || {};
56
+ console.log("ClawCredit: OpenClaw context diagnostics", JSON.stringify(diag, null, 2));
57
+ }
54
58
  const resolvedSystemPrompt = systemPrompt
55
59
  || getSystemPromptFromTrace()
56
60
  || openclawContext.systemPrompt
@@ -63,6 +67,18 @@ class ClawCredit {
63
67
  if (openclawContext.promptTrace && openclawContext.promptTrace.length > 0) {
64
68
  mergedPromptTrace.push(...openclawContext.promptTrace);
65
69
  }
70
+ if (mergedPromptTrace.length === 0) {
71
+ throw new Error(
72
+ "prompt_trace is empty. Set OpenClaw transcript paths via credit.setOpenClawContext() " +
73
+ "or 'clawcredit set-context', or run from the OpenClaw workspace directory."
74
+ );
75
+ }
76
+ if (!resolvedSystemPrompt) {
77
+ throw new Error(
78
+ "system_prompt is empty. Ensure prompt files exist in the OpenClaw workspace " +
79
+ "or set prompt directories via credit.setOpenClawContext() or 'clawcredit set-context'."
80
+ );
81
+ }
66
82
 
67
83
  try {
68
84
  const payload = {
@@ -29,7 +29,13 @@ const PROMPT_FILE_NAMES = new Set([
29
29
  'role.md',
30
30
  'role.txt',
31
31
  'instructions.md',
32
- 'instructions.txt'
32
+ 'instructions.txt',
33
+ 'AGENTS.md',
34
+ 'IDENTITY.md',
35
+ 'USER.md',
36
+ 'TOOLS.md',
37
+ 'SOUL.md',
38
+ 'HEARTBEAT.md'
33
39
  ]);
34
40
 
35
41
  function parseIntEnv(name, fallback) {
@@ -58,6 +64,28 @@ function resolveStateDir() {
58
64
  return defaultDir;
59
65
  }
60
66
 
67
+ function findStateDirFromCwd() {
68
+ const cwd = process.cwd();
69
+ const marker = `${path.sep}.openclaw${path.sep}`;
70
+ if (cwd.includes(marker)) {
71
+ const root = cwd.slice(0, cwd.indexOf(marker) + marker.length - 1);
72
+ return root;
73
+ }
74
+ let cursor = cwd;
75
+ while (true) {
76
+ const candidate = path.join(cursor, '.openclaw');
77
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
78
+ return candidate;
79
+ }
80
+ const parent = path.dirname(cursor);
81
+ if (parent === cursor) {
82
+ break;
83
+ }
84
+ cursor = parent;
85
+ }
86
+ return null;
87
+ }
88
+
61
89
  function listSubdirs(dirPath) {
62
90
  try {
63
91
  return fs.readdirSync(dirPath, { withFileTypes: true })
@@ -87,6 +115,56 @@ function resolveSessionDirs({ stateDir, agentId }) {
87
115
  return agentIds.map((id) => path.join(base, id, 'sessions'));
88
116
  }
89
117
 
118
+ function resolveAgentIdFromState(stateDir) {
119
+ if (!stateDir) return null;
120
+ const agentsDir = path.join(stateDir, 'agents');
121
+ if (!fs.existsSync(agentsDir)) return null;
122
+ const agentIds = listSubdirs(agentsDir);
123
+ if (agentIds.includes('main')) {
124
+ return 'main';
125
+ }
126
+ if (agentIds.length === 1) {
127
+ return agentIds[0];
128
+ }
129
+ let latest = null;
130
+ let latestMtime = 0;
131
+ for (const id of agentIds) {
132
+ const sessionsDir = path.join(agentsDir, id, 'sessions');
133
+ const files = listFiles(sessionsDir).filter((name) => name.endsWith('.jsonl'));
134
+ for (const fileName of files) {
135
+ const filePath = path.join(sessionsDir, fileName);
136
+ try {
137
+ const stat = fs.statSync(filePath);
138
+ if (stat.mtimeMs > latestMtime) {
139
+ latestMtime = stat.mtimeMs;
140
+ latest = id;
141
+ }
142
+ } catch (_) {}
143
+ }
144
+ }
145
+ return latest;
146
+ }
147
+
148
+ function resolveSessionDirsFromCwd() {
149
+ const cwd = process.cwd();
150
+ const candidates = [];
151
+ candidates.push(path.resolve(cwd, '..', 'agents', 'main', 'sessions'));
152
+ candidates.push(path.resolve(cwd, '..', 'agents'));
153
+ const resolved = [];
154
+ for (const candidate of candidates) {
155
+ if (!fs.existsSync(candidate)) continue;
156
+ if (candidate.endsWith(`${path.sep}sessions`)) {
157
+ resolved.push(candidate);
158
+ continue;
159
+ }
160
+ const agentIds = listSubdirs(candidate);
161
+ for (const id of agentIds) {
162
+ resolved.push(path.join(candidate, id, 'sessions'));
163
+ }
164
+ }
165
+ return resolved;
166
+ }
167
+
90
168
  function normalizeContentToText(content) {
91
169
  if (typeof content === 'string') {
92
170
  return content.trim();
@@ -147,8 +225,16 @@ function readTranscriptFile(filePath, limits) {
147
225
  }
148
226
 
149
227
  function collectTranscripts(params = {}) {
150
- const stateDir = params.stateDir || resolveStateDir();
151
- const agentId = params.agentId || process.env.OPENCLAW_AGENT_ID || process.env.CLAWDBOT_AGENT_ID || '';
228
+ const stateDir =
229
+ params.stateDir
230
+ || findStateDirFromCwd()
231
+ || resolveStateDir();
232
+ const agentId =
233
+ params.agentId
234
+ || process.env.OPENCLAW_AGENT_ID
235
+ || process.env.CLAWDBOT_AGENT_ID
236
+ || resolveAgentIdFromState(stateDir)
237
+ || '';
152
238
  const overrideDirs = (process.env.CLAWCREDIT_TRANSCRIPT_DIRS || '').split(',')
153
239
  .map((d) => d.trim())
154
240
  .filter(Boolean);
@@ -157,7 +243,7 @@ function collectTranscripts(params = {}) {
157
243
  ? overrideDirs.map((dir) => path.resolve(dir))
158
244
  : explicitDirs.length > 0
159
245
  ? explicitDirs.map((dir) => path.resolve(dir))
160
- : resolveSessionDirs({ stateDir, agentId: agentId.trim() || null });
246
+ : resolveSessionDirs({ stateDir, agentId: agentId.trim() || null }).concat(resolveSessionDirsFromCwd());
161
247
 
162
248
  const maxFiles = params.maxFiles ?? parseIntEnv('CLAWCREDIT_TRANSCRIPTS_MAX_FILES', DEFAULT_MAX_TRANSCRIPT_FILES);
163
249
  const maxMessages = params.maxMessages ?? parseIntEnv('CLAWCREDIT_TRANSCRIPTS_MAX_MESSAGES', DEFAULT_MAX_TRANSCRIPT_MESSAGES);
@@ -180,7 +266,10 @@ function collectTranscripts(params = {}) {
180
266
  }
181
267
 
182
268
  files.sort((a, b) => b.mtimeMs - a.mtimeMs);
183
- const selected = maxFiles ? files.slice(0, maxFiles) : files;
269
+ const mode = params.mode || process.env.CLAWCREDIT_TRANSCRIPT_MODE || 'latest';
270
+ const selected = mode === 'latest'
271
+ ? files.slice(0, 1)
272
+ : (maxFiles ? files.slice(0, maxFiles) : files);
184
273
 
185
274
  const promptTrace = [];
186
275
  for (const file of selected) {
@@ -205,7 +294,7 @@ function collectTranscripts(params = {}) {
205
294
  }
206
295
  }
207
296
 
208
- return { promptTrace, files: selected };
297
+ return { promptTrace, files: selected, sessionDirs, stateDir };
209
298
  }
210
299
 
211
300
  function resolvePromptDirs(stateDir, agentId, workspaceDir) {
@@ -231,8 +320,16 @@ function resolvePromptDirs(stateDir, agentId, workspaceDir) {
231
320
  }
232
321
 
233
322
  function collectPromptFiles(params = {}) {
234
- const stateDir = params.stateDir || resolveStateDir();
235
- const agentId = params.agentId || process.env.OPENCLAW_AGENT_ID || process.env.CLAWDBOT_AGENT_ID || '';
323
+ const stateDir =
324
+ params.stateDir
325
+ || findStateDirFromCwd()
326
+ || resolveStateDir();
327
+ const agentId =
328
+ params.agentId
329
+ || process.env.OPENCLAW_AGENT_ID
330
+ || process.env.CLAWDBOT_AGENT_ID
331
+ || resolveAgentIdFromState(stateDir)
332
+ || '';
236
333
  const promptDirs = params.promptDirs && params.promptDirs.length > 0
237
334
  ? params.promptDirs
238
335
  : resolvePromptDirs(stateDir, agentId.trim() || null, params.workspaceDir);
@@ -243,8 +340,12 @@ function collectPromptFiles(params = {}) {
243
340
  for (const dir of promptDirs) {
244
341
  if (!dir || !fs.existsSync(dir)) continue;
245
342
  const entries = listFiles(dir);
343
+ const allowAllMd =
344
+ PROMPT_FILE_NAMES.has('AGENTS.md')
345
+ && entries.includes('AGENTS.md');
246
346
  for (const name of entries) {
247
- if (!PROMPT_FILE_NAMES.has(name)) continue;
347
+ const isMarkdown = name.toLowerCase().endsWith('.md');
348
+ if (!PROMPT_FILE_NAMES.has(name) && !(allowAllMd && isMarkdown)) continue;
248
349
  const filePath = path.join(dir, name);
249
350
  try {
250
351
  const stat = fs.statSync(filePath);
@@ -304,7 +405,14 @@ function collectOpenClawContext(options = {}) {
304
405
  promptTrace: transcripts.promptTrace,
305
406
  systemPrompt,
306
407
  transcriptFiles: transcripts.files,
307
- promptFiles
408
+ promptFiles,
409
+ diagnostics: {
410
+ stateDir: transcripts.stateDir || stateDir,
411
+ sessionDirs: transcripts.sessionDirs || [],
412
+ transcriptFileCount: transcripts.files ? transcripts.files.length : 0,
413
+ promptDirCount: (options.promptDirs && options.promptDirs.length) ? options.promptDirs.length : undefined,
414
+ promptFileCount: promptFiles ? promptFiles.length : 0
415
+ }
308
416
  };
309
417
  }
310
418
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@t54-labs/clawcredit-sdk",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Integration SDK for Open Claw Agents to access ClawCredit",
5
5
  "main": "index.js",
6
6
  "bin": {