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 +25 -15
- package/package.json +1 -1
- package/src/commands/entities.js +16 -9
- package/src/commands/ingest.js +8 -5
- package/src/commands/init.js +1 -1
- package/src/commands/query.js +15 -12
- package/src/commands/recall.js +14 -11
- package/src/commands/roots.js +15 -9
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
|
|
21
|
-
npx trucontext
|
|
22
|
-
--
|
|
23
|
-
--
|
|
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
|
|
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
|
|
146
|
-
| `query <question>` | Query the
|
|
147
|
-
| `recall <query>` | Semantic
|
|
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
|
-
###
|
|
162
|
+
### Entities
|
|
150
163
|
| Command | Description |
|
|
151
164
|
|---------|-------------|
|
|
152
165
|
| `entities list` | List entities |
|
|
153
|
-
| `entities create` | Create entity
|
|
166
|
+
| `entities create` | Create entity (requires `--context`) |
|
|
154
167
|
| `entities get <id>` | Get entity details |
|
|
155
|
-
| `entities update <id>` | Update
|
|
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
package/src/commands/entities.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import {
|
|
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 = `/
|
|
49
|
+
const path = `/apps/${appId}/entities${qs ? `?${qs}` : ''}`;
|
|
48
50
|
|
|
49
|
-
const res = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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) {
|
package/src/commands/ingest.js
CHANGED
|
@@ -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 {
|
|
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
|
|
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: ${
|
|
56
|
-
if (
|
|
57
|
-
console.log(chalk.yellow(`Large file — upload to: ${
|
|
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}`));
|
package/src/commands/init.js
CHANGED
|
@@ -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',
|
|
108
|
+
await controlPlane('POST', `/apps/${app.appId}/roots`, {
|
|
109
109
|
entityId: rootId,
|
|
110
110
|
type: rootType,
|
|
111
111
|
recipe_id: rootRecipe,
|
package/src/commands/query.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import {
|
|
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
|
|
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 (
|
|
34
|
+
if (data.answer?.summary) {
|
|
32
35
|
console.log(chalk.bold('\nAnswer:'));
|
|
33
|
-
console.log(
|
|
34
|
-
if (
|
|
35
|
-
console.log(chalk.dim(`\nConfidence: ${(
|
|
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 (
|
|
43
|
+
if (data.concepts?.length > 0) {
|
|
41
44
|
console.log(chalk.bold('\nConcepts:'));
|
|
42
|
-
for (const c of
|
|
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 (
|
|
51
|
+
if (data.people?.length > 0) {
|
|
49
52
|
console.log(chalk.bold('\nPeople:'));
|
|
50
|
-
for (const p of
|
|
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${
|
|
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);
|
package/src/commands/recall.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import {
|
|
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
|
|
25
|
+
const res = await controlPlane('POST', `/apps/${appId}/recall`, body);
|
|
26
|
+
const data = res.data || res;
|
|
24
27
|
|
|
25
28
|
// Print synthesis
|
|
26
|
-
if (
|
|
29
|
+
if (data.synthesis?.summary) {
|
|
27
30
|
console.log(chalk.bold('\nSynthesis:'));
|
|
28
|
-
console.log(
|
|
31
|
+
console.log(data.synthesis.summary);
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
// Print seeds
|
|
32
|
-
if (
|
|
33
|
-
console.log(chalk.bold(`\nSeeds (${
|
|
34
|
-
for (const s of
|
|
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 (
|
|
43
|
-
console.log(chalk.dim(`\nGraph: ${
|
|
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(`${
|
|
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);
|
package/src/commands/roots.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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);
|