trucontext 0.6.0 → 0.7.1

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
@@ -17,17 +17,20 @@ That's it. No API keys to copy, no config files to edit. Browser-based OAuth, th
17
17
  ## The 30-Second Version
18
18
 
19
19
  ```bash
20
- # Create an entity for the person your agent works with
21
- npx trucontext entities create --id dustin --type Person \
22
- --properties '{"name": "Dustin", "role": "founder"}' \
23
- --recipe recipe:personal-assistant-memory
20
+ # Create a root node the intelligence boundary for your agent's memory
21
+ npx trucontext roots create --id dustin --type Person \
22
+ --recipe recipe:personal-assistant-memory \
23
+ --properties '{"name": "Dustin", "role": "founder"}'
24
+
25
+ # Set it as the active root for queries
26
+ npx trucontext roots use dustin
24
27
 
25
28
  # Ingest what your agent learned
26
29
  npx trucontext ingest "Dustin prefers async communication and dark mode. \
27
30
  He values clean code over backwards compatibility." \
28
31
  --context dustin:ABOUT --confidence 0.85 --temporal
29
32
 
30
- # Ask an intelligent question
33
+ # Ask an intelligent question (scoped to the root's ego network)
31
34
  npx trucontext query "What does Dustin prefer?"
32
35
  ```
33
36
 
@@ -135,28 +138,35 @@ Observe → Capture → Sleep → Wake → Query → Observe → ...
135
138
  ### Apps
136
139
  | Command | Description |
137
140
  |---------|-------------|
138
- | `init [name]` | Create a new app with AI-generated schema |
141
+ | `init [name]` | Create a new app with schema + root node |
139
142
  | `apps` | List your apps |
140
143
  | `use <app>` | Set the active app |
141
144
 
145
+ ### Root Nodes
146
+ | Command | Description |
147
+ |---------|-------------|
148
+ | `roots create` | Create a root node with recipe and dreamers |
149
+ | `roots list` | List all root nodes |
150
+ | `roots show <id>` | Show root node details and linked roots |
151
+ | `roots update <id>` | Update recipe, dreamers, or properties |
152
+ | `roots delete <id>` | Demote to regular entity |
153
+ | `roots use <id>` | Set active root for queries |
154
+
142
155
  ### Data
143
156
  | Command | Description |
144
157
  |---------|-------------|
145
- | `ingest <source>` | Ingest text or file with confidence, temporal, and context linking |
146
- | `query <question>` | Query the intelligent knowledge graph |
147
- | `recall <query>` | Semantic memory recall with graph expansion |
158
+ | `ingest <source>` | Ingest text or file (requires `--context`) |
159
+ | `query <question>` | Query the graph (requires active root or `--root`) |
160
+ | `recall <query>` | Semantic recall (requires active root or `--root`) |
148
161
 
149
- ### Structure
162
+ ### Entities
150
163
  | Command | Description |
151
164
  |---------|-------------|
152
165
  | `entities list` | List entities |
153
- | `entities create` | Create entity with recipe, confidence, temporal, contexts |
166
+ | `entities create` | Create entity (requires `--context`) |
154
167
  | `entities get <id>` | Get entity details |
155
- | `entities update <id>` | Update entity properties, recipe, heartbeat settings |
168
+ | `entities update <id>` | Update properties, recipe, heartbeat |
156
169
  | `entities delete <id>` | Delete entity |
157
- | `contexts list` | List contexts |
158
- | `contexts create <name>` | Create a scoping context |
159
- | `contexts delete <id>` | Delete context |
160
170
 
161
171
  ### Intelligence
162
172
  | Command | Description |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trucontext",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "TruContext CLI — contextual memory for AI applications",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,5 +1,6 @@
1
1
  import chalk from 'chalk';
2
- import { dataPlane } from '../client.js';
2
+ import { controlPlane } from '../client.js';
3
+ import { getActiveApp } from '../config.js';
3
4
 
4
5
  // Fields stored on the node but not user-declared properties
