@asobacloud/nehanda 0.0.0-stage → 0.2.0
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/LICENSE +21 -0
- package/LICENSE.md +57 -0
- package/README.md +404 -2
- package/bin/agent.mjs +1260 -0
- package/bin/nehanda-ui.mjs +824 -0
- package/lib/anthropicHeaders.mjs +8 -0
- package/lib/app.mjs +313 -0
- package/lib/auth.mjs +255 -0
- package/lib/bashguard.mjs +159 -0
- package/lib/compact.mjs +306 -0
- package/lib/energy-middleware.mjs +318 -0
- package/lib/hookShapeOrder.mjs +30 -0
- package/lib/hookplane.mjs +228 -0
- package/lib/instructions.mjs +23 -0
- package/lib/jev_read.mjs +216 -0
- package/lib/jev_write.mjs +253 -0
- package/lib/laya.mjs +296 -0
- package/lib/laya_sidecar.mjs +198 -0
- package/lib/mcp-config.mjs +189 -0
- package/lib/mcp.mjs +169 -0
- package/lib/modelConfig.mjs +152 -0
- package/lib/modelDiscovery.mjs +205 -0
- package/lib/openaiCompat.mjs +147 -0
- package/lib/orchestrate.mjs +1163 -0
- package/lib/paths.mjs +20 -0
- package/lib/permissions.mjs +44 -0
- package/lib/scripts/__pycache__/laya_worker.cpython-314.pyc +0 -0
- package/lib/scripts/audit-code-integrity.py +437 -0
- package/lib/scripts/ensure-laya-env.sh +55 -0
- package/lib/scripts/js-safety-checker.cjs +308 -0
- package/lib/scripts/laya_worker.py +171 -0
- package/lib/scripts/odse-transform.py +97 -0
- package/lib/scripts/python-safety-checker.py +197 -0
- package/lib/scripts/shell-safety-checker.sh +306 -0
- package/lib/settings.mjs +147 -0
- package/lib/store.mjs +278 -0
- package/lib/team.mjs +69 -0
- package/lib/testgen.mjs +146 -0
- package/lib/tools/AuditCodeIntegrity.json +30 -0
- package/lib/tools/JsSafetyChecker.json +29 -0
- package/lib/tools/PythonSafetyChecker.json +29 -0
- package/lib/tools/ShellSafetyChecker.json +29 -0
- package/lib/tools.mjs +907 -0
- package/lib/transcript.mjs +223 -0
- package/lib/ui.mjs +286 -0
- package/lib/workflow.mjs +91 -0
- package/lib/worktree.mjs +56 -0
- package/package.json +50 -4
- package/schema.sql +181 -0
- package/scripts/postinstall.mjs +62 -0
- package/scripts/sdlc-acceptance.sh +477 -0
- package/scripts/verify-sdlc-hook-order.mjs +26 -0
- package/templates/test_e2e.sh +32 -0
- package/templates/test_file.sh +14 -0
- package/templates/test_hook.sh +27 -0
- package/templates/test_http.sh +17 -0
- package/templates/test_phase.sh +23 -0
- package/templates/test_process.sh +14 -0
- package/templates/test_tool.sh +24 -0
- package/templates/validate_test.sh +55 -0
package/package.json
CHANGED
|
@@ -1,6 +1,52 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asobacloud/nehanda",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Agentic terminal REPL and single-process engine for governed AI deep research and software development. Connects to Nehanda v3, Ollama, LM Studio, or any OpenAI-compatible API.",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=22"
|
|
8
|
+
},
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public",
|
|
11
|
+
"registry": "https://registry.npmjs.org/"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"bin/",
|
|
15
|
+
"lib/",
|
|
16
|
+
"scripts/",
|
|
17
|
+
"templates/",
|
|
18
|
+
"schema.sql",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE",
|
|
21
|
+
"LICENSE.md"
|
|
22
|
+
],
|
|
23
|
+
"bin": {
|
|
24
|
+
"ona": "./bin/agent.mjs",
|
|
25
|
+
"ona-code": "./bin/agent.mjs",
|
|
26
|
+
"nehanda": "./bin/nehanda-ui.mjs"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"postinstall": "node scripts/postinstall.mjs",
|
|
30
|
+
"start": "node ./bin/agent.mjs",
|
|
31
|
+
"repl": "node ./bin/agent.mjs",
|
|
32
|
+
"verify": "node ./scripts/verify-sdlc-hook-order.mjs",
|
|
33
|
+
"acceptance": "bash ./scripts/sdlc-acceptance.sh",
|
|
34
|
+
"test:bugs": "bash tests/bugs/run-all.sh",
|
|
35
|
+
"test:property": "bash tests/property/run-settings-persistence-tests.sh",
|
|
36
|
+
"test:unit": "bash tests/unit/run-all.sh"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@anthropic-ai/sdk": "^0.39.0",
|
|
40
|
+
"better-sqlite3": "^11.8.1",
|
|
41
|
+
"chalk": "^5.6.2",
|
|
42
|
+
"esprima": "^4.0.1",
|
|
43
|
+
"ink": "^6.8.0",
|
|
44
|
+
"ink-text-input": "^6.0.0",
|
|
45
|
+
"marked": "^15.0.12",
|
|
46
|
+
"marked-terminal": "^7.3.0",
|
|
47
|
+
"react": "^19.2.4"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"fast-check": "^4.7.0"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/schema.sql
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
-- CLEAN_ROOM_SPEC.md §4.3 (canonical DDL)
|
|
2
|
+
|
|
3
|
+
PRAGMA journal_mode = WAL;
|
|
4
|
+
|
|
5
|
+
CREATE TABLE IF NOT EXISTS schema_meta (
|
|
6
|
+
key TEXT PRIMARY KEY,
|
|
7
|
+
value TEXT NOT NULL
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
CREATE TABLE IF NOT EXISTS conversations (
|
|
11
|
+
id TEXT PRIMARY KEY,
|
|
12
|
+
project_dir TEXT NOT NULL,
|
|
13
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
14
|
+
last_active TEXT NOT NULL DEFAULT (datetime('now')),
|
|
15
|
+
phase TEXT NOT NULL DEFAULT 'idle'
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
19
|
+
session_id TEXT PRIMARY KEY,
|
|
20
|
+
conversation_id TEXT NOT NULL REFERENCES conversations(id),
|
|
21
|
+
started_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
CREATE TABLE IF NOT EXISTS state (
|
|
25
|
+
conversation_id TEXT NOT NULL,
|
|
26
|
+
key TEXT NOT NULL,
|
|
27
|
+
value TEXT,
|
|
28
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
29
|
+
PRIMARY KEY (conversation_id, key)
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
CREATE TABLE IF NOT EXISTS plans (
|
|
33
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
34
|
+
conversation_id TEXT NOT NULL,
|
|
35
|
+
file_path TEXT,
|
|
36
|
+
content TEXT NOT NULL,
|
|
37
|
+
hash TEXT NOT NULL,
|
|
38
|
+
status TEXT NOT NULL DEFAULT 'draft',
|
|
39
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
40
|
+
approved_at TEXT,
|
|
41
|
+
completed_at TEXT
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
CREATE TABLE IF NOT EXISTS summaries (
|
|
45
|
+
conversation_id TEXT PRIMARY KEY REFERENCES conversations(id),
|
|
46
|
+
content TEXT NOT NULL,
|
|
47
|
+
word_count INTEGER NOT NULL,
|
|
48
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
52
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
53
|
+
conversation_id TEXT NOT NULL,
|
|
54
|
+
session_id TEXT,
|
|
55
|
+
timestamp TEXT NOT NULL DEFAULT (datetime('now')),
|
|
56
|
+
event_type TEXT NOT NULL,
|
|
57
|
+
detail TEXT
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
CREATE TABLE IF NOT EXISTS task_ratings (
|
|
61
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
62
|
+
conversation_id TEXT NOT NULL,
|
|
63
|
+
rating INTEGER NOT NULL,
|
|
64
|
+
objective TEXT,
|
|
65
|
+
timestamp TEXT NOT NULL DEFAULT (datetime('now'))
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
69
|
+
id TEXT PRIMARY KEY,
|
|
70
|
+
type TEXT NOT NULL,
|
|
71
|
+
title TEXT NOT NULL,
|
|
72
|
+
content TEXT,
|
|
73
|
+
keywords TEXT,
|
|
74
|
+
anticipated_queries TEXT,
|
|
75
|
+
concept_tags TEXT,
|
|
76
|
+
project_scope TEXT,
|
|
77
|
+
correction_count INTEGER DEFAULT 1,
|
|
78
|
+
created_at INTEGER NOT NULL,
|
|
79
|
+
updated_at INTEGER NOT NULL,
|
|
80
|
+
last_accessed INTEGER,
|
|
81
|
+
access_count INTEGER DEFAULT 0,
|
|
82
|
+
attention_score REAL DEFAULT 0.5,
|
|
83
|
+
-- Jev-Mem canonical node extensions (schema_version >= 2)
|
|
84
|
+
conversation_id TEXT,
|
|
85
|
+
session_id TEXT,
|
|
86
|
+
provenance TEXT,
|
|
87
|
+
entities_json TEXT DEFAULT '[]',
|
|
88
|
+
t_episodic REAL DEFAULT 0,
|
|
89
|
+
t_semantic REAL DEFAULT 0,
|
|
90
|
+
t_procedural REAL DEFAULT 0,
|
|
91
|
+
t_preference REAL DEFAULT 0,
|
|
92
|
+
source_entry_id INTEGER,
|
|
93
|
+
observation_ts INTEGER
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
|
|
97
|
+
title, content, keywords, anticipated_queries,
|
|
98
|
+
tokenize='porter unicode61'
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
-- Multi-relational memory plane: independent views over shared nodes
|
|
102
|
+
CREATE TABLE IF NOT EXISTS memory_edges (
|
|
103
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
104
|
+
src_id TEXT NOT NULL REFERENCES memories(id),
|
|
105
|
+
dst_id TEXT NOT NULL REFERENCES memories(id),
|
|
106
|
+
relation TEXT NOT NULL CHECK (relation IN ('semantic', 'temporal', 'causal', 'entity')),
|
|
107
|
+
weight REAL NOT NULL DEFAULT 0,
|
|
108
|
+
meta_json TEXT,
|
|
109
|
+
created_at INTEGER NOT NULL,
|
|
110
|
+
UNIQUE(src_id, dst_id, relation)
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
CREATE INDEX IF NOT EXISTS idx_memory_edges_src ON memory_edges(src_id, relation);
|
|
114
|
+
CREATE INDEX IF NOT EXISTS idx_memory_edges_dst ON memory_edges(dst_id, relation);
|
|
115
|
+
CREATE INDEX IF NOT EXISTS idx_memories_conversation ON memories(conversation_id, observation_ts);
|
|
116
|
+
|
|
117
|
+
-- System-1 session control block (canonical state header for System-2)
|
|
118
|
+
CREATE TABLE IF NOT EXISTS laya_jev_state (
|
|
119
|
+
session_id TEXT PRIMARY KEY REFERENCES sessions(session_id),
|
|
120
|
+
task_status TEXT NOT NULL DEFAULT 'investigating'
|
|
121
|
+
CHECK (task_status IN (
|
|
122
|
+
'investigating', 'modifying_code', 'awaiting_user',
|
|
123
|
+
'verifying', 'blocked', 'idle'
|
|
124
|
+
)),
|
|
125
|
+
file_target TEXT,
|
|
126
|
+
escalation_risk INTEGER NOT NULL DEFAULT 0 CHECK (escalation_risk IN (0, 1)),
|
|
127
|
+
confidence_score REAL NOT NULL DEFAULT 0,
|
|
128
|
+
state_json TEXT,
|
|
129
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
CREATE TABLE IF NOT EXISTS transcript_entries (
|
|
133
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
134
|
+
session_id TEXT NOT NULL REFERENCES sessions(session_id),
|
|
135
|
+
sequence INTEGER NOT NULL,
|
|
136
|
+
parent_entry_id INTEGER REFERENCES transcript_entries(id),
|
|
137
|
+
entry_type TEXT NOT NULL,
|
|
138
|
+
payload_json TEXT NOT NULL,
|
|
139
|
+
tool_use_id TEXT,
|
|
140
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
141
|
+
UNIQUE(session_id, sequence)
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
CREATE INDEX IF NOT EXISTS idx_transcript_session ON transcript_entries(session_id, sequence);
|
|
145
|
+
|
|
146
|
+
CREATE TABLE IF NOT EXISTS hook_invocations (
|
|
147
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
148
|
+
session_id TEXT NOT NULL,
|
|
149
|
+
conversation_id TEXT NOT NULL,
|
|
150
|
+
hook_event TEXT NOT NULL,
|
|
151
|
+
hook_ordinal INTEGER NOT NULL,
|
|
152
|
+
matcher TEXT NOT NULL,
|
|
153
|
+
command TEXT NOT NULL,
|
|
154
|
+
tool_use_id TEXT,
|
|
155
|
+
tool_name TEXT,
|
|
156
|
+
input_json TEXT NOT NULL,
|
|
157
|
+
exit_code INTEGER,
|
|
158
|
+
stdout_text TEXT,
|
|
159
|
+
stderr_text TEXT,
|
|
160
|
+
started_at TEXT NOT NULL,
|
|
161
|
+
completed_at TEXT,
|
|
162
|
+
skipped_reason TEXT
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
CREATE INDEX IF NOT EXISTS idx_hook_inv_session ON hook_invocations(session_id, hook_ordinal);
|
|
166
|
+
|
|
167
|
+
CREATE TABLE IF NOT EXISTS tool_permission_log (
|
|
168
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
169
|
+
session_id TEXT NOT NULL,
|
|
170
|
+
tool_use_id TEXT NOT NULL,
|
|
171
|
+
tool_name TEXT NOT NULL,
|
|
172
|
+
decision TEXT NOT NULL,
|
|
173
|
+
reason_json TEXT,
|
|
174
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
CREATE TABLE IF NOT EXISTS settings_snapshot (
|
|
178
|
+
scope TEXT PRIMARY KEY,
|
|
179
|
+
json TEXT NOT NULL,
|
|
180
|
+
updated_at TEXT NOT NULL
|
|
181
|
+
);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* postinstall.mjs — runs automatically after `npm install`.
|
|
4
|
+
*
|
|
5
|
+
* Provisions the Laya System-1 venv (~808 MB one-time download) so the CLI
|
|
6
|
+
* starts instantly on every subsequent run. Skips if already provisioned.
|
|
7
|
+
*
|
|
8
|
+
* Exits non-zero on failure so `npm install` surfaces the error clearly.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { spawnSync } from 'node:child_process'
|
|
12
|
+
import fs from 'node:fs'
|
|
13
|
+
import path from 'node:path'
|
|
14
|
+
import { fileURLToPath } from 'node:url'
|
|
15
|
+
|
|
16
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
17
|
+
|
|
18
|
+
const NEHANDA_HOME = process.env.NEHANDA_HOME
|
|
19
|
+
|| path.join(process.env.HOME || '/tmp', '.nehanda')
|
|
20
|
+
const MARKER = path.join(NEHANDA_HOME, '.laya-ready')
|
|
21
|
+
const VENV_PYTHON = path.join(NEHANDA_HOME, 'venv', 'bin', 'python')
|
|
22
|
+
const ENSURE_SCRIPT = path.resolve(__dirname, '..', 'lib', 'scripts', 'ensure-laya-env.sh')
|
|
23
|
+
|
|
24
|
+
// Already provisioned — nothing to do.
|
|
25
|
+
if (fs.existsSync(MARKER) && fs.existsSync(VENV_PYTHON) && !process.env.LAYA_FORCE_REPROVISION) {
|
|
26
|
+
process.stderr.write('[nehanda] Laya already provisioned, skipping.\n')
|
|
27
|
+
process.exit(0)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!fs.existsSync(ENSURE_SCRIPT)) {
|
|
31
|
+
process.stderr.write(
|
|
32
|
+
`[nehanda] ERROR: provisioning script not found at ${ENSURE_SCRIPT}\n` +
|
|
33
|
+
` The package may be corrupted. Try: npm install @asobacloud/nehanda --force\n`
|
|
34
|
+
)
|
|
35
|
+
process.exit(1)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
process.stderr.write(
|
|
39
|
+
'\n[nehanda] Provisioning Laya System-1 venv (~808 MB one-time download).\n' +
|
|
40
|
+
'[nehanda] This only happens once. The CLI will start instantly after this.\n\n'
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
const result = spawnSync('bash', [ENSURE_SCRIPT], {
|
|
44
|
+
stdio: 'inherit',
|
|
45
|
+
env: { ...process.env },
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
if (result.error) {
|
|
49
|
+
process.stderr.write(`[nehanda] Failed to run provisioning script: ${result.error.message}\n`)
|
|
50
|
+
process.exit(1)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (result.status !== 0) {
|
|
54
|
+
process.stderr.write(
|
|
55
|
+
`\n[nehanda] Laya provisioning failed (exit code ${result.status}).\n` +
|
|
56
|
+
` Check the output above for details.\n` +
|
|
57
|
+
` You can retry manually: bash ${ENSURE_SCRIPT}\n`
|
|
58
|
+
)
|
|
59
|
+
process.exit(result.status ?? 1)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
process.stderr.write('\n[nehanda] Laya provisioned successfully. Run: nehanda\n\n')
|