@lamentis/naome 1.1.2 → 1.2.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/Cargo.lock +2 -2
- package/Cargo.toml +1 -1
- package/LICENSE +180 -21
- package/README.md +49 -6
- package/bin/naome-node.js +2 -1579
- package/bin/naome.js +68 -16
- package/crates/naome-cli/Cargo.toml +1 -1
- package/crates/naome-cli/src/check_commands.rs +135 -0
- package/crates/naome-cli/src/cli_args.rs +5 -0
- package/crates/naome-cli/src/dispatcher.rs +37 -0
- package/crates/naome-cli/src/install_bridge.rs +83 -0
- package/crates/naome-cli/src/main.rs +60 -341
- package/crates/naome-cli/src/prompt_commands.rs +68 -0
- package/crates/naome-cli/src/quality_commands.rs +229 -0
- package/crates/naome-cli/src/simple_commands.rs +53 -0
- package/crates/naome-cli/src/workflow_commands.rs +153 -0
- package/crates/naome-core/Cargo.toml +1 -1
- package/crates/naome-core/src/decision/checks.rs +64 -0
- package/crates/naome-core/src/decision/idle.rs +67 -0
- package/crates/naome-core/src/decision/json.rs +36 -0
- package/crates/naome-core/src/decision/states.rs +165 -0
- package/crates/naome-core/src/decision.rs +131 -353
- package/crates/naome-core/src/harness_health/integrity.rs +96 -0
- package/crates/naome-core/src/harness_health.rs +14 -126
- package/crates/naome-core/src/install_plan.rs +5 -0
- package/crates/naome-core/src/intent/classifier.rs +171 -0
- package/crates/naome-core/src/intent/envelope.rs +108 -0
- package/crates/naome-core/src/intent/legacy.rs +138 -0
- package/crates/naome-core/src/intent/legacy_response.rs +76 -0
- package/crates/naome-core/src/intent/model.rs +71 -0
- package/crates/naome-core/src/intent/patterns.rs +170 -0
- package/crates/naome-core/src/intent/resolver.rs +162 -0
- package/crates/naome-core/src/intent/resolver_active.rs +17 -0
- package/crates/naome-core/src/intent/resolver_baseline.rs +55 -0
- package/crates/naome-core/src/intent/resolver_catalog.rs +167 -0
- package/crates/naome-core/src/intent/resolver_policy.rs +72 -0
- package/crates/naome-core/src/intent/resolver_shared.rs +55 -0
- package/crates/naome-core/src/intent/risk.rs +40 -0
- package/crates/naome-core/src/intent/segment.rs +170 -0
- package/crates/naome-core/src/intent.rs +64 -879
- package/crates/naome-core/src/journal.rs +9 -20
- package/crates/naome-core/src/lib.rs +15 -0
- package/crates/naome-core/src/paths.rs +3 -1
- package/crates/naome-core/src/quality/adapter_support.rs +89 -0
- package/crates/naome-core/src/quality/adapters.rs +131 -0
- package/crates/naome-core/src/quality/baseline.rs +75 -0
- package/crates/naome-core/src/quality/checks/duplicate_blocks.rs +175 -0
- package/crates/naome-core/src/quality/checks/near_duplicates.rs +130 -0
- package/crates/naome-core/src/quality/checks.rs +228 -0
- package/crates/naome-core/src/quality/cleanup.rs +84 -0
- package/crates/naome-core/src/quality/config.rs +102 -0
- package/crates/naome-core/src/quality/config_support.rs +24 -0
- package/crates/naome-core/src/quality/mod.rs +108 -0
- package/crates/naome-core/src/quality/scanner/repo_paths.rs +103 -0
- package/crates/naome-core/src/quality/scanner.rs +379 -0
- package/crates/naome-core/src/quality/structure/adapters.rs +84 -0
- package/crates/naome-core/src/quality/structure/checks/basic.rs +153 -0
- package/crates/naome-core/src/quality/structure/checks/directory.rs +144 -0
- package/crates/naome-core/src/quality/structure/checks/pairing.rs +63 -0
- package/crates/naome-core/src/quality/structure/checks.rs +124 -0
- package/crates/naome-core/src/quality/structure/classify/roles.rs +188 -0
- package/crates/naome-core/src/quality/structure/classify.rs +94 -0
- package/crates/naome-core/src/quality/structure/config.rs +89 -0
- package/crates/naome-core/src/quality/structure/defaults.rs +107 -0
- package/crates/naome-core/src/quality/structure/mod.rs +77 -0
- package/crates/naome-core/src/quality/structure/model.rs +124 -0
- package/crates/naome-core/src/quality/types.rs +292 -0
- package/crates/naome-core/src/route/builtin_checks.rs +155 -0
- package/crates/naome-core/src/route/builtin_context.rs +73 -0
- package/crates/naome-core/src/route/builtin_integrity.rs +49 -0
- package/crates/naome-core/src/route/builtin_require.rs +40 -0
- package/crates/naome-core/src/route/context.rs +180 -0
- package/crates/naome-core/src/route/execution.rs +96 -0
- package/crates/naome-core/src/route/execution_baselines.rs +146 -0
- package/crates/naome-core/src/route/execution_support.rs +57 -0
- package/crates/naome-core/src/route/execution_tasks.rs +71 -0
- package/crates/naome-core/src/route/git_ops.rs +72 -0
- package/crates/naome-core/src/route/quality_gate.rs +73 -0
- package/crates/naome-core/src/route/quality_gate_config.rs +126 -0
- package/crates/naome-core/src/route/quality_gate_snapshot.rs +69 -0
- package/crates/naome-core/src/route/worktree.rs +75 -0
- package/crates/naome-core/src/route/worktree_files.rs +32 -0
- package/crates/naome-core/src/route/worktree_plan.rs +131 -0
- package/crates/naome-core/src/route.rs +44 -1155
- package/crates/naome-core/src/task_state/admission.rs +63 -0
- package/crates/naome-core/src/task_state/admission_proof.rs +72 -0
- package/crates/naome-core/src/task_state/api.rs +130 -0
- package/crates/naome-core/src/task_state/commit_gate.rs +138 -0
- package/crates/naome-core/src/task_state/compact_proof.rs +160 -0
- package/crates/naome-core/src/task_state/completed_refresh.rs +89 -0
- package/crates/naome-core/src/task_state/completion.rs +72 -0
- package/crates/naome-core/src/task_state/deleted_paths.rs +47 -0
- package/crates/naome-core/src/task_state/diff.rs +95 -0
- package/crates/naome-core/src/task_state/evidence.rs +154 -0
- package/crates/naome-core/src/task_state/git_io.rs +86 -0
- package/crates/naome-core/src/task_state/git_parse.rs +86 -0
- package/crates/naome-core/src/task_state/git_refs.rs +37 -0
- package/crates/naome-core/src/task_state/human_review_state.rs +31 -0
- package/crates/naome-core/src/task_state/mod.rs +38 -0
- package/crates/naome-core/src/task_state/process_guard.rs +40 -0
- package/crates/naome-core/src/task_state/progress.rs +123 -0
- package/crates/naome-core/src/task_state/proof.rs +139 -0
- package/crates/naome-core/src/task_state/proof_entry.rs +66 -0
- package/crates/naome-core/src/task_state/proof_model.rs +70 -0
- package/crates/naome-core/src/task_state/proof_sources.rs +76 -0
- package/crates/naome-core/src/task_state/push_gate.rs +49 -0
- package/crates/naome-core/src/task_state/reconcile.rs +7 -0
- package/crates/naome-core/src/task_state/repair.rs +168 -0
- package/crates/naome-core/src/task_state/shape.rs +117 -0
- package/crates/naome-core/src/task_state/task_diff_api.rs +170 -0
- package/crates/naome-core/src/task_state/task_records.rs +131 -0
- package/crates/naome-core/src/task_state/task_references.rs +126 -0
- package/crates/naome-core/src/task_state/types.rs +87 -0
- package/crates/naome-core/src/task_state/util.rs +137 -0
- package/crates/naome-core/src/verification/render.rs +122 -0
- package/crates/naome-core/src/verification.rs +177 -58
- package/crates/naome-core/src/verification_contract.rs +49 -21
- package/crates/naome-core/src/workflow/integrity.rs +123 -0
- package/crates/naome-core/src/workflow/integrity_normalize.rs +7 -0
- package/crates/naome-core/src/workflow/integrity_support.rs +110 -0
- package/crates/naome-core/src/workflow/mod.rs +18 -0
- package/crates/naome-core/src/workflow/mutation.rs +68 -0
- package/crates/naome-core/src/workflow/output.rs +111 -0
- package/crates/naome-core/src/workflow/phase_inference.rs +73 -0
- package/crates/naome-core/src/workflow/phases.rs +169 -0
- package/crates/naome-core/src/workflow/policy.rs +156 -0
- package/crates/naome-core/src/workflow/processes.rs +91 -0
- package/crates/naome-core/src/workflow/types.rs +42 -0
- package/crates/naome-core/tests/decision.rs +24 -118
- package/crates/naome-core/tests/harness_health.rs +5 -0
- package/crates/naome-core/tests/intent.rs +97 -792
- package/crates/naome-core/tests/intent_support/mod.rs +133 -0
- package/crates/naome-core/tests/intent_v2.rs +90 -0
- package/crates/naome-core/tests/quality.rs +319 -0
- package/crates/naome-core/tests/quality_structure.rs +116 -0
- package/crates/naome-core/tests/quality_structure_adapters.rs +98 -0
- package/crates/naome-core/tests/quality_structure_policy.rs +125 -0
- package/crates/naome-core/tests/quality_structure_support/mod.rs +249 -0
- package/crates/naome-core/tests/repo_support/mod.rs +16 -0
- package/crates/naome-core/tests/repo_support/repo.rs +113 -0
- package/crates/naome-core/tests/repo_support/repo_factories.rs +99 -0
- package/crates/naome-core/tests/repo_support/repo_helpers.rs +123 -0
- package/crates/naome-core/tests/repo_support/routes.rs +81 -0
- package/crates/naome-core/tests/repo_support/verification.rs +168 -0
- package/crates/naome-core/tests/repo_support/verification_values.rs +135 -0
- package/crates/naome-core/tests/route.rs +1 -1476
- package/crates/naome-core/tests/route_baseline.rs +86 -0
- package/crates/naome-core/tests/route_completion.rs +141 -0
- package/crates/naome-core/tests/route_harness_refresh.rs +135 -0
- package/crates/naome-core/tests/route_user_diff.rs +198 -0
- package/crates/naome-core/tests/route_worktree.rs +54 -0
- package/crates/naome-core/tests/task_state.rs +60 -429
- package/crates/naome-core/tests/task_state_compact.rs +110 -0
- package/crates/naome-core/tests/task_state_compact_support/mod.rs +5 -0
- package/crates/naome-core/tests/task_state_compact_support/repo.rs +130 -0
- package/crates/naome-core/tests/task_state_compact_support/states.rs +151 -0
- package/crates/naome-core/tests/task_state_support/mod.rs +163 -0
- package/crates/naome-core/tests/task_state_support/states.rs +84 -0
- package/crates/naome-core/tests/verification.rs +4 -45
- package/crates/naome-core/tests/verification_contract.rs +22 -78
- package/crates/naome-core/tests/workflow_integrity.rs +85 -0
- package/crates/naome-core/tests/workflow_policy.rs +139 -0
- package/crates/naome-core/tests/workflow_support/mod.rs +194 -0
- package/installer/agents.js +90 -0
- package/installer/context.js +67 -0
- package/installer/filesystem.js +166 -0
- package/installer/flows.js +84 -0
- package/installer/git-boundary.js +170 -0
- package/installer/git-hook-content.js +36 -0
- package/installer/git-hooks.js +134 -0
- package/installer/git-local.js +2 -0
- package/installer/git-shared.js +35 -0
- package/installer/harness-file-ops.js +140 -0
- package/installer/harness-files.js +56 -0
- package/installer/harness-verification.js +123 -0
- package/installer/install-plan.js +66 -0
- package/installer/main.js +25 -0
- package/installer/manifest-state.js +167 -0
- package/installer/native-build.js +24 -0
- package/installer/native-format.js +6 -0
- package/installer/native.js +162 -0
- package/installer/output.js +131 -0
- package/installer/version.js +32 -0
- package/native/darwin-arm64/naome +0 -0
- package/native/linux-x64/naome +0 -0
- package/package.json +3 -2
- package/templates/naome-root/.naome/bin/check-harness-health.js +66 -85
- package/templates/naome-root/.naome/bin/check-task-state.js +9 -10
- package/templates/naome-root/.naome/bin/naome.js +51 -76
- package/templates/naome-root/.naome/manifest.json +22 -18
- package/templates/naome-root/.naome/repository-quality-baseline.json +5 -0
- package/templates/naome-root/.naome/repository-quality.json +24 -0
- package/templates/naome-root/.naome/repository-structure.json +90 -0
- package/templates/naome-root/.naome/task-contract.schema.json +93 -11
- package/templates/naome-root/.naome/upgrade-state.json +1 -1
- package/templates/naome-root/.naome/verification.json +38 -0
- package/templates/naome-root/AGENTS.md +3 -0
- package/templates/naome-root/docs/naome/agent-workflow.md +25 -12
- package/templates/naome-root/docs/naome/execution.md +25 -21
- package/templates/naome-root/docs/naome/index.md +5 -3
- package/templates/naome-root/docs/naome/repository-quality.md +46 -0
- package/templates/naome-root/docs/naome/repository-structure.md +51 -0
- package/templates/naome-root/docs/naome/testing.md +13 -0
- package/crates/naome-core/src/task_state.rs +0 -2210
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
use sha2::{Digest, Sha256};
|
|
2
|
+
|
|
3
|
+
const HEALTH_CHECKER_RELATIVE_PATH: &str = ".naome/bin/check-harness-health.js";
|
|
4
|
+
const TASK_STATE_CHECKER_RELATIVE_PATH: &str = ".naome/bin/check-task-state.js";
|
|
5
|
+
|
|
6
|
+
pub(crate) const NAOME_COMMAND_RELATIVE_PATH: &str = ".naome/bin/naome.js";
|
|
7
|
+
|
|
8
|
+
#[cfg(windows)]
|
|
9
|
+
pub(crate) const NATIVE_BINARY_RELATIVE_PATH: &str = ".naome/bin/naome-rust.exe";
|
|
10
|
+
#[cfg(not(windows))]
|
|
11
|
+
pub(crate) const NATIVE_BINARY_RELATIVE_PATH: &str = ".naome/bin/naome-rust";
|
|
12
|
+
|
|
13
|
+
pub(crate) fn machine_integrity_hash(relative_path: &str, content: &[u8]) -> String {
|
|
14
|
+
format!("sha256:{}", machine_sha256(relative_path, content))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
pub(crate) fn sha256_bytes(content: &[u8]) -> String {
|
|
18
|
+
let mut hasher = Sha256::new();
|
|
19
|
+
hasher.update(content);
|
|
20
|
+
format!("{:x}", hasher.finalize())
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
pub(crate) fn is_integrity_hash(value: &str) -> bool {
|
|
24
|
+
value.len() == "sha256:".len() + 64
|
|
25
|
+
&& value.starts_with("sha256:")
|
|
26
|
+
&& value["sha256:".len()..]
|
|
27
|
+
.chars()
|
|
28
|
+
.all(|ch| ch.is_ascii_hexdigit() && !ch.is_ascii_uppercase())
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
pub(crate) fn native_integrity_from_naome_command(content: &str) -> Option<String> {
|
|
32
|
+
let prefix = "const expectedNativeBinaryIntegrity = \"";
|
|
33
|
+
let start = content.find(prefix)? + prefix.len();
|
|
34
|
+
let rest = &content[start..];
|
|
35
|
+
let end = rest.find("\";")?;
|
|
36
|
+
let value = &rest[..end];
|
|
37
|
+
is_integrity_hash(value).then(|| value.to_string())
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
fn machine_sha256(relative_path: &str, content: &[u8]) -> String {
|
|
41
|
+
let normalized = normalize_machine_owned_content(relative_path, content);
|
|
42
|
+
sha256_bytes(&normalized)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fn normalize_machine_owned_content(relative_path: &str, content: &[u8]) -> Vec<u8> {
|
|
46
|
+
if relative_path == HEALTH_CHECKER_RELATIVE_PATH
|
|
47
|
+
|| relative_path == TASK_STATE_CHECKER_RELATIVE_PATH
|
|
48
|
+
{
|
|
49
|
+
return replace_expected_integrity_block(content);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if relative_path == NAOME_COMMAND_RELATIVE_PATH {
|
|
53
|
+
return replace_native_integrity_line(content);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
content.to_vec()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
fn replace_expected_integrity_block(content: &[u8]) -> Vec<u8> {
|
|
60
|
+
let text = String::from_utf8_lossy(content);
|
|
61
|
+
let start_marker = "const expectedMachineOwnedIntegrity = Object.freeze({\n";
|
|
62
|
+
let normalized =
|
|
63
|
+
"const expectedMachineOwnedIntegrity = Object.freeze({\n __generated__: \"sha256:generated\"\n});\n";
|
|
64
|
+
let Some(start) = text.find(start_marker) else {
|
|
65
|
+
return content.to_vec();
|
|
66
|
+
};
|
|
67
|
+
let after_start = start + start_marker.len();
|
|
68
|
+
let Some(relative_end) = text[after_start..].find("\n});\n") else {
|
|
69
|
+
return content.to_vec();
|
|
70
|
+
};
|
|
71
|
+
let end = after_start + relative_end + "\n});\n".len();
|
|
72
|
+
|
|
73
|
+
let mut next = String::with_capacity(text.len());
|
|
74
|
+
next.push_str(&text[..start]);
|
|
75
|
+
next.push_str(normalized);
|
|
76
|
+
next.push_str(&text[end..]);
|
|
77
|
+
next.into_bytes()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fn replace_native_integrity_line(content: &[u8]) -> Vec<u8> {
|
|
81
|
+
let text = String::from_utf8_lossy(content);
|
|
82
|
+
let prefix = "const expectedNativeBinaryIntegrity = \"";
|
|
83
|
+
let Some(start) = text.find(prefix) else {
|
|
84
|
+
return content.to_vec();
|
|
85
|
+
};
|
|
86
|
+
let Some(relative_end) = text[start..].find(";\n") else {
|
|
87
|
+
return content.to_vec();
|
|
88
|
+
};
|
|
89
|
+
let end = start + relative_end + ";\n".len();
|
|
90
|
+
|
|
91
|
+
let mut next = String::with_capacity(text.len());
|
|
92
|
+
next.push_str(&text[..start]);
|
|
93
|
+
next.push_str("const expectedNativeBinaryIntegrity = \"sha256:generated\";\n");
|
|
94
|
+
next.push_str(&text[end..]);
|
|
95
|
+
next.into_bytes()
|
|
96
|
+
}
|
|
@@ -1,49 +1,19 @@
|
|
|
1
|
+
mod integrity;
|
|
2
|
+
|
|
1
3
|
use std::collections::{HashMap, HashSet};
|
|
2
4
|
use std::fs;
|
|
3
5
|
use std::path::{Component, Path};
|
|
4
6
|
|
|
5
7
|
use serde_json::Value;
|
|
6
|
-
use sha2::{Digest, Sha256};
|
|
7
8
|
|
|
9
|
+
pub(crate) use self::integrity::machine_integrity_hash;
|
|
10
|
+
use self::integrity::{
|
|
11
|
+
is_integrity_hash, native_integrity_from_naome_command, sha256_bytes,
|
|
12
|
+
NAOME_COMMAND_RELATIVE_PATH, NATIVE_BINARY_RELATIVE_PATH,
|
|
13
|
+
};
|
|
14
|
+
use crate::install_plan::{MACHINE_OWNED_PATHS, PROJECT_OWNED_PATHS};
|
|
8
15
|
use crate::models::NaomeError;
|
|
9
16
|
|
|
10
|
-
const HEALTH_CHECKER_RELATIVE_PATH: &str = ".naome/bin/check-harness-health.js";
|
|
11
|
-
const TASK_STATE_CHECKER_RELATIVE_PATH: &str = ".naome/bin/check-task-state.js";
|
|
12
|
-
const NAOME_COMMAND_RELATIVE_PATH: &str = ".naome/bin/naome.js";
|
|
13
|
-
|
|
14
|
-
#[cfg(windows)]
|
|
15
|
-
const NATIVE_BINARY_RELATIVE_PATH: &str = ".naome/bin/naome-rust.exe";
|
|
16
|
-
#[cfg(not(windows))]
|
|
17
|
-
const NATIVE_BINARY_RELATIVE_PATH: &str = ".naome/bin/naome-rust";
|
|
18
|
-
|
|
19
|
-
const EXPECTED_MACHINE_OWNED_PATHS: &[&str] = &[
|
|
20
|
-
"AGENTS.md",
|
|
21
|
-
".naome/package.json",
|
|
22
|
-
".naome/bin/naome.js",
|
|
23
|
-
".naome/bin/check-task-state.js",
|
|
24
|
-
".naome/bin/check-harness-health.js",
|
|
25
|
-
".naome/task-contract.schema.json",
|
|
26
|
-
"docs/naome/index.md",
|
|
27
|
-
"docs/naome/first-run.md",
|
|
28
|
-
"docs/naome/agent-workflow.md",
|
|
29
|
-
"docs/naome/execution.md",
|
|
30
|
-
"docs/naome/upgrade.md",
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
const EXPECTED_PROJECT_OWNED_PATHS: &[&str] = &[
|
|
34
|
-
".naomeignore",
|
|
35
|
-
".naome/init-state.json",
|
|
36
|
-
".naome/manifest.json",
|
|
37
|
-
".naome/task-state.json",
|
|
38
|
-
".naome/upgrade-state.json",
|
|
39
|
-
".naome/verification.json",
|
|
40
|
-
"docs/naome/architecture.md",
|
|
41
|
-
"docs/naome/decisions.md",
|
|
42
|
-
"docs/naome/repo-profile.md",
|
|
43
|
-
"docs/naome/security.md",
|
|
44
|
-
"docs/naome/testing.md",
|
|
45
|
-
];
|
|
46
|
-
|
|
47
17
|
#[derive(Debug, Clone, Default)]
|
|
48
18
|
pub struct HarnessHealthOptions {
|
|
49
19
|
pub expected_integrity: HashMap<String, String>,
|
|
@@ -57,11 +27,11 @@ pub fn validate_harness_health(
|
|
|
57
27
|
) -> Result<Vec<String>, NaomeError> {
|
|
58
28
|
let mut errors = Vec::new();
|
|
59
29
|
|
|
60
|
-
for relative_path in
|
|
30
|
+
for relative_path in MACHINE_OWNED_PATHS {
|
|
61
31
|
validate_regular_file(root, relative_path, &mut errors)?;
|
|
62
32
|
}
|
|
63
33
|
|
|
64
|
-
for relative_path in
|
|
34
|
+
for relative_path in PROJECT_OWNED_PATHS {
|
|
65
35
|
validate_regular_file(root, relative_path, &mut errors)?;
|
|
66
36
|
}
|
|
67
37
|
|
|
@@ -130,13 +100,13 @@ fn validate_manifest_ownership(manifest: &Value, errors: &mut Vec<String>) {
|
|
|
130
100
|
|
|
131
101
|
validate_contains_all(
|
|
132
102
|
&machine_owned,
|
|
133
|
-
|
|
103
|
+
MACHINE_OWNED_PATHS,
|
|
134
104
|
".naome/manifest.json machineOwned",
|
|
135
105
|
errors,
|
|
136
106
|
);
|
|
137
107
|
validate_contains_all(
|
|
138
108
|
&project_owned,
|
|
139
|
-
|
|
109
|
+
PROJECT_OWNED_PATHS,
|
|
140
110
|
".naome/manifest.json projectOwned",
|
|
141
111
|
errors,
|
|
142
112
|
);
|
|
@@ -152,7 +122,7 @@ fn validate_manifest_integrity(
|
|
|
152
122
|
return Ok(());
|
|
153
123
|
};
|
|
154
124
|
|
|
155
|
-
for relative_path in
|
|
125
|
+
for relative_path in MACHINE_OWNED_PATHS {
|
|
156
126
|
let packaged_expected = options.expected_integrity.get(*relative_path);
|
|
157
127
|
let manifest_expected = integrity.get(*relative_path).and_then(Value::as_str);
|
|
158
128
|
|
|
@@ -190,7 +160,7 @@ fn validate_manifest_integrity(
|
|
|
190
160
|
}
|
|
191
161
|
|
|
192
162
|
let content = fs::read(root.join(relative_path))?;
|
|
193
|
-
let actual =
|
|
163
|
+
let actual = machine_integrity_hash(relative_path, &content);
|
|
194
164
|
if actual != *packaged_expected {
|
|
195
165
|
errors.push(format!(
|
|
196
166
|
"{relative_path} integrity mismatch. Expected {packaged_expected}, got {actual}."
|
|
@@ -475,85 +445,3 @@ fn is_version(value: &str) -> bool {
|
|
|
475
445
|
.iter()
|
|
476
446
|
.all(|part| !part.is_empty() && part.chars().all(|ch| ch.is_ascii_digit()))
|
|
477
447
|
}
|
|
478
|
-
|
|
479
|
-
fn is_integrity_hash(value: &str) -> bool {
|
|
480
|
-
value.len() == "sha256:".len() + 64
|
|
481
|
-
&& value.starts_with("sha256:")
|
|
482
|
-
&& value["sha256:".len()..]
|
|
483
|
-
.chars()
|
|
484
|
-
.all(|ch| ch.is_ascii_hexdigit() && !ch.is_ascii_uppercase())
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
fn machine_sha256(relative_path: &str, content: &[u8]) -> String {
|
|
488
|
-
let normalized = normalize_machine_owned_content(relative_path, content);
|
|
489
|
-
sha256_bytes(&normalized)
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
fn sha256_bytes(content: &[u8]) -> String {
|
|
493
|
-
let mut hasher = Sha256::new();
|
|
494
|
-
hasher.update(content);
|
|
495
|
-
format!("{:x}", hasher.finalize())
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
fn normalize_machine_owned_content(relative_path: &str, content: &[u8]) -> Vec<u8> {
|
|
499
|
-
if relative_path == HEALTH_CHECKER_RELATIVE_PATH
|
|
500
|
-
|| relative_path == TASK_STATE_CHECKER_RELATIVE_PATH
|
|
501
|
-
{
|
|
502
|
-
return replace_expected_integrity_block(content);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
if relative_path == NAOME_COMMAND_RELATIVE_PATH {
|
|
506
|
-
return replace_native_integrity_line(content);
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
content.to_vec()
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
fn replace_expected_integrity_block(content: &[u8]) -> Vec<u8> {
|
|
513
|
-
let text = String::from_utf8_lossy(content);
|
|
514
|
-
let start_marker = "const expectedMachineOwnedIntegrity = Object.freeze({\n";
|
|
515
|
-
let normalized =
|
|
516
|
-
"const expectedMachineOwnedIntegrity = Object.freeze({\n __generated__: \"sha256:generated\"\n});\n";
|
|
517
|
-
let Some(start) = text.find(start_marker) else {
|
|
518
|
-
return content.to_vec();
|
|
519
|
-
};
|
|
520
|
-
let after_start = start + start_marker.len();
|
|
521
|
-
let Some(relative_end) = text[after_start..].find("\n});\n") else {
|
|
522
|
-
return content.to_vec();
|
|
523
|
-
};
|
|
524
|
-
let end = after_start + relative_end + "\n});\n".len();
|
|
525
|
-
|
|
526
|
-
let mut next = String::with_capacity(text.len());
|
|
527
|
-
next.push_str(&text[..start]);
|
|
528
|
-
next.push_str(normalized);
|
|
529
|
-
next.push_str(&text[end..]);
|
|
530
|
-
next.into_bytes()
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
fn replace_native_integrity_line(content: &[u8]) -> Vec<u8> {
|
|
534
|
-
let text = String::from_utf8_lossy(content);
|
|
535
|
-
let prefix = "const expectedNativeBinaryIntegrity = \"";
|
|
536
|
-
let Some(start) = text.find(prefix) else {
|
|
537
|
-
return content.to_vec();
|
|
538
|
-
};
|
|
539
|
-
let Some(relative_end) = text[start..].find(";\n") else {
|
|
540
|
-
return content.to_vec();
|
|
541
|
-
};
|
|
542
|
-
let end = start + relative_end + ";\n".len();
|
|
543
|
-
let replacement = "const expectedNativeBinaryIntegrity = \"sha256:generated\";\n";
|
|
544
|
-
|
|
545
|
-
let mut next = String::with_capacity(text.len());
|
|
546
|
-
next.push_str(&text[..start]);
|
|
547
|
-
next.push_str(replacement);
|
|
548
|
-
next.push_str(&text[end..]);
|
|
549
|
-
next.into_bytes()
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
fn native_integrity_from_naome_command(content: &str) -> Option<String> {
|
|
553
|
-
let prefix = "const expectedNativeBinaryIntegrity = \"";
|
|
554
|
-
let start = content.find(prefix)? + prefix.len();
|
|
555
|
-
let rest = &content[start..];
|
|
556
|
-
let end = rest.find("\";")?;
|
|
557
|
-
let value = &rest[..end];
|
|
558
|
-
is_integrity_hash(value).then(|| value.to_string())
|
|
559
|
-
}
|
|
@@ -21,9 +21,14 @@ pub const PROJECT_OWNED_PATHS: &[&str] = &[
|
|
|
21
21
|
".naome/task-state.json",
|
|
22
22
|
".naome/upgrade-state.json",
|
|
23
23
|
".naome/verification.json",
|
|
24
|
+
".naome/repository-quality.json",
|
|
25
|
+
".naome/repository-structure.json",
|
|
26
|
+
".naome/repository-quality-baseline.json",
|
|
24
27
|
"docs/naome/architecture.md",
|
|
25
28
|
"docs/naome/decisions.md",
|
|
26
29
|
"docs/naome/repo-profile.md",
|
|
30
|
+
"docs/naome/repository-quality.md",
|
|
31
|
+
"docs/naome/repository-structure.md",
|
|
27
32
|
"docs/naome/security.md",
|
|
28
33
|
"docs/naome/testing.md",
|
|
29
34
|
];
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
use std::collections::HashSet;
|
|
2
|
+
|
|
3
|
+
use super::envelope::parse_routing_envelope;
|
|
4
|
+
use super::model::{CanonicalIntent, IntentCandidate, IntentKind, PromptSegment, SegmentKind};
|
|
5
|
+
use super::patterns;
|
|
6
|
+
use super::risk::detect_risk;
|
|
7
|
+
use super::segment::{actionable_text, segment_prompt};
|
|
8
|
+
|
|
9
|
+
const DIRECT_WORKFLOW_CONFIDENCE: u8 = 90;
|
|
10
|
+
const NEW_TASK_CONFIDENCE: u8 = 80;
|
|
11
|
+
const REVISION_CONFIDENCE: u8 = 78;
|
|
12
|
+
const GENERIC_WORK_CONFIDENCE: u8 = 50;
|
|
13
|
+
|
|
14
|
+
pub(crate) fn canonical_intent(prompt: &str) -> CanonicalIntent {
|
|
15
|
+
let segments = segment_prompt(prompt);
|
|
16
|
+
let actionable = actionable_text(&segments);
|
|
17
|
+
let mut candidates = classify_candidates(&segments);
|
|
18
|
+
let mut risk = detect_risk(&segments);
|
|
19
|
+
|
|
20
|
+
if let Some(envelope) = parse_routing_envelope(prompt) {
|
|
21
|
+
candidates.clear();
|
|
22
|
+
candidates.extend(envelope.candidates);
|
|
23
|
+
risk.risk_codes.extend(envelope.risk.risk_codes);
|
|
24
|
+
risk.risk_codes.sort();
|
|
25
|
+
risk.risk_codes.dedup();
|
|
26
|
+
risk.has_risky_terms = !risk.risk_codes.is_empty();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
candidates = dedupe_candidates(candidates);
|
|
30
|
+
let references_current_task = has_candidate_kind(&candidates, IntentKind::TaskRevision);
|
|
31
|
+
if risk.has_risky_terms {
|
|
32
|
+
candidates.push(IntentCandidate {
|
|
33
|
+
kind: IntentKind::Unsafe,
|
|
34
|
+
confidence: 100,
|
|
35
|
+
evidence: risk.risk_codes.join(","),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
CanonicalIntent {
|
|
40
|
+
is_empty: actionable.trim().is_empty(),
|
|
41
|
+
references_current_task,
|
|
42
|
+
has_workflow_conflict: has_workflow_conflict(&candidates),
|
|
43
|
+
candidates,
|
|
44
|
+
risk,
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
pub(crate) fn winning_intent(canonical: &CanonicalIntent) -> IntentKind {
|
|
49
|
+
if canonical.is_empty || canonical.has_workflow_conflict {
|
|
50
|
+
return IntentKind::Ambiguous;
|
|
51
|
+
}
|
|
52
|
+
for kind in [
|
|
53
|
+
IntentKind::Unsafe,
|
|
54
|
+
IntentKind::StatusQuestion,
|
|
55
|
+
IntentKind::RepairRequest,
|
|
56
|
+
IntentKind::CancelRequest,
|
|
57
|
+
IntentKind::ReviewRequest,
|
|
58
|
+
IntentKind::NoCommitRequest,
|
|
59
|
+
IntentKind::CommitRequest,
|
|
60
|
+
IntentKind::NewTask,
|
|
61
|
+
IntentKind::TaskRevision,
|
|
62
|
+
IntentKind::TaskCompletion,
|
|
63
|
+
] {
|
|
64
|
+
if has_candidate(canonical, kind) {
|
|
65
|
+
return kind;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
IntentKind::Ambiguous
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
pub(crate) fn has_candidate(canonical: &CanonicalIntent, kind: IntentKind) -> bool {
|
|
72
|
+
canonical
|
|
73
|
+
.candidates
|
|
74
|
+
.iter()
|
|
75
|
+
.any(|candidate| candidate.kind == kind)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
fn classify_candidates(segments: &[PromptSegment]) -> Vec<IntentCandidate> {
|
|
79
|
+
let mut candidates = Vec::new();
|
|
80
|
+
for segment in action_segments(segments) {
|
|
81
|
+
classify_direct_workflow(&mut candidates, segment);
|
|
82
|
+
classify_task_work(&mut candidates, &segment.text);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if candidates.is_empty() {
|
|
86
|
+
let actionable = actionable_text(segments);
|
|
87
|
+
if patterns::is_generic_work_request(&actionable) {
|
|
88
|
+
push(
|
|
89
|
+
&mut candidates,
|
|
90
|
+
IntentKind::NewTask,
|
|
91
|
+
GENERIC_WORK_CONFIDENCE,
|
|
92
|
+
"generic_work_request",
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
dedupe_candidates(candidates)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
fn classify_direct_workflow(candidates: &mut Vec<IntentCandidate>, segment: &PromptSegment) {
|
|
101
|
+
let intents = if segment.kind == SegmentKind::CommandLike {
|
|
102
|
+
patterns::command_workflow_intents(&segment.text)
|
|
103
|
+
} else {
|
|
104
|
+
patterns::structured_workflow_intents(&segment.text)
|
|
105
|
+
};
|
|
106
|
+
for intent in intents {
|
|
107
|
+
push(
|
|
108
|
+
candidates,
|
|
109
|
+
intent,
|
|
110
|
+
DIRECT_WORKFLOW_CONFIDENCE,
|
|
111
|
+
&patterns::normalized(&segment.text),
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fn classify_task_work(candidates: &mut Vec<IntentCandidate>, text: &str) {
|
|
117
|
+
if let Some(intent) = patterns::explicit_task_intent(text) {
|
|
118
|
+
let confidence = if intent == IntentKind::NewTask {
|
|
119
|
+
NEW_TASK_CONFIDENCE
|
|
120
|
+
} else {
|
|
121
|
+
REVISION_CONFIDENCE
|
|
122
|
+
};
|
|
123
|
+
push(candidates, intent, confidence, &patterns::normalized(text));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
fn has_candidate_kind(candidates: &[IntentCandidate], kind: IntentKind) -> bool {
|
|
128
|
+
candidates.iter().any(|candidate| candidate.kind == kind)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
fn has_workflow_conflict(candidates: &[IntentCandidate]) -> bool {
|
|
132
|
+
let workflow = candidates
|
|
133
|
+
.iter()
|
|
134
|
+
.filter(|candidate| candidate.confidence >= DIRECT_WORKFLOW_CONFIDENCE)
|
|
135
|
+
.filter(|candidate| {
|
|
136
|
+
!matches!(
|
|
137
|
+
candidate.kind,
|
|
138
|
+
IntentKind::NewTask | IntentKind::TaskRevision | IntentKind::Unsafe
|
|
139
|
+
)
|
|
140
|
+
})
|
|
141
|
+
.map(|candidate| candidate.kind)
|
|
142
|
+
.collect::<HashSet<_>>();
|
|
143
|
+
workflow.len() > 1
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
fn action_segments(segments: &[PromptSegment]) -> impl Iterator<Item = &PromptSegment> {
|
|
147
|
+
segments.iter().filter(|segment| {
|
|
148
|
+
matches!(
|
|
149
|
+
segment.kind,
|
|
150
|
+
super::model::SegmentKind::Prose
|
|
151
|
+
| super::model::SegmentKind::ListItem
|
|
152
|
+
| super::model::SegmentKind::CommandLike
|
|
153
|
+
)
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
fn push(candidates: &mut Vec<IntentCandidate>, kind: IntentKind, confidence: u8, text: &str) {
|
|
158
|
+
candidates.push(IntentCandidate {
|
|
159
|
+
kind,
|
|
160
|
+
confidence,
|
|
161
|
+
evidence: text.to_string(),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
fn dedupe_candidates(candidates: Vec<IntentCandidate>) -> Vec<IntentCandidate> {
|
|
166
|
+
let mut seen = HashSet::new();
|
|
167
|
+
candidates
|
|
168
|
+
.into_iter()
|
|
169
|
+
.filter(|candidate| seen.insert((candidate.kind, candidate.evidence.clone())))
|
|
170
|
+
.collect()
|
|
171
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
use serde::Deserialize;
|
|
2
|
+
|
|
3
|
+
use super::model::{IntentCandidate, IntentKind, RiskContext};
|
|
4
|
+
|
|
5
|
+
const ENVELOPE_FENCE: &str = "```naome-intent-v2";
|
|
6
|
+
|
|
7
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
8
|
+
pub(crate) struct RoutingEnvelope {
|
|
9
|
+
pub candidates: Vec<IntentCandidate>,
|
|
10
|
+
pub risk: RiskContext,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
#[derive(Debug, Deserialize)]
|
|
14
|
+
#[serde(rename_all = "camelCase")]
|
|
15
|
+
struct EnvelopeInput {
|
|
16
|
+
schema: Option<String>,
|
|
17
|
+
workflow_action: Option<String>,
|
|
18
|
+
task_intent: Option<String>,
|
|
19
|
+
risk: Option<String>,
|
|
20
|
+
risk_codes: Option<Vec<String>>,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
pub(crate) fn parse_routing_envelope(prompt: &str) -> Option<RoutingEnvelope> {
|
|
24
|
+
let json = envelope_json(prompt)?;
|
|
25
|
+
let input = serde_json::from_str::<EnvelopeInput>(json).ok()?;
|
|
26
|
+
if !matches!(input.schema.as_deref(), Some("naome.intent.v2")) {
|
|
27
|
+
return None;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let mut candidates = Vec::new();
|
|
31
|
+
if let Some(kind) = workflow_action(input.workflow_action.as_deref()) {
|
|
32
|
+
candidates.push(candidate(kind, "envelope.workflowAction"));
|
|
33
|
+
}
|
|
34
|
+
if let Some(kind) = task_intent(input.task_intent.as_deref()) {
|
|
35
|
+
candidates.push(candidate(kind, "envelope.taskIntent"));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let risk_codes = envelope_risk_codes(input.risk.as_deref(), input.risk_codes);
|
|
39
|
+
let risk = RiskContext {
|
|
40
|
+
has_risky_terms: !risk_codes.is_empty(),
|
|
41
|
+
risk_codes,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
Some(RoutingEnvelope { candidates, risk })
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fn envelope_json(prompt: &str) -> Option<&str> {
|
|
48
|
+
let start = prompt.find(ENVELOPE_FENCE)?;
|
|
49
|
+
let after_open = &prompt[start + ENVELOPE_FENCE.len()..];
|
|
50
|
+
let after_line = after_open
|
|
51
|
+
.strip_prefix('\n')
|
|
52
|
+
.or_else(|| after_open.strip_prefix("\r\n"))?;
|
|
53
|
+
let end = after_line.find("\n```")?;
|
|
54
|
+
Some(after_line[..end].trim())
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fn workflow_action(value: Option<&str>) -> Option<IntentKind> {
|
|
58
|
+
match normalized_value(value)?.as_str() {
|
|
59
|
+
"none" | "unknown" => None,
|
|
60
|
+
"commit_request" => Some(IntentKind::CommitRequest),
|
|
61
|
+
"review_request" => Some(IntentKind::ReviewRequest),
|
|
62
|
+
"repair_request" => Some(IntentKind::RepairRequest),
|
|
63
|
+
"cancel_request" => Some(IntentKind::CancelRequest),
|
|
64
|
+
"task_completion" => Some(IntentKind::TaskCompletion),
|
|
65
|
+
"status_question" => Some(IntentKind::StatusQuestion),
|
|
66
|
+
"no_commit_request" => Some(IntentKind::NoCommitRequest),
|
|
67
|
+
_ => None,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
fn task_intent(value: Option<&str>) -> Option<IntentKind> {
|
|
72
|
+
match normalized_value(value)?.as_str() {
|
|
73
|
+
"none" | "unknown" => None,
|
|
74
|
+
"new_task" => Some(IntentKind::NewTask),
|
|
75
|
+
"task_revision" => Some(IntentKind::TaskRevision),
|
|
76
|
+
_ => None,
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fn envelope_risk_codes(risk: Option<&str>, risk_codes: Option<Vec<String>>) -> Vec<String> {
|
|
81
|
+
let mut codes = risk_codes.unwrap_or_default();
|
|
82
|
+
match normalized_value(risk).as_deref() {
|
|
83
|
+
Some("none") | Some("unknown") | None => {}
|
|
84
|
+
Some("unsafe") | Some("unsafe_context") => codes.push("envelope_risk:unsafe".to_string()),
|
|
85
|
+
Some("credential_context") => {
|
|
86
|
+
codes.push("envelope_risk:credential_context".to_string());
|
|
87
|
+
}
|
|
88
|
+
Some("bypass_context") => codes.push("envelope_risk:bypass_context".to_string()),
|
|
89
|
+
Some(other) => codes.push(format!("envelope_risk:{other}")),
|
|
90
|
+
}
|
|
91
|
+
codes.sort();
|
|
92
|
+
codes.dedup();
|
|
93
|
+
codes
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
fn normalized_value(value: Option<&str>) -> Option<String> {
|
|
97
|
+
value
|
|
98
|
+
.map(|value| value.trim().to_ascii_lowercase())
|
|
99
|
+
.filter(|value| !value.is_empty())
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
fn candidate(kind: IntentKind, evidence: &str) -> IntentCandidate {
|
|
103
|
+
IntentCandidate {
|
|
104
|
+
kind,
|
|
105
|
+
confidence: 100,
|
|
106
|
+
evidence: evidence.to_string(),
|
|
107
|
+
}
|
|
108
|
+
}
|