5
6
  const SYSTEM_FIELDS = new Set([
@@ -38,15 +39,16 @@ function displayEntity(e) {
38
39
 
39
40
  export async function entitiesListCommand(options) {
40
41
  try {
42
+ const appId = getActiveApp();
41
43
  const params = new URLSearchParams();
42
44
  if (options.type) params.set('type', options.type);
43
45
  if (options.context) params.set('context_id', options.context);
44
46
  if (options.scope) params.set('scope', options.scope);
45
47
  if (options.limit) params.set('limit', options.limit);
46
48
  const qs = params.toString();
47
- const path = `/v1/entities${qs ? `?${qs}` : ''}`;
49
+ const path = `/apps/${appId}/entities${qs ? `?${qs}` : ''}`;
48
50
 
49
- const res = await dataPlane('GET', path);
51
+ const res = await controlPlane('GET', path);
50
52
  const entities = Array.isArray(res.data) ? res.data : (res.data?.entities || []);
51
53
 
52
54
  if (entities.length === 0) {
@@ -66,13 +68,14 @@ export async function entitiesListCommand(options) {
66
68
 
67
69
  export async function entitiesGetCommand(entityId) {
68
70
  try {
69
- const res = await dataPlane('GET', `/v1/entities/${entityId}`);
71
+ const appId = getActiveApp();
72
+ const res = await controlPlane('GET', `/apps/${appId}/entities/${entityId}`);
70
73
  const e = res.data || res;
71
74
  displayEntity(e);
72
75
 
73
76
  // Show edges if available
74
77
  try {
75
- const edgeRes = await dataPlane('GET', `/v1/entities/${entityId}/edges`);
78
+ const edgeRes = await controlPlane('GET', `/apps/${appId}/entities/${entityId}/edges`);
76
79
  const edges = edgeRes.data?.edges || edgeRes.data || [];
77
80
  if (edges.length > 0) {
78
81
  console.log(chalk.dim(`\n Edges (${edges.length}):`));
@@ -101,6 +104,7 @@ export async function entitiesCreateCommand(options) {
101
104
  process.exit(1);
102
105
  }
103
106
 
107
+ const appId = getActiveApp();
104
108
  const body = {
105
109
  entityId: options.id,
106
110
  type: options.type,
@@ -133,7 +137,7 @@ export async function entitiesCreateCommand(options) {
133
137
  });
134
138
  }
135
139
 
136
- const res = await dataPlane('POST', '/v1/entities', body);
140
+ const res = await controlPlane('POST', `/apps/${appId}/entities`, body);
137
141
  const e = res.data || res;
138
142
  console.log(chalk.green(`Created: ${chalk.bold(e.entityId || options.id)}`));
139
143
  } catch (err) {
@@ -144,6 +148,7 @@ export async function entitiesCreateCommand(options) {
144
148
 
145
149
  export async function entitiesUpdateCommand(entityId, options) {
146
150
  try {
151
+ const appId = getActiveApp();
147
152
  const body = {};
148
153
 
149
154
  if (options.properties) {
@@ -173,7 +178,7 @@ export async function entitiesUpdateCommand(entityId, options) {
173
178
  });
174
179
  }
175
180
 
176
- await dataPlane('PUT', `/v1/entities/${entityId}`, body);
181
+ await controlPlane('PUT', `/apps/${appId}/entities/${entityId}`, body);
177
182
  console.log(chalk.green(`Updated: ${entityId}`));
178
183
  } catch (err) {
179
184
  console.error(chalk.red(`Failed: ${err.message}`));
@@ -183,7 +188,8 @@ export async function entitiesUpdateCommand(entityId, options) {
183
188
 
184
189
  export async function entitiesDeleteCommand(entityId) {
185
190
  try {
186
- await dataPlane('DELETE', `/v1/entities/${entityId}`);
191
+ const appId = getActiveApp();
192
+ await controlPlane('DELETE', `/apps/${appId}/entities/${entityId}`);
187
193
  console.log(chalk.green(`Deleted: ${entityId}`));
188
194
  } catch (err) {
189
195
  console.error(chalk.red(`Failed: ${err.message}`));
@@ -193,7 +199,8 @@ export async function entitiesDeleteCommand(entityId) {
193
199
 
194
200
  export async function entitiesEdgesCommand(entityId) {
195
201
  try {
196
- const res = await dataPlane('GET', `/v1/entities/${entityId}/edges`);
202
+ const appId = getActiveApp();
203
+ const res = await controlPlane('GET', `/apps/${appId}/entities/${entityId}/edges`);
197
204
  const edges = res.data?.edges || res.data || [];
198
205
 
199
206
  if (edges.length === 0) {
@@ -1,10 +1,12 @@
1
1
  import chalk from 'chalk';
2
2
  import { readFileSync, existsSync } from 'fs';
3
3
  import { basename } from 'path';
4
- import { dataPlane } from '../client.js';
4
+ import { controlPlane } from '../client.js';
5
+ import { getActiveApp } from '../config.js';
5
6
 
6
7
  export async function ingestCommand(source, options) {
7
8
  try {
9
+ const appId = getActiveApp();
8
10
  let body;
9
11
 
10
12
  if (existsSync(source)) {
@@ -50,11 +52,12 @@ export async function ingestCommand(source, options) {
50
52
  };
51
53
  });
52
54
 
53
- const res = await dataPlane('POST', '/v1/ingest', body);
55
+ const res = await controlPlane('POST', `/apps/${appId}/ingest`, body);
56
+ const data = res.data || res;
54
57
  console.log(chalk.green('Accepted'));
55
- console.log(chalk.dim(`Content ID: ${res.contentId}`));
56
- if (res.status === 'upload_required') {
57
- console.log(chalk.yellow(`Large file — upload to: ${res.uploadUrl}`));
58
+ console.log(chalk.dim(`Content ID: ${data.contentId}`));
59
+ if (data.status === 'upload_required') {
60
+ console.log(chalk.yellow(`Large file — upload to: ${data.uploadUrl}`));
58
61
  }
59
62
  } catch (err) {
60
63
  console.error(chalk.red(`Ingest failed: ${err.message}`));
@@ -105,7 +105,7 @@ export async function initCommand(name, options) {
105
105
  const rootName = await input({ message: 'Display name (e.g., Dustin Henderson):' });
106
106
 
107
107
  try {
108
- await controlPlane('POST', '/roots', {
108
+ await controlPlane('POST', `/apps/${app.appId}/roots`, {
109
109
  entityId: rootId,
110
110
  type: rootType,
111
111
  recipe_id: rootRecipe,
@@ -1,9 +1,11 @@
1
1
  import chalk from 'chalk';
2
- import { dataPlane } from '../client.js';
3
- import { getConfig } from '../config.js';
2
+ import { controlPlane } from '../client.js';
3
+ import { getConfig, getActiveApp } from '../config.js';
4
4
 
5
5
  export async function queryCommand(question, options) {
6
6
  try {
7
+ const appId = getActiveApp();
8
+
7
9
  // root_id is required — from --root flag or active root
8
10
  const rootId = options.root || getConfig().activeRoot;
9
11
  if (!rootId) {
@@ -25,34 +27,35 @@ export async function queryCommand(question, options) {
25
27
 
26
28
  if (options.context) body.context_id = options.context;
27
29
 
28
- const res = await dataPlane('POST', '/v1/query', body);
30
+ const res = await controlPlane('POST', `/apps/${appId}/mind/query`, body);
31
+ const data = res.data || res;
29
32
 
30
33
  // Print answer
31
- if (res.answer?.summary) {
34
+ if (data.answer?.summary) {
32
35
  console.log(chalk.bold('\nAnswer:'));
33
- console.log(res.answer.summary);
34
- if (res.answer.confidence) {
35
- console.log(chalk.dim(`\nConfidence: ${(res.answer.confidence * 100).toFixed(0)}%`));
36
+ console.log(data.answer.summary);
37
+ if (data.answer.confidence) {
38
+ console.log(chalk.dim(`\nConfidence: ${(data.answer.confidence * 100).toFixed(0)}%`));
36
39
  }
37
40
  }
38
41
 
39
42
  // Print concepts
40
- if (res.concepts?.length > 0) {
43
+ if (data.concepts?.length > 0) {
41
44
  console.log(chalk.bold('\nConcepts:'));
42
- for (const c of res.concepts.slice(0, 5)) {
45
+ for (const c of data.concepts.slice(0, 5)) {
43
46
  console.log(` ${c.label} ${chalk.dim(`(${c.evidence_count} evidence)`)}`);
44
47
  }
45
48
  }
46
49
 
47
50
  // Print people
48
- if (res.people?.length > 0) {
51
+ if (data.people?.length > 0) {
49
52
  console.log(chalk.bold('\nPeople:'));
50
- for (const p of res.people.slice(0, 5)) {
53
+ for (const p of data.people.slice(0, 5)) {
51
54
  console.log(` ${p.name} ${chalk.dim(p.relationship || '')}`);
52
55
  }
53
56
  }
54
57
 
55
- console.log(chalk.dim(`\n${res.latency_ms}ms`));
58
+ console.log(chalk.dim(`\n${data.latency_ms}ms`));
56
59
  } catch (err) {
57
60
  console.error(chalk.red(`Query failed: ${err.message}`));
58
61
  process.exit(1);
@@ -1,9 +1,11 @@
1
1
  import chalk from 'chalk';
2
- import { dataPlane } from '../client.js';
3
- import { getConfig } from '../config.js';
2
+ import { controlPlane } from '../client.js';
3
+ import { getConfig, getActiveApp } from '../config.js';
4
4
 
5
5
  export async function recallCommand(query, options) {
6
6
  try {
7
+ const appId = getActiveApp();
8
+
7
9
  // root_id is required — from --root flag or active root
8
10
  const rootId = options.root || getConfig().activeRoot;
9
11
  if (!rootId) {
@@ -20,18 +22,19 @@ export async function recallCommand(query, options) {
20
22
 
21
23
  if (options.context) body.context_id = options.context;
22
24
 
23
- const res = await dataPlane('POST', '/v1/recall', body);
25
+ const res = await controlPlane('POST', `/apps/${appId}/recall`, body);
26
+ const data = res.data || res;
24
27
 
25
28
  // Print synthesis
26
- if (res.synthesis?.summary) {
29
+ if (data.synthesis?.summary) {
27
30
  console.log(chalk.bold('\nSynthesis:'));
28
- console.log(res.synthesis.summary);
31
+ console.log(data.synthesis.summary);
29
32
  }
30
33
 
31
34
  // Print seeds
32
- if (res.seeds?.length > 0) {
33
- console.log(chalk.bold(`\nSeeds (${res.seeds.length}):`));
34
- for (const s of res.seeds) {
35
+ if (data.seeds?.length > 0) {
36
+ console.log(chalk.bold(`\nSeeds (${data.seeds.length}):`));
37
+ for (const s of data.seeds) {
35
38
  const label = s.snippet || s.label || s.id;
36
39
  const score = (s.score * 100).toFixed(0);
37
40
  console.log(` ${chalk.dim(`${score}%`)} ${label.slice(0, 100)}`);
@@ -39,11 +42,11 @@ export async function recallCommand(query, options) {
39
42
  }
40
43
 
41
44
  // Print graph context summary
42
- if (res.context) {
43
- console.log(chalk.dim(`\nGraph: ${res.context.nodes?.length || 0} nodes, ${res.context.edges?.length || 0} edges`));
45
+ if (data.context) {
46
+ console.log(chalk.dim(`\nGraph: ${data.context.nodes?.length || 0} nodes, ${data.context.edges?.length || 0} edges`));
44
47
  }
45
48
 
46
- console.log(chalk.dim(`${res.latency_ms}ms`));
49
+ console.log(chalk.dim(`${data.latency_ms}ms`));
47
50
  } catch (err) {
48
51
  console.error(chalk.red(`Recall failed: ${err.message}`));
49
52
  process.exit(1);
@@ -5,7 +5,7 @@
5
5
 
6
6
  import chalk from 'chalk';
7
7
  import { controlPlane } from '../client.js';
8
- import { getConfig, saveConfig } from '../config.js';
8
+ import { getConfig, saveConfig, getActiveApp } from '../config.js';
9
9
 
10
10
  export function registerRootsCommand(program) {
11
11
  const roots = program
@@ -20,7 +20,7 @@ export function registerRootsCommand(program) {
20
20
  .requiredOption('--id <entityId>', 'Unique identifier for the root node')
21
21
  .requiredOption('--type <type>', 'Node type (e.g., Person, Agent, Organization)')
22
22
  .requiredOption('--recipe <recipeId>', 'Recipe ID (e.g., recipe:personal-assistant-memory)')
23
- .option('--dreamers <list>', 'Dreamers: "all" or comma-separated (e.g., decay,curiosity)', 'all')
23
+ .option('--dreamers <list>', 'Dreamers: "all" or comma-separated. Valid: decay, concepts, people, primitives, curiosity, entity-linking, consolidation, fitness', 'all')
24
24
  .option('--hop-depth <n>', 'Max traversal depth for ego network (default: unlimited)', parseInt)
25
25
  .option('--do-not-follow', 'Mark as structural root — do not traverse into from other roots')
26
26
  .option('--properties <json>', 'JSON properties object')
@@ -54,7 +54,8 @@ export function registerRootsCommand(program) {
54
54
  });
55
55
  }
56
56
 
57
- const res = await controlPlane('POST', '/roots', body);
57
+ const appId = getActiveApp();
58
+ const res = await controlPlane('POST', `/apps/${appId}/roots`, body);
58
59
  const r = res.data;
59
60
  console.log(chalk.green(`Root node created: ${r.entityId}`));
60
61
  console.log(` Type: ${r.type}`);
@@ -73,7 +74,8 @@ export function registerRootsCommand(program) {
73
74
  .description('List all root nodes for the active app')
74
75
  .action(async () => {
75
76
  try {
76
- const res = await controlPlane('GET', '/roots');
77
+ const appId = getActiveApp();
78
+ const res = await controlPlane('GET', `/apps/${appId}/roots`);
77
79
  const rootNodes = res.data?.roots || [];
78
80
  if (rootNodes.length === 0) {
79
81
  console.log('No root nodes. Create one with: trucontext roots create');
@@ -99,7 +101,8 @@ export function registerRootsCommand(program) {
99
101
  .description('Show details of a root node')
100
102
  .action(async (rootId) => {
101
103
  try {
102
- const res = await controlPlane('GET', `/roots/${rootId}`);
104
+ const appId = getActiveApp();
105
+ const res = await controlPlane('GET', `/apps/${appId}/roots/${rootId}`);
103
106
  const r = res.data;
104
107
  console.log(chalk.bold(`Root: ${r.entityId}`));
105
108
  console.log(` Type: ${r.type}`);
@@ -125,7 +128,7 @@ export function registerRootsCommand(program) {
125
128
  .command('update <rootId>')
126
129
  .description('Update a root node')
127
130
  .option('--recipe <recipeId>', 'New recipe ID')
128
- .option('--dreamers <list>', 'New dreamers: "all" or comma-separated')
131
+ .option('--dreamers <list>', 'New dreamers: "all" or comma-separated. Valid: decay, concepts, people, primitives, curiosity, entity-linking, consolidation, fitness')
129
132
  .option('--hop-depth <n>', 'New hop depth', parseInt)
130
133
  .option('--do-not-follow', 'Set do-not-follow flag')
131
134
  .option('--no-do-not-follow', 'Clear do-not-follow flag')
@@ -144,7 +147,8 @@ export function registerRootsCommand(program) {
144
147
  }
145
148
  }
146
149
 
147
- await controlPlane('PUT', `/roots/${rootId}`, body);
150
+ const appId = getActiveApp();
151
+ await controlPlane('PUT', `/apps/${appId}/roots/${rootId}`, body);
148
152
  console.log(chalk.green(`Updated: ${rootId}`));
149
153
  } catch (err) {
150
154
  console.error(chalk.red('Failed:'), err.response?.data?.error || err.message);
@@ -158,7 +162,8 @@ export function registerRootsCommand(program) {
158
162
  .description('Demote a root node to a regular entity (does not delete the node)')
159
163
  .action(async (rootId) => {
160
164
  try {
161
- await controlPlane('DELETE', `/roots/${rootId}`);
165
+ const appId = getActiveApp();
166
+ await controlPlane('DELETE', `/apps/${appId}/roots/${rootId}`);
162
167
  console.log(chalk.green(`Demoted: ${rootId} (no longer a root node)`));
163
168
 
164
169
  const config = getConfig();
@@ -178,7 +183,8 @@ export function registerRootsCommand(program) {
178
183
  .description('Set the active root node for queries and recall')
179
184
  .action(async (rootId) => {
180
185
  try {
181
- await controlPlane('GET', `/roots/${rootId}`);
186
+ const appId = getActiveApp();
187
+ await controlPlane('GET', `/apps/${appId}/roots/${rootId}`);
182
188
  const config = getConfig();
183
189
  config.activeRoot = rootId;
184
190
  saveConfig(config);